/* PLUGINS */

/*
 Slimbox v1.71 - The ultimate lightweight Lightbox clone
 (c) 2007-2009 Christophe Beyls <http://www.digitalia.be>
 MIT-style license.
 */
var Slimbox = (function() {
	var F = window, n = Browser.Engine.trident4, u, g, G = -1, o, w, E, v, y, M, s, m = {}, t = new Image(), K = new Image(), I, a, h, q, J, e, H, c, A, L, x, i, d, C;
	F.addEvent("domready", function() {
		$(document.body).adopt($$( I = new Element("div", {
			id : "lbOverlay",
			events : {
				click : D
			}
		}), a = new Element("div", {
			id : "lbCenter"
		}), H = new Element("div", {
			id : "lbBottomContainer"
		})).setStyle("display", "none"));
		h = new Element("div", {
			id : "lbImage"
		}).injectInside(a).adopt( q = new Element("div", {
			styles : {
				position : "relative"
			}
		}).adopt( J = new Element("a", {
			id : "lbPrevLink",
			href : "#",
			events : {
				click : B
			}
		}), e = new Element("a", {
			id : "lbNextLink",
			href : "#",
			events : {
				click : f
			}
		})));
		c = new Element("div", {
			id : "lbBottom"
		}).injectInside(H).adopt(new Element("a", {
			id : "lbCloseLink",
			href : "#",
			events : {
				click : D
			}
		}), A = new Element("div", {
			id : "lbCaption"
		}), L = new Element("div", {
			id : "lbNumber"
		}), new Element("div", {
			styles : {
				clear : "both"
			}
		}))
	});
	function z() {
		var N = F.getScroll(), O = F.getSize();
		$$(a, H).setStyle("left", N.x + (O.x / 2));
		if(v) {
			I.setStyles({
				left : N.x,
				top : N.y,
				width : O.x,
				height : O.y
			})
		}
	}

	function l(N) {
		["object", n ? "select" : "embed"].forEach(function(P) {
			Array.forEach(document.getElementsByTagName(P), function(Q) {
				if(N) {
					Q._slimbox = Q.style.visibility
				}
				Q.style.visibility = N ? "hidden" : Q._slimbox
			})
		});
		I.style.display = N ? "" : "none";
		var O = N ? "addEvent" : "removeEvent";
		F[O]("scroll",z)[O]("resize", z);
		document[O]("keydown", p)
	}

	function p(O) {
		var N = O.code;
		return u.closeKeys.contains(N) ? D() : u.nextKeys.contains(N) ? f() : u.previousKeys.contains(N) ? B() : false
	}

	function B() {
		return b(w)
	}

	function f() {
		return b(E)
	}

	function b(N) {
		if(N >= 0) {
			G = N;
			o = g[N][0];
			w = (G || (u.loop ? g.length : 0)) - 1;
			E = ((G + 1) % g.length) || (u.loop ? 0 : -1);
			r();
			a.className = "lbLoading";
			m = new Image();
			m.onload = k;
			m.src = o
		}
		return false
	}

	function k() {
		a.className = "";
		d.set(0);
		h.setStyles({
			backgroundImage : "url(" + o + ")",
			display : ""
		});
		q.setStyle("width", m.width);
		$$(q, J, e).setStyle("height", m.height);
		A.set("html", g[G][1] || "");
		L.set("html", (((g.length > 1) && u.counterText) || "").replace(/{x}/, G + 1).replace(/{y}/, g.length));
		if(w >= 0) {
			t.src = g[w][0]
		}
		if(E >= 0) {
			K.src = g[E][0]
		}
		M = h.offsetWidth;
		s = h.offsetHeight;
		var P = Math.max(0, y - (s / 2)), N = 0, O;
		if(a.offsetHeight != s) {
			N = i.start({
				height : s,
				top : P
			})
		}
		if(a.offsetWidth != M) {
			N = i.start({
				width : M,
				marginLeft : -M / 2
			})
		}
		O = function() {
			H.setStyles({
				width : M,
				top : P + s,
				marginLeft : -M / 2,
				visibility : "hidden",
				display : ""
			});
			d.start(1)
		};
		if(N) {
			i.chain(O)
		} else {
			O()
		}
	}

	function j() {
		if(w >= 0) {
			J.style.display = ""
		}
		if(E >= 0) {
			e.style.display = ""
		}
		C.set(-c.offsetHeight).start(0);
		H.style.visibility = ""
	}

	function r() {
		m.onload = $empty;
		m.src = t.src = K.src = o;
		i.cancel();
		d.cancel();
		C.cancel();
		$$(J, e, h, H).setStyle("display", "none")
	}

	function D() {
		if(G >= 0) {
			r();
			G = w = E = -1;
			a.style.display = "none";
			x.cancel().chain(l).start(0)
		}
		return false
	}
	Element.implement({
		slimbox : function(N, O) {
			$$(this).slimbox(N, O);
			return this
		}
	});
	Elements.implement({
		slimbox : function(N, Q, P) {
			Q = Q ||
			function(R) {
				return [R.href, R.title]
			};

			P = P ||
			function() {
				return true
			};

			var O = this;
			O.removeEvents("click").addEvent("click", function() {
				var R = O.filter(P, this);
				return Slimbox.open(R.map(Q), R.indexOf(this), N)
			});
			return O
		}
	});
	return {
		open : function(P, O, N) {
			u = $extend({
				loop : false,
				overlayOpacity : 0.8,
				overlayFadeDuration : 400,
				resizeDuration : 400,
				resizeTransition : false,
				initialWidth : 250,
				initialHeight : 250,
				imageFadeDuration : 400,
				captionAnimationDuration : 400,
				counterText : "Image {x} of {y}",
				closeKeys : [27, 88, 67],
				previousKeys : [37, 80],
				nextKeys : [39, 78]
			}, N || {});
			x = new Fx.Tween(I, {
				property : "opacity",
				duration : u.overlayFadeDuration
			});
			i = new Fx.Morph(a, $extend({
				duration : u.resizeDuration,
				link : "chain"
			}, u.resizeTransition ? {
				transition : u.resizeTransition
			} : {}));
			d = new Fx.Tween(h, {
				property : "opacity",
				duration : u.imageFadeDuration,
				onComplete : j
			});
			C = new Fx.Tween(c, {
				property : "margin-top",
				duration : u.captionAnimationDuration
			});
			if( typeof P == "string") {
				P = [[P, O]];
				O = 0
			}
			y = F.getScrollTop() + (F.getHeight() / 2);
			M = u.initialWidth;
			s = u.initialHeight;
			a.setStyles({
				top : Math.max(0, y - (s / 2)),
				width : M,
				height : s,
				marginLeft : -M / 2,
				display : ""
			});
			v = n || (I.currentStyle && (I.currentStyle.position != "fixed"));
			if(v) {
				I.style.position = "absolute"
			}
			x.set(0).start(u.overlayOpacity);
			z();
			l(1);
			g = P;
			u.loop = u.loop && (g.length > 1);
			return b(O)
		}
	}
})();

