text: // 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');
  
  
  /**
   * Whether two values are not observably distinguishable. This
   * correctly detects that 0 is not the same as -0 and two NaNs are
   * practically equivalent.
   *
   * The implementation is as suggested by harmony:egal proposal.
   *
   * @param {*} v The first value to compare.
   * @param {*} v2 The second value to compare.
   * @return {boolean} Whether two values are not observably distinguishable.
   * @see http://wiki.ecmascript.org/doku.php?id=harmony:egal
   */
  goog.object.is = function(v, v2) {
    if (v === v2) {
      // 0 === -0, but they are not identical.
      // We need the cast because the compiler requires that v2 is a
      // number (although 1/v2 works with non-number). We cast to ? to
      // stop the compiler from type-checking this statement.
      return v !== 0 || 1 / v === 1 / /** @type {?} */ (v2);
    }
  
    // NaN is non-reflexive: NaN !== NaN, although they are identical.
    return v !== v && v2 !== v2;
  };
  
  
  /**
   * 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 value, the
   *     key 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(/** @type {?} */ (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 value, the key 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(/** @type {?} */ (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 value, the key 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(/** @type {?} */ (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 value, the key 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(/** @type {?} */ (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 value, the key 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(/** @type {?} */ (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) {
    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|!IArrayLike<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 obj !== null && 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(/** @type {?} */ (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 /** @type {!Object} */ (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 (obj !== null && 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 (obj !== null && 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 /** @type {!Object} */ (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;
  };
  
  
  /**
   * Returns a shallow 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 {T} obj The value to clone.
   * @return {T} A clone of the input value.
   * @template T
   */
  goog.object.unsafeClone = function(obj) {
    var type = goog.typeOf(obj);
    if (type == 'object' || type == 'array') {
      if (goog.isFunction(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);
  };
  
  
  /**
   * Get all properties names on a given Object regardless of enumerability.
   *
   * <p> If the browser does not support {@code Object.getOwnPropertyNames} nor
   * {@code Object.getPrototypeOf} then this is equivalent to using {@code
   * goog.object.getKeys}
   *
   * @param {?Object} obj The object to get the properties of.
   * @param {boolean=} opt_includeObjectPrototype Whether properties defined on
   *     {@code Object.prototype} should be included in the result.
   * @param {boolean=} opt_includeFunctionPrototype Whether properties defined on
   *     {@code Function.prototype} should be included in the result.
   * @return {!Array<string>}
   * @public
   */
  goog.object.getAllPropertyNames = function(
      obj, opt_includeObjectPrototype, opt_includeFunctionPrototype) {
    if (!obj) {
      return [];
    }
  
    // Naively use a for..in loop to get the property names if the browser doesn't
    // support any other APIs for getting it.
    if (!Object.getOwnPropertyNames || !Object.getPrototypeOf) {
      return goog.object.getKeys(obj);
    }
  
    var visitedSet = {};
  
    // Traverse the prototype chain and add all properties to the visited set.
    var proto = obj;
    while (proto &&
           (proto !== Object.prototype || !!opt_includeObjectPrototype) &&
           (proto !== Function.prototype || !!opt_includeFunctionPrototype)) {
      var names = Object.getOwnPropertyNames(proto);
      for (var i = 0; i < names.length; i++) {
        visitedSet[names[i]] = true;
      }
      proto = Object.getPrototypeOf(proto);
    }
  
    return goog.object.getKeys(visitedSet);
  };
  