/**
 * Autocompleter
 *
 * @version		1.1rc1
 *
 * @todo: Caching, no-result handling!
 *
 *
 * @license		MIT-style license
 * @author		Harald Kirschner <mail [at] digitarald.de>
 * @copyright	Author
 */
var Autocompleter = {};

Autocompleter.Base = new Class({

	options: {
		minLength: 1, // start guessing at char no ?
		markQuery: true, // mark the string that is inside the textbox by class autocompleter-queried
		choicesWidth: 'inherit', // width of the dropdown is set as ?? or inherit from input
		maxChoices: 10,
		injectChoice: null,
		customChoices: null,
		className: 'autocompleter-choices', // name of the class we want to use for the dropdown
		zIndex: 42,
		delay: 400,
		observerOptions: {},
		fxOptions: {},
		onOver: $empty,
		onSelect: $empty,
		onSelection: $empty,
		onShow: $empty,
		onHide: $empty,
		onBlur: $empty,
		onFocus: $empty,

		autoSubmit: false, // after selected, we will automatically submit the form?
		overflow: false,
		overflowMargin: 25,
		selectFirst: true, // press down-arrow key can select the first element?
		filter: null,
		filterCase: false,
		filterSubset: false,
		forceSelect: false,
		selectMode: true, // type-ahead, the nearest string will appear right after you press; pick, only appear dropdown, no replacing
		choicesMatch: null,

		multiple: false, // use multiple selects like tags, emails...
		separator: ', ', // separate by what character ,
		separatorSplit: /\s*[,;]\s*/,
		autoTrim: true,
		allowDupes: false,
		cache: true
	},

	initialize: function(el, options) {
		this.setOptions(options);
		this.element = $(el);
		this.build();
		this.observer = new Observer(this.element, this.prefetch.bind(this), $merge({
			'delay': this.options.delay
		}, this.options.observerOptions));
		this.queryValue = null;
		if (this.options.filter) this.filter = this.options.filter.bind(this);
		var mode = this.options.selectMode;
		this.typeAhead = (mode == 'type-ahead');
		this.selectMode = (mode === true) ? 'selection' : mode;
		this.cached = [];
	},

	/**
	 * build - Initialize DOM
	 *
	 * Builds the html structure for choices and appends the events to the element.
	 * Override this function to modify the html generation.
	 */
	build: function() {
		if ($(this.options.customChoices)) {
			this.choices = this.options.customChoices;
		} else {
			this.choices = new Element('ul', {
				'class': this.options.className,
				'styles': {
					'zIndex': this.options.zIndex
				}
			}).inject(this.element, 'after');
			this.fix = new OverlayFix(this.choices);
			this.relative = this.choices.getOffsetParent();
		}
		if (!this.options.separator.test(this.options.separatorSplit)) {
			this.options.separatorSplit = this.options.separator;
		}
		this.fx = (!this.options.fxOptions) ? null : new Fx.Tween(this.choices, 'opacity', $merge({
			'link': 'cancel',
			'duration': 200
		}, this.options.fxOptions)).addEvent('onStart', Chain.prototype.clearChain).set(0);
		this.element.setProperty('autocomplete', 'off')
			.addEvent((Browser.Engine.trident || Browser.Engine.webkit) ? 'keydown' : 'keypress', this.onCommand.bind(this))
			.addEvent('click', this.onCommand.bind(this, [false]))
			.addEvent('focus', this.toggleFocus.create({bind: this, arguments: true, delay: 100}))
			.addEvent('blur', this.toggleFocus.create({bind: this, arguments: false, delay: 100}))
			.addEvent('trash', this.destroy.bind(this));
	},

	destroy: function() {
		this.choices = this.selected = this.choices.destroy();
	},

	toggleFocus: function(state) {
		this.focussed = state;
		if (!state) this.hideChoices(true);
		this.fireEvent((state) ? 'onFocus' : 'onBlur', [this.element]);
	},

	onCommand: function(e) {
		if (!e && this.focussed) return this.prefetch();
		if (e && e.key && !e.shift) {
			switch (e.key) {
				case 'enter':
					if (this.element.value != this.opted) return true;
					if (this.selected && this.visible) {
						this.choiceSelect(this.selected);
						return !!(this.options.autoSubmit);
					}
					break;
				case 'up': case 'down':
					if (!this.prefetch() && this.queryValue !== null) {
						var up = (e.key == 'up');
						this.choiceOver((this.selected || this.choices)[
							(this.selected) ? ((up) ? 'getPrevious' : 'getNext') : ((up) ? 'getLast' : 'getFirst')
						](this.options.choicesMatch), true);
					}
					return false;
				case 'esc': case 'tab':
					this.hideChoices(true);
					break;
			}
		}
		return true;
	},

	setSelection: function(finish) {
		var input = this.selected.inputValue, value = input;
		var start = this.queryValue.length, end = input.length;
		if (input.substr(0, start).toLowerCase() != this.queryValue.toLowerCase()) start = 0;
		if (this.options.multiple) {
			value = this.element.value, split = this.options.separatorSplit;
			start += this.queryIndex;
			end += this.queryIndex;
			var old = value.substr(this.queryIndex).split(split, 1)[0];
			value = value.substr(0, this.queryIndex) + input + value.substr(this.queryIndex + old.length);
			if (finish) {
				var tokens = value.split(this.options.separatorSplit).clean();
				if (!this.options.allowDupes) tokens = [].combine(tokens);
				var sep = this.options.separator;
				value = tokens.join(sep) + sep;
				end = value.length;
			}
		}
		this.observer.setValue(value);
		this.opted = value;
		if (finish || this.selectMode == 'pick') start = end;
		this.element.selectRange(start, end);
		this.fireEvent('onSelection', [this.element, this.selected, value, input]);
	},

	showChoices: function() {
		var match = this.options.choicesMatch, first = this.choices.getFirst(match);
		this.selected = this.selectedValue = null;
		if (this.relative) {
			var pos = this.element.getCoordinates(this.relative), width = this.options.choicesWidth || 'auto';
			this.choices.setStyles({
				'left': pos.left,
				'top': pos.bottom,
				'width': (width === true || width == 'inherit') ? pos.width : width
			});
		}
		if (!first) return;
		if (!this.visible) {
			this.visible = true;
			if (this.fx) this.fx.start(1);
			this.choices.setStyle('display', '');
			this.fix.show();
			this.fireEvent('onShow', [this.element, this.choices]);
		}
		if (this.options.selectFirst || this.typeAhead || first.inputValue == this.queryValue) this.choiceOver(first, this.typeAhead);
		var items = this.choices.getChildren(match), max = this.options.maxChoices;
		var styles = {'overflowY': 'hidden', 'height': ''};
		if (items.length > max) {
			var item = items[max - 1];
			styles.overflowY = 'scroll';
			styles.height = item.getCoordinates(this.choices).bottom;
		};
		this.choices.setStyles(styles);
	},

	hideChoices: function(clear) {
		if (!this.visible) return;
		this.visible = false;
		if (clear) {
			var value = this.element.value;
			if (this.options.forceSelect) value = this.opted;
			if (this.options.autoTrim) {
				value = value.split(this.options.separatorSplit).clean().join(this.options.separator);
			}
			this.observer.setValue(value);
		}
		this.observer.clear();
		var hide = function(){
			this.choices.setStyle('display', 'none');
			this.fix.hide();
		}.bind(this);
		if (this.fx) this.fx.start(0).chain(hide);
		else hide();
		this.fireEvent('onHide', [this.element, this.choices]);
	},

	prefetch: function() {
		var value = this.element.value, query = value;
		if (this.options.multiple) {
			var split = this.options.separatorSplit;
			var values = value.split(split);
			var index = this.element.getCaretPosition();
			var toIndex = value.substr(0, index).split(split);
			var last = toIndex.length - 1;
			index -= toIndex[last].length;
			query = values[last];
		}
		if (query.length < this.options.minLength) {
			this.hideChoices();
		} else {
			if (query === this.queryValue || (this.visible && query == this.selectedValue)) {
				if (this.visible) return false;
				this.showChoices();
			} else {
				this.queryValue = query;
				this.queryIndex = index;
				if (!this.fetchCached()) this.query();
			}
		}
		return true;
	},

	fetchCached: function() {
		return false;
		if (!this.options.cache
			|| !this.cached
			|| !this.cached.length
			|| this.cached.length >= this.options.maxChoices
			|| this.queryValue) return false;
		this.update(this.filter(this.cached));
		return true;
	},

	update: function(tokens) {
		this.choices.empty();
		this.cached = tokens;
		if (!tokens || !tokens.length) {
			this.hideChoices();
		} else {
			if (this.options.maxChoices < tokens.length && !this.options.overflow) tokens.length = this.options.maxChoices;
			tokens.each(this.options.injectChoice || function(token){
				var choice = new Element('li', {'html': this.markQueryValue(token)});
				choice.inputValue = token;
				this.addChoiceEvents(choice).inject(this.choices);
			}, this);
			this.showChoices();
		}
	},

	choiceOver: function(choice, selection) {
		if (!choice || choice == this.selected) return;
		if (this.selected) this.selected.removeClass('autocompleter-selected');
		this.selected = choice.addClass('autocompleter-selected');
		this.fireEvent('onSelect', [this.element, this.selected, selection]);
		if (!selection) return;
		this.selectedValue = this.selected.inputValue;
		if (this.options.overflow) {
			var coords = this.selected.getCoordinates(this.choices), margin = this.options.overflowMargin,
				top = this.choices.scrollTop, height = this.choices.offsetHeight, bottom = top + height;
			if (coords.top - margin < top && top) this.choices.scrollTop = Math.max(coords.top - margin, 0);
			else if (coords.bottom + margin > bottom) this.choices.scrollTop = Math.min(coords.bottom - height + margin, bottom);
		}
		if (this.selectMode) this.setSelection();
	},

	choiceSelect: function(choice) {
		if (choice) this.choiceOver(choice);
		this.setSelection(true);
		this.queryValue = false;
		this.hideChoices();
	},

	filter: function(tokens) {
		var regex = new RegExp(((this.options.filterSubset) ? '' : '^') + this.queryValue.escapeRegExp(), (this.options.filterCase) ? '' : 'i');
		return (tokens || this.tokens).filter(regex.test, regex);
	},

	/**
	 * markQueryValue
	 *
	 * Marks the queried word in the given string with <span class="autocompleter-queried">*</span>
	 * Call this i.e. from your custom parseChoices, same for addChoiceEvents
	 *
	 * @param		{String} Text
	 * @return		{String} Text
	 */
	markQueryValue: function(str) {
		return (!this.options.markQuery || !this.queryValue) ? str
			: str.replace(new RegExp('(' + ((this.options.filterSubset) ? '' : '^') + this.queryValue.escapeRegExp() + ')', (this.options.filterCase) ? '' : 'i'), '<span class="autocompleter-queried">$1</span>');
	},

	/**
	 * addChoiceEvents
	 *
	 * Appends the needed event handlers for a choice-entry to the given element.
	 *
	 * @param		{Element} Choice entry
	 * @return		{Element} Choice entry
	 */
	addChoiceEvents: function(el) {
		return el.addEvents({
			'mouseover': this.choiceOver.bind(this, [el]),
			'click': this.choiceSelect.bind(this, [el])
		});
	}
});

Autocompleter.Base.implement(new Events);
Autocompleter.Base.implement(new Options);

Autocompleter.Local = new Class({

	Extends: Autocompleter.Base,

	options: {
		minLength: 0,
		delay: 200
	},

	initialize: function(el, tokens, options) {
		this.parent(el, options);
		this.tokens = tokens;
	},

	query: function() {
		this.update(this.filter());
	}

});

Autocompleter.Ajax = {};

Autocompleter.Ajax.Base = new Class({

	Extends: Autocompleter.Base,

	options: {
		postVar: 'value',
		postData: {},
		ajaxOptions: {},
		onRequest: $empty,
		onComplete: $empty
	},

	initialize: function(el, options) {
		this.parent(el, options);
		var indicator = $(this.options.indicator);
		this.addEvents({
			'onRequest': indicator.setStyle.bind(indicator, ['display', '']),
			'onComplete': indicator.setStyle.bind(indicator, ['display', 'none'])
		}, true);
	},

	query: function(){
		var data = $unlink(this.options.postData);
		data[this.options.postVar] = this.queryValue;
		this.fireEvent('onRequest', [this.element, this.request, data, this.queryValue]);
		this.request.send({'data': data});
	},

	/**
	 * queryResponse - abstract
	 *
	 * Inherated classes have to extend this function and use arguments.callee.parent(resp)
	 *
	 * @param		{String} Response
	 */
	queryResponse: function() {
		this.fireEvent('onComplete', [this.element, this.request, this.response]);
	}

});

Autocompleter.Ajax.Json = new Class({

	Extends: Autocompleter.Ajax.Base,

	initialize: function(el, url, options) {
		this.parent(el, options);
		this.request = new Request.JSON($merge({
			'url': url,
			'link': 'cancel'
		}, this.options.ajaxOptions)).addEvent('onComplete', this.queryResponse.bind(this));
	},

	queryResponse: function(response) {
		this.parent();
		this.update(response);
	}

});

Autocompleter.Ajax.Xhtml = new Class({

	Extends: Autocompleter.Ajax.Base,

	initialize: function(el, url, options) {
		arguments.callee.parent(el, options);
		this.request = new Request.HTML($merge({
			'url': url,
			'link': 'cancel',
			'update': this.choices
		}, this.options.ajaxOptions)).addEvent('onComplete', this.queryResponse.bind(this));
	},

	queryResponse: function(response) {
		arguments.callee.parent();
		if (!response || !response.length) {
			this.hideChoices();
		} else {
			this.choices.getChildren(this.options.choicesMatch).each(this.options.injectChoice || function(choice) {
				var value = choice.innerHTML;
				choice.inputValue = value;
				choice.set('html', this.markQueryValue(value));
			}, this);
			this.showChoices();
		}

	}

});