// AUTOLOAD CODE BLOCK (MAY BE CHANGED OR REMOVED)
Slimbox.scanPage = function() {
	$$("a").filter(function(el) {
		return el.rel && el.rel.test(/^lightbox/i);
	}).slimbox({/* Put custom options here */}, null, function(el) {
		return (this == el) || ((this.rel.length > 8) && (this.rel == el.rel));
	});
};
if(!/android|iphone|ipod|series60|symbian|windows ce|blackberry/i.test(navigator.userAgent)) {
	window.addEvent("domready", Slimbox.scanPage);
}

/*
 ---
 description: TinyTab - Tiny and simple tab handler for Mootools.

 license: MIT-style

 authors:
 - Danillo C�sar de O. Melo

 requires:
 - core/1.2.4: '*'

 provides: TinyTab

 ...
 */
(function($) {
	this.TinyTab = new Class({
		Implements : Events,
		initialize : function(tabs, contents, opt) {
			this.tabs = tabs;
			this.contents = contents;
			if(!opt)
				opt = {};
			this.css = opt.selectedClass || 'selected';
			this.select(this.tabs[0]);
			tabs.addEvent('click', function(e) {
				this.select(e.target);
				e.stop();
			}.bind(this));
		},
		select : function(el) {
			this.tabs.removeClass(this.css);
			el.addClass(this.css);
			this.contents.setStyle('display', 'none');
			var content = this.contents[this.tabs.indexOf(el)];
			content.setStyle('display', 'block');
			this.fireEvent('change', [content, el]);
		}
	});
})(document.id);

