UNPKG

chrome-stub

Version:

Easily stub out chrome API calls for great test coverage

1,775 lines (1,474 loc) 174 kB
!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.chrome=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){ var Chrome = _dereq_('./lib/Chrome'); module.exports = new Chrome(); },{"./lib/Chrome":2}],2:[function(_dereq_,module,exports){ var ContextMenus = _dereq_('./chrome/ContextMenus'); var Runtime = _dereq_('./chrome/Runtime'); var Tabs = _dereq_('./chrome/Tabs'); var I18n = _dereq_('./chrome/I18n'); module.exports = Chrome; /** * @constructor */ function Chrome() { this.resetMock(); } /** * Re-initialize all mocks */ Chrome.prototype.resetMock = function () { this.contextMenus = new ContextMenus(this); this.runtime = new Runtime(this); this.tabs = new Tabs(this); this.i18n = new I18n(this); }; },{"./chrome/ContextMenus":4,"./chrome/I18n":5,"./chrome/Runtime":6,"./chrome/Tabs":7}],3:[function(_dereq_,module,exports){ var sinon = _dereq_('sinon'); module.exports = Event; function Event() { this.listeners = []; this.addListener = sinon.spy(this.addListener.bind(this)); } /** * @property {Array} */ Event.prototype.listeners = null; /** * Add an event handler function * @param {function} fn */ Event.prototype.addListener = function (fn) { this.listeners.push(fn); }; /** * Trigger event * @param {...Object} arg */ Event.prototype.trigger = function () { var args = arguments; this.listeners.forEach(function (fn) { fn.apply(null, args); }); }; },{"sinon":13}],4:[function(_dereq_,module,exports){ var Event = _dereq_('../Event'); var sinon = _dereq_('sinon'); module.exports = ContextMenus; /** * Use the <code>chrome.contextMenus</code> API to add items to Google Chrome's context * menu. You can choose what types of objects your context menu additions apply to, * such as images, hyperlinks, and pages. * @constructor * @param {Object} chrome */ function ContextMenus(chrome) { this.chrome = chrome; /** * The maximum number of top level extension items that can be added to an extension * action context menu. Any items beyond this limit will be ignored. * @property {undefined} */ this.ACTION_MENU_TOP_LEVEL_LIMIT = null; /** * Creates a new context menu item. Note that if an error occurs during creation, you * may not find out until the creation callback fires (the details will be in chro * me.runtime.lastError). * * @param {Object} createProperties * @param {function} callback Called when the item has been created in the browser. If there were any probl... * @returns {undefined} The ID of the newly created item. */ this.create = sinon.spy(function (createProperties, callback) { if (typeof callback === 'function') { callback(); } }); /** * Updates a previously created context menu item. * * @param {undefined} id The ID of the item to update. * @param {Object} updateProperties The properties to update. Accepts the same values as the create function. * @param {function} callback Called when the context menu has been updated. */ this.update = sinon.spy(function (id, updateProperties, callback) { if (typeof callback === 'function') { callback(); } }); /** * Removes a context menu item. * * @param {undefined} menuItemId The ID of the context menu item to remove. * @param {function} callback Called when the context menu has been removed. */ this.remove = sinon.spy(function (menuItemId, callback) { if (typeof callback === 'function') { callback(); } }); /** * Removes all context menu items added by this extension. * * @param {function} callback Called when removal is complete. */ this.removeAll = sinon.spy(function (callback) { if (typeof callback === 'function') { callback(); } }); /** * onClicked Event */ this.onClicked = new Event(); } },{"../Event":3,"sinon":13}],5:[function(_dereq_,module,exports){ (function (process){ var Event = _dereq_('../Event'); var sinon = _dereq_('sinon'); var path = _dereq_('path'); /* CONSTANTS */ // To use this library create a messages.json inside of your src folder // by default you can see that I am using src/_locales/en/messages.json // if you _locales directory is in a different root directory simply change below var DEFAULT_FILENAME = path.join(process.cwd(), 'src', '_locales', 'en', 'messages.json'); //change this to your locale var LOCALE = 'en_US'; module.exports = i18n; /** * Use the <code>chrome.i8n</code> to implement internationalization across your whole app or extension. * @constructor * @param {Object} chrome */ function i18n (chrome) { this.chrome = chrome; //loads a file from your this._locales = null; // todo implement this method // /** // * Gets the accept-languages of the browser. This is different from the locale // * used by the browser; to get the locale, use // * // * @param {function} callback - Array of the accept languages of the browser, such as en-US,en,zh-CN // */ // this.getAcceptLanguages = sinon.spy(function (callback){ // // if (typeof callback === 'function') { // callback(); // } // // }); /** * Load default locales */ this.loadDefaults = function () { this._locales = _dereq_(DEFAULT_FILENAME); }; /** * Gets the localized string for the specified message. If the message is missing, * this method returns an empty string (''). If the format of the getMessage() call * is wrong — for example, messageName is not a string or the substitutions array * has more than 9 elements — this method returns undefined. * * @param {number} messageName - The name of the message, as specified in the messages.json file. * @param {Object} substitutions - Up to 9 substitution strings, if the message requires any. * @returns {string} */ this.getMessage = sinon.spy(function (messageName) { //todo implement real '@@ui_locale' method - for now hard code to onstant if(messageName === '@@ui_locale'){ return LOCALE; } var message = this._locales[messageName].message; return message; }); // todo implement this method // /** // * Gets the browser UI language of the browser. This is different // * from i18n.getAcceptLanguages which returns the preferred user languages. // * // * @param {number} tabId // * @param {Object} connectInfo // * @returns {undefined} A port that can be used to communicate with the content scripts running in th... // */ // this.getUILanguage = sinon.spy(function (tabId, connectInfo){ // // }); } }).call(this,_dereq_("JkpR2F")) },{"../Event":3,"JkpR2F":10,"path":9,"sinon":13}],6:[function(_dereq_,module,exports){ var Event = _dereq_('../Event'); var sinon = _dereq_('sinon'); module.exports = Runtime; /** * Use the <code>chrome.runtime</code> API to retrieve the background page, return details * about the manifest, and listen for and respond to events in the app or extension * lifecycle. You can also use this API to convert the relative path of URLs to fully-qualified * URLs. * @constructor * @param {Object} chrome */ function Runtime(chrome) { this.chrome = chrome; /** * This will be defined during an API method callback if there was an error * @property {Object} */ this.lastError = null; /** * The ID of the extension/app. * @property {string} */ this.id = 'xxxxxxx'; /** * Retrieves the JavaScript 'window' object for the background page running inside the * current extension/app. If the background page is an event page, the system will ensure * it is loaded before calling the callback. If there is no background page, an error * is set. * * @param {function} callback */ this.getBackgroundPage = sinon.spy(function (callback) { if (typeof callback === 'function') { callback(); } }); /** * Returns details about the app or extension from the manifest. The object returned * is a serialization of the full <a href="manifest.html">manifest file</a>. * * @returns {Object} The manifest details. */ this.getManifest = sinon.spy(function () { }); /** * Converts a relative path within an app/extension install directory to a fully-qualified * URL. * * @param {string} path A path to a resource within an app/extension expressed relative to its instal... * @returns {string} The fully-qualified URL to the resource. */ this.getURL = sinon.spy(function () { }); /** * Sets the URL to be visited upon uninstallation. This may be used to clean up server-side * data, do analytics, and implement surveys. Maximum 255 characters. * * @param {string} url */ this.setUninstallURL = sinon.spy(function () { }); /** * Reloads the app or extension. * */ this.reload = sinon.spy(function () { }); /** * Requests an update check for this app/extension. * * @param {function} callback */ this.requestUpdateCheck = sinon.spy(function (callback) { if (typeof callback === 'function') { callback(); } }); /** * Restart the ChromeOS device when the app runs in kiosk mode. Otherwise, it's no * -op. * */ this.restart = sinon.spy(function () { }); /** * Attempts to connect to connect listeners within an extension/app (such as the background * page), or other extensions/apps. This is useful for content scripts connecting to * their extension processes, inter-app/extension communication, and <a href="manifest/externally_connectable.html">web * messaging</a>. Note that this does not connect to any listeners in a content script. * Extensions may connect to content scripts embedded in tabs via <a href="extensi * ons/tabs#method-connect">tabs.connect</a>. * * @param {string} extensionId The ID of the extension or app to connect to. If omitted, a connection will b... * @param {Object} connectInfo * @returns {undefined} Port through which messages can be sent and received. The port's $(ref:runtim... */ this.connect = sinon.spy(function () { }); /** * Connects to a native application in the host machine. * * @param {string} application The name of the registered application to connect to. * @returns {undefined} Port through which messages can be sent and received with the application */ this.connectNative = sinon.spy(function (application) { }); /** * Sends a single message to event listeners within your extension/app or a different * extension/app. Similar to $(ref:runtime.connect) but only sends a single message, * with an optional response. If sending to your extension, the $(ref:runtime.onMessage) * event will be fired in each page, or $(ref:runtime.onMessageExternal), if a different * extension. Note that extensions cannot send messages to content scripts using this * method. To send messages to content scripts, use <a href="extensions/tabs#metho * d-sendMessage">tabs.sendMessage</a>. * * @param {string} extensionId The ID of the extension/app to send the message to. If omitted, the message w... * @param {any} message * @param {Object} options * @param {function} responseCallback */ this.sendMessage = sinon.spy(function (extensionId, message, options, responseCallback) { if (typeof responseCallback === 'function') { responseCallback(); } }); /** * Send a single message to a native application. * * @param {string} application The name of the native messaging host. * @param {Object} message The message that will be passed to the native messaging host. * @param {function} responseCallback */ this.sendNativeMessage = sinon.spy(function (application, message, responseCallback) { if (typeof responseCallback === 'function') { responseCallback(); } }); /** * Returns information about the current platform. * * @param {function} callback Called with results */ this.getPlatformInfo = sinon.spy(function (callback) { if (typeof callback === 'function') { callback(); } }); /** * Returns a DirectoryEntry for the package directory. * * @param {function} callback */ this.getPackageDirectoryEntry = sinon.spy(function (callback) { if (typeof callback === 'function') { callback(); } }); /** * Fired when a profile that has this extension installed first starts up. This event * is not fired when an incognito profile is started, even if this extension is operating * in 'split' incognito mode. */ this.onStartup = new Event(); /** * Fired when the extension is first installed, when the extension is updated to a new * version, and when Chrome is updated to a new version. */ this.onInstalled = new Event(); /** * Sent to the event page just before it is unloaded. This gives the extension opportunity * to do some clean up. Note that since the page is unloading, any asynchronous operations * started while handling this event are not guaranteed to complete. If more activity * for the event page occurs before it gets unloaded the onSuspendCanceled event will * be sent and the page won't be unloaded. */ this.onSuspend = new Event(); /** * Sent after onSuspend to indicate that the app won't be unloaded after all. */ this.onSuspendCanceled = new Event(); /** * Fired when an update is available, but isn't installed immediately because the app * is currently running. If you do nothing, the update will be installed the next time * the background page gets unloaded, if you want it to be installed sooner you can * explicitly call chrome.runtime.reload(). If your extension is using a persistent * background page, the background page of course never gets unloaded, so unless you * call chrome.runtime.reload() manually in response to this event the update will not * get installed until the next time chrome itself restarts. If no handlers are listening * for this event, and your extension has a persistent background page, it behaves as * if chrome.runtime.reload() is called in response to this event. */ this.onUpdateAvailable = new Event(); /** * Fired when a Chrome update is available, but isn't installed immediately because * a browser restart is required. */ this.onBrowserUpdateAvailable = new Event(); /** * Fired when a connection is made from either an extension process or a content s * cript. */ this.onConnect = new Event(); /** * Fired when a connection is made from another extension. */ this.onConnectExternal = new Event(); /** * Fired when a message is sent from either an extension process or a content scri * pt. */ this.onMessage = new Event(); /** * Fired when a message is sent from another extension/app. Cannot be used in a content * script. */ this.onMessageExternal = new Event(); /** * Fired when an app or the device that it runs on needs to be restarted. The app should * close all its windows at its earliest convenient time to let the restart to happen. * If the app does nothing, a restart will be enforced after a 24-hour grace period * has passed. Currently, this event is only fired for Chrome OS kiosk apps. */ this.onRestartRequired = new Event(); } },{"../Event":3,"sinon":13}],7:[function(_dereq_,module,exports){ var Event = _dereq_('../Event'); var sinon = _dereq_('sinon'); module.exports = Tabs; /** * Use the <code>chrome.tabs</code> API to interact with the browser's tab system. You * can use this API to create, modify, and rearrange tabs in the browser. * @constructor * @param {Object} chrome */ function Tabs(chrome) { this.chrome = chrome; /** * Retrieves details about the specified tab. * * @param {number} tabId * @param {function} callback */ this.get = sinon.spy(function (tabId, callback) { if (typeof callback === 'function') { callback(); } }); /** * Gets the tab that this script call is being made from. May be undefined if called * from a non-tab context (for example: a background page or popup view). * * @param {function} callback */ this.getCurrent = sinon.spy(function (callback) { if (typeof callback === 'function') { callback(); } }); /** * Connects to the content script(s) in the specified tab. The $(ref:runtime.onConnect) * event is fired in each content script running in the specified tab for the current * extension. For more details, see <a href='messaging'>Content Script Messaging</ * a>. * * @param {number} tabId * @param {Object} connectInfo * @returns {undefined} A port that can be used to communicate with the content scripts running in th... */ this.connect = sinon.spy(function (tabId) { }); /** * Sends a single request to the content script(s) in the specified tab, with an optional * callback to run when a response is sent back. The $(ref:extension.onRequest) event * is fired in each content script running in the specified tab for the current ex * tension. * * @param {number} tabId * @param {any} request * @param {function} responseCallback */ this.sendRequest = sinon.spy(function (tabId, request, responseCallback) { if (typeof responseCallback === 'function') { responseCallback(); } }); /** * Sends a single message to the content script(s) in the specified tab, with an optional * callback to run when a response is sent back. The $(ref:runtime.onMessage) event * is fired in each content script running in the specified tab for the current ex * tension. * * @param {number} tabId * @param {any} message * @param {function} responseCallback */ this.sendMessage = sinon.spy(function (tabId, message, responseCallback) { if (typeof responseCallback === 'function') { responseCallback(); } }); /** * Gets the tab that is selected in the specified window. * * @param {number} windowId Defaults to the <a href='windows#current-window'>current window</a>. * @param {function} callback */ this.getSelected = sinon.spy(function (windowId, callback) { if (typeof callback === 'function') { callback(); } }); /** * Gets details about all tabs in the specified window. * * @param {number} windowId Defaults to the <a href='windows#current-window'>current window</a>. * @param {function} callback */ this.getAllInWindow = sinon.spy(function (windowId, callback) { if (typeof callback === 'function') { callback(); } }); /** * Creates a new tab. * * @param {Object} createProperties * @param {function} callback */ this.create = sinon.spy(function (createProperties, callback) { if (typeof callback === 'function') { callback(); } }); /** * Duplicates a tab. * * @param {number} tabId The ID of the tab which is to be duplicated. * @param {function} callback */ this.duplicate = sinon.spy(function (tabId, callback) { if (typeof callback === 'function') { callback(); } }); /** * Gets all tabs that have the specified properties, or all tabs if no properties are * specified. * * @param {Object} queryInfo * @param {function} callback */ this.query = sinon.spy(function (queryInfo, callback) { if (typeof callback === 'function') { callback([{id: 0}]); } }); /** * Highlights the given tabs. * * @param {Object} highlightInfo * @param {function} callback */ this.highlight = sinon.spy(function (highlightInfo, callback) { if (typeof callback === 'function') { callback(); } }); /** * Modifies the properties of a tab. Properties that are not specified in <var>updateProperties</var> * are not modified. * * @param {number} tabId Defaults to the selected tab of the <a href='windows#current-window'>current ... * @param {Object} updateProperties * @param {function} callback */ this.update = sinon.spy(function (tabId, updateProperties, callback) { if (typeof callback === 'function') { callback(); } }); /** * Moves one or more tabs to a new position within its window, or to a new window. Note * that tabs can only be moved to and from normal (window.type === "normal") windo * ws. * * @param {undefined} tabIds The tab or list of tabs to move. * @param {Object} moveProperties * @param {function} callback */ this.move = sinon.spy(function (tabIds, moveProperties, callback) { if (typeof callback === 'function') { callback(); } }); /** * Reload a tab. * * @param {number} tabId The ID of the tab to reload; defaults to the selected tab of the current window. * @param {Object} reloadProperties * @param {function} callback */ this.reload = sinon.spy(function (tabId, reloadProperties, callback) { if (typeof callback === 'function') { callback(); } }); /** * Closes one or more tabs. * * @param {undefined} tabIds The tab or list of tabs to close. * @param {function} callback */ this.remove = sinon.spy(function (tabIds, callback) { if (typeof callback === 'function') { callback(); } }); /** * Detects the primary language of the content in a tab. * * @param {number} tabId Defaults to the active tab of the <a href='windows#current-window'>current wi... * @param {function} callback */ this.detectLanguage = sinon.spy(function (tabId, callback) { if (typeof callback === 'function') { callback(); } }); /** * Captures the visible area of the currently active tab in the specified window. You * must have <a href='declare_permissions'>&lt;all_urls&gt;</a> permission to use this * method. * * @param {number} windowId The target window. Defaults to the <a href='windows#current-window'>current w... * @param {undefined} options * @param {function} callback */ this.captureVisibleTab = sinon.spy(function (windowId, options, callback) { if (typeof callback === 'function') { callback(); } }); /** * Injects JavaScript code into a page. For details, see the <a href='content_scripts#pi'>programmatic * injection</a> section of the content scripts doc. * * @param {number} tabId The ID of the tab in which to run the script; defaults to the active tab of t... * @param {undefined} details Details of the script to run. * @param {function} callback Called after all the JavaScript has been executed. */ this.executeScript = sinon.spy(function (tabId, details, callback) { if (typeof callback === 'function') { callback(); } }); /** * Injects CSS into a page. For details, see the <a href='content_scripts#pi'>programmatic * injection</a> section of the content scripts doc. * * @param {number} tabId The ID of the tab in which to insert the CSS; defaults to the active tab of t... * @param {undefined} details Details of the CSS text to insert. * @param {function} callback Called when all the CSS has been inserted. */ this.insertCSS = sinon.spy(function (tabId, details, callback) { if (typeof callback === 'function') { callback(); } }); /** * Zooms a specified tab. * * @param {number} tabId The ID of the tab to zoom; defaults to the active tab of the current window. * @param {number} zoomFactor The new zoom factor. * @param {function} callback Called after the zoom factor has been changed. */ this.setZoom = sinon.spy(function (tabId, zoomFactor, callback) { if (typeof callback === 'function') { callback(); } }); /** * Gets the current zoom factor of a specified tab. * * @param {number} tabId The ID of the tab to get the current zoom factor from; defaults to the active... * @param {function} callback Called with the tab's current zoom factor after it has been fetched. */ this.getZoom = sinon.spy(function (tabId, callback) { if (typeof callback === 'function') { callback(); } }); /** * Sets the zoom settings for a specified tab, which define how zoom changes are handled. * These settings are reset to defaults upon navigating the tab. * * @param {number} tabId The ID of the tab to change the zoom settings for; defaults to the active tab... * @param {undefined} zoomSettings Defines how zoom changes are handled and at what scope. * @param {function} callback Called after the zoom settings have been changed. */ this.setZoomSettings = sinon.spy(function (tabId, zoomSettings, callback) { if (typeof callback === 'function') { callback(); } }); /** * Gets the current zoom settings of a specified tab. * * @param {number} tabId The ID of the tab to get the current zoom settings from; defaults to the acti... * @param {function} callback Called with the tab's current zoom settings. */ this.getZoomSettings = sinon.spy(function (tabId, callback) { if (typeof callback === 'function') { callback(); } }); /** * Fired when a tab is created. Note that the tab's URL may not be set at the time this * event fired, but you can listen to onUpdated events to be notified when a URL is * set. */ this.onCreated = new Event(); /** * Fired when a tab is updated. */ this.onUpdated = new Event(); /** * Fired when a tab is moved within a window. Only one move event is fired, representing * the tab the user directly moved. Move events are not fired for the other tabs that * must move in response. This event is not fired when a tab is moved between windows. * For that, see $(ref:tabs.onDetached). */ this.onMoved = new Event(); /** * Fires when the selected tab in a window changes. */ this.onSelectionChanged = new Event(); /** * Fires when the selected tab in a window changes. Note that the tab's URL may not * be set at the time this event fired, but you can listen to $(ref:tabs.onUpdated) * events to be notified when a URL is set. */ this.onActiveChanged = new Event(); /** * Fires when the active tab in a window changes. Note that the tab's URL may not be * set at the time this event fired, but you can listen to onUpdated events to be notified * when a URL is set. */ this.onActivated = new Event(); /** * Fired when the highlighted or selected tabs in a window changes. */ this.onHighlightChanged = new Event(); /** * Fired when the highlighted or selected tabs in a window changes. */ this.onHighlighted = new Event(); /** * Fired when a tab is detached from a window, for example because it is being moved * between windows. */ this.onDetached = new Event(); /** * Fired when a tab is attached to a window, for example because it was moved between * windows. */ this.onAttached = new Event(); /** * Fired when a tab is closed. */ this.onRemoved = new Event(); /** * Fired when a tab is replaced with another tab due to prerendering or instant. */ this.onReplaced = new Event(); /** * Fired when a tab is zoomed. */ this.onZoomChange = new Event(); } },{"../Event":3,"sinon":13}],8:[function(_dereq_,module,exports){ if (typeof Object.create === 'function') { // implementation from standard node.js 'util' module module.exports = function inherits(ctor, superCtor) { ctor.super_ = superCtor ctor.prototype = Object.create(superCtor.prototype, { constructor: { value: ctor, enumerable: false, writable: true, configurable: true } }); }; } else { // old school shim for old browsers module.exports = function inherits(ctor, superCtor) { ctor.super_ = superCtor var TempCtor = function () {} TempCtor.prototype = superCtor.prototype ctor.prototype = new TempCtor() ctor.prototype.constructor = ctor } } },{}],9:[function(_dereq_,module,exports){ (function (process){ // 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. // resolves . and .. elements in a path array with directory names there // must be no slashes, empty elements, or device names (c:\) in the array // (so also no leading and trailing slashes - it does not distinguish // relative and absolute paths) function normalizeArray(parts, allowAboveRoot) { // if the path tries to go above the root, `up` ends up > 0 var up = 0; for (var i = parts.length - 1; i >= 0; i--) { var last = parts[i]; if (last === '.') { parts.splice(i, 1); } else if (last === '..') { parts.splice(i, 1); up++; } else if (up) { parts.splice(i, 1); up--; } } // if the path is allowed to go above the root, restore leading ..s if (allowAboveRoot) { for (; up--; up) { parts.unshift('..'); } } return parts; } // Split a filename into [root, dir, basename, ext], unix version // 'root' is just a slash, or nothing. var splitPathRe = /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/; var splitPath = function(filename) { return splitPathRe.exec(filename).slice(1); }; // path.resolve([from ...], to) // posix version exports.resolve = function() { var resolvedPath = '', resolvedAbsolute = false; for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) { var path = (i >= 0) ? arguments[i] : process.cwd(); // Skip empty and invalid entries if (typeof path !== 'string') { throw new TypeError('Arguments to path.resolve must be strings'); } else if (!path) { continue; } resolvedPath = path + '/' + resolvedPath; resolvedAbsolute = path.charAt(0) === '/'; } // At this point the path should be resolved to a full absolute path, but // handle relative paths to be safe (might happen when process.cwd() fails) // Normalize the path resolvedPath = normalizeArray(filter(resolvedPath.split('/'), function(p) { return !!p; }), !resolvedAbsolute).join('/'); return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.'; }; // path.normalize(path) // posix version exports.normalize = function(path) { var isAbsolute = exports.isAbsolute(path), trailingSlash = substr(path, -1) === '/'; // Normalize the path path = normalizeArray(filter(path.split('/'), function(p) { return !!p; }), !isAbsolute).join('/'); if (!path && !isAbsolute) { path = '.'; } if (path && trailingSlash) { path += '/'; } return (isAbsolute ? '/' : '') + path; }; // posix version exports.isAbsolute = function(path) { return path.charAt(0) === '/'; }; // posix version exports.join = function() { var paths = Array.prototype.slice.call(arguments, 0); return exports.normalize(filter(paths, function(p, index) { if (typeof p !== 'string') { throw new TypeError('Arguments to path.join must be strings'); } return p; }).join('/')); }; // path.relative(from, to) // posix version exports.relative = function(from, to) { from = exports.resolve(from).substr(1); to = exports.resolve(to).substr(1); function trim(arr) { var start = 0; for (; start < arr.length; start++) { if (arr[start] !== '') break; } var end = arr.length - 1; for (; end >= 0; end--) { if (arr[end] !== '') break; } if (start > end) return []; return arr.slice(start, end - start + 1); } var fromParts = trim(from.split('/')); var toParts = trim(to.split('/')); var length = Math.min(fromParts.length, toParts.length); var samePartsLength = length; for (var i = 0; i < length; i++) { if (fromParts[i] !== toParts[i]) { samePartsLength = i; break; } } var outputParts = []; for (var i = samePartsLength; i < fromParts.length; i++) { outputParts.push('..'); } outputParts = outputParts.concat(toParts.slice(samePartsLength)); return outputParts.join('/'); }; exports.sep = '/'; exports.delimiter = ':'; exports.dirname = function(path) { var result = splitPath(path), root = result[0], dir = result[1]; if (!root && !dir) { // No dirname whatsoever return '.'; } if (dir) { // It has a dirname, strip trailing slash dir = dir.substr(0, dir.length - 1); } return root + dir; }; exports.basename = function(path, ext) { var f = splitPath(path)[2]; // TODO: make this comparison case-insensitive on windows? if (ext && f.substr(-1 * ext.length) === ext) { f = f.substr(0, f.length - ext.length); } return f; }; exports.extname = function(path) { return splitPath(path)[3]; }; function filter (xs, f) { if (xs.filter) return xs.filter(f); var res = []; for (var i = 0; i < xs.length; i++) { if (f(xs[i], i, xs)) res.push(xs[i]); } return res; } // String.prototype.substr - negative index don't work in IE8 var substr = 'ab'.substr(-1) === 'b' ? function (str, start, len) { return str.substr(start, len) } : function (str, start, len) { if (start < 0) start = str.length + start; return str.substr(start, len); } ; }).call(this,_dereq_("JkpR2F")) },{"JkpR2F":10}],10:[function(_dereq_,module,exports){ // shim for using process in browser var process = module.exports = {}; process.nextTick = (function () { var canSetImmediate = typeof window !== 'undefined' && window.setImmediate; var canPost = typeof window !== 'undefined' && window.postMessage && window.addEventListener ; if (canSetImmediate) { return function (f) { return window.setImmediate(f) }; } if (canPost) { var queue = []; window.addEventListener('message', function (ev) { var source = ev.source; if ((source === window || source === null) && ev.data === 'process-tick') { ev.stopPropagation(); if (queue.length > 0) { var fn = queue.shift(); fn(); } } }, true); return function nextTick(fn) { queue.push(fn); window.postMessage('process-tick', '*'); }; } return function nextTick(fn) { setTimeout(fn, 0); }; })(); process.title = 'browser'; process.browser = true; process.env = {}; process.argv = []; function noop() {} process.on = noop; process.addListener = noop; process.once = noop; process.off = noop; process.removeListener = noop; process.removeAllListeners = noop; process.emit = noop; process.binding = function (name) { throw new Error('process.binding is not supported'); } // TODO(shtylman) process.cwd = function () { return '/' }; process.chdir = function (dir) { throw new Error('process.chdir is not supported'); }; },{}],11:[function(_dereq_,module,exports){ module.exports = function isBuffer(arg) { return arg && typeof arg === 'object' && typeof arg.copy === 'function' && typeof arg.fill === 'function' && typeof arg.readUInt8 === 'function'; } },{}],12:[function(_dereq_,module,exports){ (function (process,global){ // 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. var formatRegExp = /%[sdj%]/g; exports.format = function(f) { if (!isString(f)) { var objects = []; for (var i = 0; i < arguments.length; i++) { objects.push(inspect(arguments[i])); } return objects.join(' '); } var i = 1; var args = arguments; var len = args.length; var str = String(f).replace(formatRegExp, function(x) { if (x === '%%') return '%'; if (i >= len) return x; switch (x) { case '%s': return String(args[i++]); case '%d': return Number(args[i++]); case '%j': try { return JSON.stringify(args[i++]); } catch (_) { return '[Circular]'; } default: return x; } }); for (var x = args[i]; i < len; x = args[++i]) { if (isNull(x) || !isObject(x)) { str += ' ' + x; } else { str += ' ' + inspect(x); } } return str; }; // Mark that a method should not be used. // Returns a modified function which warns once by default. // If --no-deprecation is set, then it is a no-op. exports.deprecate = function(fn, msg) { // Allow for deprecating things in the process of starting up. if (isUndefined(global.process)) { return function() { return exports.deprecate(fn, msg).apply(this, arguments); }; } if (process.noDeprecation === true) { return fn; } var warned = false; function deprecated() { if (!warned) { if (process.throwDeprecation) { throw new Error(msg); } else if (process.traceDeprecation) { console.trace(msg); } else { console.error(msg); } warned = true; } return fn.apply(this, arguments); } return deprecated; }; var debugs = {}; var debugEnviron; exports.debuglog = function(set) { if (isUndefined(debugEnviron)) debugEnviron = process.env.NODE_DEBUG || ''; set = set.toUpperCase(); if (!debugs[set]) { if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) { var pid = process.pid; debugs[set] = function() { var msg = exports.format.apply(exports, arguments); console.error('%s %d: %s', set, pid, msg); }; } else { debugs[set] = function() {}; } } return debugs[set]; }; /** * Echos the value of a value. Trys to print the value out * in the best way possible given the different types. * * @param {Object} obj The object to print out. * @param {Object} opts Optional options object that alters the output. */ /* legacy: obj, showHidden, depth, colors*/ function inspect(obj, opts) { // default options var ctx = { seen: [], stylize: stylizeNoColor }; // legacy... if (arguments.length >= 3) ctx.depth = arguments[2]; if (arguments.length >= 4) ctx.colors = arguments[3]; if (isBoolean(opts)) { // legacy... ctx.showHidden = opts; } else if (opts) { // got an "options" object exports._extend(ctx, opts); } // set default options if (isUndefined(ctx.showHidden)) ctx.showHidden = false; if (isUndefined(ctx.depth)) ctx.depth = 2; if (isUndefined(ctx.colors)) ctx.colors = false; if (isUndefined(ctx.customInspect)) ctx.customInspect = true; if (ctx.colors) ctx.stylize = stylizeWithColor; return formatValue(ctx, obj, ctx.depth); } exports.inspect = inspect; // http://en.wikipedia.org/wiki/ANSI_escape_code#graphics inspect.colors = { 'bold' : [1, 22], 'italic' : [3, 23], 'underline' : [4, 24], 'inverse' : [7, 27], 'white' : [37, 39], 'grey' : [90, 39], 'black' : [30, 39], 'blue' : [34, 39], 'cyan' : [36, 39], 'green' : [32, 39], 'magenta' : [35, 39], 'red' : [31, 39], 'yellow' : [33, 39] }; // Don't use 'blue' not visible on cmd.exe inspect.styles = { 'special': 'cyan', 'number': 'yellow', 'boolean': 'yellow', 'undefined': 'grey', 'null': 'bold', 'string': 'green', 'date': 'magenta', // "name": intentionally not styling 'regexp': 'red' }; function stylizeWithColor(str, styleType) { var style = inspect.styles[styleType]; if (style) { return '\u001b[' + inspect.colors[style][0] + 'm' + str + '\u001b[' + inspect.colors[style][1] + 'm'; } else { return str; } } function stylizeNoColor(str, styleType) { return str; } function arrayToHash(array) { var hash = {}; array.forEach(function(val, idx) { hash[val] = true; }); return hash; } function formatValue(ctx, value, recurseTimes) { // Provide a hook for user-specified inspect functions. // Check that value is an object with an inspect function on it if (ctx.customInspect && value && isFunction(value.inspect) && // Filter out the util module, it's inspect function is special value.inspect !== exports.inspect && // Also filter out any prototype objects using the circular check. !(value.constructor && value.constructor.prototype === value)) { var ret = value.inspect(recurseTimes, ctx); if (!isString(ret)) { ret = formatValue(ctx, ret, recurseTimes); } return ret; } // Primitive types cannot have properties var primitive = formatPrimitive(ctx, value); if (primitive) { return primitive; } // Look up the keys of the object. var keys = Object.keys(value); var visibleKeys = arrayToHash(keys); if (ctx.showHidden) { keys = Object.getOwnPropertyNames(value); } // IE doesn't make error fields non-enumerable // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx if (isError(value) && (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) { return formatError(value); } // Some type of object without properties can be shortcutted. if (keys.length === 0) { if (isFunction(value)) { var name = value.name ? ': ' + value.name : ''; return ctx.stylize('[Function' + name + ']', 'special'); } if (isRegExp(value)) { return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); } if (isDate(value)) { return ctx.stylize(Date.prototype.toString.call(value), 'date'); } if (isError(value)) { return formatError(value); } } var base = '', array = false, braces = ['{', '}']; // Make Array say that they are Array if (isArray(value)) { array = true; braces = ['[', ']']; } // Make functions say that they are functions if (isFunction(value)) { var n = value.name ? ': ' + value.name : ''; base = ' [Function' + n + ']'; } // Make RegExps say that they are RegExps if (isRegExp(value)) { base = ' ' + RegExp.prototype.toString.call(value); } // Make dates with properties first say the date if (isDate(value)) { base = ' ' + Date.prototype.toUTCString.call(value); } // Make error with message first say the error if (isError(value)) { base = ' ' + formatError(value); } if (keys.length === 0 && (!array || value.length == 0)) { return braces[0] + base + braces[1]; } if (recurseTimes < 0) { if (isRegExp(value)) { return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); } else { return ctx.stylize('[Object]', 'special'); } } ctx.seen.push(value); var output; if (array) { output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); } else { output = keys.map(function(key) { return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); }); } ctx.seen.pop(); return reduceToSingleString(output, base, braces); } function formatPrimitive(ctx, value) { if (isUndefined(value)) return ctx.stylize('undefined', 'undefined'); if (isString(value)) { var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') .replace(/'/g, "\\'") .replace(/\\"/g, '"') + '\''; return ctx.stylize(simple, 'string'); } if (isNumber(value)) return ctx.stylize('' + value, 'number'); if (isBoolean(value)) return ctx.stylize('' + value, 'boolean'); // For some reason typeof null is "object", so special case here. if (isNull(value)) return ctx.stylize('null', 'null'); } function formatError(value) { return '[' + Error.prototype.toString.call(value) + ']'; } function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { var output = []; for (var i = 0, l = value.length; i < l; ++i) { if (hasOwnProperty(value, String(i))) { output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, String(i), true)); } else { output.push(''); } } keys.forEach(function(key) { if (!key.match(/^\d+$/)) { output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, key, true)); } }); return output; } function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { var name, str, desc; desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] }; if (desc.get) { if (desc.set) { str = ctx.stylize('[Getter/Setter]', 'special'); } else { str = ctx.stylize('[Getter]', 'special'); } } else { if (desc.set) { str = ctx.stylize('[Setter]', 'special'); } } if (!hasOwnProperty(visibleKeys, key)) { name = '[' + key + ']'; } if (!str) { if (ctx.seen.indexOf(desc.value) < 0) { if (isNull(recurseTimes)) { str = formatValue(ctx, desc.value, null); } else { str = formatValue(ctx, desc.value, recurseTimes - 1); } if (str.indexOf('\n') > -1) { if (array) { str = str.split('\n').map(function(line) { return ' ' + line; }).join('\n').substr(2); } else { str = '\n' + str.split('\n').map(function(line) { return ' ' + line; }).join('\n'); } } } else { str = ctx.stylize('[Circular]', 'special'); } } if (isUndefined(name)) { if (array && key.match(/^\d+$/)) { return str; } name = JSON.stringify('' + key); if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { name = name.substr(1, name.length - 2); name = ctx.stylize(name, 'name'); } else { name = name.replace(/'/g, "\\'") .replace(/\\"/g, '"') .replace(/(^"|"$)/g, "'"); name = ctx.stylize(name, 'string'); } } return name + ': ' + str; } function reduceToSingleString(output, base, braces) { var numLinesEst = 0; var length = output.reduce(function(prev, cur) { numLinesEst++; if (cur.indexOf('\n') >= 0) numLinesEst++; return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1; }, 0); if (length > 60) { return braces[0] + (base === '' ? '' : base + '\n ') + ' ' + output.join(',\n ') + ' ' + braces[1]; } return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; } // NOTE: These type checking functions intentionally don't use `instanceof` // because it is fragile and can be easily faked with `Object.create()`. function isArray(ar) { return Array.isArray(ar); } exports.isArray = isArray; function isBoolean(arg) { return typeof arg === 'boolean'; } exports.isBoolean = isBoolean; function isNull(arg) { return arg === null; } exports.isNull = isNull; function isNullOrUndefined(arg) { return arg == null; } exports.isNullOrUndefined = isNullOrUndefined; function isNumber(arg) { return typeof arg === 'number'; } exports.isNumber = isNumber; function isString(arg) { return typeof arg === 'string'; } exports.isString = isString; function isSymbol(arg) { return typeof arg === 'symbol'; } exports.isSymbol = isSymbol; function isUndefined(arg) { ret