var OverlayFix = new Class({

	initialize: function(el) {
		if (Browser.Engine.trident) {
			this.element = $(el).addEvent('trash', this.destroy.bind(this));
			this.relative = this.element.getOffsetParent();
			this.fix = new Element('iframe', {
				'frameborder': '0',
				'scrolling': 'no',
				'src': 'javascript:false;',
				'styles': {
					'position': 'absolute',
					'border': 'none',
					'display': 'none',
					'filter': 'progid:DXImageTransform.Microsoft.Alpha(opacity=0)'
				}
			}).inject(this.element, 'after');
		}
	},

	show: function() {
		if (this.fix) {
			this.fix.setStyles($extend(this.element.getCoordinates(this.relative), {
				'display': '',
				'zIndex': (this.element.getStyle('zIndex') || 1) - 1
			}));
		}
		return this;
	},

	hide: function() {
		if (this.fix) this.fix.setStyle('display', 'none');
		return this;
	},

	destroy: function() {
		this.fix = this.fix.destroy();
	}

});

Element.implement({

	getCaretPosition: function() {
		if (!Browser.Engine.trident) return this.selectionStart;
		this.focus();
		var work = document.selection.createRange();
		var all = this.createTextRange();
		work.setEndPoint('StartToStart', all);
		return work.text.length;
	},

	selectRange: function(start, end) {
		if (Browser.Engine.trident) {
			var range = this.createTextRange();
			range.collapse(true);
			range.moveEnd('character', end);
			range.moveStart('character', start);
			range.select();
		} else {
			this.focus();
			this.setSelectionRange(start, end);
		}
		return this;
	}

});

/**
 * Observer - Observe formelements for changes
 *
 * @version		1.0rc3
 *
 * @license		MIT-style license
 * @author		Harald Kirschner <mail [at] digitarald.de>
 * @copyright	Author
 */
var Observer = new Class({

	Implements: [Options, Events],

	options: {
		periodical: false,
		delay: 1000
	},

	initialize: function(el, onFired, options){
		this.setOptions(options);
		this.addEvent('onFired', onFired);
		this.element = $(el) || $$(el);
		this.value = this.element.get('value');
		if (this.options.periodical) this.timer = this.changed.periodical(this.options.periodical, this);
		else this.element.addEvent('keyup', this.changed.bind(this));
	},

	changed: function() {
		var value = this.element.get('value');
		if ($equals(this.value, value)) return;
		this.clear();
		this.value = value;
		this.timeout = this.onFired.delay(this.options.delay, this);
	},

	setValue: function(value) {
		this.value = value;
		this.element.set('value', value);
		return this.clear();
	},

	onFired: function() {
		this.fireEvent('onFired', [this.value, this.element]);
	},

	clear: function() {
		$clear(this.timeout || null);
		return this;
	}

});

var $equals = function(obj1, obj2) {
	return (obj1 == obj2 || JSON.encode(obj1) == JSON.encode(obj2));
};

// This code was written by Tyler Akins and has been placed in the
// public domain.  It would be nice if you left this header intact.
// Base64 code from Tyler Akins -- http://rumkin.com
// mootools version by [R]

var Base64 = new Hash({
	$keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
	encode: function(input){
    	var output = "";
	   	if (input == "") {
	    	return output;
	   	}
	   	var chr1, chr2, chr3;
	   	var enc1, enc2, enc3, enc4;
	   	var i = 0;
	   	do {
	    	chr1 = input.charCodeAt(i++);
	    	chr2 = input.charCodeAt(i++);
	    	chr3 = input.charCodeAt(i++);
			enc1 = chr1 >> 2;
	      	enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
	      	enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
	      	enc4 = chr3 & 63;
	      	if (isNaN(chr2)) {
	        	enc3 = enc4 = 64;
	      	} else if (isNaN(chr3)) {
	         	enc4 = 64;
	      	}
	      	output = output + Base64.$keyStr.charAt(enc1) + Base64.$keyStr.charAt(enc2) + Base64.$keyStr.charAt(enc3) + Base64.$keyStr.charAt(enc4);
	   	} while (i < input.length);
	   	return output;
	},

	decode: function(input){
    	var output = "";
		if (input == "") {
	    	return output;
		}
	   	var chr1, chr2, chr3;
	   	var enc1, enc2, enc3, enc4;
	   	var i = 0;
	   	// remove all characters that are not A-Z, a-z, 0-9, +, /, or =
	   	input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
	   	do {
	    	enc1 = Base64.$keyStr.indexOf(input.charAt(i++));
	    	enc2 = Base64.$keyStr.indexOf(input.charAt(i++));
	    	enc3 = Base64.$keyStr.indexOf(input.charAt(i++));
	    	enc4 = Base64.$keyStr.indexOf(input.charAt(i++));
	      	chr1 = (enc1 << 2) | (enc2 >> 4);
	      	chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
	      	chr3 = ((enc3 & 3) << 6) | enc4;
	      	output = output + String.fromCharCode(chr1);
	      	if (enc3 != 64) {
	        	output = output + String.fromCharCode(chr2);
	      	}
	      	if (enc4 != 64) {
	        	output = output + String.fromCharCode(chr3);
	      	}
	   } while (i < input.length);
	   return output;
	}
});

  /*********************************************************/
 /*   vlaCalendar version 2.1 for mootools release 1.2    */
/*********************************************************/

var vlaCalendar = new Class({
	'slideDuration': 500,
	'fadeDuration': 500,
	'transition': Fx.Transitions.Quart.easeOut,
	'startMonday': false,
	'filePath': 'inc/',
	'defaultView': 'month',
	'style': '',

	initialize: function(_container, _options) {
		//Add the provided options to this object by extending
		if(_options) $extend(this, _options);

		this.loading = false;
		this.container = _container = $(_container);
		var _class = this;

		//Insert the base into the container and initialize elements
		var  pars = 'defaultView='+ this.defaultView;
		if(this.picker) {
			if($type(this.prefillDate) == 'object' && this.getInputDate(this.prefillDate)) pars += '&pickedDate='+ this.getInputDate(this.prefillDate);
			if(this.linkWithInput) pars += '&gotoPickedDate=1';
		}
		this.u('base', pars, function() {
			_class.mainLoader = _container.getElement('div[class=loaderA]');
			_class.tempLoader = _container.getElement('div[class=loaderB]');
			_class.label 	  = _container.getElement('span[class=label]');
			_class.arrowLeft  = _container.getElement('div[class=arrowLeft]');
			_class.arrowRight = _container.getElement('div[class=arrowRight]');
			_class.initializeCalendarFunctions();

			//Prefill/load picker date elements
			if(_class.picker) {
				if($type(_class.prefillDate) == 'object' && _class.getInputDate(_class.prefillDate)) _class.pick(_class.prefillDate);
				else if(_class.prefillDate == true) _class.pick(JSON.decode(_class.label.getProperty('date')));
			}
		}, _container);
	},

	initializeCalendarFunctions: function() {
		this.resetArrows();

		//Retrieve data (label, timestamp etc) which are stored as a Json string in the table attribute summary
		var vars = JSON.decode(this.mainLoader.getElement('table').getProperty('summary'));
		var _class = this;

		//Change the label
		this.label.removeClass('noHover').set('html', vars.label)
			.onclick = vars.parent ? function() { _class.u(vars.parent, 'ts=' + vars.ts + '&parent=' + vars.current, function() { _class.fade() }) } : null;

		//Hide arrows if necessary and add arrow click events
		if(vars.hide_left_arrow) this.hideLeftArrow();
		else if(vars.hide_right_arrow) this.hideRightArrow();

		this.arrowLeft.onclick  = function() { _class.u(vars.current, 'ts=' + vars.pr_ts, function() { _class.slideLeft() }) }
		this.arrowRight.onclick = function() { _class.u(vars.current, 'ts=' + vars.nx_ts, function() { _class.slideRight() }) }

		//Add cell click events
		var clickables = this.mainLoader.getElements('td');
		switch(vars.current) {
			case 'month':
				if(this.picker) {
					clickables.each(function(_clickable) {
						_clickable.onclick = function() {
							_class.pick(JSON.decode(_clickable.getProperty('date')));
							_class.mainLoader.getElements('td').each(function(_clickable) { _clickable.removeClass('selected') });
							this.addClass('selected');
						}
					});
				}
				break;
			case 'year':
				clickables.each(function(_clickable) {
					_clickable.onclick = function() { _class.u('month', 'ts=' + _clickable.getProperty('ts'), function() { _class.fade() }) }
				});
				break;
			case 'decade':
				this.label.addClass('noHover');
				clickables.each(function(_clickable) {
					_clickable.onclick = function() { _class.u('year', 'ts=' + _clickable.getProperty('ts') + '&m_ts=' + _clickable.getProperty('m_ts'), function() { _class.fade() }) }
				});
				break;
		}
	},

	//Ajax updater function which handles all requests
	u: function(_url, _pars, _onComplete, _id) {
		if(!this.loading && !this.transitioning) {
			var _class = this;
			this.loading = true;
			var element = $(_id ? _id : this.tempLoader);
			_pars += '&picker=' + (this.picker ? 1 : 0) + '&startMonday=' + (this.startMonday ? 1 : 0) + '&style=' +  this.style;
			if(this.picker && this.getInputDate()) _pars += '&pickedDate='+ this.getInputDate();
			new Request({ method: 'post',
						  url: this.filePath + _url + '.php',
						  onComplete: function(data) { element.set('html', data); _onComplete(); _class.loading = false; }
						}).send(_pars);
		}
	},

	slideLeft: function() {
		var _class = this;
		this.transitioning = true;
		this.tempLoader.setStyle('opacity', 1).set('tween', { duration: this.slideDuration, transition: this.transition }).tween('margin-left', [-164, 0]);
		this.mainLoader.setStyle('opacity', 1).set('tween', { duration: this.slideDuration, transition: this.transition, onComplete: function() { _class.transitioning = false } })
			.tween('margin-left', [0, 164]);
		this.switchLoaders();
	},

	slideRight: function() {
		var _class = this;
		this.transitioning = true;
		this.mainLoader.setStyle('opacity', 1).set('tween', { duration: this.slideDuration, transition: this.transition }).tween('margin-left', [0, -164]);
		this.tempLoader.setStyle('opacity', 1).set('tween', { duration: this.slideDuration, transition: this.transition, onComplete: function() { _class.transitioning = false } })
			.tween('margin-left', [164, 0]);
		this.switchLoaders();
	},

	fade: function(overRuleTrans) {
		var _class = this;
		this.transitioning = overRuleTrans ? false : true;
		this.tempLoader.setStyles({'opacity': 0, 'margin-left': 0});
		this.mainLoader.set('tween', { duration: this.fadeDuration, transition: this.transition}).fade('out');
		this.tempLoader.set('tween', { duration: this.fadeDuration, transition: this.transition,
			onComplete: function() {
					_class.tempLoader.setStyles({'opacity': 1, 'margin-left': -999});
					_class.transitioning = false;
				}
			}).fade('in');
		this.switchLoaders();
	},

	switchLoaders: function() {
		this.mainLoader = this.mainLoader.className == 'loaderA' ? this.container.getElement('div[class=loaderB]') : this.container.getElement('div[class=loaderA]');
		this.tempLoader = this.tempLoader.className == 'loaderA' ? this.container.getElement('div[class=loaderB]') : this.container.getElement('div[class=loaderA]');
		this.initializeCalendarFunctions();
	},

	resetArrows: function() {
		this.arrowLeft.setStyle('visibility', 'visible');
		this.arrowRight.setStyle('visibility', 'visible');
	},

	hideLeftArrow: function() {
		this.arrowLeft.setStyle('visibility', 'hidden');
	},

	hideRightArrow: function() {
		this.arrowRight.setStyle('visibility', 'hidden');
	}
});

