/*
---

name: Loop

description: Runs a class method on a periodical

license: MIT-style license.

authors: Ryan Florence <http://ryanflorence.com>

docs: http://moodocs.net/rpflo/mootools-rpflo/Loop

requires:
- Core/Class

provides: [Loop]

...
*/

var Loop = new Class({

	loopCount: 0,
	isLooping: false,
	loopMethod: function(){},

	setLoop: function(fn, delay){
		wasLooping = this.isLooping;
		if (wasLooping) this.stopLoop();
		this.loopMethod = fn;
		this.loopDelay = delay || 3000;
		if (wasLooping) this.startLoop();
		return this;
	},

	stopLoop: function(){
		this.isLooping = false;
		clearInterval(this.periodical);
		return this;
	},

	startLoop: function(delay, now){
		if (!this.isLooping){
			this.isLooping = true;
			if (now) this.looper();
			this.periodical = this.looper.periodical(delay || this.loopDelay, this);
		};
		return this;
	},

	resetLoop: function(){
		this.loopCount = 0;
		return this;
	},

	looper: function(){
		this.loopCount++;
		this.loopMethod(this.loopCount);
		return this;
	}

});

/*
---

name: "SlideShow"

description: "Extensible mid-level class that manages transitions of elements that share the same space, typically for slideshows, tabs, and galleries."

license: "MIT-style license."

authors: "Ryan Florence <http://ryanflorence.com>"

requires:
  - Core/Fx.Tween
  - Core/Slick.Parser
  - Loop/Loop

provides:
  - SlideShow

...
*/

;(function(){

var SlideShow = this.SlideShow = new Class({

	Implements: [Options, Events, Loop],

	options: {
		/*
		onShow: function(){},
		onShowComplete: function(){},
		onReverse: function(){},
		onPlay: function(){},
		onPause: function(){},
		*/
		delay: 7000,
		transition: 'crossFade',
		duration: 500,
		autoplay: false,
		dataAttribute: 'data-slideshow',
		selector: '> *',
		initialSlideIndex: 0
	},

	transitioning: false,
	reversed: false,

	initialize: function(element, options, noSetup){
		this.element = document.id(element);
		this.setOptions(options);
		if (!noSetup) this.setup();
	},

	setup: function(options){
		if (options) this.setOptions(options);
		this.slides = this.element.getElements(this.options.selector);
		this.setupElement().setupSlides();
		this.current = this.current || this.slides[this.options.initialSlideIndex];
		this.index = this.current.retrieve('slideshow-index');
		this.setLoop(this.show.pass(this.reversed ? 'previous' : 'next', this), this.options.delay);
		if (this.options.autoplay) this.play();
		return this;
	},

	show: function(slide, options){
		if (slide == 'next' || slide == 'previous') slide = this[slide + 'Slide']();
		if (typeof slide == 'number') slide = this.slides[slide];
		if (slide == this.current || this.transitioning) return this;

		this.transitioning = true;
		this.current.store('slideshow:oldStyles', this.current.get('style'));

		var transition = (options && options.transition) ? options.transition : slide.retrieve('slideshow-transition'),
			duration = (options && options.duration) ? options.duration : slide.retrieve('slideshow-duration'),
			previous = this.current.setStyle('z-index', 1),
			next = this.reset(slide).setStyle('z-index', 0),
			nextIndex = this.index = next.retrieve('slideshow-index')
			slideData = {
				previous: { element: previous, index: previous.retrieve('slideshow-index') },
				next:     { element: next,     index: nextIndex }
			};

		this.fireEvent('show', slideData);

		SlideShow.transitions[transition]({
			previous: previous,
			next: next,
			duration: duration,
			instance: this
		});

		(function(){
			previous.setStyle('display', 'none');
			this.fireEvent('showComplete', slideData);
			this.transitioning = false;
		}).bind(this).delay(duration);

		this.current = next;
		return this;
	},

	play: function(){
		this.startLoop();
		this.fireEvent('play');
		return this;
	},

	pause: function(){
		this.stopLoop();
		this.fireEvent('pause');
		return this;
	},

	reverse: function(){
		this.setLoop(this.show.pass(this.reversed ? 'next' : 'previous', this), this.options.delay);
		this.reversed = !this.reversed;
		this.fireEvent('reverse');
		return this;
	},

	setupElement: function(){
		this.storeData(this.element);
		this.options.duration = this.element.retrieve('slideshow-duration');
		this.options.transition = this.element.retrieve('slideshow-transition');
		this.options.delay = this.element.retrieve('slideshow-delay');
		if (this.element.getStyle('position') == 'static') this.element.setStyle('position', 'relative');
		return this;
	},

	setupSlides: function(){
		this.slides.each(function(slide, index){
			slide.store('slideshow-index', index).store('slideshow:oldStyles', slide.get('style'));
			this.storeData(slide);
			slide.setStyle('display', (this.current || index == this.options.initialSlideIndex) ? '' : 'none');
		}, this);
		return this;
	},

	storeData: function(element){
		var ops = this.options;
		// default options
		element.store('slideshow-transition', ops.transition);
		element.store('slideshow-duration', ops.duration);
		if (element == this.element) element.store('slideshow-delay', ops.delay);
		// override from data attribute
		var data = element.get(this.options.dataAttribute);
		if (!data) return this;
		Slick.parse(data).expressions[0].each(function(option){
			element.store('slideshow-' + option.tag, option.pseudos[0].key);
		});
		return this;
	},

	reset: function(slide){
		return slide.set('style', slide.retrieve('slideshow:oldStyles'));
	},

	nextSlide: function(){
		return this.slides[this.index + 1] || this.slides[0];
	},

	previousSlide: function(){
		return this.slides[this.index - 1] || this.slides.getLast();
	},

	toElement: function(){
		return this.element;
	}

});

SlideShow.transitions = {};

SlideShow.defineTransition = function(name, fn){
	SlideShow.transitions[name] = fn;
};

SlideShow.defineTransitions = function(transitions){
	Object.each(transitions, function(item, index){
		SlideShow.defineTransition(index, item);
	});
};

})();

