summaryrefslogtreecommitdiffstats
path: root/devtools/client/shared/vendor/seamless-immutable.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/shared/vendor/seamless-immutable.js')
-rw-r--r--devtools/client/shared/vendor/seamless-immutable.js392
1 files changed, 392 insertions, 0 deletions
diff --git a/devtools/client/shared/vendor/seamless-immutable.js b/devtools/client/shared/vendor/seamless-immutable.js
new file mode 100644
index 000000000..ef893f384
--- /dev/null
+++ b/devtools/client/shared/vendor/seamless-immutable.js
@@ -0,0 +1,392 @@
+(function(){
+ "use strict";
+
+ function addPropertyTo(target, methodName, value) {
+ Object.defineProperty(target, methodName, {
+ enumerable: false,
+ configurable: false,
+ writable: false,
+ value: value
+ });
+ }
+
+ function banProperty(target, methodName) {
+ addPropertyTo(target, methodName, function() {
+ throw new ImmutableError("The " + methodName +
+ " method cannot be invoked on an Immutable data structure.");
+ });
+ }
+
+ var immutabilityTag = "__immutable_invariants_hold";
+
+ function addImmutabilityTag(target) {
+ addPropertyTo(target, immutabilityTag, true);
+ }
+
+ function isImmutable(target) {
+ if (typeof target === "object") {
+ return target === null || target.hasOwnProperty(immutabilityTag);
+ } else {
+ // In JavaScript, only objects are even potentially mutable.
+ // strings, numbers, null, and undefined are all naturally immutable.
+ return true;
+ }
+ }
+
+ function isMergableObject(target) {
+ return target !== null && typeof target === "object" && !(target instanceof Array) && !(target instanceof Date);
+ }
+
+ var mutatingObjectMethods = [
+ "setPrototypeOf"
+ ];
+
+ var nonMutatingObjectMethods = [
+ "keys"
+ ];
+
+ var mutatingArrayMethods = mutatingObjectMethods.concat([
+ "push", "pop", "sort", "splice", "shift", "unshift", "reverse"
+ ]);
+
+ var nonMutatingArrayMethods = nonMutatingObjectMethods.concat([
+ "map", "filter", "slice", "concat", "reduce", "reduceRight"
+ ]);
+
+ function ImmutableError(message) {
+ var err = new Error(message);
+ err.__proto__ = ImmutableError;
+
+ return err;
+ }
+ ImmutableError.prototype = Error.prototype;
+
+ function makeImmutable(obj, bannedMethods) {
+ // Tag it so we can quickly tell it's immutable later.
+ addImmutabilityTag(obj);
+
+ if ("development" === "development") {
+ // Make all mutating methods throw exceptions.
+ for (var index in bannedMethods) {
+ if (bannedMethods.hasOwnProperty(index)) {
+ banProperty(obj, bannedMethods[index]);
+ }
+ }
+
+ // Freeze it and return it.
+ Object.freeze(obj);
+ }
+
+ return obj;
+ }
+
+ function makeMethodReturnImmutable(obj, methodName) {
+ var currentMethod = obj[methodName];
+
+ addPropertyTo(obj, methodName, function() {
+ return Immutable(currentMethod.apply(obj, arguments));
+ });
+ }
+
+ function makeImmutableArray(array) {
+ // Don't change their implementations, but wrap these functions to make sure
+ // they always return an immutable value.
+ for (var index in nonMutatingArrayMethods) {
+ if (nonMutatingArrayMethods.hasOwnProperty(index)) {
+ var methodName = nonMutatingArrayMethods[index];
+ makeMethodReturnImmutable(array, methodName);
+ }
+ }
+
+ addPropertyTo(array, "flatMap", flatMap);
+ addPropertyTo(array, "asObject", asObject);
+ addPropertyTo(array, "asMutable", asMutableArray);
+
+ for(var i = 0, length = array.length; i < length; i++) {
+ array[i] = Immutable(array[i]);
+ }
+
+ return makeImmutable(array, mutatingArrayMethods);
+ }
+
+ /**
+ * Effectively performs a map() over the elements in the array, using the
+ * provided iterator, except that whenever the iterator returns an array, that
+ * array's elements are added to the final result instead of the array itself.
+ *
+ * @param {function} iterator - The iterator function that will be invoked on each element in the array. It will receive three arguments: the current value, the current index, and the current object.
+ */
+ function flatMap(iterator) {
+ // Calling .flatMap() with no arguments is a no-op. Don't bother cloning.
+ if (arguments.length === 0) {
+ return this;
+ }
+
+ var result = [],
+ length = this.length,
+ index;
+
+ for (index = 0; index < length; index++) {
+ var iteratorResult = iterator(this[index], index, this);
+
+ if (iteratorResult instanceof Array) {
+ // Concatenate Array results into the return value we're building up.
+ result.push.apply(result, iteratorResult);
+ } else {
+ // Handle non-Array results the same way map() does.
+ result.push(iteratorResult);
+ }
+ }
+
+ return makeImmutableArray(result);
+ }
+
+ /**
+ * Returns an Immutable copy of the object without the given keys included.
+ *
+ * @param {array} keysToRemove - A list of strings representing the keys to exclude in the return value. Instead of providing a single array, this method can also be called by passing multiple strings as separate arguments.
+ */
+ function without(keysToRemove) {
+ // Calling .without() with no arguments is a no-op. Don't bother cloning.
+ if (arguments.length === 0) {
+ return this;
+ }
+
+ // If we weren't given an array, use the arguments list.
+ if (!(keysToRemove instanceof Array)) {
+ keysToRemove = Array.prototype.slice.call(arguments);
+ }
+
+ var result = this.instantiateEmptyObject();
+
+ for (var key in this) {
+ if (this.hasOwnProperty(key) && (keysToRemove.indexOf(key) === -1)) {
+ result[key] = this[key];
+ }
+ }
+
+ return makeImmutableObject(result,
+ {instantiateEmptyObject: this.instantiateEmptyObject});
+ }
+
+ function asMutableArray(opts) {
+ var result = [], i, length;
+
+ if(opts && opts.deep) {
+ for(i = 0, length = this.length; i < length; i++) {
+ result.push( asDeepMutable(this[i]) );
+ }
+ } else {
+ for(i = 0, length = this.length; i < length; i++) {
+ result.push(this[i]);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Effectively performs a [map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) over the elements in the array, expecting that the iterator function
+ * will return an array of two elements - the first representing a key, the other
+ * a value. Then returns an Immutable Object constructed of those keys and values.
+ *
+ * @param {function} iterator - A function which should return an array of two elements - the first representing the desired key, the other the desired value.
+ */
+ function asObject(iterator) {
+ // If no iterator was provided, assume the identity function
+ // (suggesting this array is already a list of key/value pairs.)
+ if (typeof iterator !== "function") {
+ iterator = function(value) { return value; };
+ }
+
+ var result = {},
+ length = this.length,
+ index;
+
+ for (index = 0; index < length; index++) {
+ var pair = iterator(this[index], index, this),
+ key = pair[0],
+ value = pair[1];
+
+ result[key] = value;
+ }
+
+ return makeImmutableObject(result);
+ }
+
+ function asDeepMutable(obj) {
+ if(!obj || !obj.hasOwnProperty(immutabilityTag) || obj instanceof Date) { return obj; }
+ return obj.asMutable({deep: true});
+ }
+
+ function quickCopy(src, dest) {
+ for (var key in src) {
+ if (src.hasOwnProperty(key)) {
+ dest[key] = src[key];
+ }
+ }
+
+ return dest;
+ }
+
+ /**
+ * Returns an Immutable Object containing the properties and values of both
+ * this object and the provided object, prioritizing the provided object's
+ * values whenever the same key is present in both objects.
+ *
+ * @param {object} other - The other object to merge. Multiple objects can be passed as an array. In such a case, the later an object appears in that list, the higher its priority.
+ * @param {object} config - Optional config object that contains settings. Supported settings are: {deep: true} for deep merge and {merger: mergerFunc} where mergerFunc is a function
+ * that takes a property from both objects. If anything is returned it overrides the normal merge behaviour.
+ */
+ function merge(other, config) {
+ // Calling .merge() with no arguments is a no-op. Don't bother cloning.
+ if (arguments.length === 0) {
+ return this;
+ }
+
+ if (other === null || (typeof other !== "object")) {
+ throw new TypeError("Immutable#merge can only be invoked with objects or arrays, not " + JSON.stringify(other));
+ }
+
+ var anyChanges = false,
+ result = quickCopy(this, this.instantiateEmptyObject()), // A shallow clone of this object.
+ receivedArray = (other instanceof Array),
+ deep = config && config.deep,
+ merger = config && config.merger,
+ key;
+
+ // Use the given key to extract a value from the given object, then place
+ // that value in the result object under the same key. If that resulted
+ // in a change from this object's value at that key, set anyChanges = true.
+ function addToResult(currentObj, otherObj, key) {
+ var immutableValue = Immutable(otherObj[key]);
+ var mergerResult = merger && merger(currentObj[key], immutableValue, config);
+ if (merger && mergerResult && mergerResult === currentObj[key]) return;
+
+ anyChanges = anyChanges ||
+ mergerResult !== undefined ||
+ (!currentObj.hasOwnProperty(key) ||
+ ((immutableValue !== currentObj[key]) &&
+ // Avoid false positives due to (NaN !== NaN) evaluating to true
+ (immutableValue === immutableValue)));
+
+ if (mergerResult) {
+ result[key] = mergerResult;
+ } else if (deep && isMergableObject(currentObj[key]) && isMergableObject(immutableValue)) {
+ result[key] = currentObj[key].merge(immutableValue, config);
+ } else {
+ result[key] = immutableValue;
+ }
+ }
+
+ // Achieve prioritization by overriding previous values that get in the way.
+ if (!receivedArray) {
+ // The most common use case: just merge one object into the existing one.
+ for (key in other) {
+ if (other.hasOwnProperty(key)) {
+ addToResult(this, other, key);
+ }
+ }
+ } else {
+ // We also accept an Array
+ for (var index=0; index < other.length; index++) {
+ var otherFromArray = other[index];
+
+ for (key in otherFromArray) {
+ if (otherFromArray.hasOwnProperty(key)) {
+ addToResult(this, otherFromArray, key);
+ }
+ }
+ }
+ }
+
+ if (anyChanges) {
+ return makeImmutableObject(result,
+ {instantiateEmptyObject: this.instantiateEmptyObject});
+ } else {
+ return this;
+ }
+ }
+
+ function asMutableObject(opts) {
+ var result = this.instantiateEmptyObject(), key;
+
+ if(opts && opts.deep) {
+ for (key in this) {
+ if (this.hasOwnProperty(key)) {
+ result[key] = asDeepMutable(this[key]);
+ }
+ }
+ } else {
+ for (key in this) {
+ if (this.hasOwnProperty(key)) {
+ result[key] = this[key];
+ }
+ }
+ }
+
+ return result;
+ }
+
+ // Creates plain object to be used for cloning
+ function instantiatePlainObject() {
+ return {};
+ }
+
+ // Finalizes an object with immutable methods, freezes it, and returns it.
+ function makeImmutableObject(obj, options) {
+ var instantiateEmptyObject =
+ (options && options.instantiateEmptyObject) ?
+ options.instantiateEmptyObject : instantiatePlainObject;
+
+ addPropertyTo(obj, "merge", merge);
+ addPropertyTo(obj, "without", without);
+ addPropertyTo(obj, "asMutable", asMutableObject);
+ addPropertyTo(obj, "instantiateEmptyObject", instantiateEmptyObject);
+
+ return makeImmutable(obj, mutatingObjectMethods);
+ }
+
+ function Immutable(obj, options) {
+ if (isImmutable(obj)) {
+ return obj;
+ } else if (obj instanceof Array) {
+ return makeImmutableArray(obj.slice());
+ } else if (obj instanceof Date) {
+ return makeImmutable(new Date(obj.getTime()));
+ } else {
+ // Don't freeze the object we were given; make a clone and use that.
+ var prototype = options && options.prototype;
+ var instantiateEmptyObject =
+ (!prototype || prototype === Object.prototype) ?
+ instantiatePlainObject : (function() { return Object.create(prototype); });
+ var clone = instantiateEmptyObject();
+
+ for (var key in obj) {
+ if (obj.hasOwnProperty(key)) {
+ clone[key] = Immutable(obj[key]);
+ }
+ }
+
+ return makeImmutableObject(clone,
+ {instantiateEmptyObject: instantiateEmptyObject});
+ }
+ }
+
+ // Export the library
+ Immutable.isImmutable = isImmutable;
+ Immutable.ImmutableError = ImmutableError;
+
+ Object.freeze(Immutable);
+
+ /* istanbul ignore if */
+ if (typeof module === "object") {
+ module.exports = Immutable;
+ } else if (typeof exports === "object") {
+ exports.Immutable = Immutable;
+ } else if (typeof window === "object") {
+ window.Immutable = Immutable;
+ } else if (typeof global === "object") {
+ global.Immutable = Immutable;
+ }
+})();