(function($)
{	
	// constructor
	function TopNavMenu(root, conf)
	{	
		// Private fields ------------------------------------------------------------------
	
		var _root = $(root),
			_domElement = _root[0],
			_self = this,
			_hotspot = _root.parent(),
			_menu,
			_hideTimer,
			_showWait = 150,
			_hideWait = 250,
			_animDur = 150,
			_menuHeight,
			_positionLeft = "default", //hotspot-left 
			_positionTop = "default", //hotspot-top 
			_opts = {
				debug: false
			}
			;
			$.extend(_opts, conf);
	
		// Public methods ------------------------------------------------------------------
		
		$.extend(_self, {
			toggleMenu: function()
			{
				if (_root.hasClass("on")) {
					_self.debug("calling closeMenu");
					_self.closeMenu();
				}
				else {
					_self.debug("calling doMouseEnter");
					_self.doMouseEnter();
				}
			},
			positionMenu: function()
			{
				if (_menu.hasClass("body-flyout") && _menu.parent()[0].tagName != "BODY") {
					_self.debug("moving menu element to end of document body");
					$("body").append(_menu); //move in-body flyout to end of body (if it's not there already) to avoid messy cascading styles
				}
				
				if (_positionLeft != "default") {
					var targetX;
					switch (_positionLeft) {
						case "hotspot-left":{
							targetX = _hotspot.offset().left;
							break;
						}
						case "hotspot-right":{
							targetY = _hotspot.offset().left + _hotspot.outerWidth();
							break;
						}
					}
					_self.debug("setting left position: " + targetX);
					_menu.css({
						left: targetX
					});
				} else {
					_self.debug("using default _positionLeft");
				}
				if (_positionTop != "default") {
					var targetY;
					switch (_positionTop) {
						case "hotspot-top":{
							targetY = _hotspot.offset().top;
							break;
						}
						case "hotspot-bottom":{
							targetY = _hotspot.offset().top + _hotspot.outerHeight();
							break;
						}
					}
					_self.debug("setting top position: " + targetY);
					_menu.css({
						top: targetY
					});
				} else {
					_self.debug("using default _positionTop");
				}
			},
			openMenu: function()
			{
				if (_root.hasClass("on") == false) {
					_self.debug("openMenu");
					_self.positionMenu();
					_menu.show();
					_menu.animate({height:_menuHeight}, _animDur, "swing", function() { _self.onOpen(); });
				}
			},
			onOpen: function()
			{
				_self.debug("onOpen");
				_root.addClass("on");
			},
			closeMenu: function()
			{
				_self.debug("closeMenu");
				_menu.animate({height:"0px"}, _animDur, "swing", function() { _self.onClose(); });
			},
			onClose: function()
			{
				_self.debug("onClose");
				_menu.hide();
				_root.removeClass("on");
			},
			doMouseEnter: function()
			{
				_self.debug("doMouseEnter");
				clearTimeout(_hideTimer);
				_hideTimer = setTimeout(function(){
					_self.openMenu();
				}, _showWait);
			},
			doMouseLeave: function()
			{
				_self.debug("doMouseLeave");
				clearTimeout(_hideTimer);
				_hideTimer = setTimeout(function(){
					_self.closeMenu();
				}, _hideWait);
			},
			debug: function(str)
			{
				if (_opts.debug == true) {
					try {
						trace(str);
					} 
					catch (e) {
						alert("str: " + str);
					}
				}
			}
		});
	
		// Private methods -----------------------------------------------------------------
		
		// generic binding function
		function bind(name, fn)
		{
			if ($.isFunction(fn) == false)
				return;
				
			$(_self).bind(name, fn);
		};
	
		function init()
		{	
			var hasMenuName = /\bmenu:\s*(\S+)\b/.test(_domElement.className);
			
			_self.debug("_domElement.className: " + _domElement.className);
			_self.debug("hasMenuName: " + hasMenuName);
			
			if (hasMenuName == true) {
				var menuElementName = /\bmenu:\s*(\S+)\b/.exec(_domElement.className)[1];
				_self.debug("menuElementName: " + menuElementName);
				_menu = $("#" + menuElementName);
				
				//by default, just expands the menu in-place; if over-ridden, it positions the menu relative to hotspot
				var hasPositionOverride = /\bposition:\s*(\S+)\b/.test(_domElement.className);
				if (hasPositionOverride == true) {
					var posString = /\bposition:\s*(\S+)\b/.exec(_domElement.className)[1];
					_self.debug("posString: " + posString);
					if (posString.indexOf("left") != -1) {
						_positionLeft = "hotspot-left";
					}
					if (posString.indexOf("top") != -1) {
						_positionTop = "hotspot-top";
					}
					if (posString.indexOf("bottom") != -1) {
						_positionTop = "hotspot-bottom";
					}
				}
			}
			
			if (_menu != undefined) {
				_menuHeight = _menu.height();
				_menu.height("0px");
				
				_hotspot.click(function(){
					_self.toggleMenu();
				});
				_hotspot.mouseenter(function(){
					_self.doMouseEnter();
				});
				_hotspot.mouseleave(function(){
					_self.doMouseLeave();
				});
				_menu.mouseenter(function(){
					_self.doMouseEnter();
				});
				_menu.mouseleave(function(){
					_self.doMouseLeave();
				});
				_menu.find('a').click(function(){
					_self.doMouseLeave();	
				});
			}

		};
	
		// Initialization ------------------------------------------------------------------
		init();
	};

	// jQuery plugin implementation
	$.fn.topNavMenu = function(conf)
	{
		var opts = {};
		$.extend(opts, conf);
		
		this.each(function()
		{
			var $instance = new TopNavMenu($(this), opts);
		});
		return this; 
	};
})(jQuery);