/* TAB */

/*
 Script: PeriodicalExecuter.js
 port of the Prototype.js timer to Mootools

 License: MIT-style license.
 Copyright: Copyright (c) 2007 Thierry bela <bntfr at yahoo dot fr>

 License:
 MIT-style license.

 Authors:
 Thierry Bela

 TODO: possibility to stop the timer when the window is idle ?
 */

var PeriodicalExecuter = new Class({
	initialize : function(callback, frequency) {
		this.callback = callback;
		this.frequency = frequency;
		this.currentlyExecuting = false;
		this.registerCallback()
	},
	registerCallback : function() {
		this.stop();
		this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
		return this
	},
	execute : function() {
		this.callback(this);
		return this
	},
	stop : function() {
		if(!this.timer)
			return this;
		clearInterval(this.timer);
		this.timer = null;
		return this
	},
	onTimerEvent : function() {
		if(!this.currentlyExecuting) {
			try {
				this.currentlyExecuting = true;
				this.execute();
			} finally {
				this.currentlyExecuting = false;
			}
		}
		return this
	}
});

/*
 ---
 script: Tab.js
 license: MIT-style license.
 description: Tab - Minimalistic but extensible tab swapper.
 copyright: Copyright (c) 2008 Thierry Bela
 authors: [Thierry Bela]

 requires:
 core:1.2.3:
 - Class.Extras
 - Element.Event
 - Element.Style
 - Element.Dimensions
 - Fx.Morph
 - Array
 provides: [Tab, Tab.plugins.None]
 ...
 */

var Tab = new Class({
	options : {
		fx : {
			transition : 'sine:out',
			link : 'chain'
		},
		inactiveClass : '',
		activeClass : '',
		animation : 'None',
		selector: 'li',
	},
	current : 0,
	Implements : [Options, Events],
	initialize : function(options) {
		this.addEvents({
			onCreate : function(newPanel, index) {
				this.tabs.each(function(el, val) {
					el[val==index?'removeClass':'addClass'](options.inactiveClass)[val==index?'addClass':'removeClass'](options.activeClass)
				});
				this.selected = newPanel;
				this.current = index
			}.bind(this),
			onChange : function(newPanel, oldPanel, index, oldIndex) {
				var _new = this.tabs[index], _old = this.tabs[oldIndex], options = this.options;
				if(_old)
					_old.removeClass(options.activeClass).addClass(options.inactiveClass);
				if(_new)
					_new.removeClass(options.inactiveClass).addClass(options.activeClass);
				this.selected = newPanel;
				this.current = index
			}.bind(this)
		}).setOptions(options);
		options = this.options;
		this.tabs = $$(options.tabs);
		this.panels = $(options.container).getChildren(options.selector);
		this.tabs.each(function(el, index) {
			el.set({
				styles : {
					cursor : 'pointer'
				},
				events : {
					click : function(e) {
						e.stop();
						var forward = this.current < index ? index - this.current : this.panels.length - this.current + index, backward = this.current > index ? this.current - index : this.current + this.panels.length - index;
						this.setSelectedIndex(index, Math.abs(forward) <= Math.abs(backward) ? 1 : -1)
					}.bind(this)
				}
			}).addClass(options.inactiveClass).removeClass(options.activeClass);
		}, this);
		this.anim = new this.plugins[options.animation](this.panels, options.params, options.fx);
		var current = options.current || 0;
		this.fireEvent('onCreate', [this.panels[current], current]);
		this.setSelectedIndex(current || 0);
		return this
	},
	next : function() {
		return this.setSelectedIndex((this.getSelectedIndex() + this.panels.length + 1) % this.panels.length, 1);
	},
	previous : function() {
		return this.setSelectedIndex((this.getSelectedIndex() + this.panels.length - 1) % this.panels.length, -1);
	},
	getSelectedIndex : function() {
		return this.current
	},
	setSelectedIndex : function(index, direction) {
		var current = this.current, curPanel = this.panels[current], newPanel = this.panels[index], params = [newPanel, curPanel, index, current, direction];
		if(this.current == index || this.selected == newPanel || index < 0 || index >= this.panels.length)
			return this;
		this.anim.move.apply(this.anim, params);
		return this.fireEvent('onChange', params)
	}
});
Tab.prototype.plugins = {
	None : new Class({
		initialize : function(panels) {
			panels.each(function(el, index) {
				el.setStyle('display', index == 0 ? 'block' : 'none')
			})
		},
		move : function(newPanel, oldPanel) {
			newPanel.setStyle('display', 'block');
			if(oldPanel)
				oldPanel.setStyle('display', 'none')
		}
	})
};

