var ResourceInfo = {
	
	configCache: {}
	
	// put some default text for empty descriptions and tags
	, defaultDescriptionText: "Describe it"
	
	, defaultTagsText: "TAG IT"
	
	, sourceEditorUrlTemplate: "/apps/editor/#"
	
	, mybitsText: "My Bit"
	
	// get the URL for a resource image
	, getImageUrl: function( baseUrl ) {

		if(!baseUrl) {
			// todo: error handling
			return "";
		}

		var suffix = baseUrl.charAt(baseUrl.length - 1) == "/" ? "" : "/";
		return baseUrl + suffix + "configuration/icon.png";
	}

	// get the URL for the configuration file for a resource
	, getConfigUrl: function( baseUrl ) {

		if(!baseUrl) {
			// todo: error handling
			return ""; 
		}

		var suffix = baseUrl.charAt(baseUrl.length - 1) == "/" ? "" : "/";
		return baseUrl + suffix + "configuration/configure.json";
	}
	
	/** Refine the input value in the following way :
	
	    for name : if bitmarkValue is null, extract val from baseUrl
	    for tags / description : if null, return the default text for them

	**/
	, refineBitmark: function( resourceBaseUrl, bitmarkName, bitmarkValue, returnDefault ) {
	    
	    if( !bitmarkName || !resourceBaseUrl ) {
	        // TODO: error!
	        return "";
	    }
	    
	    switch(bitmarkName) {
	        
	        case "description" : retVal = this.getDescription(resourceBaseUrl, bitmarkValue, returnDefault);
	                    break;
	        
	        case "tags" : retVal = this.getTags(resourceBaseUrl, bitmarkValue, returnDefault);
	                    break;
	                    
	        default: // TODO: ERROR! 
	                retVal = "";
	                break;
	    }
	    
	    //TODO : sanitize retVal
        // retVal = AXIS.Util.escapeEntities(retVal);
	    
	    return retVal;
	}

	/**
	 * convert the tags list to a list of anchor tags
	 * @resourceBaseUrl : base Url of the resource being displayed
	 * @tags : an array of tags on the resource
	 */
	, getTags: function( resourceBaseUrl, tags, returnDefault) {
	    
	    var tagsTemplate = "<span class='defaultTags <!--defaultTags-->'>TAG IT</span> \
	                        <span class='tagsHeader <!--hideTags-->'>TAGS:&nbsp; \
	                        <span class='tagsContainer' bitmark='tags'><!--tagItems--></span></span>";
	    
        // split the tags, put them in an anchor, add them to an array, join the array later
        var tagItemTemplate = "<a href='#url=/&tag=<!--tagname-->' class='tagsText' tagname='<!--tagname-->'><!--tagname--></a>";
        
        var tagStringArr = [];
        var tagLimit = Gallery.LIMIT_TAGS; // so that we don show too many tags
        for(var i = 0; i < tags.length; i++) {
            // Check if we've run past the limit we can display
            if (tagLimit > 0) {
                tagLimit = tagLimit - tags[i].length;
            }
            else 
            {
                tagStringArr.push('...');
                break;                
            }

            tags[i] = AXIS.Util.escapeEntities(tags[i]);
            tagStringArr.push(tagItemTemplate.replace(/<!--tagname-->/g, tags[i]));
        }
        
        var tagListString = tagStringArr.join(", ");
        var returnString = tagsTemplate;
        
        returnString = returnString.replace(/<!--tagItems-->/g, tagListString);
        
        if(tagListString ==="") {
            returnString = returnString.replace(/<!--hideTags-->/g, "hidden");
            
            if(returnDefault) {
                returnString = returnString.replace(/<!--defaultTags-->/g, "");
            }
            else {
                returnString = returnString.replace(/<!--defaultTags-->/g, "hidden");
            }
        } else {
            returnString = returnString.replace(/<!--defaultTags-->/g, "hidden");
            returnString = returnString.replace(/<!--hideTags-->/g, "");
        }

        return returnString;
	}
	
	, getDescription: function( resourceBaseUrl, description, returnDefault ) {
	    return AXIS.Util.escapeEntities(description || (returnDefault ? this.defaultDescriptionText : ""));
	}
	
	/* return the markup for a single item rendered in the gallery. If configObject is null,
	 * a request is made to the server to fetch the config data for the particular resource.
	 * After fetching the resource, stores it in the global config map as well
	 * @resource: the resource metadata
	 * @config : config for the resource: used to build the HTML to be displayed
	*/
	, getResourceMarkup: function( resourceBaseUrl, config ) {

        // var config = ResourceInfo.getResourceConfig(resourceBaseUrl);

		if(!config || !resourceBaseUrl) {
			return "";
		}
		
		// var imgUrl = Gallery.getImageUrl( resourceBaseUrl );
        // var convertedSubdomainUrl = AXIS.Util.convertToSubdomainUrl( resourceBaseUrl );
        // console.log("ConvertedSubdomain url : " + convertedSubdomainUrl);
		var resourceMarkup = Gallery.state.getCurrentResourceTemplate();
		var tagTemplate = Gallery.navList.tagnameTemplate;
        // var absoluteBaseUrl = AXIS.Util.getAbsoluteUrl( resourceBaseUrl );
        // use the shiny new getBitUrl function
		var absoluteBaseUrl = AXIS.Util.uri.getBitUrl( resourceBaseUrl );
		
		var imgUrl = thumbnailService.getThumbUrlFromService( absoluteBaseUrl + "?nobar" );

		// de-"http://"ize and de "trailing-/"ize the URL
		var convertedSubdomainUrl = absoluteBaseUrl.replace(/^http:\/\/|\/$/g, "");

		/* switch for changing dates */
        var lastModifiedDate = AXIS.Util.getDateFromISO8601String(config.lastmodified);
		var dateString = AXIS.Util.prettifyDate(lastModifiedDate);
		// the displayname 'should' be set on all bits
		var bitName = AXIS.Util.escapeEntities(config.displayname);
		bitName = AXIS.Util.lang.truncate(bitName, Gallery.LIMIT_DISPLAYNAME);   
        
        // var bitName = AXIS.Util.getBitNameFromRelativeUrl(resourceBaseUrl);
        // var bitName = this.refineBitmark(resourceBaseUrl, "name", DataModel.getBitmark(resourceBaseUrl, "name") );
        var tagsArr = DataModel.getBitmark(resourceBaseUrl, "tags", "array");
        // return the default value (Tag it / Describe it) only for the owners
        var tags = this.refineBitmark(resourceBaseUrl, "tags", tagsArr, config.isOwner);
        var description = DataModel.getBitmark(resourceBaseUrl, "description");
        description = this.refineBitmark(resourceBaseUrl, "description", description, config.isOwner);
		description = AXIS.Util.lang.truncate(description, Gallery.LIMIT_DEFAULT);
        
        resourceMarkup = resourceMarkup.replace(/<!--rname-->/g, bitName);
		resourceMarkup = resourceMarkup.replace(/<!--thumb_url-->/g, imgUrl);
        resourceMarkup = resourceMarkup.replace(/<!--resource_c_date-->/g, dateString);
        // resourceMarkup = resourceMarkup.replace(/<!--resource_views-->/g, config.popularity);
        // resourceMarkup = resourceMarkup.replace(/<!--resource_c_date-->/g, creationDate);
        resourceMarkup = resourceMarkup.replace(/<!--display_url-->/g, convertedSubdomainUrl);
        resourceMarkup = resourceMarkup.replace(/<!--resource_rel_url-->/g, resourceBaseUrl);
        resourceMarkup = resourceMarkup.replace(/<!--rdescription-->/g, description);
        resourceMarkup = resourceMarkup.replace(/<!--rtags-->/g, tags);
		resourceMarkup = resourceMarkup.replace(/<!--resource_url-->/g, absoluteBaseUrl);
        
        var ownerTxt = config.isOwner ? this.mybitsText : config.owner;
        resourceMarkup = resourceMarkup.replace(/<!--owner_display-->/g, ownerTxt);
        
        resourceMarkup = resourceMarkup.replace(/<!--owner_name-->/g, config.owner);
        
        var editorUrl = this.sourceEditorUrlTemplate + resourceBaseUrl;
        resourceMarkup = resourceMarkup.replace(/<!--editor_url-->/g, editorUrl);

        if( config.isOwner) {
            resourceMarkup = resourceMarkup.replace(/<!--isowner-->/g, "owner");
            // my bit
        } else {
            resourceMarkup = resourceMarkup.replace(/<!--isowner-->/g, "visitor");
            // replace owner name in markup
        }
        
        // See if we need to highlight this bit ..
        if(AXIS.Cookies.read(resourceBaseUrl)) {
            // See if we need to highlight a bit
	        resourceMarkup = resourceMarkup.replace(/<!--highlight-->/g, "highlight");
	        // add the "new bit" banner as well.. clean this up later
            resourceMarkup = resourceMarkup.replace(/<input type='hidden' name='href'/, "<a class='justcopied' href='"+absoluteBaseUrl+"'></a><input type='hidden' name='href'");
            AXIS.Cookies.erase(resourceBaseUrl, { path: '/apps/' });
        } else {
	        resourceMarkup = resourceMarkup.replace(/<!--highlight-->/g, "");
        }

		return resourceMarkup;
	}
	
	/* get config information given the base URL */
	, getResourceConfig: function( resourceBaseUrl ) {
		
		if( !resourceBaseUrl ) {
			return null;
		}
		
		return DataModel.getPropertiesForUrl( resourceBaseUrl );
/*		
		// see if we have the config available already
		var config = this.configCache[resourceBaseUrl];

		if( !config ) {

			var configUrl = ResourceInfo.getConfigUrl( resourceBaseUrl );
	
			var configObj = AXIS.WebDAV.GET({ 
											headers:{'X-No-Rewrite':'1'}, 
											asynch: false, 
											url: configUrl
										});
			config = AXIS.evalJSON( configObj.responseText );

			if(!config) {
				//  no config, return null
				return null;
			}

			// store config in the global object
			this.configCache[resourceBaseUrl] = config;
	
		}
		
		return config;
*/
	}
};

