- Author:
- David Nickerson <nickerso@users.sourceforge.net>
- Date:
- 2009-07-16 02:00:03+12:00
- Desc:
- the starting point for the HH tutorial example
- Permanent Source URI:
- https://models.fieldml.org/workspace/a1/rawfile/f6a8f90307388eb4b040ee3566b84d88b59247f7/dojo-presentation/js/dojo/dojox/data/XmlStore.js
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 = [], ec=element.childNodes;
for(var i = 0; i < ec.length; i++){
var node = ec[i];
if(node.nodeType === 3 || node.nodeType === 4){
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){
var url;
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: " + this._getPostContent(item)));
}
return;
}
var saveArgs = {
url: url,
method: (method || "POST"),
contentType: "text/xml",
handleAs: "xml"
};
var saveHandler;
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 = "<?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 || node.nodeType === 4) {
str = node.nodeValue;
break;
}
}
}
return str; //String
}
});
dojo.extend(dojox.data.XmlStore,dojo.data.util.simpleFetch);