Update wmts-hidpi, add nicer-api-docs
This commit is contained in:
@@ -0,0 +1,559 @@
|
||||
// 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
|
||||
* Central class for registering and accessing data sources
|
||||
* Also handles processing of data events.
|
||||
*
|
||||
* There is a shared global instance that most client code should access via
|
||||
* goog.ds.DataManager.getInstance(). However you can also create your own
|
||||
* DataManager using new
|
||||
*
|
||||
* Implements DataNode to provide the top element in a data registry
|
||||
* Prepends '$' to top level data names in path to denote they are root object
|
||||
*
|
||||
*/
|
||||
goog.provide('goog.ds.DataManager');
|
||||
|
||||
goog.require('goog.ds.BasicNodeList');
|
||||
goog.require('goog.ds.DataNode');
|
||||
goog.require('goog.ds.Expr');
|
||||
goog.require('goog.string');
|
||||
goog.require('goog.structs');
|
||||
goog.require('goog.structs.Map');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Create a DataManger
|
||||
* @extends {goog.ds.DataNode}
|
||||
* @constructor
|
||||
*/
|
||||
goog.ds.DataManager = function() {
|
||||
this.dataSources_ = new goog.ds.BasicNodeList();
|
||||
this.autoloads_ = new goog.structs.Map();
|
||||
this.listenerMap_ = {};
|
||||
this.listenersByFunction_ = {};
|
||||
this.aliases_ = {};
|
||||
this.eventCount_ = 0;
|
||||
this.indexedListenersByFunction_ = {};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Global instance
|
||||
* @private
|
||||
*/
|
||||
goog.ds.DataManager.instance_ = null;
|
||||
goog.inherits(goog.ds.DataManager, goog.ds.DataNode);
|
||||
|
||||
|
||||
/**
|
||||
* Get the global instance
|
||||
* @return {goog.ds.DataManager} The data manager singleton.
|
||||
*/
|
||||
goog.ds.DataManager.getInstance = function() {
|
||||
if (!goog.ds.DataManager.instance_) {
|
||||
goog.ds.DataManager.instance_ = new goog.ds.DataManager();
|
||||
}
|
||||
return goog.ds.DataManager.instance_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Clears the global instance (for unit tests to reset state).
|
||||
*/
|
||||
goog.ds.DataManager.clearInstance = function() {
|
||||
goog.ds.DataManager.instance_ = null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Add a data source
|
||||
* @param {goog.ds.DataNode} ds The data source.
|
||||
* @param {boolean=} opt_autoload Whether to automatically load the data,
|
||||
* defaults to false.
|
||||
* @param {string=} opt_name Optional name, can also get name
|
||||
* from the datasource.
|
||||
*/
|
||||
goog.ds.DataManager.prototype.addDataSource = function(ds, opt_autoload,
|
||||
opt_name) {
|
||||
var autoload = !!opt_autoload;
|
||||
var name = opt_name || ds.getDataName();
|
||||
if (!goog.string.startsWith(name, '$')) {
|
||||
name = '$' + name;
|
||||
}
|
||||
ds.setDataName(name);
|
||||
this.dataSources_.add(ds);
|
||||
this.autoloads_.set(name, autoload);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Create an alias for a data path, very similar to assigning a variable.
|
||||
* For example, you can set $CurrentContact -> $Request/Contacts[5], and all
|
||||
* references to $CurrentContact will be procesed on $Request/Contacts[5].
|
||||
*
|
||||
* Aliases will hide datasources of the same name.
|
||||
*
|
||||
* @param {string} name Alias name, must be a top level path ($Foo).
|
||||
* @param {string} dataPath Data path being aliased.
|
||||
*/
|
||||
goog.ds.DataManager.prototype.aliasDataSource = function(name, dataPath) {
|
||||
if (!this.aliasListener_) {
|
||||
this.aliasListener_ = goog.bind(this.listenForAlias_, this);
|
||||
}
|
||||
if (this.aliases_[name]) {
|
||||
var oldPath = this.aliases_[name].getSource();
|
||||
this.removeListeners(this.aliasListener_, oldPath + '/...', name);
|
||||
}
|
||||
this.aliases_[name] = goog.ds.Expr.create(dataPath);
|
||||
this.addListener(this.aliasListener_, dataPath + '/...', name);
|
||||
this.fireDataChange(name);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Listener function for matches of paths that have been aliased.
|
||||
* Fires a data change on the alias as well.
|
||||
*
|
||||
* @param {string} dataPath Path of data event fired.
|
||||
* @param {string} name Name of the alias.
|
||||
* @private
|
||||
*/
|
||||
goog.ds.DataManager.prototype.listenForAlias_ = function(dataPath, name) {
|
||||
var aliasedExpr = this.aliases_[name];
|
||||
|
||||
if (aliasedExpr) {
|
||||
// If it's a subpath, appends the subpath to the alias name
|
||||
// otherwise just fires on the top level alias
|
||||
var aliasedPath = aliasedExpr.getSource();
|
||||
if (dataPath.indexOf(aliasedPath) == 0) {
|
||||
this.fireDataChange(name + dataPath.substring(aliasedPath.length));
|
||||
} else {
|
||||
this.fireDataChange(name);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
goog.ds.DataManager.prototype.getDataSource = function(name) {
|
||||
if (this.aliases_[name]) {
|
||||
return this.aliases_[name].getNode();
|
||||
} else {
|
||||
return this.dataSources_.get(name);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Get the value of the node
|
||||
* @return {Object} The value of the node, or null if no value.
|
||||
* @override
|
||||
*/
|
||||
goog.ds.DataManager.prototype.get = function() {
|
||||
return this.dataSources_;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ds.DataManager.prototype.set = function(value) {
|
||||
throw Error('Can\'t set on DataManager');
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ds.DataManager.prototype.getChildNodes = function(opt_selector) {
|
||||
if (opt_selector) {
|
||||
return new goog.ds.BasicNodeList(
|
||||
[this.getChildNode(/** @type {string} */(opt_selector))]);
|
||||
} else {
|
||||
return this.dataSources_;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 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.DataManager.prototype.getChildNode = function(name) {
|
||||
return this.getDataSource(name);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ds.DataManager.prototype.getChildNodeValue = function(name) {
|
||||
var ds = this.getDataSource(name);
|
||||
return ds ? ds.get() : null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Get the name of the node relative to the parent node
|
||||
* @return {string} The name of the node.
|
||||
* @override
|
||||
*/
|
||||
goog.ds.DataManager.prototype.getDataName = function() {
|
||||
return '';
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the a qualified data path to this node
|
||||
* @return {string} The data path.
|
||||
* @override
|
||||
*/
|
||||
goog.ds.DataManager.prototype.getDataPath = function() {
|
||||
return '';
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Load or reload the backing data for this node
|
||||
* only loads datasources flagged with autoload
|
||||
* @override
|
||||
*/
|
||||
goog.ds.DataManager.prototype.load = function() {
|
||||
var len = this.dataSources_.getCount();
|
||||
for (var i = 0; i < len; i++) {
|
||||
var ds = this.dataSources_.getByIndex(i);
|
||||
var autoload = this.autoloads_.get(ds.getDataName());
|
||||
if (autoload) {
|
||||
ds.load();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the state of the backing data for this node
|
||||
* @return {goog.ds.LoadState} The state.
|
||||
* @override
|
||||
*/
|
||||
goog.ds.DataManager.prototype.getLoadState = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Whether the value of this node is a homogeneous list of data
|
||||
* @return {boolean} True if a list.
|
||||
* @override
|
||||
*/
|
||||
goog.ds.DataManager.prototype.isList = function() {
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Get the total count of events fired (mostly for debugging)
|
||||
* @return {number} Count of events.
|
||||
*/
|
||||
goog.ds.DataManager.prototype.getEventCount = function() {
|
||||
return this.eventCount_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds a listener
|
||||
* Listeners should fire when any data with path that has dataPath as substring
|
||||
* is changed.
|
||||
* TODO(user) Look into better listener handling
|
||||
*
|
||||
* @param {Function} fn Callback function, signature function(dataPath, id).
|
||||
* @param {string} dataPath Fully qualified data path.
|
||||
* @param {string=} opt_id A value passed back to the listener when the dataPath
|
||||
* is matched.
|
||||
*/
|
||||
goog.ds.DataManager.prototype.addListener = function(fn, dataPath, opt_id) {
|
||||
// maxAncestor sets how distant an ancestor you can be of the fired event
|
||||
// and still fire (you always fire if you are a descendant).
|
||||
// 0 means you don't fire if you are an ancestor
|
||||
// 1 means you only fire if you are parent
|
||||
// 1000 means you will fire if you are ancestor (effectively infinite)
|
||||
var maxAncestors = 0;
|
||||
if (goog.string.endsWith(dataPath, '/...')) {
|
||||
maxAncestors = 1000;
|
||||
dataPath = dataPath.substring(0, dataPath.length - 4);
|
||||
} else if (goog.string.endsWith(dataPath, '/*')) {
|
||||
maxAncestors = 1;
|
||||
dataPath = dataPath.substring(0, dataPath.length - 2);
|
||||
}
|
||||
|
||||
opt_id = opt_id || '';
|
||||
var key = dataPath + ':' + opt_id + ':' + goog.getUid(fn);
|
||||
var listener = {dataPath: dataPath, id: opt_id, fn: fn};
|
||||
var expr = goog.ds.Expr.create(dataPath);
|
||||
|
||||
var fnUid = goog.getUid(fn);
|
||||
if (!this.listenersByFunction_[fnUid]) {
|
||||
this.listenersByFunction_[fnUid] = {};
|
||||
}
|
||||
this.listenersByFunction_[fnUid][key] = {listener: listener, items: []};
|
||||
|
||||
while (expr) {
|
||||
var listenerSpec = {listener: listener, maxAncestors: maxAncestors};
|
||||
var matchingListeners = this.listenerMap_[expr.getSource()];
|
||||
if (matchingListeners == null) {
|
||||
matchingListeners = {};
|
||||
this.listenerMap_[expr.getSource()] = matchingListeners;
|
||||
}
|
||||
matchingListeners[key] = listenerSpec;
|
||||
maxAncestors = 0;
|
||||
expr = expr.getParent();
|
||||
this.listenersByFunction_[fnUid][key].items.push(
|
||||
{key: key, obj: matchingListeners});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds an indexed listener.
|
||||
*
|
||||
* Indexed listeners allow for '*' in data paths. If a * exists, will match
|
||||
* all values and return the matched values in an array to the callback.
|
||||
*
|
||||
* Currently uses a promiscuous match algorithm: Matches everything before the
|
||||
* first '*', and then does a regex match for all of the returned events.
|
||||
* Although this isn't optimized, it is still an improvement as you can collapse
|
||||
* 100's of listeners into a single regex match
|
||||
*
|
||||
* @param {Function} fn Callback function, signature (dataPath, id, indexes).
|
||||
* @param {string} dataPath Fully qualified data path.
|
||||
* @param {string=} opt_id A value passed back to the listener when the dataPath
|
||||
* is matched.
|
||||
*/
|
||||
goog.ds.DataManager.prototype.addIndexedListener = function(fn, dataPath,
|
||||
opt_id) {
|
||||
var firstStarPos = dataPath.indexOf('*');
|
||||
// Just need a regular listener
|
||||
if (firstStarPos == -1) {
|
||||
this.addListener(fn, dataPath, opt_id);
|
||||
return;
|
||||
}
|
||||
|
||||
var listenPath = dataPath.substring(0, firstStarPos) + '...';
|
||||
|
||||
// Create regex that matches * to any non '\' character
|
||||
var ext = '$';
|
||||
if (goog.string.endsWith(dataPath, '/...')) {
|
||||
dataPath = dataPath.substring(0, dataPath.length - 4);
|
||||
ext = '';
|
||||
}
|
||||
var regExpPath = goog.string.regExpEscape(dataPath);
|
||||
var matchRegExp = regExpPath.replace(/\\\*/g, '([^\\\/]+)') + ext;
|
||||
|
||||
// Matcher function applies the regex and calls back the original function
|
||||
// if the regex matches, passing in an array of the matched values
|
||||
var matchRegExpRe = new RegExp(matchRegExp);
|
||||
var matcher = function(path, id) {
|
||||
var match = matchRegExpRe.exec(path);
|
||||
if (match) {
|
||||
match.shift();
|
||||
fn(path, opt_id, match);
|
||||
}
|
||||
};
|
||||
this.addListener(matcher, listenPath, opt_id);
|
||||
|
||||
// Add the indexed listener to the map so that we can remove it later.
|
||||
var fnUid = goog.getUid(fn);
|
||||
if (!this.indexedListenersByFunction_[fnUid]) {
|
||||
this.indexedListenersByFunction_[fnUid] = {};
|
||||
}
|
||||
var key = dataPath + ':' + opt_id;
|
||||
this.indexedListenersByFunction_[fnUid][key] = {
|
||||
listener: {dataPath: listenPath, fn: matcher, id: opt_id}
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Removes indexed listeners with a given callback function, and optional
|
||||
* matching datapath and matching id.
|
||||
*
|
||||
* @param {Function} fn Callback function, signature function(dataPath, id).
|
||||
* @param {string=} opt_dataPath Fully qualified data path.
|
||||
* @param {string=} opt_id A value passed back to the listener when the dataPath
|
||||
* is matched.
|
||||
*/
|
||||
goog.ds.DataManager.prototype.removeIndexedListeners = function(
|
||||
fn, opt_dataPath, opt_id) {
|
||||
this.removeListenersByFunction_(
|
||||
this.indexedListenersByFunction_, true, fn, opt_dataPath, opt_id);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Removes listeners with a given callback function, and optional
|
||||
* matching dataPath and matching id
|
||||
*
|
||||
* @param {Function} fn Callback function, signature function(dataPath, id).
|
||||
* @param {string=} opt_dataPath Fully qualified data path.
|
||||
* @param {string=} opt_id A value passed back to the listener when the dataPath
|
||||
* is matched.
|
||||
*/
|
||||
goog.ds.DataManager.prototype.removeListeners = function(fn, opt_dataPath,
|
||||
opt_id) {
|
||||
|
||||
// Normalize data path root
|
||||
if (opt_dataPath && goog.string.endsWith(opt_dataPath, '/...')) {
|
||||
opt_dataPath = opt_dataPath.substring(0, opt_dataPath.length - 4);
|
||||
} else if (opt_dataPath && goog.string.endsWith(opt_dataPath, '/*')) {
|
||||
opt_dataPath = opt_dataPath.substring(0, opt_dataPath.length - 2);
|
||||
}
|
||||
|
||||
this.removeListenersByFunction_(
|
||||
this.listenersByFunction_, false, fn, opt_dataPath, opt_id);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Removes listeners with a given callback function, and optional
|
||||
* matching dataPath and matching id from the given listenersByFunction
|
||||
* data structure.
|
||||
*
|
||||
* @param {Object} listenersByFunction The listeners by function.
|
||||
* @param {boolean} indexed Indicates whether the listenersByFunction are
|
||||
* indexed or not.
|
||||
* @param {Function} fn Callback function, signature function(dataPath, id).
|
||||
* @param {string=} opt_dataPath Fully qualified data path.
|
||||
* @param {string=} opt_id A value passed back to the listener when the dataPath
|
||||
* is matched.
|
||||
* @private
|
||||
*/
|
||||
goog.ds.DataManager.prototype.removeListenersByFunction_ = function(
|
||||
listenersByFunction, indexed, fn, opt_dataPath, opt_id) {
|
||||
var fnUid = goog.getUid(fn);
|
||||
var functionMatches = listenersByFunction[fnUid];
|
||||
if (functionMatches != null) {
|
||||
for (var key in functionMatches) {
|
||||
var functionMatch = functionMatches[key];
|
||||
var listener = functionMatch.listener;
|
||||
if ((!opt_dataPath || opt_dataPath == listener.dataPath) &&
|
||||
(!opt_id || opt_id == listener.id)) {
|
||||
if (indexed) {
|
||||
this.removeListeners(
|
||||
listener.fn, listener.dataPath, listener.id);
|
||||
}
|
||||
if (functionMatch.items) {
|
||||
for (var i = 0; i < functionMatch.items.length; i++) {
|
||||
var item = functionMatch.items[i];
|
||||
delete item.obj[item.key];
|
||||
}
|
||||
}
|
||||
delete functionMatches[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Get the total number of listeners (per expression listened to, so may be
|
||||
* more than number of times addListener() has been called
|
||||
* @return {number} Number of listeners.
|
||||
*/
|
||||
goog.ds.DataManager.prototype.getListenerCount = function() {
|
||||
var count = 0;
|
||||
goog.structs.forEach(this.listenerMap_, function(matchingListeners) {
|
||||
count += goog.structs.getCount(matchingListeners);
|
||||
});
|
||||
return count;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Disables the sending of all data events during the execution of the given
|
||||
* callback. This provides a way to avoid useless notifications of small changes
|
||||
* when you will eventually send a data event manually that encompasses them
|
||||
* all.
|
||||
*
|
||||
* Note that this function can not be called reentrantly.
|
||||
*
|
||||
* @param {Function} callback Zero-arg function to execute.
|
||||
*/
|
||||
goog.ds.DataManager.prototype.runWithoutFiringDataChanges = function(callback) {
|
||||
if (this.disableFiring_) {
|
||||
throw Error('Can not nest calls to runWithoutFiringDataChanges');
|
||||
}
|
||||
|
||||
this.disableFiring_ = true;
|
||||
try {
|
||||
callback();
|
||||
} finally {
|
||||
this.disableFiring_ = false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Fire a data change event to all listeners
|
||||
*
|
||||
* If the path matches the path of a listener, the listener will fire
|
||||
*
|
||||
* If your path is the parent of a listener, the listener will fire. I.e.
|
||||
* if $Contacts/bob@bob.com changes, then we will fire listener for
|
||||
* $Contacts/bob@bob.com/Name as well, as the assumption is that when
|
||||
* a parent changes, all children are invalidated.
|
||||
*
|
||||
* If your path is the child of a listener, the listener may fire, depending
|
||||
* on the ancestor depth.
|
||||
*
|
||||
* A listener for $Contacts might only be interested if the contact name changes
|
||||
* (i.e. $Contacts doesn't fire on $Contacts/bob@bob.com/Name),
|
||||
* while a listener for a specific contact might
|
||||
* (i.e. $Contacts/bob@bob.com would fire on $Contacts/bob@bob.com/Name).
|
||||
* Adding "/..." to a lisetener path listens to all children, and adding "/*" to
|
||||
* a listener path listens only to direct children
|
||||
*
|
||||
* @param {string} dataPath Fully qualified data path.
|
||||
*/
|
||||
goog.ds.DataManager.prototype.fireDataChange = function(dataPath) {
|
||||
if (this.disableFiring_) {
|
||||
return;
|
||||
}
|
||||
|
||||
var expr = goog.ds.Expr.create(dataPath);
|
||||
var ancestorDepth = 0;
|
||||
|
||||
// Look for listeners for expression and all its parents.
|
||||
// Parents of listener expressions are all added to the listenerMap as well,
|
||||
// so this will evaluate inner loop every time the dataPath is a child or
|
||||
// an ancestor of the original listener path
|
||||
while (expr) {
|
||||
var matchingListeners = this.listenerMap_[expr.getSource()];
|
||||
if (matchingListeners) {
|
||||
for (var id in matchingListeners) {
|
||||
var match = matchingListeners[id];
|
||||
var listener = match.listener;
|
||||
if (ancestorDepth <= match.maxAncestors) {
|
||||
listener.fn(dataPath, listener.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
ancestorDepth++;
|
||||
expr = expr.getParent();
|
||||
}
|
||||
this.eventCount_++;
|
||||
};
|
||||
@@ -0,0 +1,656 @@
|
||||
// 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 Generic rich data access API.
|
||||
*
|
||||
* Abstraction for data sources that allows listening for changes at different
|
||||
* levels of the data tree and updating the data via XHR requests
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
goog.provide('goog.ds.BaseDataNode');
|
||||
goog.provide('goog.ds.BasicNodeList');
|
||||
goog.provide('goog.ds.DataNode');
|
||||
goog.provide('goog.ds.DataNodeList');
|
||||
goog.provide('goog.ds.EmptyNodeList');
|
||||
goog.provide('goog.ds.LoadState');
|
||||
goog.provide('goog.ds.SortedNodeList');
|
||||
goog.provide('goog.ds.Util');
|
||||
goog.provide('goog.ds.logger');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.log');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Interface for node in rich data tree.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
goog.ds.DataNode = function() {};
|
||||
|
||||
|
||||
/**
|
||||
* Get the value of the node
|
||||
* @param {...?} var_args Do not check arity of arguments, because
|
||||
* some subclasses require args.
|
||||
* @return {*} The value of the node, or null if no value.
|
||||
*/
|
||||
goog.ds.DataNode.prototype.get = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Set the value of the node
|
||||
* @param {*} value The new value of the node.
|
||||
*/
|
||||
goog.ds.DataNode.prototype.set = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Gets all of the child nodes of the current node.
|
||||
* Should return an empty DataNode list if no child nodes.
|
||||
* @param {string=} opt_selector String selector to choose child nodes.
|
||||
* @return {goog.ds.DataNodeList} The child nodes.
|
||||
*/
|
||||
goog.ds.DataNode.prototype.getChildNodes = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Gets a named child node of the current node
|
||||
* @param {string} name The node name.
|
||||
* @param {boolean=} opt_canCreate Whether to create a child node if it does not
|
||||
* exist.
|
||||
* @return {goog.ds.DataNode} The child node, or null
|
||||
* if no node of this name exists.
|
||||
*/
|
||||
goog.ds.DataNode.prototype.getChildNode = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
goog.ds.DataNode.prototype.getChildNodeValue = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Sets a named child node of the current node.
|
||||
*
|
||||
* @param {string} name The node name.
|
||||
* @param {Object} value The value to set, can be DataNode, object, property,
|
||||
* or null. If value is null, removes the child node.
|
||||
* @return {Object} The child node, if the node was set.
|
||||
*/
|
||||
goog.ds.DataNode.prototype.setChildNode = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Get the name of the node relative to the parent node
|
||||
* @return {string} The name of the node.
|
||||
*/
|
||||
goog.ds.DataNode.prototype.getDataName = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Set the name of the node relative to the parent node
|
||||
* @param {string} name The name of the node.
|
||||
*/
|
||||
goog.ds.DataNode.prototype.setDataName = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Gets the a qualified data path to this node
|
||||
* @return {string} The data path.
|
||||
*/
|
||||
goog.ds.DataNode.prototype.getDataPath = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Load or reload the backing data for this node
|
||||
*/
|
||||
goog.ds.DataNode.prototype.load = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Gets the state of the backing data for this node
|
||||
* @return {goog.ds.LoadState} The state.
|
||||
*/
|
||||
goog.ds.DataNode.prototype.getLoadState = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Whether the value of this node is a homogeneous list of data
|
||||
* @return {boolean} True if a list.
|
||||
*/
|
||||
goog.ds.DataNode.prototype.isList = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Enum for load state of a DataNode.
|
||||
* @enum {string}
|
||||
*/
|
||||
goog.ds.LoadState = {
|
||||
LOADED: 'LOADED',
|
||||
LOADING: 'LOADING',
|
||||
FAILED: 'FAILED',
|
||||
NOT_LOADED: 'NOT_LOADED'
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Base class for data node functionality, has default implementations for
|
||||
* many of the functions.
|
||||
*
|
||||
* implements {goog.ds.DataNode}
|
||||
* @constructor
|
||||
*/
|
||||
goog.ds.BaseDataNode = function() {};
|
||||
|
||||
|
||||
/**
|
||||
* Set the value of the node
|
||||
* @param {Object} value The new value of the node.
|
||||
*/
|
||||
goog.ds.BaseDataNode.prototype.set = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Gets all of the child nodes of the current node.
|
||||
* Should return an empty DataNode list if no child nodes.
|
||||
* @param {string=} opt_selector String selector to choose child nodes.
|
||||
* @return {goog.ds.DataNodeList} The child nodes.
|
||||
*/
|
||||
goog.ds.BaseDataNode.prototype.getChildNodes = function(opt_selector) {
|
||||
return new goog.ds.EmptyNodeList();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets a named child node of the current node
|
||||
* @param {string} name The node name.
|
||||
* @param {boolean=} opt_canCreate Whether you can create the child node if
|
||||
* it doesn't exist already.
|
||||
* @return {goog.ds.DataNode} The child node, or null if no node of
|
||||
* this name exists and opt_create is false.
|
||||
*/
|
||||
goog.ds.BaseDataNode.prototype.getChildNode = function(name, opt_canCreate) {
|
||||
return null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
goog.ds.BaseDataNode.prototype.getChildNodeValue = function(name) {
|
||||
return null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Get the name of the node relative to the parent node
|
||||
* @return {string} The name of the node.
|
||||
*/
|
||||
goog.ds.BaseDataNode.prototype.getDataName = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Gets the a qualified data path to this node
|
||||
* @return {string} The data path.
|
||||
*/
|
||||
goog.ds.BaseDataNode.prototype.getDataPath = function() {
|
||||
var parentPath = '';
|
||||
var myName = this.getDataName();
|
||||
if (this.getParent && this.getParent()) {
|
||||
parentPath = this.getParent().getDataPath() +
|
||||
(myName.indexOf(goog.ds.STR_ARRAY_START) != -1 ? '' :
|
||||
goog.ds.STR_PATH_SEPARATOR);
|
||||
}
|
||||
|
||||
return parentPath + myName;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Load or reload the backing data for this node
|
||||
*/
|
||||
goog.ds.BaseDataNode.prototype.load = goog.nullFunction;
|
||||
|
||||
|
||||
/**
|
||||
* Gets the state of the backing data for this node
|
||||
* @return {goog.ds.LoadState} The state.
|
||||
*/
|
||||
goog.ds.BaseDataNode.prototype.getLoadState = function() {
|
||||
return goog.ds.LoadState.LOADED;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the parent node. Subclasses implement this function
|
||||
* @type {Function}
|
||||
* @protected
|
||||
*/
|
||||
goog.ds.BaseDataNode.prototype.getParent = null;
|
||||
|
||||
|
||||
/**
|
||||
* Interface for node list in rich data tree.
|
||||
*
|
||||
* Has both map and list-style accessors
|
||||
*
|
||||
* @constructor
|
||||
* @extends {goog.ds.DataNode}
|
||||
*/
|
||||
// TODO(arv): Use interfaces when available.
|
||||
goog.ds.DataNodeList = function() {};
|
||||
|
||||
|
||||
/**
|
||||
* Add a node to the node list.
|
||||
* If the node has a dataName, uses this for the key in the map.
|
||||
*
|
||||
* @param {goog.ds.DataNode} node The node to add.
|
||||
*/
|
||||
goog.ds.DataNodeList.prototype.add = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Get a node by string key.
|
||||
* Returns null if node doesn't exist.
|
||||
*
|
||||
* @param {string} key String lookup key.
|
||||
* @return {*} The node, or null if doesn't exist.
|
||||
* @override
|
||||
*/
|
||||
goog.ds.DataNodeList.prototype.get = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Get a node by index
|
||||
* Returns null if the index is out of range
|
||||
*
|
||||
* @param {number} index The index of the node.
|
||||
* @return {goog.ds.DataNode} The node, or null if doesn't exist.
|
||||
*/
|
||||
goog.ds.DataNodeList.prototype.getByIndex = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Gets the size of the node list
|
||||
*
|
||||
* @return {number} The size of the list.
|
||||
*/
|
||||
goog.ds.DataNodeList.prototype.getCount = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Sets a node in the list of a given name
|
||||
* @param {string} name Name of the node.
|
||||
* @param {goog.ds.DataNode} node The node.
|
||||
*/
|
||||
goog.ds.DataNodeList.prototype.setNode = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Removes a node in the list of a given name
|
||||
* @param {string} name Name of the node.
|
||||
* @return {boolean} True if node existed and was deleted.
|
||||
*/
|
||||
goog.ds.DataNodeList.prototype.removeNode = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Simple node list implementation with underlying array and map
|
||||
* implements goog.ds.DataNodeList.
|
||||
*
|
||||
* 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 {Array.<goog.ds.DataNode>=} opt_nodes optional nodes to add to list.
|
||||
* @constructor
|
||||
* @extends {goog.ds.DataNodeList}
|
||||
*/
|
||||
// TODO(arv): Use interfaces when available.
|
||||
goog.ds.BasicNodeList = function(opt_nodes) {
|
||||
this.map_ = {};
|
||||
this.list_ = [];
|
||||
this.indexMap_ = {};
|
||||
if (opt_nodes) {
|
||||
for (var i = 0, node; node = opt_nodes[i]; i++) {
|
||||
this.add(node);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Add a node to the node list.
|
||||
* If the node has a dataName, uses this for the key in the map.
|
||||
* TODO(user) Remove function as well
|
||||
*
|
||||
* @param {goog.ds.DataNode} node The node to add.
|
||||
* @override
|
||||
*/
|
||||
goog.ds.BasicNodeList.prototype.add = function(node) {
|
||||
this.list_.push(node);
|
||||
var dataName = node.getDataName();
|
||||
if (dataName) {
|
||||
this.map_[dataName] = node;
|
||||
this.indexMap_[dataName] = this.list_.length - 1;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Get a node by string key.
|
||||
* Returns null if node doesn't exist.
|
||||
*
|
||||
* @param {string} key String lookup key.
|
||||
* @return {goog.ds.DataNode} The node, or null if doesn't exist.
|
||||
* @override
|
||||
*/
|
||||
goog.ds.BasicNodeList.prototype.get = function(key) {
|
||||
return this.map_[key] || null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Get a node by index
|
||||
* Returns null if the index is out of range
|
||||
*
|
||||
* @param {number} index The index of the node.
|
||||
* @return {goog.ds.DataNode} The node, or null if doesn't exist.
|
||||
* @override
|
||||
*/
|
||||
goog.ds.BasicNodeList.prototype.getByIndex = function(index) {
|
||||
return this.list_[index] || null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the size of the node list
|
||||
*
|
||||
* @return {number} The size of the list.
|
||||
* @override
|
||||
*/
|
||||
goog.ds.BasicNodeList.prototype.getCount = function() {
|
||||
return this.list_.length;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets a node in the list of a given name
|
||||
* @param {string} name Name of the node.
|
||||
* @param {goog.ds.DataNode} node The node.
|
||||
* @override
|
||||
*/
|
||||
goog.ds.BasicNodeList.prototype.setNode = function(name, node) {
|
||||
if (node == null) {
|
||||
this.removeNode(name);
|
||||
} else {
|
||||
var existingNode = this.indexMap_[name];
|
||||
if (existingNode != null) {
|
||||
this.map_[name] = node;
|
||||
this.list_[existingNode] = node;
|
||||
} else {
|
||||
this.add(node);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Removes a node in the list of a given name
|
||||
* @param {string} name Name of the node.
|
||||
* @return {boolean} True if node existed and was deleted.
|
||||
* @override
|
||||
*/
|
||||
goog.ds.BasicNodeList.prototype.removeNode = function(name) {
|
||||
var existingNode = this.indexMap_[name];
|
||||
if (existingNode != null) {
|
||||
this.list_.splice(existingNode, 1);
|
||||
delete this.map_[name];
|
||||
delete this.indexMap_[name];
|
||||
for (var index in this.indexMap_) {
|
||||
if (this.indexMap_[index] > existingNode) {
|
||||
this.indexMap_[index]--;
|
||||
}
|
||||
}
|
||||
}
|
||||
return existingNode != null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Get the index of a named node
|
||||
* @param {string} name The name of the node to get the index of.
|
||||
* @return {number|undefined} The index.
|
||||
*/
|
||||
goog.ds.BasicNodeList.prototype.indexOf = function(name) {
|
||||
return this.indexMap_[name];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Immulatable empty node list
|
||||
* @extends {goog.ds.BasicNodeList}
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
goog.ds.EmptyNodeList = function() {
|
||||
goog.ds.BasicNodeList.call(this);
|
||||
};
|
||||
goog.inherits(goog.ds.EmptyNodeList, goog.ds.BasicNodeList);
|
||||
|
||||
|
||||
/**
|
||||
* Add a node to the node list.
|
||||
* If the node has a dataName, uses this for the key in the map.
|
||||
*
|
||||
* @param {goog.ds.DataNode} node The node to add.
|
||||
* @override
|
||||
*/
|
||||
goog.ds.EmptyNodeList.prototype.add = function(node) {
|
||||
throw Error('Can\'t add to EmptyNodeList');
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Node list implementation which maintains sort order during insertion and
|
||||
* modification operations based on a comparison function.
|
||||
*
|
||||
* The SortedNodeList does not guarantee sort order will be maintained if
|
||||
* the underlying data nodes are modified externally.
|
||||
*
|
||||
* 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 {Function} compareFn Comparison function by which the
|
||||
* node list is sorted. Should take 2 arguments to compare, and return a
|
||||
* negative integer, zero, or a positive integer depending on whether the
|
||||
* first argument is less than, equal to, or greater than the second.
|
||||
* @param {Array.<goog.ds.DataNode>=} opt_nodes optional nodes to add to list;
|
||||
* these are assumed to be in sorted order.
|
||||
* @extends {goog.ds.BasicNodeList}
|
||||
* @constructor
|
||||
*/
|
||||
goog.ds.SortedNodeList = function(compareFn, opt_nodes) {
|
||||
this.compareFn_ = compareFn;
|
||||
goog.ds.BasicNodeList.call(this, opt_nodes);
|
||||
};
|
||||
goog.inherits(goog.ds.SortedNodeList, goog.ds.BasicNodeList);
|
||||
|
||||
|
||||
/**
|
||||
* Add a node to the node list, maintaining sort order.
|
||||
* If the node has a dataName, uses this for the key in the map.
|
||||
*
|
||||
* @param {goog.ds.DataNode} node The node to add.
|
||||
* @override
|
||||
*/
|
||||
goog.ds.SortedNodeList.prototype.add = function(node) {
|
||||
if (!this.compareFn_) {
|
||||
this.append(node);
|
||||
return;
|
||||
}
|
||||
|
||||
var searchLoc = goog.array.binarySearch(this.list_, node, this.compareFn_);
|
||||
|
||||
// if there is another node that is "equal" according to the comparison
|
||||
// function, insert before that one; otherwise insert at the location
|
||||
// goog.array.binarySearch indicated
|
||||
if (searchLoc < 0) {
|
||||
searchLoc = -(searchLoc + 1);
|
||||
}
|
||||
|
||||
// update any indexes that are after the insertion point
|
||||
for (var index in this.indexMap_) {
|
||||
if (this.indexMap_[index] >= searchLoc) {
|
||||
this.indexMap_[index]++;
|
||||
}
|
||||
}
|
||||
|
||||
goog.array.insertAt(this.list_, node, searchLoc);
|
||||
var dataName = node.getDataName();
|
||||
if (dataName) {
|
||||
this.map_[dataName] = node;
|
||||
this.indexMap_[dataName] = searchLoc;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds the given node to the end of the SortedNodeList. This should
|
||||
* only be used when the caller can guarantee that the sort order will
|
||||
* be maintained according to this SortedNodeList's compareFn (e.g.
|
||||
* when initializing a new SortedNodeList from a list of nodes that has
|
||||
* already been sorted).
|
||||
* @param {goog.ds.DataNode} node The node to append.
|
||||
*/
|
||||
goog.ds.SortedNodeList.prototype.append = function(node) {
|
||||
goog.ds.SortedNodeList.superClass_.add.call(this, node);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets a node in the list of a given name, maintaining sort order.
|
||||
* @param {string} name Name of the node.
|
||||
* @param {goog.ds.DataNode} node The node.
|
||||
* @override
|
||||
*/
|
||||
goog.ds.SortedNodeList.prototype.setNode = function(name, node) {
|
||||
if (node == null) {
|
||||
this.removeNode(name);
|
||||
} else {
|
||||
var existingNode = this.indexMap_[name];
|
||||
if (existingNode != null) {
|
||||
if (this.compareFn_) {
|
||||
var compareResult = this.compareFn_(this.list_[existingNode], node);
|
||||
if (compareResult == 0) {
|
||||
// the new node can just replace the old one
|
||||
this.map_[name] = node;
|
||||
this.list_[existingNode] = node;
|
||||
} else {
|
||||
// remove the old node, then add the new one
|
||||
this.removeNode(name);
|
||||
this.add(node);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.add(node);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The character denoting an attribute.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ds.STR_ATTRIBUTE_START = '@';
|
||||
|
||||
|
||||
/**
|
||||
* The character denoting all children.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ds.STR_ALL_CHILDREN_SELECTOR = '*';
|
||||
|
||||
|
||||
/**
|
||||
* The wildcard character.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ds.STR_WILDCARD = '*';
|
||||
|
||||
|
||||
/**
|
||||
* The character denoting path separation.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ds.STR_PATH_SEPARATOR = '/';
|
||||
|
||||
|
||||
/**
|
||||
* The character denoting the start of an array.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ds.STR_ARRAY_START = '[';
|
||||
|
||||
|
||||
/**
|
||||
* Shared logger instance for data package
|
||||
* @type {goog.log.Logger}
|
||||
*/
|
||||
goog.ds.logger = goog.log.getLogger('goog.ds');
|
||||
|
||||
|
||||
/**
|
||||
* Create a data node that references another data node,
|
||||
* useful for pointer-like functionality.
|
||||
* All functions will return same values as the original node except for
|
||||
* getDataName()
|
||||
* @param {!goog.ds.DataNode} node The original node.
|
||||
* @param {string} name The new name.
|
||||
* @return {!goog.ds.DataNode} The new data node.
|
||||
*/
|
||||
goog.ds.Util.makeReferenceNode = function(node, name) {
|
||||
/**
|
||||
* @constructor
|
||||
* @extends {goog.ds.DataNode}
|
||||
*/
|
||||
var nodeCreator = function() {};
|
||||
nodeCreator.prototype = node;
|
||||
var newNode = new nodeCreator();
|
||||
newNode.getDataName = function() {
|
||||
return name;
|
||||
};
|
||||
return newNode;
|
||||
};
|
||||
544
nicer-api-docs/closure-library/closure/goog/datasource/expr.js
Normal file
544
nicer-api-docs/closure-library/closure/goog/datasource/expr.js
Normal file
@@ -0,0 +1,544 @@
|
||||
// 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
|
||||
* Expression evaluation utilities. Expression format is very similar to XPath.
|
||||
*
|
||||
* Expression details:
|
||||
* - Of format A/B/C, which will evaluate getChildNode('A').getChildNode('B').
|
||||
* getChildNodes('C')|getChildNodeValue('C')|getChildNode('C') depending on
|
||||
* call
|
||||
* - If expression ends with '/name()', will get the name() of the node
|
||||
* referenced by the preceding path.
|
||||
* - If expression ends with '/count()', will get the count() of the nodes that
|
||||
* match the expression referenced by the preceding path.
|
||||
* - If expression ends with '?', the value is OK to evaluate to null. This is
|
||||
* not enforced by the expression evaluation functions, instead it is
|
||||
* provided as a flag for client code which may ignore depending on usage
|
||||
* - If expression has [INDEX], will use getChildNodes().getByIndex(INDEX)
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
goog.provide('goog.ds.Expr');
|
||||
|
||||
goog.require('goog.ds.BasicNodeList');
|
||||
goog.require('goog.ds.EmptyNodeList');
|
||||
goog.require('goog.string');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Create a new expression. An expression uses a string expression language, and
|
||||
* from this string and a passed in DataNode can evaluate to a value, DataNode,
|
||||
* or a DataNodeList.
|
||||
*
|
||||
* @param {string=} opt_expr The string expression.
|
||||
* @constructor
|
||||
*/
|
||||
goog.ds.Expr = function(opt_expr) {
|
||||
if (opt_expr) {
|
||||
this.setSource_(opt_expr);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Set the source expression text & parse
|
||||
*
|
||||
* @param {string} expr The string expression source.
|
||||
* @param {Array=} opt_parts Array of the parts of an expression.
|
||||
* @param {goog.ds.Expr=} opt_childExpr Optional child of this expression,
|
||||
* passed in as a hint for processing.
|
||||
* @param {goog.ds.Expr=} opt_prevExpr Optional preceding expression
|
||||
* (i.e. $A/B/C is previous expression to B/C) passed in as a hint for
|
||||
* processing.
|
||||
* @private
|
||||
*/
|
||||
goog.ds.Expr.prototype.setSource_ = function(expr, opt_parts,
|
||||
opt_childExpr, opt_prevExpr) {
|
||||
this.src_ = expr;
|
||||
|
||||
if (!opt_childExpr && !opt_prevExpr) {
|
||||
// Check whether it can be empty
|
||||
if (goog.string.endsWith(expr, goog.ds.Expr.String_.CAN_BE_EMPTY)) {
|
||||
this.canBeEmpty_ = true;
|
||||
expr = expr.substring(0, expr.length - 1);
|
||||
}
|
||||
|
||||
// Check whether this is an node function
|
||||
if (goog.string.endsWith(expr, '()')) {
|
||||
if (goog.string.endsWith(expr, goog.ds.Expr.String_.NAME_EXPR) ||
|
||||
goog.string.endsWith(expr, goog.ds.Expr.String_.COUNT_EXPR) ||
|
||||
goog.string.endsWith(expr, goog.ds.Expr.String_.POSITION_EXPR)) {
|
||||
var lastPos = expr.lastIndexOf(goog.ds.Expr.String_.SEPARATOR);
|
||||
if (lastPos != -1) {
|
||||
this.exprFn_ = expr.substring(lastPos + 1);
|
||||
expr = expr.substring(0, lastPos);
|
||||
} else {
|
||||
this.exprFn_ = expr;
|
||||
expr = goog.ds.Expr.String_.CURRENT_NODE_EXPR;
|
||||
}
|
||||
if (this.exprFn_ == goog.ds.Expr.String_.COUNT_EXPR) {
|
||||
this.isCount_ = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Split into component parts
|
||||
this.parts_ = opt_parts || expr.split('/');
|
||||
this.size_ = this.parts_.length;
|
||||
this.last_ = this.parts_[this.size_ - 1];
|
||||
this.root_ = this.parts_[0];
|
||||
|
||||
if (this.size_ == 1) {
|
||||
this.rootExpr_ = this;
|
||||
this.isAbsolute_ = goog.string.startsWith(expr, '$');
|
||||
} else {
|
||||
this.rootExpr_ = goog.ds.Expr.createInternal_(this.root_, null,
|
||||
this, null);
|
||||
this.isAbsolute_ = this.rootExpr_.isAbsolute_;
|
||||
this.root_ = this.rootExpr_.root_;
|
||||
}
|
||||
|
||||
if (this.size_ == 1 && !this.isAbsolute_) {
|
||||
// Check whether expression maps to current node, for convenience
|
||||
this.isCurrent_ = (expr == goog.ds.Expr.String_.CURRENT_NODE_EXPR ||
|
||||
expr == goog.ds.Expr.String_.EMPTY_EXPR);
|
||||
|
||||
// Whether this expression is just an attribute (i.e. '@foo')
|
||||
this.isJustAttribute_ =
|
||||
goog.string.startsWith(expr, goog.ds.Expr.String_.ATTRIBUTE_START);
|
||||
|
||||
// Check whether this is a common node expression
|
||||
this.isAllChildNodes_ = expr == goog.ds.Expr.String_.ALL_CHILD_NODES_EXPR;
|
||||
this.isAllAttributes_ = expr == goog.ds.Expr.String_.ALL_ATTRIBUTES_EXPR;
|
||||
this.isAllElements_ = expr == goog.ds.Expr.String_.ALL_ELEMENTS_EXPR;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Get the source data path for the expression
|
||||
* @return {string} The path.
|
||||
*/
|
||||
goog.ds.Expr.prototype.getSource = function() {
|
||||
return this.src_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the last part of the expression.
|
||||
* @return {?string} Last part of the expression.
|
||||
*/
|
||||
goog.ds.Expr.prototype.getLast = function() {
|
||||
return this.last_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the parent expression of this expression, or null if this is top level
|
||||
* @return {goog.ds.Expr} The parent.
|
||||
*/
|
||||
goog.ds.Expr.prototype.getParent = function() {
|
||||
if (!this.parentExprSet_) {
|
||||
if (this.size_ > 1) {
|
||||
this.parentExpr_ = goog.ds.Expr.createInternal_(null,
|
||||
this.parts_.slice(0, this.parts_.length - 1), this, null);
|
||||
}
|
||||
this.parentExprSet_ = true;
|
||||
}
|
||||
return this.parentExpr_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the parent expression of this expression, or null if this is top level
|
||||
* @return {goog.ds.Expr} The parent.
|
||||
*/
|
||||
goog.ds.Expr.prototype.getNext = function() {
|
||||
if (!this.nextExprSet_) {
|
||||
if (this.size_ > 1) {
|
||||
this.nextExpr_ = goog.ds.Expr.createInternal_(null, this.parts_.slice(1),
|
||||
null, this);
|
||||
}
|
||||
this.nextExprSet_ = true;
|
||||
}
|
||||
return this.nextExpr_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Evaluate an expression on a data node, and return a value
|
||||
* Recursively walks through child nodes to evaluate
|
||||
* TODO(user) Support other expression functions
|
||||
*
|
||||
* @param {goog.ds.DataNode=} opt_ds Optional datasource to evaluate against.
|
||||
* If not provided, evaluates against DataManager global root.
|
||||
* @return {*} Value of the node, or null if doesn't exist.
|
||||
*/
|
||||
goog.ds.Expr.prototype.getValue = function(opt_ds) {
|
||||
if (opt_ds == null) {
|
||||
opt_ds = goog.ds.DataManager.getInstance();
|
||||
} else if (this.isAbsolute_) {
|
||||
opt_ds = opt_ds.getDataRoot ? opt_ds.getDataRoot() :
|
||||
goog.ds.DataManager.getInstance();
|
||||
}
|
||||
|
||||
if (this.isCount_) {
|
||||
var nodes = this.getNodes(opt_ds);
|
||||
return nodes.getCount();
|
||||
}
|
||||
|
||||
if (this.size_ == 1) {
|
||||
return opt_ds.getChildNodeValue(this.root_);
|
||||
} else if (this.size_ == 0) {
|
||||
return opt_ds.get();
|
||||
}
|
||||
|
||||
var nextDs = opt_ds.getChildNode(this.root_);
|
||||
|
||||
if (nextDs == null) {
|
||||
return null;
|
||||
} else {
|
||||
return this.getNext().getValue(nextDs);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Evaluate an expression on a data node, and return matching nodes
|
||||
* Recursively walks through child nodes to evaluate
|
||||
*
|
||||
* @param {goog.ds.DataNode=} opt_ds Optional datasource to evaluate against.
|
||||
* If not provided, evaluates against data root.
|
||||
* @param {boolean=} opt_canCreate If true, will try to create new nodes.
|
||||
* @return {goog.ds.DataNodeList} Matching nodes.
|
||||
*/
|
||||
goog.ds.Expr.prototype.getNodes = function(opt_ds, opt_canCreate) {
|
||||
return /** @type {goog.ds.DataNodeList} */(this.getNodes_(opt_ds,
|
||||
false, opt_canCreate));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Evaluate an expression on a data node, and return the first matching node
|
||||
* Recursively walks through child nodes to evaluate
|
||||
*
|
||||
* @param {goog.ds.DataNode=} opt_ds Optional datasource to evaluate against.
|
||||
* If not provided, evaluates against DataManager global root.
|
||||
* @param {boolean=} opt_canCreate If true, will try to create new nodes.
|
||||
* @return {goog.ds.DataNode} Matching nodes, or null if doesn't exist.
|
||||
*/
|
||||
goog.ds.Expr.prototype.getNode = function(opt_ds, opt_canCreate) {
|
||||
return /** @type {goog.ds.DataNode} */(this.getNodes_(opt_ds,
|
||||
true, opt_canCreate));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Evaluate an expression on a data node, and return the first matching node
|
||||
* Recursively walks through child nodes to evaluate
|
||||
*
|
||||
* @param {goog.ds.DataNode=} opt_ds Optional datasource to evaluate against.
|
||||
* If not provided, evaluates against DataManager global root.
|
||||
* @param {boolean=} opt_selectOne Whether to return single matching DataNode
|
||||
* or matching nodes in DataNodeList.
|
||||
* @param {boolean=} opt_canCreate If true, will try to create new nodes.
|
||||
* @return {goog.ds.DataNode|goog.ds.DataNodeList} Matching node or nodes,
|
||||
* depending on value of opt_selectOne.
|
||||
* @private
|
||||
*/
|
||||
goog.ds.Expr.prototype.getNodes_ = function(opt_ds, opt_selectOne,
|
||||
opt_canCreate) {
|
||||
if (opt_ds == null) {
|
||||
opt_ds = goog.ds.DataManager.getInstance();
|
||||
} else if (this.isAbsolute_) {
|
||||
opt_ds = opt_ds.getDataRoot ? opt_ds.getDataRoot() :
|
||||
goog.ds.DataManager.getInstance();
|
||||
}
|
||||
|
||||
if (this.size_ == 0 && opt_selectOne) {
|
||||
return opt_ds;
|
||||
} else if (this.size_ == 0 && !opt_selectOne) {
|
||||
return new goog.ds.BasicNodeList([opt_ds]);
|
||||
} else if (this.size_ == 1) {
|
||||
if (opt_selectOne) {
|
||||
return opt_ds.getChildNode(this.root_, opt_canCreate);
|
||||
}
|
||||
else {
|
||||
var possibleListChild = opt_ds.getChildNode(this.root_);
|
||||
if (possibleListChild && possibleListChild.isList()) {
|
||||
return possibleListChild.getChildNodes();
|
||||
} else {
|
||||
return opt_ds.getChildNodes(this.root_);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var nextDs = opt_ds.getChildNode(this.root_, opt_canCreate);
|
||||
if (nextDs == null && opt_selectOne) {
|
||||
return null;
|
||||
} else if (nextDs == null && !opt_selectOne) {
|
||||
return new goog.ds.EmptyNodeList();
|
||||
}
|
||||
return this.getNext().getNodes_(nextDs, opt_selectOne, opt_canCreate);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Whether the expression can be null.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.ds.Expr.prototype.canBeEmpty_ = false;
|
||||
|
||||
|
||||
/**
|
||||
* The parsed paths in the expression
|
||||
*
|
||||
* @type {Array.<string>}
|
||||
* @private
|
||||
*/
|
||||
goog.ds.Expr.prototype.parts_ = [];
|
||||
|
||||
|
||||
/**
|
||||
* Number of paths in the expression
|
||||
*
|
||||
* @type {?number}
|
||||
* @private
|
||||
*/
|
||||
goog.ds.Expr.prototype.size_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* The root node path in the expression
|
||||
*
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
goog.ds.Expr.prototype.root_;
|
||||
|
||||
|
||||
/**
|
||||
* The last path in the expression
|
||||
*
|
||||
* @type {?string}
|
||||
* @private
|
||||
*/
|
||||
goog.ds.Expr.prototype.last_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* Whether the expression evaluates to current node
|
||||
*
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.ds.Expr.prototype.isCurrent_ = false;
|
||||
|
||||
|
||||
/**
|
||||
* Whether the expression is just an attribute
|
||||
*
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.ds.Expr.prototype.isJustAttribute_ = false;
|
||||
|
||||
|
||||
/**
|
||||
* Does this expression select all DOM-style child nodes (element and text)
|
||||
*
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.ds.Expr.prototype.isAllChildNodes_ = false;
|
||||
|
||||
|
||||
/**
|
||||
* Does this expression select all DOM-style attribute nodes (starts with '@')
|
||||
*
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.ds.Expr.prototype.isAllAttributes_ = false;
|
||||
|
||||
|
||||
/**
|
||||
* Does this expression select all DOM-style element child nodes
|
||||
*
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.ds.Expr.prototype.isAllElements_ = false;
|
||||
|
||||
|
||||
/**
|
||||
* The function used by this expression
|
||||
*
|
||||
* @type {?string}
|
||||
* @private
|
||||
*/
|
||||
goog.ds.Expr.prototype.exprFn_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* Cached value for the parent expression.
|
||||
* @type {goog.ds.Expr?}
|
||||
* @private
|
||||
*/
|
||||
goog.ds.Expr.prototype.parentExpr_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* Cached value for the next expression.
|
||||
* @type {goog.ds.Expr?}
|
||||
* @private
|
||||
*/
|
||||
goog.ds.Expr.prototype.nextExpr_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* Create an expression from a string, can use cached values
|
||||
*
|
||||
* @param {string} expr The expression string.
|
||||
* @return {goog.ds.Expr} The expression object.
|
||||
*/
|
||||
goog.ds.Expr.create = function(expr) {
|
||||
var result = goog.ds.Expr.cache_[expr];
|
||||
|
||||
if (result == null) {
|
||||
result = new goog.ds.Expr(expr);
|
||||
goog.ds.Expr.cache_[expr] = result;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Create an expression from a string, can use cached values
|
||||
* Uses hints from related expressions to help in creation
|
||||
*
|
||||
* @param {?string=} opt_expr The string expression source.
|
||||
* @param {Array=} opt_parts Array of the parts of an expression.
|
||||
* @param {goog.ds.Expr=} opt_childExpr Optional child of this expression,
|
||||
* passed in as a hint for processing.
|
||||
* @param {goog.ds.Expr=} opt_prevExpr Optional preceding expression
|
||||
* (i.e. $A/B/C is previous expression to B/C) passed in as a hint for
|
||||
* processing.
|
||||
* @return {goog.ds.Expr} The expression object.
|
||||
* @private
|
||||
*/
|
||||
goog.ds.Expr.createInternal_ = function(opt_expr, opt_parts, opt_childExpr,
|
||||
opt_prevExpr) {
|
||||
var expr = opt_expr || opt_parts.join('/');
|
||||
var result = goog.ds.Expr.cache_[expr];
|
||||
|
||||
if (result == null) {
|
||||
result = new goog.ds.Expr();
|
||||
result.setSource_(expr, opt_parts, opt_childExpr, opt_prevExpr);
|
||||
goog.ds.Expr.cache_[expr] = result;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Cache of pre-parsed expressions
|
||||
* @private
|
||||
*/
|
||||
goog.ds.Expr.cache_ = {};
|
||||
|
||||
|
||||
/**
|
||||
* Commonly used strings in expressions.
|
||||
* @enum {string}
|
||||
* @private
|
||||
*/
|
||||
goog.ds.Expr.String_ = {
|
||||
SEPARATOR: '/',
|
||||
CURRENT_NODE_EXPR: '.',
|
||||
EMPTY_EXPR: '',
|
||||
ATTRIBUTE_START: '@',
|
||||
ALL_CHILD_NODES_EXPR: '*|text()',
|
||||
ALL_ATTRIBUTES_EXPR: '@*',
|
||||
ALL_ELEMENTS_EXPR: '*',
|
||||
NAME_EXPR: 'name()',
|
||||
COUNT_EXPR: 'count()',
|
||||
POSITION_EXPR: 'position()',
|
||||
INDEX_START: '[',
|
||||
INDEX_END: ']',
|
||||
CAN_BE_EMPTY: '?'
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Standard expressions
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* The current node
|
||||
*/
|
||||
goog.ds.Expr.CURRENT = goog.ds.Expr.create(
|
||||
goog.ds.Expr.String_.CURRENT_NODE_EXPR);
|
||||
|
||||
|
||||
/**
|
||||
* For DOM interop - all DOM child nodes (text + element).
|
||||
* Text nodes have dataName #text
|
||||
*/
|
||||
goog.ds.Expr.ALL_CHILD_NODES =
|
||||
goog.ds.Expr.create(goog.ds.Expr.String_.ALL_CHILD_NODES_EXPR);
|
||||
|
||||
|
||||
/**
|
||||
* For DOM interop - all DOM element child nodes
|
||||
*/
|
||||
goog.ds.Expr.ALL_ELEMENTS =
|
||||
goog.ds.Expr.create(goog.ds.Expr.String_.ALL_ELEMENTS_EXPR);
|
||||
|
||||
|
||||
/**
|
||||
* For DOM interop - all DOM attribute nodes
|
||||
* Attribute nodes have dataName starting with "@"
|
||||
*/
|
||||
goog.ds.Expr.ALL_ATTRIBUTES =
|
||||
goog.ds.Expr.create(goog.ds.Expr.String_.ALL_ATTRIBUTES_EXPR);
|
||||
|
||||
|
||||
/**
|
||||
* Get the dataName of a node
|
||||
*/
|
||||
goog.ds.Expr.NAME = goog.ds.Expr.create(goog.ds.Expr.String_.NAME_EXPR);
|
||||
|
||||
|
||||
/**
|
||||
* Get the count of nodes matching an expression
|
||||
*/
|
||||
goog.ds.Expr.COUNT = goog.ds.Expr.create(goog.ds.Expr.String_.COUNT_EXPR);
|
||||
|
||||
|
||||
/**
|
||||
* Get the position of the "current" node in the current node list
|
||||
* This will only apply for datasources that support the concept of a current
|
||||
* node (none exist yet). This is similar to XPath position() and concept of
|
||||
* current node
|
||||
*/
|
||||
goog.ds.Expr.POSITION = goog.ds.Expr.create(goog.ds.Expr.String_.POSITION_EXPR);
|
||||
@@ -0,0 +1,811 @@
|
||||
// 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.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 '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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
// 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);
|
||||
};
|
||||
@@ -0,0 +1,460 @@
|
||||
// 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.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}
|
||||
*/
|
||||
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}
|
||||
*/
|
||||
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_;
|
||||
};
|
||||
@@ -0,0 +1,148 @@
|
||||
// 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.ds.DataManager');
|
||||
goog.require('goog.ds.JsDataSource');
|
||||
goog.require('goog.ds.LoadState');
|
||||
goog.require('goog.ds.logger');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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('script', {'src': uriToCall});
|
||||
goog.dom.getElementsByTagNameAndClass('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'] = {};
|
||||
@@ -0,0 +1,195 @@
|
||||
// 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
|
||||
*/
|
||||
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;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,415 @@
|
||||
// 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.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}
|
||||
*/
|
||||
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());
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user