var vlaDatePicker = new Class({
	Extends: vlaCalendar,

	'separateInput': false,
	'prefillDate': true,
	'linkWithInput': true,
	'leadingZero': true,
	'twoDigitYear': false,
	'separator': '/',
	'format': 'd/m/y',
	'openWith': null,
	'alignX': 'right',
	'alignY': 'inputTop',
	'offset': { 'x': 0, 'y': 0 },
	'style': '',
	'ieTransitionColor' : '#ffffff',
	'toggleDuration': 350,

	initialize: function(_element, _options) {
		//Add the provided options to this object by extending
		if(_options) $extend(this, _options);

		this.element = $(_element);
		if(!this.element) throw 'No (existing) element to create a datepicker for specified: new vlaDatePicker(ELEMENT, [options])';

		//Check if the user wants multiple input
		if(this.separateInput) {
			this.element.day   = this.element.getElement('input[name='+ this.separateInput.day +']');
			this.element.month = this.element.getElement('input[name='+ this.separateInput.month +']');
			this.element.year  = this.element.getElement('input[name='+ this.separateInput.year +']');
		}

		//Create the picker and calendar and inject in in the body
		this.picker = new Element('div', { 'class': 'vlaCalendarPicker' + (this.style != '' ? ' ' + this.style : '') }).injectTop($(document.body));
		this.pickerContent = new Element('div', { 'class': 'pickerBackground' }).injectTop(this.picker);
		this.parent(this.pickerContent);

		//Add events for showing and hiding the picker
		var _class = this;
		(this.openWith ? $(this.openWith) : this.element)
			.addEvent('focus',  function() { _class.show(); })
			.addEvent('click',  function() { _class.openWith ? _class.toggle() : _class.show() })
			.addEvent('change', function() { _class.hide(); });

		//If the datepicker is visible an outside click makes it hide
		document.addEvent('mousedown', function(e) { if(_class.outsideHide && _class.outsideClick(e, _class.picker)) _class.hide() });

		//linkWithInput
		if(this.linkWithInput) {
			if(this.separateInput) {
				this.element.day.addEvent('keyup',  function() { _class.linkedUpdate() });
				this.element.month.addEvent('keyup',  function() { _class.linkedUpdate() });
				this.element.year.addEvent('keyup',  function() { _class.linkedUpdate() });
			} else {
				this.element.addEvent('keyup',  function() { _class.linkedUpdate() });
			}
		}

		this.visible = false;
		this.outsideHide = false;
	},

	//Position the picker
	position: function() {
		var top, left;

		switch(this.alignX) {
			case 'left':
				left = this.element.getLeft();
				break;
			case 'center':
				var pickerMiddle = this.pickerContent.getStyle('width').toInt() / 2;
				if(pickerMiddle == 0) pickerMiddle = 83;
				left = this.element.getLeft() + (this.element.getSize().x / 2) - pickerMiddle -
						((parseInt(this.pickerContent.getStyle('padding-left')) + parseInt(this.pickerContent.getStyle('padding-right'))) / 2);
				break;
			case 'right': default:
				left = this.element.getLeft() + this.element.getSize().x;
				break;
		}

		switch(this.alignY) {
			case 'bottom':
				top = this.getPos(this.element).y + this.element.getSize().y;
				break;
			case 'top':
				top = this.getPos(this.element).y - parseInt(this.pickerContent.getStyle('height')) -
					(parseInt(this.pickerContent.getStyle('padding-top')) + parseInt(this.pickerContent.getStyle('padding-bottom')));
				break;
			case 'inputTop': default:
				top = this.getPos(this.element).y;
		}

		if(this.isNumber(this.offset.x)) left += this.offset.x;
		if(this.isNumber(this.offset.y)) top += this.offset.y;

		this.picker.setStyles({ 'top': top, 'left': left });
	},

	show: function() {
		this.position();
		if(!this.visible) {
			this.visible = true;
			var _class = this;
			this.picker.setStyles({ 'opacity': 0, 'display': 'inline' });
			if(Browser.Engine.trident5) this.picker.setStyle('background-color', this.ieTransitionColor); //Ugly transition fix for IE7
			this.picker.set('tween', { onComplete: function() {
					if(Browser.Engine.trident5) _class.picker.setStyle('background-color', 'transparent');
					_class.outsideHide = true;
				}, duration: this.toggleDuration }).fade('in');
		}
	},

	hide: function() {
		if(this.visible) {
			this.visible = false;
			var _class = this;
			if(Browser.Engine.trident5) this.picker.setStyle('background-color', this.ieTransitionColor); //Ugly transition fix for IE7
			this.picker.set('tween', { onComplete: function() { _class.picker.setStyle('display', 'none'); _class.outsideHide = false; }, duration: this.toggleDuration }).fade('out');
		}
	},

	toggle: function() {
		if(this.visible) this.hide();
		else this.show();
	},

	pick: function(_date) {
		if(this.leadingZero) {
			if(_date.day < 10)   _date.day = '0' + _date.day;
			if(_date.month < 10) _date.month = '0' + _date.month;
		}
		if(this.twoDigitYear) _date.year = _date.year.toString().substring(2, 4);

		if(this.separateInput) {
			if(this.element.day)   this.element.day.set('value', _date.day);
			if(this.element.month) this.element.month.set('value', _date.month);
			if(this.element.year)  this.element.year.set('value', _date.year);
			this.hide();
		} else {
			switch(this.format) {
				case "m/d/y": this.element.set('value', _date.month + this.separator + _date.day + this.separator + _date.year); break;
				case "y/m/d": this.element.set('value', _date.year + this.separator + _date.month + this.separator + _date.day); break;
				case "y/d/m": this.element.set('value', _date.year + this.separator +  _date.day + this.separator + _date.month); break;
				case "d/m/y": default: this.element.set('value', _date.day + this.separator + _date.month + this.separator + _date.year);
			}
			this.hide();
		}
	},

	getInputDate: function(_date) {
		var day, month, year;

		if(_date) {
			day = _date.day;
			month = _date.month;
			year = _date.year;
		} else if(this.separateInput) {
			day = this.element.day.get('value').toInt();
			month = this.element.month.get('value').toInt();
			year = this.element.year.get('value').toInt();
		} else {
			var date = this.element.get('value').split(this.separator);
			if(date.length != 3) return null;
			switch(this.format) {
				case "m/d/y": day = date[1]; month = date[0]; year = date[2]; break;
				case "y/m/d": day = date[2]; month = date[1]; year = date[0]; break;
				case "y/d/m": day = date[1]; month = date[2]; year = date[0]; break;
				case "d/m/y": default: day = date[0]; month = date[1]; year = date[2];
			}
		}

		if( !this.isNumber(day) || !this.isNumber(month) || !this.isNumber(year) ||	day == 0 || month == 0 || year == '0' ||
		    (this.twoDigitYear && year > 99) || (!this.twoDigitYear && year < 1979) || (!this.twoDigitYear && year > 2030) || month > 12 || day > 31 ) return null;

		if(this.twoDigitYear && this.isNumber(year) && year < 100) {
			year = year.toInt();
			if(year < 10) year = '200'+  year;
			else if(year < 70) year = '20'+  year;
			else if(year > 69) year = '19'+  year;
			else year = new Date().getFullYear();
		}

		return day +'/'+ month +'/'+ year;
	},

	//This function is being called on keyup event if linkWithInput is set to true and when a date is picked
	//If the full date is inserted the picker will change itself to that specific date (month view)
	linkedUpdate: function() {
		var _class = this;
		var date = this.getInputDate();
		if(date && this.pickedDate != date) {
			this.u('month', 'gotoPickedDate=1', function() { _class.fade(true) });
			this.pickedDate = date;
		}
	},

	outsideClick: function(_event, _element) {
		var mousePos = this.getMousePos(_event);
		var elementData = _element.getCoordinates();
		return (mousePos.x > elementData.left && mousePos.x < (elementData.left + elementData.width)) &&
			   (mousePos.y > elementData.top  && mousePos.y < (elementData.top + elementData.height)) ? false : true;
	},

	getMousePos: function(_event) {
		if(document.all) {
			return { 'x': window.event.clientX + window.getScrollLeft(),
					 'y': window.event.clientY + window.getScrollTop() };
		} else {
			return { 'x': _event.page['x'],
					 'y': _event.page['y'] };
		}
	},

	isNumber: function(_number) {
		if(_number == '') return false;
		return (_number >= 0) || (_number < 0) ? true : false;
	},

	//Retrieving positition funtions (like getCoordinates, getTop etc) don't seem to return correct values in some situations in mootools 1.2;
	//Opera returns wrong values, IE returns too small values. This function returns the correct coordinates.
	getPos: function(_element) {
		var x, y = 0;
		if(_element.offsetParent) {
			do {
				x += _element.offsetLeft;
				y += _element.offsetTop;
			} while(_element = _element.offsetParent);
		} else if(_element.x) {
			x += _element.x;
			y += _element.y;
		}
		return { 'x': x, 'y': y };
	}
});

/**
 * HistoryManager
 *
 * Observes back/forward button usage and saves states
 * for registered modules into the hash. This allows to
 * bookmark specific states for an application.
 *
 * Mootools 1.2 compatible version, updated by aNobii Ltd. (anobii.com)
 * @version		1.2
 *
 * @see			Events, Options
 *
 * @license		MIT License
 * @author		Harald Kirschner <mail [at] digitarald.de>
 * @1.2author	aNobii Ltd. <contact [at] anobii.com>
 * @copyright	2008 Author
 */


var HistoryManager = new Class ( {

	/**
	 * Default options - Can be overridden with setOptions
	 *
	 * observeDelay: Duration for checking the state, default 100ms
	 * stateSeparator: Seperator for module-state join, default ';'
	 * iframeSrc: Scr for IE6/7 iframe, must exist on server!
	 * onStart: Fires on start
	 * onRegister: Fires on register
	 * onUnregister: Fires on unregister
	 * onUpdate: Fires when state changes from ...
	 * onStateChange: ... module changes
	 * onObserverChange: ... history change
	 */
	options: {
		observeDelay: 100,
		stateSeparator: ';',
		iframeSrc: 'blank.html',
		onStart: Class.empty,
		onRegister: Class.empty,
		onUnregister: Class.empty,
		onStart: Class.empty,
		onUpdate: Class.empty,
		onStateChange: Class.empty,
		onObserverChange: Class.empty
	},

	/**
	 * Default options for register
	 *
	 * defaults: Default values array, initially empty.
	 * regexpParams: When regexp is a String, this is the second argument for new RegExp.
	 * skipDefaultMatch: default true; When true onGenerate is not called when current values are similar to the default values.
	 */
	dataOptions: {
		skipDefaultMatch: true,
		defaults: [],
		regexpParams: ''
	},

	/**
	 * Constructur - Class.initialize
	 *
	 * Options:
	 *  - observeDelay: duration in ms, default 100 - BackBuddy observe the hash for changes periodical
	 *  - stateSeparator: char, default ';' - Separator for multiple module-states in the hash
	 *  - iframeSrc: string, default 'blank.html' - File for the iframe (IE6/7), must exist on the server!
	 *  - Events: onStart, onRegister, onStart, onUpdate, onStateChange, onObserverChange
	 *
	 * @return	this
	 *
	 * @param	{Object} options
	 */
	initialize: function(options) {
		if (this.modules) return this;
		this.setOptions(options);
		this.modules = $H({});
		this.count = history.length;
		this.states = [];
		this.states[this.count] = this.getHash();
		this.state = null;
		return this;
	},

	/**
	 * Start - Check hash and start observer
	 *
	 * Call start after registering ALL modules. This start the observer,
	 * reads the state from the hash and calls onMatch for effected modules.
	 *
	 * @return	this
	 *
	 */
	start: function() {
		this.observe.periodical(this.options.observeDelay, this);
		this.started = true;
		this.observe();
		this.update();
		this.fireEvent('onStart', [this.state]);
		return this;
	},

	/**
	 * Registers a module
	 *
	 * @return	{Object} Object with shortcuts for setValues, setValue, generate and unregister
	 *
	 * @param	{String} Module key
	 * @param	{RegExp}/{String} Regular expression that matches the string updated from onGenerate
	 * @param	{Function} Will be called when the regexp matches, with the new values as argument.
	 * @param	{Function} Should return the string for the state string, values are first argument
	 * @param	{Array} default values, the input values given to onMatch and onGenerate will be complemented with these
	 * @param	{Object} (optional) options
	 */
	register: function(key, defaults, onMatch, onGenerate, regexp, options) {
		if (!this.modules) this.initialize();
		var data = $merge(this.dataOptions, options || {}, {
			defaults: defaults,
			onMatch: onMatch,
			onGenerate: onGenerate,
			regexp: regexp
		});
		data.regexp = data.regexp || key + '-([\\w_-]*)';
		if (typeof data.regexp == 'string') data.regexp = new RegExp(data.regexp, data.regexpParams);
		data.onGenerate = data.onGenerate || function(values) { return key + '-' + values[0]; };
//		alert (defaults);
		data.values = $A(data.defaults);
		this.modules.set(key, data);
		this.fireEvent('onUnregister', [key, data]);
		return {
			setValues: function(values) {
				return this.setValues(key, values);
			}.bind(this),
			setValue: function(index, value) {
				return this.setValue(key, index, value);
			}.bind(this),
			generate: function(values) {
				return this.generate(key, values);
			}.bind(this),
			unregister: function() {
				return this.unregister(key);
			}.bind(this)
		};
	},

	/**
	 * unregister - Removes an module from the
	 *
	 * @param	{String} Module key
	 */
	unregister: function(key) {
		this.fireEvent('onRegister', [key]);
		this.modules.remove(key);
	},

	/**
	 * setValues - Set all values new, updates new state
	 *
	 * @param	{String} Module key
	 * @param	{Object} Complete values
	 */
	setValues: function(key, values) {
		var data = this.modules.get(key);
		if (!data || data.values.isSimilar(values)) return this;
		data.values = values;
		this.update();
		return this;
	},

	/**
	 * setValue - Set one value, updates new state
	 *
	 * @param	{String} Module key
	 * @param	{Number} Value index
	 * @param	{Object} Value
	 */
	setValue: function(key, index, value) {
		var data = this.modules.get(key);
		if (!data || (data.values[index] && data.values[index] == value)) return this;
		data.values[index] = value;
		this.update();
		return this;
	},

	/**
	 * generate - Generates a hash from the given
	 *
	 * @param	{String} Module key
	 * @param	{Number} Value index
	 * @param	{Object} Value
	 */
	generate: function(key, values) {
		var data = this.modules.get(key);
		var current = $A(data.values);
		data.values = values;
		var state = this.generateState();
		data.values = current;
		return '#' + state;
	},

	observe: function() {
		if (this.timeout) return;
		var state = this.getState();
		if (this.state == state) return;
		if ((Browser.Engine.trident || Browser.Engine.webkit419) && (this.state !== null)) this.setState(state, true);
		else this.state = state;

		this.modules.each(function(data, key) {
			var bits = state.match(data.regexp);
			if (bits) {
				bits.splice(0, 1);
				bits.complement(data.defaults);
				if (!bits.isSimilar(data.defaults)) data.values = bits;
			} else data.values = $A(data.defaults);
			data.onMatch(data.values, data.defaults);
		});
		this.fireEvent('onStateChange', [state]).fireEvent('onObserverChange', [state]);
	},

	generateState: function() {
		var state = [];
		this.modules.each(function(data, key) {
			if (data.skipDefaultMatch && data.values.isSimilar(data.defaults)) return;
			state.push(data.onGenerate(data.values));
		});
		return state.join(this.options.stateSeparator);
	},

	update: function() {
		if (!this.started) return this;
		var state = this.generateState();
		if ((!this.state && !state) || (this.state == state)) return this;
		this.setState(state);
		this.fireEvent('onStateChange', [state]).fireEvent('onUpdate', [state]);
		return this;
	},

	observeTimeout: function() {
		if (this.timeout) this.timeout = $clear(this.timeout);
		else this.timeout = this.observeTimeout.delay(200, this);
	},

	getHash: function() {
		var href = window.location.href;
		var pos = href.indexOf('#') + 1;
		return (pos) ? href.substr(pos) : '';
	},

	getState: function() {
		var state = this.getHash();
		if (this.iframe) {
			var doc = this.iframe.contentWindow.document;
			if (doc && doc.body.id == 'state') {
				var istate = doc.body.innerText;
				if (this.state == state) return istate;
				this.istateOld = true;
			} else return this.istate;
		}
		if (Browser.Engine.webkit419 && history.length != this.count) {
			this.count = history.length;
			return $pick(this.states[this.count - 1], state);
		}
		return state;
	},

	setState: function(state, fix) {
		state = $pick(state, '');
		if (Browser.Engine.webkit419) {
			if (!this.form) this.form = new Element('form', {method: 'get'}).injectInside(document.body);
			this.count = history.length;
			this.states[this.count] = state;
			this.observeTimeout();
			this.form.setProperty('action', '#' + state).submit();
		} else top.location.hash = state || '#';
		if (Browser.Engine.trident && (!fix || this.istateOld)) {
			if (!this.iframe) {
				this.iframe = new Element('iframe', {
					src: this.options.iframeSrc,
					styles: {
						'visibility': 'hidden'
					},
					width: '1px',
					height: '1px'
				}).injectInside(document.body);
				this.istate = this.state;
			}
			try {
				var doc = this.iframe.contentWindow.document;
				doc.open();
				doc.write('<html><body id="state">' + state + '</body></html>');
				doc.close();
				this.istateOld = false;
			} catch(e) {};
		}
		this.state = state;
	},

	extend: $extend
});
HistoryManager.implement(Events.prototype);
HistoryManager.implement(Options.prototype);


