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 documents constructor: 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 URL console.log("XmlStore()"); if(args){ this.url = args.url; this.rootItem = (args.rootItem || args.rootitem || this.rootItem); this.keyAttribute = (args.keyAttribute || args.keyattribute || this.keyAttribute); this._attributeMap = (args.attributeMap || args.attributemap); this.label = args.label || this.label; this.sendQuery = (args.sendQuery || args.sendquery || this.sendQuery); } this._newItems = []; this._deletedItems = []; this._modifiedItems = []; }, //Values that may be set by the parser. //Ergo, have to be instantiated to something //So the parser knows how to set them. url: "", rootItem: "", keyAttribute: "", label: "", sendQuery: false, /* 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 array var 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 found var 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 attribute attributes.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 false return (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 false var 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 false if(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 false return 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.label !== "") && this.isItem(item)){ var label = this.getValue(item,this.label); if(label){ return label.toString(); } } return undefined; //undefined }, getLabelAttributes: function(/* item */ item){ // summary: // See dojo.data.api.Read.getLabelAttributes() if(this.label !== ""){ return [this.label]; //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 error var 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 URL if(!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 items var query = null; if(request){ query = request.query; } var items = []; var nodes = null; console.log("Looking up root item: " + this.rootItem); 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 element console.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: // True console.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 true if(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 true if(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 true if(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 callbacks if(!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){ // reparented this._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: // True console.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 // false if (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{ // POST url = 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{ // POST saveArgs.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 URL return 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 URL return 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 URL var url = this.url; if (item && this.keyAttribute !== "") { var value = this.getValue(item, this.keyAttribute); if (value) { var key = this.keyAttribute.charAt(0) ==='@' ? this.keyAttribute.substring(1): this.keyAttribute; url += url.indexOf('?') < 0 ? '?' : '&'; url += key + '=' + 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 content var element = item.element; var declaration = ""; // 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 content var element = item.element; var declaration = ""; // 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 attribute value = 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 element toString: function() { // summary: // Return a value of the first text child of the element // returns: // a value of the first text child of the element var 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); }