/*
	Filename: moo.rd - A lightweight Mootools extension
	
	Author: Riccardo Degni, <http://www.riccardodegni.it/> and the moo.rd Team
	
	License: GNU GPL License
	
	Copyright: copyright 2007 Riccardo Degni
	
	[Credits]
		[li] moo.rd is based on the MooTools framework <http://mootools.net/>, and uses the MooTools syntax
		[li] moo.rd constructors extends some of the MooTools Classes
		[li] moo.rd Documentation is written by Riccardo Degni
	[/Credits]
*/

var Moo = {};

Moo.Rd = {
	version: '1.3.2',
	author: 'Riccardo Degni',
	members: [
		'Cristiano Fino',
		'Moocha'
	]
};

/*
	Filename: constructors.js
	
	[Description] 
		Contains some of the moo.rd native Constructors based on the MooTools Class. It permits a major modularity.
	[/Description]
	
	Contains: Class Table, Class Make
*/
/*
	Class: Table
	Description: Allows you to customize tables, tables rows, cells and columns 
*/
var Table = new Class({	
	initialize: function(element) {
		this.element = $(element);
		this.rows = this.element.getElements('tr');
		this.cells = this.element.getElements('tr').getElements('td');
	}
});

/*
	Class: Make
	Description: Wrapper to create Classes that make dinamically Elements.  
*/
var Make = new Class({
	Implements: [Options],
	options: {
		content: 'text'
	}
});

