Blame | Last modification | View Log | RSS feed
if(!dojo._hasResource["dojox.data.XmlStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.dojo._hasResource["dojox.data.XmlStore"] = true;dojo.provide("dojox.data.XmlStore");dojo.provide("dojox.data.XmlItem");dojo.require("dojo.data.util.simpleFetch");dojo.require("dojo.data.util.filter");dojo.require("dojox.data.dom");dojo.declare("dojox.data.XmlStore", null, {// summary:// A data store for XML based services or documents// description:// A data store for XML based services or documentsconstructor: function(/* object */ args) {// summary:// Constructor for the XML store.// args:// An anonymous object to initialize properties. It expects the following values:// url: The url to a service or an XML document that represents the store// rootItem: A tag name for root items// keyAttribute: An attribute name for a key or an indentify// attributeMap: An anonymous object contains properties for attribute mapping,// {"tag_name.item_attribute_name": "@xml_attribute_name", ...}// sendQuery: A boolean indicate to add a query string to the service URLconsole.log("XmlStore()");if(args){this._url = args.url;this._rootItem = (args.rootItem || args.rootitem);this._keyAttribute = (args.keyAttribute || args.keyattribute);this._attributeMap = (args.attributeMap || args.attributemap);this._labelAttr = args.label;this._sendQuery = (args.sendQuery || args.sendquery);}this._newItems = [];this._deletedItems = [];this._modifiedItems = [];},/* dojo.data.api.Read */getValue: function(/* item */ item, /* attribute || attribute-name-string */ attribute, /* value? */ defaultValue){// summary:// Return an attribute value// description:// 'item' must be an instance of a dojox.data.XmlItem from the store instance.// If 'attribute' specifies "tagName", the tag name of the element is// returned.// If 'attribute' specifies "childNodes", the first element child is// returned.// If 'attribute' specifies "text()", the value of the first text// child is returned.// For generic attributes, if '_attributeMap' is specified,// an actual attribute name is looked up with the tag name of// the element and 'attribute' (concatenated with '.').// Then, if 'attribute' starts with "@", the value of the XML// attribute is returned.// Otherwise, the first child element of the tag name specified with// 'attribute' is returned.// item:// An XML element that holds the attribute// attribute:// A tag name of a child element, An XML attribute name or one of// special names// defaultValue:// A default value// returns:// An attribute value found, otherwise 'defaultValue'var element = item.element;if(attribute === "tagName"){return element.nodeName;}else if (attribute === "childNodes"){for (var i = 0; i < element.childNodes.length; i++) {var node = element.childNodes[i];if (node.nodeType === 1 /*ELEMENT_NODE*/) {return this._getItem(node); //object}}return defaultValue;}else if(attribute === "text()"){for(var i = 0; i < element.childNodes.length; i++){var node = element.childNodes[i];if(node.nodeType === 3 /*TEXT_NODE*/ ||node.nodeType === 4 /*CDATA_SECTION_NODE*/){return node.nodeValue; //string}}return defaultValue;}else{attribute = this._getAttribute(element.nodeName, attribute);if(attribute.charAt(0) === '@'){var name = attribute.substring(1);var value = element.getAttribute(name);return (value !== undefined) ? value : defaultValue; //object}else{for(var i = 0; i < element.childNodes.length; i++){var node = element.childNodes[i];if( node.nodeType === 1 /*ELEMENT_NODE*/ &&node.nodeName === attribute){return this._getItem(node); //object}}return defaultValue; //object}}},getValues: function(/* item */ item, /* attribute || attribute-name-string */ attribute){// summary:// Return an array of attribute values// description:// 'item' must be an instance of a dojox.data.XmlItem from the store instance.// If 'attribute' specifies "tagName", the tag name of the element is// returned.// If 'attribute' specifies "childNodes", child elements are returned.// If 'attribute' specifies "text()", the values of child text nodes// are returned.// For generic attributes, if '_attributeMap' is specified,// an actual attribute name is looked up with the tag name of// the element and 'attribute' (concatenated with '.').// Then, if 'attribute' starts with "@", the value of the XML// attribute is returned.// Otherwise, child elements of the tag name specified with// 'attribute' are returned.// item:// An XML element that holds the attribute// attribute:// A tag name of child elements, An XML attribute name or one of// special names// returns:// An array of attribute values found, otherwise an empty arrayvar element = item.element;if(attribute === "tagName"){return [element.nodeName];}else if(attribute === "childNodes"){var values = [];for(var i = 0; i < element.childNodes.length; i++){var node = element.childNodes[i];if(node.nodeType === 1 /*ELEMENT_NODE*/){values.push(this._getItem(node));}}return values; //array}else if(attribute === "text()"){var values = [];for(var i = 0; i < element.childNodes.length; i++){var node = childNodes[i];if(node.nodeType === 3){values.push(node.nodeValue);}}return values; //array}else{attribute = this._getAttribute(element.nodeName, attribute);if(attribute.charAt(0) === '@'){var name = attribute.substring(1);var value = element.getAttribute(name);return (value !== undefined) ? [value] : []; //array}else{var values = [];for(var i = 0; i < element.childNodes.length; i++){var node = element.childNodes[i];if( node.nodeType === 1 /*ELEMENT_NODE*/ &&node.nodeName === attribute){values.push(this._getItem(node));}}return values; //array}}},getAttributes: function(/* item */ item) {// summary:// Return an array of attribute names// description:// 'item' must be an instance of a dojox.data.XmlItem from the store instance.// tag names of child elements and XML attribute names of attributes// specified to the element are returned along with special attribute// names applicable to the element including "tagName", "childNodes"// if the element has child elements, "text()" if the element has// child text nodes, and attribute names in '_attributeMap' that match// the tag name of the element.// item:// An XML element// returns:// An array of attributes foundvar element = item.element;var attributes = [];attributes.push("tagName");if(element.childNodes.length > 0){var names = {};var childNodes = true;var text = false;for(var i = 0; i < element.childNodes.length; i++){var node = element.childNodes[i];if (node.nodeType === 1 /*ELEMENT_NODE*/) {var name = node.nodeName;if(!names[name]){attributes.push(name);names[name] = name;}childNodes = true;}else if(node.nodeType === 3){text = true;}}if(childNodes){attributes.push("childNodes");}if(text){attributes.push("text()");}}for(var i = 0; i < element.attributes.length; i++){attributes.push("@" + element.attributes[i].nodeName);}if(this._attributeMap){for (var key in this._attributeMap){var i = key.indexOf('.');if(i > 0){var tagName = key.substring(0, i);if (tagName === element.nodeName){attributes.push(key.substring(i + 1));}}else{ // global attributeattributes.push(key);}}}return attributes; //array},hasAttribute: function(/* item */ item, /* attribute || attribute-name-string */ attribute){// summary:// Check whether an element has the attribute// item:// 'item' must be an instance of a dojox.data.XmlItem from the store instance.// attribute:// A tag name of a child element, An XML attribute name or one of// special names// returns:// True if the element has the attribute, otherwise falsereturn (this.getValue(item, attribute) !== undefined); //boolean},containsValue: function(/* item */ item, /* attribute || attribute-name-string */ attribute, /* anything */ value){// summary:// Check whether the attribute values contain the value// item:// 'item' must be an instance of a dojox.data.XmlItem from the store instance.// attribute:// A tag name of a child element, An XML attribute name or one of// special names// returns:// True if the attribute values contain the value, otherwise falsevar values = this.getValues(item, attribute);for(var i = 0; i < values.length; i++){if((typeof value === "string")){if(values[i].toString && values[i].toString() === value){return true;}}else if (values[i] === value){return true; //boolean}}return false;//boolean},isItem: function(/* anything */ something){// summary:// Check whether the object is an item (XML element)// item:// An object to check// returns:// True if the object is an XML element, otherwise falseif(something && something.element && something.store && something.store === this){return true; //boolean}return false; //boolran},isItemLoaded: function(/* anything */ something){// summary:// Check whether the object is an item (XML element) and loaded// item:// An object to check// returns:// True if the object is an XML element, otherwise falsereturn this.isItem(something); //boolean},loadItem: function(/* object */ keywordArgs){// summary:// Load an item (XML element)// keywordArgs:// object containing the args for loadItem. See dojo.data.api.Read.loadItem()},getFeatures: function() {// summary:// Return supported data APIs// returns:// "dojo.data.api.Read" and "dojo.data.api.Write"var features = {"dojo.data.api.Read": true,"dojo.data.api.Write": true};return features; //array},getLabel: function(/* item */ item){// summary:// See dojo.data.api.Read.getLabel()if(this._labelAttr && this.isItem(item)){var label = this.getValue(item,this._labelAttr);if(label){return label.toString();}}return undefined; //undefined},getLabelAttributes: function(/* item */ item){// summary:// See dojo.data.api.Read.getLabelAttributes()if(this._labelAttr){return [this._labelAttr]; //array}return null; //null},_fetchItems: function(request, fetchHandler, errorHandler) {// summary:// Fetch items (XML elements) that match to a query// description:// If '_sendQuery' is true, an XML document is loaded from// '_url' with a query string.// Otherwise, an XML document is loaded and list XML elements that// match to a query (set of element names and their text attribute// values that the items to contain).// A wildcard, "*" can be used to query values to match all// occurrences.// If '_rootItem' is specified, it is used to fetch items.// request:// A request object// fetchHandler:// A function to call for fetched items// errorHandler:// A function to call on errorvar url = this._getFetchUrl(request);console.log("XmlStore._fetchItems(): url=" + url);if(!url){errorHandler(new Error("No URL specified."));return;}var localRequest = (!this._sendQuery ? request : null); // use request for _getItems()var self = this;var getArgs = {url: url,handleAs: "xml",preventCache: true};var getHandler = dojo.xhrGet(getArgs);getHandler.addCallback(function(data){var items = self._getItems(data, localRequest);console.log("XmlStore._fetchItems(): length=" + (items ? items.length : 0));if (items && items.length > 0) {fetchHandler(items, request);}else {fetchHandler([], request);}});getHandler.addErrback(function(data){errorHandler(data, request);});},_getFetchUrl: function(request){// summary:// Generate a URL for fetch// description:// This default implementation generates a query string in the form of// "?name1=value1&name2=value2..." off properties of 'query' object// specified in 'request' and appends it to '_url', if '_sendQuery'// is set to false.// Otherwise, '_url' is returned as is.// Sub-classes may override this method for the custom URL generation.// request:// A request object// returns:// A fetch URLif(!this._sendQuery){return this._url;}var query = request.query;if(!query){return this._url;}if(dojo.isString(query)){return this._url + query;}var queryString = "";for(var name in query){var value = query[name];if(value){if(queryString){queryString += "&";}queryString += (name + "=" + value);}}if(!queryString){return this._url;}//Check to see if the URL already has query params or not.var fullUrl = this._url;if(fullUrl.indexOf("?") < 0){fullUrl += "?";}else{fullUrl += "&";}return fullUrl + queryString;},_getItems: function(document, request) {// summary:// Fetch items (XML elements) in an XML document based on a request// description:// This default implementation walks through child elements of// the document element to see if all properties of 'query' object// match corresponding attributes of the element (item).// If 'request' is not specified, all child elements are returned.// Sub-classes may override this method for the custom search in// an XML document.// document:// An XML document// request:// A request object// returns:// An array of itemsvar query = null;if(request){query = request.query;}var items = [];var nodes = null;if(this._rootItem){nodes = document.getElementsByTagName(this._rootItem);}else{nodes = document.documentElement.childNodes;}for(var i = 0; i < nodes.length; i++){var node = nodes[i];if(node.nodeType != 1 /*ELEMENT_NODE*/){continue;}var item = this._getItem(node);if(query){var found = true;var ignoreCase = request.queryOptions ? request.queryOptions.ignoreCase : false;//See if there are any string values that can be regexp parsed first to avoid multiple regexp gens on the//same value for each item examined. Much more efficient.var regexpList = {};for(var key in query){var value = query[key];if(typeof value === "string"){regexpList[key] = dojo.data.util.filter.patternToRegExp(value, ignoreCase);}}for(var attribute in query){var value = this.getValue(item, attribute);if(value){var queryValue = query[attribute];if ((typeof value) === "string" &&(regexpList[attribute])){if((value.match(regexpList[attribute])) !== null){continue;}}else if((typeof value) === "object"){if( value.toString &&(regexpList[attribute])){var stringValue = value.toString();if((stringValue.match(regexpList[attribute])) !== null){continue;}}else{if(queryValue === "*" || queryValue === value){continue;}}}}found = false;break;}if(!found){continue;}}items.push(item);}dojo.forEach(items,function(item){item.element.parentNode.removeChild(item.element); // make it root},this);return items;},close: function(/*dojo.data.api.Request || keywordArgs || null */ request){// summary:// See dojo.data.api.Read.close()},/* dojo.data.api.Write */newItem: function(/* object? */ keywordArgs){// summary:// Return a new dojox.data.XmlItem// description:// At least, 'keywordArgs' must contain "tagName" to be used for// the new element.// Other attributes in 'keywordArgs' are set to the new element,// including "text()", but excluding "childNodes".// keywordArgs:// An object containing initial attributes// returns:// An XML elementconsole.log("XmlStore.newItem()");keywordArgs = (keywordArgs || {});var tagName = keywordArgs.tagName;if(!tagName){tagName = this._rootItem;if(!tagName){return null;}}var document = this._getDocument();var element = document.createElement(tagName);for(var attribute in keywordArgs){if(attribute === "tagName"){continue;}else if(attribute === "text()"){var text = document.createTextNode(keywordArgs[attribute]);element.appendChild(text);}else{attribute = this._getAttribute(tagName, attribute);if(attribute.charAt(0) === '@'){var name = attribute.substring(1);element.setAttribute(name, keywordArgs[attribute]);}else{var child = document.createElement(attribute);var text = document.createTextNode(keywordArgs[attribute]);child.appendChild(text);element.appendChild(child);}}}var item = this._getItem(element);this._newItems.push(item);return item; //object},deleteItem: function(/* item */ item){// summary:// Delete an dojox.data.XmlItem (wrapper to a XML element).// item:// An XML element to delete// returns:// Trueconsole.log("XmlStore.deleteItem()");var element = item.element;if(element.parentNode){this._backupItem(item);element.parentNode.removeChild(element);return true;}this._forgetItem(item);this._deletedItems.push(item);return true; //boolean},setValue: function(/* item */ item, /* attribute || string */ attribute, /* almost anything */ value){// summary:// Set an attribute value// description:// 'item' must be an instance of a dojox.data.XmlItem from the store instance.// If 'attribute' specifies "tagName", nothing is set and false is// returned.// If 'attribute' specifies "childNodes", the value (XML element) is// added to the element.// If 'attribute' specifies "text()", a text node is created with// the value and set it to the element as a child.// For generic attributes, if '_attributeMap' is specified,// an actual attribute name is looked up with the tag name of// the element and 'attribute' (concatenated with '.').// Then, if 'attribute' starts with "@", the value is set to the XML// attribute.// Otherwise, a text node is created with the value and set it to// the first child element of the tag name specified with 'attribute'.// If the child element does not exist, it is created.// item:// An XML element that holds the attribute// attribute:// A tag name of a child element, An XML attribute name or one of// special names// value:// A attribute value to set// returns:// False for "tagName", otherwise trueif(attribute === "tagName"){return false; //boolean}this._backupItem(item);var element = item.element;if(attribute === "childNodes"){var child = value.element;element.appendChild(child);}else if(attribute === "text()"){while (element.firstChild){element.removeChild(element.firstChild);}var text = this._getDocument(element).createTextNode(value);element.appendChild(text);}else{attribute = this._getAttribute(element.nodeName, attribute);if(attribute.charAt(0) === '@'){var name = attribute.substring(1);element.setAttribute(name, value);}else{var child = null;for(var i = 0; i < element.childNodes.length; i++){var node = element.childNodes[i];if( node.nodeType === 1 /*ELEMENT_NODE*/&&node.nodeName === attribute){child = node;break;}}var document = this._getDocument(element);if(child){while(child.firstChild){child.removeChild(child.firstChild);}}else{child = document.createElement(attribute);element.appendChild(child);}var text = document.createTextNode(value);child.appendChild(text);}}return true; //boolean},setValues: function(/* item */ item, /* attribute || string */ attribute, /* array */ values){// summary:// Set attribute values// description:// 'item' must be an instance of a dojox.data.XmlItem from the store instance.// If 'attribute' specifies "tagName", nothing is set and false is// returned.// If 'attribute' specifies "childNodes", the value (array of XML// elements) is set to the element's childNodes.// If 'attribute' specifies "text()", a text node is created with// the values and set it to the element as a child.// For generic attributes, if '_attributeMap' is specified,// an actual attribute name is looked up with the tag name of// the element and 'attribute' (concatenated with '.').// Then, if 'attribute' starts with "@", the first value is set to// the XML attribute.// Otherwise, child elements of the tag name specified with// 'attribute' are replaced with new child elements and their// child text nodes of values.// item:// An XML element that holds the attribute// attribute:// A tag name of child elements, an XML attribute name or one of// special names// value:// A attribute value to set// returns:// False for "tagName", otherwise trueif(attribute === "tagName"){return false; //boolean}this._backupItem(item);var element = item.element;if(attribute === "childNodes"){while(element.firstChild){element.removeChild(element.firstChild);}for(var i = 0; i < values.length; i++){var child = values[i].element;element.appendChild(child);}}else if(attribute === "text()"){while (element.firstChild){element.removeChild(element.firstChild);}var value = "";for(var i = 0; i < values.length; i++){value += values[i];}var text = this._getDocument(element).createTextNode(value);element.appendChild(text);}else{attribute = this._getAttribute(element.nodeName, attribute);if(attribute.charAt(0) === '@'){var name = attribute.substring(1);element.setAttribute(name, values[0]);}else{for(var i = element.childNodes.length - 1; i >= 0; i--){var node = element.childNodes[i];if( node.nodeType === 1 /*ELEMENT_NODE*/ &&node.nodeName === attribute){element.removeChild(node);}}var document = this._getDocument(element);for(var i = 0; i < values.length; i++){var child = document.createElement(attribute);var text = document.createTextNode(values[i]);child.appendChild(text);element.appendChild(child);}}}return true; //boolean},unsetAttribute: function(/* item */ item, /* attribute || string */ attribute){// summary:// Remove an attribute// description:// 'item' must be an instance of a dojox.data.XmlItem from the store instance.// 'attribute' can be an XML attribute name of the element or one of// special names described below.// If 'attribute' specifies "tagName", nothing is removed and false is// returned.// If 'attribute' specifies "childNodes" or "text()", all child nodes// are removed.// For generic attributes, if '_attributeMap' is specified,// an actual attribute name is looked up with the tag name of// the element and 'attribute' (concatenated with '.').// Then, if 'attribute' starts with "@", the XML attribute is removed.// Otherwise, child elements of the tag name specified with// 'attribute' are removed.// item:// An XML element that holds the attribute// attribute:// A tag name of child elements, an XML attribute name or one of// special names// returns:// False for "tagName", otherwise trueif(attribute === "tagName"){return false; //boolean}this._backupItem(item);var element = item.element;if(attribute === "childNodes" || attribute === "text()"){while(element.firstChild){element.removeChild(element.firstChild);}}else{attribute = this._getAttribute(element.nodeName, attribute);if(attribute.charAt(0) === '@'){var name = attribute.substring(1);element.removeAttribute(name);}else{for(var i = element.childNodes.length - 1; i >= 0; i--){var node = element.childNodes[i];if( node.nodeType === 1 /*ELEMENT_NODE*/ &&node.nodeName === attribute){element.removeChild(node);}}}}return true; //boolean},save: function(/* object */ keywordArgs){// summary:// Save new and/or modified items (XML elements)// description:// '_url' is used to save XML documents for new, modified and/or// deleted XML elements.// keywordArgs:// An object for callbacksif(!keywordArgs){keywordArgs = {};}for(var i = 0; i < this._modifiedItems.length; i++){this._saveItem(this._modifiedItems[i], keywordArgs, "PUT");}for(var i = 0; i < this._newItems.length; i++){var item = this._newItems[i];if(item.element.parentNode){ // reparentedthis._newItems.splice(i, 1);i--;continue;}this._saveItem(this._newItems[i], keywordArgs, "POST");}for(var i = 0; i < this._deletedItems.length; i++){this._saveItem(this._deletedItems[i], keywordArgs, "DELETE");}},revert: function(){// summary:// Invalidate changes (new and/or modified elements)// returns:// Trueconsole.log("XmlStore.revert() _newItems=" + this._newItems.length);console.log("XmlStore.revert() _deletedItems=" + this._deletedItems.length);console.log("XmlStore.revert() _modifiedItems=" + this._modifiedItems.length);this._newItems = [];this._restoreItems(this._deletedItems);this._deletedItems = [];this._restoreItems(this._modifiedItems);this._modifiedItems = [];return true; //boolean},isDirty: function(/* item? */ item){// summary:// Check whether an item is new, modified or deleted// description:// If 'item' is specified, true is returned if the item is new,// modified or deleted.// Otherwise, true is returned if there are any new, modified// or deleted items.// item:// An item (XML element) to check// returns:// True if an item or items are new, modified or deleted, otherwise// falseif (item) {var element = this._getRootElement(item.element);return (this._getItemIndex(this._newItems, element) >= 0 ||this._getItemIndex(this._deletedItems, element) >= 0 ||this._getItemIndex(this._modifiedItems, element) >= 0); //boolean}else {return (this._newItems.length > 0 ||this._deletedItems.length > 0 ||this._modifiedItems.length > 0); //boolean}},_saveItem: function(item, keywordArgs, method){if(method === "PUT"){url = this._getPutUrl(item);}else if(method === "DELETE"){url = this._getDeleteUrl(item);}else{ // POSTurl = this._getPostUrl(item);}if(!url){if(keywordArgs.onError){keywordArgs.onError.call(scope, new Error("No URL for saving content: " + postContent));}return;}var saveArgs = {url: url,method: (method || "POST"),contentType: "text/xml",handleAs: "xml"};var saveHander;if(method === "PUT"){saveArgs.putData = this._getPutContent(item);saveHandler = dojo.rawXhrPut(saveArgs);}else if(method === "DELETE"){saveHandler = dojo.xhrDelete(saveArgs);}else{ // POSTsaveArgs.postData = this._getPostContent(item);saveHandler = dojo.rawXhrPost(saveArgs);}var scope = (keywordArgs.scope || dojo.global);var self = this;saveHandler.addCallback(function(data){self._forgetItem(item);if(keywordArgs.onComplete){keywordArgs.onComplete.call(scope);}});saveHandler.addErrback(function(error){if(keywordArgs.onError){keywordArgs.onError.call(scope, error);}});},_getPostUrl: function(item){// summary:// Generate a URL for post// description:// This default implementation just returns '_url'.// Sub-classes may override this method for the custom URL.// item:// An item to save// returns:// A post URLreturn this._url; //string},_getPutUrl: function(item){// summary:// Generate a URL for put// description:// This default implementation just returns '_url'.// Sub-classes may override this method for the custom URL.// item:// An item to save// returns:// A put URLreturn this._url; //string},_getDeleteUrl: function(item){// summary:// Generate a URL for delete// description:// This default implementation returns '_url' with '_keyAttribute'// as a query string.// Sub-classes may override this method for the custom URL based on// changes (new, deleted, or modified).// item:// An item to delete// returns:// A delete URLif (!this._url) {return this._url; //string}var url = this._url;if (item && this._keyAttribute) {var value = this.getValue(item, this._keyAttribute);if (value) {url = url + '?' + this._keyAttribute + '=' + value;}}return url; //string},_getPostContent: function(item){// summary:// Generate a content to post// description:// This default implementation generates an XML document for one// (the first only) new or modified element.// Sub-classes may override this method for the custom post content// generation.// item:// An item to save// returns:// A post contentvar element = item.element;var declaration = "<?xml version=\"1.0\"?>"; // FIXME: encoding?return declaration + dojox.data.dom.innerXML(element); //XML string},_getPutContent: function(item){// summary:// Generate a content to put// description:// This default implementation generates an XML document for one// (the first only) new or modified element.// Sub-classes may override this method for the custom put content// generation.// item:// An item to save// returns:// A post contentvar element = item.element;var declaration = "<?xml version=\"1.0\"?>"; // FIXME: encoding?return declaration + dojox.data.dom.innerXML(element); //XML string},/* internal API */_getAttribute: function(tagName, attribute){if(this._attributeMap){var key = tagName + "." + attribute;var value = this._attributeMap[key];if(value){attribute = value;}else{ // look for global attributevalue = this._attributeMap[attribute];if(value){attribute = value;}}}return attribute; //object},_getItem: function(element){return new dojox.data.XmlItem(element, this); //object},_getItemIndex: function(items, element){for(var i = 0; i < items.length; i++){if(items[i].element === element){return i; //int}}return -1; //int},_backupItem: function(item){var element = this._getRootElement(item.element);if( this._getItemIndex(this._newItems, element) >= 0 ||this._getItemIndex(this._modifiedItems, element) >= 0){return; // new or already modified}if(element != item.element){item = this._getItem(element);}item._backup = element.cloneNode(true);this._modifiedItems.push(item);},_restoreItems: function(items){dojo.forEach(items,function(item){if(item._backup){item.element = item._backup;item._backup = null;}},this);},_forgetItem: function(item){var element = item.element;var index = this._getItemIndex(this._newItems, element);if(index >= 0){this._newItems.splice(index, 1);}index = this._getItemIndex(this._deletedItems, element);if(index >= 0){this._deletedItems.splice(index, 1);}index = this._getItemIndex(this._modifiedItems, element);if(index >= 0){this._modifiedItems.splice(index, 1);}},_getDocument: function(element){if(element){return element.ownerDocument; //DOMDocument}else if(!this._document){return dojox.data.dom.createDocument(); // DOMDocument}},_getRootElement: function(element){while(element.parentNode){element = element.parentNode;}return element; //DOMElement}});//FIXME: Is a full class here really needed for containment of the item or would//an anon object work fine?dojo.declare("dojox.data.XmlItem", null, {constructor: function(element, store) {// summary:// Initialize with an XML element// element:// An XML element// store:// The containing store, if any.this.element = element;this.store = store;},// summary:// A data item of 'XmlStore'// description:// This class represents an item of 'XmlStore' holding an XML element.// 'element'// element:// An XML elementtoString: function() {// summary:// Return a value of the first text child of the element// returns:// a value of the first text child of the elementvar str = "";if (this.element) {for (var i = 0; i < this.element.childNodes.length; i++) {var node = this.element.childNodes[i];if (node.nodeType === 3) {str = node.nodeValue;break;}}}return str; //String}});dojo.extend(dojox.data.XmlStore,dojo.data.util.simpleFetch);}