/*

The MIT License

BySlideMenu (http://www.byscripts.info/mootools/byslidemenu)
Copyright (c) 2008 ByScripts.info (http://www.byscripts.info)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

*/

var BySlideMenu = new Class({
	Implements: Options,
	
	version: 2.0,
	
	options: {
		defaultIndex: 0,			// index of the slide to show by default (set to -1 to show all slides evenly spaced)
		triggerSelector: null,		// selector of the slides trigger, if any (ie: 'div.handle'), if null, whole slide will be the trigger
		onStart: $empty,			// function run when the whole animation of slides start (see doc for more infos)
		onComplete: $empty,			// function run when the whole animation of slides is complete (see doc for more infos)
		onSlideStart: $empty,		// function run when the animation of a slide start (see doc for more infos)
		onSlideComplete: $empty,	// function run when the animation of a slide is complete (see doc for more infos)
		expandOnClick: false,		// true: slide is shown by mouse click. false: slide is shown by mouse over
		pin: false,					// if true, a shown slide will be pinned, else the default slide will be shown on mouse out
		vertical: false,			// set to true if you want a vertical slide menu
		compressSize: 50,			// when a slide is shown, all others will be compressed to this size
		duration: 500,				// duration of the sliding effect
		transition: 'linear',		// transition of the sliding effect
		overflowSelector: false		// selector of the slide contents which required overflow (see doc for more info)

	},
	
	elements: null,					// contains all slides
	elementsSize: {},				// contains size of the elements
	containerSize: {},				// contains width of the container
	pinnedIndex: 0,					// contains the index of the currently pinned slide
	currentIndex: 0,				// contains the index of the currently shown slide
	currentSlide: null,				// contains the currently shown slide
	animatedSlides: 0,				// contains number of currently animated slides,
	overflowElements: new Array(),
	
	
	initialize: function(containerId, options){
		
		this.setOptions(options);
		
		var container = $($pick(containerId, 'byslidemenu'));
		
		// no container = no menu
		if(!container)
			return;	
		
		this.elements = container.getChildren();
		this.elementsCount = this.elements.length;
		
		// no elements = no menu
		if(!this.elementsCount)
			return;
		
		// checking the default index range
		this.options.defaultIndex = this.options.defaultIndex.limit(-1, this.elementsCount - 1);
		
		// for now, the current index is the default index
		this.currentIndex = this.options.defaultIndex;
		
		// for now, the pinned index is the default index
		this.pinnedIndex = this.options.defaultIndex;
		
		// the size of all elements is the same as the first one
		this.elementsSize = this.elements[0].getSize();

		// Calculate the container size
		if(!this.options.vertical)
		{
			this.containerSize = {
				x: this.elementsSize.x + ((this.elementsCount - 1) * this.options.compressSize),
				y: this.elementsSize.y
			}
		}
		else
		{
			this.containerSize = {
				x: this.elementsSize.x,
				y: this.elementsSize.y + ((this.elementsCount - 1) * this.options.compressSize)
			}
		}
		
		// Set the container styles
		container.setStyles({
			position: 'relative',
			overflow: 'hidden',
			width: this.containerSize.x,
			height: this.containerSize.y
		});
		
		// If not using "pin", show the default index when mouse leave container
		if(!this.options.pin)
		{
			container.addEvent('mouseleave', function(){
				this.showSlide(this.options.defaultIndex);
			}.bind(this));
		}
		
		this.elements.each(function(element, index){
			
			element.store('index', index);
			element.setStyle('position', 'absolute');
			
			// Calculate the "before" position
			var before_position = index * this.options.compressSize;
			
			// Calculate the other positions (middle and after)
			if(!this.options.vertical)
			{
				var after_position = ((index - 1) * this.options.compressSize) + this.elementsSize.x;
				var element_style = 'left';
				
				if(this.options.defaultIndex == -1)
					var middle_position = index * (this.containerSize.x / this.elementsCount);
			}
			else
			{
				var after_position = ((index - 1) * this.options.compressSize) + this.elementsSize.y;
				var element_style = 'top';
				
				if(this.options.defaultIndex == -1)
					var middle_position = index * (this.containerSize.y / this.elementsCount);
			}
			
			// Set the correct positions
			if(this.options.defaultIndex == -1)
			{
				element.setStyle(element_style, middle_position);
				element.store('current_position', 'middle');
			}
			else if(index <= this.options.defaultIndex)
			{
				element.setStyle(element_style, before_position);
				element.store('current_position', 'before');
			}
			else
			{
				element.setStyle(element_style, after_position);
				element.store('current_position', 'after');
			}
			
			// Store the different elements which need overflow
			if(this.options.overflowSelector === '')
				this.overflowElements[index] = element;
			else if(this.options.overflowSelector !== false)
				this.overflowElements[index] = element.getElements(this.options.overflowSelector);
			
			// Configuring the element animation
			element.set('tween', {
				onStart: function(element, index){
					
					// Run the onSlideStart function
					this.options.onSlideStart.bind(element, index).run();
					
					// If there is no other animated slide, run the onStart function
					if(this.animatedSlides == 0)
					{
						this.options.onStart.bind(this.currentSlide, this.currentIndex).run();
						
						// Hide overflow if needed
						if(this.options.overflowSelector !== false)
							this.hideOverflow();
					}

					this.animatedSlides++;
				}.bind(this, [element, index]),
				onComplete: function(){
					
					// Run the onSlideComplete function
					this.options.onSlideComplete.bind(element, index).run();
					
					this.animatedSlides--;
					
					// If there is no other animated slide, run the onComplete function
					if(this.animatedSlides == 0)
					{
						this.options.onComplete.bind(this.currentSlide, this.currentIndex).run();
						
						// Show overflow if needed
						if(this.options.overflowSelector !== false)
							this.showOverflow(this.currentIndex);
					}
				}.bind(this, [element, index]),
				duration: this.options.duration,
				transition: this.options.transition,
				property: element_style
			});
			
			// Store the different positions to the element
			element.store('before_position', before_position);
			element.store('middle_position', middle_position);
			element.store('after_position', after_position);

			// Using trigger selector if defined
			if(this.options.triggerSelector)
				var handle = element.getElements(this.options.triggerSelector);
			
			// If the is no handle (triggerSelector not defined, or handle not found)
			// the whole slide is used
			if(!handle)
				var handle = element;

			// Add event to the handle
			handle.addEvent(this.options.expandOnClick ? 'click' : 'mouseenter', function(element){
				this.showSlide(element);
			}.bind(this, element));

		}, this);
	},
	
	showSlide: function(identifier){
		
		// Identifier can be an index, an id, or an element
		if($type(identifier) == 'number')
		{
			if(identifier == -1)
				return this.showAllSlides();
			
			var element = this.elements[identifier];
			var index = identifier;
		}
		else
		{
			var element = $(identifier);
			var index = element.retrieve('index');
		}
		
		// No element = no slide to show
		if(!element)
			return;
		
		// If the asked index is already displayed, there is nothing to do
		if(this.currentIndex == index)
			return;
		
		// If using pinning
		if(this.options.pin)
			this.pinnedIndex = index;
		
		this.currentIndex = index;
		this.currentSlide = element;

		// Current slide and previous ones must be moved to "before" position
		for(var i = 0; i <= index; i++)
			this.move(i, 'before');
		
		// Next slides must be moved to "after" position
		for(var j = index + 1; j < this.elementsCount; j++)
			this.move(j, 'after');
	},
	
	showAllSlides: function(){
		// Show all slides evenly spaced
		for(var i = 0; i < this.elementsCount; i++)
			this.move(i, 'middle');
	},
	
	hideOverflow: function(){
		this.overflowElements.each(function(element){
			element.setStyle('overflow', 'hidden');
		});
	},
	
	showOverflow: function(index){
		this.overflowElements[index].setStyle('overflow', 'auto');
	},
	
	move: function(index, where){
		var element = this.elements[index];

		// If the slide is already in the correct position, there is nothing to do
		if(element.retrieve('current_position') == where)
			return;
		
		element.store('current_position', where);
		element.get('tween').start(element.retrieve(where + '_position'));
	}
});