/*
	Filename: effects_cycle.js
	
	[Description] 
		Contains the Fx.Cycle Class and the Fx.Cycle extensions for creating powerful cycle effects, based on different 
		style transitions.
	[/Description]
	
	Contains: Class Fx.Cycle and its extensions, Class Element
	
	[Summary]
		Fx.Cycle ::: A wrapper which allows you to create powerful cycle effects
		Element ::: Contains the cycle method which returns a determinate Fx.Cycle instance
	[/Summary]
*/
/*
	Class: Fx.Cycle
	
	[Description]  
		With the Fx.Cycle extensions, you can create the "cycle effect" which means a slideshow based on a particular
		style transition. This file contains 14 Fx.Cycle extensions, like Fx.Cycle.zoom and Fx.Cycle.fade.
		In addiction, you can create your own cycle effect, either customizing an existing one or creating a new one.
		Below you can find all the Fx.Cycle extensions.
	[/Description]
	
	Extends: Fx.Morph
	
	Constructor: new Fx.Cycle.cycleType (element, options)
	
	[Properties] 
		element - the wrapper which contains your slides
		options - optional. The Fx options plus those described below
	[/Properties]
	
	[Options]
		animeOut : an object whose properties are the styles to alter when the slide gets out.
		animeIn : an object whose properties are the styles to alter when the slide comes in.
		cssBefore : an object whose properties are the styles to alter immediately before the slide comes in.
		animeInType : indicates the method to use with the animeIn object. It can be 'set' (default) or 'start' 
		overflow : the overflow of the wrapper. Depends on the type of cycle effect.
		steps : the timer used by autostart to change periodically the slides. Default is 2000 ms
		handles : an object which attaches events to elements passed in as values. The keywords represent the Fx.Cycle's methods
		autostart : if true the slides will be changed automatically. Deafult is true
		enable : an object containing some keywords to enable extra features. See below.
	[/Options]
	
	>> enable:
	[List]
		[li] keyboard: if true you can navigate with the keyboard too. In addition to the arrows, you can use the following keys. 'a' for autostart, 's' for stop, 'f' to go to the first slide and 'l' to go to the last slide. 
	[/List]
	
	[Events]
		onAnimeIn : function fired immediately before the element is animed in. The current and the next slide will be passed in
		onAnimeOut : function fired immediately before the element is animed out. The current and the next slide will be passed in
	[/Events]
	
	[Methods]
		next -- switches to the next slide
		prev -- switches to the previous slide
		goTo -- swicthes to the given slide
		toFirst -- switches to the first slide
		toLast -- switches to the last slide
		autostart -- enables the cycle slideshow
		stop -- stops the slideshow either if start method has been called or autostart option has been enabled
		animeIn -- sets the animeIn properties
		animeOut -- sets the animeOut properties
		cssBefore -- sets the cssBefore properties
	[/Methods]
	
	[Note]
		You can use the keywords 'this.height' and 'this.width' into the animeIn, animeOut and cssBefore methods,
		to refer respectively to the images' height and width. Using the keyword 'this.parentHeight' and 'this.parentWidth'
		you can refer to the parent element dimensions.
		In addiction, you can use the keywords 'this.currSlide' and 'this.nextSlide' to refer respectively to the current and
		the next slide.
	[/Note]
	
	Method: next
	Description:  switches to the next slide
	[Example]  
		> fx.next();
	[/Example]
	
	Method: prev
	Description:  switches to the previous slide
	[Example]  
		> fx.prev();
	[/Example]
	
	Method: goTo
	Description:  swicthes to the given slide
	[Arguments]
		num :: the position of the needed slide
	[/Arguments]
	[Example]  
		> fx.goTo(2);
	[/Example]
	
	Method: toFirst
	Description:  switches to the first slide
	[Example]  
		> fx.toFirst();
	[/Example]
	
	Method: toLast
	Description:  switches to the last slide
	[Example]  
		> fx.toLast();
	[/Example]
	
	Method: autostart
	Description:  enables the cycle slideshow
	[Example]  
		> fx.autostart();
	[/Example]
	
	Method: stop
	Description:  stops the slideshow either if start method has been called or autostart option has been enabled
	[Example]  
		> fx.stop();
	[/Example]
	
	Method: animeIn
	Description:  sets the animeIn properties
	[Arguments]
		styles :: an object which contains the animeIn css styles
	[/Arguments]
	[Example]  
		> fx.animeIn({top: 200, left: 200});
	[/Example]
	
	Method: animeOut
	Description:  sets the animeOut properties
	[Arguments]
		styles :: an object which contains the animeOut css styles
	[/Arguments]
	[Example]  
		> fx.animeOut({top: -200, opacity: 0});
	[/Example]
	
	Method: cssBefore
	Description:  sets the cssBefore properties
	[Arguments]
		styles :: an object which contains the cssBefore css styles
	[/Arguments]
	[Example]  
		> fx.cssBefore({left: 200, opacity: 1, height: 0, width: 0});
	[/Example]
*/
Fx.Cycle = new Class({
	
	Extends: Fx.Morph,
	
	options: {
		animeOut: {},
		animeIn: {},
		cssBefore: {},
		//cssAfter: {},
		animeInType: 'set',
		overflow: 'visible',
		autostart: true,
		steps: 2000,
		handles: {
			next: false,
			prev: false,
			toFirst: false,
			toLast: false,
			autostart: false,
			stop: false
		},
		enable: {
			keyboard: false	
		},
		onAnimeIn: $empty,
		onAnimeOut: $empty
	},
	
	initialize: function(element, options) {
		this.parent(element, options);
		this.imgs = this.element.getChildren();
		this.uimgs = this.element.getChildren().reverse();
		this.element.setStyles({'position': 'relative', 'overflow': this.options.overflow});
		this.first = this.element.getFirst();
		
		this.height = this.first.getStyle('height').toInt();
		this.width = this.first.getStyle('width').toInt();
		this.parentHeight = this.element.getStyle('height').toInt();
		this.parentWidth = this.element.getStyle('width').toInt();
		
		var count = 0;
		this.imgs.each(function(img, i) {
			img.setStyles({'position': 'absolute', 'top': '0px', 'left': '0px', 'z-index': (count++<2 ? 2-i:0)});
		}, this);
		this.count = 0;
		this.length = this.imgs.length-1;
		this.fullLength = this.imgs.length;
		if(this.options.autostart) this._autostart = this.next.periodical(this.options.steps, this);
		this.attachHandles();
		if(this.options.enable.keyboard) this.attachKeys.bindWithEvent(this)();
	},
	
	next: function() {
		if(!this.timer) {
			this.checkAutostart();
			this.element = this.imgs[this.count];
			(this.count != this.length) ? this.count++ : this.count = 0;
			this.main();
		}
	},
	
	prev: function() {
		if(!this.timer) {
			this.checkAutostart();
			this.element = this.imgs[this.count];
			(this.count == 0) ? this.count = this.length : this.count--;
			this.main();
		}
	},
	
	goTo: function(to) {
		if(!this.timer && to != this.count) {
			this.checkAutostart();
			this.element = this.imgs[this.count];
			this.count = to;
			this.main();
		}
	},
	
	toFirst: function() {
		if(!this.timer && this.count != 0) {
			this.checkAutostart();
			this.element = this.imgs[this.count];
			this.count = 0;
			this.main();
		}
	},
	
	toLast: function() {
		if(!this.timer && this.count != this.length) {
			this.checkAutostart();
			this.element = this.imgs[this.count];
			this.count = this.length;
			this.main();
		}
	},
	
	autostart: function() {
		this._autostart = this.next.periodical(this.options.steps, this);
	},
	
	stop: function() {
		this._autostart = $clear(this._autostart);	
	},
	
	checkAutostart: function() {
		if(this._autostart) {
			this._autostart = $clear(this._autostart);
			this._autostart = this.next.periodical(this.options.steps, this);
		}
	},
	
	attachHandles: function() {
		for(var mtd in this.options.handles) {
			var method = mtd.toString();
			if(this.options.handles[mtd] && $function(this[method])) {
				$(this.options.handles[mtd]).addEvent('click', function(event, method) {
					event.preventDefault();	
					this[method]();
				}.bindWithEvent(this, method));	
			}
		}
	},
	
	attachKeys: function(event) {
		$(document).addEvent('keydown', function(event) {
			switch(event.key) {
				case 'left': this.prev();
					break;
				case 'right': this.next();
					break;
				case 'a': this.autostart();
					break;
				case 's': this.stop();
					break;
				case 'f': this.toFirst();
					break;
				case 'l': this.toLast();
					break;
			}
		}.bind(this));
	},
	
	main: function() {
		this.element.setStyle('z-index', this.fullLength);
		this.imgs[this.count].setStyle('z-index', 1);
		
		this.currSlide = this.element;
		this.nextSlide = this.imgs[this.count];
		
		this.fireEvent('onAnimeOut', [this.currSlide, this.nextSlide]);
		this.start(this.options.animeOut).chain(
			function() {
				var type = this.options.animeInType;
				if(type == 'set') this.fireEvent('onAnimeIn', [this.currSlide, this.nextSlide]);
				this.element.setStyle('z-index', 0);
				this.imgs.each(function(img, i) {
					if(i != this.count) img.setStyle('z-index', 0).setStyles(this.options.cssBefore);
				}, this);
				if(type == 'set') this[type](this.options.animeIn); // set instead of start
				if(type == 'start') this[type](this.options.animeIn).chain(function() { this.fireEvent('onAnimeIn', [this.currSlide, this.nextSlide]); });
			}
		);
	}
					 
});