/*
 ---
 script: Tab.Extra.js
 license: MIT-style license.
 description: Extends Tab with automatic slide.
 copyright: Copyright (c) 2008 Thierry Bela
 authors: [Thierry Bela]

 requires:
 - Tab
 provides: [Tab.Extra]
 ...
 */

Tab.Extra = new Class({
	Extends : Tab,
	Binds : ['update', 'start', 'stop'],
	initialize : function(options) {
		this.parent(options);
		this.reverse = !!this.options.reverse;
		this.timer = new PeriodicalExecuter(this.update, options.interval || 10);
		return this
	},
	update : function() {
		return this[this.reverse?'previous':'next']()
	},
	start : function() {
		return this.timer.registerCallback();
		return this
	},
	stop : function() {
		this.timer.stop();
		return this
	}
});

/*
 ---
 script: Tabs.Plugins.Move.js
 license: MIT-style license.
 description: Move - swap tab horizontally or vertically.
 copyright: Copyright (c) 2008 Thierry Bela
 authors: [Thierry Bela]

 requires:
 tab:0.1.3.6:
 - Tab
 provides: [Tab.plugins.Move]
 ...
 */

Tab.prototype.plugins.Move = new Class({
	options : {
		mode : 'horizontal'
	},
	fx : {
		link : 'chain',
		duration : 1000
	},
	initialize : function(panels, options, fx) {
		this.options = $merge(this.options, options);
		this.panels = panels.map(function(el) {
			return el.setStyles({
				position : 'absolute',
				display : 'block'
			})
		});
		this.fx = new Fx.Elements(panels, $merge(this.fx, fx));
		this.reorder(0, 1).container = panels[0].getParent().setStyles({
			overflow : 'hidden',
			position : 'relative',
			width : panels[0].offsetWidth,
			height : panels[0].offsetHeight
		})
	},
	reorder : function(offset, direction) {
		var pos = 0, i, options = this.options, panels = this.panels, length = panels.length, horizontal = options.mode == 'horizontal', side = horizontal ? 'offsetWidth' : 'offsetHeight';
		if(direction == -1) {
			for( i = length; i > 0; i--) {
				index = (i + offset + length) % length;
				panel = panels[index];
				if(horizontal)
					panel.setStyle('left', pos);
				else
					panel.setStyles({
						left : 0,
						top : pos
					});
				pos -= panel[side];
			}
		} else if(direction == 1)
			for( i = 0; i < length; i++) {
				index = (i + offset + length) % length;
				panel = panels[index];
				if(horizontal)
					panel.setStyle('left', pos);
				else
					panel.setStyles({
						left : 0,
						top : pos
					});
				pos += panel[side];
			}
		return this
	},
	move : function(newTab, curTab, newIndex, oldIndex, direction) {
		this.fx.cancel();
		var obj = {}, options = this.options, horizontal = options.mode == 'horizontal', property = horizontal ? 'offsetLeft' : 'offsetTop', offset, opacity = options.opacity || .7;
		if(oldIndex != undefined && options.circular)
			this.reorder(oldIndex, direction);
		offset = newTab[property];
		this.panels.each(function(panel, index) {
			obj[index] = horizontal ? {
				opacity : opacity,
				left : [panel[property], panel[property] - offset]
			} : {
				opacity : opacity,
				top : [panel[property], panel[property] - offset]
			};
		});
		if(!options.useOpacity)
			$each(obj, function(k) {
				delete k.opacity
			});
		this.fx.start(obj).chain( function() {
			newTab.tween('opacity', 1)
		}.bind(this));
		this.container.morph({
			height : newTab.offsetHeight,
			width : newTab.offsetWidth
		})
	}
});