/**
 * Extends Array with 2 helpers: isSimilar(array) and complement(array)
 *
 */
Array.prototype.isSimilar = function (array){
		return (this.toString() == array.toString());
}

Array.prototype.complement = function (array){
		for (var i = 0, j = this.length; i < j; i++) this[i] = $pick(this[i], array[i] || null);
		return this;
}
/*
Array.extend({

	/**
	 * isSimilar - Returns true for similar arrays, type-insensitive
	 *
	 * @example
	 *  [1].isSimilar(['1']) == true
	 *  [1, 2].isSimilar([1, false]) == false
	 *
	 * @return	{Boolean}
	 * @param	{Object} Array
	 /
	isSimilar: function(array) {
		return (this.toString() == array.toString());
	},

	/**
	 * complement - Fills up empty array values from another array, length is the same
	 *
	 * @example
	 *  [1, null].complement([3, 4]) == [1, 4]
	 *	[undefined, '1'].complement([2, 3, 4]) == [2, '1']

	 * @return	{Array} this
	 * @param	{Object} Array
	 /
	complement: function(array) {
		for (var i = 0, j = this.length; i < j; i++) this[i] = $pick(this[i], array[i] || null);
		return this;
	}
});
*/


/*Copyright (c) 2008 Andrew Gleave, Tangerine Smash

MIT licence

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 ImageScrubber= new Class({
    Implements: [Options],
    options: {
        reset: true
    },
    initialize: function(element, obj, options) {
        this.setOptions(options);
        this.element = $(element);
        this.obj = obj;
        this.size = this.element.getStyle('width').toInt();
        this.bound = {
            'setup': this.setup.bind(this),
            'swap': this.swapImage.bind(this)
        };
        this.loadData();
    },
    loadData: function() {
		this.element.empty();
		LBP.toggleLoading(true);
		this.imageList = this.obj;
		var tempImageList = this.imageList;
		this.images = new Asset.images(
			tempImageList.map(
				function(t, i) {
					return t.image;
				}
			),
			{
				onComplete: this.bound.setup
			}
		);
		LBP.toggleLoading(false);
    },
    setup: function() {
        this.element.empty();
        this.position = this.element.getPosition();
        this.ratio = (this.size / this.images.length).round().limit(1, this.size);
        this.offset = (this.ratio / 2).round();
        this.images[0].inject(this.element);
        this.element.addEvent('mousemove', this.bound.swap);
        if(this.options.reset)
            this.element.addEvent('mouseleave', function(event) {
                this.setImage(0);
            }.bind(this));
        this.oldIndex = 0;
    },
    setImage: function(index) {
        this.element.empty();
        var anchor = new Element('a', {
            'href': this.imageList[index].link
        });
        this.images[index].inject(anchor);
        anchor.inject(this.element);
        this.oldIndex = index;
    },
    swapImage: function(event) {
        var relX = ((event.client.x - this.position.x) - this.offset).limit(0, this.size);
        var index = (relX / this.ratio).round().limit(0, this.images.length - 1);
        if(index != this.oldIndex) this.setImage(index);
    }
});

/***
 * MooRainbow
 *
 * @version		1.2b1
 * @license		MIT-style license
 * @author		Djamil Legato (w00fz) - < w00fzIT [at] gmail.com >
 * @infos		http://moorainbow.woolly-sheep.net
 * @copyright	Author
 *
 *
 */

