	var DataModel = {

		/* obtained from the server: a list of URLs pointing to resources
		 * model structure : map href => resource properties
		 */
		model : null
		
		/** The default JS hashtable doesn't return elements in the order they 
		 * were inserted in. The modelOrder array takes care of that
		 */
		, modelOrder: null
		
		/* 
		 * bitmarks : {
		 *                resourceURL/href : {
		 *                               "tags" : [
		 *                                          {
    	 *                                          "value" : ...,
    	 *                                          "href" : ...
    	 *                                          },
		 *                                          { ... }
		 *                                      ],
		 *                              "description" : {
		 *                                          "value" : ...,
		 *                                          "href" : ...
		 *                                      }
		 *               },
		 *               
		 *               resourceURL/href : {
         *                        ...
		 *               }
		 *   }
		 */
		, bitmarks : null

		// a hash table of tags:resourceURLs for fast lookup/classification by tags
		, tagHashTable : null
		
		, pageTemplates : null
		
		, listItemTemplates : null
		
		, tagsDir : null
		
		, bitmarkBaseUrl: "/bitmarks/"
		
		, bitmarkNS : AXIS.WebDAV.ns.bm
		
		// default number of items to fetch from the server
		, defaultPageSize:20
		
		/*
		 * Initialize the dataModel with resources fetched from
		 * the passed in 'url' and filtered on 'tag'
		 */
		, init: function( url, tag, templateUrl, options ) {
		    this.initTagDataStructures();
		    this.fetchTagAndlistItemTemplates(templateUrl);
		    this.fetchResourcesAndAddToMap(url, options, tag);
		    this.fetchTagCounts( url, this.tagsDir );
		}
		
		/**
		 * Fetches new resources from the server,
		 * updates it's internal data structures,
		 * and calls the success function (if one is passed in options)
		 * args : @options :: success - success function 
		 *                  :: offset - offset of resources to fetch if we fetched a subset of resources earlier  
		 */
		, fetchNewResources: function(options) {
		    
		    if(!options) {
				if(jQuery.isFunction(options.success)) {
				    options.success.call(options.scope, []);
				}
		    }
		    
		    this.fetchResourcesAndAddToMap(options.url, options, options.tagName);
		}
		
		/* Function to refresh the list once the search criteria change */
		, refreshData: function( url, tag, options ) {
		    //TODO: throw exception / Error handling when url or tag are invalid
            this.clearTagTables();
            this.fetchResourcesAndAddToMap(url, options, tag);
		}
		
		/* Function to do webDAV search and find the bits to be displayed
		 * args : @bitsUrl : URL for fetching the bit(s)
		 *        @options : Other options :
         *                    - depth: depth to search for
         *                    - success : Callback called on success
         *                    - scope : Scope for the sucess callback
         *        @tagName : Only fetch bits with tag "tagName"
         *      TODO : @bitmark in options : to filter resources based on a bitmark
         *              bitsUrl: roll that and tagName into @options
		 */
		, fetchResourcesAndAddToMap: function( bitsUrl, options, tagName ) {
			
			var dav = AXIS.WebDAV;
			
			// TODO: bitmark filtering
			
			// if no URL is passed in, use /bits by default
            if(!bitsUrl) {
                //TODO: error handling;
                alert("Invalid url. Can't fetch resources");
                return;
            }
			
			var bitmarkNS = new String(this.bitmarkNS);
			var limebitsNS = new String(AXIS.WebDAV.ns.lb);
			var davNS = new String(AXIS.WebDAV.ns.d);
			
			var defaultOptions = {
			    "depth": "infinity",
			    "where": dav.SEARCH.is_bit(),
			    "limit": DataModel.defaultPageSize,
			    "offset": "0"
			};
			
			options = jQuery.extend(defaultOptions, options);

			if(tagName && tagName !== "All") {
                options.where = dav.SEARCH.and(options.where, dav.SEARCH.eq([bitmarkNS, "tag"],
                                                                            tagName, {
                                                                                    "ns":bitmarkNS,
                                                                                    "name":"bitmark"
                                                                                    }
                                                                            ));
			}
			
			// for properties other than displayname, the namespace should be 'limebits'
			var orderby = options.orderby;
			if( orderby && !AXIS.isArray(orderby)) {
			    if( (orderby == "displayname") || (orderby == "lastmodified") || (orderby == "popularity") ) {
			        orderby = [davNS, orderby];
			    } else {
			        orderby = [limebitsNS, orderby];
			    }
			}
			
	        dav.SEARCH({
	            url: bitsUrl,
              props: ["resource-id", "lastmodified", "popularity", "owner", "displayname"],
              where: options.where,
              orderby: orderby,
	            order: options.order,
	            depth: options.depth,
	            limit: options.limit,
	            offset: options.offset, 
              bitmarks: {
                        ns: bitmarkNS,
                        names: ["tag", "description"]
              },
	            callback: function(response) {
	                if (response.httpHandle.status > 210) {
	                    return; // fail silently
	                }

                    var urls = [];
                    

                    try {
                        // If there's no response, still call the success callback on a 2xx response
    					var resp = response.responseXMLObject().multistatus.response;
    					if(!resp) {
    						alert("Couldn't get SEARCH response from the server");
    						return;
    					}
					
    					// Convert resp to an array if it is not already one
                        if(!jQuery.isArray(resp)) {
                            resp = [resp];
                        }
                    

					
    					for(var i = 0; i < resp.length; i++) {
    					    // store the returned URLs and pass it to the callbacks
    						urls.push(resp[i].href);
					    
    						// Add resource URL and it's properties to the data model
    						var pstat = resp[i].propstat;
                            if (!jQuery.isArray(pstat)) { pstat = [pstat]; }
    						for(var j = 0; j < pstat.length; j++) {
    						    if(pstat[j].status.search(/200 OK/) != -1) {
    						        // in the callback, use 'DataModel' instead of 'this'
    		 						DataModel.addResourceProps(resp[i].href, pstat[j].prop);
    						    }
    						}
						
                            var bstat = resp[i].bitmarkstat;

                            // Convert bitmark to an array if it isn't already
                            if(!jQuery.isArray(bstat)) {
                                bstat = [bstat];
                            }
                        
                        
                            for(var k = 0; k < bstat.length; k++) {
                                if(bstat[k].status.search(/200 OK/) != -1) {
                                    var bitmark = bstat[k].bitmark;
                                
                                    // Convert bitmark to an array if it isn't already
                                    if(!jQuery.isArray(bitmark)) {
                                        bitmark = [bitmark];
                                    }
                                    // Store the bitmarks
                                    for(var l = 0; l < bitmark.length; l++) {
                                        //TODO : this works for name and description. Modify this for tags
                                        DataModel.addResourceBitmark(resp[i].href, bitmark[l]);
                                    }
                                }
                            }
    					}
					} catch(e) {}
					
					DataModel.modelOrder = DataModel.modelOrder.concat(urls);
					
					if(jQuery.isFunction(options.success)) {
					    options.success.call(options.scope, urls);
					}
	            }
	        });
	
            // this.buildTagIndex( this.model );
	        
		}
		
		/**
		 * Removes a resource from the datamodel
		 * @href: href of the resource deleted
		 */
		, removeResource: function(href) {
		    
		    if(!href) {
		        return;
		    }
		    
		    if(this.model[href]) {
		        delete this.model[href];
		    }
		    
		    var indexInOrder = this.modelOrder.indexOf(href);
		    if(indexInOrder != -1) {
		        this.modelOrder.splice(indexInOrder, 1);
		    }
		    
		    if(this.bitmarks[href]) {
		        delete this.bitmarks[href];
		    }

		    // TODO: remove reference from the taghashtable once it is in use again
		}
		
		/**
		 * Moves the said resource to trash. Set a property on it (for restore) before the move
		 *  args : @bitUrl : URL of the resource being moved
		 *         @options : options for moving :
		 *                      - success : success function 
		 *                      - failure : failure function
		 *                      - scope   : scope for calling the functions
		 */
		, moveResourceToTrash: function(bitUrl, options) {
		    
		    // Function that calls the failure function if one is passed in
		    function callFailureFn(str) {
		        if(jQuery.isFunction(options.failure)) {
		            options.failure.call(options.scope, str);
		        }
		    }
		    
		    function callSuccessFn(str) {
		        if(jQuery.isFunction(options.success)) {
		            options.success.call(options.scope, str);
		        }
		    }
		    
		    // trash folder is : /home/username/trash
            var trashFolderUrl = "/home/" + AXIS.User.username() + "/trash";
            /**
            * Need to ensure that the /trash/ folder exists. Create it if it doesn't
            */
            var configObj = AXIS.WebDAV.MKCOL({ 
                headers:{'If-None-Match':'*'}, 
            		asynch: false, 
            		url: trashFolderUrl,
                    callback: function(resp) {
                    if (response.httpHandle.status > 210) {
                        callFailureFn("Couldn't create trash folder");
                        return;
                    }
                    // everything is fine .. move along
                }
            });

            /**
              * Trash folder location + filename...
              */
              // assume that the bit name doesn't include "/"s
             var bitFolderName = bitUrl.substring(bitUrl.lastIndexOf("/") + 1);
             var dest = trashFolderUrl + "/" + bitFolderName;
             
             // dest needs to be an absolute URL
             dest = AXIS.Util.getAbsoluteUrl(dest);
             
             if(!dest) {
                 // something really wrong
                 // TODO: error handling
                 callFailureFn("Destination missing or Invalid");
                 return;
             }

            /** 
            * Do what the editor does too
            * Need to add a property to the resource which stores its
            * original location.
            */
            var pp = AXIS.WebDAV.setProperty({
                url: bitUrl,
                setProperties: {
                  name: 'Editor_restorepath',
                  value: bitUrl
                }
            });

            var p = AXIS.WebDAV.MOVE({
                url:          bitUrl,
                destination:  dest,
                overwrite:    true,
                onFailure:    function(response) {callFailureFn("Couldn't delete bit!");},
                onSuccess:    function(response) {
                    // Delete the bit from the DataModel and refresh display
                    DataModel.removeResource(bitUrl);
                    callSuccessFn();
                }
            });
		}
		
		/**
		 * Duplicates a resource and calls the provided callback after 
		 *  args :  @bitUrl : URL of the bit to be duplicated : required
		 *          @options :  success : the success callback
		 *                      failure : the failure callback : TODO: streamline errors passed
		 *                      scope   : Scope for the handlers
		 * 
		 *  TODO: Add support for enhanced error /success information ?
		 */
		, duplicateResource: function(bitUrl, options) {
		    // duplication = copying the resource in it's parent folder (no over-writing)
		    return this.copyResource(bitUrl, bitUrl, options);
	    }
	    
	    , addBlankResource: function(bitUrl, options) {
	        
	        var destUrl = AXIS.User.getBitsFolderName() + AXIS.Util.uri.basename(bitUrl);

	        return this.copyResource(bitUrl, destUrl, options);
	    }
		    
		, copyResource: function(bitUrl, destUrl, options) {
		    
		    function callFailureFn(errorStr) {
		        if(jQuery.isFunction(options.failure)) {
		            options.failure.call(options.scope, errorStr);
		        }
		    }
		    
		    if(!bitUrl) {
		        callFailureFn("Invalid Bit URL");
		        return;
		    }
		    
		    function dupeCallback(response) {

                if (response.httpHandle.status > 210) {
                    // couldn't duplicate!
                    callFailureFn("Couldn't duplicate bit. Server returned :" + response.httpHandle.status);
                    return; // fail silently
                }
                
                // Copied bit should be given a new name...
                var alternateName = AXIS.Util.uri.findAlternateName(DataModel.model[bitUrl].displayname, DataModel.getAllBitNames());
                DataModel.saveProperty(response.destination, 'displayname', alternateName); 

		        // depth 0 search for the resource to fetch bitmarks and properties
		        jQuery.extend(options, {"depth":"0"});
		        
		        // var successObj = {
		        //                    "success" : function() {
		                if(options.success && jQuery.isFunction(options.success)) {
		                    options.success.call(options.scope || this, response.destination);
		                }
                //     }
                // };
		        // we asked the copy to pick an alternate ame for us : response.destination is the name that was picked
		        // TODO: Don't fetch the copied resource by default
                // DataModel.fetchResourcesAndAddToMap(response.destination, successObj);
		    }
		    
    	    AXIS.Util.bit.copy(bitUrl, destUrl, {"success":dupeCallback});
		}
		
        // Get array of all bits' display names
        , getAllBitNames : function()
        {
            var names = [];
            for(var bitLoc in DataModel.model)
            {
                names.push(DataModel.model[bitLoc].displayname);
            }            
            return names;
        }
        
		/* 
		 * Stores bitmarks for the resource:
		 * 
		 */
		, addResourceProps: function(href, properties) {
		    
		    if(!href || !properties) {
		        return;
		    }
		    
		    if(!this.model[href]) {
		        this.model[href] = {};
		    }
		    
		    jQuery.extend(this.model[href], properties);
		    
		    // sanitize or normalize the resource ID / UUID
		    var resource = this.model[href];
		    resource["resource-id"].href = AXIS.Util.normalizeUUID( resource["resource-id"].href );
		}
		
		/* Adds bitmarks for resources
		 * args: @resourceUrl : URL for the resource
		 *       @bmarkInfo : {
		 *                      <name of the bitmark (name/description/tag)> : "value"
		 *                      href : <href of the bitmark file>
		 *                  }
		 *
		 */
		, addResourceBitmark: function(resourceUrl, bmarkInfo) {
		    
		    if(!resourceUrl || !bmarkInfo) {
		        return;
		    }
		    
		    if(!this.bitmarks[resourceUrl]) {
		        this.bitmarks[resourceUrl] = {};
		    }
		    
		    for(var bmark in bmarkInfo) {
		        
		        switch(bmark) {
		            case "description":
		                        this.bitmarks[resourceUrl][bmark] = {
	                                "value" : bmarkInfo[bmark],
	                                "href" : bmarkInfo.href
		                        };
		                        break;
		                        
		            case "tag" :
		                        if(!this.bitmarks[resourceUrl]["tags"]) {
		                            this.bitmarks[resourceUrl]["tags"] = [];
		                        }
		                        this.bitmarks[resourceUrl]["tags"].push({
		                            "value": bmarkInfo[bmark],
		                            "href" : bmarkInfo.href
		                        });
		                        break;
		            
		        }
		    }
            // jQuery.extend(this.bitmarks[href], bmarks);
		    
		}
		
		
		/* Clear out the resource/tag info we have */
		, clearTagTables: function() {
		    this.tagHashTable = {};
		    this.model = {};
		    this.bitmarks = {};
		    this.modelOrder = [];
		}
		
		// Initialize the tag hash table
		, initTagDataStructures: function() {
		    this.clearTagTables();
			this.pageTemplates = {};
			this.listItemTemplates = {};
		}
		
		/*
		 * Fetch the template / templates for tags from the server
		 * 
		 * {
		 *	pageTemplates : {"tag1" : "tagTemplate1", "tag2": "tagTemplate2", ...},
		 *  listItemTemplates : { "view1" : "viewTemplate1", "view2" : "viewTemplate2", ...}
		 * }
		 * 
		 */
		, fetchTagAndlistItemTemplates: function( templateUrl ) {
			
			/*
			templateUrl = templateUrl || "/configuration/tags.json";
			*/
			
			//TODO: default URL instead of error alert
			if(!templateUrl) {
				// signal error: template file URL is non-existant
				alert("File URL for tag template file invalid");
				return false;
			}
			
			var templates = AXIS.evalJSON( (AXIS.WebDAV.GET({
													headers:{'X-No-Rewrite':'1'}, 
													asynch:false,
													url:templateUrl
													})).responseText );
			
			if(!templates || !templates.pageTemplates || !templates.listItemTemplates) {
				// Invalid json for tags template
				alert("Invalid / malformatted JSON for tag templates");
				return false;
			}
			
			this.listItemTemplates = templates.listItemTemplates;
			
			this.pageTemplates = templates.pageTemplates;
			
			this.tagsDir = templates.tagsDir;
			
			return true;
		}
		
		, initialized: function(){
			return !!this.model;
		}
		
		/*
		 * Get the properties for a particular HREF resource
		 */
		 , getPropertiesForUrl: function( href ) {
		     return this.model[href] ? this.model[href] : "";
		 }

		/* Get the information about a tag:
		 * param @tagname : name of the tag
		 * 
		 * output: {
		 *            "numberOfResources" : number of resources that have this tag
		 *            "template" : Template for this tag
		 *           }
		 */
		, getTagInfo: function( tagname ) {
	
			// special case for "All"
			tagname = tagname || "All";

            /* var tagResourceLength = this.tagHashTable[tagname] ? this.tagHashTable[tagname].length : 0; */
            var tagResourceLength = 0;
            var tagTemplate = this.pageTemplates[tagname] || this.pageTemplates["default"];
            
            return { "numberOfResources": tagResourceLength, "template" : tagTemplate };
		}

        /*
         * Return the list Item template 
         */
        , getListItemTemplate: function( viewType ) {
            return this.listItemTemplates[viewType] || "";
        }

		// return the count of all resources
		, getResourceCount: function() {
			var allResources = this.getResourceUrls();
			return allResources.length;
		}

		// return all tags and number of items that have that tag
		, getTagsAndCounts : function() {
			// just compute this on the fly now
			// if there are performance concerns later, maintain this as a separate object
			var countObj = [];
	        var tag;
	        
			for( var i = 0; i < this.tagsDir.length; i++) {
			    tag = this.tagsDir[i];
                /* countObj.push( { tagtitle: tag, tagname : tag, count: this.tagHashTable[tag].length} ); */
                countObj.push( { tagtitle:tag, tagname: tag, count:this.tagHashTable[tag] } );
			}
			return countObj;
			
		}
		
		/**
		 * Get the count of all tags from the backend
		 *
		 * arguments : @tags - array of tags to be displayed on the left
		 *             @url - the URL to send the request to
		 */
		, fetchTagCounts: function( url, tags ) {
          {

            var d = {};
            
            d.url = "/";
            d.body    = "<?xml version='1.0' encoding='utf-8' ?> \
                        <lb:property-stats xmlns:lb='"+ AXIS.WebDAV.ns.bm + "'> \
                        <D:prop xmlns:D='" + AXIS.WebDAV.ns.d + "'> <lb:tag/> </D:prop> \
                        <lb:sample-set>                 \
                        <--lb:values-->                 \
                         </lb:sample-set>               \
                        <lb:stat> <lb:count/> </lb:stat> \
                        </lb:property-stats>";
            
            var valueTemplate = "<lb:value>--val--</lb:value>";
            var values= "";

            for(var i = 0; i < tags.length; i++) {
                values += valueTemplate.replace("--val--", tags[i]);
            }
            
            d.body = d.body.replace("<--lb:values-->", values);

            d.callback = function(response) {
                //TODO: error handling when unable to fetch tags
                if (response.httpHandle.status > 210) {
                    return; // fail silently
                }

				var respXML = response.responseXMLObject();
				var resp = respXML["property-stats"]["sample-set"]["stat"];
				if(!resp) {
					alert("Couldn't get report response from the server");
					return;
				}
				
				for(var i = 0; i < resp.length; i++) {
				    DataModel.tagHashTable[resp[i].value] = resp[i].count;
				}
            }

            return AXIS.WebDAV.REPORT(d);
          };
            
		}

		/*
		*	Get all resource URLs from the DataModel
		*  Return value: array of resource URLs or an empty array if there was an error
		* 
		*  Note that returning null / empty array have different semantics. 
		* Returning null means there was an error. Empty array means there's nothing to return
		*
		* @args - count : number of URLs to return (used for paging)
		*/
		, getResourceUrls: function( options ) {
	        var count = (options && options.count) || 10000000; // a very high default
			var returnVal = [];
					
			if(!this.initialized()) {
				return returnVal;
			}
			var i = 0;
			
			var resourceOrder = this.modelOrder;
			var num = resourceOrder.length;
			
			for(var i = 0; i < num; i++) {
			    if(i < count) {
				    returnVal.push(resourceOrder[i]);
			    }
                else {
                    break;
                }
			}
			
            // for(var url in this.model) {
            //     if(i < count) {
            //         i++;
            //      returnVal.push(url);
            //     }
            //                 else {
            //                     break;
            //                 }
            // }
					
			return returnVal;
		}
		
		/*
		 * Function to fetch the UUID of a resource given it's base URL
		 */
		 , getUUID: function( resourceBaseUrl ) {
		     var resourceId;
		     if(!resourceBaseUrl || !this.model[resourceBaseUrl] || !(resourceId = this.model[resourceBaseUrl]["resource-id"])) {
		         return 0;
		     }
		     
		     return resourceId.href;
		     
             // var uuid = resourceId.href;
             // 
             // // TODO : moar error checking ?
             // // an example href is "urn:uuid:bb3eb226-999a-11de-943c-99aa08b61ce1"
             // // strip the urn:uuid:
             // uuid = uuid.substr(uuid.lastIndexOf(":") + 1);
             // 
             // // remove the hyphens
             // uui = uuid.replace(/-/g, "");
             // 
             // return uuid;
		 }
		 
		 /**
		  * Get the elements added / subtracted from a set of elements
		  * Added elements : newSet - elements present in the originalSet
		  * Removed elements : originalSet - elements present in the newSet
		  */
		 , getSetDiff: function( originalSet, newSet ) {

		     // if either of them is non-existant, return the original arrays
		     if(!originalSet || !newSet || (originalSet.length == 0) || (newSet.length == 0) ) {
		         return {"added":newSet, "removed":originalSet};
		     }
		     
		     // temp hash is used to calculate the diff
		     var i, hash = {}, added = [], removed = [];
		     
		     for( i = 0; i < newSet.length; i++) {
		         hash[newSet[i]] = 1;
		     }
		     
		     // Hash markers :
		     // -1 : remove this tag
		     // 0 : keep it
		     // +1 : add this tag
		     
		     // Set the value to 0 if it was present greater than zero
		     // If not present, set it to -1
		     for( i = 0; i < originalSet.length; i++) {
		         // if the tag exists, check if it is present in the new tag list, if not, mark it to be removed
		         if( typeof(hash[originalSet[i]]) != "undefined" ) {
		             if( hash[originalSet[i]] > 0 ) {
		                 hash[originalSet[i]] = 0;
	                 }
		         } else {
		             hash[originalSet[i]] = -1;
		         }
		     }
		     
		     // Finally gather the results
		     for(var key in hash) {
		         if( hash[key] > 0 ) {
		             added.push(key);
		         }
		         else if ( hash[key] < 0 ) {
		             removed.push(key);
		         }
		     }
		     
		     return {"added" : added, "removed" : removed};
		 }
		 
		 
		 /*
		  * Save a property : proppatch
		  */
		  
		  , saveProperty: function(resourceBaseUrl, propName, propValue) {

              var propertiesToSet = [{
                  name: propName,
                  value: propValue
              }];

              var setProps = AXIS.WebDAV.PROPPATCH({'url': resourceBaseUrl,
                                                     'setProperties': propertiesToSet,
                                                     callback: function( response ) {
                                                         if (response.httpHandle.status > 210) {
                                                             // TODO: handle name save errors later
                                     	                    return; // fail silently
                                     	                }
							 // Update local property
							 DataModel.model[resourceBaseUrl][propName] = propValue;
                                                     }
                                                    });
		  }
		 
    	/*
    	 * Set a bitmark on a bit. If it's already set, edit it instead
    	 * @param resourceBaseUrl: The UUID of the resource being bitmarked
    	 * 
    	 * returns : the new value of the bitmark (an array of tags, or the string for name / description)
    	 */
    	 , saveBitmark: function( resourceBaseUrl, bitmarkName, bitmarkValue ) {

             var bitmarkUrl = null;
             var retVal = null;
             
             if(!resourceBaseUrl || !bitmarkName) {
                 // error!
                 // alert("Cannot set bitmark: bad input data");
                 return;
             }

             var bmark;
             
             switch(bitmarkName) {
                 
                 case "description" :

                         if(this.bitmarks[resourceBaseUrl] && (bmark = this.bitmarks[resourceBaseUrl][bitmarkName])) {
                              bitmarkUrl = bmark.href;
                          }
			 
			 if( !bitmarkValue ) 
			     {
				 this.removeBitmark(bitmarkName, bitmarkValue, resourceBaseUrl);
			     }
			 else
			     this.addEditBitmark(bitmarkUrl, bitmarkName, bitmarkValue, resourceBaseUrl);
                         
                         retVal = bitmarkValue;
                         break;
                         
                         
                case "tags" :
                        /*
                         * Array diff : get the original tags and the new tags and call "getSetDiff"
                         * for added : call addBitmark
                         * for removed : call removeBitmark
                         */
                         var i, originalTags = (this.bitmarks[resourceBaseUrl] && this.bitmarks[resourceBaseUrl][bitmarkName]) || [];
                         
                         // TODO: build a reverse index so it's easy to get tags for a resource
                         // We won't have to glean all tag names from bitmarks in that case 
                         var currentTags = [];
                         
                         for(i = 0; i < originalTags.length; i++) {
                             currentTags.push(originalTags[i].value);
                         }
                         
                         var newTags = bitmarkValue.split(",");
                         
                         for(i = 0; i < newTags.length; i++) {
                             newTags[i] = AXIS.Util.trim(newTags[i]);
                         }
                         
                         var diff = this.getSetDiff(currentTags, newTags);
                         
                         for(i = 0; i < diff.added.length; i++) {
                             this.addEditBitmark(null, "tag", diff.added[i], resourceBaseUrl);
                         }
                         
                         for(i = 0; i < diff.removed.length; i++) {
                             this.removeBitmark("tag", diff.removed[i], resourceBaseUrl);
                         }
                         
                         retVal = newTags;
                         
                         break;
             }
             return retVal;
         }
         
         /**
          * Remove a bitmark from the system
          */
         , removeBitmark: function( bitmarkName, bitmarkValue, resourceBaseUrl ) {
             
             var bitmark;
             // the bitmark is stored in an array referenced by "tags"
             bitmarkName = (bitmarkName === "tag") ? "tags" : bitmarkName;
             
             var removedBitmark; // used to store a bitmark reference for deletion later
             
             // bitmark name, and either of bitmarkURL or resourcebaseURL are required
             // if bitmarkURL is null and resourceBaseUrl is present, the respective bitmark should exist
             if(!bitmarkName ||   
                    !(resourceBaseUrl && this.bitmarks[resourceBaseUrl] && (bitmark = this.bitmarks[resourceBaseUrl][bitmarkName]))) {

                 // silent fail .. don't make noise if you can't do anything
                 // TODO: error handling: maybe add console logs ?
                 return;
             }
                 
             // Extract the bitmarkURL from the bitmarks map
             switch(bitmarkName) {
             
                 case "description":

                            bitmarkUrl = bitmark.href;
                            removedBitmark = bitmark;
                            break;
                        
                case "tags":
                        
                            // 'bitmark' is an array of tags : [{value:"...", href:"..."}, {...}, ...]
                            for(var i = 0; i < bitmark.length; i++) {
                            
                                if(bitmarkValue === bitmark[i].value) {
                                    bitmarkUrl = bitmark[i].href;
                                    removedBitmark = i;
                                    break;
                                }
                            }
                        
                            break;

                default: // something wrong!
                            return;
             }
         
             AXIS.WebDAV.DELETE({
                 asynch: false,
                 url: bitmarkUrl,
                 callback: function( response ) {

                        //TODO: better error handling
                        if (response.httpHandle.status > 210) {
     	                    return; // fail silently
     	                }
     	                
                        // update our internal records
			if( bitmarkName == 'tags' )
			    bitmark.splice(removedBitmark,1); // array deletion is different than regular deletion
			else
			    delete removedBitmark;
 					}
             });
             
             
         }
         
         /**
          * Add a bitmark to the system
          */
         
         , addEditBitmark: function( bitmarkUrl, bitmarkName, bitmarkValue, resourceBaseUrl ) {

             // bitmark name, value, and either of bitmarkURL or resourcebaseURL are required
             if(!bitmarkName || !bitmarkValue || !(bitmarkUrl || resourceBaseUrl)) {
                 // silent fail .. don't make noise if you can't do anything
                 // TODO: error handling: maybe add console logs ?
                 return;
             }
             
    	     if(!bitmarkUrl) {
    	         
    	         var uuid = this.getUUID(resourceBaseUrl);
    	         
    	         var folderUrl = this.bitmarkBaseUrl + uuid;    	         
    	     
         	     var random = Math.floor(Math.random()*Math.pow(10,17));
    	     
                 var configObj = AXIS.WebDAV.MKCOL({ 
                        headers:{'If-None-Match':'*'}, 
    					asynch: false, 
    					url: folderUrl
                 });
             
                 bitmarkUrl = folderUrl + "/" + random;
             
                 // TODO: check for errors. for now, assume there's no errors
             
                 // Create the bitmark: don't check for collisions right now
                 configObj = AXIS.WebDAV.MKCOL({ 
                        headers:{'If-None-Match':'*'}, 
                        asynch: false, 
                        url: bitmarkUrl
                 });
    	     }
    	     
	     bitmarkValue = AXIS.Util.escapeEntities(bitmarkValue);

    	     var propertiesToSet = [{
                 name: bitmarkName,
                 value: bitmarkValue,
                 ns: this.bitmarkNS
             }];
             
             var setProps = AXIS.WebDAV.PROPPATCH({'url': bitmarkUrl,
                                                    'setProperties': propertiesToSet,
                                                    callback: function( response ) {
                                                        if (response.httpHandle.status > 210) {
                                    	                    return; // fail silently
                                    	                }
                                    	                
                                                        // update the information in your records
                                                        var bmarkInfo = {};
                                                        bmarkInfo[bitmarkName] = bitmarkValue;
                                                        bmarkInfo["href"] = bitmarkUrl;
                                                        DataModel.addResourceBitmark(resourceBaseUrl, bmarkInfo);
                                                    }
                                                   });
             
    	 }
    	 
    	 /*  
    	  * Get bitmarks associated with a bit
    	  *
    	  * @returnType : return type is only used for tags right now (when we need an array and not a csv list)
    	  */
    	  , getBitmark: function(resourceBaseUrl, bitmarkName, returnType) {
    	      
    	      if(!resourceBaseUrl || !bitmarkName) {
    	          return "";
    	      }
    	      
    	      var retVal;
    	      
    	      switch(bitmarkName) {
    	          
    	          case "description":
    	                	          
                        retVal = this.bitmarks[resourceBaseUrl] && this.bitmarks[resourceBaseUrl][bitmarkName]
                                    && this.bitmarks[resourceBaseUrl][bitmarkName].value;
                                    
                        break;
                        
                case "tags" : 
                
                        var tags = (this.bitmarks[resourceBaseUrl] && this.bitmarks[resourceBaseUrl][bitmarkName]) || [];
                        var tagArr = [];
                        
                        for(var i = 0; i < tags.length; i++) {
                            tagArr.push(tags[i].value);
                        }
                        
                        if(returnType == "array") {
                            retVal = tagArr;
                        } else {
                            retVal = tagArr.join(", ");                            
                        }
                        
                        break;
                }
    	      
	            return retVal || "";
	            
    	    }// end of getBitmark

	};