/*

 Plugin:			Google Analytics Events Tracker Version 1.0
 Creator:		Copyright (C) 2010, Antonio @ Cubic-Square.com, http://www.cubic-square.com

 */

var gaEventsTracker = new Class({
	Implements : [Options],
	triggers : [],
	options : {
		method : '_trackEvent',
		category : '',
		action : '',
		label : '',
		value : '',
		labelRef : 'href',
		cleanEmail : false,
		cleanURL : false
	},
	initialize : function(triggers, options) {
		if(options == undefined)
			this.setOptions(triggers);
		if(triggers.length) {
			this.setOptions(options);
			this.build(triggers);
		}
	},
	email : function(email) {
		return;
	},
	build : function(triggers) {
		$$(triggers).each(function(el, i) {
			this.triggers.include(document.id(el));
			var elStorage = new Hash(this.options);
			elStorage.each(function(value, key) {
				if(key == 'label') {
					value = (value != '') ? value + el.get(elStorage.get('labelRef')) : el.get(elStorage.get('labelRef'));
				}
				el.store(key, value);
			});
			el.addEvent('click', function(e) {
				if(this.retrieve('cleanEmail') == true && this.retrieve('label').contains('mailto:')) {
					this.labelRef = (this.retrieve('label')).replace('mailto:', '');
					if((this.labelRef).contains('?')) {
						this.labelRef = (this.labelRef).substring(0, (this.labelRef).indexOf('?'));
					}
					this.store('label', this.labelRef);
				}
				_gaq.push([this.retrieve('method'), this.retrieve('category'), this.retrieve('action'), this.retrieve('label')]);
			});
		}, this);
	},
	add : function(el, options) {
		this.setOptions(options);
		if($$(el).length != 0) {
			this.build($splat($$(el)));
		} else {
			this.build($splat(document.id(el)));
		}
	}
});

/*
 Get Tweets with MooTools Class
 REF: http://appden.com/javascript/get-tweets-with-mootools/
 Modified: Line
 */

