| // Copyright 2007 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 |
| * Efficient implementation of DataNode API. |
| * |
| * The implementation consists of three concrete classes for modelling |
| * DataNodes with different characteristics: FastDataNode, |
| * FastPrimitiveDataNode and FastListNode. |
| * |
| * FastDataNode is for bean-like or map-like objects that consists of |
| * key/value mappings and where the primary access pattern is by key. |
| * |
| * FastPrimitiveDataNode wraps primitives like strings, boolean, and numbers. |
| * |
| * FastListNode is for array-like data nodes. It also supports key-based |
| * lookups if the data nodes have an "id" property or if child nodes are |
| * explicitly added by name. It is most efficient if these features are not |
| * used. |
| * |
| * FastDataNodes can be constructed from JSON-like objects via the function |
| * goog.ds.FastDataNode.fromJs. |
| |
| */ |
| |
| goog.provide('goog.ds.AbstractFastDataNode'); |
| goog.provide('goog.ds.FastDataNode'); |
| goog.provide('goog.ds.FastListNode'); |
| goog.provide('goog.ds.PrimitiveFastDataNode'); |
| |
| goog.require('goog.ds.DataManager'); |
| goog.require('goog.ds.DataNodeList'); |
| goog.require('goog.ds.EmptyNodeList'); |
| goog.require('goog.string'); |
| |
| /* |
| * Implementation note: In order to reduce the number of objects, |
| * FastDataNode stores its key/value mappings directly in the FastDataNode |
| * object iself (instead of a separate map). To make this work we have to |
| * sure that there are no name clashes with other attribute names used by |
| * FastDataNode (like dataName and parent). This is especially difficult in |
| * the light of automatic renaming by the JavaScript compiler. For this reason, |
| * all internal attributes start with "__" so that they are not renamed |
| * by the compiler. |
| */ |
| |
| /** |
| * Creates a new abstract data node. |
| * @param {string} dataName Name of the datanode. |
| * @param {goog.ds.DataNode=} opt_parent Parent of this data node. |
| * @constructor |
| * @extends {goog.ds.DataNodeList} |
| */ |
| // TODO(arv): Use interfaces when available. |
| goog.ds.AbstractFastDataNode = function(dataName, opt_parent) { |
| if (!dataName) { |
| throw Error('Cannot create a fast data node without a data name'); |
| } |
| this['__dataName'] = dataName; |
| this['__parent'] = opt_parent; |
| }; |
| |
| |
| /** |
| * Return the name of this data node. |
| * @return {string} Name of this data noden. |
| * @override |
| */ |
| goog.ds.AbstractFastDataNode.prototype.getDataName = function() { |
| return this['__dataName']; |
| }; |
| |
| |
| /** |
| * Set the name of this data node. |
| * @param {string} value Name. |
| * @override |
| */ |
| goog.ds.AbstractFastDataNode.prototype.setDataName = function(value) { |
| this['__dataName'] = value; |
| }; |
| |
| |
| /** |
| * Get the path leading to this data node. |
| * @return {string} Data path. |
| * @override |
| */ |
| goog.ds.AbstractFastDataNode.prototype.getDataPath = function() { |
| var parentPath; |
| if (this['__parent']) { |
| parentPath = this['__parent'].getDataPath() + goog.ds.STR_PATH_SEPARATOR; |
| } else { |
| parentPath = ''; |
| } |
| return parentPath + this.getDataName(); |
| }; |
| |
| |
| |
| /** |
| * Creates a new fast data node, using the properties of root. |
| * @param {Object} root JSON-like object to initialize data node from. |
| * @param {string} dataName Name of this data node. |
| * @param {goog.ds.DataNode=} opt_parent Parent of this data node. |
| * @extends {goog.ds.AbstractFastDataNode} |
| * @constructor |
| */ |
| goog.ds.FastDataNode = function(root, dataName, opt_parent) { |
| goog.ds.AbstractFastDataNode.call(this, dataName, opt_parent); |
| this.extendWith(root); |
| }; |
| goog.inherits(goog.ds.FastDataNode, goog.ds.AbstractFastDataNode); |
| |
| |
| /** |
| * Add all attributes of object to this data node. |
| * @param {Object} object Object to add attributes from. |
| * @protected |
| */ |
| goog.ds.FastDataNode.prototype.extendWith = function(object) { |
| for (var key in object) { |
| this[key] = object[key]; |
| } |
| }; |
| |
| |
| /** |
| * Creates a new FastDataNode structure initialized from object. This will |
| * return an instance of the most suitable sub-class of FastDataNode. |
| * |
| * You should not modify object after creating a fast data node from it |
| * or assume that changing object changes the data node. Doing so results |
| * in undefined behaviour. |
| * |
| * @param {Object|number|boolean|string} object Object to initialize data |
| * node from. |
| * @param {string} dataName Name of data node. |
| * @param {goog.ds.DataNode=} opt_parent Parent of data node. |
| * @return {!goog.ds.AbstractFastDataNode} Data node representing object. |
| */ |
| goog.ds.FastDataNode.fromJs = function(object, dataName, opt_parent) { |
| if (goog.isArray(object)) { |
| return new goog.ds.FastListNode(object, dataName, opt_parent); |
| } else if (goog.isObject(object)) { |
| return new goog.ds.FastDataNode(object, dataName, opt_parent); |
| } else { |
| return new goog.ds.PrimitiveFastDataNode(object || !!object, |
| dataName, |
| opt_parent); |
| } |
| }; |
| |
| |
| /** |
| * Static instance of an empty list. |
| * @type {!goog.ds.EmptyNodeList} |
| * @private |
| */ |
| goog.ds.FastDataNode.emptyList_ = new goog.ds.EmptyNodeList(); |
| |
| |
| /** |
| * Not supported for normal FastDataNodes. |
| * @param {*} value Value to set data node to. |
| * @override |
| */ |
| goog.ds.FastDataNode.prototype.set = function(value) { |
| throw new Error('Not implemented yet'); |
| }; |
| |
| |
| /** @override */ |
| goog.ds.FastDataNode.prototype.getChildNodes = function(opt_selector) { |
| if (!opt_selector || opt_selector == goog.ds.STR_ALL_CHILDREN_SELECTOR) { |
| return this; |
| } else if (opt_selector.indexOf(goog.ds.STR_WILDCARD) == -1) { |
| var child = this.getChildNode(opt_selector); |
| return child ? new goog.ds.FastListNode([child], '') : |
| new goog.ds.EmptyNodeList(); |
| } else { |
| throw Error('Unsupported selector: ' + opt_selector); |
| } |
| }; |
| |
| |
| /** |
| * Makes sure that a named child is wrapped in a data node structure. |
| * @param {string} name Name of child to wrap. |
| * @private |
| */ |
| goog.ds.FastDataNode.prototype.wrapChild_ = function(name) { |
| var child = this[name]; |
| if (child != null && !child.getDataName) { |
| this[name] = goog.ds.FastDataNode.fromJs(this[name], name, this); |
| } |
| }; |
| |
| |
| /** |
| * Get a child node by name. |
| * @param {string} name Name of child node. |
| * @param {boolean=} opt_create Whether to create the child if it does not |
| * exist. |
| * @return {goog.ds.DataNode} Child node. |
| * @override |
| */ |
| goog.ds.FastDataNode.prototype.getChildNode = function(name, opt_create) { |
| this.wrapChild_(name); |
| // this[name] always is a data node object, so using "||" is fine. |
| var child = this[name] || null; |
| if (child == null && opt_create) { |
| child = new goog.ds.FastDataNode({}, name, this); |
| this[name] = child; |
| } |
| return child; |
| }; |
| |
| |
| /** |
| * Sets a child node. Creates the child if it does not exist. |
| * |
| * Calling this function makes any child nodes previously obtained for name |
| * invalid. You should not use these child nodes but instead obtain a new |
| * instance by calling getChildNode. |
| * |
| * @override |
| */ |
| goog.ds.FastDataNode.prototype.setChildNode = function(name, value) { |
| if (value != null) { |
| this[name] = value; |
| } else { |
| delete this[name]; |
| } |
| goog.ds.DataManager.getInstance().fireDataChange(this.getDataPath() + |
| goog.ds.STR_PATH_SEPARATOR + name); |
| return null; |
| }; |
| |
| |
| /** |
| * Returns the value of a child node. By using this method you can avoid |
| * the need to create PrimitiveFastData nodes. |
| * @param {string} name Name of child node. |
| * @return {Object} Value of child node. |
| * @override |
| */ |
| goog.ds.FastDataNode.prototype.getChildNodeValue = function(name) { |
| var child = this[name]; |
| if (child != null) { |
| return (child.getDataName ? child.get() : child); |
| } else { |
| return null; |
| } |
| }; |
| |
| |
| /** |
| * Returns whether this data node is a list. Always returns false for |
| * instances of FastDataNode but may return true for subclasses. |
| * @return {boolean} Whether this data node is array-like. |
| * @override |
| */ |
| goog.ds.FastDataNode.prototype.isList = function() { |
| return false; |
| }; |
| |
| |
| /** |
| * Returns a javascript object representation of this data node. You should |
| * not modify the object returned by this function. |
| * @return {!Object} Javascript object representation of this data node. |
| */ |
| goog.ds.FastDataNode.prototype.getJsObject = function() { |
| var result = {}; |
| for (var key in this) { |
| if (!goog.string.startsWith(key, '__') && !goog.isFunction(this[key])) { |
| result[key] = (this[key]['__dataName'] ? this[key].getJsObject() : |
| this[key]); |
| } |
| } |
| return result; |
| }; |
| |
| |
| /** |
| * Creates a deep copy of this data node. |
| * @return {goog.ds.FastDataNode} Clone of this data node. |
| */ |
| goog.ds.FastDataNode.prototype.clone = function() { |
| return /** @type {!goog.ds.FastDataNode} */(goog.ds.FastDataNode.fromJs( |
| this.getJsObject(), this.getDataName())); |
| }; |
| |
| |
| /* |
| * Implementation of goog.ds.DataNodeList for FastDataNode. |
| */ |
| |
| |
| /** |
| * Adds a child to this data node. |
| * @param {goog.ds.DataNode} value Child node to add. |
| * @override |
| */ |
| goog.ds.FastDataNode.prototype.add = function(value) { |
| this.setChildNode(value.getDataName(), value); |
| }; |
| |
| |
| /** |
| * Gets the value of this data node (if called without opt_key) or |
| * gets a child node (if called with opt_key). |
| * @param {string=} opt_key Name of child node. |
| * @return {*} This data node or a child node. |
| * @override |
| */ |
| goog.ds.FastDataNode.prototype.get = function(opt_key) { |
| if (!goog.isDef(opt_key)) { |
| // if there is no key, DataNode#get was called |
| return this; |
| } else { |
| return this.getChildNode(opt_key); |
| } |
| }; |
| |
| |
| /** |
| * Gets a child node by index. This method has a complexity of O(n) where |
| * n is the number of children. If you need a faster implementation of this |
| * method, you should use goog.ds.FastListNode. |
| * @param {number} index Index of child node (starting from 0). |
| * @return {goog.ds.DataNode} Child node at specified index. |
| * @override |
| */ |
| goog.ds.FastDataNode.prototype.getByIndex = function(index) { |
| var i = 0; |
| for (var key in this) { |
| if (!goog.string.startsWith(key, '__') && !goog.isFunction(this[key])) { |
| if (i == index) { |
| this.wrapChild_(key); |
| return this[key]; |
| } |
| ++i; |
| } |
| } |
| return null; |
| }; |
| |
| |
| /** |
| * Gets the number of child nodes. This method has a complexity of O(n) where |
| * n is the number of children. If you need a faster implementation of this |
| * method, you should use goog.ds.FastListNode. |
| * @return {number} Number of child nodes. |
| * @override |
| */ |
| goog.ds.FastDataNode.prototype.getCount = function() { |
| var count = 0; |
| for (var key in this) { |
| if (!goog.string.startsWith(key, '__') && !goog.isFunction(this[key])) { |
| ++count; |
| } |
| } |
| // maybe cache this? |
| return count; |
| }; |
| |
| |
| /** |
| * Sets a child node. |
| * @param {string} name Name of child node. |
| * @param {Object} value Value of child node. |
| * @override |
| */ |
| goog.ds.FastDataNode.prototype.setNode = function(name, value) { |
| this.setChildNode(name, value); |
| }; |
| |
| |
| /** |
| * Removes a child node. |
| * @override |
| */ |
| goog.ds.FastDataNode.prototype.removeNode = function(name) { |
| delete this[name]; |
| return false; |
| }; |
| |
| |
| |
| /** |
| * Creates a new data node wrapping a primitive value. |
| * @param {number|boolean|string} value Value the value to wrap. |
| * @param {string} dataName name Name of this data node. |
| * @param {goog.ds.DataNode=} opt_parent Parent of this data node. |
| * @extends {goog.ds.AbstractFastDataNode} |
| * @constructor |
| * @final |
| */ |
| goog.ds.PrimitiveFastDataNode = function(value, dataName, opt_parent) { |
| this.value_ = value; |
| goog.ds.AbstractFastDataNode.call(this, dataName, opt_parent); |
| }; |
| goog.inherits(goog.ds.PrimitiveFastDataNode, goog.ds.AbstractFastDataNode); |
| |
| |
| /** |
| * Returns the value of this data node. |
| * @return {(boolean|number|string)} Value of this data node. |
| * @override |
| */ |
| goog.ds.PrimitiveFastDataNode.prototype.get = function() { |
| return this.value_; |
| }; |
| |
| |
| /** |
| * Sets this data node to a new value. |
| * @param {*} value Value to set data node to. |
| * @override |
| */ |
| goog.ds.PrimitiveFastDataNode.prototype.set = function(value) { |
| if (goog.isArray(value) || goog.isObject(value)) { |
| throw Error('can only set PrimitiveFastDataNode to primitive values'); |
| } |
| this.value_ = value; |
| goog.ds.DataManager.getInstance().fireDataChange(this.getDataPath()); |
| }; |
| |
| |
| /** |
| * Returns child nodes of this data node. Always returns an unmodifiable, |
| * empty list. |
| * @return {!goog.ds.DataNodeList} (Empty) list of child nodes. |
| * @override |
| */ |
| goog.ds.PrimitiveFastDataNode.prototype.getChildNodes = function() { |
| return goog.ds.FastDataNode.emptyList_; |
| }; |
| |
| |
| /** |
| * Get a child node by name. Always returns null. |
| * @param {string} name Name of child node. |
| * @return {goog.ds.DataNode} Child node. |
| * @override |
| */ |
| goog.ds.PrimitiveFastDataNode.prototype.getChildNode = function(name) { |
| return null; |
| }; |
| |
| |
| /** |
| * Returns the value of a child node. Always returns null. |
| * @param {string} name Name of child node. |
| * @return {Object} Value of child node. |
| * @override |
| */ |
| goog.ds.PrimitiveFastDataNode.prototype.getChildNodeValue = function(name) { |
| return null; |
| }; |
| |
| |
| /** |
| * Not supported by primitive data nodes. |
| * @param {string} name Name of child node. |
| * @param {Object} value Value of child node. |
| * @override |
| */ |
| goog.ds.PrimitiveFastDataNode.prototype.setChildNode = |
| function(name, value) { |
| throw Error('Cannot set a child node for a PrimitiveFastDataNode'); |
| }; |
| |
| |
| /** |
| * Returns whether this data node is a list. Always returns false for |
| * instances of PrimitiveFastDataNode. |
| * @return {boolean} Whether this data node is array-like. |
| * @override |
| */ |
| goog.ds.PrimitiveFastDataNode.prototype.isList = function() { |
| return false; |
| }; |
| |
| |
| /** |
| * Returns a javascript object representation of this data node. You should |
| * not modify the object returned by this function. |
| * @return {*} Javascript object representation of this data node. |
| */ |
| goog.ds.PrimitiveFastDataNode.prototype.getJsObject = function() { |
| return this.value_; |
| }; |
| |
| |
| /** |
| * Creates a new list node from an array. |
| * @param {Array<?>} values values hold by this list node. |
| * @param {string} dataName name of this node. |
| * @param {goog.ds.DataNode=} opt_parent parent of this node. |
| * @extends {goog.ds.AbstractFastDataNode} |
| * @constructor |
| * @final |
| */ |
| // TODO(arv): Use interfaces when available. This implements DataNodeList |
| // as well. |
| goog.ds.FastListNode = function(values, dataName, opt_parent) { |
| this.values_ = []; |
| for (var i = 0; i < values.length; ++i) { |
| var name = values[i].id || ('[' + i + ']'); |
| this.values_.push(goog.ds.FastDataNode.fromJs(values[i], name, this)); |
| if (values[i].id) { |
| if (!this.map_) { |
| this.map_ = {}; |
| } |
| this.map_[values[i].id] = i; |
| } |
| } |
| goog.ds.AbstractFastDataNode.call(this, dataName, opt_parent); |
| }; |
| goog.inherits(goog.ds.FastListNode, goog.ds.AbstractFastDataNode); |
| |
| |
| /** |
| * Not supported for FastListNodes. |
| * @param {*} value Value to set data node to. |
| * @override |
| */ |
| goog.ds.FastListNode.prototype.set = function(value) { |
| throw Error('Cannot set a FastListNode to a new value'); |
| }; |
| |
| |
| /** |
| * Returns child nodes of this data node. Currently, only supports |
| * returning all children. |
| * @return {!goog.ds.DataNodeList} List of child nodes. |
| * @override |
| */ |
| goog.ds.FastListNode.prototype.getChildNodes = function() { |
| return this; |
| }; |
| |
| |
| /** |
| * Get a child node by name. |
| * @param {string} key Name of child node. |
| * @param {boolean=} opt_create Whether to create the child if it does not |
| * exist. |
| * @return {goog.ds.DataNode} Child node. |
| * @override |
| */ |
| goog.ds.FastListNode.prototype.getChildNode = function(key, opt_create) { |
| var index = this.getKeyAsNumber_(key); |
| if (index == null && this.map_) { |
| index = this.map_[key]; |
| } |
| if (index != null && this.values_[index]) { |
| return this.values_[index]; |
| } else if (opt_create) { |
| this.setChildNode(key, {}); |
| return this.getChildNode(key); |
| } else { |
| return null; |
| } |
| }; |
| |
| |
| /** |
| * Returns the value of a child node. |
| * @param {string} key Name of child node. |
| * @return {*} Value of child node. |
| * @override |
| */ |
| goog.ds.FastListNode.prototype.getChildNodeValue = function(key) { |
| var child = this.getChildNode(key); |
| return (child ? child.get() : null); |
| }; |
| |
| |
| /** |
| * Tries to interpret key as a numeric index enclosed by square brakcets. |
| * @param {string} key Key that should be interpreted as a number. |
| * @return {?number} Numeric index or null if key is not of the form |
| * described above. |
| * @private |
| */ |
| goog.ds.FastListNode.prototype.getKeyAsNumber_ = function(key) { |
| if (key.charAt(0) == '[' && key.charAt(key.length - 1) == ']') { |
| return Number(key.substring(1, key.length - 1)); |
| } else { |
| return null; |
| } |
| }; |
| |
| |
| /** |
| * Sets a child node. Creates the child if it does not exist. To set |
| * children at a certain index, use a key of the form '[index]'. Note, that |
| * you can only set values at existing numeric indices. To add a new node |
| * to this list, you have to use the add method. |
| * |
| * Calling this function makes any child nodes previously obtained for name |
| * invalid. You should not use these child nodes but instead obtain a new |
| * instance by calling getChildNode. |
| * |
| * @override |
| */ |
| goog.ds.FastListNode.prototype.setChildNode = function(key, value) { |
| var count = this.values_.length; |
| if (value != null) { |
| if (!value.getDataName) { |
| value = goog.ds.FastDataNode.fromJs(value, key, this); |
| } |
| var index = this.getKeyAsNumber_(key); |
| if (index != null) { |
| if (index < 0 || index >= this.values_.length) { |
| throw Error('List index out of bounds: ' + index); |
| } |
| this.values_[key] = value; |
| } else { |
| if (!this.map_) { |
| this.map_ = {}; |
| } |
| this.values_.push(value); |
| this.map_[key] = this.values_.length - 1; |
| } |
| } else { |
| this.removeNode(key); |
| } |
| var dm = goog.ds.DataManager.getInstance(); |
| dm.fireDataChange(this.getDataPath() + goog.ds.STR_PATH_SEPARATOR + key); |
| if (this.values_.length != count) { |
| this.listSizeChanged_(); |
| } |
| return null; |
| }; |
| |
| |
| /** |
| * Fire data changes that are appropriate when the size of this list changes. |
| * Should be called whenever the list size has changed. |
| * @private |
| */ |
| goog.ds.FastListNode.prototype.listSizeChanged_ = function() { |
| var dm = goog.ds.DataManager.getInstance(); |
| dm.fireDataChange(this.getDataPath()); |
| dm.fireDataChange(this.getDataPath() + goog.ds.STR_PATH_SEPARATOR + |
| 'count()'); |
| }; |
| |
| |
| /** |
| * Returns whether this data node is a list. Always returns true. |
| * @return {boolean} Whether this data node is array-like. |
| * @override |
| */ |
| goog.ds.FastListNode.prototype.isList = function() { |
| return true; |
| }; |
| |
| |
| /** |
| * Returns a javascript object representation of this data node. You should |
| * not modify the object returned by this function. |
| * @return {!Object} Javascript object representation of this data node. |
| */ |
| goog.ds.FastListNode.prototype.getJsObject = function() { |
| var result = []; |
| for (var i = 0; i < this.values_.length; ++i) { |
| result.push(this.values_[i].getJsObject()); |
| } |
| return result; |
| }; |
| |
| |
| /* |
| * Implementation of goog.ds.DataNodeList for FastListNode. |
| */ |
| |
| |
| /** |
| * Adds a child to this data node |
| * @param {goog.ds.DataNode} value Child node to add. |
| * @override |
| */ |
| goog.ds.FastListNode.prototype.add = function(value) { |
| if (!value.getDataName) { |
| value = goog.ds.FastDataNode.fromJs(value, |
| String('[' + (this.values_.length) + ']'), this); |
| } |
| this.values_.push(value); |
| var dm = goog.ds.DataManager.getInstance(); |
| dm.fireDataChange(this.getDataPath() + goog.ds.STR_PATH_SEPARATOR + |
| '[' + (this.values_.length - 1) + ']'); |
| this.listSizeChanged_(); |
| }; |
| |
| |
| /** |
| * Gets the value of this data node (if called without opt_key) or |
| * gets a child node (if called with opt_key). |
| * @param {string=} opt_key Name of child node. |
| * @return {Array|goog.ds.DataNode} Array of child nodes (if called without |
| * opt_key), or a named child node otherwise. |
| * @override |
| */ |
| goog.ds.FastListNode.prototype.get = function(opt_key) { |
| // if there are no arguments, DataNode.get was called |
| if (!goog.isDef(opt_key)) { |
| return this.values_; |
| } else { |
| return this.getChildNode(opt_key); |
| } |
| }; |
| |
| |
| /** |
| * Gets a child node by (numeric) index. |
| * @param {number} index Index of child node (starting from 0). |
| * @return {goog.ds.DataNode} Child node at specified index. |
| * @override |
| */ |
| goog.ds.FastListNode.prototype.getByIndex = function(index) { |
| var child = this.values_[index]; |
| return (child != null ? child : null); // never return undefined |
| }; |
| |
| |
| /** |
| * Gets the number of child nodes. |
| * @return {number} Number of child nodes. |
| * @override |
| */ |
| goog.ds.FastListNode.prototype.getCount = function() { |
| return this.values_.length; |
| }; |
| |
| |
| /** |
| * Sets a child node. |
| * @param {string} name Name of child node. |
| * @param {Object} value Value of child node. |
| * @override |
| */ |
| goog.ds.FastListNode.prototype.setNode = function(name, value) { |
| throw Error('Setting child nodes of a FastListNode is not implemented, yet'); |
| }; |
| |
| |
| /** |
| * Removes a child node. |
| * @override |
| */ |
| goog.ds.FastListNode.prototype.removeNode = function(name) { |
| var index = this.getKeyAsNumber_(name); |
| if (index == null && this.map_) { |
| index = this.map_[name]; |
| } |
| if (index != null) { |
| this.values_.splice(index, 1); |
| if (this.map_) { |
| var keyToDelete = null; |
| for (var key in this.map_) { |
| if (this.map_[key] == index) { |
| keyToDelete = key; |
| } else if (this.map_[key] > index) { |
| --this.map_[key]; |
| } |
| } |
| if (keyToDelete) { |
| delete this.map_[keyToDelete]; |
| } |
| } |
| var dm = goog.ds.DataManager.getInstance(); |
| dm.fireDataChange(this.getDataPath() + goog.ds.STR_PATH_SEPARATOR + |
| '[' + index + ']'); |
| this.listSizeChanged_(); |
| } |
| return false; |
| }; |
| |
| |
| /** |
| * Returns the index of a named child nodes. This method only works if |
| * this list uses mixed name/indexed lookup, i.e. if its child node have |
| * an 'id' attribute. |
| * @param {string} name Name of child node to determine index of. |
| * @return {number} Index of child node named name. |
| */ |
| goog.ds.FastListNode.prototype.indexOf = function(name) { |
| var index = this.getKeyAsNumber_(name); |
| if (index == null && this.map_) { |
| index = this.map_[name]; |
| } |
| if (index == null) { |
| throw Error('Cannot determine index for: ' + name); |
| } |
| return /** @type {number} */(index); |
| }; |