/*
Sky.Menu
copyright by MySign AG (http://www.mysign.ch), Marcus Fihlon
depencies:
	- Prototype 1.6.1
	- SkyPrototypeExtensions
	- Scriptaculous 1.8.3
*/

// creating namespace
var Sky;
if ( !Sky ) Sky = {};

Sky.Menu = Class.create();

Sky.Menu._requirementsChecked = false;
Sky.Menu._REQUIRED_PROTOTYPE = '1.6.1';
Sky.Menu._REQUIRED_SCRIPTACULOUS_EFFECTS = '1.8.3';

Sky.Menu.prototype =
{
	initialize: function ( id, opts )
	{
		// checking requirements
		if( !Sky.Menu._requirementsChecked )
		{
			// checking Sky.PrototypeExtensions
			if( Object.isUndefined( Sky.PrototypeExtensions ) )
			{
				throw( "Sky.Menu requires the Sky.PrototypeExtensions JavaScript framework" );
			}
			// checking Prototype
			if( !Sky.PrototypeExtensions.isPrototypeLoaded( Sky.Menu._REQUIRED_PROTOTYPE ) )
			{
				throw( "Sky.Menu requires the Prototype JavaScript framework >= " + Sky.Menu._REQUIRED_PROTOTYPE );
			}
			// checking script.aculo.us effects
			if( !Sky.PrototypeExtensions.isScriptaculousEffectsLoaded( Sky.Menu._REQUIRED_SCRIPTACULOUS_EFFECTS ) )
			{
				throw( "Sky.Menu requires the script.aculo.us effects JavaScript framework >= " + Sky.Menu._REQUIRED_SCRIPTACULOUS_EFFECTS );
			}
			
			Sky.Menu._requirementsChecked = true;
		}
		
		// default options
		this.options = 
		{
			cssClassHover:			'menuhover', // class will be added on mouseover to the li- and a-Tag
			cssClassOut:			'menuout', // class will be added on mouseout to the li- and a-Tag
			cssClassSubs:			'sub', // class will be added on all li's with subs
			cssClassHiddenSub:		'hiddenSubMenu', // used to define display:none; on sub ul-elements; will be removed after init Sky.Menu
			
			hideDuration:			0.2, // duration to hide a subnav
			showDuration:			0.4, // duration to show a subnav

			hideFunction:			Effect.Fade, // function for hiding a subnav
			showFunction:			Effect.BlindDown, // function for showing a subnav

			zIndex:					100, // start z-index for all subnav layers
			hideLayerTopZIndex:		90, // z-index for the close layer top
			hideLayerLeftZIndex:	90, // z-index for the close layer left
			hideLayerRightZIndex:	90, // z-index for the close layer right
			hideLayerBottomZIndex:	70, // z-index for the close layer bottom
			menuZIndex:				80 // z-index for menu container
		};
		Object.extend( this.options, opts || {} );
		
		this.id = id;
		this.menu = $(this.id);
		this.menu.setStyle( 'z-index: ' + ( this.options.menuZIndex ) + ';' );
		this.mouseOverElements = [];
		this.hideLayerVisible = false;
		this.hideLayers = [];
		
		// initialize the menu
		$$('#' + this.id + ' li').each( function ( liElement ) {
			var ulElement = liElement.down( 'ul' );
			
			if ( ulElement != null && !Object.isUndefined( ulElement ) )
			{
				ulElement.hide();
				ulElement.removeClassName( this.options.cssClassHiddenSub );
				
				ulElement.setStyle( 'z-index:' + ( this.options.zIndex++ ) + ';' );
				
				liElement.addClassName( this.options.cssClassSubs );
				liElement.down( 'a' ).addClassName( this.options.cssClassSubs );
				
				liElement.addClassName( this.options.cssClassOut );
				liElement.down( 'a' ).addClassName( this.options.cssClassOut );
			}
			
			liElement.observe( 'mouseover', this._handleMouseOver.bindAsEventListener( this ) );
		}.bind( this ) );
		
		// hide layer top
		this.hideLayers.push( this._getEmptyHideLayer() );
		
		// hide layer left
		this.hideLayers.push( this._getEmptyHideLayer() );
		
		// hide layer right
		this.hideLayers.push( this._getEmptyHideLayer() );
		
		// hide layer bottom
		this.hideLayers.push( this._getEmptyHideLayer() );
		
		// add all hide layers to the body at the top and add the event listener _hideMenu
		this.hideLayers.each( function ( hideLayer ) {
			$(document.body).insert( { 'top': hideLayer } );
			$(hideLayer).observe( 'mouseover', this._hideMenu.bind( this ) );
		}.bind( this ) );
		
		this._setWidthAndHeightCloseLayer();
	},
	
	// get an empty hide layer
	_getEmptyHideLayer: function ()
	{
		var returnValue = new Element( 'div' );
		returnValue.setStyle( 'background-color: #000000; position: absolute;' );
		returnValue.setOpacity( 0 );
		returnValue.hide();
		return returnValue;
	},
	
	// to set the height, width and position of hide layers 
	_setWidthAndHeightCloseLayer: function ()
	{
		var documentWidth = this._getDocumentWidth();
		var documentHeight = this._getDocumentHeight();
		
		var lis = this.menu.childElements();
		var firstLI = lis.first();
		var lastLI = lis.last();

		var firstLIOffset = firstLI.cumulativeOffset();
		var lastLIOffset = lastLI.cumulativeOffset();
		
		// hide layer top
		var hideLayer = this.hideLayers[0];
		hideLayer.setStyle( 'top: 0px; left: 0px; height: ' + ( firstLIOffset.top ) + 'px; width: ' + ( documentWidth ) + 'px;' );
		hideLayer.setStyle( 'z-index: ' + ( this.options.hideLayerTopZIndex ) + ';' );
		
		// hide layer left
		hideLayer = this.hideLayers[1];
		hideLayer.setStyle( 'top: ' + ( firstLIOffset.top ) + 'px; left: 0px; height: ' + firstLI.getHeight() + 'px; width: ' + ( firstLIOffset.left ) + 'px;' );
		hideLayer.setStyle( 'z-index: ' + ( this.options.hideLayerLeftZIndex ) + ';' );
		
		// hide layer right
		hideLayer = this.hideLayers[2];
		hideLayer.setStyle( 'top: ' + ( firstLIOffset.top ) + 'px; left: ' + ( lastLIOffset.left + lastLI.getWidth() ) + 'px; height: ' + firstLI.getHeight() + 'px; width: ' + ( documentWidth - ( lastLIOffset.left + lastLI.getWidth() ) ) + 'px;' );
		hideLayer.setStyle( 'z-index: ' + ( this.options.hideLayerRightZIndex ) + ';' );
		
		// hide layer bottom
		hideLayer = this.hideLayers[3];
		hideLayer.setStyle( 'top: ' + ( firstLIOffset.top + firstLI.getHeight() ) + 'px; left: 0px; height: ' + ( documentHeight - ( firstLIOffset.top + firstLI.getHeight() ) ) + 'px; width: ' + ( documentWidth ) + 'px;' );
		if ( !( Prototype.Browser.IE && !Prototype.Browser.IE8up ) )
		{
			hideLayer.setStyle( 'z-index: ' + ( this.options.hideLayerBottomZIndex ) + ';' );
		}
	},
	
	// get document height (used by _setWidthAndHeightCloseLayer)
	_getDocumentHeight: function()
	{
		return (window.innerHeight ||  document.documentElement.clientHeight || document.body.clientHeight || 0) - 10;
	},
	
	// get document width (used by _setWidthAndHeightCloseLayer)
	_getDocumentWidth: function()
	{
		return (window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth || 0) - 20;
	},
	
	_getDataObj: function ( arguments )
	{
		var returnValue = $A(arguments);
		returnValue.shift();
		return returnValue[0];
	},
	
	// hide menu (called by event 'mouseover' bind to hide layers)
	_hideMenu: function ( event )
	{
		Event.stop( event );
		
		if ( this.hideLayerVisible )
		{
			this.hideLayerVisible = false;
			this.hideLayers.each( function ( hideLayer ) {
				hideLayer.setStyle( 'display: none;' );
			} );
			
			// IE6 fix (show selects)
			if ( Prototype.Browser.IE && !Prototype.Browser.IE7up )
			{
				$$('select').each( function ( elem ) {
					elem.show();
				} );
			}
			
			// hide all open navs
			for ( var i = this.mouseOverElements.length - 1; i >= 0; i-- )
			{
				var liElement = this.mouseOverElements[i];
				
				this.mouseOverElements.pop();
				
				var ulElement = liElement.down( 'ul' );
				this._closeSub( liElement, ulElement, this.options.hideDuration );
			}
		}
	},
	
	// close a sub nav layer
	_closeSub: function ( liElement, ulElement, duration )
	{
		// remove hover class name
		liElement.removeClassName( this.options.cssClassHover );
		liElement.addClassName( this.options.cssClassOut );
		liElement.down( 'a' ).removeClassName( this.options.cssClassHover );
		liElement.down( 'a' ).addClassName( this.options.cssClassOut );
		
		if ( liElement.skyMenu != null && !Object.isUndefined( liElement.skyMenu ) )
		{
			liElement.skyMenu.finish();
			liElement.skyMenu = null;
		}
		
		liElement.skyMenu = new Effect.Parallel(
			[
				this.options.hideFunction(
					ulElement,
					{
						sync: true
					}
				)
			],
			{
				duration: duration,
				afterFinish: function ()
				{
					this.setStyle( 'height: ;' );
					this.setOpacity( 1 );
				}.bind( ulElement )
			}
		);
	},
	
	_getParentIdWithSubs: function ( liElement )
	{
		var returnValue = '';
		
		var parent = liElement.up();
		while ( parent != null && !Object.isUndefined( parent ) && parent.identify() != this.id && !( parent.tagName == 'LI' && !Object.isUndefined( parent.down( 'ul' ) ) ) )
		{
			parent = parent.up();
		}
		
		if ( !Object.isUndefined( parent ) )
		{
			returnValue = parent.identify();
		}
		
		return returnValue;
	},
	
	// handle mouse over li tag (called by event 'mouseover' bind to all li-Tag's)
	_handleMouseOver: function ( event )
	{
		Event.stop( event );
		
		var liElement = event.element();
		while ( liElement.tagName != 'LI' )
		{
			liElement = liElement.up();
		}
		
		if ( this.mouseOverElements.length > 0 && this.mouseOverElements.last().identify() != liElement.identify() )
		{
			var id = this._getParentIdWithSubs( liElement );
			
			for ( var i = this.mouseOverElements.length - 1; i >= 0; i-- )
			{
				var tmpLIElement = this.mouseOverElements[i];
				
				if ( tmpLIElement.identify() != id )
				{
					this.mouseOverElements.pop();
					
					var tmpULElement = tmpLIElement.down( 'ul' );
					this._closeSub( tmpLIElement, tmpULElement, this.options.hideDuration );
				}
				else
				{
					break;
				}
			}
		}
		
		if ( ( this.mouseOverElements.length == 0 || this.mouseOverElements.last().identify() != liElement.identify() ) && !Object.isUndefined( liElement.down( 'ul' ) ) )
		{
			this.mouseOverElements.push( liElement );
			
			if ( !this.hideLayerVisible )
			{
				// IE6 fix (hide selects)
				if ( Prototype.Browser.IE && !Prototype.Browser.IE7up )
				{
					$$('select').each( function ( elem ) {
						elem.hide();
					} );
				}
				
				this.hideLayerVisible = true;
				this._setWidthAndHeightCloseLayer();
				this.hideLayers.each( function ( hideLayer ) {
					hideLayer.setStyle( 'display: block;' );
				} );
			}
			
			var data = this._getDataObj( $A(arguments) );
			
			// add hover class name
			liElement.addClassName( this.options.cssClassHover );
			liElement.removeClassName( this.options.cssClassOut );
			liElement.down( 'a' ).addClassName( this.options.cssClassHover );
			liElement.down( 'a' ).removeClassName( this.options.cssClassOut );
			
			var ulElement = liElement.down( 'ul' );
			
			if ( liElement.skyMenu != null && !Object.isUndefined( liElement.skyMenu ) )
			{
				liElement.skyMenu.finish();
				liElement.skyMenu = null;
			}
			
			liElement.skyMenu = new Effect.Parallel(
				[
					this.options.showFunction(
						ulElement,
						{
							sync: true
						}
					)
				],
				{
					duration: this.options.showDuration,
					afterFinish: function ()
					{
						this.setStyle( 'height: ;' );
						this.setOpacity( 1 );
					}.bind( ulElement )
				}
			);
		}
	}
};