if(!dojo._hasResource["bepixeld.DWRWriteStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["bepixeld.DWRWriteStore"] = true;
dojo.provide("bepixeld.DWRWriteStore");
dojo.require("bepixeld.DWRReadStore");

dojo.declare("bepixeld.DWRWriteStore", bepixeld.DWRReadStore, {
	constructor: function(/* object */ keywordParameters){
		//	keywordParameters: {typeMap: object)
		//		The structure of the typeMap object is as follows:
		//		{
		//			type0: function || object,
		//			type1: function || object,
		//			...
		//			typeN: function || object
		//		}
		//		Where if it is a function, it is assumed to be an object constructor that takes the 
		//		value of _value as the initialization parameters.  It is serialized assuming object.toString()
		//		serialization.  If it is an object, then it is assumed
		//		to be an object of general form:
		//		{
		//			type: function, //constructor.
		//			deserialize:	function(value) //The function that parses the value and constructs the object defined by type appropriately.
		//			serialize:	function(object) //The function that converts the object back into the proper file format form.
		//		}

		// DWRWriteStore extends DWRReadStore to implement these additional dojo.data APIs
		this._features['dojo.data.api.Write'] = true;
		this._features['dojo.data.api.Notification'] = true;
		
		this._deleteMethod=keywordParameters.deleteMethod;
		if(typeof this._deleteMethod == "string")
			this._deleteMethod = eval(this._deleteMethod);
		this._notifyRevertMethod=keywordParameters.notifyRevertMethod;
		if(typeof this._notifyRevertMethod == "string")
			this._notifyRevertMethod = eval(this._notifyRevertMethod);
		this._saveMethod=keywordParameters.saveMethod;
		if(typeof this._saveMethod == "string")
			this._saveMethod = eval(this._saveMethod);
		this._newMethod=keywordParameters.newMethod;
		if(typeof this._newMethod == "string")
			this._newMethod = eval(this._newMethod);
		
		// For keeping track of changes so that we can implement isDirty and revert
		this._pending = {
			_newItems:{}, 
			_modifiedItems:{}, 
			_deletedItems:{}
		};

		if(!this._datatypeMap['Date'].serialize){
			this._datatypeMap['Date'].serialize = function(obj){
				return dojo.date.stamp.toISOString(obj, {zulu:true});
			}
		}
		//Disable only if explicitly set to false.
		if(keywordParameters && (keywordParameters.referenceIntegrity === false)){
			this.referenceIntegrity = false;
		}

		// this._saveInProgress is set to true, briefly, from when save() is first called to when it completes
		this._saveInProgress = false;
		
		this._excludeProperties = keywordParameters.excludeProperties || "";
		this._excludeProperties = this._excludeProperties.split(",");
		
		for (var i = 0; i < this._excludeProperties.length; i++) {
			this._excludeProperties[i] = dojo.trim(this._excludeProperties[i]);
		}
		this._excludeProperties.sort();
	}, 
	
	referenceIntegrity: true,  //Flag that defaultly enabled reference integrity tracking.  This way it can also be disabled pogrammatially or declaratively.
	
	_assert: function(/* boolean */ condition){
		if(!condition) {
			throw new Error("assertion failed in DWRWriteStore");
		}
	},

	_getIdentifierAttribute: function(){
		var identifierAttribute = this._identifierAttr;
		// this._assert((identifierAttribute === Number) || (dojo.isString(identifierAttribute)));
		return identifierAttribute;
	},
	
	deleteMethod: "",
	
	saveMethod: "",
	
	newMethod: "",
	
	notifyRevertMethod: "",
	
	excludeProperties: "",
	
/* dojo.data.api.Write */

	newItem: function(/* Object? */ keywordArgs, /* Object? */ parentInfo){
		// summary: See dojo.data.api.Write.newItem()

		this._assert(!this._saveInProgress);

		if (!this._loadFinished){
			// We need to do this here so that we'll be able to find out what
			// identifierAttribute was specified in the data file.
			this._forceLoad();
		}

		if(typeof keywordArgs != "object" && typeof keywordArgs != "undefined"){
			throw new Error("newItem() was passed something other than an object");
		}
		var newIdentity = null;
		var identifierAttribute = this._getIdentifierAttribute();
		if(identifierAttribute === Number){
			newIdentity = this._arrayOfAllItems.length;
		}else{
			newIdentity = keywordArgs[identifierAttribute];
			if (typeof newIdentity === "undefined"){
				throw new Error("newItem() was not passed an identity for the new item");
			}
			if (dojo.isArray(newIdentity)){
				throw new Error("newItem() was not passed an single-valued identity");
			}
		}
		
		// make sure this identity is not already in use by another item, if identifiers were 
		// defined in the file.  Otherwise it would be the item count, 
		// which should always be unique in this case.
		if(this._itemsByIdentity){
			this._assert(typeof this._itemsByIdentity[newIdentity] === "undefined");
		}
		this._assert(typeof this._pending._newItems[newIdentity] === "undefined");
		this._assert(typeof this._pending._deletedItems[newIdentity] === "undefined");
		
		var newItem = {};
		newItem[this._storeRefPropName] = this;		
		newItem[this._itemNumPropName] = this._arrayOfAllItems.length;
		if(this._itemsByIdentity){
			this._itemsByIdentity[newIdentity] = newItem;
			//We have to set the identifier now, otherwise we can't look it
			//up at calls to setValueorValues in parentInfo handling.
			newItem[identifierAttribute] = [newIdentity];
		}
		this._arrayOfAllItems.push(newItem);

		//We need to construct some data for the onNew call too...
		var pInfo = null;
		
		// Now we need to check to see where we want to assign this thing if any.
		if(parentInfo && parentInfo.parent && parentInfo.attribute){
			pInfo = {
				item: parentInfo.parent,
				attribute: parentInfo.attribute,
				oldValue: undefined
			};

			//See if it is multi-valued or not and handle appropriately
			//Generally, all attributes are multi-valued for this store
			//So, we only need to append if there are already values present.
			var values = this.getValues(parentInfo.parent, parentInfo.attribute);
			if(values && values.length > 0){
				var tempValues = values.slice(0, values.length);
				if(values.length === 1){
					pInfo.oldValue = values[0];
				}else{
					pInfo.oldValue = values.slice(0, values.length);
				}
				tempValues.push(newItem);
				this._setValueOrValues(parentInfo.parent, parentInfo.attribute, tempValues, false);
				pInfo.newValue = this.getValues(parentInfo.parent, parentInfo.attribute);
			}else{
				this._setValueOrValues(parentInfo.parent, parentInfo.attribute, newItem, false);
				pInfo.newValue = newItem;
			}
		}else{
			//Toplevel item, add to both top list as well as all list.
			newItem[this._rootItemPropName]=true;
			this._arrayOfTopLevelItems.push(newItem);
		}
		
		this._pending._newItems[newIdentity] = newItem;
		
		//Clone over the properties to the new item
		for(var key in keywordArgs){
			if(key === this._storeRefPropName || key === this._itemNumPropName){
				// Bummer, the user is trying to do something like
				// newItem({_S:"foo"}).  Unfortunately, our superclass,
				// ItemFileReadStore, is already using _S in each of our items
				// to hold private info.  To avoid a naming collision, we 
				// need to move all our private info to some other property 
				// of all the items/objects.  So, we need to iterate over all
				// the items and do something like: 
				//    item.__S = item._S;
				//    item._S = undefined;
				// But first we have to make sure the new "__S" variable is 
				// not in use, which means we have to iterate over all the 
				// items checking for that.
				throw new Error("encountered bug in DWRWriteStore.newItem");
			}
			var value = keywordArgs[key];
			if(!dojo.isArray(value)){
				value = [value];
			}
			newItem[key] = value;
			if(this.referenceIntegrity){
				for(var i = 0; i < value.length; i++){
					var val = value[i];
					if(this.isItem(val)){
						this._addReferenceToMap(val, newItem, key);
					}
				}
			}
		}
		this.onNew(newItem, pInfo); // dojo.data.api.Notification call
		return newItem; // item
	},
	
	_removeArrayElement: function(/* Array */ array, /* anything */ element){
		var index = dojo.indexOf(array, element);
		if (index != -1){
			array.splice(index, 1);
			return true;
		}
		return false;
	},
	
	deleteItem: function(/* item */ item){
		// summary: See dojo.data.api.Write.deleteItem()
		this._assert(!this._saveInProgress);
		this._assertIsItem(item);
		
		var _finished = false;
		var _result = false;
		var self = this;
		var _deleter = function() {
			// Remove this item from the _arrayOfAllItems, but leave a null value in place
			// of the item, so as not to change the length of the array, so that in newItem() 
			// we can still safely do: newIdentity = this._arrayOfAllItems.length;
			var indexInArrayOfAllItems = item[self._itemNumPropName];
			var identity = self.getIdentity(item);

			//If we have reference integrity on, we need to do reference cleanup for the deleted item
			if(self.referenceIntegrity){
				//First scan all the attributes of this items for references and clean them up in the map 
				//As this item is going away, no need to track its references anymore.

				//Get the attributes list before we generate the backup so it 
				//doesn't pollute the attributes list.
				var attributes = self.getAttributes(item);

				//Backup the map, we'll have to restore it potentially, in a revert.
				if(item[self._reverseRefMap]){
					item["backup_" + self._reverseRefMap] = dojo.clone(item[self._reverseRefMap]);
				}
				
				//TODO:  This causes a reversion problem.  This list won't be restored on revert since it is
				//attached to the 'value'. item, not ours.  Need to back tese up somehow too.
				//Maybe build a map of the backup of the entries and attach it to the deleted item to be restored
				//later.  Or just record them and call _addReferenceToMap on them in revert.
				dojo.forEach(attributes, function(attribute){
					dojo.forEach(self.getValues(item, attribute), function(value){
						if(self.isItem(value)){
							//We have to back up all the references we had to others so they can be restored on a revert.
							if(!item["backupRefs_" + self._reverseRefMap]){
								item["backupRefs_" + self._reverseRefMap] = [];
							}
							item["backupRefs_" + self._reverseRefMap].push({id: self.getIdentity(value), attr: attribute});
							self._removeReferenceFromMap(value, item, attribute);
						}
					}, self);
				}, self);

				//Next, see if we have references to this item, if we do, we have to clean them up too.
				var references = item[self._reverseRefMap];
				if(references){
					//Look through all the items noted as references to clean them up.
					for(var itemId in references){
						var containingItem = null;
						if(self._itemsByIdentity){
							containingItem = self._itemsByIdentity[itemId];
						}else{
							containingItem = self._arrayOfAllItems[itemId];
						}
						//We have a reference to a containing item, now we have to process the
						//attributes and clear all references to the item being deleted.
						if(containingItem){
							for(var attribute in references[itemId]){
								var oldValues = self.getValues(containingItem, attribute) || [];
								var newValues = dojo.filter(oldValues, function(possibleItem){
								   return !(self.isItem(possibleItem) && self.getIdentity(possibleItem) == identity);
								}, self);
								//Remove the note of the reference to the item and set the values on the modified attribute.
								self._removeReferenceFromMap(item, containingItem, attribute); 
								if(newValues.length < oldValues.length){
									self.setValues(containingItem, attribute, newValues);
								}
							}
						}
					}
				}
			}

			self._arrayOfAllItems[indexInArrayOfAllItems] = null;

			item[self._storeRefPropName] = null;
			if(self._itemsByIdentity){
				delete self._itemsByIdentity[identity];
			}
			self._pending._deletedItems[identity] = item;
			
			//Remove from the toplevel items, if necessary...
			if(item[self._rootItemPropName]){
				self._removeArrayElement(self._arrayOfTopLevelItems, item);
			}			
			self.onDelete(item); // dojo.data.api.Notification call
			_finished = true;
			_result = true;
		}
		
		if(this._deleteMethod) {
			var serializableItem = this._serialize(item);		
			this._deleteMethod(serializableItem, {
				callback: _deleter,
				errorHandler: function(message) {	
					console.error(message);				
					debugger;
				}
			});								
			return true;
		}
		else {
			_deleter();
			return true;
		}
	},

	setValue: function(/* item */ item, /* attribute-name-string */ attribute, /* almost anything */ value){
		// summary: See dojo.data.api.Write.set()
		return this._setValueOrValues(item, attribute, value, true); // boolean
	},
	
	setValues: function(/* item */ item, /* attribute-name-string */ attribute, /* array */ values){
		// summary: See dojo.data.api.Write.setValues()
		return this._setValueOrValues(item, attribute, values, true); // boolean
	},
	
	unsetAttribute: function(/* item */ item, /* attribute-name-string */ attribute){
		// summary: See dojo.data.api.Write.unsetAttribute()
		return this._setValueOrValues(item, attribute, [], true);
	},
	
	_setValueOrValues: function(/* item */ item, /* attribute-name-string */ attribute, /* anything */ newValueOrValues, /*boolean?*/ callOnSet){
		this._assert(!this._saveInProgress);
		
		// Check for valid arguments
		this._assertIsItem(item);
		this._assert(dojo.isString(attribute));
		this._assert(typeof newValueOrValues !== "undefined");

		// Make sure the user isn't trying to change the item's identity
		var identifierAttribute = this._getIdentifierAttribute();
		if(attribute == identifierAttribute){
			throw new Error("DWRWriteStore does not have support for changing the value of an item's identifier.");
		}

		// To implement the Notification API, we need to make a note of what
		// the old attribute value was, so that we can pass that info when
		// we call the onSet method.
		var oldValueOrValues = this._getValueOrValues(item, attribute);

		var identity = this.getIdentity(item);
		if(!this._pending._modifiedItems[identity]){
			// Before we actually change the item, we make a copy of it to 
			// record the original state, so that we'll be able to revert if 
			// the revert method gets called.  If the item has already been
			// modified then there's no need to do this now, since we already
			// have a record of the original state.
			var copyOfItemState = {};
			for(var key in item){
				if((key === this._storeRefPropName) || (key === this._itemNumPropName) || (key === this._rootItemPropName)){
					copyOfItemState[key] = item[key];
				}else if(key === this._reverseRefMap){
					copyOfItemState[key] = dojo.clone(item[key]);
				}else{
					copyOfItemState[key] = item[key].slice(0, item[key].length);
				}
			}
			// Now mark the item as dirty, and save the copy of the original state
			this._pending._modifiedItems[identity] = copyOfItemState;
		}
		
		// Okay, now we can actually change this attribute on the item
		var success = false;
		
		if(dojo.isArray(newValueOrValues) && newValueOrValues.length === 0){
			
			// If we were passed an empty array as the value, that counts
			// as "unsetting" the attribute, so we need to remove this 
			// attribute from the item.
			success = delete item[attribute];
			newValueOrValues = undefined; // used in the onSet Notification call below

			if(this.referenceIntegrity && oldValueOrValues){
				var oldValues = oldValueOrValues;
				if (!dojo.isArray(oldValues)){
					oldValues = [oldValues];
				}
				for(var i = 0; i < oldValues.length; i++){
					var value = oldValues[i];
					if(this.isItem(value)){
						this._removeReferenceFromMap(value, item, attribute);
					}
				}
			}
		}else{
			var newValueArray;
			if(dojo.isArray(newValueOrValues)){
				var newValues = newValueOrValues;
				// Unfortunately, it's not safe to just do this:
				//    newValueArray = newValues;
				// Instead, we need to copy the array, which slice() does very nicely.
				// This is so that our internal data structure won't  
				// get corrupted if the user mucks with the values array *after*
				// calling setValues().
				newValueArray = newValueOrValues.slice(0, newValueOrValues.length);
			}else{
				newValueArray = [newValueOrValues];
			}

			//We need to handle reference integrity if this is on. 
			//In the case of set, we need to see if references were added or removed
			//and update the reference tracking map accordingly.
			if(this.referenceIntegrity){
				if(oldValueOrValues){
					var oldValues = oldValueOrValues;
					if(!dojo.isArray(oldValues)){
						oldValues = [oldValues];
					}
					//Use an associative map to determine what was added/removed from the list.
					//Should be O(n) performant.  First look at all the old values and make a list of them
					//Then for any item not in the old list, we add it.  If it was already present, we remove it.
					//Then we pass over the map and any references left it it need to be removed (IE, no match in
					//the new values list).
					var map = {};
					dojo.forEach(oldValues, function(possibleItem){
						if(this.isItem(possibleItem)){
							var id = this.getIdentity(possibleItem);
							map[id.toString()] = true;
						}
					}, this);
					dojo.forEach(newValueArray, function(possibleItem){
						if(this.isItem(possibleItem)){
							var id = this.getIdentity(possibleItem);
							if(map[id.toString()]){
								delete map[id.toString()];
							}else{
								this._addReferenceToMap(possibleItem, item, attribute); 
							}
						}
					}, this);
					for(var rId in map){
						var removedItem;
						if(this._itemsByIdentity){
							removedItem = this._itemsByIdentity[rId];
						}else{
							removedItem = this._arrayOfAllItems[rId];
						}
						this._removeReferenceFromMap(removedItem, item, attribute);
					}
				}else{
					//Everything is new (no old values) so we have to just
					//insert all the references, if any.
					for(var i = 0; i < newValueArray.length; i++){
						var value = newValueArray[i];
						if(this.isItem(value)){
							this._addReferenceToMap(value, item, attribute);
						}
					}
				}
			}
			item[attribute] = newValueArray;
			success = true;
		}

		// Now we make the dojo.data.api.Notification call
		if(callOnSet){
			this.onSet(item, attribute, oldValueOrValues, newValueOrValues); 
		}
		return success; // boolean
	},

	_addReferenceToMap: function(/*item*/ refItem, /*item*/ parentItem, /*string*/ attribute){
		//	summary:
		//		Method to add an reference map entry for an item and attribute.
		//	description:
		//		Method to add an reference map entry for an item and attribute. 		 //
		//	refItem:
		//		The item that is referenced.
		//	parentItem:
		//		The item that holds the new reference to refItem.
		//	attribute:
		//		The attribute on parentItem that contains the new reference.
		 
		var parentId = this.getIdentity(parentItem);
		var references = refItem[this._reverseRefMap];

		if(!references){
			references = refItem[this._reverseRefMap] = {};
		}
		var itemRef = references[parentId];
		if(!itemRef){
			itemRef = references[parentId] = {};
		}
		itemRef[attribute] = true;
	},

	_removeReferenceFromMap: function(/* item */ refItem, /* item */ parentItem, /*strin*/ attribute){
		//	summary:
		//		Method to remove an reference map entry for an item and attribute.
		//	description:
		//		Method to remove an reference map entry for an item and attribute.  This will
		//		also perform cleanup on the map such that if there are no more references at all to 
		//		the item, its reference object and entry are removed.
		//
		//	refItem:
		//		The item that is referenced.
		//	parentItem:
		//		The item holding a reference to refItem.
		//	attribute:
		//		The attribute on parentItem that contains the reference.
		var identity = this.getIdentity(parentItem);
		var references = refItem[this._reverseRefMap];
		var itemId;
		if(references){
			for(itemId in references){
				if(itemId == identity){
					delete references[itemId][attribute];
					if(this._isEmpty(references[itemId])){
						delete references[itemId];
					}
				}
			}
			if(this._isEmpty(references)){
				delete refItem[this._reverseRefMap];
			}
		}
	},

	_dumpReferenceMap: function(){
		//	summary:
		//		Function to dump the reverse reference map of all items in the store for debug purposes.
		//	description:
		//		Function to dump the reverse reference map of all items in the store for debug purposes.
		var i;
		for(i = 0; i < this._arrayOfAllItems.length; i++){
			var item = this._arrayOfAllItems[i];
			if(item && item[this._reverseRefMap]){
				console.log("Item: [" + this.getIdentity(item) + "] is referenced by: " + dojo.toJson(item[this._reverseRefMap]));
			}
		}
	},

	_getValueOrValues: function(/* item */ item, /* attribute-name-string */ attribute){
		var valueOrValues = undefined;
		if(this.hasAttribute(item, attribute)){
			var valueArray = this.getValues(item, attribute);
			if(valueArray.length == 1){
				valueOrValues = valueArray[0];
			}else{
				valueOrValues = valueArray;
			}
		}
		return valueOrValues;
	},
	
	_flatten: function(/* anything */ value){
		if(this.isItem(value)){
			var item = value;
			// Given an item, return an serializable object that provides a 
			// reference to the item.
			// For example, given kermit:
			//    var kermit = store.newItem({id:2, name:"Kermit"});
			// we want to return
			//    {_reference:2}
			var identity = this.getIdentity(item);
			var referenceObject = {_reference: identity};
			return referenceObject;
		}else{
			if(typeof value === "object"){
				for(var type in this._datatypeMap){
					var typeMap = this._datatypeMap[type];
					if (dojo.isObject(typeMap) && !dojo.isFunction(typeMap)){
						if(value instanceof typeMap.type){
							if(!typeMap.serialize){
								throw new Error("DWRWriteStore:  No serializer defined for type mapping: [" + type + "]");
							}
							return {_type: type, _value: typeMap.serialize(value)};
						}
					} else if(value instanceof typeMap){
						//SImple mapping, therefore, return as a toString serialization.
						return {_type: type, _value: value.toString()};
					}
				}
			}
			return value;
		}
	},
	
	_getNewFileContentString: function(){
		// summary: 
		//		Generate a string that can be saved to a file.
		//		The result should look similar to:
		//		http://trac.dojotoolkit.org/browser/dojo/trunk/tests/data/countries.json
		var serializableStructure = {};
		
		var identifierAttribute = this._getIdentifierAttribute();
		if(identifierAttribute !== Number){
			serializableStructure.identifier = identifierAttribute;
		}
		if(this._labelAttr){
			serializableStructure.label = this._labelAttr;
		}
		serializableStructure.items = [];
		for(var i = 0; i < this._arrayOfAllItems.length; ++i){
			var item = this._arrayOfAllItems[i];
			if(item !== null){
				var serializableItem = {};
				for(var key in item){
					if(key !== this._storeRefPropName && key !== this._itemNumPropName){
						var attribute = key;
						var valueArray = this.getValues(item, attribute);
						if(valueArray.length == 1){
							serializableItem[attribute] = this._flatten(valueArray[0]);
						}else{
							var serializableArray = [];
							for(var j = 0; j < valueArray.length; ++j){
								serializableArray.push(this._flatten(valueArray[j]));
								serializableItem[attribute] = serializableArray;
							}
						}
					}
				}
				serializableStructure.items.push(serializableItem);
			}
		}
		var prettyPrint = true;
		return dojo.toJson(serializableStructure, prettyPrint);
	},
	
	_isEmpty: function(something){
		//	summary: 
		//		Function to determine if an array or object has no properties or values.
		//	something:
		//		The array or object to examine.
		var empty = true;
		if(dojo.isObject(something)){
			var i;
			for(i in something){
				empty = false;
				break;
			}
		}else if(dojo.isArray(something)){
			if(something.length > 0){
				empty = false;
			}
		}
		return empty; //boolean
	},
	
	save: function(/* object */ keywordArgs){
		// summary: See dojo.data.api.Write.save()
		this._assert(!this._saveInProgress);
		
		// this._saveInProgress is set to true, briefly, from when save is first called to when it completes
		this._saveInProgress = true;
		
		var self = this;
		var saveCompleteCallback = function(result, mode, item){
			self._pending = {
				_newItems:{}, 
				_modifiedItems:{},
				_deletedItems:{}
			};
			
			self._saveInProgress = false; // must come after this._pending is cleared, but before any callbacks
			if(keywordArgs && keywordArgs.onComplete){
				var scope = keywordArgs.scope || dojo.global;
				keywordArgs.onComplete.call(scope, result, mode, item);
			}
		};
		var saveFailedCallback = function(message, mode, item){
			self._saveInProgress = false;
			if(keywordArgs && keywordArgs.onError){
				var scope = keywordArgs.scope || dojo.global;
				keywordArgs.onError.call(scope, message, mode, item);
			}
		};
		
		if(this._saveDWR){
			this._saveDWR(saveCompleteCallback, saveFailedCallback);
		}
		if(this._saveEverything){
			var newFileContentString = this._getNewFileContentString();
			this._saveEverything(saveCompleteCallback, saveFailedCallback, newFileContentString);
		}
		if(this._saveCustom){
			this._saveCustom(saveCompleteCallback, saveFailedCallback);
		}
		if(!this._saveEverything && !this._saveCustom && !this._saveDWR){
			// Looks like there is no user-defined save-handler function.
			// That's fine, it just means the datastore is acting as a "mock-write"
			// store -- changes get saved in memory but don't get saved to disk.
			saveCompleteCallback();
		}
	},
	
	_serialize: function(item) {
		var serializableItem = {};
		for(var key in item){
			if(key !== this._storeRefPropName && key !== this._itemNumPropName && (dojo.indexOf(this._excludeProperties, key) < 0 )){
				var attribute = key;
				var valueArray = this.getValues(item, attribute);
				if(valueArray.length == 1){
					var v = this._flatten(valueArray[0]);
					if(v != null) {
						serializableItem[attribute] = v;
					}
				}else{
					var serializableArray = [];
					for(var j = 0; j < valueArray.length; ++j){
						var v = this._flatten(valueArray[j]);
						if(v != null) {
							serializableArray.push(v);
						}						
					}
					serializableItem[attribute] = serializableArray;
				}
			}
		}
		return serializableItem;
	},
	
	_saveDWR: function(saveCompleteCallback, saveFailedCallback) {
		var serializableStructure = {};		
		var identifierAttribute = this._getIdentifierAttribute();
		if(identifierAttribute !== Number){
			serializableStructure.identifier = identifierAttribute;
		}
		if(this._labelAttr){
			serializableStructure.label = this._labelAttr;
		}
		serializableStructure.items = [];
		for(var i = 0; i < this._arrayOfAllItems.length; ++i){
			var item = this._arrayOfAllItems[i];
			var identity = this.getIdentity(item);
			var _mode = 0;
			if(this._pending._newItems[identity]) {
				_mode =  1;
			} else if(this._pending._modifiedItems[identity]) {
				_mode =  2;
			}
			if (_mode === 0) {
				continue;
			}
			if(item !== null){
				var serializableItem = this._serialize(item);
				if(_mode & 1) {
					var mode = _mode;
					this._newMethod(serializableItem, {
						callback: function(result) {
							saveCompleteCallback(result, mode, item);
						},
						errorHandler: function(message) {
							saveFailedCallback(message, mode, item);
						}
					});
				}
				if(_mode & 2) {
					var mode = _mode;
					this._saveMethod(serializableItem, {
						callback: function(result) {
							saveCompleteCallback(result, mode, item);
						},
						errorHandler: function(message) {
							saveFailedCallback(message, mode, item);
						}
					});
				}
			}
		}			
	},
	
	revert: function(){
		// summary: See dojo.data.api.Write.revert()
		this._assert(!this._saveInProgress);

		var identity;
		for(identity in this._pending._newItems){
			var newItem = this._pending._newItems[identity];
			newItem[this._storeRefPropName] = null;
			// null out the new item, but don't change the array index so
			// so we can keep using _arrayOfAllItems.length.
			this._arrayOfAllItems[newItem[this._itemNumPropName]] = null;
			if(newItem[this._rootItemPropName]){
				this._removeArrayElement(this._arrayOfTopLevelItems, newItem);
			}
			if(this._itemsByIdentity){
				delete this._itemsByIdentity[identity];
			}
		}
		for(identity in this._pending._modifiedItems){
			// find the original item and the modified item that replaced it
			var originalItem = this._pending._modifiedItems[identity];
			var modifiedItem = null;
			if(this._itemsByIdentity){
				modifiedItem = this._itemsByIdentity[identity];
			}else{
				modifiedItem = this._arrayOfAllItems[identity];
			}
			
			// make the original item into a full-fledged item again
			originalItem[this._storeRefPropName] = this;
			modifiedItem[this._storeRefPropName] = null;

			// replace the modified item with the original one
			var arrayIndex = modifiedItem[this._itemNumPropName];
			this._arrayOfAllItems[arrayIndex] = originalItem;
			
			if(modifiedItem[this._rootItemPropName]){
				var i;
				for (i = 0; i < this._arrayOfTopLevelItems.length; i++) {
					var possibleMatch = this._arrayOfTopLevelItems[i];
					if (this.getIdentity(possibleMatch) == identity){
						this._arrayOfTopLevelItems[i] = originalItem;
						break;
					}
				}
			}
			if(this._itemsByIdentity){
				this._itemsByIdentity[identity] = originalItem;
			}			
		}
		var deletedItem;
		for(identity in this._pending._deletedItems){
			deletedItem = this._pending._deletedItems[identity];
			deletedItem[this._storeRefPropName] = this;
			var index = deletedItem[this._itemNumPropName];
			
			//Restore the reverse refererence map, if any.
			if(deletedItem["backup_" + this._reverseRefMap]){
				deletedItem[this._reverseRefMap] = deletedItem["backup_" + this._reverseRefMap];
				delete deletedItem["backup_" + this._reverseRefMap];
			}
			this._arrayOfAllItems[index] = deletedItem;
			if (this._itemsByIdentity) {
				this._itemsByIdentity[identity] = deletedItem;
			}
			if(deletedItem[this._rootItemPropName]){
				this._arrayOfTopLevelItems.push(deletedItem);
			}
		}
		//We have to pass through it again and restore the reference maps after all the
		//undeletes have occurred.
		for(identity in this._pending._deletedItems){
			deletedItem = this._pending._deletedItems[identity];
			if(deletedItem["backupRefs_" + this._reverseRefMap]){
				dojo.forEach(deletedItem["backupRefs_" + this._reverseRefMap], function(reference){
					var refItem;
					if(this._itemsByIdentity){
						refItem = this._itemsByIdentity[reference.id];
					}else{
						refItem = this._arrayOfAllItems[reference.id];
					}
					this._addReferenceToMap(refItem, deletedItem, reference.attr);
				}, this);
				delete deletedItem["backupRefs_" + this._reverseRefMap]; 
			}
		}
		
		if(this._notifyRevertMethod) {			
			this._notifyRevertMethod(function(){});
		}

		this._pending = {
			_newItems:{}, 
			_modifiedItems:{}, 
			_deletedItems:{}
		};
		return true; // boolean
	},
	
	reload: function(silent){
		this._loadInProgress = false;
		this._loadComplete = false;
		for(var i = 0;i < this._items.length;i++) {
			if(!silent){
				this.onDelete(this._items[i]);
			}
		}
		this._items = [];
		this._arrayOfAllItems = [];
		this._arrayOfAllTopLevelItems = [];
		this._itemsByIdentity = null;
		this.fetch(this.lastKeywordArgs);		
	},

	isDirty: function(/* item? */ item){
		// summary: See dojo.data.api.Write.isDirty()
		if(item){
			// return true if the item is dirty
			var identity = this.getIdentity(item);
			return new Boolean(this._pending._newItems[identity] || 
				this._pending._modifiedItems[identity] ||
				this._pending._deletedItems[identity]); // boolean
		}else{
			// return true if the store is dirty -- which means return true
			// if there are any new items, dirty items, or modified items
			if(!this._isEmpty(this._pending._newItems) || 
			   !this._isEmpty(this._pending._modifiedItems) ||
			   !this._isEmpty(this._pending._deletedItems)){
				return true;
			}
			return false; // boolean
		}
	}
});

}
