/**
 * @author Nathan Kelly - http://www.nathan-kelly.com/
 *  my email address is my first name @nathan-kelly.com
 * @copyright 2009 Nathan Kelly
 * @package nk-navigate
 * @version 1.3.0
 * @url http://projects.nathan-kelly.com/nk-navigate/
 * @dependencies 
 * 	prototype.js 1.6.0.2+ (http://www.prototypejs.org/)
 *	scriptaculous.js 1.8.1+ (http://script.aculo.us/)
 * @license
 *	Creative Commons Attribution 2.5 Australia License
 *	http://creativecommons.org/licenses/by/2.5/au/
 * @known Bugs
 *  IE 7 and lower seems to have trouble hiding nested ul's using the:
 *  $(root).select('ul').invoke('hide'); method on line 105, any ideas on a
 *  workaround for this would be appreciated if you are interested in helping to
 *  improve this script.
 */
 
var nk_navigate = Class.create({

	// global settings edit at will
	_rootClass:	'nk-navigate',
	_overClass:	'nk-nav-selected',
	_hasChildClass:	'nk-nav-haschild',
	_flippedClass:	'nk-nav-flipped',
	_openDelay:	7,
	_closeDelay:	3,
	_openTimeout: 	null,
	_closeTimeout: 	null,
	
	// don't edit below here
	initialize: function () {

		if ( !$$('.' + this._rootClass) ) { return; }

		var roots = $$('.' + this._rootClass), haschild = this._hasChildClass;
		
		for ( var i = 0; i < roots.length; i++ ) {

			var root = roots[i];

			root.select('ul').each( function (e) { !$($(e).parentNode).className.match(haschild) ? $($(e).parentNode).addClassName(haschild) : null; }).invoke('hide');
			
			root.observe( 'mouseover', ( function (e) {				
				
				var ti = Event.findElement(e, 'LI');

				if ( ti ) { Event.stop(e); this._navOver(ti, root); }

			}).bindAsEventListener(this));

			root.observe( 'mouseout', ( function (e) {
				
				this._navOut(root);

			}).bindAsEventListener(this));

		}

	},

	_navOver: function (ti, root) {
				
		clearTimeout( this._openTimeout );

		var tl = ti.childElements().find( function (e) { return e.nodeName.toUpperCase() == 'UL'; });
		
		var selected = $$('.' + this._overClass).findAll( function (s) { return !ti.descendantOf(s) && s != ti; });
			
		var that = this;

		this._closeTimeout = window.setTimeout( function () {			

			ti.addClassName(that._overClass);

			selected.each( function(e) { e.select('ul').invoke('hide'); });

			selected.invoke('removeClassName', that._overClass, that._flippedClass);

			if ( tl) {

				var pos = that.positionTargetUL(ti, tl, root);

				tl.setStyle({left:pos[0], top:pos[1]});

			}

		}, this._closeDelay * 100 );

	},

	_navOut: function (root) {

		clearTimeout( this._closeTimeout );
		
		var that = this;

		this._openTimeout = window.setTimeout( function () {
			
			// IE struggles with the following method, leaving ul's
			// that are nested below the root ul showing but their
			// child li's are correctly hidden?
			root.select('ul').invoke('hide');
			
			$$('.' + that._overClass).invoke('removeClassName', that._overClass, that._flippedClass);

		}, this._openDelay * 100 );

	},

	positionTargetUL: function (ti, tl, root) {

		// viewport dimensions
		var vd = document.viewport.getDimensions();

		// target item dimentions
		var td = $(ti).getDimensions();

		// target item position
		var tp = $(ti).positionedOffset();

		// initialize target child position
		$(tl).setStyle({left:tp.left + 'px', top:(tp.top + td.height) + 'px'});

		// display child so we can get the initial dimensions and position
		$(tl).setStyle({display:'block'});

		// get target child dimentions
		var cd = $(tl).getDimensions();

		// get initial child position
		var cp = $(tl).cumulativeOffset();

		// get target child absolute right offset
		var cpar = cp.left + cd.width;

		// get target child relative right offset
		var cprr = cp.left + td.width;

		// set final top position, same for all elements for now
		this.ft = tp.top + td.height;

		// check if this is a top level menu
		if ( $(ti).parentNode == $(root) ) {

			// if far right is outside viewport bring it back inside
			this.fl = cpar > vd.width ? cprr - cd.width : tp.left;

		} else {

			// if far right is outside viewport flip menu over to left of current menu
			this.fl = (cpar + cd.width) > vd.width ? tp.left - cd.width : tp.left + cd.width;

			// if we flip this ul we should attach a css class incase we need to make visual adjustments
			if ( (cpar + cd.width) > vd.width ) { $(tl).addClassName(this._flippedClass); }

		}

		// set final positions
		var x = this.fl + 'px';
		var y = this.ft + 'px';

		// return positions
		return [x, y];

	}

});

document.observe('dom:loaded', function () { new nk_navigate(); });