var Gallery = {
    
    // The command handlers installed on the page
    // NOTE: state needs to be the first module to get the command as the other modules
    // depend on the state!!
    commandHandlers: ["rList","navList", "chrome", "pagebar", "toolbar"]// toolbar coming soon : , "toolbar"]
    
    , LIMIT_DISPLAYNAME : 70
    , LIMIT_DEFAULT : 500
    , LIMIT_TAGS : 500
    
    // The URL for the blank bit to copy when "Add bit" is clicked
    , blankBitUrl: "/home/bits/bits/new-bit"
    
    /** The main gallery command handler
      * The command has the following members
      *     cmd         : name of the command to execute
      *     cmdArgs     : arguments passed to the command handler
      *     source      : source where the command was fired from
      * 
      * The handler calls all the other command handlers and stops
      * if one of them returns a true value (signifying that the command 
      * was handled and shouldn't be propagated). Keepin' it flexible!
      *     
      */
    , handleCommand: function( command ) {
        
        if(!command || !command.cmd) {
            return;
        }
        
        var sortBy = this.state.getSortBy();
        var sortOrder = this.state.getSortOrder();
        
        var defaultDataOptions = {"orderby": sortBy,
                                    "order": sortOrder,
                                    "limit":Gallery.rList.defaultPageSize + 1
                                };
        
        switch(command.cmd) {
            
            case "urlFilter":   this.clearConfigCache();
                                DataModel.refreshData( command.cmdArgs, this.state.getSelectedTag(), defaultDataOptions );
                                break;

            case "tagFilter":   this.clearConfigCache();
                                DataModel.refreshData( this.state.getUrlForBits(), command.cmdArgs, defaultDataOptions );
                                // this.updateSelectedTagDescription();
                                break;
                                
            case "contextChange": 
            case "authUpdate":
                                
            default:            break;
        }
        
        // Call all of the module command handlers
        var handlers = Gallery.commandHandlers;
        for(var i = 0; i < handlers.length; i++) {
            var handler = Gallery[handlers[i]];
            if( handler && handler.handleCommand && handler.handleCommand.call(handler, command) ) {
                break;
            }
        }
        
    }
    
    , pagebar : null
    
    // Update the tag description, name, and count 
    // TODO: the new "chrome" component in the gallery should take care of this
	, updateSelectedTagDescription: function( tagname ) {

		tagname = tagname || Gallery.state.getSelectedTag();

		var tagObj = DataModel.getTagInfo(tagname);
		
		// a better solution might be to get the first text-node and doing a nodeValue = tag-description
		// there used to be a bug in IE when a space right after tag open was treated as a different text node
		// this solution, although a little inefficient works cross browser in all cases
		var heading = jQuery(".listbit-heading");


        // var tagCount =   heading.find(".tag-count").text("(" + tagObj.numberOfResources + ")").clone();

        // tagCount.appendTo(heading.find("h1:first").text(tagname));

		// remove the elements between intro and the list and insert the description
		// jQuery("div.intro").nextAll(":not('.listbit')").remove().end().after(tagObj.description);
		// debugger;
		
        jQuery(".intro-content").html(tagObj.template);
        //                 jQuery(".intro-content").remove();
        // jQuery("div.intro").after(tagObj.template);
	}
	
	/**
	 * Component to handle the envelope around the list of resources (for now)
	 */
	, chrome: {

        init: function(contextInfo) {
            var cmdObj = {"cmd":"contextChange", 
                            "cmdArgs":contextInfo, 
                            "source":"chromeInit"
                            };
            Gallery.handleCommand(cmdObj);
        }

        , handleCommand: function(command) {
            if(!command || !command.cmd) {
                return;
            }
            
            switch(command.cmd) {

                case "contextChange":
                case "authUpdate":
                        /*contextInfo is of the form : {
                            type : user / global
                            info : name of user if type == user .. name of tag if type == tag
                        } */
                        var contextInfo = command.cmdArgs;
                        
                        this.handleContextChange(contextInfo);
                        
                        break;
                        
                default: break;
            }
        }
        
        , specialStates : {
            
            "new" : "<p><strong>Welcome to your new space</strong> for hosting, sharing and making anything! \
                            We’ve preloaded a couple goodies. Modify them, get more, add your own.</p>",
            
            "upgraded" : "<p><strong>Welcome to your upgraded workspace.</strong> Tag and organize Bits. Find new Bits. \
                                Check out the cleaned-up Source editor.<br>And ping us with your feedback. Thanks!</p>",
            
            "noBits" : "<a class='btnSpriteSave' href='#tag=All&url=/'><strong>Find Bits</strong></a> \
                        <a class='btnSpriteSave makeBit' href='#'><strong>Make new Bit</strong></a>",
                        
            "ownerGotNoBits" : "<p><strong>You got no Bits ...</strong><br/>but lots of potential</p>",
            
            "ownerNoBitsSubTxt" : "<a class='btnSpriteSave' href='#tag=All&url=/'><strong>Find Bits</strong></a> \
                        <a class='btnSpriteSave makeBit' href='#'><strong>Make new Bit</strong></a>",
                        
            "visitorGotNoBits" : "<p>We looked hard and long but ...<strong><!--user-->'s got no Bits</strong></p>",
            
            "visitorNoBitsSubTxt" : "<a class='btnSpriteSave' href='#tag=All&url=/'><strong>Find Bits</strong></a>",

            "visitorSubTxt" : "<p>Lovely things for nice folks like you. Grab your favs.</p>"
            
        }
        
        , userContextChange : function(contextInfo) {

            var heading, subtext, hideToolbar;
            
            var gotNoBits = (DataModel.getResourceUrls().length === 0) ? true : false;
            // see who one is .. for replacement in text templates
            var whoAmI = contextInfo.isOwner ? "owner" : "visitor";
            var username = (whoAmI == "visitor") ? contextInfo.info : "You";
            
            if(gotNoBits) {
                heading = this.specialStates[whoAmI + "GotNoBits"].replace(/<!--user-->/g, username);
                subtext = this.specialStates[whoAmI + "NoBitsSubTxt"];
            } else if (!contextInfo.isOwner) {
                heading = contextInfo.info + "'s Bits";
                subtext = this.specialStates["visitorSubTxt"];
            } else if(contextInfo.isOwner){
                heading = "My Bits";
                var userState = Gallery.state.getUserState();
                subtext = this.specialStates[userState];
                if(subtext) {
                    Gallery.state.resetUserState();
                }
            }
                
            var newValues = {
                "heading" : heading || contextInfo.info,
                "subtext" : subtext || "",
                "hideToolbar" : gotNoBits || false
            };
            
            return newValues;
        }
        
        , toolbarDisplay: function(hideToolbar) {
            if(hideToolbar) {
                jQuery(".toolbar").hide();
            } else {
                jQuery(".toolbar").show();
            }
        }
        
        /* The global tag filter was changed: fix the chrome to reflect that */
        , globalContextChange: function(contextInfo) {
            
            var tagName = contextInfo.info;
            var tagObj = DataModel.getTagInfo(tagName);
            var template = tagObj.template;
            
            // Special case : for tag name "All", the heading is "Find Bits"
            if(tagName == "All") {
                tagName = "Find Bits";
            }

            var dValues = {
                "heading": tagName,
                "hideToolbar": false,
                "subtext": template
            };            
            
            return dValues;
        }
        
        , handleContextChange: function(contextInfo) {
            var dValues;
            
            if (contextInfo.type == "user") {
                dValues = this.userContextChange(contextInfo);
            } else {
                dValues = this.globalContextChange(contextInfo);
            }
            
            jQuery(".listheader").html(dValues.heading);
            jQuery(".intro-content").html(dValues.subtext);

            this.toolbarDisplay(dValues.hideToolbar);

            jQuery(".makeBit").click(Gallery.toolbar.handleAddBitClick);
	    }
	    
    }
    , rList: {

/*
    	// Template for rendering an item in the gallery
    	resourceTemplate : "<div class='item'>\
    							<a class='editItemButton button copy'>Get bit!</a>\
    							<input type='hidden' name='href' value='<!--resource_loc-->'></input>\
    							<div class='itemImage'><a href='<!--resource_loc-->'><img src='<!--imageUrl-->'></img></a></div>\
    							<h4 class='itemTitle'><a href='<!--resource_loc-->'><!--rtitle--></a></h4>\
    							<p class='itemDescription'><!--rDescription--></p>\
    							<p class='itemTagContainer'><span class='itemTagTitle'>Tags:</span>\
    							<a class='itemTags' a href='#'><!--rtags--></a></p>\
    						</div>", 
*/
        init: function() {
            // TODO: Find the base element for the list in the template and render itself
            // refreshDisplay is handled when the 'contextChange' command is sent
            // this.refreshDisplay();

            // attach handler for more bits click
            jQuery(".moreBits").click(this.handleMoreBitsClick);

        }

        // filter the resources in the list based on tag
        , refreshDisplay: function( tagname, options ) {

            if(!tagname) {
                tagname = Gallery.state.getSelectedTag();
            }
            
            // make sure sort buttons have the correct class
            // refreshDisplay here shouldn't be messing around in the toolbar, wth is wrong with our design sense ?
//            jQuery(".sortListItem.selected").removeClass("selected");
//            var listitem = jQuery("div[sortBy='" + Gallery.state.getSortBy() + "']").parent();
//            listitem.addClass("selected");

//            if (Gallery.state.isSortAscending()) {
//                listitem.addClass("ascending");
//            } else {
//                listitem.addClass("descending");
//            }

    	    var resources = DataModel.getResourceUrls();

            // reset the "more..."
            jQuery(".moreBits").attr("nextPage", 1);

    	    // Finally render the resources 
    	    Gallery.rList.render(resources);

    	}
    	
    	// number of items to display in a page
        , defaultPageSize: 20
        
        /** Render an array of resources
         */
    	, render: function(resources) {
    	    
    		// by default, render all resources
    		if(!resources) {
    			resources = DataModel.getResourceUrls();
    			if(!resources) {
    				// error handling please
    				return;
    			} 
    		}

    		jQuery(".listbit-items").empty();

            this.append(resources);

            // jQuery(".moreBits").attr("nextPage", 1).show();
            // jQuery(".resourceDate").timeago();
    	}
    	
    	, getMarkupForResources: function(resources) {
    	    
    	    var resourceHtml = "";
    	    
     	    for(var i = 0; i < resources.length; i++) {

    			// fetch the config in the list renderer since we need to use this config
    			// for controlling event handlers as well
    			var config = ResourceInfo.getResourceConfig(resources[i]);

        		if(!config) {
                    continue;
        		}
                 // TODO : better privilege/permissions handling
                 var owner = AXIS.Util.uri.basename(config.owner.href);
                 // var isOwner = AXIS.User.isLoggedIn() && (AXIS.Util.uri.basename(config.owner.href) == AXIS.User.username());
                 var isOwner = (owner === AXIS.User.username());
                 
                 var markupConfig = jQuery.extend({}, config);
                 
                 markupConfig["owner"] = owner;
                 markupConfig["isOwner"] = isOwner;
                 

    			try {
    				resourceHtml += ResourceInfo.getResourceMarkup( resources[i], markupConfig );
    			} catch(e) {continue;}
    			
    		}
    		
    		return resourceHtml;
    		
    	}
    	
    	/* Append item resources to the current list
    	 * Assumes that 'defaultPageSize' (or less) items will be appended.
    	 * In case the resources being displayed are less than "defaultPageSize",
    	 * doesn't show a "more" at the end
    	 */
    	, append: function(resources, options) {
    	    
    	    if(!resources) {
    	        return;
    	    }  else if (resources.length <= Gallery.rList.defaultPageSize) {
    	        jQuery(".moreBits").hide();
    	    } else {
    	        /* We fetched one element too many, to determine whether to show the 'more'
                 * button, in which case, we don't need to show the last element
                 */
                 resources.splice(-1, 1);
	             jQuery(".moreBits").show();                 
    	    }
    	    
			var resourceHtml = this.getMarkupForResources(resources);
            
			jQuery(".listbit-items").append(resourceHtml);
            this.attachBitItemHandlers();

            try { // weird hack... basically, the first time we get here, the module hasnt loaded up... and this is the best place i can think of putting the tooltip logic 
                $AXIS.Modules.local.Tooltips.set();
            } catch(e) { } 
    	}

    	// attach event handlers to the rendered resource Item
    	, attachBitItemHandlers: function() {

            var self = this;
            
            /* This is how it works : the items, first created, have a class of "unbound" on them
                After attaching the event handlers, that class is removed(keeps it simple). This feature was introduced
                to deal with append where only the newly appended bits need to have the handlers attached.
            */
            
            jQuery("div.item.owner.unbound").each(function() {
                
                jQuery(this).removeClass("unbound");
                // goodies for the owner
                // jQuery(".editBitmark, [bitmark='description'], [bitmark='tags']", this).click( self.handleEditButtonClick );
                
                jQuery(".editBitmark, .defaultTags, [bitmark='description']", this).click( self.handleEditButtonClick );

                jQuery("[bitmarkinput]", this).keydown( function(evt) {
                    if(evt.keyCode == 27) {
                        // handle escape key
                        self.handleCancelButtonClick(evt);
                    }
                    else if(evt.keyCode == 13) {
                        // handle enter key
                        self.handleSaveButtonClick(evt);
                    }
                });

                function propagationStop(evt) { evt.stopPropagation(); }

                // jQuery("[bitmark='name']").hover( propagationStop, propagationStop );

                jQuery("a.saveBitmarkBtn", this).click( self.handleSaveButtonClick );

                jQuery("a.cancelBitmarkBtn", this).click( self.handleCancelButtonClick );

                jQuery(".bitmarkContainer", this).hover(function() {
                                                        jQuery(this).children("a.editBitmark").removeClass("hidden");
                                                    }, function() {
                                                        jQuery(this).children("a.editBitmark").addClass("hidden");
                                                    });

                jQuery(".configure", this).click( self.handleConfigureButtonClick );
                jQuery(".duplicate", this).click( self.handleDuplicateButtonClick );
                jQuery(".trash", this).click( self.handleTrashButtonClick );
                
            });

            // only get bit for the visitors
            jQuery(".item.visitor.unbound .getbit").click( self.handleGetBitClick).removeClass("unbound");
            
            // jQuery("a[tagname]").click( self.handleTagItemClick );
            
        }
        
        
        /* Click on the 'more..' at the bottom of the list of bits */
        , handleMoreBitsClick: function(event) {

            var moreElt = jQuery(event.currentTarget);

            // clicking on more increases the length of the displayed list of bits by 1 page size
            var fetchPage = parseInt(moreElt.attr("nextPage")) || 1;
            var nextPage = fetchPage + 1;
            
            moreElt.attr("nextPage", nextPage);
            
            // After adding 1 to the number of pages currently displayed, call appendToList
            Gallery.handleCommand({"cmd":"appendToList", "cmdArgs":{"page":fetchPage}, "source":"addBit"});
            
            // listElt = moreElt.parents("menubit-list");
            // listElt.style.scrollTop = listElt.style.scrollHeight;
        }
        
        
        // handle click on getBit
        , handleGetBitClick: function( event ) {

            // TODO: roll the href extraction into it's own function since all handlers use it
    	    var item = jQuery(event.currentTarget).parents("div.item:first");
            var bitUrl = item.find("input[name='href']").val();
            
            AXIS.Util.bit.getBit(bitUrl);
        }

    	// do the needful when the trash button is clicked
    	, handleTrashButtonClick: function( event ) {

    	    var item = jQuery(event.currentTarget).parents("div.item:first");
            var bitUrl = item.find("input[name='href']").val();

            // just in case ...
            if(!bitUrl) {
                return;
            }
            
            DataModel.moveResourceToTrash(bitUrl, { "success": function() {
                                                        var cmdObj = { "cmd": "refreshList", 
                                                                        "source":"bitItemTrash"
                                                                        };
                            		                    Gallery.handleCommand(cmdObj);
    	                                                Gallery.handleCommand({"cmd":"contextChange", "cmdArgs": Gallery.state.getContext(), "source":"bitItemTrash"});
                                                    }});
            
    	}
    	
    	, handleDuplicateButtonClick: function( event ) {
    	    
			//TODO: make this into a generic "refreshDatainList" command for the rList
    	    var item = jQuery(event.currentTarget).parents("div.item:first");
            var bitUrl = item.find("input[name='href']").val();
    	    
    	    function dupeSuccess(newBitUrl) {
				var sortBy = Gallery.state.getSortBy();
                var sortOrder = Gallery.state.getSortOrder();
    	        
    	        var options = {
                    "orderby": sortBy,
                    "order": sortOrder,
                    "limit": Gallery.rList.defaultPageSize + 1,
                    "success": function(resources) {
                        var cmdObj = {"cmd":"refreshList",
                                        "source":"addBit"
                                        };
                        Gallery.handleCommand(cmdObj);
		    	        Gallery.handleCommand({"cmd":"highlightItem", "source":"duplicateBit", "cmdArgs":newBitUrl});
                    }
                }

                var tag = Gallery.state.getSelectedTag();
                var url = Gallery.state.getUrlForBits();

                DataModel.refreshData(url, tag, options);
				
    	        //Gallery.handleCommand({"cmd":"refreshList", "source":"duplicateBit"});
    	    }

    	    // Add the bit to the DataModel and refresh Display
    	    DataModel.duplicateResource(bitUrl, {"success":dupeSuccess});
    	}
    	
    	, handleConfigureButtonClick: function( event ) {
    	    
    	    var item = jQuery(event.currentTarget).parents("div.item:first");
            var bitUrl = item.find("input[name='href']").val();
            
            AXIS.Util.bit.openConfigureTab(bitUrl);
    	    
    	}
    	
    	, handleCancelButtonClick: function( event ) {
    	    
    	    var editContainer = jQuery(event.currentTarget).parent();
    	    editContainer.addClass("hidden");
            editContainer.prev(".bitmarkContainer").show();
            
    	    event.preventDefault();
    	    event.stopPropagation();
    	}

        // Save the name when the user clicks 'save'
        // or hits "return" when the input textbox is in focus
        , handleSaveButtonClick: function( event ) {
            
            event.preventDefault();
            
            var element = jQuery(event.currentTarget);
            
            var item = element.parents("div.item:first");
            var bitUrl = item.find("input[name='href']").val();
            var bitmarkEditor = element.parents(".bitmarkEditor");
        
            var bitmarkName = element.attr("bitmarkinput");
            var newVal = "";

            if(bitmarkName) { // "return" on input element 
                newVal = element.val();
            } 
            else {
                var bitmarkInput = element.siblings(":input[bitmarkinput]");
                bitmarkName = bitmarkInput.attr("bitmarkinput");
                newVal = bitmarkInput.val();
            }

            
            switch(bitmarkName) {
                
                case "name" : 
                            
                            newVal = AXIS.Util.escapeEntities(newVal);
                            
                            DataModel.saveProperty(bitUrl, "displayname", newVal);
                            
                            break;
                
                default:
                            // sane values: persist them
                            newVal = DataModel.saveBitmark(bitUrl, bitmarkName, newVal);

                            // get or sanitize the value
                            newVal = ResourceInfo.refineBitmark( bitUrl, bitmarkName, newVal );
                
                            break;
                
            }
            
            //TODO : add caja support !!
            if (bitmarkName != 'tags') { // tags were already handled (big pain to handle it here)
                newVal = AXIS.Util.lang.truncate(newVal, bitmarkName == 'name' ? Gallery.LIMIT_DISPLAYNAME : Gallery.LIMIT_DEFAULT);
            }
            item.find("." + bitmarkName + "Container").html(newVal);
            bitmarkEditor.siblings(".bitmarkContainer").show();
            
            // TODO: better error handling
            /*if(!newVal) {
                alert("can't save");
                return;
            }*/
        
            bitmarkEditor.addClass("hidden");
        }
    	
    	// When an edit button is clicked, find the resource and present an edit box
    	, handleEditButtonClick: function( event ) {
    	    
    	    var editTrigger = jQuery(event.currentTarget);
            var bitmarkContainer = editTrigger.parents(".bitmarkContainer");

    	    // check if the edit was triggered from the icon or click on the text
    	    var bitmark = (!!editTrigger.attr("bitmark")) ? editTrigger : bitmarkContainer.find("[bitmark]");
    	    bitmarkContainer.find(".editBitmark").addClass("hidden");

    	    // Hide the edit pencil image
            // if(event.currentTarget.tagName.toLowerCase() != "img") {
            //     bitmark.next().addClass("hidden");
            //          }
    	    
    	    var bitmarkValue = bitmark.text();
    	    // TODO : clean demarkation of resourceItem and the list of items
            if( (bitmarkValue == ResourceInfo.defaultTagsText) || (bitmarkValue == ResourceInfo.defaultDescriptionText) ) {
                bitmarkValue = "";
            }
    	    
    	    var bitmarkName = bitmark.attr("bitmark");
    	    
    	    var bitmarkEditor = bitmarkContainer.siblings(".bitmarkEditor");
    	    // hide the bitmark container
            bitmarkContainer.hide();
            
    	    // show the bitmark editor 
    	    bitmarkEditor.removeClass("hidden");
    	    if(bitmarkName == "description") {
        	    bitmarkEditor.children("textarea").text(bitmarkValue).select();
    	    } else {
        	    bitmarkEditor.children("input:text").val(bitmarkValue).select();
    	    }

    	}

/*
    not needed for now 
    	/*
    	 * Handle click on a tag Item in the navigation list
    	 *
    	, handleTagItemClick: function( event ) {

    		// to stop the clearing of the #value in the URL
    		event.preventDefault();

    		// use currentTarget since the handler is attached to the <a> and not on the <span>'s
    		var target = event.currentTarget;

    		// var tagname = jQuery(target).children("span.menubit-item-name").text();
    		var tagname = jQuery(target).attr('tagname');

    		// don't re-render if the selected tag is clicked
    		if(tagname == Gallery.state.getSelectedTag()) {
    			return;
    		}

            Gallery.state.goToState("tag", tagname);
            // Fire command to filter by tag
            // var filterCmd = {
            //                 cmd: "tagFilter" ,
            //                 cmdArgs: tagname ,
            //                 source: "tagList"
            // };
            // Gallery.handleCommand(filterCmd);
            // Gallery.state.setSelectedTag( tagname );
    	}
*/
    	/* get the tag and filter the resources based on it
    	 * Main function that will handle refreshing the display of resources
    	 * and react to events
    	 */
    	, refreshResourceDisplay: function( tagFilter ) {

    		if(!tagFilter) {
    		    tagFilter = Gallery.state.getSelectedTag();
                // tagFilter = YAHOO.util.History.getCurrentState("tag");
    		}
    		var cmdObj = {"cmd": "tagFilter",
    		                cmdArgs: tagFilter,
    		                source:"url"
    		                };
    		Gallery.handleCommand(cmdObj);
    	}
    	
    	/** Highlight one of the (just copied/duplicated) items 
         *  in the gallery after duplication
         */
        , highlightItem: function( bitUrl ) {
            var absoluteUrl = AXIS.Util.uri.getBitUrl( bitUrl );
            jQuery("input[value='" + AXIS.Util.escapeEntities(bitUrl) + "']").parents("div.item").addClass("highlight").prepend("<a class='justcopied' href='"+absoluteUrl+"'></a>");
        }
        
        /** Fetch new bits in the datamodel and append them to the current list
         */
        , fetchNewBitsAndAppend: function(options) {

            var successFn = function(resourceList) {
                Gallery.rList.append(resourceList);
            };

            /* Important: We try to fetch "pageSize + 1" bits but display only "pageSize" bits.
             * This is done to determine if there are more bits we can fetch later and hence use this information
             * to show or hide the "More" button
             */
            
            var defaultOptions = {
                "page": "0",
                "limit" : Gallery.rList.defaultPageSize + 1,
                "success": successFn,
                "url": Gallery.state.getUrlForBits(),
                "tagName": Gallery.state.getSelectedTag(),
                "orderby": Gallery.state.getSortBy(),
                "order": Gallery.state.getSortOrder()
            };
            
            var options = jQuery.extend(defaultOptions, options);
            options.offset = options.page * Gallery.rList.defaultPageSize;
            
            DataModel.fetchNewResources(options);
        }
        
        /** Set the sort order in the state object 
         */
        , setSortState: function(obj) {
            
            if(!obj) {
                return;
            }
            
            Gallery.state.setSortBy(obj.sortBy);
            Gallery.state.setSortOrder(obj.sortOrder);
            
        }
		
		/** Sort the list by a specific criterion
		 */
		, refreshDataAndList: function() {
			
			var sortBy = Gallery.state.getSortBy();
      		var sortOrder = Gallery.state.getSortOrder();
           
            var options = {
                "orderby": sortBy,
                "order": sortOrder,
                "limit": Gallery.rList.defaultPageSize + 1,
                "success": function(resources) {
                    var cmdObj = {"cmd":"refreshList",
                                    "source":"sortButtonClick"
                                    };
                    Gallery.handleCommand(cmdObj);
                }
            }
            
            
            var tag = Gallery.state.getSelectedTag();
            var url = Gallery.state.getUrlForBits();

            DataModel.refreshData(url, tag, options);

		}
    	
    	// This is the main command handler for the list
    	/* Command:
    	 *          cmd     : name of the command
    	 *          cmdArgs : arguments for the command
    	 *          source  : who fired it
    	 *
    	 */
    	, handleCommand: function( command ) {
    	    
    	    if(!command || !command.cmd) {
    	        return;
    	    }
    	    
    	    var retVal = false;
    	    
    	    switch(command.cmd) {
    	        
    	        case "contextChange":
    	                        // Refresh the displayed list
    	                        this.refreshDisplay();
    	                        break;

    	        case "refreshList":
    	                        this.refreshDisplay(null, command.cmdArgs);
    	                        retVal = true;
    	                        break;
    	                        
    	        case "appendToList":
    	                        this.fetchNewBitsAndAppend(command.cmdArgs);
    	                        // nobody else needs to handle this event (for now)
    	                        retVal = true;
    	                        break;
    	                        
    	        case "highlightItem":
    	                        var urlToHighlight = command.cmdArgs;
    	                        this.highlightItem(urlToHighlight);
    	                        retVal = true;
    	                        break;
				
				case "sortBy":
				                this.setSortState(command.cmdArgs);
				                this.refreshDataAndList();
				                break;
				
				case "refreshDataAndList":
								this.refreshDataAndList();
								break;
    	                        
    	        default:
    	                        break;
    	    }
    	    
    	    return retVal;
    	}
    	
    }
    
    // the object for the navigation list
    , navList: {
        
        init: function(context) {
            // initialize and display the navigation List
    		Gallery.navList.render(DataModel.getTagsAndCounts());
            
            var cmdObj = { "cmd":"contextChange",
                            "cmdArgs":context,
                            "source":"navListInit"
                            };
            this.handleCommand(cmdObj);
        }
        
        // the function to render the list
        , render: function( list ) {
            
            list = list || DataModel.getTagsAndCounts();
	
			if(!list) {
				alert("Can't get tag list");
				return;
			}
	
			var selectedTag = Gallery.state.getSelectedTag();

			jQuery(".menubit-list").empty();
			
			for( var i = 0; i < list.length; i++) {

				var listItemHtml = "";						
				var name = list[i].tagname;
				var count = list[i].count;
				var tagtitle = list[i].tagtitle;
				var selected = (name == selectedTag) ? Gallery.navList.navSelectedClass : "";
								
				listItemHtml = Gallery.navList.tagItemTemplate.replace(/<!--tagtitle-->/g, tagtitle).replace(/<!--tagname-->/g, name).replace(/<!--count-->/g, count).replace(/<!--isSelected-->/g, selected);
				
				jQuery(".menubit-list").append( listItemHtml);	
			}
			
            // jQuery(".moreBits").attr("pages", 1).show();
			
        }
    	
    	, tagItemTemplate: "<li class='<!--isSelected-->'><a href='#url=/&tag=<!--tagname-->' class='menubit-item' tagname='<!--tagname-->'><span class='menubit-item-name'><!--tagtitle--></span><span class='menubit-item-count'> (<!--count-->)</span></a></li>"

    	// the tags shown along with the resource
    	, tagnameTemplate: "<a tagname='<!--tagname-->' href='#url=/&tag=<!--tagname-->'><!--tagname--></a>"

    	// Class name for a selected li
    	, navSelectedClass: "selected"

    	// Resource templates for different views
    	, listItemTemplates: {}
    	
    	// handle changing of tag filter
    	, tagFilterChange: function(tagname) {

    	    // unselect the previous tag item and select a new one
    		var cName = this.navSelectedClass;

    		jQuery(".menubit-list ." + cName).removeClass( cName );

    		// add selected class to nav item
    		jQuery(".menubit-list a[tagname='"+tagname+"']").parent("li:first").addClass(cName);
    	}
    	
    	// Main command handler for the navigation list
    	, handleCommand: function( command ) {
    	    
    	    if(!command || !command.cmd) {
    	        return;
    	    }
    	    
    	    switch(command.cmd) {
    	        
                case "contextChange" :
                
                                var ctxInfo = command.cmdArgs;
                                
                                if(ctxInfo.type == "global") {
                                    this.tagFilterChange(ctxInfo.info);
                                }
                                
                                if(ctxInfo.isOwner) {
                                    jQuery(".menubit-tags").hide();
                                    jQuery(".menubit-static").show();
                                } else {
                                    jQuery(".menubit-static").hide();
                                    jQuery(".menubit-tags").show();
                                }
                                
                                jQuery(".username").text(AXIS.User.username());
                                
                                break;

    	        default:        break;
    	    }
    	    
    	    // by default, don't cancel the command
    	    return false;
    	}
    	
    }
    
    //TODO : Add various list display type toggle later
    // object representing the toolbar
    , toolbar: {
        init: function() {
            Gallery.toolbar.attachToolbarHandlers();
            this.setSortUI();
        }
        
        /** Set the state of sort buttons on the toolbar
          * options : args: @options.sortBy: what is the UI sorted on
          *                  @options.sortOrder: if the sort is ascending or descending
          *    
          * Note: By default, the arg values are obtained from Gallery.state
          */
        , setSortUI: function(options) {

            var sortOrder = (options && options.sortOrder) || Gallery.state.getSortOrder();
            var sortBy = (options && options.sortBy) || Gallery.state.getSortBy();

            var button = jQuery("div[sortBy='" + sortBy + "']");
            var listitem = button.parent();

            jQuery(".sortListItem.selected").removeClass("selected ascending descending");
			
            listitem.addClass("selected");
			listitem.addClass(sortOrder);
        }
    
        , setViewState: function( newState ) {
            if(!newState) {
                return;
            }
            
            // change the view state of the button
        }
        
        , setViewToggleButtonState: function() {

            // var selectedClass = "viewToggleSelectedButton";
            // jQuery("." + selectedClass).removeClass("." + selectedClass);
            // jQuery("." + Gallery.state.getCurrentViewType()).addClass(selectedClass);
    	}
    	
    	, toggleListItemView: function( state ) {

            // if(!state) {
            //  state = Gallery.state.getCurrentResourceTemplate();
            // }
            // 
            // // update the UI button in the toolbar
            // var selectedClass = "viewToggleSelectedButton";
            // // find the selected button, unselect it, and select the button for the new state
            // jQuery("." + selectedClass).removeClass("." + selectedClass);
            // jQuery("." + state).addClass(selectedClass);
            // 
            // Gallery.state.viewChangeHandler( state );
    	}
    	
    	, attachToolbarHandlers: function() {
            // jQuery(".viewToggleButton").click( Gallery.toolbar.handleViewToggleButtonClick );
                jQuery(".sortButton").click( Gallery.toolbar.handleSortButtonClick );
                if(AXIS.User.isLoggedIn()) {
                    jQuery(".addBit").click(Gallery.toolbar.handleAddBitClick);                    
                } else {
                    jQuery(".addBit").attr("href", AXIS.Util.getSignInUrl());
                }
    	}

        /**
         * Add a new blank resource when the Add bit button is clicked
         *
         */
        , handleAddBitClick: function( event ) {
    	    
            event.preventDefault();
    	    
    	    function addSuccess(bitUrl) {
    	        
                var cmdObj = {"cmd":"sortBy",
                                "cmdArgs": {"sortBy": "lastmodified", "sortOrder":"descending"},
                                "source":"addBit"
                                };
                Gallery.handleCommand(cmdObj);
    	        
                // Gallery.handleCommand({"cmd":"refreshList", "source":"addBit"});
                // // why are we doing this contextChange here ?
                Gallery.handleCommand({"cmd":"contextChange", "cmdArgs": Gallery.state.getContext(), "source":"addBit"});
    	    }
    	    
    	    DataModel.addBlankResource(Gallery.blankBitUrl, {"success":addSuccess});
    	}
    	

    	/* handle the click on the view toggle button in the toolbar */
    	, handleViewToggleButtonClick: function( event ) {
            // var classes = jQuery(event.currentTarget).attr("class").split(" ");
            // var viewName = "";
            // for(var i = 0; i < classes.length; i++) {
            //  if(Gallery.navList.listItemTemplates[classes[i]]) {
            //      viewName = classes[i];
            //      break;
            //  }
            // }
            // // couldn't find any classnames in the listItem templates: error!
            // if(!viewName) {
            //  alert("cannot switch views. CSS class on button incorrect");
            //  return;
            // }
            // 
            // // found the name of the view to switch to: call the function to toggle the listItem view
            // Gallery.toolbar.toggleListItemView(viewName);

    	}

        , handleSortButtonClick: function( event ) {
            var button = jQuery(event.currentTarget);
            var listitem = button.parent();
            var sortBy = button.attr("sortBy");
            
            // if we're sorted by the current criterion, just toggle the sort order
            if (Gallery.state.getSortBy() == sortBy) {
                Gallery.state.toggleSortOrder();
            }
            else { // if a new sort criterion is selected, do the ui manipulation and set the sort order
                jQuery(".sortListItem.selected").removeClass("selected");
                listitem.addClass("selected");
                Gallery.state.setSortBy(jQuery(event.currentTarget).attr("sortBy"));
                if(jQuery(event.currentTarget).hasClass("ascending")) {
                    Gallery.state.setSortAscending();
                } else {
                    Gallery.state.setSortDescending();
                }

            }
            
            var sortClass = Gallery.state.isSortAscending() ? "ascending" : "descending";
            var removeClass = (sortClass == "ascending") ? "descending" : "ascending";
            listitem.addClass(sortClass);
            listitem.removeClass(removeClass);

            var cmdObj = {"cmd":"refreshDataAndList",
                          "source":"sortButtonClick"
                         };
            Gallery.handleCommand(cmdObj);
        }
        
        , handleCommand: function( command ) {

            if(!command || !command.cmd) {
                return false;
            }
            
            var contextInfo = command.cmdArgs;
            switch(command.cmd) {
                
                case "contextChange": 
                                if(contextInfo.isOwner) {
                                    jQuery(".addBit").show();
                                } else {
                                    jQuery(".addBit").hide();
                                }
                                break;
                
                case "sortBy": this.setSortUI(command.cmdArgs);
                                break;
                
                default:        break; 
            }
            
            return false;
        }
        
    }
    // Manage the state of the gallery: in particular, the view, the URL, the tag
    , state: {
        
        // Initialize the history function
    	init: function() {

    		var initialTagState = YAHOO.util.History.getBookmarkedState("tag") || "All";
            // var initialTagState = bookmarkedState || "All";
            var startLoc = AXIS.User.isLoggedIn() ? "/home/" + AXIS.User.username() + "/bits" : "/";
    		var initialUrlState = YAHOO.util.History.getBookmarkedState("url") || startLoc;
    		var loggedIn = AXIS.User.isLoggedIn();
    		
    		this.updateContext(initialUrlState, initialTagState, true);
    		
    		var initialViewState = YAHOO.util.History.getBookmarkedState("view") || "ecom";

    		YAHOO.util.History.register("tag", initialTagState, Gallery.state.tagChangeHandler);
    		YAHOO.util.History.register("url", initialUrlState, Gallery.state.urlChangeHandler);
            // YAHOO.util.History.register("view", initialViewState, Gallery.toolbar.toggleListItemView);

            // if(onReadyHandler) {
            //     YAHOO.util.History.onReady(onReadyHandler);
            // }

    		YAHOO.util.History.initialize("yui-history-field", "yui-history-iframe");

    		this.setCurrentViewType(initialViewState);
    		this.setUrlForBits(initialUrlState);
    		this.setSelectedTag(initialTagState);
    		this.setLoggedIn(loggedIn);

            return this.context;

    	}
    	
    	/**
    	 * Set the user context based on url and tag
    	 * make this generic later ?
    	 *
    	 * @args : dontFireEvent : don't fire an event (we're being init'ed)
    	 */
    	, updateContext: function( url, tag, dontFireEvent ) {

            var type, info, isOwner;

            if(!url) {
                // can't do anything without the URL
                url = this.getUrlForBits();
            }
            
            if(!tag) {
                tag = this.getSelectedTag();
            }
            
            dontFireEvent = !!dontFireEvent;
            
            // match URL first
            var urlMatch = url.match("\/home\/([^\/]+)(?:\/.*)?");
            
    	    if(urlMatch) {
                type = "user";
                info = urlMatch[1];
                isOwner = (urlMatch[1] === AXIS.User.username());
                Gallery.state.setSortBy("lastmodified");
                Gallery.state.setSortDescending();
    	    }
            else {
                type = "global";
                info = tag;
                isOwner = false;
                Gallery.state.setSortBy("popularity");
                Gallery.state.setSortDescending();
            }
            
            var loggedIn = AXIS.User.isLoggedIn();

            // make a shallow copy of the old context
            var oldContext = jQuery.extend({}, this.context);

    	    this.context.type = type;
    	    this.context.info = info;
    	    this.context.isOwner = isOwner;
    	    this.context.loggedIn = loggedIn;
            
            if(!dontFireEvent && ((info != oldContext.info) || (isOwner != oldContext.isOwner) 
    	        || (type != oldContext.type) || (loggedIn != oldContext.loggedIn))) {
    	            
    	        var cmdObj = { "cmd":"contextChange",
    	                        "cmdArgs": this.context,
    	                        "source":"urlChange"
    	                    };
    	        
    	        Gallery.handleCommand(cmdObj);
    	    }

            return ((info != oldContext.info) || (isOwner != oldContext.isOwner) 
                    || (type != oldContext.type) || (loggedIn != oldContext.loggedIn));
    	}
    	
    	// return the current context
    	, getContext: function() {
    	    return this.context;
    	}
        
        , userState: null

        /** A function with name 'getUserState' shouldn't have any side-effects .. bad, bad, BAD! */
        , getUserState: function() {

            if (this.userState) {
                return this.userState;
            }

            var user = AXIS.Cookies.read('user');

            if (!user || user == "unauthenticated") {
                this.userState = "old"
                return this.userState;
            }

            var r = AXIS.WebDAV.getProperty({
                url: '/home/' + user,
                properties: [{ns: AXIS.WebDAV.ns.lb, name: "state"}],
                asynch: false
            });

            try {
                this.userState = r.responseXMLObject().multistatus.response.propstat.prop.state || "old"; 
            }
            catch(e) {
                this.userState = "old";
            }

            return this.userState;

        }

        , resetUserState: function() {
            var user = AXIS.Cookies.read('user');

            if (!user || user == "unauthenticated") {
                return;
            }
            
            AXIS.WebDAV.removeProperty({
                url: '/home/' + user,
                removeProperties: [{ ns: AXIS.WebDAV.ns.lb, name: "state" }]
            });
        }


/*  not needed for now
        , goToState: function(name, value) {
            YAHOO.util.History.navigate(name, value);
        }
*/
        
        // The application context
        , context : {}
        
        // Tag the current gallery is filtered on
    	, selectedTag: ""

    	// URL where bits are fetched from
    	, urlForBits: ""

    	// Type of view currently used to render resources
    	, currentViewType: "ecom"

        // current sort parameter
        , sortBy: "popularity"
        
        // ascending: 1, descending: -1
        , sortOrder: "ascending"
        
        // see if the user is logged in (used for context change)
        , loggedIn: false
        
        // getter/ setter for 'sortBy'
        , getSortBy: function() {
            return this.sortBy;
        }
        
        , setSortBy: function(newSortBy) {
            if(newSortBy) {
                this.sortBy = newSortBy;
            }
        }
        
        // setter for loggedIn
        , setLoggedIn: function(loggedIn) {
            this.loggedIn = loggedIn || false;
        }
        
        // getter for loggedIn
        , getLoggedIn: function() {
            return this.loggedIn;
        }
        
        // Toggle the sort order for the list
        , toggleSortOrder: function() {
            this.sortOrder = (this.sortOrder == "ascending") ? "descending" : "ascending";
        }
        
        // get the current sort order
        , getSortOrder: function() {
            return this.sortOrder;
        }
        
        // check if the sort is currently ascending
        , isSortAscending: function() {
            return this.sortOrder == "ascending";
        }
        
        // check if the sort is currently descending
        , isSortDescending: function() {
            return this.sortOrder == "descending";
        }
        
        , setSortOrder: function(order) {
            this.sortOrder = order;
        }
        
        // set sort as ascending
        , setSortAscending: function() {
            this.setSortOrder("ascending");
        }
        // set sort as descending
        , setSortDescending: function() {
            this.setSortOrder("descending");
        }
        
    	// return the tag the gallery is currently filtered on
    	, getSelectedTag: function() {
    		return this.selectedTag;
    	}
    	
    	// set the tag the gallery is currently filtered on
    	, setSelectedTag: function( tagname ) {
    		this.selectedTag = tagname;
    	}

    	// get the currently active resource template
    	, getCurrentResourceTemplate: function() {
    		var curView = this.getCurrentViewType();
    		var rTemplate = DataModel.getListItemTemplate(curView);

    		return rTemplate;
    	}

    	, getCurrentViewType: function() {
    		return this.currentViewType;
    	}

    	, setCurrentViewType: function( viewType ) {
    		this.currentViewType = viewType;
    	}

    	// set the url where bits are being fetched from
    	, setUrlForBits: function( url ) {
    		this.urlForBits = url;
    	}

    	// return the URL where the bits are being fetched from
    	, getUrlForBits: function() {
    		return this.urlForBits;
    	}
    	
    	// get URL for fetching resource and tag templates
    	, getTemplateUrl: function() {
    	    return "configuration/templates/templates.json";
    	}

    	// the history change handler
    	// state is the name 
    	, tagChangeHandler: function(tagname) {
         
            var self = Gallery.state;
            if(tagname == self.getSelectedTag()) {
                return;
            }
        
            self.setSelectedTag(tagname);
        
            var context_has_changed = self.updateContext(null, tagname, true);
            var cmdObj = {
                        cmd: "tagFilter",
                        cmdArgs: tagname,
                        source: "url"
            };
            Gallery.handleCommand(cmdObj);

            if (context_has_changed) {
    	        cmdObj = { "cmd":"contextChange",
    	                        "cmdArgs": Gallery.state.getContext(),
    	                        "source":"tagChange"
    	                    };
    	        
    	        Gallery.handleCommand(cmdObj);
            }

        }

    	// fetch bits from a different location
    	, urlChangeHandler: function( newUrl ) {
                
            var self = Gallery.state;
            if(newUrl == self.getUrlForBits()) {
                return;
            }
            
            self.setUrlForBits(newUrl);
            
            var context_has_changed = self.updateContext(newUrl, self.getSelectedTag(), true);
            var cmdObj = {
                        cmd: "urlFilter",
                        cmdArgs: newUrl,
                        source: "url"
            };
            Gallery.handleCommand(cmdObj);

            if (context_has_changed) {
    	        cmdObj = { "cmd":"contextChange",
    	                        "cmdArgs": Gallery.state.getContext(),
    	                        "source":"urlChange"
    	                    };
    	        
    	        Gallery.handleCommand(cmdObj);
            }

    	}

    	// re-render current bits using a new resource template
    	, viewChangeHandler: function( state ) {

    		Gallery.state.setCurrentViewType( state );
    		Gallery.rList.refreshResourceDisplay();
    	}
        
    }

    /* Clear the config cache when refreshing display/data */
    , clearConfigCache: function() {
        this.configCache = {};
    }
    
    /*
     * Initialize the gallery
     */
    , init: function(initInfo) {
        
        this.pagebar = window.limebar;
        
        jQuery(".username").text(AXIS.User.username());
        
        // Initialize the state before initializing other modules
        var contextInfo = this.state.init();
        
        var selectedTag = this.state.getSelectedTag();
        var selectedUrl = this.state.getUrlForBits();
        var templateUrl = this.state.getTemplateUrl();
        
        var dataModelOptions = {
            // fetch 1 more than required
            "limit" : Gallery.rList.defaultPageSize + 1,
            "order": this.state.getSortOrder(),
            "orderby": this.state.getSortBy()
        };
        DataModel.init(selectedUrl, selectedTag, templateUrl, dataModelOptions);
        // this.updateSelectedTagDescription();
        
        this.toolbar.init();
        this.navList.init(contextInfo);
        this.rList.init();
        
        this.chrome.init(contextInfo);
        
        AXIS.Login.onAuthUpdate.subscribe({
            callback: function() {
                // update the context and fire the updateContext event 
                Gallery.state.updateContext(null, null);
            }
        });
        
        window.galleryLoaded = true;
    }

};