var MooRainbow = new Class({
	Implements: [Options, Events],
	options: {
		id: 'mooRainbow',
		prefix: 'moor-',
		imgPath: 'images/',
		startColor: [255, 0, 0],
		wheel: false,
		onComplete: Class.empty,
		onChange: Class.empty,
		selectText: 'Select'
	},

	initialize: function(el, options) {
		this.element = $(el); if (!this.element) return;
		this.setOptions(options);

		this.sliderPos = 0;
		this.pickerPos = {x: 0, y: 0};
		this.backupColor = this.options.startColor;
		this.currentColor = this.options.startColor;
		this.sets = {
			rgb: [],
			hsb: [],
			hex: []
		};
		this.pickerClick = this.sliderClick  = false;
		if (!this.layout) this.doLayout();
		this.OverlayEvents();
		this.sliderEvents();
		this.backupEvent();
		if (this.options.wheel) this.wheelEvents();
		this.element.addEvent('click', function(e) { this.toggle(e); }.bind(this));

		this.layout.overlay.setStyle('background-color', this.options.startColor.rgbToHex());
		this.layout.backup.setStyle('background-color', this.backupColor.rgbToHex());

		this.pickerPos.x = this.snippet('curPos').l + this.snippet('curSize', 'int').w;
		this.pickerPos.y = this.snippet('curPos').t + this.snippet('curSize', 'int').h;

		this.manualSet(this.options.startColor);

		this.pickerPos.x = this.snippet('curPos').l + this.snippet('curSize', 'int').w;
		this.pickerPos.y = this.snippet('curPos').t + this.snippet('curSize', 'int').h;
		this.sliderPos = this.snippet('arrPos') - this.snippet('arrSize', 'int');

		if (Browser.Engine.webkit) this.hide();
	},

	toggle: function() {
		this[this.visible ? 'hide' : 'show']()
	},

	show: function() {
		this.rePosition();
		this.layout.setStyle('display', 'block');
		this.visible = true;
	},

	hide: function() {
		this.layout.setStyles({'display': 'none'});
		this.visible = false;
	},

	manualSet: function(color, type) {
		if (!type || (type != 'hsb' && type != 'hex')) type = 'rgb';
		var rgb, hsb, hex;

		if (type == 'rgb') { rgb = color; hsb = color.rgbToHsb(); hex = color.rgbToHex(); }
		else if (type == 'hsb') { hsb = color; rgb = color.hsbToRgb(); hex = rgb.rgbToHex(); }
		else { hex = color; rgb = color.hexToRgb(true); hsb = rgb.rgbToHsb(); }

		this.setMooRainbow(rgb);
		this.autoSet(hsb);
	},

	autoSet: function(hsb) {
		var curH = this.snippet('curSize', 'int').h;
		var curW = this.snippet('curSize', 'int').w;
		var oveH = this.layout.overlay.height;
		var oveW = this.layout.overlay.width;
		var sliH = this.layout.slider.height;
		var arwH = this.snippet('arrSize', 'int');
		var hue;

		var posx = Math.round(((oveW * hsb[1]) / 100) - curW);
		var posy = Math.round(- ((oveH * hsb[2]) / 100) + oveH - curH);

		var c = Math.round(((sliH * hsb[0]) / 360)); c = (c == 360) ? 0 : c;
		var position = sliH - c + this.snippet('slider') - arwH;
		hue = [this.sets.hsb[0], 100, 100].hsbToRgb().rgbToHex();

		this.layout.cursor.setStyles({'top': posy, 'left': posx});
		this.layout.arrows.setStyle('top', position);
		this.layout.overlay.setStyle('background-color', hue);
		this.sliderPos = this.snippet('arrPos') - arwH;
		this.pickerPos.x = this.snippet('curPos').l + curW;
		this.pickerPos.y = this.snippet('curPos').t + curH;
	},

	setMooRainbow: function(color, type) {
		if (!type || (type != 'hsb' && type != 'hex')) type = 'rgb';
		var rgb, hsb, hex;

		if (type == 'rgb') { rgb = color; hsb = color.rgbToHsb(); hex = color.rgbToHex(); }
		else if (type == 'hsb') { hsb = color; rgb = color.hsbToRgb(); hex = rgb.rgbToHex(); }
		else { hex = color; rgb = color.hexToRgb(); hsb = rgb.rgbToHsb(); }

		this.sets = {
			rgb: rgb,
			hsb: hsb,
			hex: hex
		};

		if (!$chk(this.pickerPos.x))
			this.autoSet(hsb);

		this.RedInput.value = rgb[0];
		this.GreenInput.value = rgb[1];
		this.BlueInput.value = rgb[2];
		this.HueInput.value = hsb[0];
		this.SatuInput.value =  hsb[1];
		this.BrighInput.value = hsb[2];
		this.hexInput.value = hex;

		this.currentColor = rgb;

		this.chooseColor.setStyle('background-color', rgb.rgbToHex());
	},

	parseColors: function(x, y, z) {
		var s = Math.round((x * 100) / this.layout.overlay.width);
		var b = 100 - Math.round((y * 100) / this.layout.overlay.height);
		var h = 360 - Math.round((z * 360) / this.layout.slider.height) + this.snippet('slider') - this.snippet('arrSize', 'int');
		h -= this.snippet('arrSize', 'int');
		h = (h >= 360) ? 0 : (h < 0) ? 0 : h;
		s = (s > 100) ? 100 : (s < 0) ? 0 : s;
		b = (b > 100) ? 100 : (b < 0) ? 0 : b;

		return [h, s, b];
	},

	OverlayEvents: function() {
		var lim, curH, curW, inputs;
		curH = this.snippet('curSize', 'int').h;
		curW = this.snippet('curSize', 'int').w;
		inputs = $A(this.arrRGB).concat(this.arrHSB, this.hexInput);

		document.addEvent('click', function() {
			if(this.visible) this.hide(this.layout);
		}.bind(this));

		inputs.each(function(el) {
			if(el) {
				el.addEvent('keydown', this.eventKeydown.bindWithEvent(this, el));
				el.addEvent('keyup', this.eventKeyup.bindWithEvent(this, el));
			}
		}, this);
		[this.element, this.layout].each(function(el) {
			el.addEvents({
				'click': function(e) { new Event(e).stop();},
				'keyup': function(e) {
					e = new Event(e);
					if(e.key == 'esc' && this.visible) this.hide(this.layout);
				}.bind(this)
			}, this);
		}, this);

		lim = {
			x: [0 - curW, (this.layout.overlay.width - curW)],
			y: [0 - curH, (this.layout.overlay.height - curH)]
		};

		this.layout.drag = new Drag(this.layout.cursor, {
			limit: lim,
			onStart: this.overlayDrag.bind(this),
			onDrag: this.overlayDrag.bind(this),
			snap: 0
		});

		this.layout.overlay2.addEvent('mousedown', function(e){
			e = new Event(e);
			this.layout.cursor.setStyles({
				'top': e.page.y - this.layout.overlay.getPosition().y - curH,
				'left': e.page.x - this.layout.overlay.getPosition().x - curW
			});
			this.overlayDrag();
			this.layout.drag.start(e);
		}.bind(this));

		this.okButton.addEvent('click', function() {
			if(this.currentColor == this.options.startColor) {
				this.hide();
				this.fireEvent('onComplete', [this.sets, this]);
			}
			else {
				this.backupColor = this.currentColor;
				this.layout.backup.setStyle('background-color', this.backupColor.rgbToHex());
				this.hide();
				this.fireEvent('onComplete', [this.sets, this]);
			}
		}.bind(this));
	},

	overlayDrag: function() {
		var curH = this.snippet('curSize', 'int').h;
		var curW = this.snippet('curSize', 'int').w;
		this.pickerPos.x = this.snippet('curPos').l + curW;
		this.pickerPos.y = this.snippet('curPos').t + curH;

		this.setMooRainbow(this.parseColors(this.pickerPos.x, this.pickerPos.y, this.sliderPos), 'hsb');
		this.fireEvent('onChange', [this.sets, this]);
	},

	sliderEvents: function() {
		var arwH = this.snippet('arrSize', 'int'), lim;

		lim = [0 + this.snippet('slider') - arwH, this.layout.slider.height - arwH + this.snippet('slider')];
		this.layout.sliderDrag = new Drag(this.layout.arrows, {
			limit: {y: lim},
			modifiers: {x: false},
			onStart: this.sliderDrag.bind(this),
			onDrag: this.sliderDrag.bind(this),
			snap: 0
		});

		this.layout.slider.addEvent('mousedown', function(e){
			e = new Event(e);

			this.layout.arrows.setStyle(
				'top', e.page.y - this.layout.slider.getPosition().y + this.snippet('slider') - arwH
			);
			this.sliderDrag();
			this.layout.sliderDrag.start(e);
		}.bind(this));
	},

	sliderDrag: function() {
		var arwH = this.snippet('arrSize', 'int'), hue;

		this.sliderPos = this.snippet('arrPos') - arwH;
		this.setMooRainbow(this.parseColors(this.pickerPos.x, this.pickerPos.y, this.sliderPos), 'hsb');
		hue = [this.sets.hsb[0], 100, 100].hsbToRgb().rgbToHex();
		this.layout.overlay.setStyle('background-color', hue);
		this.fireEvent('onChange', [this.sets, this]);
	},

	backupEvent: function() {
		this.layout.backup.addEvent('click', function() {
			this.manualSet(this.backupColor);
			this.fireEvent('onChange', [this.sets, this]);
		}.bind(this));
	},

	wheelEvents: function() {
		var arrColors = $A(this.arrRGB).extend(this.arrHSB);

		arrColors.each(function(el) {
			el.addEvents({
				'mousewheel': this.eventKeys.bindWithEvent(this, el),
				'keydown': this.eventKeys.bindWithEvent(this, el)
			});
		}, this);

		[this.layout.arrows, this.layout.slider].each(function(el) {
			el.addEvents({
				'mousewheel': this.eventKeys.bindWithEvent(this, [this.arrHSB[0], 'slider']),
				'keydown': this.eventKeys.bindWithEvent(this, [this.arrHSB[0], 'slider'])
			});
		}, this);
	},

	eventKeys: function(e, el, id) {
		var wheel, type;
		id = (!id) ? el.id : this.arrHSB[0];

		if (e.type == 'keydown') {
			if (e.key == 'up') wheel = 1;
			else if (e.key == 'down') wheel = -1;
			else return;
		} else if (e.type == Element.Events.mousewheel.base) wheel = (e.wheel > 0) ? 1 : -1;

		if (this.arrRGB.contains(el)) type = 'rgb';
		else if (this.arrHSB.contains(el)) type = 'hsb';
		else type = 'hsb';

		if (type == 'rgb') {
			var rgb = this.sets.rgb, hsb = this.sets.hsb, prefix = this.options.prefix, pass;
			var value = (el.value.toInt() || 0) + wheel;
			value = (value > 255) ? 255 : (value < 0) ? 0 : value;

			switch(el.className) {
				case prefix + 'rInput': pass = [value, rgb[1], rgb[2]];	break;
				case prefix + 'gInput': pass = [rgb[0], value, rgb[2]];	break;
				case prefix + 'bInput':	pass = [rgb[0], rgb[1], value];	break;
				default : pass = rgb;
			}
			this.manualSet(pass);
			this.fireEvent('onChange', [this.sets, this]);
		} else {
			var rgb = this.sets.rgb, hsb = this.sets.hsb, prefix = this.options.prefix, pass;
			var value = (el.value.toInt() || 0) + wheel;

			if (el.className.test(/(HueInput)/)) value = (value > 359) ? 0 : (value < 0) ? 0 : value;
			else value = (value > 100) ? 100 : (value < 0) ? 0 : value;

			switch(el.className) {
				case prefix + 'HueInput': pass = [value, hsb[1], hsb[2]]; break;
				case prefix + 'SatuInput': pass = [hsb[0], value, hsb[2]]; break;
				case prefix + 'BrighInput':	pass = [hsb[0], hsb[1], value]; break;
				default : pass = hsb;
			}

			this.manualSet(pass, 'hsb');
			this.fireEvent('onChange', [this.sets, this]);
		}
		e.stop();
	},

	eventKeydown: function(e, el) {
		var n = e.code, k = e.key;

		if 	((!el.className.test(/hexInput/) && !(n >= 48 && n <= 57)) &&
			(k!='backspace' && k!='tab' && k !='delete' && k!='left' && k!='right'))
		e.stop();
	},

	eventKeyup: function(e, el) {
		var n = e.code, k = e.key, pass, prefix, chr = el.value.charAt(0);

		if (!$chk(el.value)) return;
		if (el.className.test(/hexInput/)) {
			if (chr != "#" && el.value.length != 6) return;
			if (chr == '#' && el.value.length != 7) return;
		} else {
			if (!(n >= 48 && n <= 57) && (!['backspace', 'tab', 'delete', 'left', 'right'].contains(k)) && el.value.length > 3) return;
		}

		prefix = this.options.prefix;

		if (el.className.test(/(rInput|gInput|bInput)/)) {
			if (el.value  < 0 || el.value > 255) return;
			switch(el.className){
				case prefix + 'rInput': pass = [el.value, this.sets.rgb[1], this.sets.rgb[2]]; break;
				case prefix + 'gInput': pass = [this.sets.rgb[0], el.value, this.sets.rgb[2]]; break;
				case prefix + 'bInput': pass = [this.sets.rgb[0], this.sets.rgb[1], el.value]; break;
				default : pass = this.sets.rgb;
			}
			this.manualSet(pass);
			this.fireEvent('onChange', [this.sets, this]);
		}
		else if (!el.className.test(/hexInput/)) {
			if (el.className.test(/HueInput/) && el.value  < 0 || el.value > 360) return;
			else if (el.className.test(/HueInput/) && el.value == 360) el.value = 0;
			else if (el.className.test(/(SatuInput|BrighInput)/) && el.value  < 0 || el.value > 100) return;
			switch(el.className){
				case prefix + 'HueInput': pass = [el.value, this.sets.hsb[1], this.sets.hsb[2]]; break;
				case prefix + 'SatuInput': pass = [this.sets.hsb[0], el.value, this.sets.hsb[2]]; break;
				case prefix + 'BrighInput': pass = [this.sets.hsb[0], this.sets.hsb[1], el.value]; break;
				default : pass = this.sets.hsb;
			}
			this.manualSet(pass, 'hsb');
			this.fireEvent('onChange', [this.sets, this]);
		} else {
			pass = el.value.hexToRgb(true);
			if (isNaN(pass[0])||isNaN(pass[1])||isNaN(pass[2])) return;

			if ($chk(pass)) {
				this.manualSet(pass);
				this.fireEvent('onChange', [this.sets, this]);
			}
		}

	},

	doLayout: function() {
		var id = this.options.id, prefix = this.options.prefix;
		var idPrefix = id + ' .' + prefix;

		this.layout = new Element('div', {
			'styles': {'display': 'block', 'position': 'absolute'},
			'id': id
		}).inject(document.body);

		var box = new Element('div', {
			'styles':  {'position': 'relative'},
			'class': prefix + 'box'
		}).inject(this.layout);

		var div = new Element('div', {
			'styles': {'position': 'absolute', 'overflow': 'hidden'},
			'class': prefix + 'overlayBox'
		}).inject(box);

		var ar = new Element('div', {
			'styles': {'position': 'absolute', 'zIndex': 1},
			'class': prefix + 'arrows'
		}).inject(box);
		ar.width = ar.getStyle('width').toInt();
		ar.height = ar.getStyle('height').toInt();

		var ov = new Element('img', {
			'styles': {'background-color': '#fff', 'position': 'relative', 'zIndex': 2},
			'src': this.options.imgPath + 'moor_woverlay.png',
			'class': prefix + 'overlay'
		}).inject(div);

		var ov2 = new Element('img', {
			'styles': {'position': 'absolute', 'top': 0, 'left': 0, 'zIndex': 2},
			'src': this.options.imgPath + 'moor_boverlay.png',
			'class': prefix + 'overlay'
		}).inject(div);

		if (Browser.Engine.trident4) {
			div.setStyle('overflow', '');
			var src = ov.src;
			ov.src = this.options.imgPath + 'blank.gif';
			ov.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + src + "', sizingMethod='scale')";
			src = ov2.src;
			ov2.src = this.options.imgPath + 'blank.gif';
			ov2.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + src + "', sizingMethod='scale')";
		}
		ov.width = ov2.width = div.getStyle('width').toInt();
		ov.height = ov2.height = div.getStyle('height').toInt();

		var cr = new Element('div', {
			'styles': {'overflow': 'hidden', 'position': 'absolute', 'zIndex': 2},
			'class': prefix + 'cursor'
		}).inject(div);
		cr.width = cr.getStyle('width').toInt();
		cr.height = cr.getStyle('height').toInt();

		var sl = new Element('img', {
			'styles': {'position': 'absolute', 'z-index': 2},
			'src': this.options.imgPath + 'moor_slider.png',
			'class': prefix + 'slider'
		}).inject(box);
		this.layout.slider = $$('#' + idPrefix + 'slider')[0];
		sl.width = sl.getStyle('width').toInt();
		sl.height = sl.getStyle('height').toInt();

		new Element('div', {
			'styles': {'position': 'absolute'},
			'class': prefix + 'colorBox'
		}).inject(box);

		new Element('div', {
			'styles': {'zIndex': 2, 'position': 'absolute'},
			'class': prefix + 'chooseColor'
		}).inject(box);

		this.layout.backup = new Element('div', {
			'styles': {'zIndex': 2, 'position': 'absolute', 'cursor': 'pointer'},
			'class': prefix + 'currentColor'
		}).inject(box);

		var R = new Element('label').inject(box).setStyle('position', 'absolute');
		var G = R.clone().inject(box).addClass(prefix + 'gLabel').set('html', 'G: ');
		var B = R.clone().inject(box).addClass(prefix + 'bLabel').set('html', 'B: ');
		R.set('html', 'R: ').addClass(prefix + 'rLabel');

		var inputR = new Element('input');
		var inputG = inputR.clone().inject(G).addClass(prefix + 'gInput');
		var inputB = inputR.clone().inject(B).addClass(prefix + 'bInput');
		inputR.inject(R).addClass(prefix + 'rInput');

		var HU = new Element('label').inject(box).setStyle('position', 'absolute');
		var SA = HU.clone().inject(box).addClass(prefix + 'SatuLabel').set('html', 'S: ');
		var BR = HU.clone().inject(box).addClass(prefix + 'BrighLabel').set('html', 'B: ');
		HU.set('html', 'H: ').addClass(prefix + 'HueLabel');

		var inputHU = new Element('input');
		var inputSA = inputHU.clone().inject(SA).addClass(prefix + 'SatuInput');
		var inputBR = inputHU.clone().inject(BR).addClass(prefix + 'BrighInput');
		inputHU.inject(HU).addClass(prefix + 'HueInput');
		SA.innerHTML += " %"; BR.innerHTML += " %";
		SP = new Element('span', {'styles': {'position': 'absolute'}, 'class': prefix + 'ballino'})
		SP.innerHTML = " &deg;"
		SP.inject(HU,'after');

		var hex = new Element('label').inject(box).setStyle('position', 'absolute').addClass(prefix + 'hexLabel').set('html', '#hex: ').adopt(new Element('input').addClass(prefix + 'hexInput'));

		var ok = new Element('input', {
			'styles': {'position': 'absolute'},
			'type': 'button',
			'value': this.options.selectText,
			'class': prefix + 'okButton'
		}).inject(box);

		this.rePosition();

		var overlays = $$('#' + idPrefix + 'overlay');
		this.layout.overlay = overlays[0];
		this.layout.overlay2 = overlays[1];
		this.layout.cursor = $$('#' + idPrefix + 'cursor')[0];
		this.layout.arrows = $$('#' + idPrefix + 'arrows')[0];
		this.chooseColor = $$('#' + idPrefix + 'chooseColor')[0];
		this.layout.backup = $$('#' + idPrefix + 'currentColor')[0];
		this.RedInput = $$('#' + idPrefix + 'rInput')[0];
		this.GreenInput = $$('#' + idPrefix + 'gInput')[0];
		this.BlueInput = $$('#' + idPrefix + 'bInput')[0];
		this.HueInput = $$('#' + idPrefix + 'HueInput')[0];
		this.SatuInput = $$('#' + idPrefix + 'SatuInput')[0];
		this.BrighInput = $$('#' + idPrefix + 'BrighInput')[0];
		this.hexInput = $$('#' + idPrefix + 'hexInput')[0];

		this.arrRGB = [this.RedInput, this.GreenInput, this.BlueInput];
		this.arrHSB = [this.HueInput, this.SatuInput, this.BrighInput];
		this.okButton = $$('#' + idPrefix + 'okButton')[0];

		if (!Browser.Engine.webkit419) this.hide();
	},
	rePosition: function() {
		var coords = this.element.getCoordinates();
		this.layout.setStyles({
			'left': coords.left,
			'top': coords.top + coords.height + 1
		});
	},

	snippet: function(mode, type) {
		var size; type = (type) ? type : 'none';

		switch(mode) {
			case 'arrPos':
				var t = this.layout.arrows.getStyle('top').toInt();
				size = t;
				break;
			case 'arrSize':
				var h = this.layout.arrows.height;
				h = (type == 'int') ? (h/2).toInt() : h;
				size = h;
				break;
			case 'curPos':
				var l = this.layout.cursor.getStyle('left').toInt();
				var t = this.layout.cursor.getStyle('top').toInt();
				size = {'l': l, 't': t};
				break;
			case 'slider':
				var t = this.layout.slider.getStyle('marginTop').toInt();
				size = t;
				break;
			default :
				var h = this.layout.cursor.height;
				var w = this.layout.cursor.width;
				h = (type == 'int') ? (h/2).toInt() : h;
				w = (type == 'int') ? (w/2).toInt() : w;
				size = {w: w, h: h};
		};
		return size;
	}
});

/**
Script: Slideshow.js
	Slideshow - A javascript class for Mootools to stream and animate the presentation of images on your website.

License:
	MIT-style license.

Copyright:
	Copyright (c) 2008 [Aeron Glemann](http://www.electricprism.com/aeron/).

Dependencies:
	Mootools 1.2 Core: Fx.Morph, Fx.Tween, Selectors, Element.Dimensions.
	Mootools 1.2 More: Assets.
*/

