/**
 *
 * Base class for expanding menus
 * @class Base class for expanding menus
 * <p>Usage: var mainnav = new W.Menu('mainnav', mainnav_rollovers);</p>
 * @requires W
 * @requires W.Event
 * @requires W.Dom
 * @constructor
 * @param {String}	menu	for the div that contains the menu
 * @param {Object}	object	Preload object (optional)
 *
 *
 * FIXME: bottom of sub-menus gets cut off because of top style
 *
 *
 */

W.Menu = function(menu, rollover)
{
	this.menu		= menu;
	this.rollover	= false || rollover;
	this.rollOn		= false;
	var self		= this;
	
	this.onInit		= function(){};
	this.onOver		= function(){};
	this.onOpen		= function(){};
	this.onOut		= function(){};
	this.onClose	= function(){};

	W.Event.add(window, 'load', function() { self.init(); });
};



W.Menu.prototype = {

	menu 			: null,
	timers			: null,
	overDelay		: 20,
	outDelay		: 40,
	isMacIE			: null,
	container_index	: 0,		// used for generating a unique ID for each container div
	tweens			: null,
	duration		: 20,		// duration of opening/closing animation
	debug			: false,
	
	onInit			: null,
	onOpen			: null,
	onClose			: null,



	init : function() 
	{
		this.timers				= {};
		this.tweens				= {};
		this.tweens.opening		= {};
		this.tweens.closing		= {};
		this.isMacIE 			= (navigator.userAgent.indexOf('MSIE') != -1 && navigator.userAgent.indexOf('PowerPC') != -1);
		
		
		// delete after debugging
		if (this.debug && !window.console) {
			window.console	= {};
			window.console.log = function() {};
		}
		
		this.attachEvents();
		this.onInit();
	},
	
	
	
	attachEvents : function()
	{
		var uls			= W.$(this.menu).getElementsByTagName('ul');
		
		if (!uls.length) { return; }
		
		var lis			= uls[0].getElementsByTagName('li');
		var node		= null;
		var self		= this;
		
		for (var i = 0, max = lis.length; i < max; ++i) {
			node = lis[i];
			if (node.nodeName.toLowerCase() == 'li' && node.getElementsByTagName('ul').length > 0) {
				W.Event.add(node, 'mouseover', (function(node) { return function(e) { self.pre_open(e, node); }; } )(node), false);
				W.Event.add(node, 'click', (function(node) { return function(e) { self.pre_close(e, node); }; } )(node), false);
				
				this.create_container(node);
			}
		}
	},
	
	
	
	pre_open : function(e, targetElement)
	{
		var source	= W.Event.source(e);
		if (source.tagName.toUpperCase() == 'DIV') return;		// don't open menu if the mouse is over the containing div
		
		var el		= W.Event.target(e, targetElement);
		var self	= this;
		if (el && el.timer) { clearTimeout(el.timer); }
		
		this.onOver(e);
		
		if (this.rollover) { this.setRollover(el, true); }
		
		if (!el) { return; }
		
		if (this.isMacIE) {
			self.open(el);
		} else {
			el.timer = setTimeout(function() { self.open(el); }, this.overDelay);
		}
	},
	
	
	
	open : function(el)
	{
		var node	= this.find_container(el);		// container
		var close	= null;
		
		if (node) {
			var ul		= node.getElementsByTagName('ul')[0];
			var id		= node.id;
			
			// check if menu is not already opening
			if (!this.opening(id)) {
			
				// retrieve direction
				var attrib			= ul.attrib;
				
				// check if menu is already open
				if (parseInt(ul.style[attrib], 10) >= 0) { return; }
				if (this.debug) { console.log(id + ' is not open'); }
				
				// determine clipping region
				var height		= node.clip_height;
				var width		= node.clip_width;
				
				// determine start and end positions
				var start		= 0;
				var end			= 0;
				switch (attrib) {
					case 'top':
						start	= parseInt(ul.style.top, 10);
						end		= 0;
						break;
					case 'left':
						start	= parseInt(ul.style.left, 10);
						end		= 0;
						break;
					case 'right':
						start	= parseInt(ul.style.right, 10);
						end		= 0;
						break;
					case 'bottom':
						start	= parseInt(ul.style.bottom, 10);
						end		= 0;
						break;
				}
				
				if (this.debug) { console.log('open ' + id + ' starting at ' + start + ' and ending at ' + end); }
				
				// stop menu if it is already closing
				if ((close = this.tweens.closing[id]) !== undefined) {
					close.stop();
					this.closed([ul]);
					if (this.debug) { console.log('interrupt closing ' + id); }
				}
				
				// set styles
				node.style.visibility		= 'visible';
				node.style.clip				= 'rect(0, ' + width + 'px, ' + height + 'px, 0)';
				node.style.display			= 'block';		// opened menus are inline-block in IE to remove clipping
				
				// animate
				//this.tweens.opening[id]		= new W.Tween({start: start, end: end, duration: this.duration, obj:this, bind: 'animate', method: 'easeInOutQuad', callback: 'opened', args: [ul, attrib]});
			
				this.onOpen();
			}
		}
	},
	
	
	
	pre_close : function(e, targetElement)
	{
		var el		= W.Event.target(e, targetElement);
		var self	= this;
		

		if (!el) { return; }
		
		if (this.isMacIE) {
			self.close(el);
		} else {
			el.timer = setTimeout(function() { self.close(el); }, this.outDelay);
		}
	},
	
	
	
	close : function(el)
	{
		var node	= this.find_container(el);		// container
		var open	= null;
		
		if (node) {
			
			var ul		= node.getElementsByTagName('ul')[0];
			var id		= node.id;

			// retrieve direction
			var attrib			= ul.attrib;
			
			// make sure menu is not open
			if (parseInt(ul.style[attrib], 10) > 0) { return; }
			if (this.debug) { console.log(id + ' is open'); }
			
			// determine clipping region
			var height		= node.clip_height;
			var width		= node.clip_width;

			// determine start and end positions
			var start		= 0;
			var end			= 0;
			switch (attrib) {
				case 'top':
					start	= parseInt(ul.style.top, 10);
					end		= -height;
					break;
				case 'left':
					start	= parseInt(ul.style.left, 10);
					end		= -width;
					break;
				case 'right':
					start	= parseInt(ul.style.right, 10);
					end		= -width;
					break;
				case 'bottom':
					start	= parseInt(ul.style.bottom, 10);
					end		= -height;
			}
						
			if (this.debug) { console.log('close ' + id + ' starting at ' + start + ' and ending at ' + end); }
			
			// stop menu if it is already opening
			if ((open = this.tweens.opening[id]) !== undefined) {
				open.stop();
				this.opened([ul]);
				if (this.debug) { console.log('interrupt opening ' + id); }
			}
			
			// set styles
			node.style.clip				= 'rect(0, ' + width + 'px, ' + height + 'px, 0)';
			
			// animate
			//this.tweens.closing[id]		= new W.Tween({start: start, end: end, duration: this.duration, obj:this, bind: 'animate', method: 'easeInOutQuad', callback: 'closed', args: [ul, attrib]});
		
			this.onClose();
		}
		
		if (this.rollover) { this.setRollover(el, false); }
	},
	
	
	
	get_clip_height : function(elem)
	{
		if (this.debug) { console.log('clip: ' + elem.offsetHeight + ', ' + Math.abs(parseInt(W.Dom.getStyle(elem, 'top'), 10)) + ', ' + Math.abs(parseInt(W.Dom.getStyle(elem, 'bottom'), 10))); }

		var top		= Math.abs(parseInt(W.Dom.getStyle(elem, 'top'), 10));
		var bottom	= Math.abs(parseInt(W.Dom.getStyle(elem, 'bottom'), 10));
		var height	= elem.offsetHeight;
		
		if (!isNaN(top)) 	{ height += top; }
		if (!isNaN(bottom))	{ height += bottom; }
		
		return 	height;
	},
	
	
	
	get_clip_width : function(elem)
	{
		if (this.debug) { console.log('clip: ' + elem.offsetWidth + ', ' + Math.abs(parseInt(W.Dom.getStyle(elem, 'left'), 10)) + ', ' + Math.abs(parseInt(W.Dom.getStyle(elem, 'right'), 10))); }
		
		var left	= Math.abs(parseInt(W.Dom.getStyle(elem, 'left'), 10));
		var right	= Math.abs(parseInt(W.Dom.getStyle(elem, 'right'), 10));
		var width	= elem.offsetWidth;
		
		if (!isNaN(left)) 	{ width += left; }
		if (!isNaN(right))	{ width += right; }
		
		return 	width;
	},
	
	
	
	animate : function(num, args)
	{
		var ul				= args[0];
		var attrib			= args[1];
		
		ul.style[attrib]	= num + 'px';
		if (this.debug) { console.log('opening ' + ul.parentNode.id + ' ' + attrib + ' to ' + num); }
	},
	
	
	
	opening : function(id)
	{
		return this.tweens.opening[id];
	},
	
	
	
	closing : function(id)
	{
		return this.tweens.closing[id];
	},
	
	
	
	opened : function(args)
	{
		var ul		= args[0];
		var node	= ul.parentNode;
		
		// should be node.style.clip = 'auto' but Safari and IE have a problem with it
		if (document.all) {
			node.style.clip	= 'rect(auto, auto, auto, auto)';
			node.style.display = 'inline-block';				// IE6 will still apply clipping unline the display is changed
		 } else { 
		 	node.style.clip = null; 
		 }
		delete this.tweens.opening[node.id];
	},
	
	
	
	closed : function(args)
	{
		var ul		= args[0];
		
		ul.parentNode.style.visibility	= 'hidden';
		delete this.tweens.closing[ul.parentNode.id];
	},
	
	
	
	setRollover : function(el, state)
	{
		var img = el.getElementsByTagName('img')[0];	// el is the <li> that contains the rollover image, and the nested <ul> submenu
														// assume the first <img> in <li> is the rollover image
		if (img.name !== '') { this.rollover.rollover(img.name, state); }
	},
	
	
	find_container : function(el)
	{
		var node	= null;
		
		for (var i = 0, max = el.childNodes.length; i < max; ++i) {
			
			node = el.childNodes[i];
			
			if (node.nodeName.toLowerCase() == 'div' && node.className == 'container') { 
				return node;
			}
		}
	},
	
	
	
	create_container : function(node)
	{
		var ul				= node.getElementsByTagName('ul')[0];
		var width			= ul.offsetWidth + 'px';
		var height			= ul.offsetHeight + 'px';
		var left			= ul.offsetLeft;
		var top				= ul.offsetTop;
		var id				= this.menu + '_container' + (++this.container_index);
		
		var div				= document.createElement('div');
		div.className		= 'container';
		div.id				= id;
		
		ul					= node.replaceChild(div, ul);
		ul					= div.insertBefore(ul, null);
		
		if (this.debug) { console.log('create ' + id + ' top: ' + top + ', left: ' + left); }
		if (Math.abs(top) > Math.abs(left)) {
			if (top  > 0) {								// slide from top to bottom
				ul.style.top = '-' + height;
				ul.attrib		= 'top';
			} else {									// slide from bottom to top
				ul.style.bottom	= '-' + height;
				ul.attrib		= 'bottom';
			}
		} else {
			if (left > 0) {								// slide from left to right
				ul.style.left	= '-' + width;
				ul.attrib		= 'left';
			} else {									// slide from right to left
				ul.style.right 	= '-' + width;
				ul.attrib		= 'right';
			}
		}
		
		ul.style.visibility	= 'inherit';			// set to be invisible in the style sheet
		div.style.width		= width;
		div.style.height	= height;
		
		div.clip_width		= this.get_clip_width(ul);
		div.clip_height		= this.get_clip_height(ul);
	}
};