Request.Twitter = new Class({
	Extends : Request.JSONP,
	options : {
		linkify : true,
		url : 'http://twitter.com/statuses/user_timeline/{term}.json',
		data : {
			count : 5
		}
	},
	initialize : function(term, options) {
		this.parent(options);
		this.options.url = this.options.url.substitute({
			term : term
		});
	},
	success : function(data, script) {
		if(this.options.linkify) {
			data[0].each(function(tweet) {
				tweet.text = this.linkify(tweet.text);
			}, this);
		}
		if(data[0])
			this.options.data.since_id = data[0].id;
		this.parent(data, script);
	},
	linkify : function(text) {
		return text.replace(/(https?:\/\/[\w\-:;?&=+.%#\/]+)/gi, '<a href="$1">$1</a>').replace(/(^|\W)@(\w+)/g, '$1<a href="http://twitter.com/$2">@$2</a>').replace(/(^|\W)#(\w+)/g, '$1#<a href="http://search.twitter.com/search?q=%23$2">$2</a>');
	}
});

/**
 * Autocompleter
 *
 * http://digitarald.de/project/autocompleter/
 *
 * @version		1.1.2
 *
 * @license		MIT-style license
 * @author		Harald Kirschner <mail [at] digitarald.de>
 * @copyright	Author
 */

var Autocompleter = new Class({
	Implements : [Options, Events],
	options : {
		minLength : 1,
		markQuery : true,
		width : 'inherit',
		maxChoices : 10,
		injectChoice : null,
		customChoices : null,
		emptyChoices : null,
		visibleChoices : true,
		className : 'autocompleter-choices',
		zIndex : 42,
		delay : 400,
		observerOptions : {},
		fxOptions : {},
		autoSubmit : false,
		overflow : false,
		overflowMargin : 25,
		selectFirst : false,
		filter : null,
		filterCase : false,
		filterSubset : false,
		forceSelect : false,
		selectMode : true,
		choicesMatch : null,
		multiple : false,
		separator : ', ',
		separatorSplit : /\s*[,;]\s*/,
		autoTrim : false,
		allowDupes : false,
		cache : true,
		relative : false
	},
	initialize : function(element, options) {
		this.element = $(element);
		this.setOptions(options);
		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 : 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(document.body);
			this.relative = false;
			if(this.options.relative) {
				this.choices.inject(this.element, 'after');
				this.relative = this.element.getOffsetParent();
			}
			this.fix = new OverlayFix(this.choices);
		}
		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, $merge({
			'property' : 'opacity',
			'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
		}));
	},
	destroy : function() {
		if(this.fix)
			this.fix.destroy();
		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) {
			var split = this.options.separatorSplit;
			value = this.element.value;
			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).filter(function(entry) {
					return this.test(entry);
				}, /[^\s,]+/);
				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.fix) {
			var pos = this.element.getCoordinates(this.relative), width = this.options.width || '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;
			this.choices.setStyle('display', '');
			if(this.fx)
				this.fx.start(1);
			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' : ''
		};
		this.overflown = false;
		if(items.length > max) {
			var item = items[max - 1];
			styles.overflowY = 'scroll';
			styles.height = item.getCoordinates(this.choices).bottom;
			this.overflown = true;
		};
		this.choices.setStyles(styles);
		this.fix.show();
		if(this.options.visibleChoices) {
			var scroll = document.getScroll(), size = document.getSize(), coords = this.choices.getCoordinates();
			if(coords.right > scroll.x + size.x)
				scroll.x = coords.right - size.x;
			if(coords.bottom > scroll.y + size.y)
				scroll.y = coords.bottom - size.y;
			window.scrollTo(Math.min(scroll.x, coords.left), Math.min(scroll.y, coords.top));
		}
	},
	hideChoices : function(clear) {
		if(clear) {
			var value = this.element.value;
			if(this.options.forceSelect)
				value = this.opted;
			if(this.options.autoTrim) {
				value = value.split(this.options.separatorSplit).filter($arguments(0)).join(this.options.separator);
			}
			this.observer.setValue(value);
		}
		if(!this.visible)
			return;
		this.visible = false;
		if(this.selected)
			this.selected.removeClass('autocompleter-selected');
		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.getSelectedRange().start;
			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;
		var type = tokens && $type(tokens);
		if(!type || (type == 'array' && !tokens.length) || (type == 'hash' && !tokens.getLength())) {
			(this.options.emptyChoices || this.hideChoices).call(this);
		} 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(!this.selectMode)
			this.opted = this.element.value;
		if(!selection)
			return;
		this.selectedValue = this.selected.inputValue;
		if(this.overflown) {
			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) {
		return (tokens || this.tokens).filter(function(token) {
			return this.test(token);
		}, new RegExp(((this.options.filterSubset) ? '' : '^') + this.queryValue.escapeRegExp(), (this.options.filterCase) ? '' : 'i'));
	},
	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 : function(el) {
		return el.addEvents({
			'mouseover' : this.choiceOver.bind(this, [el]),
			'click' : this.choiceSelect.bind(this, [el])
		});
	}
});
var OverlayFix = new Class({
	initialize : function(el) {
		if(Browser.Engine.trident) {
			this.element = $(el);
			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) {
			var coords = this.element.getCoordinates(this.relative);
			delete coords.right;
			delete coords.bottom;
			this.fix.setStyles($extend(coords, {
				'display' : '',
				'zIndex' : (this.element.getStyle('zIndex') || 1) - 1
			}));
		}
		return this;
	},
	hide : function() {
		if(this.fix)
			this.fix.setStyle('display', 'none');
		return this;
	},
	destroy : function() {
		if(this.fix)
			this.fix = this.fix.destroy();
	}
});
Element.implement({
	getSelectedRange : function() {
		if(!Browser.Engine.trident)
			return {
				start : this.selectionStart,
				end : this.selectionEnd
			};
		var pos = {
			start : 0,
			end : 0
		};
		var range = this.getDocument().selection.createRange();
		if(!range || range.parentElement() != this)
			return pos;
		var dup = range.duplicate();
		if(this.type == 'text') {
			pos.start = 0 - dup.moveStart('character', -100000);
			pos.end = pos.start + range.text.length;
		} else {
			var value = this.value;
			var offset = value.length - value.match(/[\n\r]*$/)[0].length;
			dup.moveToElementText(this);
			dup.setEndPoint('StartToEnd', range);
			pos.end = offset - dup.text.length;
			dup.setEndPoint('StartToStart', range);
			pos.start = offset - dup.text.length;
		}
		return pos;
	},
	selectRange : function(start, end) {
		if(Browser.Engine.trident) {
			var diff = this.value.substr(start, end - start).replace(/\r/g, '').length;
			start = this.value.substr(0, start).replace(/\r/g, '').length;
			var range = this.createTextRange();
			range.collapse(true);
			range.moveEnd('character', start + diff);
			range.moveStart('character', start);
			range.select();
		} else {
			this.focus();
			this.setSelectionRange(start, end);
		}
		return this;
	}
});
Autocompleter.Base = Autocompleter;

