/**
 * Menu jQuery Plugin - To Popup A Beautiful Menu
 * Copyright (c) 2009 eSobi
 * Author: David Hung
 * Version: 1.03
 * Last Revision: 2009-12-31
 *
 * This jQuery plugin has been tested in the following browsers:
 * - IE 7, 8
 * - Firefox 3.5
 * - Opera 9.64
 * - Safari 4.0
 * - Chrome 2.0
 * It will only show alert message in Safari, Chrome and Opera
 *
 * Required jQuery Libraries:
 * jquery.js         (v1.3.2)
 * jquery-ui.js      (v1.7.2)
 * simplemodalDialog (v1.3)
 * jGrowl.js         (v1.2.0)
 * jquery.cookie.js
 * jquery.center.js  (v2.0)
 *
 * Version 1.03 change log:
 * - Fix checking height bug
 * - Add "checking" flag to avoid IE send double onresize event
 *
 * Version 1.02 change log:
 * - Fix no checking height while window resize bug
 *
 * Version 1.01 change log:
 * - Avoid same caller panel to be inserted duplicated html
 * - Add alert warning while title or url is not provided correctly
 */
$.fn.menu = function(options){
	var caller = $(this);
	return $.Menu.getInstance(caller, options);
};

// Creating Module
$.Menu = (function()  {
	// static private methods
	// static constants
	var VERSION = "1.02";           // the version of current plugin

	// static variables
	var menuIndex = 0;            // index to create window instance id

	// get browser available screen width
	function getScreenWidth() {
		return document.documentElement.clientWidth;
	}

	// get browser available screen height
	function getScreenHeight() {
		return document.documentElement.clientHeight;
	}

	function constructor(caller, options) {
		// instance private methods
		var menuId = "menu_" + (menuIndex++); // the menu's id
		var menuPanel = null;         // jquery <ul> object of menu
		var subMenuPanel = null;      // jquery <ul> object of submenu
		var menuOpen = false;
		var ignoreClose = false
		var orgMenuWidth = -1;        // to save original menu panel width
		var orgMenuHeight = -1;       // to save original menu panel height
		var openSubMenu = false;
		var enterMenuRegion = false;
		var checking = false;         // to avoid IE send double onresize event

		var options = jQuery.extend({
			content: null,              // [string, element, jquery object] the html content, if this got value, list attribute won't work
			width: -1,                  // [number] unit: pixel
			maxHeight: -1,              // [number] -1 = infinite height. if got a postive value, it will show scroll bar when height exceed.
			showSpeed: 400,             // [number] menu show speed, in milliseconds
			closeSpeed: 200,            // [number] menu close speed, in milliseconds
			subMenuDelay: 500,          // [number] sub menu trigger open interval, in milliseconds
			onItemChosen: null,         // [function] a callback function while menu item is chosen
			onShow: null,               // [function] a callback function while menu popup
			onClose: null,              // [function] a callback fucntion while menu closed
			doCheckHeight: true,        // [boolean] to decide do checking height routine
			hoverTriggerPopup: false,   // [booelan] to decide popup menu while mouse over
			hoverTriggerTime: 200,      // [number] delay time of popup menu while mouse over trigger button, in milliseconds
			itemHoverClass: null,       // [string] the class name to add while mouse over item
			itemSwapContentClass: null, // [string] the class name to add while mouse over item
			positionOffset: {x:0, y:0}, // [json object] popup menu position offset
			showLog: false              // [boolean] to decide show log in firebug console or not
		}, options);

		function initialize() {
			// construct button
			caller.addClass('menu_button');

			bindButtonEvent();
			if( options.content != null ) {
				menuPanel = $("#"+menuId);
				if( menuPanel.get(0) == null ) {
					$("body").append("<ul id='"+menuId+"' class='menu_popup_menu menu_panel ui-corner-all ui-widget ui-widget-content' style='display:none;'></ul>");
					menuPanel = $("#"+menuId);

					// listen hover event
					menuPanel.hover(
						function() {
							enterMenuRegion = true;
						},
						function() {
							enterMenuRegion = false;
							if( options.hoverTriggerPopup ) {
								mouseoutTriggerClose();
							}
						}
					);
				}
				// copy html content from selected item inner ul element
				var content = null;
				if( typeof options.content == 'object' ) {
					content = $(options.content).clone();
				} else if( typeof options.content == 'string' ) {
					content = options.content;
				}
				menuPanel.append(content);
				menuPanel.children().show();
				initMenuPanel();
			}
		}

		function log(msg) {
			if(options != null && options.showLog && window.console != null) {
				console.log(msg);
			}
		}

		function bindButtonEvent() {
			caller.mousedown(function() {
				if( menuOpen == false ) {
					showMenu();
				} else {
					closeMenu();
				}
				if( options.callback != null && $.isFunction(callback) ) {
					options.callback(menuOpen);
				}
			});

			if( options.hoverTriggerPopup ) {
				caller.hover(
					function() {
						enterMenuRegion = true;
						if( options.hoverTriggerTime > 0 ) {
							window.setTimeout(function() { // popup menu delay a while
								if( enterMenuRegion ) {
									showMenu();
								}
							}, options.hoverTriggerTime);
						} else { // popup menu immedately
							showMenu();
						}
					}, function() {
						enterMenuRegion = false;
						mouseoutTriggerClose();
					}
				);
			}
		}

		function mouseoutTriggerClose() {
			window.setTimeout(function() {
				if( enterMenuRegion == false ) {
					closeMenu();
				}
			}, 500);
		}

		function bindItemEvent(item) {
			item.hover(
				function() {
					var item = $(this);
					if( options.itemHoverClass || options.itemSwapContentClass ) {
						item.addClass(options.itemHoverClass);
						if( options.itemSwapContentClass != null ) {
							item.children("*").hide();
							item.children("."+options.itemSwapContentClass).show();
						}
					} else {
						item.addClass("ui-state-hover ui-state-default ui-corner-all");
						item.css({
							border: 0
						});
					}

					if( item.children("ul").get(0) != null ) {
						openSubMenu = true;
						setTimeout(function() {
							if( openSubMenu ) {
								showSubMenu(item);
							}
						}, options.subMenuDelay);
					} else if( subMenuPanel != null && item.parent().get(0) != subMenuPanel.get(0) ) {
						closeSubMenu(true);
					}
				}, function() {
					openSubMenu = false;
					var item = $(this);
					if( options.itemHoverClass || options.itemSwapContentClass ) {
						item.removeClass(options.itemHoverClass);
						if( options.itemSwapContentClass != null ) {
							item.children("*").show();
							item.children("."+options.itemSwapContentClass).hide();
						}
					} else {
						item.removeClass("ui-state-hover ui-state-default ui-corner-all");
					}
				}
			);

			// bind click event
			item.click(function() {
				// show submenu
				if( item.children("ul").get(0) != null ) {
					showSubMenu(item);
				}

				if( options.onItemChosen != null && $.isFunction(options.onItemChosen) ) {
					options.onItemChosen(this, $(this).text());
				}
			});
		}

		function showSubMenu(item) {
			var innerMenuContent = item.children("ul");
			if( innerMenuContent.get(0) != null ) {
				subMenuPanel = $("#menu_popup_sub_menu");
				if( subMenuPanel.get(0) == null ) {
					$("body").append("<ul id='menu_popup_sub_menu' class='menu_panel ui-corner-all ui-widget ui-widget-content'></ul>");
					subMenuPanel = $("#menu_popup_sub_menu");
				}
				// copy html content from selected item inner ul element
				subMenuPanel.html(innerMenuContent.html());

				// init menu items
				initMenuItems(subMenuPanel);

				// get menu item position
				var pos = item.offset();
				subMenuPanel.css({
					left: pos.left + orgMenuWidth + 6,
					top: pos.top - 5,
					width: options.width
				});
				subMenuPanel.show(options.showSpeed);
			}
		}

		function closeSubMenu(bImmediately) {
			if( subMenuPanel != null ) {
				log( "[closeSubMenu] "+bImmediately );
				if( bImmediately ) {
					subMenuPanel.hide();
				} else {
					subMenuPanel.hide(options.closeSpeed, function() {
					});
				}
			}
		}

		function showMenu(force) {
			if( menuOpen == false ) {
				log( "==========[showMenu]=========" );
				log( menuPanel );
				if( menuPanel != null ) {
					menuPanel.slideDown(options.showSpeed);
				}
				menuOpen = true;
				if( options.onShow != null && $.isFunction(options.onShow) ) {
					options.onShow();
				}
			}
		}

		function closeMenu(bImmediately) {
			if( menuOpen ){
				log( "==========[closeMenu]=========" );
				if( ignoreClose && bImmediately == false) {
					ignoreClose = false;
				} else {
					if( menuPanel != null ) {
						if( bImmediately ) {
							menuPanel.hide();
						} else {
							menuPanel.slideUp(options.closeSpeed);
						}
					}
					closeSubMenu(bImmediately);
					menuOpen = false;
					if( options.onClose != null && $.isFunction(options.onClose) ) {
						options.onClose();
					}
				}
			}
		}

		function resetHeight() {
			menuPanel.height(orgMenuHeight);
			menuPanel.removeClass("menu_scroll");
		}

		function checkHeight() {
			log("====================[checkHeight]=====================");
			var menuHeight = menuPanel.outerHeight();
			if( options.doCheckHeight ) {
				var ctop = caller.offset().top;
				var cheight = caller.height();
				var screenHeight = getScreenHeight();
				if( screenHeight == null || screenHeight <= 0 ) {
					screenHeight = document.body.clientHeight;
				}
				log("menu panel height: "+menuHeight);
				log("screen height: "+screenHeight);
				log("caller position top/height: "+ctop+", "+cheight);
				if( (ctop+cheight) + menuHeight + 20 > screenHeight ) {
					log( "exteed window height, add scroll...");
					var adjHeight = screenHeight - (ctop+cheight) - 20;
					log( "adjusted height: "+adjHeight);
					menuPanel.height(adjHeight);
					menuPanel.addClass("menu_scroll");
					menuHeight = menuPanel.outerHeight();
					log( "adjusted menu panel height: "+menuHeight);
				}
			}

			if( options.maxHeight > 0 && menuHeight > options.maxHeight ) {
				log( "exteed maximum height, add scroll...");
				menuPanel.height(options.maxHeight+1);
				menuPanel.addClass("menu_scroll");
			}
		}

		function initMenuPanel() {
			log("=================[initMenuPanel]==================");
			resetMenuPanelPosition();

			// bind menu panel event
			menuPanel.mousedown(function() {
				ignoreClose = true;
			});

			// init menu items
			initMenuItems(menuPanel);

			$(window).load(function() {
				checkHeight();
			});

			$(window).resize(function() {
				if( checking == false ) {
					checking = true; // lock flag to avoid do checking routine repeatedly, for IE

					resetHeight();
					resetMenuPanelPosition();
					closeMenu(true);
					checkHeight();

					// unlock the flag
					window.setTimeout(function() {
						checking = false;
					}, 100)
				}
			});

			// remember original menu width/height
			orgMenuWidth = menuPanel.width();
			orgMenuHeight = menuPanel.height();
			log( "[initMenuPanel] orgMenuWidth/orgMenuHeight: "+orgMenuWidth+"/"+orgMenuHeight );
			checkHeight();
		}

		function resetMenuPanelPosition() {
			if( menuPanel != null ) {
				var pos = caller.offset();
				menuPanel.css({
					left: pos.left + options.positionOffset.x,
					top: pos.top + options.positionOffset.y + caller.height(),
					width: options.width
				});
			}
		}

		function initMenuItems(panel) {
			var items = panel.children("li");
			for( var i=0; i<items.length; i++ ) {
				var item = $(items[i]);
				item.addClass("menu_item");

				// bind menu item event
				bindItemEvent(item);

				// hide sub menu panel
				var innerMenuContent = item.children("ul");
				if( innerMenuContent.get(0) != null ) {
					innerMenuContent.hide();
					// add icon
					item.prepend("<span class='sub_menu_icon ui-icon ui-icon-triangle-1-e'></span>");
				}
			}
		}

		return { // Public members.
			initialize: initialize,
			getHtmlContent: function() {
				return caller.html();
			},
			clearHtmlContent: function() {
				caller.html("");
			},
			currentStatus: function() {
				return menuOpen;
			},
			showMenu: function() {
				showMenu(true);
			},
			closeMenu: function() {
				closeMenu(true);
			},
			insertMenuItem: function( obj ) {
				if( obj != null ) {
					menuPanel.append("<li id='"+obj.id+"' class='menu_item'>"+obj.text+"</li>");
					checkHeight();

					var item = menuPanel.children("li#"+obj.id);
					bindItemEvent(item); // bind event
				} else {
					log("WARNING!! insertMenuItem parameter is empty!");
				}
			},
			removeMenuItem: function(id) {
				if( id != null ) {
					var item = menuPanel.children("li#"+id);
					item.remove();
					checkHeight();
					log("remove menu item: "+id);
				} else {
					log("[removeMenuItem] WARNING!! cannot find id");
				}
			}
		};
	} // constructor end

	// Singleton control code
	return {
		getInstance: function(caller, options)	{
			var instance = constructor(caller, options);
			instance.initialize();
			return instance;
		},
		getVersion: function() {
			return VERSION;
		}
	}
})();

// alias methods
$.menu = $.Menu;