/**
 * @author Mark Cassar
 * @version 1.0.0 
 * @lastmodified 27/10/07
 */

com.cs.initClass("com.cs.ui");


//STATICS

/**
 * This will create a popup menu that will open to the left or right.
 * 
 * To use this class, you have to define a series of div tags with the menu structure.  The structure is as follows:
 * 
 * <div id="mainmenu">
	 * <div>Element 1</div>
	 * <div>Element 2</div>
	 * <div>	  		
	 * 		<div>Element with Submenu</div>
	 * 		<div>
	 * 			<div>Submenu element 1</div>
	 * 			<div>Submenu element 2</div>
	 * 		<div>
	 * 		
	 * </div>
	 * <div>Element 3</div>
	 * <div>Element 4</div>
 * </div>
 * 
 * Then pass the div as a parameter to the com.cs.ui.PopUpMenu class
 * 
 * EVENTS:
 * -------
 * 
 * You will have 2 events with this popup menu.  onMenuOpen will trigger whenever a new menu is opened.  It will contain the div of the opened
 * menu as a parameter.
 * 
 * The other event is onMenuClose which is triggered whenver the menu is closed.  The div element will be
 * hidden with property div.style.display = 'none'
 * 
 * @param {Object} div div of main menu
 * @param {Object} openLeft Wheter to open left or right. Boolean.
 * @param {Object} closeTimeout Amount in ms to close popup menu after mouse has rolled out of the menu
 */
com.cs.ui.PopUpMenu = function(div, closeTimeout, openLeft) {
    /**
    * PUBLIC PROPERTIES*/
    this.openLeft = openLeft;
    this.closeTimeout = closeTimeout || 50;

    /** PUBLIC EVENTS */
    this.onMenuOpen = function(div) { };
    this.onMenuClose = function() { };

    if (!div) {
        alert("ERROR: com.cs.ui.PopupMenu > Please pass root div element for menu.");
        return;
    }
    if (div.tagName.toLowerCase() != "div") {
        alert("ERROR: com.cs.ui.PopupMenu > Root node must be a div element");
    }



    this._callOutTimeoutID;
    this._callOut = false;
    this._rootMenuLevel = null;
    var scope = this;

    this.mouseOverDiv = function(menuItem) {
        clearTimeout(scope._callOutTimeoutID);
        menuItem.menuLevel.hideSubMenu(); // hide any previous opened submenus of this level
        if (menuItem.subMenu) {
            menuItem.subMenu.show();
        }
    }
    /**
    * This function is called whenever mouse is moved out of the menu
    * @param {Object} menuItem
    */
    this.mouseOutDiv = function(menuItem) {
        //This timeout is there since when a div calls an onout event, if the mouse is on another div, the other div will call the on in event
        //In that case, we don't want the on out to clear the menu.  The timeout makes sure that the mouse didnt stop on another div in the menu
        clearTimeout(scope._callOutTimeoutID);
        scope._callOutTimeoutID = setTimeout(scope.triggerOut, scope.closeTimeout);
    }
    this.triggerOut = function() {
        scope._rootMenuLevel.hideSubMenu();
    }
    this.init = function() {
        var prevDisplay = div.style.display;
        scope._rootMenuLevel = new com.cs.ui.PopUpMenu.MenuLevel(div, null, null, this);
        div.style.display = prevDisplay;
    }
    this.init();


}
/**
 * This will check whether a particular div contains a sub menu or not.
 * @param {Object} div Object with two properties divTitle and divSubMenu.
 */
com.cs.ui.PopUpMenu.getDivs = function(div) {

	var obj = new Object();
	
	for (var i=0;i<div.childNodes.length;i++)
	{
		var node = div.childNodes[i];
	
		if (node.tagName && node.tagName.toLowerCase() == "div") {
			if (!obj.divTitle) {
				obj.divTitle = node;
			}
			else {
				obj.divSubMenu = node;
				break;
			}
		}
	}
	
	if (!obj.divTitle && !obj.divSubMenu) {
		return null;
	}
	else {
		if (!obj.divTitle || !obj.divSubMenu) {
			var errMsg = "Error: com.cs.ui.PopUpMenu\n\n";
			errMsg += "There is an error in the structure of one of the submenus.\n";
			errMsg += "You need to have the following structure for a submenu:\n";
			errMsg += "<div>\n\t<div>\n\t\tTITLE HERE\n\t</div>\n\t<div {MAIN MENU DIV}>\n\t</div>\n<div>";
			errMsg += "-----------------------------\n";
			errMsg += "Error found in the following div HTML code:\n";
			errMsg += divItem.innerHTML;
			alert(errMsg);
		}
		else {
			return obj;
		}
	}	
}