// element extensions

Element.Properties.slideshow = {

	set: function(options){
		this.get('slideshow').setup(options);
		return this;
	},

	get: function(){
		var instance = this.retrieve('slideshow');
		if (!instance){
			instance = new SlideShow(this, {}, true);
			this.store('slideshow', instance);
		}
		return instance;
	}

};

Element.implement({

	playSlideShow: function(options){
		this.get('slideshow').setup(options).play();
		return this;
	},

	pauseSlideShow: function(){
		this.get('slideshow').pause();
		return this;
	}

});

// 19 transitions :D
SlideShow.defineTransitions({

	none: function(data){
		data.previous.setStyle('display', 'none');
		return this;
	},

	fade: function(data){
		data.previous.set('tween', {duration: data.duration}).fade('out');
		return this;
	},

	crossFade: function(data){
		data.previous.set('tween', {duration: data.duration}).fade('out');
		data.next.set('tween', {duration: data.duration}).fade('in');
		return this;
	},

	fadeThroughBackground: function(data){
		var half = data.duration / 2;
		data.next.set('tween', {duration: half}).fade('hide');
		data.previous.set('tween',{
			duration: half,
			onComplete: function(){ data.next.fade('in'); }
		}).fade('out');
		return this;
	}

});

(function(){

	function getStyles(direction){
		return {
			property: (direction == 'left' || direction == 'right') ? 'left' : 'top',
			inverted: (direction == 'left' || direction == 'up') ? 1 : -1
		};
	}

	function go(type, styles, data){
		var tweenOptions = {duration: data.duration, unit: '%'};
		if (type == 'blind') {
			data.next.setStyle('z-index', 2);
		}
		if (type != 'slide') {
			data.next
			    .set('tween', tweenOptions)
			    .setStyle(styles.property, 100 * styles.inverted + '%');
			data.next.tween(styles.property, 0);
		}
		if (type != 'blind'){
			data.previous
			    .set('tween', tweenOptions)
			    .tween(styles.property, -(100 * styles.inverted));
		}
	}

	['left', 'right', 'up', 'down'].each(function(direction){

		var capitalized = direction.capitalize(),
		    blindName = 'blind' + capitalized,
		    slideName = 'slide' + capitalized;

		[
			['push' + capitalized, (function(){
				var styles = getStyles(direction);
				return function(data){
					go('push', styles, data);
				}
			}())],

			[blindName, (function(){
				var styles = getStyles(direction);
				return function(data){
					go('blind', styles, data);
				}
			}())],

			[slideName, (function(){
				var styles = getStyles(direction);
				return function(data){
					go('slide', styles, data);
				}
			}())],

			[blindName + 'Fade', function(data){
				this.fade(data)[blindName](data);
				return this;
			}]
		].each(function(transition){
			SlideShow.defineTransition(transition[0], transition[1]);
		});
	});

})();

/*
---

script: Tips.js

name: Tips

description: Class for creating nice tips that follow the mouse cursor when hovering an element.

license: MIT-style license

authors:
  - Valerio Proietti
  - Christoph Pojer
  - Luis Merino

requires:
  - Core/Options
  - Core/Events
  - Core/Element.Event
  - Core/Element.Style
  - Core/Element.Dimensions
  - /MooTools.More

provides: [Tips]

...
*/

