/**
* slidingPanels is similar to a horizontal accordion navigation, or holding 
* some playing cards in your hand... sliding panels around to reveal one of
* interest.
* 
* slidingPanels r1 // 2008.02.18 // jQuery 1.2.3+
* <http://cherne.net/brian/resources/jquery.slidingPanels.html>
* 
* REQUIRES jQuery hoverIntent plug-in
* <http://cherne.net/brian/resources/jquery.hoverIntent.html>
*
* slidingPanels is currently available for use in all personal or commercial 
* projects under both MIT and GPL licenses. This means that you can choose 
* the license that best suits your project, and use it accordingly.
* 
* // basic usage // assumes ul/li html structure
* $("ul").slidingPanels();
* 
* // advanced usage receives configuration object only
* $("div#wrapper").slidingPanels({
*   panelSelector: "div.panel",  // cssSelector = indicates the panel elements (default = "li")
*   slideDuration: 200,          // number = milliseconds of slide animation (default = 200)
*   onEnterState: handleEnter,   // function = callback after panel has entered a state (undefined by default)
*   onLeaveState: handleLeave,   // function = callback before panel leaves a state (undefined by default)
*   onLeaveDefaultDelay: 0,      // number = milliseconds to delay sliding animation when leaving default state (default = 0)
*   onLeaveStateDelay: 0         // number = milliseconds to delay sliding animation when leaving min/max states (default = 0)
* });
* 
* @param  options  An object with configuration options
* @author    Brian Cherne <brian@cherne.net>
*/
(function($) {
	$.fn.slidingPanels = function(options) {
		// default configuration options
		var cfg = {
			panelSelector: "li",
			slideDuration: 200,
			onLeaveDefaultDelay: 0,
			onLeaveStateDelay: 0
		};
		// override configuration options with user supplied object
		cfg = $.extend(cfg, options);
		
		// iterate through each jQuery object sent to this plug-in and return it to allow chaining
		return this.each(function(){

			var wrapper = $(this).addClass("slidingPanelsActivated");
			var cPanels = $(cfg.panelSelector,wrapper);
			var nPanels = cPanels.length;
			var nPanelDefaultX = ((1/nPanels)*100);
			var nPanelBaseX = nPanelDefaultX/2;
			var nPanelWidth = 100-(nPanelBaseX*(nPanels-1));
			var unit = "%";
			var iMaximizedPanel; // set later

			// ////////////////////////////////////////////////////////////////
			// iterate through panels
			// ////////////////////////////////////////////////////////////////
			cPanels.each(function(i){
				var properties = {
					index: i,
					position: "D",
					state: "Default",
					D2L: -i,
					D2R: nPanels-i,
					L2D: i,
					R2D: i-nPanels,
					L2R: nPanels, // assumes nPanelBaseX = nPanelDefaultX/2
					R2L: -nPanels // assumes nPanelBaseX = nPanelDefaultX/2
				}
				$.data(this,"slidingPanels",properties);

				// for each panel, set default css
				$(this).attr("slidingPanelState","Default").css({
					left: nPanelDefaultX*i+unit,
					width: nPanelWidth+unit
				});
			}).hoverIntent(function(){

				iMaximizedPanel = cPanels.index( this );

				cPanels.each(function(i){

					var position = $.data(this,"slidingPanels").position;

					if ( i <= iMaximizedPanel ){
						if (position != "L"){ $.data(this,"slidingPanels").direction = position + "2L" }
					} else {
						if (position != "R"){ $.data(this,"slidingPanels").direction = position + "2R" }
					}
				}); // close each

				onLeavePanels();

			},function(){});//close cPanels.hoverIntent

			// ////////////////////////////////////////////////////////////////
			// attach events to wrapper
			// ////////////////////////////////////////////////////////////////
			wrapper.hoverIntent({
				over: function(){},
				out: function(){
					cPanels.each(function(i){
						var position = $.data(this,"slidingPanels").position;
						$.data(this,"slidingPanels").direction = position + "2D"
					});
					onLeavePanels();
				},
				// because we use relative animation, wait until any opening animations are done
				timeout: cfg.slideDuration+10
			});//close wrapper.hoverIntent


			// ////////////////////////////////////////////////////////////////
			// has changed state // returns true | false
			// ////////////////////////////////////////////////////////////////
			var hasChangedState = function( panel ){
				// captures panels going to or coming from default state
				if ( $.data(panel,"slidingPanels").direction && $.data(panel,"slidingPanels").direction.indexOf("D") != -1 ){
					return true;
				}
				// captures leaving minimized for maximized state // captures entering maximized state
				if ( $.data(panel,"slidingPanels").index == iMaximizedPanel ){
					return true;
				}
				// captures leaving maximized for minimized state // captures entering maximized state
				if ( $.data(panel,"slidingPanels").state == "Maximized" ){
					return true;
				}
				
				return false;
			};// close hasChangedState


			// ////////////////////////////////////////////////////////////////
			// on leave panels // before animate
			// ////////////////////////////////////////////////////////////////
			var onLeavePanels = function(){
				// if onLeaveState callback is defined
				if ( cfg.onLeaveState ){
					var state;
					// iterate through panels // determine who is moving and who is maximized, they are leaving a state
					cPanels.each(function(i){
						var direction = $.data(this,"slidingPanels").direction;
						state = $.data(this,"slidingPanels").state;
						if ( hasChangedState(this) ){ cfg.onLeaveState.apply(this,[state]); }
					});
					// choose the correct delay // if it's greater than zero, delay animating the panels
					var delay = (state == "Default") ? cfg.onLeaveDefaultDelay : cfg.onLeaveStateDelay;
					if (delay > 0){
						setTimeout(animatePanels,delay);
						return;
					}
				}
				animatePanels();
			};// close onLeavePanels


			// ////////////////////////////////////////////////////////////////
			// animate panels
			// ////////////////////////////////////////////////////////////////
			var animatePanels = function(){
				var difference = 0;
				
				// create a DIV that never gets appended to the DOM to animate from
				$('<div></div>').css("left",0).animate({left:nPanelBaseX},{
					// panels use "know" their direction (D2L,D2R,etc.) and use multiplier against step-by-step animate values
					// keeps panels moving together, allows bug free percent based movement and is always relative to previous position
					step:function(now){
						difference = now - difference;
						cPanels.each(function(i){
							var direction = $.data(this,"slidingPanels").direction;
							if ( direction != undefined ){
								var currentX = Number(this.style.left.substring(0,this.style.left.length-1));
								var desiredX = currentX + (difference * $.data(this,"slidingPanels")[direction]);
								$(this).css("left",desiredX+unit);
							}
						});
						// difference is really now "previous" but we're using the same variable to be efficient
						difference = now;
					},
					duration: cfg.slideDuration,
					// when animation is complete, clean up
					complete: function(){
						cPanels.each(function(i){
							var direction = $.data(this,"slidingPanels").direction;
							var position;
							var state = $.data(this,"slidingPanels").state;
							if ( direction != undefined ){
								// set new position based on direction
								position = $.data(this,"slidingPanels").position = direction.substring(2);
								// round x to the nearest tenth to keep things clean (doesn't really matter)
								var desiredX = Math.round(Number(this.style.left.substring(0,this.style.left.length-1))*100)/100;
								$(this).css("left",desiredX+unit);
							}
							// captures entering minimized from maximized
							var wasMaximized = ( state == "Maximized" ) ? true : false;
							// set state based on position and index of maximized panel
							if( position == "D" ) { state = "Default"; } else { state = (i == iMaximizedPanel) ? "Maximized" : "Minimized"; }
							$.data(this,"slidingPanels").state = state;
							// if the panel has changed state, make sure to call onEnterState function
							if ( cfg.onEnterState && ( wasMaximized || hasChangedState(this) ) ){ cfg.onEnterState.apply(this,[state]); }
							// clear direction
							$.data(this,"slidingPanels").direction = undefined;
						});// close cPanels.each
					}// close div.animate.complete
				});// close div.animate
			};// close animatePanels

		});// close each slidingPanels object
	};// close slidingPanels
})(jQuery);
