New file |
0,0 → 1,1126 |
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._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 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._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 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; |
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 |
if (!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 content |
var 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 content |
var 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 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); |
|
} |