feature-toggle
Version:
A painless feature toggle system in JavaScript. Decouple development and deployment.
1,844 lines (1,538 loc) • 58.2 kB
JavaScript
!function(e){"object"==typeof exports?module.exports=e():"function"==typeof define&&define.amd?define(e):"undefined"!=typeof window?window.setFeatures=e():"undefined"!=typeof global?global.setFeatures=e():"undefined"!=typeof self&&(self.setFeatures=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(require,module,exports){
'use strict';
var union = require('mout/array/union'),
contains = require('mout/array/contains'),
EventEmitter = require('events').EventEmitter,
stampit = require('stampit'),
/**
* Grab the url parameters and build a map of
* parameter names and values.
* @return {Object} params object
*/
getParams = function getParams() {
var params = {};
if (window.location.search) {
var parts = window.location.search.slice(1).split('&');
parts.forEach(function (part) {
var pair = part.split('=');
pair[0] = decodeURIComponent(pair[0]);
pair[1] = decodeURIComponent(pair[1]);
params[pair[0]] = (pair[1] !== 'undefined') ?
pair[1] : true;
});
}
return params;
},
/**
* Get a list of feature names from the url
* parameters.
* @return {Array} Features list
*/
getParamFeatures = function getParamFeatures() {
var features = getParams().ft;
return features ? features.split(',') : undefined;
},
/**
* Get a list of deselected features that need
* to be turned off. This list will override
* settings from baseFeatures.
* @return {Array} Features list
*/
getInactiveFeatures = function getInactiveFeatures() {
var features = getParams()['ft-off'];
return features ? features.split(',') : undefined;
},
/**
* Combine the list of base features with
* the features passed in via URL parameters.
* @type {Array} active features
*/
getActiveFeatures =
function getActiveFeatures(baseFeatures,
paramFeatures, inactiveFeatures) {
var features = union(baseFeatures, paramFeatures);
inactiveFeatures = inactiveFeatures || [];
return features.filter(function (feature) {
return inactiveFeatures.indexOf(feature) === -1;
});
},
/**
* Takes an array of features and creates a class for
* each of them on the body tag.
* New features should be hidden in CSS by default
* and revealed only when the feature toggle is set:
*
* .new-feature { display: none; }
* .ft-new-feature .new-feature { display: block; }
*
* @param {Array} features An array of active features.
*/
setFlags = function setFlags(features) {
var featureClasses = features.map(function (feature) {
return 'ft-' + feature;
}).join(' '),
classNames = document.getElementsByTagName('body')[0]
.className.split(' ').filter(function (className) {
return !className.match(/^ft/);
});
document.getElementsByTagName('body')[0].className =
classNames.join(' ') + ' ' + featureClasses;
},
/**
* Take an optional list of features, set the feature
* classes on the body tag, and return the feature
* toggle object.
* @param {Array} baseFeatures List of base features.
* @return {Object} feature object
*/
setFeatures = function setFeatures(baseFeatures) {
var paramFeatures = getParamFeatures(),
inactiveFeatures = getInactiveFeatures(),
activeFeatures = getActiveFeatures(baseFeatures,
paramFeatures, inactiveFeatures),
methods = {
/**
* Check to see if a feature is active.
* @param {String} feature
* @return {Boolean}
*/
active: function active(feature) {
var testFeature = feature && feature.trim &&
feature.trim();
return contains(activeFeatures, testFeature);
},
/**
* Activate a list of features.
* @emits activated
* @param {Array} features
* @return {Object} this (for chaining)
*/
/**
* activated event.
*
* @event activated
* @type {Array} activated features
*/
activate: function activate(features) {
activeFeatures = union(activeFeatures, features);
setFlags(activeFeatures);
this.emit('activated', features);
return this;
},
/**
* Deactivate a list of features.
* @emits deactivated
* @param {Array} features
* @return {Object} this (for chaining)
*/
/**
* deactivated event.
*
* @event deactivated
* @type {Array} deactivated features
*/
deactivate: function deactivate(features) {
activeFeatures =
activeFeatures.filter(function (feature) {
return !contains(features, feature);
});
setFlags(activeFeatures);
this.emit('deactivated', features);
return this;
}
},
// Creates the feature toggle object by
// composing the methods above with an
// event emitter using the Stampit
// prototypal inheritance library.
ft = stampit.compose(
stampit.convertConstructor(EventEmitter),
stampit(methods)
).create();
// Kick things off by setting feature classes
// for the currently active features.
setFlags(activeFeatures);
return ft;
};
module.exports = setFeatures;
},{"events":3,"mout/array/contains":6,"mout/array/union":9,"stampit":38}],2:[function(require,module,exports){
//
// The shims in this file are not fully implemented shims for the ES5
// features, but do work for the particular usecases there is in
// the other modules.
//
var toString = Object.prototype.toString;
var hasOwnProperty = Object.prototype.hasOwnProperty;
// Array.isArray is supported in IE9
function isArray(xs) {
return toString.call(xs) === '[object Array]';
}
exports.isArray = typeof Array.isArray === 'function' ? Array.isArray : isArray;
// Array.prototype.indexOf is supported in IE9
exports.indexOf = function indexOf(xs, x) {
if (xs.indexOf) return xs.indexOf(x);
for (var i = 0; i < xs.length; i++) {
if (x === xs[i]) return i;
}
return -1;
};
// Array.prototype.filter is supported in IE9
exports.filter = function filter(xs, fn) {
if (xs.filter) return xs.filter(fn);
var res = [];
for (var i = 0; i < xs.length; i++) {
if (fn(xs[i], i, xs)) res.push(xs[i]);
}
return res;
};
// Array.prototype.forEach is supported in IE9
exports.forEach = function forEach(xs, fn, self) {
if (xs.forEach) return xs.forEach(fn, self);
for (var i = 0; i < xs.length; i++) {
fn.call(self, xs[i], i, xs);
}
};
// Array.prototype.map is supported in IE9
exports.map = function map(xs, fn) {
if (xs.map) return xs.map(fn);
var out = new Array(xs.length);
for (var i = 0; i < xs.length; i++) {
out[i] = fn(xs[i], i, xs);
}
return out;
};
// Array.prototype.reduce is supported in IE9
exports.reduce = function reduce(array, callback, opt_initialValue) {
if (array.reduce) return array.reduce(callback, opt_initialValue);
var value, isValueSet = false;
if (2 < arguments.length) {
value = opt_initialValue;
isValueSet = true;
}
for (var i = 0, l = array.length; l > i; ++i) {
if (array.hasOwnProperty(i)) {
if (isValueSet) {
value = callback(value, array[i], i, array);
}
else {
value = array[i];
isValueSet = true;
}
}
}
return value;
};
// String.prototype.substr - negative index don't work in IE8
if ('ab'.substr(-1) !== 'b') {
exports.substr = function (str, start, length) {
// did we get a negative start, calculate how much it is from the beginning of the string
if (start < 0) start = str.length + start;
// call the original function
return str.substr(start, length);
};
} else {
exports.substr = function (str, start, length) {
return str.substr(start, length);
};
}
// String.prototype.trim is supported in IE9
exports.trim = function (str) {
if (str.trim) return str.trim();
return str.replace(/^\s+|\s+$/g, '');
};
// Function.prototype.bind is supported in IE9
exports.bind = function () {
var args = Array.prototype.slice.call(arguments);
var fn = args.shift();
if (fn.bind) return fn.bind.apply(fn, args);
var self = args.shift();
return function () {
fn.apply(self, args.concat([Array.prototype.slice.call(arguments)]));
};
};
// Object.create is supported in IE9
function create(prototype, properties) {
var object;
if (prototype === null) {
object = { '__proto__' : null };
}
else {
if (typeof prototype !== 'object') {
throw new TypeError(
'typeof prototype[' + (typeof prototype) + '] != \'object\''
);
}
var Type = function () {};
Type.prototype = prototype;
object = new Type();
object.__proto__ = prototype;
}
if (typeof properties !== 'undefined' && Object.defineProperties) {
Object.defineProperties(object, properties);
}
return object;
}
exports.create = typeof Object.create === 'function' ? Object.create : create;
// Object.keys and Object.getOwnPropertyNames is supported in IE9 however
// they do show a description and number property on Error objects
function notObject(object) {
return ((typeof object != "object" && typeof object != "function") || object === null);
}
function keysShim(object) {
if (notObject(object)) {
throw new TypeError("Object.keys called on a non-object");
}
var result = [];
for (var name in object) {
if (hasOwnProperty.call(object, name)) {
result.push(name);
}
}
return result;
}
// getOwnPropertyNames is almost the same as Object.keys one key feature
// is that it returns hidden properties, since that can't be implemented,
// this feature gets reduced so it just shows the length property on arrays
function propertyShim(object) {
if (notObject(object)) {
throw new TypeError("Object.getOwnPropertyNames called on a non-object");
}
var result = keysShim(object);
if (exports.isArray(object) && exports.indexOf(object, 'length') === -1) {
result.push('length');
}
return result;
}
var keys = typeof Object.keys === 'function' ? Object.keys : keysShim;
var getOwnPropertyNames = typeof Object.getOwnPropertyNames === 'function' ?
Object.getOwnPropertyNames : propertyShim;
if (new Error().hasOwnProperty('description')) {
var ERROR_PROPERTY_FILTER = function (obj, array) {
if (toString.call(obj) === '[object Error]') {
array = exports.filter(array, function (name) {
return name !== 'description' && name !== 'number' && name !== 'message';
});
}
return array;
};
exports.keys = function (object) {
return ERROR_PROPERTY_FILTER(object, keys(object));
};
exports.getOwnPropertyNames = function (object) {
return ERROR_PROPERTY_FILTER(object, getOwnPropertyNames(object));
};
} else {
exports.keys = keys;
exports.getOwnPropertyNames = getOwnPropertyNames;
}
// Object.getOwnPropertyDescriptor - supported in IE8 but only on dom elements
function valueObject(value, key) {
return { value: value[key] };
}
if (typeof Object.getOwnPropertyDescriptor === 'function') {
try {
Object.getOwnPropertyDescriptor({'a': 1}, 'a');
exports.getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
} catch (e) {
// IE8 dom element issue - use a try catch and default to valueObject
exports.getOwnPropertyDescriptor = function (value, key) {
try {
return Object.getOwnPropertyDescriptor(value, key);
} catch (e) {
return valueObject(value, key);
}
};
}
} else {
exports.getOwnPropertyDescriptor = valueObject;
}
},{}],3:[function(require,module,exports){
// 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 util = require('util');
function EventEmitter() {
this._events = this._events || {};
this._maxListeners = this._maxListeners || undefined;
}
module.exports = EventEmitter;
// Backwards-compat with node 0.10.x
EventEmitter.EventEmitter = EventEmitter;
EventEmitter.prototype._events = undefined;
EventEmitter.prototype._maxListeners = undefined;
// By default EventEmitters will print a warning if more than 10 listeners are
// added to it. This is a useful default which helps finding memory leaks.
EventEmitter.defaultMaxListeners = 10;
// Obviously not all Emitters should be limited to 10. This function allows
// that to be increased. Set to zero for unlimited.
EventEmitter.prototype.setMaxListeners = function(n) {
if (!util.isNumber(n) || n < 0)
throw TypeError('n must be a positive number');
this._maxListeners = n;
return this;
};
EventEmitter.prototype.emit = function(type) {
var er, handler, len, args, i, listeners;
if (!this._events)
this._events = {};
// If there is no 'error' event listener then throw.
if (type === 'error') {
if (!this._events.error ||
(util.isObject(this._events.error) && !this._events.error.length)) {
er = arguments[1];
if (er instanceof Error) {
throw er; // Unhandled 'error' event
} else {
throw TypeError('Uncaught, unspecified "error" event.');
}
return false;
}
}
handler = this._events[type];
if (util.isUndefined(handler))
return false;
if (util.isFunction(handler)) {
switch (arguments.length) {
// fast cases
case 1:
handler.call(this);
break;
case 2:
handler.call(this, arguments[1]);
break;
case 3:
handler.call(this, arguments[1], arguments[2]);
break;
// slower
default:
len = arguments.length;
args = new Array(len - 1);
for (i = 1; i < len; i++)
args[i - 1] = arguments[i];
handler.apply(this, args);
}
} else if (util.isObject(handler)) {
len = arguments.length;
args = new Array(len - 1);
for (i = 1; i < len; i++)
args[i - 1] = arguments[i];
listeners = handler.slice();
len = listeners.length;
for (i = 0; i < len; i++)
listeners[i].apply(this, args);
}
return true;
};
EventEmitter.prototype.addListener = function(type, listener) {
var m;
if (!util.isFunction(listener))
throw TypeError('listener must be a function');
if (!this._events)
this._events = {};
// To avoid recursion in the case that type === "newListener"! Before
// adding it to the listeners, first emit "newListener".
if (this._events.newListener)
this.emit('newListener', type,
util.isFunction(listener.listener) ?
listener.listener : listener);
if (!this._events[type])
// Optimize the case of one listener. Don't need the extra array object.
this._events[type] = listener;
else if (util.isObject(this._events[type]))
// If we've already got an array, just append.
this._events[type].push(listener);
else
// Adding the second element, need to change to array.
this._events[type] = [this._events[type], listener];
// Check for listener leak
if (util.isObject(this._events[type]) && !this._events[type].warned) {
var m;
if (!util.isUndefined(this._maxListeners)) {
m = this._maxListeners;
} else {
m = EventEmitter.defaultMaxListeners;
}
if (m && m > 0 && this._events[type].length > m) {
this._events[type].warned = true;
console.error('(node) warning: possible EventEmitter memory ' +
'leak detected. %d listeners added. ' +
'Use emitter.setMaxListeners() to increase limit.',
this._events[type].length);
console.trace();
}
}
return this;
};
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
EventEmitter.prototype.once = function(type, listener) {
if (!util.isFunction(listener))
throw TypeError('listener must be a function');
function g() {
this.removeListener(type, g);
listener.apply(this, arguments);
}
g.listener = listener;
this.on(type, g);
return this;
};
// emits a 'removeListener' event iff the listener was removed
EventEmitter.prototype.removeListener = function(type, listener) {
var list, position, length, i;
if (!util.isFunction(listener))
throw TypeError('listener must be a function');
if (!this._events || !this._events[type])
return this;
list = this._events[type];
length = list.length;
position = -1;
if (list === listener ||
(util.isFunction(list.listener) && list.listener === listener)) {
delete this._events[type];
if (this._events.removeListener)
this.emit('removeListener', type, listener);
} else if (util.isObject(list)) {
for (i = length; i-- > 0;) {
if (list[i] === listener ||
(list[i].listener && list[i].listener === listener)) {
position = i;
break;
}
}
if (position < 0)
return this;
if (list.length === 1) {
list.length = 0;
delete this._events[type];
} else {
list.splice(position, 1);
}
if (this._events.removeListener)
this.emit('removeListener', type, listener);
}
return this;
};
EventEmitter.prototype.removeAllListeners = function(type) {
var key, listeners;
if (!this._events)
return this;
// not listening for removeListener, no need to emit
if (!this._events.removeListener) {
if (arguments.length === 0)
this._events = {};
else if (this._events[type])
delete this._events[type];
return this;
}
// emit removeListener for all listeners on all events
if (arguments.length === 0) {
for (key in this._events) {
if (key === 'removeListener') continue;
this.removeAllListeners(key);
}
this.removeAllListeners('removeListener');
this._events = {};
return this;
}
listeners = this._events[type];
if (util.isFunction(listeners)) {
this.removeListener(type, listeners);
} else {
// LIFO order
while (listeners.length)
this.removeListener(type, listeners[listeners.length - 1]);
}
delete this._events[type];
return this;
};
EventEmitter.prototype.listeners = function(type) {
var ret;
if (!this._events || !this._events[type])
ret = [];
else if (util.isFunction(this._events[type]))
ret = [this._events[type]];
else
ret = this._events[type].slice();
return ret;
};
EventEmitter.listenerCount = function(emitter, type) {
var ret;
if (!emitter._events || !emitter._events[type])
ret = 0;
else if (util.isFunction(emitter._events[type]))
ret = 1;
else
ret = emitter._events[type].length;
return ret;
};
},{"util":4}],4:[function(require,module,exports){
// 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 shims = require('_shims');
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;
};
/**
* 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 = {};
shims.forEach(array, 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);
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 = shims.keys(value);
var visibleKeys = arrayToHash(keys);
if (ctx.showHidden) {
keys = shims.getOwnPropertyNames(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('');
}
}
shims.forEach(keys, 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 = shims.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 (shims.indexOf(ctx.seen, 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 = shims.reduce(output, 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 shims.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) {
return arg === void 0;
}
exports.isUndefined = isUndefined;
function isRegExp(re) {
return isObject(re) && objectToString(re) === '[object RegExp]';
}
exports.isRegExp = isRegExp;
function isObject(arg) {
return typeof arg === 'object' && arg;
}
exports.isObject = isObject;
function isDate(d) {
return isObject(d) && objectToString(d) === '[object Date]';
}
exports.isDate = isDate;
function isError(e) {
return isObject(e) && objectToString(e) === '[object Error]';
}
exports.isError = isError;
function isFunction(arg) {
return typeof arg === 'function';
}
exports.isFunction = isFunction;
function isPrimitive(arg) {
return arg === null ||
typeof arg === 'boolean' ||
typeof arg === 'number' ||
typeof arg === 'string' ||
typeof arg === 'symbol' || // ES6 symbol
typeof arg === 'undefined';
}
exports.isPrimitive = isPrimitive;
function isBuffer(arg) {
return arg && typeof arg === 'object'
&& typeof arg.copy === 'function'
&& typeof arg.fill === 'function'
&& typeof arg.binarySlice === 'function'
;
}
exports.isBuffer = isBuffer;
function objectToString(o) {
return Object.prototype.toString.call(o);
}
function pad(n) {
return n < 10 ? '0' + n.toString(10) : n.toString(10);
}
var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
'Oct', 'Nov', 'Dec'];
// 26 Feb 16:19:34
function timestamp() {
var d = new Date();
var time = [pad(d.getHours()),
pad(d.getMinutes()),
pad(d.getSeconds())].join(':');
return [d.getDate(), months[d.getMonth()], time].join(' ');
}
// log is just a thin wrapper to console.log that prepends a timestamp
exports.log = function() {
console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments));
};
/**
* Inherit the prototype methods from one constructor into another.
*
* The Function.prototype.inherits from lang.js rewritten as a standalone
* function (not on Function.prototype). NOTE: If this file is to be loaded
* during bootstrapping this function needs to be rewritten using some native
* functions as prototype setup using normal JavaScript does not work as
* expected during bootstrapping (see mirror.js in r114903).
*
* @param {function} ctor Constructor function which needs to inherit the
* prototype.
* @param {function} superCtor Constructor function to inherit prototype from.
*/
exports.inherits = function(ctor, superCtor) {
ctor.super_ = superCtor;
ctor.prototype = shims.create(superCtor.prototype, {
constructor: {
value: ctor,
enumerable: false,
writable: true,
configurable: true
}
});
};
exports._extend = function(origin, add) {
// Don't do anything if add isn't an object
if (!add || !isObject(add)) return origin;
var keys = shims.keys(add);
var i = keys.length;
while (i--) {
origin[keys[i]] = add[keys[i]];
}
return origin;
};
function hasOwnProperty(obj, prop) {
return Object.prototype.hasOwnProperty.call(obj, prop);
}
},{"_shims":2}],5:[function(require,module,exports){
/**
* Appends an array to the end of another.
* The first array will be modified.
*/
function append(arr1, arr2) {
if (arr2 == null) {
return arr1;
}
var pad = arr1.length,
i = -1,
len = arr2.length;
while (++i < len) {
arr1[pad + i] = arr2[i];
}
return arr1;
}
module.exports = append;
},{}],6:[function(require,module,exports){
var indexOf = require('./indexOf');
/**
* If array contains values.
*/
function contains(arr, val) {
return indexOf(arr, val) !== -1;
}
module.exports = contains;
},{"./indexOf":8}],7:[function(require,module,exports){
var makeIterator = require('../function/makeIterator_');
/**
* Array filter
*/
function filter(arr, callback, thisObj) {
callback = makeIterator(callback, thisObj);
var results = [];
if (arr == null) {
return results;
}
var i = -1, len = arr.length, value;
while (++i < len) {
value = arr[i];
if (callback(value, i, arr)) {
results.push(value);
}
}
return results;
}
module.exports = filter;
},{"../function/makeIterator_":11}],8:[function(require,module,exports){
/**
* Array.indexOf
*/
function indexOf(arr, item, fromIndex) {
fromIndex = fromIndex || 0;
if (arr == null) {
return -1;
}
var len = arr.length,
i = fromIndex < 0 ? len + fromIndex : fromIndex;
while (i < len) {
// we iterate over sparse items since there is no way to make it
// work properly on IE 7-8. see #64
if (arr[i] === item) {
return i;
}
i++;
}
return -1;
}
module.exports = indexOf;
},{}],9:[function(require,module,exports){
var unique = require('./unique');
var append = require('./append');
/**
* Concat multiple arrays and remove duplicates
*/
function union(arrs) {
var results = [];
var i = -1, len = arguments.length;
while (++i < len) {
append(results, arguments[i]);
}
return unique(results);
}
module.exports = union;
},{"./append":5,"./unique":10}],10:[function(require,module,exports){
var indexOf = require('./indexOf');
var filter = require('./filter');
/**
* @return {array} Array of unique items
*/
function unique(arr){
return filter(arr, isUnique);
}
function isUnique(item, i, arr){
return indexOf(arr, item, i+1) === -1;
}
module.exports = unique;
},{"./filter":7,"./indexOf":8}],11:[function(require,module,exports){
var prop = require('./prop');
var deepMatches = require('../object/deepMatches');
/**
* Converts argument into a valid iterator.
* Used internally on most array/object/collection methods that receives a
* callback/iterator providing a shortcut syntax.
*/
function makeIterator(src, thisObj){
switch(typeof src) {
case 'function':
// function is the first to improve perf (most common case)
return (typeof thisObj !== 'undefined')? function(val, i, arr){
return src.call(thisObj, val, i, arr);
} : src;
case 'object':
// typeof null == "object"
return (src != null)? function(val){
return deepMatches(val, src);
} : src;
case 'string':
case 'number':
return prop(src);
default:
return src;
}
}
module.exports = makeIterator;
},{"../object/deepMatches":16,"./prop":12}],12:[function(require,module,exports){
/**
* Returns a function that gets a property of the passed object
*/
function prop(name){
return function(obj){
return obj[name];
};
}
module.exports = prop;
},{}],13:[function(require,module,exports){
var isKind = require('./isKind');
/**
*/
var isArray = Array.isArray || function (val) {
return isKind(val, 'Array');
};
module.exports = isArray;
},{"./isKind":14}],14:[function(require,module,exports){
var kindOf = require('./kindOf');
/**
* Check if value is from a specific "kind".
*/
function isKind(val, kind){
return kindOf(val) === kind;
}
module.exports = isKind;
},{"./kindOf":15}],15:[function(require,module,exports){
var _rKind = /^\[object (.*)\]$/,
_toString = Object.prototype.toString,
UNDEF;
/**
* Gets the "kind" of value. (e.g. "String", "Number", etc)
*/
function kindOf(val) {
if (val === null) {
return 'Null';
} else if (val === UNDEF) {
return 'Undefined';
} else {
return _rKind.exec( _toString.call(val) )[1];
}
}
module.exports = kindOf;
},{}],16:[function(require,module,exports){
var forOwn = require('./forOwn');
var isArray = require('../lang/isArray');
function containsMatch(array, pattern) {
var i = -1, length = array.length;
while (++i < length) {
if (deepMatches(array[i], pattern)) {
return true;
}
}
return false;
}
function matchArray(target, pattern) {
var i = -1, patternLength = pattern.length;
while (++i < patternLength) {
if (!containsMatch(target, pattern[i])) {
return false;
}
}
return true;
}
function matchObject(target, pattern) {
var result = true;
forOwn(pattern, function(val, key) {
if (!deepMatches(target[key], val)) {
// Return false to break out of forOwn early
return (result = false);
}
});
return result;
}
/**
* Recursively check if the objects match.
*/
function deepMatches(target, pattern){
if (target && typeof target === 'object') {
if (isArray(target) && isArray(pattern)) {
return matchArray(target, pattern);
} else {
return matchObject(target, pattern);
}
} else {
return target === pattern;
}
}
module.exports = deepMatches;
},{"../lang/isArray":13,"./forOwn":18}],17:[function(require,module,exports){
var _hasDontEnumBug,
_dontEnums;
function checkDontEnum(){
_dontEnums = [
'toString',
'toLocaleString',
'valueOf',
'hasOwnProperty',
'isPrototypeOf',
'propertyIsEnumerable',
'constructor'
];
_hasDontEnumBug = true;
for (var key in {'toString': null}) {
_hasDontEnumBug = false;
}
}
/**
* Similar to Array/forEach but works over object properties and fixes Don't
* Enum bug on IE.
* based on: http://whattheheadsaid.com/2010/10/a-safer-object-keys-compatibility-implementation
*/
function forIn(obj, fn, thisObj){
var key, i = 0;
// no need to check if argument is a real object that way we can use
// it for arrays, functions, date, etc.
//post-pone check till needed
if (_hasDontEnumBug == null) checkDontEnum();
for (key in obj) {
if (exec(fn, obj, key, thisObj) === false) {
break;
}
}
if (_hasDontEnumBug) {
while (key = _dontEnums[i++]) {
// since we aren't using hasOwn check we need to make sure the
// property was overwritten
if (obj[key] !== Object.prototype[key]) {
if (exec(fn, obj, key, thisObj) === false) {
break;
}
}
}
}
}
function exec(fn, obj, key, thisObj){
return fn.call(thisObj, obj[key], key, obj);
}
module.exports = forIn;
},{}],18:[function(require,module,exports){
var hasOwn = require('./hasOwn');
var forIn = require('./forIn');
/**
* Similar to Array/forEach but works over object properties and fixes Don't
* Enum bug on IE.
* based on: http://whattheheadsaid.com/2010/10/a-safer-object-keys-compatibility-implementation
*/
function forOwn(obj, fn, thisObj){
forIn(obj, function(val, key){
if (hasOwn(obj, key)) {
return fn.call(thisObj, obj[key], key, obj);
}
});
}
module.exports = forOwn;
},{"./forIn":17,"./hasOwn":19}],19:[function(require,module,exports){
/**
* Safer Object.hasOwnProperty
*/
function hasOwn(obj, prop){
return Object.prototype.hasOwnProperty.call(obj, prop);
}
module.exports = hasOwn;
},{}],20:[function(require,module,exports){
var forIn = require('mout/object/forIn');
function copyProp(val, key){
this[key] = val;
}
module.exports = function mixInChain(target, objects){
var i = 0,
n = arguments.length,
obj;
while(++i < n){
obj = arguments[i];
if (obj != null) {
forIn(obj, copyProp, target);
}
}
return target;
};
},{"mout/object/forIn":33}],21:[function(require,module,exports){
/**
* Array forEach
*/
function forEach(arr, callback, thisObj) {
if (arr == null) {
return;
}
var i = -1,
len = arr.length;
while (++i < len) {
// we iterate over sparse items since there is no way to make it
// work properly on IE 7-8. see #64
if ( callback.call(thisObj, arr[i], i, arr) === false ) {
break;
}
}
}
module.exports = forEach;
},{}],22:[function(require,module,exports){
var forEach = require('./forEach');
var makeIterator = require('../function/makeIterator_');
/**
* Array map
*/
function map(arr, callback, thisObj) {
callback = makeIterator(callback, thisObj);
var results = [];
if (arr == null){
return results;
}
var i = -1, len = arr.length;
while (++i < len) {
results[i] = callback(arr[i], i, arr);
}
return results;
}
module.exports = map;
},{"../function/makeIterator_":23,"./forEach":21}],23:[function(require,module,exports){
var prop = require('./prop');
var deepMatches = require('../object/deepMatches');
/**
* Converts argument into a valid iterator.
* Used internally on most array/object/collection methods that receives a
* callback/iterator providing a shortcut syntax.
*/
function makeIterator(src, thisObj){
switch(typeof src) {
case 'object':
// typeof null == "object"
return (src != null)? function(val, key, target){
return deepMatches(val, src);
} : src;
case 'string':
case 'number':
return prop(src);
case 'function':
if (typeof thisObj === 'undefined') {
return src;
} else {
return function(val, i, arr){
return src.call(thisObj, val, i, arr);
};
}
default:
return src;
}
}
module.exports = makeIterator;
},{"../object/deepMatches":32,"./prop":24}],24:[function(require,module,exports){
module.exports=require(12)
},{}],25:[function(require,module,exports){
var kindOf = require('./kindOf');
var isPlainObject = require('./isPlainObject');
var mixIn = require('../object/mixIn');
/**
* Clone native types.
*/
function clone(val){
switch (kindOf(val)) {
case 'Object':
return cloneObject(val);
case 'Array':
return cloneArray(val);
case 'RegExp':
return cloneRegExp(val);
case 'Date':
return cloneDate(val);
default:
return val;
}
}
function cloneObject(source) {
if (isPlainObject(source)) {
return mixIn({}, source);
} else {
return source;
}
}
function cloneRegExp(r) {
var flags = '';
flags += r.multiline ? 'm' : '';
flags += r.global ? 'g' : '';
flags += r.ignorecase ? 'i' : '';
return new RegExp(r.source, flags);
}
function cloneDate(date) {
return new Date(+date);
}
function cloneArray(arr) {
return arr.slice();
}
module.exports = clone;
},{"../object/mixIn":37,"./isPlainObject":30,"./kindOf":31}],26:[function(require,module,exports){
var clone = require('./clone');
var forOwn = require('../object/forOwn');
var kindOf = require('./kindOf');
var isPlainObject = require('./isPlainObject');
/**
* Recursively clone native types.
*/
function deepClone(val, instanceClone) {
switch ( kindOf(val) ) {
case 'Object':
return cloneObject(val, instanceClone);
case 'Array':
return cloneArray(val, instanceClone);
default:
return clone(val);
}
}
function cloneObject(source, instanceClone) {
if (isPlainObject(source)) {
var out = {};
forOwn(source, function(val, key) {
this[key] = deepClone(val, instanceClone);
}, out);
return out;
} else if (instanceClone) {
return instanceClone(source);
} else {
return source;
}
}
function cloneArray(arr, instanceClone) {
var out = [],
i = -1,
n = arr.length,
val;
while (++i < n) {
out[i] = deepClone(arr[i], instanceClone);
}
return out;
}
module.exports = deepClone;
},{"../object/forOwn":34,"./clone":25,"./isPlainObject":30,"./kindOf":31}],27:[function(require,module,exports){
module.exports=require(13)
},{"./isKind":28}],28:[function(require,module,exports){
module.exports=require(14)
},{"./kindOf":31}],29:[function(require,module,exports){
var isKind = require('./isKind');
/**
*/
function isObject(val) {
return isKind(val, 'Object');
}
module.exports = isObject;
},{"./isKind":28}],30:[function(require,module,exports){
/**
* Checks if the value is created by the `Object` constructor.
*/
function isPlainObject(value) {
return (!!value
&& typeof value === 'object'
&& value.constructor === Object);
}
module.exports = isPlainObject;
},{}],31:[function(require,module,exports){
module.exports=require(15)
},{}],32:[function(require,module,exports){
arguments[4][16][0].apply(exports,arguments)
},{"../lang/isArray":27,"./for