<commit_before>var URLS = {
    "pub_upload": "/upload_pubkey/",
    "pri_upload": "/upload_prikey/",
    "get_pubkeys": "/get_pubkeys/",
    "get_prikey": "/get_prikey/",
};

function getPubKeysFromServer(usernames) {
    var url = buildUrl(URLS.get_pubkeys);
    var data = {
        "requested_keys": JSON.stringify(usernames),
    };
    var pub_keys;
    $.ajax({
        type: "GET",
        url: url,
        data: data,
        success: function(res) {
            pub_keys = res;
            var cachedUsers = loadLocalStore('cachedUsers');
            for (username in res) {
                cachedUsers[username] = res[username];
            }
            writeLocalStorage("cachedUsers", cachedUsers);
        },
        failure: function() {
            var e_message = "JS ERROR: getPubKeysFromServer ";
            logErrorToServer(e_message);
            return false;
        },
        async: false
    });
    return pub_keys
}

function refreshLocalStorage(username, password, encrypted_pri_key) {
    writeLocalStorage("userMeta", {
        'username': username,
        'pri_key': recoverPriKey(encrypted_pri_key, password),
        'defaultEncrypt': true,
    });
}

function recoverPriKey(encrypted_pri_key, password) {
    return JSON.parse(decryptAES(encrypted_pri_key, password));
}

function getPriKeyFromServer() {
    var url = buildUrl(URLS.get_prikey);
    var pri_key;
    $.ajax({
        type: "GET",
        url: url,
        success: function(res) {
            pri_key = res.pri_key;
        },
        failure: function() {
            var e_message = "JS ERROR: getPriKeyFromServer ";
            logErrorToServer(e_message);
            return false;
        },
        async: false
    });
    return pri_key
}

function uploadPubKey(username, pub_key) {
    var url = buildUrl(URLS.pub_upload);
    $.ajax({
        type: "POST",
        url: url,
        data: {
            "username": username,
            "pub_key": pub_key
        },
        success: function(res) {
            return true;
        },
        failure: function() {
            var e_message = "JS ERROR: uploadPubKey | " + username;
            logErrorToServer(e_message);
            return false;
        },
        async: false
    });
}

function uploadPriKey(username, pri_key) {
    var url = buildUrl(URLS.pri_upload);
    $.ajax({
        type: "POST",
        url: url,
        data: {
            "username": username,
            "pri_key": pri_key
        },
        success: function(res) {
            return true;
        },
        failure: function() {
            var e_message = "JS ERROR: uploadPriKey | " + username;
            logErrorToServer(e_message);
            return false;
        },
        async: false
    });
}<commit_msg>no blanks in cache<commit_after>var URLS = {
    "pub_upload": "/upload_pubkey/",
    "pri_upload": "/upload_prikey/",
    "get_pubkeys": "/get_pubkeys/",
    "get_prikey": "/get_prikey/",
};

function getPubKeysFromServer(usernames) {
    var url = buildUrl(URLS.get_pubkeys);
    var data = {
        "requested_keys": JSON.stringify(usernames),
    };
    var pub_keys;
    $.ajax({
        type: "GET",
        url: url,
        data: data,
        success: function(res) {
            pub_keys = res;
            var cachedUsers = loadLocalStore('cachedUsers');
            for (username in res) {
                var pub_key = res[username];
                if (pub_key.length != 0) {
                    cachedUsers[username] = res[username];
                }
            }
            writeLocalStorage("cachedUsers", cachedUsers);
        },
        failure: function() {
            var e_message = "JS ERROR: getPubKeysFromServer ";
            logErrorToServer(e_message);
            return false;
        },
        async: false
    });
    return pub_keys
}

function refreshLocalStorage(username, password, encrypted_pri_key) {
    writeLocalStorage("userMeta", {
        'username': username,
        'pri_key': recoverPriKey(encrypted_pri_key, password),
        'defaultEncrypt': true,
    });
}

function recoverPriKey(encrypted_pri_key, password) {
    return JSON.parse(decryptAES(encrypted_pri_key, password));
}

function getPriKeyFromServer() {
    var url = buildUrl(URLS.get_prikey);
    var pri_key;
    $.ajax({
        type: "GET",
        url: url,
        success: function(res) {
            pri_key = res.pri_key;
        },
        failure: function() {
            var e_message = "JS ERROR: getPriKeyFromServer ";
            logErrorToServer(e_message);
            return false;
        },
        async: false
    });
    return pri_key
}

function uploadPubKey(username, pub_key) {
    var url = buildUrl(URLS.pub_upload);
    $.ajax({
        type: "POST",
        url: url,
        data: {
            "username": username,
            "pub_key": pub_key
        },
        success: function(res) {
            return true;
        },
        failure: function() {
            var e_message = "JS ERROR: uploadPubKey | " + username;
            logErrorToServer(e_message);
            return false;
        },
        async: false
    });
}

function uploadPriKey(username, pri_key) {
    var url = buildUrl(URLS.pri_upload);
    $.ajax({
        type: "POST",
        url: url,
        data: {
            "username": username,
            "pri_key": pri_key
        },
        success: function(res) {
            return true;
        },
        failure: function() {
            var e_message = "JS ERROR: uploadPriKey | " + username;
            logErrorToServer(e_message);
            return false;
        },
        async: false
    });
}<|endoftext|>


<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.
 *
 */

/*eslint max-statements:0, complexity:0*/

// 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/');
} else {
    if (phantom.version.major === 2) {
        //setting other phantom.args if using phantomjs 2.x
        var system = require('system');
        var argsdeprecated = system.args;
        argsdeprecated.shift();
        phantom.args = argsdeprecated;
    }
}

// Common polyfills
if (typeof Function.prototype.bind !== "function") {
    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind#Compatibility
    Function.prototype.bind = function (oThis) {
        "use strict";
        if (typeof this !== "function") {
            // closest thing possible to the ECMAScript 5 internal IsCallable function
            throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
        }
        var aArgs = Array.prototype.slice.call(arguments, 1),
            fToBind = this,
            fNOP = function() {},
            fBound = function() {
              return fToBind.apply(this instanceof fNOP && oThis ? this : oThis,
                                   aArgs.concat(Array.prototype.slice.call(arguments)));
            };
        fNOP.prototype = this.prototype;
        fBound.prototype = new fNOP();
        return fBound;
    };
}