/**
 * Autocompleter.Request
 *
 * http://digitarald.de/project/autocompleter/
 *
 * @version		1.1.2
 *
 * @license		MIT-style license
 * @author		Harald Kirschner <mail [at] digitarald.de>
 * @copyright	Author
 */
Autocompleter.Request = new Class({
	Extends : Autocompleter,
	options : {
		postData : {},
		ajaxOptions : {},
		postVar : 'value'
	},
	query : function() {
		var data = $unlink(this.options.postData) || {};
		data[this.options.postVar] = this.queryValue;
		var indicator = $(this.options.indicator);
		if(indicator)
			indicator.setStyle('display', '');
		var cls = this.options.indicatorClass;
		if(cls)
			this.element.addClass(cls);
		this.fireEvent('onRequest', [this.element, this.request, data, this.queryValue]);
		this.request.send({
			'data' : data
		});
	},
	queryResponse : function() {
		var indicator = $(this.options.indicator);
		if(indicator)
			indicator.setStyle('display', 'none');
		var cls = this.options.indicatorClass;
		if(cls)
			this.element.removeClass(cls);
		return this.fireEvent('onComplete', [this.element, this.request]);
	}
});
Autocompleter.Request.JSON = new Class({
	Extends : Autocompleter.Request,
	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.Request.HTML = new Class({
	Extends : Autocompleter.Request,
	initialize : function(el, url, options) {
		this.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(tree, elements) {
		this.parent();
		if(!elements || !elements.length) {
			this.hideChoices();
		} else {
			this.choices.getChildren(this.options.choicesMatch).each(this.options.injectChoice ||
			function(choice) {
				var value = choice.innerHTML;
				choice.inputValue = value;
				this.addChoiceEvents(choice.set('html', this.markQueryValue(value)));
			}, this);


			this.showChoices();
		}
	}
});
Autocompleter.Ajax = {
	Base : Autocompleter.Request,
	Json : Autocompleter.Request.JSON,
	Xhtml : Autocompleter.Request.HTML
};

/**
 * Observer - Observe formelements for changes
 *
 * - Additional code from clientside.cnet.com
 *
 * @version		1.1
 *
 * @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.element = $(el) || $$(el);
		this.addEvent('onFired', onFired);
		this.setOptions(options);
		this.bound = this.changed.bind(this);
		this.resume();
	},
	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;
	},
	pause : function() {
		if(this.timer)
			$clear(this.timer);
		else
			this.element.removeEvent('keyup', this.bound);
		return this.clear();
	},
	resume : function() {
		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.bound);
		return this;
	}
});
var $equals = function(obj1, obj2) {
	return (obj1 == obj2 || JSON.encode(obj1) == JSON.encode(obj2));
};