Slideshow = new Class({
	Implements: [Chain, Events, Options],

	options: {
		captions: false,
		center: true,
		classes: [],
		controller: false,
		delay: 2000,
		duration: 750,
		fast: false,
		height: false,
		href: '',
		hu: '',
		linked: false,
		loader: {'animate': ['css/loader-#.png', 12]},
		loop: true,
		match: /\?slide=(\d+)$/,
		/*
		onComplete: $empty,
		onStart: $empty,
		*/
		overlap: true,
		paused: false,
		random: false,
		replace: [/\.(.{3})$/, 't.$1'],
		resize: 'width',
		slide: 0,
		thumbnails: false,
		transition: function(p){return -(Math.cos(Math.PI * p) - 1) / 2;},
		width: false
	},

/**
Constructor: initialize
	Creates an instance of the Slideshow class.

Arguments:
	element - (element) The wrapper element.
	data - (array or object) The images and optional thumbnails, captions and links for the show.
	options - (object) The options below.

Syntax:
	var myShow = new Slideshow(element, data, options);
*/

	initialize: function(el, data, options){
		this.setOptions(options);
		this.slideshow = $(el);
		if (!this.slideshow)
			return;
		this.slideshow.set('styles', {'display': 'block', 'position': 'relative', 'z-index': 0});
		var match = window.location.href.match(this.options.match);
		this.slide = (this.options.match && match) ? match[1].toInt() : this.options.slide;
		this.counter = this.delay = this.transition = 0;
		this.direction = 'left';
		this.paused = false;
		if (!this.options.overlap)
			this.options.duration *= 2;
		var anchor = this.slideshow.getElement('a') || new Element('a');
		if (!this.options.href)
			this.options.href = anchor.get('href') || '';
		if (this.options.hu.length && this.options.hu.substr(-1) != '/')
			this.options.hu += '/';

		// styles

		var keys = ['slideshow', 'first', 'prev', 'play', 'pause', 'next', 'last', 'images', 'captions', 'controller', 'thumbnails', 'hidden', 'visible', 'inactive', 'active', 'loader'];
		var values = keys.map(function(key, i){
			return this.options.classes[i] || key;
		}, this);
		this.classes = values.associate(keys);
		this.classes.get = function(){
			var str = '.' + this.slideshow;
			for (var i = 0, l = arguments.length; i < l; i++)
				str += ('-' + this[arguments[i]]);
			return str;
		}.bind(this.classes);

		// data

		if (!data){
			this.options.hu = '';
			data = {};
			var thumbnails = this.slideshow.getElements(this.classes.get('thumbnails') + ' img');
			this.slideshow.getElements(this.classes.get('images') + ' img').each(function(img, i){
				var src = img.get('src');
				var caption = img.get('alt') || img.get('title') || '';
				var href = img.getParent().get('href') || '';
				var thumbnail = thumbnails[i].get('src') || '';
				data[src] = {'caption': caption, 'href': href, 'thumbnail': thumbnail};
			});
		}

		var loaded = this.load(data);
		if (!loaded)
			return;

		// events

		this.events = $H({'keydown': [], 'keyup': [], 'mousemove': []});
		var keyup = function(e){
			switch(e.key){
				case 'left':
					this.prev(e.shift); break;
				case 'right':
					this.next(e.shift); break;
				case 'p':
					this.pause(); break;
			}
		}.bind(this);
		this.events.keyup.push(keyup);
		document.addEvent('keyup', keyup);

		// required elements

		var el = this.slideshow.getElement(this.classes.get('images'));
		var images = (el) ? el.empty() : new Element('div', {'class': this.classes.get('images').substr(1)}).inject(this.slideshow);
		var div = images.getSize();
		this.height = this.options.height || div.y;
		this.width = this.options.width || div.x;
		images.set({'styles': {'display': 'block', 'height': this.height, 'overflow': 'hidden', 'position': 'relative', 'width': this.width}});
		this.slideshow.store('images', images);
		this.a = this.image = this.slideshow.getElement('img') || new Element('img');
		this.a.set('styles', {'display': 'none', 'position': 'absolute', 'zIndex': 1});
		this.b = this.a.clone();
		[this.a, this.b].each(function(img){
			anchor.clone().grab(img).inject(images);
		});

		// optional elements

		if (this.options.captions)
 			this._captions();
		if (this.options.controller)
			this._controller();
		if (this.options.loader)
 			this._loader();
		if (this.options.thumbnails)
			this._thumbnails();

		// begin show

		this._preload();
	},

/**
Public method: go
	Jump directly to a slide in the show.

Arguments:
	n - (integer) The index number of the image to jump to, 0 being the first image in the show.

Syntax:
	myShow.go(n);
*/

	go: function(n, direction){
		if ((this.slide - 1 + this.data.images.length) % this.data.images.length == n || $time() < this.transition)
			return;
		$clear(this.timer);
		this.delay = 0;
		this.direction = (direction) ? direction : ((n < this.slide) ? 'right' : 'left');
		this.slide = n;
		if (this.preloader)
			this.preloader = this.preloader.destroy();
		this._preload(this.options.fast || this.paused);
	},

/**
Public method: first
	Goes to the first image in the show.

Syntax:
	myShow.first();
*/

	first: function(){
		this.prev(true);
	},

/**
Public method: prev
	Goes to the previous image in the show.

Syntax:
	myShow.prev();
*/

	prev: function(first){
		var n = 0;
		if (!first){
			if (this.options.random){

				// if it's a random show get the previous slide from the showed array

				if (this.showed.i < 2)
					return;
				this.showed.i -= 2;
				n = this.showed.array[this.showed.i];
			}
			else
				n = (this.slide - 2 + this.data.images.length) % this.data.images.length;
		}
		this.go(n, 'right');
	},

/**
Public method: pause
	Toggles play / pause state of the show.

Arguments:
	p - (undefined, 1 or 0) Call pause with no arguments to toggle the pause state. Call pause(1) to force pause, or pause(0) to force play.

Syntax:
	myShow.pause(p);
*/

	pause: function(p){
		if ($chk(p))
			this.paused = (p) ? false : true;
		if (this.paused){
			this.paused = false;
			this.delay = this.transition = 0;
			this.timer = this._preload.delay(100, this);
			[this.a, this.b].each(function(img){
				['morph', 'tween'].each(function(p){
					if (this.retrieve(p)) this.get(p).resume();
				}, img);
			});
			if (this.options.controller)
				this.slideshow.getElement('.' + this.classes.pause).removeClass(this.classes.play);
		}
		else {
			this.paused = true;
			this.delay = Number.MAX_VALUE;
			this.transition = 0;
			$clear(this.timer);
			[this.a, this.b].each(function(img){
				['morph', 'tween'].each(function(p){
					if (this.retrieve(p)) this.get(p).pause();
				}, img);
			});
			if (this.options.controller)
				this.slideshow.getElement('.' + this.classes.pause).addClass(this.classes.play);
		}
	},

/**
Public method: next
	Goes to the next image in the show.

Syntax:
	myShow.next();
*/

	next: function(last){
		var n = (last) ? this.data.images.length - 1 : this.slide;
		this.go(n, 'left');
	},

/**
Public method: last
	Goes to the last image in the show.

Syntax:
	myShow.last();
*/

	last: function(){
		this.next(true);
	},

/**
Public method: load
	Loads a new data set into the show: will stop the current show, rewind and rebuild thumbnails if applicable.

Arguments:
	data - (array or object) The images and optional thumbnails, captions and links for the show.

Syntax:
	myShow.load(data);
*/

	load: function(data){
		this.firstrun = true;
		this.showed = {'array': [], 'i': 0};
		if ($type(data) == 'array'){
			this.options.captions = false;
			data = new Array(data.length).associate(data);
		}
		this.data = {'images': [], 'captions': [], 'hrefs': [], 'thumbnails': []};
		for (image in data){
			var obj = data[image] || {};
			var caption = (obj.caption) ? obj.caption.trim() : '';
			var href = (obj.href) ? obj.href.trim() : ((this.options.linked) ? this.options.hu + image : this.options.href);
			var thumbnail = (obj.thumbnail) ? obj.thumbnail.trim() : image.replace(this.options.replace[0], this.options.replace[1]);
			this.data.images.push(image);
			this.data.captions.push(caption);
			this.data.hrefs.push(href);
			this.data.thumbnails.push(thumbnail);
		}

		// only run when data is loaded dynamically into an existing slideshow instance

		if (this.options.thumbnails && this.slideshow.retrieve('thumbnails'))
			this._thumbnails();
		if (this.slideshow.retrieve('images')){
			[this.a, this.b].each(function(img){
				['morph', 'tween'].each(function(p){
					if (this.retrieve(p)) this.get(p).cancel();
				}, img);
			});
			this.slide = this.transition = 0;
			this.go(0);
		}
		return this.data.images.length;
	},

/**
Public method: destroy
	Destroys a Slideshow instance.

Arguments:
	p - (string) The images and optional thumbnails, captions and links for the show.

Syntax:
	myShow.destroy(p);
*/

	destroy: function(p){
		this.events.each(function(array, e){
			array.each(function(fn){ document.removeEvent(e, fn); });
		});
		this.pause(1);
		if (this.options.loader)
			$clear(this.slideshow.retrieve('loader').retrieve('timer'));
		if (this.options.thumbnails)
			$clear(this.slideshow.retrieve('thumbnails').retrieve('timer'));
		Element.Storage[this.slideshow.uid] = {};
		if (p)
			$try(this.slideshow[p]());
	},

/**
Private method: preload
	Preloads the next slide in the show, once loaded triggers the show, updates captions, thumbnails, etc.
*/

	_preload: function(fast){
		if (!this.preloader)
		 	this.preloader = new Asset.image(this.options.hu + this.data.images[this.slide], {'onload': function(){
				this.store('loaded', true);
			}});
		if (this.preloader.retrieve('loaded') && $time() > this.delay && $time() > this.transition){
			if (this.stopped){
				if (this.options.captions)
					this.slideshow.retrieve('captions').get('morph').cancel().start(this.classes.get('captions', 'hidden'));
				this.pause(1);
				this.stopped = false;
				return;
			}
			this.image = (this.counter % 2) ? this.b : this.a;
			this.image.set('styles', {'display': 'block', 'height': 'auto', 'visibility': 'hidden', 'width': 'auto', 'zIndex': this.counter});
			/*this.image.set('styles', {'display': 'block', 'height': 'auto', 'width': 'auto', 'zIndex': this.counter});*/
			['src', 'height', 'width'].each(function(prop){
				this.image.set(prop, this.preloader.get(prop));
			}, this);
			this._resize(this.image);
			this._center(this.image);
			var anchor = this.image.getParent();
			if (this.data.hrefs[this.slide])
				anchor.set('href', this.data.hrefs[this.slide]);
			else
				anchor.erase('href');
			if (this.data.captions[this.slide])
				anchor.set('title', this.data.captions[this.slide].replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, "'"));
			else
				anchor.erase('title');
			if (this.options.loader)
				this.slideshow.retrieve('loader').fireEvent('hide');
			if (this.options.captions)
				this.slideshow.retrieve('captions').fireEvent('update', fast);
			if (this.options.thumbnails)
				this.slideshow.retrieve('thumbnails').fireEvent('update', fast);
			this._show(fast);
			this._loaded();
		}
		else {
			if ($time() > this.delay && this.options.loader)
				this.slideshow.retrieve('loader').fireEvent('show');
			this.timer = (this.paused && this.preloader.retrieve('loaded')) ? null : this._preload.delay(100, this, fast);
		}
	},

/**
Private method: show
	Does the slideshow effect.
*/

	_show: function(fast){
		if (!this.image.retrieve('morph')){
			var options = (this.options.overlap) ? {'duration': this.options.duration, 'link': 'cancel'} : {'duration': this.options.duration / 2, 'link': 'chain'};
			$$(this.a, this.b).set('morph', $merge(options, {'onStart': this._start.bind(this), 'onComplete': this._complete.bind(this), 'transition': this.options.transition}));
		}
		var hidden = this.classes.get('images', ((this.direction == 'left') ? 'next' : 'prev'));
		var visible = this.classes.get('images', 'visible');
		var img = (this.counter % 2) ? this.a : this.b;
		if (fast){
			img.get('morph').cancel().set(hidden);
			this.image.get('morph').cancel().set(visible);
		}
		else {
			if (this.options.overlap){
				img.get('morph').set(visible);
				this.image.get('morph').set(hidden).start(visible);
			}
			else	{
				var fn = function(hidden, visible){
					this.image.get('morph').set(hidden).start(visible);
				}.pass([hidden, visible], this);
				hidden = this.classes.get('images', ((this.direction == 'left') ? 'prev' : 'next'));
				img.get('morph').set(visible).start(hidden).chain(fn);
			}
		}
	},

/**
Private method: loaded
	Run after the current image has been loaded, sets up the next image to be shown.
*/

	_loaded: function(){
		this.counter++;
		this.delay = (this.paused) ? Number.MAX_VALUE : $time() + this.options.duration + this.options.delay;
		this.direction = 'left';
		this.transition = (this.paused || this.options.fast) ? 0 : $time() + this.options.duration;
		if (this.slide + 1 == this.data.images.length && !this.options.loop && !this.options.random)
			this.stopped = true;
		if (this.options.random){
			this.showed.i++;
			if (this.showed.i >= this.showed.array.length){
				var n = this.slide;
				if (this.showed.array.getLast() != n) this.showed.array.push(n);
				while (this.slide == n)
					this.slide = $random(0, this.data.images.length - 1);
			}
			else
				this.slide = this.showed.array[this.showed.i];
		}
		else
			this.slide = (this.slide + 1) % this.data.images.length;
		if (this.preloader)
			this.preloader = this.preloader.destroy();
		this._preload();
	},

/**
Private method: center
	Center an image.
*/

	_center: function(img){
		if (this.options.center){
			var size = img.getSize();
			img.set('styles', {'left': (size.x - this.width) / -2, 'top': (size.y - this.height) / -2});
		}
	},

/**
Private method: resize
	Resizes an image.
*/

	_resize: function(img){
		if (this.options.resize){
			var h = this.preloader.get('height'), w = this.preloader.get('width');
			var dh = this.height / h, dw = this.width / w, d;
			if (this.options.resize == 'length')
				d = (dh > dw) ? dw : dh;
			else
				d = (dh > dw) ? dh : dw;
			img.set('styles', {height: Math.ceil(h * d), width: Math.ceil(w * d)});
		}
	},

/**
Private method: start
	Callback on start of slide change.
*/

	_start: function(){
		this.fireEvent('start');
	},

/**
Private method: complete
	Callback on start of slide change.
*/

	_complete: function(){
		if (this.firstrun && this.options.paused){
			this.firstrun = false;
			this.pause(1);
		}
		this.fireEvent('complete');
	},

/**
Private method: captions
	Builds the optional caption element, adds interactivity.
	This method can safely be removed if the captions option is not enabled.
*/

	_captions: function(){
 		if (this.options.captions === true)
 			this.options.captions = {};
		var el = this.slideshow.getElement(this.classes.get('captions'));
		var captions = (el) ? el.empty() : new Element('div', {'class': this.classes.get('captions').substr(1)}).inject(this.slideshow);
		captions.set({
			'events': {
				'update': function(fast){
					var captions = this.slideshow.retrieve('captions');
					var empty = (this.data.captions[this.slide] === '');
					if (fast){
						var p = (empty) ? 'hidden' : 'visible';
						captions.set('html', this.data.captions[this.slide]).get('morph').cancel().set(this.classes.get('captions', p));
					}
					else {
						var fn = (empty) ? $empty : function(n){
							this.slideshow.retrieve('captions').set('html', this.data.captions[n]).morph(this.classes.get('captions', 'visible'))
						}.pass(this.slide, this);
						captions.get('morph').cancel().start(this.classes.get('captions', 'hidden')).chain(fn);
					}
				}.bind(this)
			},
			'morph': $merge(this.options.captions, {'link': 'chain'})
		});
		this.slideshow.store('captions', captions);
	},

/**
Private method: controller
	Builds the optional controller element, adds interactivity.
	This method can safely be removed if the controller option is not enabled.
*/

	_controller: function(){
 		if (this.options.controller === true)
 			this.options.controller = {};
		var el = this.slideshow.getElement(this.classes.get('controller'));
		var controller = (el) ? el.empty() : new Element('div', {'class': this.classes.get('controller').substr(1)}).inject(this.slideshow);
		var ul = new Element('ul').inject(controller);
		$H({'first': 'Shift + Leftwards Arrow', 'prev': 'Leftwards Arrow', 'pause': 'P', 'next': 'Rightwards Arrow', 'last': 'Shift + Rightwards Arrow'}).each(function(accesskey, action){
			var li = new Element('li', {
				'class': (action == 'pause' && this.options.paused) ? this.classes.play + ' ' + this.classes[action] : this.classes[action]
			}).inject(ul);
			var a = this.slideshow.retrieve(action, new Element('a', {
				'title': ((action == 'pause') ? this.classes.play.capitalize() + ' / ' : '') + this.classes[action].capitalize() + ' [' + accesskey + ']'
			}).inject(li));
			a.set('events', {
				'click': function(action){this[action]();}.pass(action, this),
				'mouseenter': function(active){this.addClass(active);}.pass(this.classes.active, a),
				'mouseleave': function(active){this.removeClass(active);}.pass(this.classes.active, a)
			});
		}, this);
		controller.set({
			'events': {
				'hide': function(hidden){
					if (!this.retrieve('hidden'))
						this.store('hidden', true).morph(hidden);
				}.pass(this.classes.get('controller', 'hidden'), controller),
				'show': function(visible){
					if (this.retrieve('hidden'))
						this.store('hidden', false).morph(visible);
				}.pass(this.classes.get('controller', 'visible'), controller)
			},
			'morph': $merge(this.options.controller, {'link': 'cancel'})
		}).store('hidden', false);
		var keydown = function(e){
			if (['left', 'right', 'p'].contains(e.key)){
				var controller = this.slideshow.retrieve('controller');
				if (controller.retrieve('hidden'))
					controller.get('morph').set(this.classes.get('controller', 'visible'));
				switch(e.key){
					case 'left':
						this.slideshow.retrieve((e.shift) ? 'first' : 'prev').fireEvent('mouseenter'); break;
					case 'right':
						this.slideshow.retrieve((e.shift) ? 'last' : 'next').fireEvent('mouseenter'); break;
					default:
						this.slideshow.retrieve('pause').fireEvent('mouseenter'); break;
				}
			}
		}.bind(this);
		this.events.keydown.push(keydown);
		var keyup = function(e){
			if (['left', 'right', 'p'].contains(e.key)){
				var controller = this.slideshow.retrieve('controller');
				if (controller.retrieve('hidden'))
					controller.store('hidden', false).fireEvent('hide');
				switch(e.key){
					case 'left':
						this.slideshow.retrieve((e.shift) ? 'first' : 'prev').fireEvent('mouseleave'); break;
					case 'right':
						this.slideshow.retrieve((e.shift) ? 'last' : 'next').fireEvent('mouseleave'); break;
					default:
						this.slideshow.retrieve('pause').fireEvent('mouseleave'); break;
				}
			}
		}.bind(this);
		this.events.keyup.push(keyup);
		var mousemove = function(e){
			var images = this.slideshow.retrieve('images').getCoordinates();
			if (e.page.x > images.left && e.page.x < images.right && e.page.y > images.top && e.page.y < images.bottom)
				this.slideshow.retrieve('controller').fireEvent('show');
			else
				this.slideshow.retrieve('controller').fireEvent('hide');
		}.bind(this);
		this.events.mousemove.push(mousemove);
		document.addEvents({'keydown': keydown, 'keyup': keyup, 'mousemove': mousemove});
		this.slideshow.retrieve('controller', controller).fireEvent('hide');
	},

/**
Private method: loader
	Builds the optional loader element, adds interactivity.
	This method can safely be removed if the loader option is not enabled.
*/

	_loader: function(){
 		if (this.options.loader === true)
 			this.options.loader = {};
		var loader = new Element('div', {
			'class': this.classes.get('loader').substr(1),
			'morph': $merge(this.options.loader, {'link': 'cancel'})
		}).store('hidden', false).store('i', 1).inject(this.slideshow.retrieve('images'));
		if (this.options.loader.animate){
			for (var i = 0; i < this.options.loader.animate[1]; i++)
				img = new Asset.image(this.options.loader.animate[0].replace(/#/, i));
			if (Browser.Engine.trident4 && this.options.loader.animate[0].contains('png'))
				loader.setStyle('backgroundImage', 'none');
		}
		loader.set('events', {
			'animate': function(){
				var loader = this.slideshow.retrieve('loader');
				var i = (loader.retrieve('i').toInt() + 1) % this.options.loader.animate[1];
				loader.store('i', i);
				var img = this.options.loader.animate[0].replace(/#/, i);
				if (Browser.Engine.trident4 && this.options.loader.animate[0].contains('png'))
					loader.style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src="' + img + '", sizingMethod="scale")';
				else
					loader.setStyle('backgroundImage', 'url(' + img + ')');
			}.bind(this),
			'hide': function(){
				var loader = this.slideshow.retrieve('loader');
				if (!loader.retrieve('hidden')){
					loader.store('hidden', true).morph(this.classes.get('loader', 'hidden'));
					if (this.options.loader.animate)
						$clear(loader.retrieve('timer'));
				}
			}.bind(this),
			'show': function(){
				var loader = this.slideshow.retrieve('loader');
				if (loader.retrieve('hidden')){
					loader.store('hidden', false).morph(this.classes.get('loader', 'visible'));
					if (this.options.loader.animate)
						loader.store('timer', function(){this.fireEvent('animate');}.periodical(50, loader));
				}
			}.bind(this)
		});
		this.slideshow.retrieve('loader', loader).fireEvent('hide');
	},

/**
Private method: thumbnails
	Builds the optional thumbnails element, adds interactivity.
	This method can safely be removed if the thumbnails option is not enabled.
*/

	_thumbnails: function(){
 		if (this.options.thumbnails === true)
 			this.options.thumbnails = {};
		var el = this.slideshow.getElement(this.classes.get('thumbnails'));
		var thumbnails = (el) ? el.empty() : new Element('div', {'class': this.classes.get('thumbnails').substr(1)}).inject(this.slideshow);
		thumbnails.setStyle('overflow', 'hidden');
		var ul = new Element('ul', {'tween': {'link': 'cancel'}}).inject(thumbnails);
		this.data.thumbnails.each(function(thumbnail, i){
			var li = new Element('li').inject(ul);
			var a = new Element('a', {
				'events': {
					'click': function(i){
						this.go(i);
						return false;
					}.pass(i, this),
					'loaded': function(){
						this.data.thumbnails.pop();
						if (!this.data.thumbnails.length){
							var div = thumbnails.getCoordinates();
							var props = thumbnails.retrieve('props');
							var limit = 0, pos = props[1], size = props[2];
							thumbnails.getElements('li').each(function(li){
								var li = li.getCoordinates();
								if (li[pos] > limit) limit = li[pos];
							}, this);
							thumbnails.store('limit', div[size] + div[props[0]] - limit);
						}
					}.bind(this)
				},
				'href': this.options.hu + this.data.images[i],
				'morph': $merge(this.options.thumbnails, {'link': 'cancel'}),
				'title': this.data.captions[i]
			}).inject(li);
			var img = new Asset.image(this.options.hu + thumbnail, {
				'onload': function(){this.fireEvent('loaded');}.bind(a)
			}).inject(a);
		}, this);
		thumbnails.set('events', {
			'scroll': function(n, fast){
				var div = this.getCoordinates();
				var ul = this.getElement('ul').getPosition();
				var props = this.retrieve('props');
				var axis = props[3], delta, pos = props[0], size = props[2], value;
				var tween = this.getElement('ul').get('tween', {'property': pos});
				if ($chk(n)){
					var li = this.getElements('li')[n].getCoordinates();
					delta = div[pos] + (div[size] / 2) - (li[size] / 2) - li[pos]
					value = (ul[axis] - div[pos] + delta).limit(this.retrieve('limit'), 0);
					if (fast)
						tween.set(value);
					else
						tween.start(value);
				}
				else{
					var area = div[props[2]] / 3, page = this.retrieve('page'), velocity = -0.2;
					if (page[axis] < (div[pos] + area))
						delta = (page[axis] - div[pos] - area) * velocity;
					else if (page[axis] > (div[pos] + div[size] - area))
						delta = (page[axis] - div[pos] - div[size] + area) * velocity;
					if (delta){
						value = (ul[axis] - div[pos] + delta).limit(this.retrieve('limit'), 0);
						tween.set(value);
					}
				}
			}.bind(thumbnails),
			'update': function(fast){
				var thumbnails = this.slideshow.retrieve('thumbnails');
				thumbnails.getElements('a').each(function(a, i){
					if (i == this.slide){
						if (!a.retrieve('active', false)){
							a.store('active', true);
							var active = this.classes.get('thumbnails', 'active');
							if (fast) a.get('morph').set(active);
							else a.morph(active);
						}
					}
					else {
						if (a.retrieve('active', true)){
							a.store('active', false);
							var inactive = this.classes.get('thumbnails', 'inactive');
							if (fast) a.get('morph').set(inactive);
							else a.morph(inactive);
						}
					}
				}, this);
				if (!thumbnails.retrieve('mouseover'))
					thumbnails.fireEvent('scroll', [this.slide, fast]);
			}.bind(this)
		})
		var div = thumbnails.getCoordinates();
		thumbnails.store('props', (div.height > div.width) ? ['top', 'bottom', 'height', 'y'] : ['left', 'right', 'width', 'x']);
		var mousemove = function(e){
			var div = this.getCoordinates();
			if (e.page.x > div.left && e.page.x < div.right && e.page.y > div.top && e.page.y < div.bottom){
				this.store('page', e.page);
				if (!this.retrieve('mouseover')){
					this.store('mouseover', true);
					this.store('timer', function(){this.fireEvent('scroll');}.periodical(50, this));
				}
			}
			else {
				if (this.retrieve('mouseover')){
					this.store('mouseover', false);
					$clear(this.retrieve('timer'));
				}
			}
		}.bind(thumbnails);
		this.events.mousemove.push(mousemove);
		document.addEvent('mousemove', mousemove);
		this.slideshow.store('thumbnails', thumbnails);
	}
});

/**
Script: Slideshow.Flash.js
	Slideshow.Flash - Flash extension for Slideshow.

License:
	MIT-style license.

Copyright:
	Copyright (c) 2008 [Aeron Glemann](http://www.electricprism.com/aeron/).

Dependencies:
	Slideshow.
*/

Slideshow.Flash = new Class({
	Extends: Slideshow,
	
	options: {
		color: ['#FFF']
	},
	
/**
Constructor: initialize
	Creates an instance of the Slideshow class.

Arguments:
	element - (element) The wrapper element.
	data - (array or object) The images and optional thumbnails, captions and links for the show.
	options - (object) The options below.

Syntax:
	var myShow = new Slideshow.Flash(element, data, options);
*/

	initialize: function(el, data, options){
		options.overlap = true;
		if (options.color)
			options.color = $splat(options.color);
		this.parent(el, data, options);
	},

/**
Private method: show
	Does the slideshow effect.
*/

	_show: function(fast){
		if (!this.image.retrieve('tween'))
		  $$(this.a, this.b).set('tween', {'duration': this.options.duration, 'link': 'cancel', 'onStart': this._start.bind(this), 'onComplete': this._complete.bind(this), 'property': 'opacity'});
		if (fast)
			this.image.get('tween').cancel().set(1);
		else {
			this.slideshow.retrieve('images').setStyle('background', this.options.color[this.slide % this.options.color.length]);
			var img = (this.counter % 2) ? this.a : this.b;
			img.get('tween').cancel().set(0);
			this.image.get('tween').set(0).start(1);
		}
	}
});

/**
Script: Slideshow.Fold.js
	Slideshow.Fold - Flash extension for Slideshow.

License:
	MIT-style license.

Copyright:
	Copyright (c) 2008 [Aeron Glemann](http://www.electricprism.com/aeron/).

Dependencies:
	Slideshow.
*/

Slideshow.Fold = new Class({
	Extends: Slideshow,
	
/**
Constructor: initialize
	Creates an instance of the Slideshow class.

Arguments:
	element - (element) The wrapper element.
	data - (array or object) The images and optional thumbnails, captions and links for the show.
	options - (object) The options below.

Syntax:
	var myShow = new Slideshow.Fold(element, data, options);
*/

	initialize: function(el, data, options){
		this.parent(el, data, options);
	},

/**
Private method: show
	Does the slideshow effect.
*/

	_show: function(fast){
		if (!this.image.retrieve('tween')){
			var options = (this.options.overlap) ? {'duration': this.options.duration} : {'duration': this.options.duration / 2};
			$$(this.a, this.b).set('tween', $merge(options, {'link': 'chain', 'onStart': this._start.bind(this), 'onComplete': this._complete.bind(this), 'property': 'clip', 'transition': this.options.transition}));
		}
		var rect = this._rect(this.image);
		var img = (this.counter % 2) ? this.a : this.b;
		if (fast){			
			img.get('tween').cancel().set('rect(0, 0, 0, 0)');
			this.image.setStyle('visibility', 'visible').get('tween').cancel().set('auto'); 			
		} 
		else {
			if (this.options.overlap){	
				img.setStyle('visibility', 'visible').get('tween').set('auto');
				var tween = this.image.setStyle('visibility', 'visible').get('tween').set(rect.top + ' ' + rect.left + ' ' + Math.ceil(rect.bottom / 2) + ' ' + rect.left).start(rect.top + ' ' + rect.right + ' ' + Math.ceil(rect.bottom / 2) + ' ' + rect.left).start(rect.top + ' ' + rect.right + ' ' + rect.bottom + ' ' + rect.left);
			} 
			else	{
				var fn = function(rect){
					this.image.setStyle('visibility', 'visible').get('tween').set(rect.top + ' ' + rect.left + ' ' + Math.ceil(rect.bottom / 2) + ' ' + rect.left).start(rect.top + ' ' + rect.right + ' ' + Math.ceil(rect.bottom / 2) + ' ' + rect.left).start(rect.top + ' ' + rect.right + ' ' + rect.bottom + ' ' + rect.left);
				}.pass(rect, this);
				var rect = this._rect(img);
				img.setStyle('visibility', 'visible').get('tween').set(rect.top + ' ' + rect.right + ' ' + rect.bottom + ' ' + rect.left).start(rect.top + ' ' + rect.right + ' ' + Math.ceil(rect.bottom / 2) + ' ' + rect.left).start(rect.top + ' ' + rect.left + ' ' + Math.ceil(rect.bottom / 2) + ' ' + rect.left).chain(fn);
			}
		}
	},
	
	/**
	Private method: rect
		Calculates the clipping rect
	*/

	_rect: function(img){
		var rect = img.getCoordinates(this.slideshow.retrieve('images'));
		rect.right = (rect.right > this.width) ? this.width - rect.left : rect.width;
		rect.bottom = (rect.bottom > this.height) ? this.height - rect.top : rect.height;
		rect.top = (rect.top < 0) ? Math.abs(rect.top) : 0;
		rect.left = (rect.left < 0) ? Math.abs(rect.left) : 0;
		return rect;		
	}
});

/**
Script: Slideshow.KenBurns.js
	Slideshow.KenBurns - KenBurns extension for Slideshow, includes zooming and panning effects.

License:
	MIT-style license.

Copyright:
	Copyright (c) 2008 [Aeron Glemann](http://www.electricprism.com/aeron/).
	
Dependencies:
	Slideshow.
*/

Slideshow.KenBurns = new Class({
	Extends: Slideshow,
	
	options: {
		pan: [100, 100],
		zoom: [50, 50]
	},
	
/**
Constructor: initialize
	Creates an instance of the Slideshow class.

Arguments:
	element - (element) The wrapper element.
	data - (array or object) The images and optional thumbnails, captions and links for the show.
	options - (object) The options below.

Syntax:
	var myShow = new Slideshow.KenBurns(element, data, options);
*/

	initialize: function(el, data, options){
		options.overlap = true;
		options.resize = true;
		['pan', 'zoom'].each(function(p){
				if ($chk(this[p])){
					if ($type(this[p]) != 'array') this[p] = [this[p], this[p]];
					this[p].map(function(n){return (n.toInt() || 0).limit(0, 100);});					
				}
		}, options);
		this.parent(el, data, options);
	},

/**
Private method: show
	Does the slideshow effect.
*/

	_show: function(fast){
		if (!this.image.retrieve('morph')){
			['a', 'b'].each(function(image){
				this[image].set('tween', {
					'duration': this.options.duration, 'link': 'cancel', 'onStart': this._start.bind(this), 'onComplete': this._complete.bind(this), 'property': 'opacity'}
				).get('morph', {
					'duration': (this.options.delay + this.options.duration * 2), 'link': 'cancel', 'transition': $arguments(0)}
				);
			}, this);
		}
		this.image.set('styles', {'bottom': 'auto', 'left': 'auto', 'right': 'auto', 'top': 'auto'});
		var props = ['top left', 'top right', 'bottom left', 'bottom right'][this.counter % 4].split(' ');
		props.each(function(prop){this.image.setStyle(prop, 0);}, this);
		dh = this.height / this.preloader.height;
		dw = this.width / this.preloader.width;
		delta = (dw > dh) ? dw : dh;
		var values = {};
		var zoom = ($random.run(this.options.zoom) / 100.0) + 1;
		var pan = Math.abs(($random.run(this.options.pan) / 100.0) - 1);
		['height', 'width'].each(function(prop, i){
			var e = Math.ceil(this.preloader[prop] * delta);
			var s = (e * zoom).toInt();		
			values[prop] = [s, e];
			if (dw > dh || i){
				e = (this[prop] - this.image[prop]);
				s = (e * pan).toInt();			
				values[props[i]] = [s, e];
			}
		}, this);
		var paused = (this.firstrun && this.options.paused);
		if (fast || paused){
			this._center(this.image);
			this.image.get('morph').cancel();
			if (paused)
				this.image.get('tween').cancel().set(0).start(1);
			else
				this.image.get('tween').cancel().set(1);
		} 
		else{
			this.image.get('morph').start(values);
			this.image.get('tween').set(0).start(1);
		}
	}
});

/**
Script: Slideshow.Push.js
	Slideshow.Push - Push extension for Slideshow.

License:
	MIT-style license.

Copyright:
	Copyright (c) 2008 [Aeron Glemann](http://www.electricprism.com/aeron/).
	
Dependencies:
	Slideshow.
	Mootools 1.2 More: Fx.Elements.
*/

Slideshow.Push = new Class({
	Extends: Slideshow,
	
/**
Constructor: initialize
	Creates an instance of the Slideshow class.

Arguments:
	element - (element) The wrapper element.
	data - (array or object) The images and optional thumbnails, captions and links for the show.
	options - (object) The options below.

Syntax:
	var myShow = new Slideshow.Push(element, data, options);
*/
	
	initialize: function(el, data, options){
		options.overlap = true;		
		this.parent(el, data, options);
	},

/**
Private method: show
	Does the slideshow effect.
*/

	_show: function(fast){
		var images = [this.image, ((this.counter % 2) ? this.a : this.b)];
		if (!this.image.retrieve('fx'))
			this.image.store('fx', new Fx.Elements(images, {'duration': this.options.duration, 'link': 'cancel', 'onStart': this._start.bind(this), 'onComplete': this._complete.bind(this), 'transition': this.options.transition }));
		this.image.set('styles', {'left': 'auto', 'right': 'auto' }).setStyle(this.direction, this.width).setStyle('visibility', 'visible');
		var values = {'0': {}, '1': {} };
		values['0'][this.direction] = [this.width, 0];
		values['1'][this.direction] = [0, -this.width];
		if (images[1].getStyle(this.direction) == 'auto'){
			var width = this.width - images[1].width;	
			images[1].set('styles', {'left': 'auto', 'right': 'auto' }).setStyle(this.direction, width);		 
			values['1'][this.direction] = [width, -this.width];
		}
		if (fast){
		 	for (var prop in values)
		 		values[prop][this.direction] = values[prop][this.direction][1];			
			this.image.retrieve('fx').cancel().set(values);
		} 
		else
			this.image.retrieve('fx').start(values);
	}
});

/**
 * Swiff.Uploader - Flash FileReference Control
 *
 * @version		1.2
 *
 * @license		MIT License
 *
 * @author		Harald Kirschner <mail [at] digitarald [dot] de>
 * @copyright	Authors
 */

Swiff.Uploader = new Class({

	Extends: Swiff,

	Implements: Events,

	options: {
		path: '/objects/fu.swf',
		multiple: true,
		queued: true,
		typeFilter: null,
		url: null,
		method: 'post',
		data: null,
		fieldName: 'fileupload',
		target: null,
		height: '100%',
		width: '100%',
		callBacks: null
	},

	initialize: function(options){
		if (Browser.Plugins.Flash.version < 9) return false;
		this.setOptions(options);

		var callBacks = this.options.callBacks || this;
		if (callBacks.onLoad) this.addEvent('onLoad', callBacks.onLoad);
		if (!callBacks.onBrowse) {
			callBacks.onBrowse = function() {
				return this.options.typeFilter;
			}
		}

		var prepare = {}, self = this;
		['onBrowse', 'onSelect', 'onAllSelect', 'onCancel', 'onBeforeOpen', 'onOpen', 'onProgress', 'onComplete', 'onError', 'onAllComplete'].each(function(index) {
			var fn = callBacks[index] || $empty;
			prepare[index] = function() {
				self.fireEvent(index, arguments, 10);
				return fn.apply(self, arguments);
			};
		});

		prepare.onLoad = this.load.create({delay: 10, bind: this});
		this.options.callBacks = prepare;

		var path = this.options.path;
		if (!path.contains('?')) path += '?noCache=' + $time(); // quick fix

		this.parent(path);

		var scroll = window.getScroll();
		this.box = new Element('div', {
			styles: {
				position: 'absolute',
				visibility: 'visible',
				zIndex: 9999,
				overflow: 'hidden',
				height: 15, width: 15,
				top: scroll.y, left: scroll.x
			}
		});
		this.inject(this.box);
		this.box.inject($(this.options.container) || document.body);

		return this;
	},

	load: function(){
		this.remote('register', this.instance, this.options.multiple, this.options.queued);
		this.fireEvent('onLoad');

		this.target = $(this.options.target);
		if (Browser.Plugins.Flash.version >= 10 && this.target) {
			this.reposition();
			window.addEvent('resize', this.reposition.bind(this));
		}
	},

	reposition: function() {
		var pos = this.target.getCoordinates(this.box.getOffsetParent());
		this.box.setStyles(pos);
	},

	/*
	Method: browse
		Open the file browser.
	*/

	browse: function(typeFilter){
		this.options.typeFilter = $pick(typeFilter, this.options.typeFilter);
		return this.remote('browse');
	},

	/*
	Method: upload
		Starts the upload of all selected files.
	*/

	upload: function(options){
		var current = this.options;
		options = $extend({data: current.data, url: current.url, method: current.method, fieldName: current.fieldName}, options);
		if ($type(options.data) == 'element') options.data = $(options.data).toQueryString();
		return this.remote('upload', options);
	},

	/*
	Method: removeFile
		For multiple uploads cancels and removes the given file from queue.

	Arguments:
		name - (string) Filename
		name - (string) Filesize in byte
	*/

	removeFile: function(file){
		if (file) file = {name: file.name, size: file.size};
		return this.remote('removeFile', file);
	},

	/*
	Method: getFileList
		Returns one Array with with arrays containing name and size of the file.

	Returns:
		(array) An array with files
	*/

	getFileList: function(){
		return this.remote('getFileList');
	}

});



/**
 * @todo Clean-up, into Date.js
 */
Date.parseDuration = function(sec) {
	var units = {}, conv = Date.durations;
	for (var unit in conv) {
		var value = Math.floor(sec / conv[unit]);
		if (value) {
			units[unit] = value;
			if (!(sec -= value * conv[unit])) break;
		}
	}
	return units;
};

Date.fancyDuration = function(sec) {
	var ret = [], units = Date.parseDuration(sec);
	for (var unit in units) ret.push(units[unit] + Date.durationsAbbr[unit]);
	return ret.join(', ');
};

Date.durations = {years: 31556926, months: 2629743.83, days: 86400, hours: 3600, minutes: 60, seconds: 1, milliseconds: 0.001};
Date.durationsAbbr = {
	years: 'j',
	months: 'm',
	days: 'd',
	hours: 'h',
	minutes: 'min',
	seconds: 'sec',
	milliseconds: 'ms'
};

/**
 * Fx.ProgressBar
 *
 * @version		1.0
 *
 * @license		MIT License
 *
 * @author		Harald Kirschner <mail [at] digitarald [dot] de>
 * @copyright	Authors
 */

Fx.ProgressBar = new Class({

	Extends: Fx,

	options: {
		text: null,
		transition: Fx.Transitions.Circ.easeOut,
		link: 'cancel'
	},

	initialize: function(element, options) {
		this.element = $(element);
		this.parent(options);
		this.text = $(this.options.text);
		this.set(0);
	},

	start: function(to, total) {
		return this.parent(this.now, (arguments.length == 1) ? to.limit(0, 100) : to / total * 100);
	},

	set: function(to) {
		this.now = to;
		this.element.setStyle('backgroundPosition', (100 - to) + '% 0px');
		if (this.text) this.text.set('text', Math.round(to) + '%');
		return this;
	}

});

