Adding mapbox-gl branch
This commit is contained in:
@@ -0,0 +1,686 @@
|
||||
// Copyright 2006 The Closure Library Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview Utilities for manipulating objects/maps/hashes.
|
||||
* @author arv@google.com (Erik Arvidsson)
|
||||
*/
|
||||
|
||||
goog.provide('goog.object');
|
||||
|
||||
|
||||
/**
|
||||
* Calls a function for each element in an object/map/hash.
|
||||
*
|
||||
* @param {Object<K,V>} obj The object over which to iterate.
|
||||
* @param {function(this:T,V,?,Object<K,V>):?} f The function to call
|
||||
* for every element. This function takes 3 arguments (the element, the
|
||||
* index and the object) and the return value is ignored.
|
||||
* @param {T=} opt_obj This is used as the 'this' object within f.
|
||||
* @template T,K,V
|
||||
*/
|
||||
goog.object.forEach = function(obj, f, opt_obj) {
|
||||
for (var key in obj) {
|
||||
f.call(opt_obj, obj[key], key, obj);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Calls a function for each element in an object/map/hash. If that call returns
|
||||
* true, adds the element to a new object.
|
||||
*
|
||||
* @param {Object<K,V>} obj The object over which to iterate.
|
||||
* @param {function(this:T,V,?,Object<K,V>):boolean} f The function to call
|
||||
* for every element. This
|
||||
* function takes 3 arguments (the element, the index and the object)
|
||||
* and should return a boolean. If the return value is true the
|
||||
* element is added to the result object. If it is false the
|
||||
* element is not included.
|
||||
* @param {T=} opt_obj This is used as the 'this' object within f.
|
||||
* @return {!Object<K,V>} a new object in which only elements that passed the
|
||||
* test are present.
|
||||
* @template T,K,V
|
||||
*/
|
||||
goog.object.filter = function(obj, f, opt_obj) {
|
||||
var res = {};
|
||||
for (var key in obj) {
|
||||
if (f.call(opt_obj, obj[key], key, obj)) {
|
||||
res[key] = obj[key];
|
||||
}
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* For every element in an object/map/hash calls a function and inserts the
|
||||
* result into a new object.
|
||||
*
|
||||
* @param {Object<K,V>} obj The object over which to iterate.
|
||||
* @param {function(this:T,V,?,Object<K,V>):R} f The function to call
|
||||
* for every element. This function
|
||||
* takes 3 arguments (the element, the index and the object)
|
||||
* and should return something. The result will be inserted
|
||||
* into a new object.
|
||||
* @param {T=} opt_obj This is used as the 'this' object within f.
|
||||
* @return {!Object<K,R>} a new object with the results from f.
|
||||
* @template T,K,V,R
|
||||
*/
|
||||
goog.object.map = function(obj, f, opt_obj) {
|
||||
var res = {};
|
||||
for (var key in obj) {
|
||||
res[key] = f.call(opt_obj, obj[key], key, obj);
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Calls a function for each element in an object/map/hash. If any
|
||||
* call returns true, returns true (without checking the rest). If
|
||||
* all calls return false, returns false.
|
||||
*
|
||||
* @param {Object<K,V>} obj The object to check.
|
||||
* @param {function(this:T,V,?,Object<K,V>):boolean} f The function to
|
||||
* call for every element. This function
|
||||
* takes 3 arguments (the element, the index and the object) and should
|
||||
* return a boolean.
|
||||
* @param {T=} opt_obj This is used as the 'this' object within f.
|
||||
* @return {boolean} true if any element passes the test.
|
||||
* @template T,K,V
|
||||
*/
|
||||
goog.object.some = function(obj, f, opt_obj) {
|
||||
for (var key in obj) {
|
||||
if (f.call(opt_obj, obj[key], key, obj)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Calls a function for each element in an object/map/hash. If
|
||||
* all calls return true, returns true. If any call returns false, returns
|
||||
* false at this point and does not continue to check the remaining elements.
|
||||
*
|
||||
* @param {Object<K,V>} obj The object to check.
|
||||
* @param {?function(this:T,V,?,Object<K,V>):boolean} f The function to
|
||||
* call for every element. This function
|
||||
* takes 3 arguments (the element, the index and the object) and should
|
||||
* return a boolean.
|
||||
* @param {T=} opt_obj This is used as the 'this' object within f.
|
||||
* @return {boolean} false if any element fails the test.
|
||||
* @template T,K,V
|
||||
*/
|
||||
goog.object.every = function(obj, f, opt_obj) {
|
||||
for (var key in obj) {
|
||||
if (!f.call(opt_obj, obj[key], key, obj)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the number of key-value pairs in the object map.
|
||||
*
|
||||
* @param {Object} obj The object for which to get the number of key-value
|
||||
* pairs.
|
||||
* @return {number} The number of key-value pairs in the object map.
|
||||
*/
|
||||
goog.object.getCount = function(obj) {
|
||||
// JS1.5 has __count__ but it has been deprecated so it raises a warning...
|
||||
// in other words do not use. Also __count__ only includes the fields on the
|
||||
// actual object and not in the prototype chain.
|
||||
var rv = 0;
|
||||
for (var key in obj) {
|
||||
rv++;
|
||||
}
|
||||
return rv;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns one key from the object map, if any exists.
|
||||
* For map literals the returned key will be the first one in most of the
|
||||
* browsers (a know exception is Konqueror).
|
||||
*
|
||||
* @param {Object} obj The object to pick a key from.
|
||||
* @return {string|undefined} The key or undefined if the object is empty.
|
||||
*/
|
||||
goog.object.getAnyKey = function(obj) {
|
||||
for (var key in obj) {
|
||||
return key;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns one value from the object map, if any exists.
|
||||
* For map literals the returned value will be the first one in most of the
|
||||
* browsers (a know exception is Konqueror).
|
||||
*
|
||||
* @param {Object<K,V>} obj The object to pick a value from.
|
||||
* @return {V|undefined} The value or undefined if the object is empty.
|
||||
* @template K,V
|
||||
*/
|
||||
goog.object.getAnyValue = function(obj) {
|
||||
for (var key in obj) {
|
||||
return obj[key];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Whether the object/hash/map contains the given object as a value.
|
||||
* An alias for goog.object.containsValue(obj, val).
|
||||
*
|
||||
* @param {Object<K,V>} obj The object in which to look for val.
|
||||
* @param {V} val The object for which to check.
|
||||
* @return {boolean} true if val is present.
|
||||
* @template K,V
|
||||
*/
|
||||
goog.object.contains = function(obj, val) {
|
||||
return goog.object.containsValue(obj, val);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the values of the object/map/hash.
|
||||
*
|
||||
* @param {Object<K,V>} obj The object from which to get the values.
|
||||
* @return {!Array<V>} The values in the object/map/hash.
|
||||
* @template K,V
|
||||
*/
|
||||
goog.object.getValues = function(obj) {
|
||||
var res = [];
|
||||
var i = 0;
|
||||
for (var key in obj) {
|
||||
res[i++] = obj[key];
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the keys of the object/map/hash.
|
||||
*
|
||||
* @param {Object} obj The object from which to get the keys.
|
||||
* @return {!Array<string>} Array of property keys.
|
||||
*/
|
||||
goog.object.getKeys = function(obj) {
|
||||
var res = [];
|
||||
var i = 0;
|
||||
for (var key in obj) {
|
||||
res[i++] = key;
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Get a value from an object multiple levels deep. This is useful for
|
||||
* pulling values from deeply nested objects, such as JSON responses.
|
||||
* Example usage: getValueByKeys(jsonObj, 'foo', 'entries', 3)
|
||||
*
|
||||
* @param {!Object} obj An object to get the value from. Can be array-like.
|
||||
* @param {...(string|number|!Array<number|string>)} var_args A number of keys
|
||||
* (as strings, or numbers, for array-like objects). Can also be
|
||||
* specified as a single array of keys.
|
||||
* @return {*} The resulting value. If, at any point, the value for a key
|
||||
* is undefined, returns undefined.
|
||||
*/
|
||||
goog.object.getValueByKeys = function(obj, var_args) {
|
||||
var isArrayLike = goog.isArrayLike(var_args);
|
||||
var keys = isArrayLike ? var_args : arguments;
|
||||
|
||||
// Start with the 2nd parameter for the variable parameters syntax.
|
||||
for (var i = isArrayLike ? 0 : 1; i < keys.length; i++) {
|
||||
obj = obj[keys[i]];
|
||||
if (!goog.isDef(obj)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return obj;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Whether the object/map/hash contains the given key.
|
||||
*
|
||||
* @param {Object} obj The object in which to look for key.
|
||||
* @param {*} key The key for which to check.
|
||||
* @return {boolean} true If the map contains the key.
|
||||
*/
|
||||
goog.object.containsKey = function(obj, key) {
|
||||
return key in obj;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Whether the object/map/hash contains the given value. This is O(n).
|
||||
*
|
||||
* @param {Object<K,V>} obj The object in which to look for val.
|
||||
* @param {V} val The value for which to check.
|
||||
* @return {boolean} true If the map contains the value.
|
||||
* @template K,V
|
||||
*/
|
||||
goog.object.containsValue = function(obj, val) {
|
||||
for (var key in obj) {
|
||||
if (obj[key] == val) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Searches an object for an element that satisfies the given condition and
|
||||
* returns its key.
|
||||
* @param {Object<K,V>} obj The object to search in.
|
||||
* @param {function(this:T,V,string,Object<K,V>):boolean} f The
|
||||
* function to call for every element. Takes 3 arguments (the value,
|
||||
* the key and the object) and should return a boolean.
|
||||
* @param {T=} opt_this An optional "this" context for the function.
|
||||
* @return {string|undefined} The key of an element for which the function
|
||||
* returns true or undefined if no such element is found.
|
||||
* @template T,K,V
|
||||
*/
|
||||
goog.object.findKey = function(obj, f, opt_this) {
|
||||
for (var key in obj) {
|
||||
if (f.call(opt_this, obj[key], key, obj)) {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Searches an object for an element that satisfies the given condition and
|
||||
* returns its value.
|
||||
* @param {Object<K,V>} obj The object to search in.
|
||||
* @param {function(this:T,V,string,Object<K,V>):boolean} f The function
|
||||
* to call for every element. Takes 3 arguments (the value, the key
|
||||
* and the object) and should return a boolean.
|
||||
* @param {T=} opt_this An optional "this" context for the function.
|
||||
* @return {V} The value of an element for which the function returns true or
|
||||
* undefined if no such element is found.
|
||||
* @template T,K,V
|
||||
*/
|
||||
goog.object.findValue = function(obj, f, opt_this) {
|
||||
var key = goog.object.findKey(obj, f, opt_this);
|
||||
return key && obj[key];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Whether the object/map/hash is empty.
|
||||
*
|
||||
* @param {Object} obj The object to test.
|
||||
* @return {boolean} true if obj is empty.
|
||||
*/
|
||||
goog.object.isEmpty = function(obj) {
|
||||
for (var key in obj) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Removes all key value pairs from the object/map/hash.
|
||||
*
|
||||
* @param {Object} obj The object to clear.
|
||||
*/
|
||||
goog.object.clear = function(obj) {
|
||||
for (var i in obj) {
|
||||
delete obj[i];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Removes a key-value pair based on the key.
|
||||
*
|
||||
* @param {Object} obj The object from which to remove the key.
|
||||
* @param {*} key The key to remove.
|
||||
* @return {boolean} Whether an element was removed.
|
||||
*/
|
||||
goog.object.remove = function(obj, key) {
|
||||
var rv;
|
||||
if ((rv = key in obj)) {
|
||||
delete obj[key];
|
||||
}
|
||||
return rv;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds a key-value pair to the object. Throws an exception if the key is
|
||||
* already in use. Use set if you want to change an existing pair.
|
||||
*
|
||||
* @param {Object<K,V>} obj The object to which to add the key-value pair.
|
||||
* @param {string} key The key to add.
|
||||
* @param {V} val The value to add.
|
||||
* @template K,V
|
||||
*/
|
||||
goog.object.add = function(obj, key, val) {
|
||||
if (key in obj) {
|
||||
throw Error('The object already contains the key "' + key + '"');
|
||||
}
|
||||
goog.object.set(obj, key, val);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the value for the given key.
|
||||
*
|
||||
* @param {Object<K,V>} obj The object from which to get the value.
|
||||
* @param {string} key The key for which to get the value.
|
||||
* @param {R=} opt_val The value to return if no item is found for the given
|
||||
* key (default is undefined).
|
||||
* @return {V|R|undefined} The value for the given key.
|
||||
* @template K,V,R
|
||||
*/
|
||||
goog.object.get = function(obj, key, opt_val) {
|
||||
if (key in obj) {
|
||||
return obj[key];
|
||||
}
|
||||
return opt_val;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds a key-value pair to the object/map/hash.
|
||||
*
|
||||
* @param {Object<K,V>} obj The object to which to add the key-value pair.
|
||||
* @param {string} key The key to add.
|
||||
* @param {V} value The value to add.
|
||||
* @template K,V
|
||||
*/
|
||||
goog.object.set = function(obj, key, value) {
|
||||
obj[key] = value;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds a key-value pair to the object/map/hash if it doesn't exist yet.
|
||||
*
|
||||
* @param {Object<K,V>} obj The object to which to add the key-value pair.
|
||||
* @param {string} key The key to add.
|
||||
* @param {V} value The value to add if the key wasn't present.
|
||||
* @return {V} The value of the entry at the end of the function.
|
||||
* @template K,V
|
||||
*/
|
||||
goog.object.setIfUndefined = function(obj, key, value) {
|
||||
return key in obj ? obj[key] : (obj[key] = value);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets a key and value to an object if the key is not set. The value will be
|
||||
* the return value of the given function. If the key already exists, the
|
||||
* object will not be changed and the function will not be called (the function
|
||||
* will be lazily evaluated -- only called if necessary).
|
||||
*
|
||||
* This function is particularly useful for use with a map used a as a cache.
|
||||
*
|
||||
* @param {!Object<K,V>} obj The object to which to add the key-value pair.
|
||||
* @param {string} key The key to add.
|
||||
* @param {function():V} f The value to add if the key wasn't present.
|
||||
* @return {V} The value of the entry at the end of the function.
|
||||
* @template K,V
|
||||
*/
|
||||
goog.object.setWithReturnValueIfNotSet = function(obj, key, f) {
|
||||
if (key in obj) {
|
||||
return obj[key];
|
||||
}
|
||||
|
||||
var val = f();
|
||||
obj[key] = val;
|
||||
return val;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Compares two objects for equality using === on the values.
|
||||
*
|
||||
* @param {!Object<K,V>} a
|
||||
* @param {!Object<K,V>} b
|
||||
* @return {boolean}
|
||||
* @template K,V
|
||||
*/
|
||||
goog.object.equals = function(a, b) {
|
||||
for (var k in a) {
|
||||
if (!(k in b) || a[k] !== b[k]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (var k in b) {
|
||||
if (!(k in a)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Does a flat clone of the object.
|
||||
*
|
||||
* @param {Object<K,V>} obj Object to clone.
|
||||
* @return {!Object<K,V>} Clone of the input object.
|
||||
* @template K,V
|
||||
*/
|
||||
goog.object.clone = function(obj) {
|
||||
// We cannot use the prototype trick because a lot of methods depend on where
|
||||
// the actual key is set.
|
||||
|
||||
var res = {};
|
||||
for (var key in obj) {
|
||||
res[key] = obj[key];
|
||||
}
|
||||
return res;
|
||||
// We could also use goog.mixin but I wanted this to be independent from that.
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Clones a value. The input may be an Object, Array, or basic type. Objects and
|
||||
* arrays will be cloned recursively.
|
||||
*
|
||||
* WARNINGS:
|
||||
* <code>goog.object.unsafeClone</code> does not detect reference loops. Objects
|
||||
* that refer to themselves will cause infinite recursion.
|
||||
*
|
||||
* <code>goog.object.unsafeClone</code> is unaware of unique identifiers, and
|
||||
* copies UIDs created by <code>getUid</code> into cloned results.
|
||||
*
|
||||
* @param {*} obj The value to clone.
|
||||
* @return {*} A clone of the input value.
|
||||
*/
|
||||
goog.object.unsafeClone = function(obj) {
|
||||
var type = goog.typeOf(obj);
|
||||
if (type == 'object' || type == 'array') {
|
||||
if (obj.clone) {
|
||||
return obj.clone();
|
||||
}
|
||||
var clone = type == 'array' ? [] : {};
|
||||
for (var key in obj) {
|
||||
clone[key] = goog.object.unsafeClone(obj[key]);
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
|
||||
return obj;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns a new object in which all the keys and values are interchanged
|
||||
* (keys become values and values become keys). If multiple keys map to the
|
||||
* same value, the chosen transposed value is implementation-dependent.
|
||||
*
|
||||
* @param {Object} obj The object to transpose.
|
||||
* @return {!Object} The transposed object.
|
||||
*/
|
||||
goog.object.transpose = function(obj) {
|
||||
var transposed = {};
|
||||
for (var key in obj) {
|
||||
transposed[obj[key]] = key;
|
||||
}
|
||||
return transposed;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The names of the fields that are defined on Object.prototype.
|
||||
* @type {Array<string>}
|
||||
* @private
|
||||
*/
|
||||
goog.object.PROTOTYPE_FIELDS_ = [
|
||||
'constructor',
|
||||
'hasOwnProperty',
|
||||
'isPrototypeOf',
|
||||
'propertyIsEnumerable',
|
||||
'toLocaleString',
|
||||
'toString',
|
||||
'valueOf'
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* Extends an object with another object.
|
||||
* This operates 'in-place'; it does not create a new Object.
|
||||
*
|
||||
* Example:
|
||||
* var o = {};
|
||||
* goog.object.extend(o, {a: 0, b: 1});
|
||||
* o; // {a: 0, b: 1}
|
||||
* goog.object.extend(o, {b: 2, c: 3});
|
||||
* o; // {a: 0, b: 2, c: 3}
|
||||
*
|
||||
* @param {Object} target The object to modify. Existing properties will be
|
||||
* overwritten if they are also present in one of the objects in
|
||||
* {@code var_args}.
|
||||
* @param {...Object} var_args The objects from which values will be copied.
|
||||
*/
|
||||
goog.object.extend = function(target, var_args) {
|
||||
var key, source;
|
||||
for (var i = 1; i < arguments.length; i++) {
|
||||
source = arguments[i];
|
||||
for (key in source) {
|
||||
target[key] = source[key];
|
||||
}
|
||||
|
||||
// For IE the for-in-loop does not contain any properties that are not
|
||||
// enumerable on the prototype object (for example isPrototypeOf from
|
||||
// Object.prototype) and it will also not include 'replace' on objects that
|
||||
// extend String and change 'replace' (not that it is common for anyone to
|
||||
// extend anything except Object).
|
||||
|
||||
for (var j = 0; j < goog.object.PROTOTYPE_FIELDS_.length; j++) {
|
||||
key = goog.object.PROTOTYPE_FIELDS_[j];
|
||||
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
||||
target[key] = source[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new object built from the key-value pairs provided as arguments.
|
||||
* @param {...*} var_args If only one argument is provided and it is an array
|
||||
* then this is used as the arguments, otherwise even arguments are used as
|
||||
* the property names and odd arguments are used as the property values.
|
||||
* @return {!Object} The new object.
|
||||
* @throws {Error} If there are uneven number of arguments or there is only one
|
||||
* non array argument.
|
||||
*/
|
||||
goog.object.create = function(var_args) {
|
||||
var argLength = arguments.length;
|
||||
if (argLength == 1 && goog.isArray(arguments[0])) {
|
||||
return goog.object.create.apply(null, arguments[0]);
|
||||
}
|
||||
|
||||
if (argLength % 2) {
|
||||
throw Error('Uneven number of arguments');
|
||||
}
|
||||
|
||||
var rv = {};
|
||||
for (var i = 0; i < argLength; i += 2) {
|
||||
rv[arguments[i]] = arguments[i + 1];
|
||||
}
|
||||
return rv;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new object where the property names come from the arguments but
|
||||
* the value is always set to true
|
||||
* @param {...*} var_args If only one argument is provided and it is an array
|
||||
* then this is used as the arguments, otherwise the arguments are used
|
||||
* as the property names.
|
||||
* @return {!Object} The new object.
|
||||
*/
|
||||
goog.object.createSet = function(var_args) {
|
||||
var argLength = arguments.length;
|
||||
if (argLength == 1 && goog.isArray(arguments[0])) {
|
||||
return goog.object.createSet.apply(null, arguments[0]);
|
||||
}
|
||||
|
||||
var rv = {};
|
||||
for (var i = 0; i < argLength; i++) {
|
||||
rv[arguments[i]] = true;
|
||||
}
|
||||
return rv;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates an immutable view of the underlying object, if the browser
|
||||
* supports immutable objects.
|
||||
*
|
||||
* In default mode, writes to this view will fail silently. In strict mode,
|
||||
* they will throw an error.
|
||||
*
|
||||
* @param {!Object<K,V>} obj An object.
|
||||
* @return {!Object<K,V>} An immutable view of that object, or the
|
||||
* original object if this browser does not support immutables.
|
||||
* @template K,V
|
||||
*/
|
||||
goog.object.createImmutableView = function(obj) {
|
||||
var result = obj;
|
||||
if (Object.isFrozen && !Object.isFrozen(obj)) {
|
||||
result = Object.create(obj);
|
||||
Object.freeze(result);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {!Object} obj An object.
|
||||
* @return {boolean} Whether this is an immutable view of the object.
|
||||
*/
|
||||
goog.object.isImmutableView = function(obj) {
|
||||
return !!Object.isFrozen && Object.isFrozen(obj);
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!--
|
||||
Copyright 2006 The Closure Library Authors. All Rights Reserved.
|
||||
|
||||
Use of this source code is governed by the Apache License, Version 2.0.
|
||||
See the COPYING file for details.
|
||||
-->
|
||||
<head>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<title>
|
||||
Closure Unit Tests - goog.object
|
||||
</title>
|
||||
<script src="../base.js">
|
||||
</script>
|
||||
<script>
|
||||
goog.require('goog.objectTest');
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,530 @@
|
||||
// Copyright 2006 The Closure Library Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
goog.provide('goog.objectTest');
|
||||
goog.setTestOnly('goog.objectTest');
|
||||
|
||||
goog.require('goog.functions');
|
||||
goog.require('goog.object');
|
||||
goog.require('goog.testing.jsunit');
|
||||
goog.require('goog.testing.recordFunction');
|
||||
|
||||
function stringifyObject(m) {
|
||||
var keys = goog.object.getKeys(m);
|
||||
var s = '';
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
s += keys[i] + goog.object.get(m, keys[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
function getObject() {
|
||||
return {
|
||||
a: 0,
|
||||
b: 1,
|
||||
c: 2,
|
||||
d: 3
|
||||
};
|
||||
}
|
||||
|
||||
function testKeys() {
|
||||
var m = getObject();
|
||||
assertEquals('getKeys, The keys should be a,b,c',
|
||||
'a,b,c,d',
|
||||
goog.object.getKeys(m).join(','));
|
||||
}
|
||||
|
||||
function testValues() {
|
||||
var m = getObject();
|
||||
assertEquals('getValues, The values should be 0,1,2',
|
||||
'0,1,2,3', goog.object.getValues(m).join(','));
|
||||
}
|
||||
|
||||
function testGetAnyKey() {
|
||||
var m = getObject();
|
||||
assertTrue('getAnyKey, The key should be a,b,c or d',
|
||||
goog.object.getAnyKey(m) in m);
|
||||
assertUndefined('getAnyKey, The key should be undefined',
|
||||
goog.object.getAnyKey({}));
|
||||
}
|
||||
|
||||
function testGetAnyValue() {
|
||||
var m = getObject();
|
||||
assertTrue('getAnyValue, The value should be 0,1,2 or 3',
|
||||
goog.object.containsValue(m, goog.object.getAnyValue(m)));
|
||||
assertUndefined('getAnyValue, The value should be undefined',
|
||||
goog.object.getAnyValue({}));
|
||||
}
|
||||
|
||||
function testContainsKey() {
|
||||
var m = getObject();
|
||||
assertTrue("containsKey, Should contain the 'a' key",
|
||||
goog.object.containsKey(m, 'a'));
|
||||
assertFalse("containsKey, Should not contain the 'e' key",
|
||||
goog.object.containsKey(m, 'e'));
|
||||
}
|
||||
|
||||
function testContainsValue() {
|
||||
var m = getObject();
|
||||
assertTrue('containsValue, Should contain the value 0',
|
||||
goog.object.containsValue(m, 0));
|
||||
assertFalse('containsValue, Should not contain the value 4',
|
||||
goog.object.containsValue(m, 4));
|
||||
assertTrue('isEmpty, The map should not be empty', !goog.object.isEmpty(m));
|
||||
}
|
||||
|
||||
function testFindKey() {
|
||||
var dict = {'a': 1, 'b': 2, 'c': 3, 'd': 4};
|
||||
var key = goog.object.findKey(dict, function(v, k, d) {
|
||||
assertEquals('valid 3rd argument', dict, d);
|
||||
assertTrue('valid 1st argument', goog.object.containsValue(d, v));
|
||||
assertTrue('valid 2nd argument', k in d);
|
||||
return v % 3 == 0;
|
||||
});
|
||||
assertEquals('key "c" found', 'c', key);
|
||||
|
||||
var pred = function(value) {
|
||||
return value > 5;
|
||||
};
|
||||
assertUndefined('no match', goog.object.findKey(dict, pred));
|
||||
}
|
||||
|
||||
function testFindValue() {
|
||||
var dict = {'a': 1, 'b': 2, 'c': 3, 'd': 4};
|
||||
var value = goog.object.findValue(dict, function(v, k, d) {
|
||||
assertEquals('valid 3rd argument', dict, d);
|
||||
assertTrue('valid 1st argument', goog.object.containsValue(d, v));
|
||||
assertTrue('valid 2nd argument', k in d);
|
||||
return k.toUpperCase() == 'C';
|
||||
});
|
||||
assertEquals('value 3 found', 3, value);
|
||||
|
||||
var pred = function(value, key) {
|
||||
return key > 'd';
|
||||
};
|
||||
assertUndefined('no match', goog.object.findValue(dict, pred));
|
||||
}
|
||||
|
||||
function testClear() {
|
||||
var m = getObject();
|
||||
goog.object.clear(m);
|
||||
assertTrue('cleared so it should be empty', goog.object.isEmpty(m));
|
||||
assertFalse("cleared so it should not contain 'a' key",
|
||||
goog.object.containsKey(m, 'a'));
|
||||
}
|
||||
|
||||
function testClone() {
|
||||
var m = getObject();
|
||||
var m2 = goog.object.clone(m);
|
||||
assertFalse('clone so it should not be empty', goog.object.isEmpty(m2));
|
||||
assertTrue("clone so it should contain 'c' key",
|
||||
goog.object.containsKey(m2, 'c'));
|
||||
}
|
||||
|
||||
function testUnsafeClonePrimitive() {
|
||||
assertEquals('cloning a primitive should return an equal primitive',
|
||||
5, goog.object.unsafeClone(5));
|
||||
}
|
||||
|
||||
function testUnsafeCloneObjectThatHasACloneMethod() {
|
||||
var original = {
|
||||
name: 'original',
|
||||
clone: goog.functions.constant({name: 'clone'})
|
||||
};
|
||||
|
||||
var clone = goog.object.unsafeClone(original);
|
||||
assertEquals('original', original.name);
|
||||
assertEquals('clone', clone.name);
|
||||
}
|
||||
|
||||
function testUnsafeCloneFlatObject() {
|
||||
var original = {a: 1, b: 2, c: 3};
|
||||
var clone = goog.object.unsafeClone(original);
|
||||
assertNotEquals(original, clone);
|
||||
assertObjectEquals(original, clone);
|
||||
}
|
||||
|
||||
function testUnsafeCloneDeepObject() {
|
||||
var original = {
|
||||
a: 1,
|
||||
b: {c: 2, d: 3},
|
||||
e: {f: {g: 4, h: 5}}
|
||||
};
|
||||
var clone = goog.object.unsafeClone(original);
|
||||
|
||||
assertNotEquals(original, clone);
|
||||
assertNotEquals(original.b, clone.b);
|
||||
assertNotEquals(original.e, clone.e);
|
||||
|
||||
assertEquals(1, clone.a);
|
||||
assertEquals(2, clone.b.c);
|
||||
assertEquals(3, clone.b.d);
|
||||
assertEquals(4, clone.e.f.g);
|
||||
assertEquals(5, clone.e.f.h);
|
||||
}
|
||||
|
||||
function testUnsafeCloneFunctions() {
|
||||
var original = {
|
||||
f: goog.functions.constant('hi')
|
||||
};
|
||||
var clone = goog.object.unsafeClone(original);
|
||||
|
||||
assertNotEquals(original, clone);
|
||||
assertEquals('hi', clone.f());
|
||||
assertEquals(original.f, clone.f);
|
||||
}
|
||||
|
||||
function testForEach() {
|
||||
var m = getObject();
|
||||
var s = '';
|
||||
goog.object.forEach(m, function(val, key, m2) {
|
||||
assertNotUndefined(key);
|
||||
assertEquals(m, m2);
|
||||
s += key + val;
|
||||
});
|
||||
assertEquals(s, 'a0b1c2d3');
|
||||
}
|
||||
|
||||
function testFilter() {
|
||||
var m = getObject();
|
||||
|
||||
var m2 = goog.object.filter(m, function(val, key, m3) {
|
||||
assertNotUndefined(key);
|
||||
assertEquals(m, m3);
|
||||
return val > 1;
|
||||
});
|
||||
assertEquals(stringifyObject(m2), 'c2d3');
|
||||
}
|
||||
|
||||
|
||||
function testMap() {
|
||||
var m = getObject();
|
||||
var m2 = goog.object.map(m, function(val, key, m3) {
|
||||
assertNotUndefined(key);
|
||||
assertEquals(m, m3);
|
||||
return val * val;
|
||||
});
|
||||
assertEquals(stringifyObject(m2), 'a0b1c4d9');
|
||||
}
|
||||
|
||||
function testSome() {
|
||||
var m = getObject();
|
||||
var b = goog.object.some(m, function(val, key, m2) {
|
||||
assertNotUndefined(key);
|
||||
assertEquals(m, m2);
|
||||
return val > 1;
|
||||
});
|
||||
assertTrue(b);
|
||||
var b = goog.object.some(m, function(val, key, m2) {
|
||||
assertNotUndefined(key);
|
||||
assertEquals(m, m2);
|
||||
return val > 100;
|
||||
});
|
||||
assertFalse(b);
|
||||
}
|
||||
|
||||
function testEvery() {
|
||||
var m = getObject();
|
||||
var b = goog.object.every(m, function(val, key, m2) {
|
||||
assertNotUndefined(key);
|
||||
assertEquals(m, m2);
|
||||
return val >= 0;
|
||||
});
|
||||
assertTrue(b);
|
||||
b = goog.object.every(m, function(val, key, m2) {
|
||||
assertNotUndefined(key);
|
||||
assertEquals(m, m2);
|
||||
return val > 1;
|
||||
});
|
||||
assertFalse(b);
|
||||
}
|
||||
|
||||
function testContains() {
|
||||
var m = getObject();
|
||||
assertTrue(goog.object.contains(m, 3));
|
||||
assertFalse(goog.object.contains(m, 4));
|
||||
}
|
||||
|
||||
function testObjectProperties() {
|
||||
var m = {};
|
||||
|
||||
goog.object.set(m, 'toString', 'once');
|
||||
goog.object.set(m, 'valueOf', 'upon');
|
||||
goog.object.set(m, 'eval', 'a');
|
||||
goog.object.set(m, 'toSource', 'midnight');
|
||||
goog.object.set(m, 'prototype', 'dreary');
|
||||
goog.object.set(m, 'hasOwnProperty', 'dark');
|
||||
|
||||
assertEquals(goog.object.get(m, 'toString'), 'once');
|
||||
assertEquals(goog.object.get(m, 'valueOf'), 'upon');
|
||||
assertEquals(goog.object.get(m, 'eval'), 'a');
|
||||
assertEquals(goog.object.get(m, 'toSource'), 'midnight');
|
||||
assertEquals(goog.object.get(m, 'prototype'), 'dreary');
|
||||
assertEquals(goog.object.get(m, 'hasOwnProperty'), 'dark');
|
||||
}
|
||||
|
||||
function testSetDefault() {
|
||||
var dict = {};
|
||||
assertEquals(1, goog.object.setIfUndefined(dict, 'a', 1));
|
||||
assertEquals(1, dict['a']);
|
||||
assertEquals(1, goog.object.setIfUndefined(dict, 'a', 2));
|
||||
assertEquals(1, dict['a']);
|
||||
}
|
||||
|
||||
function createRecordedGetFoo() {
|
||||
return goog.testing.recordFunction(goog.functions.constant('foo'));
|
||||
}
|
||||
|
||||
function testSetWithReturnValueNotSet_KeyIsSet() {
|
||||
var f = createRecordedGetFoo();
|
||||
var obj = {};
|
||||
obj['key'] = 'bar';
|
||||
assertEquals(
|
||||
'bar',
|
||||
goog.object.setWithReturnValueIfNotSet(obj, 'key', f));
|
||||
f.assertCallCount(0);
|
||||
}
|
||||
|
||||
function testSetWithReturnValueNotSet_KeyIsNotSet() {
|
||||
var f = createRecordedGetFoo();
|
||||
var obj = {};
|
||||
assertEquals(
|
||||
'foo',
|
||||
goog.object.setWithReturnValueIfNotSet(obj, 'key', f));
|
||||
f.assertCallCount(1);
|
||||
}
|
||||
|
||||
function testSetWithReturnValueNotSet_KeySetValueIsUndefined() {
|
||||
var f = createRecordedGetFoo();
|
||||
var obj = {};
|
||||
obj['key'] = undefined;
|
||||
assertEquals(
|
||||
undefined,
|
||||
goog.object.setWithReturnValueIfNotSet(obj, 'key', f));
|
||||
f.assertCallCount(0);
|
||||
}
|
||||
|
||||
function testTranspose() {
|
||||
var m = getObject();
|
||||
var b = goog.object.transpose(m);
|
||||
assertEquals('a', b[0]);
|
||||
assertEquals('b', b[1]);
|
||||
assertEquals('c', b[2]);
|
||||
assertEquals('d', b[3]);
|
||||
}
|
||||
|
||||
function testExtend() {
|
||||
var o = {};
|
||||
var o2 = {a: 0, b: 1};
|
||||
goog.object.extend(o, o2);
|
||||
assertEquals(0, o.a);
|
||||
assertEquals(1, o.b);
|
||||
assertTrue('a' in o);
|
||||
assertTrue('b' in o);
|
||||
|
||||
o2 = {c: 2};
|
||||
goog.object.extend(o, o2);
|
||||
assertEquals(2, o.c);
|
||||
assertTrue('c' in o);
|
||||
|
||||
o2 = {c: 3};
|
||||
goog.object.extend(o, o2);
|
||||
assertEquals(3, o.c);
|
||||
assertTrue('c' in o);
|
||||
|
||||
o = {};
|
||||
o2 = {c: 2};
|
||||
var o3 = {c: 3};
|
||||
goog.object.extend(o, o2, o3);
|
||||
assertEquals(3, o.c);
|
||||
assertTrue('c' in o);
|
||||
|
||||
o = {};
|
||||
o2 = {a: 0, b: 1};
|
||||
o3 = {c: 2, d: 3};
|
||||
goog.object.extend(o, o2, o3);
|
||||
assertEquals(0, o.a);
|
||||
assertEquals(1, o.b);
|
||||
assertEquals(2, o.c);
|
||||
assertEquals(3, o.d);
|
||||
assertTrue('a' in o);
|
||||
assertTrue('b' in o);
|
||||
assertTrue('c' in o);
|
||||
assertTrue('d' in o);
|
||||
|
||||
o = {};
|
||||
o2 = {
|
||||
'constructor': 0,
|
||||
'hasOwnProperty': 1,
|
||||
'isPrototypeOf': 2,
|
||||
'propertyIsEnumerable': 3,
|
||||
'toLocaleString': 4,
|
||||
'toString': 5,
|
||||
'valueOf': 6
|
||||
};
|
||||
goog.object.extend(o, o2);
|
||||
assertEquals(0, o['constructor']);
|
||||
assertEquals(1, o['hasOwnProperty']);
|
||||
assertEquals(2, o['isPrototypeOf']);
|
||||
assertEquals(3, o['propertyIsEnumerable']);
|
||||
assertEquals(4, o['toLocaleString']);
|
||||
assertEquals(5, o['toString']);
|
||||
assertEquals(6, o['valueOf']);
|
||||
assertTrue('constructor' in o);
|
||||
assertTrue('hasOwnProperty' in o);
|
||||
assertTrue('isPrototypeOf' in o);
|
||||
assertTrue('propertyIsEnumerable' in o);
|
||||
assertTrue('toLocaleString' in o);
|
||||
assertTrue('toString' in o);
|
||||
assertTrue('valueOf' in o);
|
||||
}
|
||||
|
||||
function testCreate() {
|
||||
assertObjectEquals('With multiple arguments',
|
||||
{a: 0, b: 1}, goog.object.create('a', 0, 'b', 1));
|
||||
assertObjectEquals('With an array argument',
|
||||
{a: 0, b: 1}, goog.object.create(['a', 0, 'b', 1]));
|
||||
|
||||
assertObjectEquals('With no arguments',
|
||||
{}, goog.object.create());
|
||||
assertObjectEquals('With an ampty array argument',
|
||||
{}, goog.object.create([]));
|
||||
|
||||
assertThrows('Should throw due to uneven arguments', function() {
|
||||
goog.object.create('a');
|
||||
});
|
||||
assertThrows('Should throw due to uneven arguments', function() {
|
||||
goog.object.create('a', 0, 'b');
|
||||
});
|
||||
assertThrows('Should throw due to uneven length array', function() {
|
||||
goog.object.create(['a']);
|
||||
});
|
||||
assertThrows('Should throw due to uneven length array', function() {
|
||||
goog.object.create(['a', 0, 'b']);
|
||||
});
|
||||
}
|
||||
|
||||
function testCreateSet() {
|
||||
assertObjectEquals('With multiple arguments',
|
||||
{a: true, b: true}, goog.object.createSet('a', 'b'));
|
||||
assertObjectEquals('With an array argument',
|
||||
{a: true, b: true}, goog.object.createSet(['a', 'b']));
|
||||
|
||||
assertObjectEquals('With no arguments',
|
||||
{}, goog.object.createSet());
|
||||
assertObjectEquals('With an ampty array argument',
|
||||
{}, goog.object.createSet([]));
|
||||
}
|
||||
|
||||
function createTestDeepObject() {
|
||||
var obj = {};
|
||||
obj.a = {};
|
||||
obj.a.b = {};
|
||||
obj.a.b.c = {};
|
||||
obj.a.b.c.fooArr = [5, 6, 7, 8];
|
||||
obj.a.b.c.knownNull = null;
|
||||
return obj;
|
||||
}
|
||||
|
||||
function testGetValueByKeys() {
|
||||
var obj = createTestDeepObject();
|
||||
assertEquals(obj, goog.object.getValueByKeys(obj));
|
||||
assertEquals(obj.a, goog.object.getValueByKeys(obj, 'a'));
|
||||
assertEquals(obj.a.b, goog.object.getValueByKeys(obj, 'a', 'b'));
|
||||
assertEquals(obj.a.b.c, goog.object.getValueByKeys(obj, 'a', 'b', 'c'));
|
||||
assertEquals(obj.a.b.c.d,
|
||||
goog.object.getValueByKeys(obj, 'a', 'b', 'c', 'd'));
|
||||
assertEquals(8, goog.object.getValueByKeys(obj, 'a', 'b', 'c', 'fooArr', 3));
|
||||
assertNull(goog.object.getValueByKeys(obj, 'a', 'b', 'c', 'knownNull'));
|
||||
assertUndefined(goog.object.getValueByKeys(obj, 'e', 'f', 'g'));
|
||||
}
|
||||
|
||||
function testGetValueByKeysArraySyntax() {
|
||||
var obj = createTestDeepObject();
|
||||
assertEquals(obj, goog.object.getValueByKeys(obj, []));
|
||||
assertEquals(obj.a, goog.object.getValueByKeys(obj, ['a']));
|
||||
|
||||
assertEquals(obj.a.b, goog.object.getValueByKeys(obj, ['a', 'b']));
|
||||
assertEquals(obj.a.b.c, goog.object.getValueByKeys(obj, ['a', 'b', 'c']));
|
||||
assertEquals(obj.a.b.c.d,
|
||||
goog.object.getValueByKeys(obj, ['a', 'b', 'c', 'd']));
|
||||
assertEquals(8,
|
||||
goog.object.getValueByKeys(obj, ['a', 'b', 'c', 'fooArr', 3]));
|
||||
assertNull(goog.object.getValueByKeys(obj, ['a', 'b', 'c', 'knownNull']));
|
||||
assertUndefined(goog.object.getValueByKeys(obj, 'e', 'f', 'g'));
|
||||
}
|
||||
|
||||
function testImmutableView() {
|
||||
if (!Object.isFrozen) {
|
||||
return;
|
||||
}
|
||||
var x = {propA: 3};
|
||||
var y = goog.object.createImmutableView(x);
|
||||
x.propA = 4;
|
||||
x.propB = 6;
|
||||
y.propA = 5;
|
||||
y.propB = 7;
|
||||
assertEquals(4, x.propA);
|
||||
assertEquals(6, x.propB);
|
||||
assertFalse(goog.object.isImmutableView(x));
|
||||
|
||||
assertEquals(4, y.propA);
|
||||
assertEquals(6, y.propB);
|
||||
assertTrue(goog.object.isImmutableView(y));
|
||||
|
||||
assertFalse('x and y should be different references', x == y);
|
||||
assertTrue(
|
||||
'createImmutableView should not create a new view of an immutable object',
|
||||
y == goog.object.createImmutableView(y));
|
||||
}
|
||||
|
||||
function testImmutableViewStrict() {
|
||||
'use strict';
|
||||
|
||||
// IE9 supports isFrozen, but does not support strict mode. Exit early if we
|
||||
// are not actually running in strict mode.
|
||||
var isStrict = (function() { return !this; })();
|
||||
|
||||
if (!Object.isFrozen || !isStrict) {
|
||||
return;
|
||||
}
|
||||
var x = {propA: 3};
|
||||
var y = goog.object.createImmutableView(x);
|
||||
assertThrows(function() {
|
||||
y.propA = 4;
|
||||
});
|
||||
assertThrows(function() {
|
||||
y.propB = 4;
|
||||
});
|
||||
}
|
||||
|
||||
function testEmptyObjectsAreEqual() {
|
||||
assertTrue(goog.object.equals({}, {}));
|
||||
}
|
||||
|
||||
function testObjectsWithDifferentKeysAreUnequal() {
|
||||
assertFalse(goog.object.equals({'a': 1}, {'b': 1}));
|
||||
}
|
||||
|
||||
function testObjectsWithDifferentValuesAreUnequal() {
|
||||
assertFalse(goog.object.equals({'a': 1}, {'a': 2}));
|
||||
}
|
||||
|
||||
function testObjectsWithSameKeysAndValuesAreEqual() {
|
||||
assertTrue(goog.object.equals({'a': 1}, {'a': 1}));
|
||||
}
|
||||
|
||||
function testObjectsWithSameKeysInDifferentOrderAreEqual() {
|
||||
assertTrue(goog.object.equals({'a': 1, 'b': 2}, {'b': 2, 'a': 1}));
|
||||
}
|
||||
Reference in New Issue
Block a user