/**
 * This will create a level in the popup menu
 * @param {Object} divLevel The div element of the level which contains the items
 * @param {Object} parentLevel Parent level of this level
 * @param {Object} openOnMenuItem The item which will open this menu
 * @param {Object} popUpMenu The popup menu reference
 */
com.cs.ui.PopUpMenu.MenuLevel = function(divLevel, parentLevel, openOnMenuItem, popUpMenu) {
	this._openOnMenuItem = openOnMenuItem;
	this._parentLevel = parentLevel;
	this._popUpMenu = popUpMenu;
	this._menuItems = new Array();

	this._div = divLevel;
	this._divs = new Array();
	
	// PUBLIC PROPERTIES
	
	this.div = this._div;
	this.menuItems = this._menuItems;
	
	
	var scope = this;
	//This is so that submenus will not occupy space (width) within this level
	this._makeSubMenusAbsolute = function () {
		for (var i=0;i<scope._div.childNodes.length;i++) { 
			var node = scope._div.childNodes[i];
			if (node.tagName && node.tagName.toLowerCase() == "div") {
				var divItem = node;
				scope._divs.push(divItem);

				var objInnerDivs = com.cs.ui.PopUpMenu.getDivs(divItem);
				if (objInnerDivs) {
					// this div has a submen
					divItem.style.position = "relative";
					objInnerDivs.divSubMenu.style.position = "absolute";
				}
				
				
			}
		}
	}
	
	this._parseChildDivs = function() {
		for (var i=0;i<scope._divs.length;i++) {
			var div = scope._divs[i];
			scope._menuItems.push(new com.cs.ui.PopUpMenu.MenuItem(div,this,popUpMenu));		
		}
	}
	
	this.hideSubMenu = function() { // this will hide the opened child level
		if (scope.currOpenedLevel) {
			scope.currOpenedLevel.hide();
			scope.currOpenedLevel = null;
		}
	}
	this.show = function() {	
		scope._openOnMenuItem.positionSubMenu(); //Reposition the menu to the actual plaace
		scope._parentLevel.currOpenedLevel = scope;
		scope._div.style.display = "";
		if (scope._popUpMenu.onMenuOpen) {
			scope._popUpMenu.onMenuOpen(scope._div);
		}
	}
	this.hide = function() {
		scope.hideSubMenu(); // in case you have a submenu open
		scope._div.style.display = "none";
		if (scope._popUpMenu.onMenuClose) {
			scope._popUpMenu.onMenuClose(scope._div);
		}
	}
	
	this.init = function() {
		scope._makeSubMenusAbsolute();
		scope._parseChildDivs();
		scope.hide();
	}
	this.init();
	
	
}

/**
 * This is the item in the menu
 * @param {Object} divItem div element of item
 * @param {Object} menuLevel The level with which it is assigned to
 * @param {Object} popUpMenu The popup menu
 */
com.cs.ui.PopUpMenu.MenuItem = function(divItem, menuLevel, popUpMenu) {
	
	this._div = null;
	this._menuLevel = menuLevel;
	this._popUpMenu = popUpMenu;
	this._subMenu = null;
	
	var scope = this;
	
	this._initSubmenu = function() {
		divItem.style.position = "relative";
		scope.positionSubMenu();
		
	}	
	this.positionSubMenu = function() {
		if (scope._subMenu) {
			var div = scope._subMenu.div;
			var x = divItem.offsetWidth;

			if (scope._popUpMenu.openLeft) x = -x;
			div.style.left = x + "px";
			div.style.top = "0px";
		}
	}
	
	this._initHandlers = function() {
		
		scope._div.onmouseover = function () {			
			scope._popUpMenu.mouseOverDiv(scope);
		}
		scope._div.onmouseout = function() {
			scope._popUpMenu.mouseOutDiv(scope);
		}
	}
	
	this.init = function() {
		var objDivs = com.cs.ui.PopUpMenu.getDivs(divItem);
		if (!objDivs) {		
			// no sub menu
			scope._div = divItem;
		}
		else {				
			scope._div = objDivs.divTitle;
			scope._subMenu = new com.cs.ui.PopUpMenu.MenuLevel(objDivs.divSubMenu, menuLevel, scope, popUpMenu);
			scope._initSubmenu();		
		}
		scope._initHandlers();
	}
	this.init();
	
	/* PUBLIC PROPERTIES */
	this.subMenu = this._subMenu;
	this.menuLevel = this._menuLevel;
}