(function() {
var methods = {};

['animeOut', 'animeIn', 'cssBefore'].each(function(method) {
	methods[method] = function(styles) {
		for(var style in styles)
			if($defined(styles[style])) this.options[method][style] = styles[style]; 
	}					
});

Fx.Cycle.implement(methods);
})();

/* 
Class: Fx.Cycle.shuffle 
*/
Fx.Cycle.shuffle = new Class({

	Extends: Fx.Cycle,
	
	options: {
		animeInType: 'start',
		animeIn: {
			top: 0,
			left: 0
		},
		sizes: [20, -110]
	},
	
	initialize: function(element, options) {
		this.parent(element, options);
		this.animeOut({top: this.options.sizes[0], left: this.options.sizes[1]});
	}
});

/* 
Class: Fx.Cycle.fade 
*/
Fx.Cycle.fade = new Class({

	Extends: Fx.Cycle,
	
	options: {
		animeOut: {
			opacity: 0
		},
		cssBefore: {
			opacity: 1	
		}
	},
	
	initialize: function(element, options) {
		this.parent(element, options);
	}
});

/* 
Class: Fx.Cycle.slideUp 
*/
Fx.Cycle.slideUp = new Class({

	Extends: Fx.Cycle,
	
	options: {
		cssBefore: {
			top: 0, left:0
		},
		overflow: 'hidden'
	},
	
	initialize: function(element, options) {
		this.parent(element, options);
		this.animeOut({top: -this.parentHeight});
	}
});

/* 
Class: Fx.Cycle.slideDown 
*/
Fx.Cycle.slideDown = new Class({

	Extends: Fx.Cycle,
	
	options: {
		cssBefore: {
			top: 0, left:0
		},
		overflow: 'hidden'
	},
	
	initialize: function(element, options) {
		this.parent(element, options);
		this.animeOut({top: this.parentHeight});
	}
});

/* 
Class: Fx.Cycle.slideRight 
*/
Fx.Cycle.slideRight = new Class({

	Extends: Fx.Cycle,
	
	options: {
		cssBefore: {
			top: 0, left:0
		},
		overflow: 'hidden'
	},
	
	initialize: function(element, options) {
		this.parent(element, options);
		this.animeOut({left: this.parentWidth});
	}
});

