Path: blob/trunk/third_party/closure/goog/datasource/xmldatasource.js
2868 views
// Copyright 2006 The Closure Library Authors. All Rights Reserved.1//2// Licensed under the Apache License, Version 2.0 (the "License");3// you may not use this file except in compliance with the License.4// You may obtain a copy of the License at5//6// http://www.apache.org/licenses/LICENSE-2.07//8// Unless required by applicable law or agreed to in writing, software9// distributed under the License is distributed on an "AS-IS" BASIS,10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.11// See the License for the specific language governing permissions and12// limitations under the License.1314/**15* @fileoverview16* Implementations of DataNode for wrapping XML data.17*18*/1920goog.provide('goog.ds.XmlDataSource');21goog.provide('goog.ds.XmlHttpDataSource');2223goog.require('goog.Uri');24goog.require('goog.dom.NodeType');25goog.require('goog.dom.xml');26goog.require('goog.ds.BasicNodeList');27goog.require('goog.ds.DataManager');28goog.require('goog.ds.DataNode');29goog.require('goog.ds.LoadState');30goog.require('goog.ds.logger');31goog.require('goog.log');32goog.require('goog.net.XhrIo');33goog.require('goog.string');34353637/**38* Data source whose backing is an xml node39*40* @param {Node} node The XML node. Can be null.41* @param {goog.ds.XmlDataSource} parent Parent of XML element. Can be null.42* @param {string=} opt_name The name of this node relative to the parent node.43*44* @extends {goog.ds.DataNode}45* @constructor46*/47// TODO(arv): Use interfaces when available.48goog.ds.XmlDataSource = function(node, parent, opt_name) {49this.parent_ = parent;50this.dataName_ = opt_name || (node ? node.nodeName : '');51this.setNode_(node);52};535455/**56* Constant to select XML attributes for getChildNodes57* @type {string}58* @private59*/60goog.ds.XmlDataSource.ATTRIBUTE_SELECTOR_ = '@*';616263/**64* Set the current root nodeof the data source.65* Can be an attribute node, text node, or element node66* @param {Node} node The node. Can be null.67*68* @private69*/70goog.ds.XmlDataSource.prototype.setNode_ = function(node) {71this.node_ = node;72if (node != null) {73switch (node.nodeType) {74case goog.dom.NodeType.ATTRIBUTE:75case goog.dom.NodeType.TEXT:76this.value_ = node.nodeValue;77break;78case goog.dom.NodeType.ELEMENT:79if (node.childNodes.length == 1 &&80node.firstChild.nodeType == goog.dom.NodeType.TEXT) {81this.value_ = node.firstChild.nodeValue;82}83}84}85};868788/**89* Creates the DataNodeList with the child nodes for this element.90* Allows for only building list as needed.91*92* @private93*/94goog.ds.XmlDataSource.prototype.createChildNodes_ = function() {95if (this.childNodeList_) {96return;97}98var childNodeList = new goog.ds.BasicNodeList();99if (this.node_ != null) {100var childNodes = this.node_.childNodes;101for (var i = 0, childNode; childNode = childNodes[i]; i++) {102if (childNode.nodeType != goog.dom.NodeType.TEXT ||103!goog.ds.XmlDataSource.isEmptyTextNodeValue_(childNode.nodeValue)) {104var newNode =105new goog.ds.XmlDataSource(childNode, this, childNode.nodeName);106childNodeList.add(newNode);107}108}109}110this.childNodeList_ = childNodeList;111};112113114/**115* Creates the DataNodeList with the attributes for the element116* Allows for only building list as needed.117*118* @private119*/120goog.ds.XmlDataSource.prototype.createAttributes_ = function() {121if (this.attributes_) {122return;123}124var attributes = new goog.ds.BasicNodeList();125if (this.node_ != null && this.node_.attributes != null) {126var atts = this.node_.attributes;127for (var i = 0, att; att = atts[i]; i++) {128var newNode = new goog.ds.XmlDataSource(att, this, att.nodeName);129attributes.add(newNode);130}131}132this.attributes_ = attributes;133};134135136/**137* Get the value of the node138* @return {Object} The value of the node, or null if no value.139* @override140*/141goog.ds.XmlDataSource.prototype.get = function() {142this.createChildNodes_();143return this.value_;144};145146147/**148* Set the value of the node149* @param {*} value The new value of the node.150* @override151*/152goog.ds.XmlDataSource.prototype.set = function(value) {153throw Error('Can\'t set on XmlDataSource yet');154};155156157/** @override */158goog.ds.XmlDataSource.prototype.getChildNodes = function(opt_selector) {159if (opt_selector &&160opt_selector == goog.ds.XmlDataSource.ATTRIBUTE_SELECTOR_) {161this.createAttributes_();162return this.attributes_;163} else if (164opt_selector == null ||165opt_selector == goog.ds.STR_ALL_CHILDREN_SELECTOR) {166this.createChildNodes_();167return this.childNodeList_;168} else {169throw Error('Unsupported selector');170}171172};173174175/**176* Gets a named child node of the current node177* @param {string} name The node name.178* @return {goog.ds.DataNode} The child node, or null if179* no node of this name exists.180* @override181*/182goog.ds.XmlDataSource.prototype.getChildNode = function(name) {183if (goog.string.startsWith(name, goog.ds.STR_ATTRIBUTE_START)) {184var att = this.node_.getAttributeNode(name.substring(1));185return att ? new goog.ds.XmlDataSource(att, this) : null;186} else {187return /** @type {goog.ds.DataNode} */ (this.getChildNodes().get(name));188}189};190191192/**193* Gets the value of a child node194* @param {string} name The node name.195* @return {*} The value of the node, or null if no value or the child node196* doesn't exist.197* @override198*/199goog.ds.XmlDataSource.prototype.getChildNodeValue = function(name) {200if (goog.string.startsWith(name, goog.ds.STR_ATTRIBUTE_START)) {201var node = this.node_.getAttributeNode(name.substring(1));202return node ? node.nodeValue : null;203} else {204var node = this.getChildNode(name);205return node ? node.get() : null;206}207};208209210/**211* Get the name of the node relative to the parent node212* @return {string} The name of the node.213* @override214*/215goog.ds.XmlDataSource.prototype.getDataName = function() {216return this.dataName_;217};218219220/**221* Setthe name of the node relative to the parent node222* @param {string} name The name of the node.223* @override224*/225goog.ds.XmlDataSource.prototype.setDataName = function(name) {226this.dataName_ = name;227};228229230/**231* Gets the a qualified data path to this node232* @return {string} The data path.233* @override234*/235goog.ds.XmlDataSource.prototype.getDataPath = function() {236var parentPath = '';237if (this.parent_) {238parentPath = this.parent_.getDataPath() +239(this.dataName_.indexOf(goog.ds.STR_ARRAY_START) != -1 ?240'' :241goog.ds.STR_PATH_SEPARATOR);242}243244return parentPath + this.dataName_;245};246247248/**249* Load or reload the backing data for this node250* @override251*/252goog.ds.XmlDataSource.prototype.load = function() {253// Nothing to do254};255256257/**258* Gets the state of the backing data for this node259* @return {goog.ds.LoadState} The state.260* @override261*/262goog.ds.XmlDataSource.prototype.getLoadState = function() {263return this.node_ ? goog.ds.LoadState.LOADED : goog.ds.LoadState.NOT_LOADED;264};265266267/**268* Check whether a node is an empty text node. Nodes consisting of only white269* space (#x20, #xD, #xA, #x9) can generally be collapsed to a zero length270* text string.271* @param {string} str String to match.272* @return {boolean} True if string equates to empty text node.273* @private274*/275goog.ds.XmlDataSource.isEmptyTextNodeValue_ = function(str) {276return /^[\r\n\t ]*$/.test(str);277};278279280/**281* Creates an XML document with one empty node.282* Useful for places where you need a node that283* can be queried against.284*285* @return {Document} Document with one empty node.286* @private287*/288goog.ds.XmlDataSource.createChildlessDocument_ = function() {289return goog.dom.xml.createDocument('nothing');290};291292293294/**295* Data source whose backing is an XMLHttpRequest,296*297* A URI of an empty string will mean that no request is made298* and the data source will be a single, empty node.299*300* @param {(string|goog.Uri)} uri URL of the XMLHttpRequest.301* @param {string} name Name of the datasource.302*303* implements goog.ds.XmlHttpDataSource.304* @constructor305* @extends {goog.ds.XmlDataSource}306* @final307*/308goog.ds.XmlHttpDataSource = function(uri, name) {309goog.ds.XmlDataSource.call(this, null, null, name);310if (uri) {311this.uri_ = new goog.Uri(uri);312} else {313this.uri_ = null;314}315};316goog.inherits(goog.ds.XmlHttpDataSource, goog.ds.XmlDataSource);317318319/**320* Default load state is NOT_LOADED321* @private322*/323goog.ds.XmlHttpDataSource.prototype.loadState_ = goog.ds.LoadState.NOT_LOADED;324325326/**327* Load or reload the backing data for this node.328* Fires the XMLHttpRequest329* @override330*/331goog.ds.XmlHttpDataSource.prototype.load = function() {332if (this.uri_) {333goog.log.info(334goog.ds.logger, 'Sending XML request for DataSource ' +335this.getDataName() + ' to ' + this.uri_);336this.loadState_ = goog.ds.LoadState.LOADING;337338goog.net.XhrIo.send(this.uri_, goog.bind(this.complete_, this));339} else {340this.node_ = goog.ds.XmlDataSource.createChildlessDocument_();341this.loadState_ = goog.ds.LoadState.NOT_LOADED;342}343};344345346/**347* Gets the state of the backing data for this node348* @return {goog.ds.LoadState} The state.349* @override350*/351goog.ds.XmlHttpDataSource.prototype.getLoadState = function() {352return this.loadState_;353};354355356/**357* Handles the completion of an XhrIo request. Dispatches to success or load358* based on the result.359* @param {!goog.events.Event} e The XhrIo event object.360* @private361*/362goog.ds.XmlHttpDataSource.prototype.complete_ = function(e) {363var xhr = /** @type {goog.net.XhrIo} */ (e.target);364if (xhr && xhr.isSuccess()) {365this.success_(xhr);366} else {367this.failure_();368}369};370371372/**373* Success result. Checks whether valid XML was returned374* and sets the XML and loadstate.375*376* @param {!goog.net.XhrIo} xhr The successful XhrIo object.377* @private378*/379goog.ds.XmlHttpDataSource.prototype.success_ = function(xhr) {380goog.log.info(381goog.ds.logger, 'Got data for DataSource ' + this.getDataName());382var xml = xhr.getResponseXml();383384// Fix for case where IE returns valid XML as text but385// doesn't parse by default386if (xml && !xml.hasChildNodes() && goog.isObject(xhr.getResponseText())) {387xml = goog.dom.xml.loadXml(xhr.getResponseText());388}389// Failure result390if (!xml || !xml.hasChildNodes()) {391this.loadState_ = goog.ds.LoadState.FAILED;392this.node_ = goog.ds.XmlDataSource.createChildlessDocument_();393} else {394this.loadState_ = goog.ds.LoadState.LOADED;395this.node_ = xml.documentElement;396}397398if (this.getDataName()) {399goog.ds.DataManager.getInstance().fireDataChange(this.getDataName());400}401};402403404/**405* Failure result406*407* @private408*/409goog.ds.XmlHttpDataSource.prototype.failure_ = function() {410goog.log.info(411goog.ds.logger,412'Data retrieve failed for DataSource ' + this.getDataName());413414this.loadState_ = goog.ds.LoadState.FAILED;415this.node_ = goog.ds.XmlDataSource.createChildlessDocument_();416417if (this.getDataName()) {418goog.ds.DataManager.getInstance().fireDataChange(this.getDataName());419}420};421422423