chrome-stub
Version:
Easily stub out chrome API calls for great test coverage
1,775 lines (1,474 loc) • 174 kB
JavaScript
!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'><all_urls></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