http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/e2cad6e6/externs/GCL/externs/goog/datasource/fastdatanode.js ---------------------------------------------------------------------- diff --git a/externs/GCL/externs/goog/datasource/fastdatanode.js b/externs/GCL/externs/goog/datasource/fastdatanode.js new file mode 100644 index 0000000..c14750f --- /dev/null +++ b/externs/GCL/externs/goog/datasource/fastdatanode.js @@ -0,0 +1,814 @@ +// 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); +};
http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/e2cad6e6/externs/GCL/externs/goog/datasource/jsdatasource.js ---------------------------------------------------------------------- diff --git a/externs/GCL/externs/goog/datasource/jsdatasource.js b/externs/GCL/externs/goog/datasource/jsdatasource.js new file mode 100644 index 0000000..ccd469e --- /dev/null +++ b/externs/GCL/externs/goog/datasource/jsdatasource.js @@ -0,0 +1,462 @@ +// 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 An implementation of DataNode for wrapping JS data. + * + */ + + +goog.provide('goog.ds.JsDataSource'); +goog.provide('goog.ds.JsPropertyDataSource'); + +goog.require('goog.ds.BaseDataNode'); +goog.require('goog.ds.BasicNodeList'); +goog.require('goog.ds.DataManager'); +goog.require('goog.ds.DataNode'); +goog.require('goog.ds.EmptyNodeList'); +goog.require('goog.ds.LoadState'); + + +/** + * Data source whose backing is JavaScript data + * + * Names that are reserved for system use and shouldn't be used for data node + * names: eval, toSource, toString, unwatch, valueOf, watch. Behavior is + * undefined if these names are used. + * + * @param {Object} root The root JS node. + * @param {string} dataName The name of this node relative to the parent node. + * @param {Object=} opt_parent Optional parent of this JsDataSource. + * + * implements goog.ds.DataNode. + * @constructor + * @extends {goog.ds.DataNode} + */ +// TODO(arv): Use interfaces when available. +goog.ds.JsDataSource = function(root, dataName, opt_parent) { + this.parent_ = opt_parent; + this.dataName_ = dataName; + this.setRoot(root); +}; + + +/** + * The root JS object. Can be null. + * @type {*} + * @protected + * @suppress {underscore|visibility} + */ +goog.ds.JsDataSource.prototype.root_; + + +/** + * Sets the root JS object + * @param {Object} root The root JS object. Can be null. + * + * @protected + */ +goog.ds.JsDataSource.prototype.setRoot = function(root) { + this.root_ = root; + this.childNodeList_ = null; +}; + + +/** + * Set this data source to use list semantics. List data sources: + * - Are assumed to have child nodes of all of the same type of data + * - Fire data changes on the root node of the list whenever children + * are added or removed + * @param {?boolean} isList True to use list semantics. + * @private + */ +goog.ds.JsDataSource.prototype.setIsList_ = function(isList) { + this.isList_ = isList; +}; + + +/** @override */ +goog.ds.JsDataSource.prototype.get = function() { + return !goog.isObject(this.root_) ? this.root_ : this.getChildNodes(); +}; + + +/** + * Set the value of the node + * @param {*} value The new value of the node. + * @override + */ +goog.ds.JsDataSource.prototype.set = function(value) { + if (value && goog.isObject(this.root_)) { + throw Error('Can\'t set group nodes to new values yet'); + } + + if (this.parent_) { + this.parent_.root_[this.dataName_] = value; + } + this.root_ = value; + this.childNodeList_ = null; + + goog.ds.DataManager.getInstance().fireDataChange(this.getDataPath()); +}; + + +/** + * TODO(user) revisit lazy creation. + * @override + */ +goog.ds.JsDataSource.prototype.getChildNodes = function(opt_selector) { + if (!this.root_) { + return new goog.ds.EmptyNodeList(); + } + + if (!opt_selector || opt_selector == goog.ds.STR_ALL_CHILDREN_SELECTOR) { + this.createChildNodes_(false); + return this.childNodeList_; + } else if (opt_selector.indexOf(goog.ds.STR_WILDCARD) == -1) { + if (this.root_[opt_selector] != null) { + return new goog.ds.BasicNodeList([this.getChildNode(opt_selector)]); + } else { + return new goog.ds.EmptyNodeList(); + } + } else { + throw Error('Selector not supported yet (' + opt_selector + ')'); + } + +}; + + +/** + * Creates the DataNodeList with the child nodes for this element. + * Allows for only building list as needed. + * + * @param {boolean=} opt_force Whether to force recreating child nodes, + * defaults to false. + * @private + */ +goog.ds.JsDataSource.prototype.createChildNodes_ = function(opt_force) { + if (this.childNodeList_ && !opt_force) { + return; + } + + if (!goog.isObject(this.root_)) { + this.childNodeList_ = new goog.ds.EmptyNodeList(); + return; + } + + var childNodeList = new goog.ds.BasicNodeList(); + var newNode; + if (goog.isArray(this.root_)) { + var len = this.root_.length; + for (var i = 0; i < len; i++) { + // "id" is reserved node name that will map to a named child node + // TODO(user) Configurable logic for choosing id node + var node = this.root_[i]; + var id = node.id; + var name = id != null ? String(id) : '[' + i + ']'; + newNode = new goog.ds.JsDataSource(node, name, this); + childNodeList.add(newNode); + } + } else { + for (var name in this.root_) { + var obj = this.root_[name]; + // If the node is already a datasource, then add it. + if (obj.getDataName) { + childNodeList.add(obj); + } else if (!goog.isFunction(obj)) { + newNode = new goog.ds.JsDataSource(obj, name, this); + childNodeList.add(newNode); + } + } + } + this.childNodeList_ = childNodeList; +}; + + +/** + * Gets a named child node of the current node + * @param {string} name The node name. + * @param {boolean=} opt_canCreate If true, can create child node. + * @return {goog.ds.DataNode} The child node, or null if no node of + * this name exists. + * @override + */ +goog.ds.JsDataSource.prototype.getChildNode = function(name, opt_canCreate) { + if (!this.root_) { + return null; + } + var node = /** @type {goog.ds.DataNode} */ (this.getChildNodes().get(name)); + if (!node && opt_canCreate) { + var newObj = {}; + if (goog.isArray(this.root_)) { + newObj['id'] = name; + this.root_.push(newObj); + } else { + this.root_[name] = newObj; + } + node = new goog.ds.JsDataSource(newObj, name, this); + if (this.childNodeList_) { + this.childNodeList_.add(node); + } + } + return node; +}; + + +/** + * Gets the value of a child node + * @param {string} name The node name. + * @return {Object} The value of the node, or null if no value or the child + * node doesn't exist. + * @override + */ +goog.ds.JsDataSource.prototype.getChildNodeValue = function(name) { + if (this.childNodeList_) { + var node = this.getChildNodes().get(name); + return node ? node.get() : null; + } else if (this.root_) { + return this.root_[name]; + } else { + return null; + } +}; + + +/** + * Sets a named child node of the current node. + * If value is null, removes the child node. + * @param {string} name The node name. + * @param {Object} value The value to set, can be DataNode, object, + * property, or null. + * @return {Object} The child node, if set. + * @override + */ +goog.ds.JsDataSource.prototype.setChildNode = function(name, value) { + var removedPath = null; + var node = null; + var addedNode = false; + + // Set node to the DataNode to add - if the value isn't already a DataNode, + // creates a JsDataSource or JsPropertyDataSource wrapper + if (value != null) { + if (value.getDataName) { + // The value is a DataNode. We must update its parent. + node = value; + node.parent_ = this; + } else { + if (goog.isArray(value) || goog.isObject(value)) { + node = new goog.ds.JsDataSource(value, name, this); + } else { + node = new goog.ds.JsPropertyDataSource( + /** @type {goog.ds.DataNode} */ (this.root_), name, this); + } + } + } + + // This logic will get cleaner once we can remove the backing array / object + // and just rely on the childNodeList_. This is needed until dependent code + // is cleaned up. + // TODO(user) Remove backing array / object and just use childNodeList_ + + if (goog.isArray(this.root_)) { + // To remove by name, need to create a map of the child nodes by ID + this.createChildNodes_(); + var index = this.childNodeList_.indexOf(name); + if (value == null) { + // Remove the node + var nodeToRemove = this.childNodeList_.get(name); + if (nodeToRemove) { + removedPath = nodeToRemove.getDataPath(); + } + this.root_.splice(index, 1); + } else { + // Add the node + if (index) { + this.root_[index] = value; + } else { + this.root_.push(value); + } + } + if (index == null) { + addedNode = true; + } + this.childNodeList_.setNode(name, /** @type {goog.ds.DataNode} */ (node)); + } else if (goog.isObject(this.root_)) { + if (value == null) { + // Remove the node + this.createChildNodes_(); + var nodeToRemove = this.childNodeList_.get(name); + if (nodeToRemove) { + removedPath = nodeToRemove.getDataPath(); + } + delete this.root_[name]; + } else { + // Add the node + if (!this.root_[name]) { + addedNode = true; + } + this.root_[name] = value; + } + // Only need to update childNodeList_ if has been created already + if (this.childNodeList_) { + this.childNodeList_.setNode(name, /** @type {goog.ds.DataNode} */ (node)); + } + } + + // Fire the event that the node changed + var dm = goog.ds.DataManager.getInstance(); + if (node) { + dm.fireDataChange(node.getDataPath()); + if (addedNode && this.isList()) { + dm.fireDataChange(this.getDataPath()); + dm.fireDataChange(this.getDataPath() + '/count()'); + } + } else if (removedPath) { + dm.fireDataChange(removedPath); + if (this.isList()) { + dm.fireDataChange(this.getDataPath()); + dm.fireDataChange(this.getDataPath() + '/count()'); + } + } + return node; +}; + + +/** + * Get the name of the node relative to the parent node + * @return {string} The name of the node. + * @override + */ +goog.ds.JsDataSource.prototype.getDataName = function() { + return this.dataName_; +}; + + +/** + * Setthe name of the node relative to the parent node + * @param {string} dataName The name of the node. + * @override + */ +goog.ds.JsDataSource.prototype.setDataName = function(dataName) { + this.dataName_ = dataName; +}; + + +/** + * Gets the a qualified data path to this node + * @return {string} The data path. + * @override + */ +goog.ds.JsDataSource.prototype.getDataPath = function() { + var parentPath = ''; + if (this.parent_) { + parentPath = this.parent_.getDataPath() + goog.ds.STR_PATH_SEPARATOR; + } + + return parentPath + this.dataName_; +}; + + +/** + * Load or reload the backing data for this node + * @override + */ +goog.ds.JsDataSource.prototype.load = function() { + // Nothing to do +}; + + +/** + * Gets the state of the backing data for this node + * TODO(user) Discuss null value handling + * @return {goog.ds.LoadState} The state. + * @override + */ +goog.ds.JsDataSource.prototype.getLoadState = function() { + return (this.root_ == null) ? goog.ds.LoadState.NOT_LOADED : + goog.ds.LoadState.LOADED; +}; + + +/** + * Whether the value of this node is a homogeneous list of data + * @return {boolean} True if a list. + * @override + */ +goog.ds.JsDataSource.prototype.isList = function() { + return this.isList_ != null ? this.isList_ : goog.isArray(this.root_); +}; + + + +/** + * Data source for JavaScript properties that arent objects. Contains reference + * to parent object so that you can set the vaule + * + * @param {goog.ds.DataNode} parent Parent object. + * @param {string} dataName Name of this property. + * @param {goog.ds.DataNode=} opt_parentDataNode The parent data node. If + * omitted, assumes that the parent object is the parent data node. + * + * @constructor + * @extends {goog.ds.BaseDataNode} + * @final + */ +goog.ds.JsPropertyDataSource = function(parent, dataName, opt_parentDataNode) { + goog.ds.BaseDataNode.call(this); + this.dataName_ = dataName; + this.parent_ = parent; + this.parentDataNode_ = opt_parentDataNode || this.parent_; +}; +goog.inherits(goog.ds.JsPropertyDataSource, goog.ds.BaseDataNode); + + +/** + * Get the value of the node + * @return {Object} The value of the node, or null if no value. + */ +goog.ds.JsPropertyDataSource.prototype.get = function() { + return this.parent_[this.dataName_]; +}; + + +/** + * Set the value of the node + * @param {Object} value The new value of the node. + * @override + */ +goog.ds.JsPropertyDataSource.prototype.set = function(value) { + var oldValue = this.parent_[this.dataName_]; + this.parent_[this.dataName_] = value; + + if (oldValue != value) { + goog.ds.DataManager.getInstance().fireDataChange(this.getDataPath()); + } +}; + + +/** + * Get the name of the node relative to the parent node + * @return {string} The name of the node. + * @override + */ +goog.ds.JsPropertyDataSource.prototype.getDataName = function() { + return this.dataName_; +}; + + +/** @override */ +goog.ds.JsPropertyDataSource.prototype.getParent = function() { + return this.parentDataNode_; +}; http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/e2cad6e6/externs/GCL/externs/goog/datasource/jsondatasource.js ---------------------------------------------------------------------- diff --git a/externs/GCL/externs/goog/datasource/jsondatasource.js b/externs/GCL/externs/goog/datasource/jsondatasource.js new file mode 100644 index 0000000..1621002 --- /dev/null +++ b/externs/GCL/externs/goog/datasource/jsondatasource.js @@ -0,0 +1,153 @@ +// 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 Implementation of DataNode for wrapping JSON data. + * + */ + + +goog.provide('goog.ds.JsonDataSource'); + +goog.require('goog.Uri'); +goog.require('goog.dom'); +goog.require('goog.dom.TagName'); +goog.require('goog.ds.DataManager'); +goog.require('goog.ds.JsDataSource'); +goog.require('goog.ds.LoadState'); +goog.require('goog.ds.logger'); +goog.require('goog.log'); + + + +/** + * Data source whose backing is a JSON-like service, in which + * retreiving the resource specified by URL with the additional parameter + * callback. The resource retreived is executable JavaScript that + * makes a call to the named function with a JavaScript object literal + * as the only parameter. + * + * Example URI could be: + * http://www.google.com/data/search?q=monkey&callback=mycb + * which might return the JS: + * mycb({searchresults: + * [{uri: 'http://www.monkey.com', title: 'Site About Monkeys'}]}); + * + * TODO(user): Evaluate using goog.net.Jsonp here. + * + * A URI of an empty string will mean that no request is made + * and the data source will be a data source with no child nodes + * + * @param {string|goog.Uri} uri URI for the request. + * @param {string} name Name of the datasource. + * @param {string=} opt_callbackParamName The parameter name that is used to + * specify the callback. Defaults to 'callback'. + * + * @extends {goog.ds.JsDataSource} + * @constructor + * @final + */ +goog.ds.JsonDataSource = function(uri, name, opt_callbackParamName) { + goog.ds.JsDataSource.call(this, null, name, null); + if (uri) { + this.uri_ = new goog.Uri(uri); + } else { + this.uri_ = null; + } + + /** + * This is the callback parameter name that is added to the uri. + * @type {string} + * @private + */ + this.callbackParamName_ = opt_callbackParamName || 'callback'; + +}; +goog.inherits(goog.ds.JsonDataSource, goog.ds.JsDataSource); + + +/** + * Default load state is NOT_LOADED + * @private + */ +goog.ds.JsonDataSource.prototype.loadState_ = goog.ds.LoadState.NOT_LOADED; + + +/** + * Map of all data sources, needed for callbacks + * Doesn't work unless dataSources is exported (not renamed) + */ +goog.ds.JsonDataSource['dataSources'] = {}; + + +/** + * Load or reload the backing data for this node. + * Fires the JsonDataSource + * @override + */ +goog.ds.JsonDataSource.prototype.load = function() { + if (this.uri_) { + // NOTE: "dataSources" is expose above by name so that it will not be + // renamed. It should therefore be accessed via array notation here so + // that it also doesn't get renamed and stops the compiler from complaining + goog.ds.JsonDataSource['dataSources'][this.dataName_] = this; + goog.log.info(goog.ds.logger, 'Sending JS request for DataSource ' + + this.getDataName() + ' to ' + this.uri_); + + this.loadState_ = goog.ds.LoadState.LOADING; + + var uriToCall = new goog.Uri(this.uri_); + uriToCall.setParameterValue(this.callbackParamName_, + 'JsonReceive.' + this.dataName_); + + goog.global['JsonReceive'][this.dataName_] = + goog.bind(this.receiveData, this); + + var scriptEl = goog.dom.createDom(goog.dom.TagName.SCRIPT, + {'src': uriToCall}); + goog.dom.getElementsByTagNameAndClass( + goog.dom.TagName.HEAD)[0].appendChild(scriptEl); + } else { + this.root_ = {}; + this.loadState_ = goog.ds.LoadState.NOT_LOADED; + } +}; + + +/** + * Gets the state of the backing data for this node + * @return {goog.ds.LoadState} The state. + * @override + */ +goog.ds.JsonDataSource.prototype.getLoadState = function() { + return this.loadState_; +}; + + +/** + * Receives data from a Json request + * @param {Object} obj The JSON data. + */ +goog.ds.JsonDataSource.prototype.receiveData = function(obj) { + this.setRoot(obj); + this.loadState_ = goog.ds.LoadState.LOADED; + goog.ds.DataManager.getInstance().fireDataChange(this.getDataName()); +}; + + +/** +* Temp variable to hold callbacks +* until BUILD supports multiple externs.js files +*/ +goog.global['JsonReceive'] = {}; http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/e2cad6e6/externs/GCL/externs/goog/datasource/jsxmlhttpdatasource.js ---------------------------------------------------------------------- diff --git a/externs/GCL/externs/goog/datasource/jsxmlhttpdatasource.js b/externs/GCL/externs/goog/datasource/jsxmlhttpdatasource.js new file mode 100644 index 0000000..bd2a024 --- /dev/null +++ b/externs/GCL/externs/goog/datasource/jsxmlhttpdatasource.js @@ -0,0 +1,196 @@ +// 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 + * DataSource implementation that uses XMLHttpRequest as transport, with + * response as serialized JS object (not required to be JSON) that can + * be evaluated and set to a variable. + * + * Response can have unexecutable starting/ending text to prevent inclusion + * using <script src="..."> + * + */ + + +goog.provide('goog.ds.JsXmlHttpDataSource'); + +goog.require('goog.Uri'); +goog.require('goog.ds.DataManager'); +goog.require('goog.ds.FastDataNode'); +goog.require('goog.ds.LoadState'); +goog.require('goog.ds.logger'); +goog.require('goog.events'); +goog.require('goog.log'); +goog.require('goog.net.EventType'); +goog.require('goog.net.XhrIo'); + + + +/** + * Similar to JsonDataSource, with using XMLHttpRequest for transport + * Currently requires the result be a JS object that can be evaluated and + * set to a variable and doesn't require strict JSON notation. + * + * @param {(string|goog.Uri)} uri URI for the request. + * @param {string} name Name of the datasource. + * @param {string=} opt_startText Text to expect/strip before JS response. + * @param {string=} opt_endText Text to expect/strip after JS response. + * @param {boolean=} opt_usePost If true, use POST. Defaults to false (GET). + * + * @extends {goog.ds.FastDataNode} + * @constructor + * @final + */ +goog.ds.JsXmlHttpDataSource = function(uri, name, opt_startText, opt_endText, + opt_usePost) { + goog.ds.FastDataNode.call(this, {}, name, null); + if (uri) { + this.uri_ = new goog.Uri(uri); + this.xhr_ = new goog.net.XhrIo(); + this.usePost_ = !!opt_usePost; + + goog.events.listen(this.xhr_, goog.net.EventType.COMPLETE, + this.completed_, false, this); + } else { + this.uri_ = null; + } + this.startText_ = opt_startText; + this.endText_ = opt_endText; +}; +goog.inherits(goog.ds.JsXmlHttpDataSource, goog.ds.FastDataNode); + + +/** + * Delimiter for start of JSON data in response. + * null = starts at first character of response + * @type {string|undefined} + * @private + */ +goog.ds.JsXmlHttpDataSource.prototype.startText_; + + +/** + * Delimiter for end of JSON data in response. + * null = ends at last character of response + * @type {string|undefined} + * @private + */ +goog.ds.JsXmlHttpDataSource.prototype.endText_; + + +/** + * Gets the state of the backing data for this node + * @return {goog.ds.LoadState} The state. + * @override + */ +goog.ds.JsXmlHttpDataSource.prototype.getLoadState = function() { + return this.loadState_; +}; + + +/** + * Sets the request data. This can be used if it is required to + * send a specific body rather than build the body from the query + * parameters. Only used in POST requests. + * @param {string} data The data to send in the request body. + */ +goog.ds.JsXmlHttpDataSource.prototype.setQueryData = function(data) { + this.queryData_ = data; +}; + + +/** + * Load or reload the backing data for this node. + * Fires the JsonDataSource + * @override + */ +goog.ds.JsXmlHttpDataSource.prototype.load = function() { + goog.log.info(goog.ds.logger, 'Sending JS request for DataSource ' + + this.getDataName() + ' to ' + this.uri_); + + if (this.uri_) { + if (this.usePost_) { + + var queryData; + if (!this.queryData_) { + queryData = this.uri_.getQueryData().toString(); + } else { + queryData = this.queryData_; + } + + var uriNoQuery = this.uri_.clone(); + uriNoQuery.setQueryData(null); + this.xhr_.send(String(uriNoQuery), 'POST', queryData); + } else { + this.xhr_.send(String(this.uri_)); + } + } else { + this.loadState_ = goog.ds.LoadState.NOT_LOADED; + } +}; + + +/** + * Called on successful request. + * @private + */ +goog.ds.JsXmlHttpDataSource.prototype.success_ = function() { + goog.ds.DataManager.getInstance().fireDataChange(this.getDataName()); +}; + + +/** + * Completed callback. Loads data if successful, otherwise sets + * state to FAILED + * @param {goog.events.Event} e Event object, Xhr is target. + * @private + */ +goog.ds.JsXmlHttpDataSource.prototype.completed_ = function(e) { + if (this.xhr_.isSuccess()) { + goog.log.info(goog.ds.logger, + 'Got data for DataSource ' + this.getDataName()); + var text = this.xhr_.getResponseText(); + + // Look for start and end token and trim text + if (this.startText_) { + var startpos = text.indexOf(this.startText_); + text = text.substring(startpos + this.startText_.length); + } + if (this.endText_) { + var endpos = text.lastIndexOf(this.endText_); + text = text.substring(0, endpos); + } + + // Eval result + /** @preserveTry */ + try { + var jsonObj = /** @type {Object} */ (eval('[' + text + '][0]')); + this.extendWith(jsonObj); + this.loadState_ = goog.ds.LoadState.LOADED; + } + catch (ex) { + // Invalid JS + this.loadState_ = goog.ds.LoadState.FAILED; + goog.log.error(goog.ds.logger, 'Failed to parse data: ' + ex.message); + } + + // Call on a timer to avoid threading issues on IE. + goog.global.setTimeout(goog.bind(this.success_, this), 0); + } else { + goog.log.info(goog.ds.logger, 'Data retrieve failed for DataSource ' + + this.getDataName()); + this.loadState_ = goog.ds.LoadState.FAILED; + } +}; http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/e2cad6e6/externs/GCL/externs/goog/datasource/xmldatasource.js ---------------------------------------------------------------------- diff --git a/externs/GCL/externs/goog/datasource/xmldatasource.js b/externs/GCL/externs/goog/datasource/xmldatasource.js new file mode 100644 index 0000000..5327269 --- /dev/null +++ b/externs/GCL/externs/goog/datasource/xmldatasource.js @@ -0,0 +1,417 @@ +// 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 + * Implementations of DataNode for wrapping XML data. + * + */ + +goog.provide('goog.ds.XmlDataSource'); +goog.provide('goog.ds.XmlHttpDataSource'); + +goog.require('goog.Uri'); +goog.require('goog.dom.NodeType'); +goog.require('goog.dom.xml'); +goog.require('goog.ds.BasicNodeList'); +goog.require('goog.ds.DataManager'); +goog.require('goog.ds.DataNode'); +goog.require('goog.ds.LoadState'); +goog.require('goog.ds.logger'); +goog.require('goog.net.XhrIo'); +goog.require('goog.string'); + + + +/** + * Data source whose backing is an xml node + * + * @param {Node} node The XML node. Can be null. + * @param {goog.ds.XmlDataSource} parent Parent of XML element. Can be null. + * @param {string=} opt_name The name of this node relative to the parent node. + * + * @extends {goog.ds.DataNode} + * @constructor + */ +// TODO(arv): Use interfaces when available. +goog.ds.XmlDataSource = function(node, parent, opt_name) { + this.parent_ = parent; + this.dataName_ = opt_name || (node ? node.nodeName : ''); + this.setNode_(node); +}; + + +/** + * Constant to select XML attributes for getChildNodes + * @type {string} + * @private + */ +goog.ds.XmlDataSource.ATTRIBUTE_SELECTOR_ = '@*'; + + +/** + * Set the current root nodeof the data source. + * Can be an attribute node, text node, or element node + * @param {Node} node The node. Can be null. + * + * @private + */ +goog.ds.XmlDataSource.prototype.setNode_ = function(node) { + this.node_ = node; + if (node != null) { + switch (node.nodeType) { + case goog.dom.NodeType.ATTRIBUTE: + case goog.dom.NodeType.TEXT: + this.value_ = node.nodeValue; + break; + case goog.dom.NodeType.ELEMENT: + if (node.childNodes.length == 1 && + node.firstChild.nodeType == goog.dom.NodeType.TEXT) { + this.value_ = node.firstChild.nodeValue; + } + } + } +}; + + +/** + * Creates the DataNodeList with the child nodes for this element. + * Allows for only building list as needed. + * + * @private + */ +goog.ds.XmlDataSource.prototype.createChildNodes_ = function() { + if (this.childNodeList_) { + return; + } + var childNodeList = new goog.ds.BasicNodeList(); + if (this.node_ != null) { + var childNodes = this.node_.childNodes; + for (var i = 0, childNode; childNode = childNodes[i]; i++) { + if (childNode.nodeType != goog.dom.NodeType.TEXT || + !goog.ds.XmlDataSource.isEmptyTextNodeValue_(childNode.nodeValue)) { + var newNode = new goog.ds.XmlDataSource(childNode, + this, childNode.nodeName); + childNodeList.add(newNode); + } + } + } + this.childNodeList_ = childNodeList; +}; + + +/** + * Creates the DataNodeList with the attributes for the element + * Allows for only building list as needed. + * + * @private + */ +goog.ds.XmlDataSource.prototype.createAttributes_ = function() { + if (this.attributes_) { + return; + } + var attributes = new goog.ds.BasicNodeList(); + if (this.node_ != null && this.node_.attributes != null) { + var atts = this.node_.attributes; + for (var i = 0, att; att = atts[i]; i++) { + var newNode = new goog.ds.XmlDataSource(att, this, att.nodeName); + attributes.add(newNode); + } + } + this.attributes_ = attributes; +}; + + +/** + * Get the value of the node + * @return {Object} The value of the node, or null if no value. + * @override + */ +goog.ds.XmlDataSource.prototype.get = function() { + this.createChildNodes_(); + return this.value_; +}; + + +/** + * Set the value of the node + * @param {*} value The new value of the node. + * @override + */ +goog.ds.XmlDataSource.prototype.set = function(value) { + throw Error('Can\'t set on XmlDataSource yet'); +}; + + +/** @override */ +goog.ds.XmlDataSource.prototype.getChildNodes = function(opt_selector) { + if (opt_selector && opt_selector == + goog.ds.XmlDataSource.ATTRIBUTE_SELECTOR_) { + this.createAttributes_(); + return this.attributes_; + } else if (opt_selector == null || + opt_selector == goog.ds.STR_ALL_CHILDREN_SELECTOR) { + this.createChildNodes_(); + return this.childNodeList_; + } else { + throw Error('Unsupported selector'); + } + +}; + + +/** + * Gets a named child node of the current node + * @param {string} name The node name. + * @return {goog.ds.DataNode} The child node, or null if + * no node of this name exists. + * @override + */ +goog.ds.XmlDataSource.prototype.getChildNode = function(name) { + if (goog.string.startsWith(name, goog.ds.STR_ATTRIBUTE_START)) { + var att = this.node_.getAttributeNode(name.substring(1)); + return att ? new goog.ds.XmlDataSource(att, this) : null; + } else { + return /** @type {goog.ds.DataNode} */ (this.getChildNodes().get(name)); + } +}; + + +/** + * Gets the value of a child node + * @param {string} name The node name. + * @return {*} The value of the node, or null if no value or the child node + * doesn't exist. + * @override + */ +goog.ds.XmlDataSource.prototype.getChildNodeValue = function(name) { + if (goog.string.startsWith(name, goog.ds.STR_ATTRIBUTE_START)) { + var node = this.node_.getAttributeNode(name.substring(1)); + return node ? node.nodeValue : null; + } else { + var node = this.getChildNode(name); + return node ? node.get() : null; + } +}; + + +/** + * Get the name of the node relative to the parent node + * @return {string} The name of the node. + * @override + */ +goog.ds.XmlDataSource.prototype.getDataName = function() { + return this.dataName_; +}; + + +/** + * Setthe name of the node relative to the parent node + * @param {string} name The name of the node. + * @override + */ +goog.ds.XmlDataSource.prototype.setDataName = function(name) { + this.dataName_ = name; +}; + + +/** + * Gets the a qualified data path to this node + * @return {string} The data path. + * @override + */ +goog.ds.XmlDataSource.prototype.getDataPath = function() { + var parentPath = ''; + if (this.parent_) { + parentPath = this.parent_.getDataPath() + + (this.dataName_.indexOf(goog.ds.STR_ARRAY_START) != -1 ? '' : + goog.ds.STR_PATH_SEPARATOR); + } + + return parentPath + this.dataName_; +}; + + +/** + * Load or reload the backing data for this node + * @override + */ +goog.ds.XmlDataSource.prototype.load = function() { + // Nothing to do +}; + + +/** + * Gets the state of the backing data for this node + * @return {goog.ds.LoadState} The state. + * @override + */ +goog.ds.XmlDataSource.prototype.getLoadState = function() { + return this.node_ ? goog.ds.LoadState.LOADED : goog.ds.LoadState.NOT_LOADED; +}; + + +/** + * Check whether a node is an empty text node. Nodes consisting of only white + * space (#x20, #xD, #xA, #x9) can generally be collapsed to a zero length + * text string. + * @param {string} str String to match. + * @return {boolean} True if string equates to empty text node. + * @private + */ +goog.ds.XmlDataSource.isEmptyTextNodeValue_ = function(str) { + return /^[\r\n\t ]*$/.test(str); +}; + + +/** + * Creates an XML document with one empty node. + * Useful for places where you need a node that + * can be queried against. + * + * @return {Document} Document with one empty node. + * @private + */ +goog.ds.XmlDataSource.createChildlessDocument_ = function() { + return goog.dom.xml.createDocument('nothing'); +}; + + + +/** + * Data source whose backing is an XMLHttpRequest, + * + * A URI of an empty string will mean that no request is made + * and the data source will be a single, empty node. + * + * @param {(string|goog.Uri)} uri URL of the XMLHttpRequest. + * @param {string} name Name of the datasource. + * + * implements goog.ds.XmlHttpDataSource. + * @constructor + * @extends {goog.ds.XmlDataSource} + * @final + */ +goog.ds.XmlHttpDataSource = function(uri, name) { + goog.ds.XmlDataSource.call(this, null, null, name); + if (uri) { + this.uri_ = new goog.Uri(uri); + } else { + this.uri_ = null; + } +}; +goog.inherits(goog.ds.XmlHttpDataSource, goog.ds.XmlDataSource); + + +/** + * Default load state is NOT_LOADED + * @private + */ +goog.ds.XmlHttpDataSource.prototype.loadState_ = goog.ds.LoadState.NOT_LOADED; + + +/** + * Load or reload the backing data for this node. + * Fires the XMLHttpRequest + * @override + */ +goog.ds.XmlHttpDataSource.prototype.load = function() { + if (this.uri_) { + goog.log.info(goog.ds.logger, 'Sending XML request for DataSource ' + + this.getDataName() + ' to ' + this.uri_); + this.loadState_ = goog.ds.LoadState.LOADING; + + goog.net.XhrIo.send(this.uri_, goog.bind(this.complete_, this)); + } else { + this.node_ = goog.ds.XmlDataSource.createChildlessDocument_(); + this.loadState_ = goog.ds.LoadState.NOT_LOADED; + } +}; + + +/** + * Gets the state of the backing data for this node + * @return {goog.ds.LoadState} The state. + * @override + */ +goog.ds.XmlHttpDataSource.prototype.getLoadState = function() { + return this.loadState_; +}; + + +/** + * Handles the completion of an XhrIo request. Dispatches to success or load + * based on the result. + * @param {!goog.events.Event} e The XhrIo event object. + * @private + */ +goog.ds.XmlHttpDataSource.prototype.complete_ = function(e) { + var xhr = /** @type {goog.net.XhrIo} */ (e.target); + if (xhr && xhr.isSuccess()) { + this.success_(xhr); + } else { + this.failure_(); + } +}; + + +/** + * Success result. Checks whether valid XML was returned + * and sets the XML and loadstate. + * + * @param {!goog.net.XhrIo} xhr The successful XhrIo object. + * @private + */ +goog.ds.XmlHttpDataSource.prototype.success_ = function(xhr) { + goog.log.info(goog.ds.logger, + 'Got data for DataSource ' + this.getDataName()); + var xml = xhr.getResponseXml(); + + // Fix for case where IE returns valid XML as text but + // doesn't parse by default + if (xml && !xml.hasChildNodes() && + goog.isObject(xhr.getResponseText())) { + xml = goog.dom.xml.loadXml(xhr.getResponseText()); + } + // Failure result + if (!xml || !xml.hasChildNodes()) { + this.loadState_ = goog.ds.LoadState.FAILED; + this.node_ = goog.ds.XmlDataSource.createChildlessDocument_(); + } else { + this.loadState_ = goog.ds.LoadState.LOADED; + this.node_ = xml.documentElement; + } + + if (this.getDataName()) { + goog.ds.DataManager.getInstance().fireDataChange(this.getDataName()); + } +}; + + +/** + * Failure result + * + * @private + */ +goog.ds.XmlHttpDataSource.prototype.failure_ = function() { + goog.log.info(goog.ds.logger, 'Data retrieve failed for DataSource ' + + this.getDataName()); + + this.loadState_ = goog.ds.LoadState.FAILED; + this.node_ = goog.ds.XmlDataSource.createChildlessDocument_(); + + if (this.getDataName()) { + goog.ds.DataManager.getInstance().fireDataChange(this.getDataName()); + } +};