(function(){

var read = function(option, element){
	return (option) ? (typeOf(option) == 'function' ? option(element) : element.get(option)) : '';
};

this.Tips = new Class({

	Implements: [Events, Options],

	options: {/*
		id: null,
		onAttach: function(element){},
		onDetach: function(element){},
		onBound: function(coords){},*/
		onShow: function(){
			this.tip.setStyle('display', 'block');
		},
		onHide: function(){
			this.tip.setStyle('display', 'none');
		},
		title: 'title',
		text: function(element){
			return element.get('rel') || element.get('href');
		},
		showDelay: 100,
		hideDelay: 100,
		className: 'tip-wrap',
		offset: {x: 16, y: 16},
		windowPadding: {x:0, y:0},
		fixed: false,
		waiAria: true
	},

	initialize: function(){
		var params = Array.link(arguments, {
			options: Type.isObject,
			elements: function(obj){
				return obj != null;
			}
		});
		this.setOptions(params.options);
		if (params.elements) this.attach(params.elements);
		this.container = new Element('div', {'class': 'tip'});

		if (this.options.id){
			this.container.set('id', this.options.id);
			if (this.options.waiAria) this.attachWaiAria();
		}
	},

	toElement: function(){
		if (this.tip) return this.tip;

		this.tip = new Element('div', {
			'class': this.options.className,
			styles: {
				position: 'absolute',
				top: 0,
				left: 0
			}
		}).adopt(
			new Element('div', {'class': 'tip-top'}),
			this.container,
			new Element('div', {'class': 'tip-bottom'})
		);

		return this.tip;
	},

	attachWaiAria: function(){
		var id = this.options.id;
		this.container.set('role', 'tooltip');

		if (!this.waiAria){
			this.waiAria = {
				show: function(element){
					if (id) element.set('aria-describedby', id);
					this.container.set('aria-hidden', 'false');
				},
				hide: function(element){
					if (id) element.erase('aria-describedby');
					this.container.set('aria-hidden', 'true');
				}
			};
		}
		this.addEvents(this.waiAria);
	},

	detachWaiAria: function(){
		if (this.waiAria){
			this.container.erase('role');
			this.container.erase('aria-hidden');
			this.removeEvents(this.waiAria);
		}
	},

	attach: function(elements){
		$$(elements).each(function(element){
			var title = read(this.options.title, element),
				text = read(this.options.text, element);

			element.set('title', '').store('tip:native', title).retrieve('tip:title', title);
			element.retrieve('tip:text', text);
			this.fireEvent('attach', [element]);

			var events = ['enter', 'leave'];
			if (!this.options.fixed) events.push('move');

			events.each(function(value){
				var event = element.retrieve('tip:' + value);
				if (!event) event = function(event){
					this['element' + value.capitalize()].apply(this, [event, element]);
				}.bind(this);

				element.store('tip:' + value, event).addEvent('mouse' + value, event);
			}, this);
		}, this);

		return this;
	},

	detach: function(elements){
		$$(elements).each(function(element){
			['enter', 'leave', 'move'].each(function(value){
				element.removeEvent('mouse' + value, element.retrieve('tip:' + value)).eliminate('tip:' + value);
			});

			this.fireEvent('detach', [element]);

			if (this.options.title == 'title'){ // This is necessary to check if we can revert the title
				var original = element.retrieve('tip:native');
				if (original) element.set('title', original);
			}
		}, this);

		return this;
	},

	elementEnter: function(event, element){
		clearTimeout(this.timer);
		this.timer = (function(){
			this.container.empty();

			['title', 'text'].each(function(value){
				var content = element.retrieve('tip:' + value);
				var div = this['_' + value + 'Element'] = new Element('div', {
						'class': 'tip-' + value
					}).inject(this.container);
				if (content) this.fill(div, content);
			}, this);
			this.show(element);
			this.position((this.options.fixed) ? {page: element.getPosition()} : event);
		}).delay(this.options.showDelay, this);
	},

	elementLeave: function(event, element){
		clearTimeout(this.timer);
		this.timer = this.hide.delay(this.options.hideDelay, this, element);
		this.fireForParent(event, element);
	},

	setTitle: function(title){
		if (this._titleElement){
			this._titleElement.empty();
			this.fill(this._titleElement, title);
		}
		return this;
	},

	setText: function(text){
		if (this._textElement){
			this._textElement.empty();
			this.fill(this._textElement, text);
		}
		return this;
	},

	fireForParent: function(event, element){
		element = element.getParent();
		if (!element || element == document.body) return;
		if (element.retrieve('tip:enter')) element.fireEvent('mouseenter', event);
		else this.fireForParent(event, element);
	},

	elementMove: function(event, element){
		this.position(event);
	},

	position: function(event){
		if (!this.tip) document.id(this);

		var size = window.getSize(), scroll = window.getScroll(),
			tip = {x: this.tip.offsetWidth, y: this.tip.offsetHeight},
			props = {x: 'left', y: 'top'},
			bounds = {y: false, x2: false, y2: false, x: false},
			obj = {};

		for (var z in props){
			obj[props[z]] = event.page[z] + this.options.offset[z];
			if (obj[props[z]] < 0) bounds[z] = true;
			if ((obj[props[z]] + tip[z] - scroll[z]) > size[z] - this.options.windowPadding[z]){
				obj[props[z]] = event.page[z] - this.options.offset[z] - tip[z];
				bounds[z+'2'] = true;
			}
		}

		this.fireEvent('bound', bounds);
		this.tip.setStyles(obj);
	},

	fill: function(element, contents){
		if (typeof contents == 'string') element.set('html', contents);
		else element.adopt(contents);
	},

	show: function(element){
		if (!this.tip) document.id(this);
		if (!this.tip.getParent()) this.tip.inject(document.body);
		this.fireEvent('show', [this.tip, element]);
	},

	hide: function(element){
		if (!this.tip) document.id(this);
		this.fireEvent('hide', [this.tip, element]);
	}

});

})();

