/*! * Casper is a navigation utility for PhantomJS. * * Documentation: http://casperjs.org/ * Repository: http://github.com/casperjs/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 && this.process.title === "node") { console.error('CasperJS cannot be executed within a nodejs environment'); this.process.exit(1); } // phantom check if (!('phantom' in this)) { console.error('CasperJS needs to be executed in a PhantomJS environment http://phantomjs.org/'); } // Common polyfills void function() { // cujos bind shim instead of MDN shim, see #1396 var isFunction = function(o) { return 'function' === typeof o; }; var bind; var slice = [].slice; var proto = Function.prototype; var featureMap = { 'function-bind': 'bind' }; function has(feature) { var prop = featureMap[feature]; return isFunction(proto[prop]); } // check for missing features if (!has('function-bind')) { // adapted from Mozilla Developer Network example at // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind bind = function bind(obj) { var args = slice.call(arguments, 1), self = this, nop = function() { }, bound = function() { return self.apply(this instanceof nop ? this : (obj || {}), args.concat(slice.call(arguments))); }; nop.prototype = this.prototype || {}; // Firefox cries sometimes if prototype is undefined bound.prototype = new nop(); return bound; }; proto.bind = bind; } }(); // 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: ') === 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.filter.call(arguments,function(elm){ return typeof elm !== "undefined" && elm !== null; }).join('/'); }; } if (phantom.casperEngine === 'slimerjs' && slimer.version.major === 0 && slimer.version.minor <= 9){ fs.size = function (filename){ var t = fs.read(filename, "rt"); return t.length; }; } return fs; })(require('fs')); // CasperJS root path if (!phantom.casperPath) { try { phantom.casperPath = phantomArgs.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() { /* global slimer */ 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 !== '/' && prevBaseDir !== '/' && 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); // phantomjs2 has paths in require, but needs patchRequire anyway if (!("paths" in global.require) || ('phantomjs' === phantom.casperEngine && 1 < phantom.version.major) ) { global.__require = require; global.patchRequire = patchRequire; // must be called in every casperjs module as of 1.1 global.require = patchRequire(global.require); } else { // declare a dummy patchRequire function global.patchRequire = function(req) {return req;}; require.paths.push(fs.pathJoin(phantom.casperPath, 'modules')); require.paths.push(fs.workingDirectory); } if (phantom.casperEngine === 'slimerjs') { require.globals.patchRequire = global.patchRequire; require.globals.CasperError = CasperError; } // 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)); } (function nodeModulePath(path) { var resolved = false, prevBaseDir; var baseDir = path; do { path = fs.pathJoin(baseDir, 'node_modules'); resolved = fs.exists(path) && fs.isDirectory(path); prevBaseDir = baseDir; baseDir = fs.absolute(fs.pathJoin(prevBaseDir, '..')); } while (!resolved && baseDir !== '/' && prevBaseDir !== '/' && baseDir !== prevBaseDir); if (!resolved) return; require.paths.push(fs.pathJoin(prevBaseDir, 'node_modules')); })(fs.pathJoin(fs.workingDirectory, phantom.casperScriptBaseDir)); } // casper loading status flag phantom.casperLoaded = true; if (phantom.version.major === 2 && phantom.casperScript && phantom.casperScript.split('.').pop() === 'coffee' ) { return __terminate('CoffeeScript is not supported by PhantomJS > 2.'); } // 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'));