/* 
Class: Fx.Cycle.slideLeft 
*/
Fx.Cycle.slideLeft = new Class({

	Extends: Fx.Cycle,
	
	options: {
		cssBefore: {
			top: 0, left:0
		},
		overflow: 'hidden'
	},
	
	initialize: function(element, options) {
		this.parent(element, options);
		this.animeOut({left: -this.parentWidth});
	}
});

/* 
Class: Fx.Cycle.foldUp 
*/
Fx.Cycle.foldUp = new Class({

	Extends: Fx.Cycle,
	
	options: {
		animeOut: {
			height: 0
		},
		animeIn: {
			left: 0, top: 0
		},
		overflow: 'hidden'
	},
	
	initialize: function(element, options) {
		this.parent(element, options);
		this.cssBefore({top: 0, height: this.height});
	}
});

/* 
Class: Fx.Cycle.foldDown 
*/
Fx.Cycle.foldDown = new Class({

	Extends: Fx.Cycle,
	
	options: {
		animeIn: {
			left: 0, top: 0
		},
		overflow: 'hidden'
	},
	
	initialize: function(element, options) {
		this.parent(element, options);
		this.animeOut({height: 0, top: this.height});
		this.cssBefore({top: 0, height: this.height});
	}
});

/* 
Class: Fx.Cycle.foldRight 
*/
Fx.Cycle.foldRight = new Class({

	Extends: Fx.Cycle,
	
	options: {
		animeOut: {
			width: 0
		},
		animeIn: {
			left: 0, top: 0
		},
		overflow: 'hidden'
	},
	
	initialize: function(element, options) {
		this.parent(element, options);
		this.cssBefore({top: 0, width: this.width});
	}
});

/* 
Class: Fx.Cycle.foldLeft 
*/
Fx.Cycle.foldLeft = new Class({

	Extends: Fx.Cycle,
	
	options: {
		animeIn: {
			left: 0, top: 0
		},
		overflow: 'hidden'
	},
	
	initialize: function(element, options) {
		this.parent(element, options);
		this.animeOut({width: 0, left: this.width});
		this.cssBefore({top: 0, width: this.width});
	}
});

/* 
Class: Fx.Cycle.zoom 
*/
Fx.Cycle.zoom = new Class({

	Extends: Fx.Cycle,
	
	options: {
		cssBefore: {
			top: 0, left: 0
		},
		overflow: 'hidden'
	},
	
	initialize: function(element, options) {
		this.parent(element, options);
		this.animeOut({width: 0, height: 0, top: this.height/2, left: this.width/2});
		this.animeIn({top: 0, left: 0, width: this.height, height: this.width});
	}
});

/* 
Class: Fx.Cycle.diagonalUp
*/
Fx.Cycle.diagonalUp = new Class({

	Extends: Fx.Cycle,
	
	options: {
		animeIn: {
			top: 0, left: 0
		},
		overflow: 'hidden'
	},
	
	initialize: function(element, options) {
		this.parent(element, options);
		this.animeOut({top: -this.parentHeight, left: -this.parentWidth});
	}
});

/* 
Class: Fx.Cycle.diagonalDown
*/
Fx.Cycle.diagonalDown = new Class({

	Extends: Fx.Cycle,
	
	options: {
		animeIn: {
			top: 0, left: 0
		},
		overflow: 'hidden'
	},
	
	initialize: function(element, options) {
		this.parent(element, options);
		this.animeOut({top: this.parentHeight, left: this.parentWidth});
	}
});

/* 
Class: Fx.Cycle.linear 
*/
Fx.Cycle.linear = new Class({

	Extends: Fx.Cycle,
	
	options: {
		animeOut: {
			display: 'none'
		},
		cssBefore: {
			display: 'block'
		}
	},
	
	initialize: function(element, options) {
		this.parent(element, options);
	}
});

/*
	Class: Element
	
	[Description] 
		Contains the cycle method which returns a determinate Fx.Cycle instance
	[/Description]
	
	Extends: Class Element
	
	[Methods]
		cycle -- returns an Fx.Cycle instance of the given type
	[/Methods]
*/	
Element.implement({
	
	/*
	Method: cycle
	Description:  returns an Fx.Cycle instance of the given type
	[Arguments]
		type :: the Fx.Cycle transition
		options :: the Fx.Cycle options
	[/Arguments]
	[Example]  
		> // slideUp instance
		> var slideUp = $('myElement').cycle('slideUp');
		> // diagonalUp instance
		> var diagonalUp = $('myElement').cycle('diagonalUp', {duration: 4000, steps: 4000});
	[/Example]
	*/
	cycle: function(type, options) {
		return new Fx.Cycle[type](this, options);
	}				  

});