// 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, system){
    "use strict";
    // phantom args
    var phantomArgs = system.args.slice(1);

    if ("slimer" in global) {
        phantom.casperEngine = "slimerjs";
    } else {
        phantom.casperEngine = "phantomjs";
    }

    if (phantom.casperLoaded) {
        return;
    }

    function __exit(statusCode){
        setTimeout(function() { phantom.exit(statusCode); }, 0);
    }

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

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

    (function (version) {
        // required version check
        if (phantom.casperEngine === 'phantomjs') {
            if (version.major === 1) {
                if (version.minor < 9) {
                    return __die('CasperJS needs at least PhantomJS v1.9 or later.');
                }
                if (version.minor === 9 && version.patch < 1) {
                    return __die('CasperJS needs at least PhantomJS v1.9.1 or later.');
                }
            } else if (version.major === 2) {
                // No requirements yet known
            } else {
                return __die('CasperJS needs PhantomJS v1.x or v2.x');
            }
        }
    })(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 fromPackageJson(module, dir) {
            var pkgPath, pkgContents, pkg;
            pkgPath = fs.pathJoin(dir, module, 'package.json');
            if (!fs.exists(pkgPath)) {
                return;
            }
            pkgContents = fs.read(pkgPath);
            if (!pkgContents) {
                return;
            }
            try {
                pkg = JSON.parse(pkgContents);
            } catch (e) {
                return;
            }
            if (typeof pkg === "object" && pkg.main) {
                return fs.absolute(fs.pathJoin(dir, module, pkg.main));
            }
        }
        function resolveFile(path, dir) {
            var extensions = ['js', 'coffee', 'json'];
            var basenames = [path, path + '/index'];
            var paths = [];
            var nodejsScript = fromPackageJson(path, dir);
            if (nodejsScript) {
                return nodejsScript;
            }
            basenames.forEach(function(basename) {
                paths.push(fs.absolute(fs.pathJoin(dir, basename)));
                extensions.forEach(function(extension) {
                    paths.push(fs.absolute(fs.pathJoin(dir, [basename, extension].join('.'))));
                });
            });
            for (var i = 0; i < paths.length; i++) {
                if (fs.isFile(paths[i])) {
                    return paths[i];
                }
            }
            return null;
        }
        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) {
            var resolved, prevBaseDir;
            var baseDir = getCurrentScriptRoot();
            do {
                resolved = resolveFile(path, fs.pathJoin(baseDir, 'node_modules'));
                prevBaseDir = baseDir;
                baseDir = fs.absolute(fs.pathJoin(prevBaseDir, '..'));
            } while (!resolved && baseDir !== '/' && baseDir !== prevBaseDir);
            return resolved;
        }
        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) {
        /*eslint complexity:0*/
        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 (phantom.casperScript !== "/dev/stdin" && !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 ("paths" in global.require) {
        // declare a dummy patchRequire function
        global.patchRequire = function(req) {return req;};

        require.paths.push(fs.pathJoin(phantom.casperPath, 'modules'));
        require.paths.push(fs.workingDirectory);
    } else {
        global.__require = require;
        global.patchRequire = patchRequire; // must be called in every casperjs module as of 1.1
        global.require = patchRequire(global.require);
    }

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

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

    if ("paths" in global.require) {
        if ((phantom.casperScriptBaseDir || "").indexOf(fs.workingDirectory) === 0) {
            require.paths.push(phantom.casperScriptBaseDir);
        } else {
            require.paths.push(fs.pathJoin(fs.workingDirectory, phantom.casperScriptBaseDir));
        }
        require.paths.push(fs.pathJoin(require.paths[require.paths.length-1], 'node_modules'));
    }

    // 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, require('system'));
<commit_msg>backport from hexid/phantomjs2-bc to support slimerjs<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.
 *
 */

/*eslint max-statements:0, complexity:0*/

// 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/');
} else {
    if (phantom.version.major === 2) {
        //setting other phantom.args if using phantomjs 2.x
        var system = require('system');
        var argsdeprecated = system.args;
        argsdeprecated.shift();
        phantom.args = argsdeprecated;
    }
}

// Common polyfills
if (typeof Function.prototype.bind !== "function") {
    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind#Compatibility
    Function.prototype.bind = function (oThis) {
        "use strict";
        if (typeof this !== "function") {
            // closest thing possible to the ECMAScript 5 internal IsCallable function
            throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
        }
        var aArgs = Array.prototype.slice.call(arguments, 1),
            fToBind = this,
            fNOP = function() {},
            fBound = function() {
              return fToBind.apply(this instanceof fNOP && oThis ? this : oThis,
                                   aArgs.concat(Array.prototype.slice.call(arguments)));
            };
        fNOP.prototype = this.prototype;
        fBound.prototype = new fNOP();
        return fBound;
    };
}

// 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, system){
    "use strict";
    // phantom args
    var phantomArgs = system.args.slice(1);

    if ("slimer" in global) {
        phantom.casperEngine = "slimerjs";
    } else {
        phantom.casperEngine = "phantomjs";
    }

    if (phantom.casperLoaded) {
        return;
    }

    function __exit(statusCode){
        setTimeout(function() { phantom.exit(statusCode); }, 0);
    }

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

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

    (function (version) {
        // required version check
        if (phantom.casperEngine === 'phantomjs') {
            if (version.major === 1) {
                if (version.minor < 9) {
                    return __die('CasperJS needs at least PhantomJS v1.9 or later.');
                }
                if (version.minor === 9 && version.patch < 1) {
                    return __die('CasperJS needs at least PhantomJS v1.9.1 or later.');
                }
            } else if (version.major === 2) {
                // No requirements yet known
            } else {
                return __die('CasperJS needs PhantomJS v1.9.x or v2.x');
            }
        }
    })(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 fromPackageJson(module, dir) {
            var pkgPath, pkgContents, pkg;
            pkgPath = fs.pathJoin(dir, module, 'package.json');
            if (!fs.exists(pkgPath)) {
                return;
            }
            pkgContents = fs.read(pkgPath);
            if (!pkgContents) {
                return;
            }
            try {
                pkg = JSON.parse(pkgContents);
            } catch (e) {
                return;
            }
            if (typeof pkg === "object" && pkg.main) {
                return fs.absolute(fs.pathJoin(dir, module, pkg.main));
            }
        }
        function resolveFile(path, dir) {
            var extensions = ['js', 'coffee', 'json'];
            var basenames = [path, path + '/index'];
            var paths = [];
            var nodejsScript = fromPackageJson(path, dir);
            if (nodejsScript) {
                return nodejsScript;
            }
            basenames.forEach(function(basename) {
                paths.push(fs.absolute(fs.pathJoin(dir, basename)));
                extensions.forEach(function(extension) {
                    paths.push(fs.absolute(fs.pathJoin(dir, [basename, extension].join('.'))));
                });
            });
            for (var i = 0; i < paths.length; i++) {
                if (fs.isFile(paths[i])) {
                    return paths[i];
                }
            }
            return null;
        }
        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) {
            var resolved, prevBaseDir;
            var baseDir = getCurrentScriptRoot();
            do {
                resolved = resolveFile(path, fs.pathJoin(baseDir, 'node_modules'));
                prevBaseDir = baseDir;
                baseDir = fs.absolute(fs.pathJoin(prevBaseDir, '..'));
            } while (!resolved && baseDir !== '/' && baseDir !== prevBaseDir);
            return resolved;
        }
        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) {
        /*eslint complexity:0*/
        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 (phantom.casperScript !== "/dev/stdin" && !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 ("paths" in global.require) {
        // declare a dummy patchRequire function
        global.patchRequire = function(req) {return req;};
        if (phantom.casperEngine === 'slimerjs') {
            require.globals.patchRequire = global.patchRequire;
            require.globals.CasperError = CasperError;
        }
        require.paths.push(fs.pathJoin(phantom.casperPath, 'modules'));
        require.paths.push(fs.workingDirectory);
    } else {
        global.__require = require;
        global.patchRequire = patchRequire; // must be called in every casperjs module as of 1.1
        global.require = patchRequire(global.require);
    }

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

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

    if ("paths" in global.require) {
        if ((phantom.casperScriptBaseDir || "").indexOf(fs.workingDirectory) === 0) {
            require.paths.push(phantom.casperScriptBaseDir);
        } else {
            require.paths.push(fs.pathJoin(fs.workingDirectory, phantom.casperScriptBaseDir));
        }
        require.paths.push(fs.pathJoin(require.paths[require.paths.length-1], 'node_modules'));
    }

    // 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, require('system'));
<|endoftext|>


<commit_before>const path = require('path')

const webpack = require('webpack')

/*
 * webpack accepts configuration files written in multiple programming and data
 * languages.  Besides JavaScript, TypeScript is supported, among others.  See
 * https://webpack.js.org/configuration/configuration-languages/ for details.
 * This template, however, will demonstrate a webpack configuration using Node
 * compatible JavaScript.
 */

/*
 * The name of the configuration. May be used when multiple configurations are
 * defined. For example, if you pass a name to the `--config-name` CLI option,
 * Webpack will only build that specific configuration.
 */
const name = undefined

/*
 *
 */
const mode = undefined // Defaults to 'production'.
// const mode = 'development'
// const mode = 'production'
// const mode = 'none'

/*
 * The base directory, an absolute path, for resolving entry points and loaders
 * from configuration.  By default the current directory is used, but it's
 * recommended to pass a value in your configuration.  This makes your
 * configuration independent from CWD (current working directory).
 *
 * @param {string=} - context
 */
const context = path.resolve(__dirname, 'src')

/*
 * The point or points to enter the application.  If an array is passed, all
 * items will be executed.
 *
 * A dynamically loaded module is not an entry point.
 *
 * Simple rule: one entry point per HTML page.  SPA: one entry point.  MPA:
 * multiple entry points.
 *
 * @param {string} entry - The single entry point.  It will have a chunk name of
 *   `'main'`.
 * @param {string[]} entry - TODO: Determine what happens in this case.
 * @param {!Object<string|string[]>} entry - An object defining multiple
 *   entrypoints.  Each key is the name of each chunk.
 * @param {function (): string|string[]|!Object<string|string[]>} entry - A
 *   function to be called to return the entrypoint(s).  Necessary when the value
 *   is dynamic.
 */
const entry = './index.js'
const entry = {
  main: './index.js',
  chunk1: './chunk1-entry.js',
  chunk2: ['./polyfill.js', './chunk2-entry.js'],
}

/*
 * This option provides a way of excluding dependencies from the output bundles.
 * Instead, the created bundle relies on that dependency to be present in the
 * consumer's environment.
 *
 * That is, this prevents bundling of certain imported packages.
 *
 * See https://webpack.js.org/configuration/externals/ for details.
 *
 * @param {!Object<string|!Array|!Object>|!RegExp|function (context, request, callback)}
 *   - The key should match the name of the imported module.
 */
const externals = undefined
// const externals = {
//   bootstrap: '$',
//   jquery: 'jQuery',
//   react: 'React',
//   'react-dom': 'ReactDOM',
// }

/*
 * These options determine how the different types of modules within a project will
 * be treated.
 *
 * @param {?Object=}
 */
const loaders = {
  /*
   * Prevent webpack from parsing any files matching the given regular
   * expression(s).
   *
   * @param {!RegExp=|!RegExp[]=|function (string): boolean=}
   */
  noParse: /jquery|lodash/,

  /*
   * An array of rules which are matched to requests when modules are created.
   * These rules can modify how the module is created.  They can apply loaders to
   * the module, or modify the parser.
   *
   * A Rule can be separated into three parts — Conditions, Results, and nested
   * Rules.
   *
   * **Rule Conditions**
   *
   * There are two input values for the conditions:
   *
   * 1. The resource: An absolute path to the file requested.  It's already resolved
   *    according to the resolve rules.
   *
   * 2. The issuer: An absolute path to the file of the module which requested the
   *    resource.  It's the location of the import.
   *
   * In a rule, the properties `test`, `include`, `exclude`, and `resource` are
   * matched with the resource and the property `issuer` is matched with the
   * issuer.
   *
   * When using multiple conditions, all conditions must match.
   *
   * **Rule Results**
   *
   * Rule results are used only when the rule condition matches.
   *
   * There are two output values of a Rule:
   *
   * 1. Applied loaders: An array of loaders applied to the resource.
   *
   * 2. Parser options: An options object which should be used to create the parser
   *    for this module.
   *
   * These properties affect the loaders: `loader`, `options`, `use`.
   *
   * The `enforce` property affects the loader category.  Whether it's a normal,
   * pre- or post- loader.
   *
   * The `parser` property affects the parser options.
   *
   * @param {Rule[]}
   */
  rules: [
    {
      // Conditions:

      /*
       * A Condition in the following documentation is shorthand for the following
       * parameter declarations.
       *
       * @param {string}
       * @param {!RegExp}
       * @param {function (input: string): boolean}
       * @param {Condition[]}
       * @param {{ test: Condition, included: Condition, excluded: Condition, and: Condition, or: Condition, not: Condition}}
       */

      /*
       * Shorthand for `resource.test`.
       *
       * @param {Condition}
       */
      test: undefined,
      /*
       * Shorthand for `resource.include`.
       *
       * @param {Condition}
       */
      include: undefined,
      /*
       * Shorthand for `resource.exclude`.
       *
       * @param {Condition}
       */
      exclude: undefined,

      /*
       * @param {Condition}
       */
      resource: {
        test: undefined,
        include: undefined,
        exclude: undefined,
        and: [],
        or: [],
        not: [],
      },
      /*
       * @param {Condition}
       */
      resourceQuery: undefined,

      /*
       * @param {Condition}
       */
      issuer: undefined,

      // Results:

      /*
       * @param {'pre'|'post'}
       */
      enforce: undefined,

      /*
       * @param {string}
       */
      loader: undefined,
      options: {},

      use: [
        {
          /*
           * @param {string}
           */
          loader: 'foo-loader',
          /*
           * @param {Object=}
           */
          options: {},
        }
      ],

      /*
       * @param {Object=}
       */
      parser: undefined,

      // Nested Rules:

      /*
       * @param {Rule[]=}
       */
      oneOf: undefined,

      /*
       * @param {Rule[]=}
       */
      rules: undefined,
    },
    {
      test: /\.css$/,
      use: [
        'style-loader',
        'css-loader',
      ]
    },
    {
      test: /\.(gif|jpg|jpeg|png|svg|webp)$/,
      use: [
        {
          loader: 'url-loader',
          options: {
            limit: 4096
          },
        }
      ],
    },
    {
      test: /\.(eot|otf|ttf|woff|woff2)$/,
      use: [
        'file-loader',
      ],
    },
    // ...
  ],
}

/*
 * The set of options instructing webpack on how and where it should output your
 * bundles, assets, and anything else you bundle or load with webpack.
 *
 * The following only shows the most commonly used options.  See
 * https://webpack.js.org/configuration/output/ for more details.
 *
 * @param {!Object}
 */
const output = {
  /*
   * The name of each output bundle.  The bundle is written to the directory
   * specified by the `output.path` option.
   *
   * For a single entry point, this may be a static name, that is, does not contain
   * substitutions.
   *
   * However, when creating multiple bundles via more than one entry point, code
   * splitting, or various plugins, you should use one of the following
   * substitutions to give each bundle a unique name.
   *
   * | Substitution | Description |
   * | :----------: | ----------- |
   * | [chunkhash]  | The hash of the chunk content. |
   * | [file]       | The module filename. |
   * | [filebase]   | The module basename. |
   * | [hash]       | The hash of the module identifier. |
   * | [id]         | The module identifier. |
   * | [name]       | The module name. |
   * | [query]      | The module query, i.e., the string following `?` in the filename. |
   *
   * Note this option is called `filename` but you are still allowed to use
   * something like `"js/[name]/bundle.js"` to create a folder structure.
   *
   * Note this option does not affect output files for on-demand-loaded chunks.
   * For these files the `output.chunkFilename` option is used.  It also doesn't
   * affect files created by loaders.  For these files see loader options.
   *
   * Make sure to read the [Caching guide][Caching] for details.  There are more
   * steps involved than just setting this option.
   *
   * The lengths of `[hash]` and `[chunkhash]` can be specified using `[hash:#]`
   * (defaults to 20).  Alternatively, specify `output.hashDigestLength` to
   * configure the length globally.
   *
   * When using the `ExtractTextWebpackPlugin`, use `[contenthash]` to obtain a
   * hash of the extracted file (neither `[hash]` nor `[chunkhash]` work).
   *
   * [Caching]: https://webpack.js.org/guides/caching/
   *
   * @param {string}
   */
  filename: 'app.js',

  /*
   * The output directory as an absolute path.
   *
   * Note that `[hash]` in this parameter will be replaced with an hash of the
   * compilation.
   *
   * @param {string=}
   */
  path: path.resolve(__dirname, 'dist'),

  /*
   * Tells webpack to include comments in bundles with information about the
   * contained modules.  This option defaults to `false`.  In production, it
   * should be `false`.
   *
   * @param {boolean=false}
   */
  pathinfo: false,

  /*
   * This option specifies the public URL of the output directory when referenced
   * in a browser.  A relative URL is resolved relative to the HTML page (or <base>
   * tag).  Server-relative URLs, protocol-relative URLs, or absolute URLs are also
   * possible and sometimes required, i.e., when hosting assets on a CDN.
   *
   * The value of this option is prefixed to every URL created by the runtime or
   * loaders.  Because of this, the value of this option ends with `/` in most
   * cases.
   *
   * The default value is an empty string ''.
   *
   * Simple rule: The URL of your `output.path` from the view of the HTML page.
   *
   * @param {string}
   */
  publicPath: 'https://cdn.example.com/assets/',
}

/*
 * These options allows you to control how webpack notifies you of assets and
 * entrypoints that exceed a specific file limit.
 *
 * @param {?Object=}
 */
const performance = {
  /*
   * This option allows webpack to control what files are used to calculate
   * performance hints.
   *
   * @param {function (assetFilename: string): boolean=}
   */
  assetFilter: function (assetFilename) {
    return !(/\.map$/.test(assetFilename))
  },

  /*
   * Turns hints on/off.  In addition, tells webpack to throw either an error or
   * a warning when hints are found.
   *
   * @param {false|'error'|'warning'} - [hints='warning']
   */
  hints: 'error',

  /*
   * The maximum allowable size, in bytes, of an entrypoint before a hint is
   * issued.
   *
   * @param {number=} - [maxEntrypointSize=250000]
   */
  maxEntrypointSize: 250000,

  /*
   * The maximum allowable size, in bytes, of an emitted asset before a hint is
   * issued.
   *
   * @param {number=} - [maxAssetSize=250000]
   */
  maxAssetSize: 250000,
}

/*
 * The plugins option is used to customize the webpack build process in a variety
 * of ways.
 *
 * See https://webpack.js.org/plugins/ for a list of some available plugins.
 * @param {Array}
 */
const plugins = [
  new webpack.optimize.UglifyJsPlugin({
    // ...
  }),
]

/*
 * These options change how modules are resolved.  webpack provides reasonable
 * defaults, but it is possible to change the resolving in detail.  Needing to
 * change the defaults is not common.  See
 * https://webpack.js.org/configuration/resolve/ for details.
 *
 * @param {!Object=}
 */
const resolve = undefined

/*
 * Because JavaScript can be written for both server and browser, webpack offers
 * multiple deployment targets that you can set in your webpack configuration.
 *
 * See https://webpack.js.org/configuration/target/ for details.
 *
 * @param {string=} [target='web'] - May be one of `'async-node'`,
 *   `'electron-main'`, `'electron-renderer'`, `'node'`, `'node-webkit'`,
 *   `'web'`, or `'webworker'`.
 * @param {function (compiler): string=} target - Set it to a function if none of
 *   the predefined targets from the list above meet your needs.
 */
const target = undefined

const config = {
  context,
  entry,
  externals,
  mode,
  module: loaders,
  name,
  output,
  performance,
  plugins,
  resolve,
  target,
}

module.exports = config

// A Webpack configuration may be a function. See https://webpack.js.org/configuration/configuration-types/#exporting-a-function for details.
/**
 * @param {Object|String|Array<Object|String>} env - The environment options. See
 *   https://webpack.js.org/api/cli/#environment-options and
 *   https://webpack.js.org/guides/environment-variables/ for details.
 * @param {Object} argv - An options map. From what I have read, it sounds like these
     12345678901234567890123456789012345678901234567890123456789012345678901234567890
 *   come from many of the command line options. It is stated in the Webpack
 *   documentation that command-line options override configuration options. So, I am
 *   not sure the benefit of using `argv` is if it will just be overridden. More
 *   investigation is required.
 */
const configFn = (env, argv) {
  const { /* Depends on how the command-line was called. */ } = env;
  const option1 = argv['option1'];
  const optimizeMinimize = argv['optimize-minimize'];

  const config = {
    // A standard Webpack configuration object.
  }
  // Or
  const config = new Promise((resolve, reject) => {
    resolve({
      // A standard Webpack configuration object.
    })
  })
  return config
}

module.exports = configFn

// You can also setup more than one configuration.  All configurations will be
// built.  See https://webpack.js.org/configuration/configuration-types/#exporting-multiple-configurations
// for details.

const config2 = { /* ... */ }

// ...

const configN = { /* ... */ }

module.exports = [
  config,
  config2,
  // ...
  configN,
]
<commit_msg>Remove Line Length Indicator<commit_after>const path = require('path')

const webpack = require('webpack')

/*
 * webpack accepts configuration files written in multiple programming and data
 * languages.  Besides JavaScript, TypeScript is supported, among others.  See
 * https://webpack.js.org/configuration/configuration-languages/ for details.
 * This template, however, will demonstrate a webpack configuration using Node
 * compatible JavaScript.
 */

/*
 * The name of the configuration. May be used when multiple configurations are
 * defined. For example, if you pass a name to the `--config-name` CLI option,
 * Webpack will only build that specific configuration.
 */
const name = undefined

/*
 *
 */
const mode = undefined // Defaults to 'production'.
// const mode = 'development'
// const mode = 'production'
// const mode = 'none'

/*
 * The base directory, an absolute path, for resolving entry points and loaders
 * from configuration.  By default the current directory is used, but it's
 * recommended to pass a value in your configuration.  This makes your
 * configuration independent from CWD (current working directory).
 *
 * @param {string=} - context
 */
const context = path.resolve(__dirname, 'src')

/*
 * The point or points to enter the application.  If an array is passed, all
 * items will be executed.
 *
 * A dynamically loaded module is not an entry point.
 *
 * Simple rule: one entry point per HTML page.  SPA: one entry point.  MPA:
 * multiple entry points.
 *
 * @param {string} entry - The single entry point.  It will have a chunk name of
 *   `'main'`.
 * @param {string[]} entry - TODO: Determine what happens in this case.
 * @param {!Object<string|string[]>} entry - An object defining multiple
 *   entrypoints.  Each key is the name of each chunk.
 * @param {function (): string|string[]|!Object<string|string[]>} entry - A
 *   function to be called to return the entrypoint(s).  Necessary when the value
 *   is dynamic.
 */
const entry = './index.js'
const entry = {
  main: './index.js',
  chunk1: './chunk1-entry.js',
  chunk2: ['./polyfill.js', './chunk2-entry.js'],
}

/*
 * This option provides a way of excluding dependencies from the output bundles.
 * Instead, the created bundle relies on that dependency to be present in the
 * consumer's environment.
 *
 * That is, this prevents bundling of certain imported packages.
 *
 * See https://webpack.js.org/configuration/externals/ for details.
 *
 * @param {!Object<string|!Array|!Object>|!RegExp|function (context, request, callback)}
 *   - The key should match the name of the imported module.
 */
const externals = undefined
// const externals = {
//   bootstrap: '$',
//   jquery: 'jQuery',
//   react: 'React',
//   'react-dom': 'ReactDOM',
// }

/*
 * These options determine how the different types of modules within a project will
 * be treated.
 *
 * @param {?Object=}
 */
const loaders = {
  /*
   * Prevent webpack from parsing any files matching the given regular
   * expression(s).
   *
   * @param {!RegExp=|!RegExp[]=|function (string): boolean=}
   */
  noParse: /jquery|lodash/,

  /*
   * An array of rules which are matched to requests when modules are created.
   * These rules can modify how the module is created.  They can apply loaders to
   * the module, or modify the parser.
   *
   * A Rule can be separated into three parts — Conditions, Results, and nested
   * Rules.
   *
   * **Rule Conditions**
   *
   * There are two input values for the conditions:
   *
   * 1. The resource: An absolute path to the file requested.  It's already resolved
   *    according to the resolve rules.
   *
   * 2. The issuer: An absolute path to the file of the module which requested the
   *    resource.  It's the location of the import.
   *
   * In a rule, the properties `test`, `include`, `exclude`, and `resource` are
   * matched with the resource and the property `issuer` is matched with the
   * issuer.
   *
   * When using multiple conditions, all conditions must match.
   *
   * **Rule Results**
   *
   * Rule results are used only when the rule condition matches.
   *
   * There are two output values of a Rule:
   *
   * 1. Applied loaders: An array of loaders applied to the resource.
   *
   * 2. Parser options: An options object which should be used to create the parser
   *    for this module.
   *
   * These properties affect the loaders: `loader`, `options`, `use`.
   *
   * The `enforce` property affects the loader category.  Whether it's a normal,
   * pre- or post- loader.
   *
   * The `parser` property affects the parser options.
   *
   * @param {Rule[]}
   */
  rules: [
    {
      // Conditions:

      /*
       * A Condition in the following documentation is shorthand for the following
       * parameter declarations.
       *
       * @param {string}
       * @param {!RegExp}
       * @param {function (input: string): boolean}
       * @param {Condition[]}
       * @param {{ test: Condition, included: Condition, excluded: Condition, and: Condition, or: Condition, not: Condition}}
       */

      /*
       * Shorthand for `resource.test`.
       *
       * @param {Condition}
       */
      test: undefined,
      /*
       * Shorthand for `resource.include`.
       *
       * @param {Condition}
       */
      include: undefined,
      /*
       * Shorthand for `resource.exclude`.
       *
       * @param {Condition}
       */
      exclude: undefined,

      /*
       * @param {Condition}
       */
      resource: {
        test: undefined,
        include: undefined,
        exclude: undefined,
        and: [],
        or: [],
        not: [],
      },
      /*
       * @param {Condition}
       */
      resourceQuery: undefined,

      /*
       * @param {Condition}
       */
      issuer: undefined,

      // Results:

      /*
       * @param {'pre'|'post'}
       */
      enforce: undefined,

      /*
       * @param {string}
       */
      loader: undefined,
      options: {},

      use: [
        {
          /*
           * @param {string}
           */
          loader: 'foo-loader',
          /*
           * @param {Object=}
           */
          options: {},
        }
      ],

      /*
       * @param {Object=}
       */
      parser: undefined,

      // Nested Rules:

      /*
       * @param {Rule[]=}
       */
      oneOf: undefined,

      /*
       * @param {Rule[]=}
       */
      rules: undefined,
    },
    {
      test: /\.css$/,
      use: [
        'style-loader',
        'css-loader',
      ]
    },
    {
      test: /\.(gif|jpg|jpeg|png|svg|webp)$/,
      use: [
        {
          loader: 'url-loader',
          options: {
            limit: 4096
          },
        }
      ],
    },
    {
      test: /\.(eot|otf|ttf|woff|woff2)$/,
      use: [
        'file-loader',
      ],
    },
    // ...
  ],
}

/*
 * The set of options instructing webpack on how and where it should output your
 * bundles, assets, and anything else you bundle or load with webpack.
 *
 * The following only shows the most commonly used options.  See
 * https://webpack.js.org/configuration/output/ for more details.
 *
 * @param {!Object}
 */
const output = {
  /*
   * The name of each output bundle.  The bundle is written to the directory
   * specified by the `output.path` option.
   *
   * For a single entry point, this may be a static name, that is, does not contain
   * substitutions.
   *
   * However, when creating multiple bundles via more than one entry point, code
   * splitting, or various plugins, you should use one of the following
   * substitutions to give each bundle a unique name.
   *
   * | Substitution | Description |
   * | :----------: | ----------- |
   * | [chunkhash]  | The hash of the chunk content. |
   * | [file]       | The module filename. |
   * | [filebase]   | The module basename. |
   * | [hash]       | The hash of the module identifier. |
   * | [id]         | The module identifier. |
   * | [name]       | The module name. |
   * | [query]      | The module query, i.e., the string following `?` in the filename. |
   *
   * Note this option is called `filename` but you are still allowed to use
   * something like `"js/[name]/bundle.js"` to create a folder structure.
   *
   * Note this option does not affect output files for on-demand-loaded chunks.
   * For these files the `output.chunkFilename` option is used.  It also doesn't
   * affect files created by loaders.  For these files see loader options.
   *
   * Make sure to read the [Caching guide][Caching] for details.  There are more
   * steps involved than just setting this option.
   *
   * The lengths of `[hash]` and `[chunkhash]` can be specified using `[hash:#]`
   * (defaults to 20).  Alternatively, specify `output.hashDigestLength` to
   * configure the length globally.
   *
   * When using the `ExtractTextWebpackPlugin`, use `[contenthash]` to obtain a
   * hash of the extracted file (neither `[hash]` nor `[chunkhash]` work).
   *
   * [Caching]: https://webpack.js.org/guides/caching/
   *
   * @param {string}
   */
  filename: 'app.js',

  /*
   * The output directory as an absolute path.
   *
   * Note that `[hash]` in this parameter will be replaced with an hash of the
   * compilation.
   *
   * @param {string=}
   */
  path: path.resolve(__dirname, 'dist'),

  /*
   * Tells webpack to include comments in bundles with information about the
   * contained modules.  This option defaults to `false`.  In production, it
   * should be `false`.
   *
   * @param {boolean=false}
   */
  pathinfo: false,

  /*
   * This option specifies the public URL of the output directory when referenced
   * in a browser.  A relative URL is resolved relative to the HTML page (or <base>
   * tag).  Server-relative URLs, protocol-relative URLs, or absolute URLs are also
   * possible and sometimes required, i.e., when hosting assets on a CDN.
   *
   * The value of this option is prefixed to every URL created by the runtime or
   * loaders.  Because of this, the value of this option ends with `/` in most
   * cases.
   *
   * The default value is an empty string ''.
   *
   * Simple rule: The URL of your `output.path` from the view of the HTML page.
   *
   * @param {string}
   */
  publicPath: 'https://cdn.example.com/assets/',
}

/*
 * These options allows you to control how webpack notifies you of assets and
 * entrypoints that exceed a specific file limit.
 *
 * @param {?Object=}
 */
const performance = {
  /*
   * This option allows webpack to control what files are used to calculate
   * performance hints.
   *
   * @param {function (assetFilename: string): boolean=}
   */
  assetFilter: function (assetFilename) {
    return !(/\.map$/.test(assetFilename))
  },

  /*
   * Turns hints on/off.  In addition, tells webpack to throw either an error or
   * a warning when hints are found.
   *
   * @param {false|'error'|'warning'} - [hints='warning']
   */
  hints: 'error',

  /*
   * The maximum allowable size, in bytes, of an entrypoint before a hint is
   * issued.
   *
   * @param {number=} - [maxEntrypointSize=250000]
   */
  maxEntrypointSize: 250000,

  /*
   * The maximum allowable size, in bytes, of an emitted asset before a hint is
   * issued.
   *
   * @param {number=} - [maxAssetSize=250000]
   */
  maxAssetSize: 250000,
}

/*
 * The plugins option is used to customize the webpack build process in a variety
 * of ways.
 *
 * See https://webpack.js.org/plugins/ for a list of some available plugins.
 * @param {Array}
 */
const plugins = [
  new webpack.optimize.UglifyJsPlugin({
    // ...
  }),
]

/*
 * These options change how modules are resolved.  webpack provides reasonable
 * defaults, but it is possible to change the resolving in detail.  Needing to
 * change the defaults is not common.  See
 * https://webpack.js.org/configuration/resolve/ for details.
 *
 * @param {!Object=}
 */
const resolve = undefined

/*
 * Because JavaScript can be written for both server and browser, webpack offers
 * multiple deployment targets that you can set in your webpack configuration.
 *
 * See https://webpack.js.org/configuration/target/ for details.
 *
 * @param {string=} [target='web'] - May be one of `'async-node'`,
 *   `'electron-main'`, `'electron-renderer'`, `'node'`, `'node-webkit'`,
 *   `'web'`, or `'webworker'`.
 * @param {function (compiler): string=} target - Set it to a function if none of
 *   the predefined targets from the list above meet your needs.
 */
const target = undefined

const config = {
  context,
  entry,
  externals,
  mode,
  module: loaders,
  name,
  output,
  performance,
  plugins,
  resolve,
  target,
}

module.exports = config

// A Webpack configuration may be a function. See https://webpack.js.org/configuration/configuration-types/#exporting-a-function for details.
/**
 * @param {Object|String|Array<Object|String>} env - The environment options. See
 *   https://webpack.js.org/api/cli/#environment-options and
 *   https://webpack.js.org/guides/environment-variables/ for details.
 * @param {Object} argv - An options map. From what I have read, it sounds like these
 *   come from many of the command line options. It is stated in the Webpack
 *   documentation that command-line options override configuration options. So, I am
 *   not sure the benefit of using `argv` is if it will just be overridden. More
 *   investigation is required.
 */
const configFn = (env, argv) {
  const { /* Depends on how the command-line was called. */ } = env;
  const option1 = argv['option1'];
  const optimizeMinimize = argv['optimize-minimize'];

  const config = {
    // A standard Webpack configuration object.
  }
  // Or
  const config = new Promise((resolve, reject) => {
    resolve({
      // A standard Webpack configuration object.
    })
  })
  return config
}

module.exports = configFn

// You can also setup more than one configuration.  All configurations will be
// built.  See https://webpack.js.org/configuration/configuration-types/#exporting-multiple-configurations
// for details.

const config2 = { /* ... */ }

// ...

const configN = { /* ... */ }

module.exports = [
  config,
  config2,
  // ...
  configN,
]
<|endoftext|>


<commit_before>/* global $, __filename */

import {
    ACTION_JINGLE_TR_RECEIVED,
    ACTION_JINGLE_TR_SUCCESS,
    createJingleEvent
} from '../../service/statistics/AnalyticsEvents';
import { getLogger } from 'jitsi-meet-logger';
import { $iq, Strophe } from 'strophe.js';

import XMPPEvents from '../../service/xmpp/XMPPEvents';
import GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';
import RandomUtil from '../util/RandomUtil';
import Statistics from '../statistics/statistics';

import JingleSessionPC from './JingleSessionPC';
import ConnectionPlugin from './ConnectionPlugin';

const logger = getLogger(__filename);

// XXX Strophe is build around the idea of chaining function calls so allow long
// function call chains.
/* eslint-disable newline-per-chained-call */

/**
 *
 */
export default class JingleConnectionPlugin extends ConnectionPlugin {
    /**
     * Creates new <tt>JingleConnectionPlugin</tt>
     * @param {XMPP} xmpp
     * @param {EventEmitter} eventEmitter
     * @param {Object} iceConfig an object that holds the iceConfig to be passed
     * to the p2p and the jvb <tt>PeerConnection</tt>.
     */
    constructor(xmpp, eventEmitter, iceConfig) {
        super();
        this.xmpp = xmpp;
        this.eventEmitter = eventEmitter;
        this.sessions = {};
        this.jvbIceConfig = iceConfig.jvb;
        this.p2pIceConfig = iceConfig.p2p;
        this.mediaConstraints = {
            offerToReceiveAudio: true,
            offerToReceiveVideo: true
        };
    }

    /**
     *
     * @param connection
     */
    init(connection) {
        super.init(connection);
        this.connection.addHandler(this.onJingle.bind(this),
            'urn:xmpp:jingle:1', 'iq', 'set', null, null);
    }

    /**
     *
     * @param iq
     */
    onJingle(iq) {
        const sid = $(iq).find('jingle').attr('sid');
        const action = $(iq).find('jingle').attr('action');
        const fromJid = iq.getAttribute('from');

        // send ack first
        const ack = $iq({ type: 'result',
            to: fromJid,
            id: iq.getAttribute('id')
        });

        logger.log(`on jingle ${action} from ${fromJid}`, iq);
        let sess = this.sessions[sid];

        if (action !== 'session-initiate') {
            if (!sess) {
                ack.attrs({ type: 'error' });
                ack.c('error', { type: 'cancel' })
                    .c('item-not-found', {
                        xmlns: 'urn:ietf:params:xml:ns:xmpp-stanzas'
                    })
                    .up()
                    .c('unknown-session', {
                        xmlns: 'urn:xmpp:jingle:errors:1'
                    });
                logger.warn('invalid session id', iq);
                this.connection.send(ack);

                return true;
            }

            // local jid is not checked
            if (fromJid !== sess.remoteJid) {
                logger.warn(
                    'jid mismatch for session id', sid, sess.remoteJid, iq);
                ack.attrs({ type: 'error' });
                ack.c('error', { type: 'cancel' })
                    .c('item-not-found', {
                        xmlns: 'urn:ietf:params:xml:ns:xmpp-stanzas'
                    })
                    .up()
                    .c('unknown-session', {
                        xmlns: 'urn:xmpp:jingle:errors:1'
                    });
                this.connection.send(ack);

                return true;
            }
        } else if (sess !== undefined) {
            // Existing session with same session id. This might be out-of-order
            // if the sess.remoteJid is the same as from.
            ack.attrs({ type: 'error' });
            ack.c('error', { type: 'cancel' })
                .c('service-unavailable', {
                    xmlns: 'urn:ietf:params:xml:ns:xmpp-stanzas'
                })
                .up();
            logger.warn('duplicate session id', sid, iq);
            this.connection.send(ack);

            return true;
        }
        const now = window.performance.now();

        // FIXME that should work most of the time, but we'd have to
        // think how secure it is to assume that user with "focus"
        // nickname is Jicofo.
        const isP2P = Strophe.getResourceFromJid(fromJid) !== 'focus';

        // see http://xmpp.org/extensions/xep-0166.html#concepts-session

        switch (action) {
        case 'session-initiate': {
            logger.log('(TIME) received session-initiate:\t', now);
            const startMuted = $(iq).find('jingle>startmuted');

            if (startMuted && startMuted.length > 0) {
                const audioMuted = startMuted.attr('audio');
                const videoMuted = startMuted.attr('video');

                this.eventEmitter.emit(
                    XMPPEvents.START_MUTED_FROM_FOCUS,
                    audioMuted === 'true',
                    videoMuted === 'true');
            }

            logger.info(
                `Marking session from ${fromJid
                } as ${isP2P ? '' : '*not*'} P2P`);

            const iceConfig = isP2P ? this.p2pIceConfig : this.jvbIceConfig;

            sess
                = new JingleSessionPC(
                    $(iq).find('jingle').attr('sid'),
                    $(iq).attr('to'),
                    fromJid,
                    this.connection,
                    this.mediaConstraints,

                    // Makes a copy in order to prevent exception thrown on RN when either this.p2pIceConfig or
                    // this.jvbIceConfig is modified and there's a PeerConnection instance holding a reference
                    JSON.parse(JSON.stringify(iceConfig)),
                    isP2P,
                    /* initiator */ false);

            this.sessions[sess.sid] = sess;

            this.eventEmitter.emit(XMPPEvents.CALL_INCOMING,
                sess, $(iq).find('>jingle'), now);
            break;
        }
        case 'session-accept': {
            this.eventEmitter.emit(
                XMPPEvents.CALL_ACCEPTED, sess, $(iq).find('>jingle'));
            break;
        }
        case 'content-modify': {
            sess.modifyContents($(iq).find('>jingle'));
            break;
        }
        case 'transport-info': {
            this.eventEmitter.emit(
                XMPPEvents.TRANSPORT_INFO, sess, $(iq).find('>jingle'));
            break;
        }
        case 'session-terminate': {
            logger.log('terminating...', sess.sid);
            let reasonCondition = null;
            let reasonText = null;

            if ($(iq).find('>jingle>reason').length) {
                reasonCondition
                    = $(iq).find('>jingle>reason>:first')[0].tagName;
                reasonText = $(iq).find('>jingle>reason>text').text();
            }
            this.terminate(sess.sid, reasonCondition, reasonText);
            this.eventEmitter.emit(XMPPEvents.CALL_ENDED,
                sess, reasonCondition, reasonText);
            break;
        }
        case 'transport-replace':
            logger.info('(TIME) Start transport replace:\t', now);
            Statistics.sendAnalytics(createJingleEvent(
                ACTION_JINGLE_TR_RECEIVED,
                {
                    p2p: isP2P,
                    value: now
                }));

            sess.replaceTransport($(iq).find('>jingle'), () => {
                const successTime = window.performance.now();

                logger.info('(TIME) Transport replace success:\t', successTime);
                Statistics.sendAnalytics(createJingleEvent(
                    ACTION_JINGLE_TR_SUCCESS,
                    {
                        p2p: isP2P,
                        value: successTime
                    }));
            }, error => {
                GlobalOnErrorHandler.callErrorHandler(error);
                logger.error('Transport replace failed', error);
                sess.sendTransportReject();
            });
            break;
        case 'addsource': // FIXME: proprietary, un-jingleish
        case 'source-add': // FIXME: proprietary
            sess.addRemoteStream($(iq).find('>jingle>content'));
            break;
        case 'removesource': // FIXME: proprietary, un-jingleish
        case 'source-remove': // FIXME: proprietary
            sess.removeRemoteStream($(iq).find('>jingle>content'));
            break;
        default:
            logger.warn('jingle action not implemented', action);
            ack.attrs({ type: 'error' });
            ack.c('error', { type: 'cancel' })
                .c('bad-request',
                    { xmlns: 'urn:ietf:params:xml:ns:xmpp-stanzas' })
                .up();
            break;
        }
        this.connection.send(ack);

        return true;
    }

    /**
     * Creates new <tt>JingleSessionPC</tt> meant to be used in a direct P2P
     * connection, configured as 'initiator'.
     * @param {string} me our JID
     * @param {string} peer remote participant's JID
     * @return {JingleSessionPC}
     */
    newP2PJingleSession(me, peer) {
        const sess
            = new JingleSessionPC(
                RandomUtil.randomHexString(12),
                me,
                peer,
                this.connection,
                this.mediaConstraints,
                this.p2pIceConfig,
                /* P2P */ true,
                /* initiator */ true);

        this.sessions[sess.sid] = sess;

        return sess;
    }

    /**
     *
     * @param sid
     * @param reasonCondition
     * @param reasonText
     */
    terminate(sid, reasonCondition, reasonText) {
        if (this.sessions.hasOwnProperty(sid)) {
            if (this.sessions[sid].state !== 'ended') {
                this.sessions[sid].onTerminated(reasonCondition, reasonText);
            }
            delete this.sessions[sid];
        }
    }

    /**
     *
     */
    getStunAndTurnCredentials() {
        // get stun and turn configuration from server via xep-0215
        // uses time-limited credentials as described in
        // http://tools.ietf.org/html/draft-uberti-behave-turn-rest-00
        //
        // See https://modules.prosody.im/mod_turncredentials.html
        // for a prosody module which implements this.
        //
        // Currently, this doesn't work with updateIce and therefore credentials
        // with a long validity have to be fetched before creating the
        // peerconnection.
        // TODO: implement refresh via updateIce as described in
        //      https://code.google.com/p/webrtc/issues/detail?id=1650
        this.connection.sendIQ(
            $iq({ type: 'get',
                to: this.connection.domain })
                .c('services', { xmlns: 'urn:xmpp:extdisco:1' }),
            res => {
                const iceservers = [];

                $(res).find('>services>service').each((idx, el) => {
                    // eslint-disable-next-line no-param-reassign
                    el = $(el);
                    const dict = {};
                    const type = el.attr('type');

                    switch (type) {
                    case 'stun':
                        dict.urls = `stun:${el.attr('host')}`;
                        if (el.attr('port')) {
                            dict.urls += `:${el.attr('port')}`;
                        }
                        iceservers.push(dict);
                        break;
                    case 'turn':
                    case 'turns': {
                        dict.urls = `${type}:`;
                        const username = el.attr('username');

                        // https://code.google.com/p/webrtc/issues/detail
                        // ?id=1508

                        if (username) {
                            const match
                                = navigator.userAgent.match(
                                    /Chrom(e|ium)\/([0-9]+)\./);

                            if (match && parseInt(match[2], 10) < 28) {
                                dict.urls += `${username}@`;
                            } else {
                                // only works in M28
                                dict.username = username;
                            }
                        }
                        dict.urls += el.attr('host');
                        const port = el.attr('port');

                        if (port) {
                            dict.urls += `:${el.attr('port')}`;
                        }
                        const transport = el.attr('transport');

                        if (transport && transport !== 'udp') {
                            dict.urls += `?transport=${transport}`;
                        }

                        dict.credential = el.attr('password')
                                || dict.credential;
                        iceservers.push(dict);
                        break;
                    }
                    }
                });

                const options = this.xmpp.options;

                // Shuffle ICEServers for loadbalancing
                for (let i = iceservers.length - 1; i > 0; i--) {
                    const j = Math.floor(Math.random() * i);
                    const temp = iceservers[i];

                    iceservers[i] = iceservers[j];
                    iceservers[j] = temp;
                }

                if (options.useStunTurn) {
                    // we want to filter and leave only tcp/turns candidates
                    // which make sense for the jvb connections
                    this.jvbIceConfig.iceServers
                        = iceservers.filter(s => s.urls.startsWith('turns'));
                }

                if (options.p2p && options.p2p.useStunTurn) {
                    this.p2pIceConfig.iceServers = iceservers;
                }

            }, err => {
                logger.warn('getting turn credentials failed', err);
                logger.warn('is mod_turncredentials or similar installed?');
            });

        // implement push?
    }

    /**
     * Returns the data saved in 'updateLog' in a format to be logged.
     */
    getLog() {
        const data = {};

        Object.keys(this.sessions).forEach(sid => {
            const session = this.sessions[sid];
            const pc = session.peerconnection;

            if (pc && pc.updateLog) {
                // FIXME: should probably be a .dump call
                data[`jingle_${sid}`] = {
                    updateLog: pc.updateLog,
                    stats: pc.stats,
                    url: window.location.href
                };
            }
        });

        return data;
    }
}

/* eslint-enable newline-per-chained-call */
<commit_msg>feat: Add an option to allow turn/udp for the videobridge PC. (#1212)<commit_after>/* global $, __filename */

import {
    ACTION_JINGLE_TR_RECEIVED,
    ACTION_JINGLE_TR_SUCCESS,
    createJingleEvent
} from '../../service/statistics/AnalyticsEvents';
import { getLogger } from 'jitsi-meet-logger';
import { $iq, Strophe } from 'strophe.js';

import XMPPEvents from '../../service/xmpp/XMPPEvents';
import GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';
import RandomUtil from '../util/RandomUtil';
import Statistics from '../statistics/statistics';

import JingleSessionPC from './JingleSessionPC';
import ConnectionPlugin from './ConnectionPlugin';

const logger = getLogger(__filename);

// XXX Strophe is build around the idea of chaining function calls so allow long
// function call chains.
/* eslint-disable newline-per-chained-call */

/**
 *
 */
export default class JingleConnectionPlugin extends ConnectionPlugin {
    /**
     * Creates new <tt>JingleConnectionPlugin</tt>
     * @param {XMPP} xmpp
     * @param {EventEmitter} eventEmitter
     * @param {Object} iceConfig an object that holds the iceConfig to be passed
     * to the p2p and the jvb <tt>PeerConnection</tt>.
     */
    constructor(xmpp, eventEmitter, iceConfig) {
        super();
        this.xmpp = xmpp;
        this.eventEmitter = eventEmitter;
        this.sessions = {};
        this.jvbIceConfig = iceConfig.jvb;
        this.p2pIceConfig = iceConfig.p2p;
        this.mediaConstraints = {
            offerToReceiveAudio: true,
            offerToReceiveVideo: true
        };
    }

    /**
     *
     * @param connection
     */
    init(connection) {
        super.init(connection);
        this.connection.addHandler(this.onJingle.bind(this),
            'urn:xmpp:jingle:1', 'iq', 'set', null, null);
    }

    /**
     *
     * @param iq
     */
    onJingle(iq) {
        const sid = $(iq).find('jingle').attr('sid');
        const action = $(iq).find('jingle').attr('action');
        const fromJid = iq.getAttribute('from');

        // send ack first
        const ack = $iq({ type: 'result',
            to: fromJid,
            id: iq.getAttribute('id')
        });

        logger.log(`on jingle ${action} from ${fromJid}`, iq);
        let sess = this.sessions[sid];

        if (action !== 'session-initiate') {
            if (!sess) {
                ack.attrs({ type: 'error' });
                ack.c('error', { type: 'cancel' })
                    .c('item-not-found', {
                        xmlns: 'urn:ietf:params:xml:ns:xmpp-stanzas'
                    })
                    .up()
                    .c('unknown-session', {
                        xmlns: 'urn:xmpp:jingle:errors:1'
                    });
                logger.warn('invalid session id', iq);
                this.connection.send(ack);

                return true;
            }

            // local jid is not checked
            if (fromJid !== sess.remoteJid) {
                logger.warn(
                    'jid mismatch for session id', sid, sess.remoteJid, iq);
                ack.attrs({ type: 'error' });
                ack.c('error', { type: 'cancel' })
                    .c('item-not-found', {
                        xmlns: 'urn:ietf:params:xml:ns:xmpp-stanzas'
                    })
                    .up()
                    .c('unknown-session', {
                        xmlns: 'urn:xmpp:jingle:errors:1'
                    });
                this.connection.send(ack);

                return true;
            }
        } else if (sess !== undefined) {
            // Existing session with same session id. This might be out-of-order
            // if the sess.remoteJid is the same as from.
            ack.attrs({ type: 'error' });
            ack.c('error', { type: 'cancel' })
                .c('service-unavailable', {
                    xmlns: 'urn:ietf:params:xml:ns:xmpp-stanzas'
                })
                .up();
            logger.warn('duplicate session id', sid, iq);
            this.connection.send(ack);

            return true;
        }
        const now = window.performance.now();

        // FIXME that should work most of the time, but we'd have to
        // think how secure it is to assume that user with "focus"
        // nickname is Jicofo.
        const isP2P = Strophe.getResourceFromJid(fromJid) !== 'focus';

        // see http://xmpp.org/extensions/xep-0166.html#concepts-session

        switch (action) {
        case 'session-initiate': {
            logger.log('(TIME) received session-initiate:\t', now);
            const startMuted = $(iq).find('jingle>startmuted');

            if (startMuted && startMuted.length > 0) {
                const audioMuted = startMuted.attr('audio');
                const videoMuted = startMuted.attr('video');

                this.eventEmitter.emit(
                    XMPPEvents.START_MUTED_FROM_FOCUS,
                    audioMuted === 'true',
                    videoMuted === 'true');
            }

            logger.info(
                `Marking session from ${fromJid
                } as ${isP2P ? '' : '*not*'} P2P`);

            const iceConfig = isP2P ? this.p2pIceConfig : this.jvbIceConfig;

            sess
                = new JingleSessionPC(
                    $(iq).find('jingle').attr('sid'),
                    $(iq).attr('to'),
                    fromJid,
                    this.connection,
                    this.mediaConstraints,

                    // Makes a copy in order to prevent exception thrown on RN when either this.p2pIceConfig or
                    // this.jvbIceConfig is modified and there's a PeerConnection instance holding a reference
                    JSON.parse(JSON.stringify(iceConfig)),
                    isP2P,
                    /* initiator */ false);

            this.sessions[sess.sid] = sess;

            this.eventEmitter.emit(XMPPEvents.CALL_INCOMING,
                sess, $(iq).find('>jingle'), now);
            break;
        }
        case 'session-accept': {
            this.eventEmitter.emit(
                XMPPEvents.CALL_ACCEPTED, sess, $(iq).find('>jingle'));
            break;
        }
        case 'content-modify': {
            sess.modifyContents($(iq).find('>jingle'));
            break;
        }
        case 'transport-info': {
            this.eventEmitter.emit(
                XMPPEvents.TRANSPORT_INFO, sess, $(iq).find('>jingle'));
            break;
        }
        case 'session-terminate': {
            logger.log('terminating...', sess.sid);
            let reasonCondition = null;
            let reasonText = null;

            if ($(iq).find('>jingle>reason').length) {
                reasonCondition
                    = $(iq).find('>jingle>reason>:first')[0].tagName;
                reasonText = $(iq).find('>jingle>reason>text').text();
            }
            this.terminate(sess.sid, reasonCondition, reasonText);
            this.eventEmitter.emit(XMPPEvents.CALL_ENDED,
                sess, reasonCondition, reasonText);
            break;
        }
        case 'transport-replace':
            logger.info('(TIME) Start transport replace:\t', now);
            Statistics.sendAnalytics(createJingleEvent(
                ACTION_JINGLE_TR_RECEIVED,
                {
                    p2p: isP2P,
                    value: now
                }));

            sess.replaceTransport($(iq).find('>jingle'), () => {
                const successTime = window.performance.now();

                logger.info('(TIME) Transport replace success:\t', successTime);
                Statistics.sendAnalytics(createJingleEvent(
                    ACTION_JINGLE_TR_SUCCESS,
                    {
                        p2p: isP2P,
                        value: successTime
                    }));
            }, error => {
                GlobalOnErrorHandler.callErrorHandler(error);
                logger.error('Transport replace failed', error);
                sess.sendTransportReject();
            });
            break;
        case 'addsource': // FIXME: proprietary, un-jingleish
        case 'source-add': // FIXME: proprietary
            sess.addRemoteStream($(iq).find('>jingle>content'));
            break;
        case 'removesource': // FIXME: proprietary, un-jingleish
        case 'source-remove': // FIXME: proprietary
            sess.removeRemoteStream($(iq).find('>jingle>content'));
            break;
        default:
            logger.warn('jingle action not implemented', action);
            ack.attrs({ type: 'error' });
            ack.c('error', { type: 'cancel' })
                .c('bad-request',
                    { xmlns: 'urn:ietf:params:xml:ns:xmpp-stanzas' })
                .up();
            break;
        }
        this.connection.send(ack);

        return true;
    }

    /**
     * Creates new <tt>JingleSessionPC</tt> meant to be used in a direct P2P
     * connection, configured as 'initiator'.
     * @param {string} me our JID
     * @param {string} peer remote participant's JID
     * @return {JingleSessionPC}
     */
    newP2PJingleSession(me, peer) {
        const sess
            = new JingleSessionPC(
                RandomUtil.randomHexString(12),
                me,
                peer,
                this.connection,
                this.mediaConstraints,
                this.p2pIceConfig,
                /* P2P */ true,
                /* initiator */ true);

        this.sessions[sess.sid] = sess;

        return sess;
    }

    /**
     *
     * @param sid
     * @param reasonCondition
     * @param reasonText
     */
    terminate(sid, reasonCondition, reasonText) {
        if (this.sessions.hasOwnProperty(sid)) {
            if (this.sessions[sid].state !== 'ended') {
                this.sessions[sid].onTerminated(reasonCondition, reasonText);
            }
            delete this.sessions[sid];
        }
    }

    /**
     *
     */
    getStunAndTurnCredentials() {
        // get stun and turn configuration from server via xep-0215
        // uses time-limited credentials as described in
        // http://tools.ietf.org/html/draft-uberti-behave-turn-rest-00
        //
        // See https://modules.prosody.im/mod_turncredentials.html
        // for a prosody module which implements this.
        //
        // Currently, this doesn't work with updateIce and therefore credentials
        // with a long validity have to be fetched before creating the
        // peerconnection.
        // TODO: implement refresh via updateIce as described in
        //      https://code.google.com/p/webrtc/issues/detail?id=1650
        this.connection.sendIQ(
            $iq({ type: 'get',
                to: this.connection.domain })
                .c('services', { xmlns: 'urn:xmpp:extdisco:1' }),
            res => {
                const iceservers = [];

                $(res).find('>services>service').each((idx, el) => {
                    // eslint-disable-next-line no-param-reassign
                    el = $(el);
                    const dict = {};
                    const type = el.attr('type');

                    switch (type) {
                    case 'stun':
                        dict.urls = `stun:${el.attr('host')}`;
                        if (el.attr('port')) {
                            dict.urls += `:${el.attr('port')}`;
                        }
                        iceservers.push(dict);
                        break;
                    case 'turn':
                    case 'turns': {
                        dict.urls = `${type}:`;
                        const username = el.attr('username');

                        // https://code.google.com/p/webrtc/issues/detail
                        // ?id=1508

                        if (username) {
                            const match
                                = navigator.userAgent.match(
                                    /Chrom(e|ium)\/([0-9]+)\./);

                            if (match && parseInt(match[2], 10) < 28) {
                                dict.urls += `${username}@`;
                            } else {
                                // only works in M28
                                dict.username = username;
                            }
                        }
                        dict.urls += el.attr('host');
                        const port = el.attr('port');

                        if (port) {
                            dict.urls += `:${el.attr('port')}`;
                        }
                        const transport = el.attr('transport');

                        if (transport && transport !== 'udp') {
                            dict.urls += `?transport=${transport}`;
                        }

                        dict.credential = el.attr('password')
                                || dict.credential;
                        iceservers.push(dict);
                        break;
                    }
                    }
                });

                const options = this.xmpp.options;

                // Shuffle ICEServers for loadbalancing
                for (let i = iceservers.length - 1; i > 0; i--) {
                    const j = Math.floor(Math.random() * i);
                    const temp = iceservers[i];

                    iceservers[i] = iceservers[j];
                    iceservers[j] = temp;
                }

                if (options.useStunTurn) {
                    let filter;

                    if (options.useTurnUdp) {
                        filter = s => s.urls.startsWith('turn');
                    } else {
                        // By default we filter out STUN and TURN/UDP and leave only TURN/TCP.
                        filter = s => s.startsWith('turn') && s.urls.indexOf('transport=tcp');
                    }

                    this.jvbIceConfig.iceServers = iceservers.filter(filter);
                }

                if (options.p2p && options.p2p.useStunTurn) {
                    this.p2pIceConfig.iceServers = iceservers;
                }

            }, err => {
                logger.warn('getting turn credentials failed', err);
                logger.warn('is mod_turncredentials or similar installed?');
            });

        // implement push?
    }

    /**
     * Returns the data saved in 'updateLog' in a format to be logged.
     */
    getLog() {
        const data = {};

        Object.keys(this.sessions).forEach(sid => {
            const session = this.sessions[sid];
            const pc = session.peerconnection;

            if (pc && pc.updateLog) {
                // FIXME: should probably be a .dump call
                data[`jingle_${sid}`] = {
                    updateLog: pc.updateLog,
                    stats: pc.stats,
                    url: window.location.href
                };
            }
        });

        return data;
    }
}

/* eslint-enable newline-per-chained-call */
<|endoftext|>