/*	FreeTransform, version 0.84
 *	(c) 2007-2009 Savva Mikhalevski (smikhalevski@gmail.com, connect@apxe.org)
 *
 *	FreeTransform exploits Prototype javascript framework which is property of Sam Stephenson.
 *	FreeTransform is freely distributable under the terms of an MIT-style license.
 *	Please, feel free to contact me on any questions or suggestions on how to modify this lib.
 */

var Prototype,Class,Utils;
(function() {
	if (!Prototype || !Class || !Utils)
		return setTimeout(arguments.callee,1000);
	var reflections = [],minDelay = 5,man = new Utils;
	var getDOMElement = function(obj) {
		return man.isElement(obj = $(obj))?obj:null;
	};
	var getReflection = function(transform) {
		if (transform instanceof Transform)
			return reflections[transform.toString()];
		return undefined;
	};
	var Slice = Class.create({
		initialize:function(styles,grads) {
			var myself = this;
			Object.extend(this,{styles:$A(styles),grads:$A(grads)});
			this.clone = function(target) {
				return new Slice(myself.styles.collect(function(style) {
					var dest = man.isElement(style.target)?style.target:target;
					return {callback:style.callback,property:style.property,target:dest};
				}),myself.grads.invoke('clone'));
			};
			this.unzip = function(target) {
				if (!man.isUndefined(target)) with (myself)
					for (var i = 0; i < styles.length; i++)
						if (styles.target == target) {
							styles.splice(i,1);
							grads.splice(i,1);
						};
				return myself;
			};
			this.zip = function(slice) {
				if (slice instanceof Slice) with (myself) {
					styles = styles.concat(slice.styles);
					grads = grads.concat(slice.grads);
				} return myself;
			};
			this.getTargets = function() {
				var result = $A(new Array);
				myself.styles.each(function(style) {
					if (result.indexOf(style.target) < 0)
						result.push(style.target);
				});
				return result;
			};
		}
	});
	var Reflection = Class.create({
		initialize:function(schema,delay,rootNode) {
			var myself = this;
			var sys = {events:{},timers:{},index:{},drc:{},sleep:{},fncs:{}};
			this.getSchema = function() {
				return schema.collect(function(slice) {
					return slice.clone();
				});
			};
			this.setSchema = function(str,rootNode) {
				setRootNode(rootNode);
				schema = extractSchema(str);
				myself.dropIndex();
				for (var i in (str = sys.sleep))
					if (str[i]) setHardPause(str[i].obj,false);
				return myself;
			};
			this.setDelay = function(msec) {
				if (!man.isUndefined(delay)) delay = ((msec = msec || 0) < minDelay)?minDelay:msec;
				return myself;
			};
			this.getDelay = function() {
				return delay;
			};
			this.getIndex = function(obj) {
				var index = man.isElement(obj)?sys.index[getGrName(obj)]:undefined;
				if (obj instanceof Fader || obj instanceof Hover)
					index = getReflection(obj.getTransform()).getIndex(obj.getElement());
				return man.isUndefined(index)?undefined:$A(index);
			};
			this.buildIndex = function(obj,subnodes) {
				var index = undefined;
				if (index = myself.getIndex(obj)) return index;
				if (!setGrName(getTargets().concat([obj = getDOMElement(obj)]),subnodes)) return;
				index = sys.index[getGrName(obj)] = [];
				schema.each(function(slice,level) {
					if (!slice) return undefined;
					var a = index[level] = [];
					$A(slice.styles).each(function(style,i,nodes) {
						if (man.isFunction(style.callback)) return;
						var target = man.isElement(style.target)?style.target:obj;
						(nodes = $(target).descendants()).push(target);
						a[i] = getChildren(subnodes?nodes:[target],style.property);
					});
				});
				return index;
			};
			this.dropIndex = function(obj) {
				if (!man.isUndefined(obj)) sys.index[getGrName(obj)] = undefined;
				else for (var key in sys.index) if (new String(key).match(man.getRegExp('r/d{8}')))
					sys.index[key] = undefined;
				return myself;
			};
			this.pause = function(obj) {
				return void(setPause(obj,true)) || myself;
			};
			this.unpause = function(obj) {
				return void(setPause(obj,false)) || myself;
			};
			this.immediateStop = function(obj) {
				clearTimeout((getTimer(obj) || {}).timer || 0);
				if (!man.isUndefined(myself.getLastPlay(obj)))
					sys.drc[getGrName(obj)] = null;
				setHardPause(obj,false);
				return myself;
			};
			this.playForward = function(obj,subnodes,fnc) {
				return void(play(obj,true,subnodes,fnc)) || myself;
			};
			this.playBackward = function(obj,subnodes,fnc) {
				var dir = myself.getLastPlay(obj);
				if (dir === null || !man.isUndefined(dir) && !isFirstFrame(obj))
					play(obj,false,subnodes,fnc);
				else man.execArray(fnc);
				return myself;
			};
			this.rewind = function(obj) {
				sys.drc[getGrName(obj)] = undefined;
				with (getTimer(obj).phase) level = i = 0;
				setHardPause(obj,false);
				return myself;
			};
			this.removeObserver = function(obj) {
				var evt,obj = getDOMElement(obj);
				if (obj && (evt = sys.events[getGrName(obj)]))
					myself.setObserver(obj,null,null,evt.subnodes);
			};
			this.setObserver = function(obj,movr,mout,subnodes,noDel) {
				var evt,name,nodes,obj = getDOMElement(obj);
				if (!obj) return false;
				var grName = getGrName(obj) || man.randName();
				(nodes = obj.descendants()).push(obj);
				(subnodes?nodes:[obj]).each(function(node) {
					if ((evt = sys.events[getGrName(node)]) && !noDel) {
						Event.stopObserving(node,'mouseover',evt.movr);
						Event.stopObserving(node,'mouseout',evt.mout);
					} node[sys.grName] = grName;
					if (!getGrName(node)) throw $break;
				});
				sys.events[name = getGrName(obj)] = null;
				if (name && movr && mout) {
					sys.events[name] = {movr:movr,mout:mout,subnodes:subnodes};
					Event.observe(obj,'mouseover',movr);
					Event.observe(obj,'mouseout',mout);
				} else return false;
				return true;
			};
			this.getKey = function(obj) {
				return getGrName(obj);
			};
			this.getTimerObj = function(obj) {
				return getTimer(obj);
			};
			this.performRawFade = function(obj,drc,subnodes,level,step) {
				myself.setLastPlay(obj,drc = man.isUndefined(drc)?true:drc);
				subnodes = man.isUndefined(subnodes)?true:subnodes;
				with (getTimer(obj = getDOMElement(obj))) {
					level = man.isUndefined(level)?phase.level:level;
					step = man.isUndefined(step)?phase.i:step;
				} fade(obj,drc,subnodes,false,{level:level,i:step});
			};
			this.setLastPlay = function(obj,drc) {
				if (man.toBoolean(obj = getGrName(obj)))
					sys.drc[obj] = man.isUndefined(drc)?undefined:man.toBoolean(drc);
				return myself;
			};
			this.getLastPlay = function(obj) {
				return sys.drc[getGrName(obj)];
			};
			this.zip = function(obj,target,str) {
				var check = $A(str).length > schema.lenght,index = myself.getIndex(str);
				if ((str = extractSchema(str).collect(function(slice) {
					return slice.clone(target);
				})).length) schema = (check?str:schema).each(function(slice,i) {
					slice.zip((check?schema:str)[i]);
				});
				if (!man.isUndefined(index) && (buf = myself.getIndex(obj)))
					sys.index[getGrName(obj)] = buf.zip(index).invoke('flatten');
				else myself.dropIndex(obj);
				return myself;
			};
			this.unzip = function(obj,target) {
				schema = schema.collect(function(slice) {
					return slice.unzip(target);
				});
				myself.dropIndex(obj);
				return myself;
			};
			var extractSchema = function(str) {
				var schema = $A([]),slice = {},sequence = [],callback;
				if (str instanceof Transform) schema = str.getSchema();
				if (str instanceof Fader || str instanceof Hover)
					ref = str.getTransform().getSchema();
				if (Object.isArray(str)) with (str) while (length) if (slice = shift())
					if (slice instanceof Slice) schema.push(slice);
				if (Object.isString(str)) str.split('=>').each(function(str) {
					var sym = new Slice([],[]);
					if (!str.strip().length) return undefined;
					else str.split(';').each(function(str) {
						if (str.match(man.getRegExp('^function /:/: (.*?) /{(.*?)/}$'))) {
							with (man.getRegVars()) if (callback = getCallback($1,$2))
								sym.grads.push(void(sym.styles.push({callback:callback})));
						} else if (str.match(man.getRegExp('^(.*?) /:/: ([a-z/-]*?) /((.*)/)$')))
							with (man.getRegVars()) if ((sequence = getSequence($3)).length) {
								sym.styles.push({property:$1.strip(),target:undefined});
								for (var i = 0; i < sequence.length; i++)
									sequence[i] = void((slice = {})[$2.camelize()] = sequence[i]) || slice;
								sym.grads.push(sequence);
							} else return undefined;
					});
					with (sym) for (i = 0; i < styles.length; grads[i] = $A(grads[i++]))
						for (var j = i+1; j < styles.length; j++) if (styles[i] == styles[j]) {
							for (var k = 0; k < man.max([grads[i],grads[j]]); k++)
								grads[i][k] = Object.extend(grads[i][k] || {},grads[j][k] || {});
							styles.splice(j,1);
							grads.splice(j--,1);
						};
					var test = sym.styles.length+sym.grads.length;
					if (test > 0 && !(test%2)) schema.push(sym);
				});
				return schema;
			};
			var getTargets = function() {
				return schema.collect(function(slice) {
					if (slice instanceof Slice)
						return slice.getTargets();
				}).flatten().compact().uniq();
			};
			var getGrName = function(obj) {
				return (obj = getDOMElement(obj))?obj[sys.grName]:undefined;
			};
			var setGrName = function(nodes,subnodes) {
				nodes = $A(man.isElement(nodes)?[nodes]:nodes);
				if (!nodes.length) return false;
				return man.toBoolean(getGrName(nodes.each(function(obj) {
					myself.setObserver(obj,null,null,subnodes,true);
				}).first()));
			};
			var getTimer = function(obj) {
				var tmr = sys.timers,name = getGrName(obj);
				return tmr[name] = tmr[name] || setTimer(obj);
			};
			var setTimer = function(obj,timer,phase,pause) {
				pause = man.toBoolean(pause,void(timer = timer || 0));
				return (sys.timers[getGrName(obj)] = {phase:phase || {level:0,i:0},timer:timer,pause:pause});
			};
			var getChildren = function(elements,style) {
				return Selector?Selector.matchElements(elements,style || '*'):[];
			};
			var setHardPause = function(obj,state,release) {
				var sleep = sys.sleep,name = getGrName(obj);
				if (sleep[name]) clearTimeout(sleep[name].timer || 0);
				state = state && man.toBoolean(release = Math.abs(parseInt(release)/1E3));
				sleep[name] = {timer:state?setHardPause.delay(release,obj,false):0,pause:state,obj:obj};
			};
			var getHardPause = function(obj) {
				return man.toBoolean((sys.sleep[getGrName(obj)] || {}).pause);
			};
			var setPause = function(obj,state) {
				getTimer(obj).pause = man.toBoolean(state);
			};
			var getPause = function(obj) {
				return man.toBoolean((getTimer(obj) || {}).pause);
			};
			var getCallback = function(scope,source) {
				return function($this,$iteration) {
					var $delay = setHardPause.wrap(function(proceed,release) {
						proceed($this,true,release);
					});
					Try.these(function() {
						eval(source);
					});
				};
			};
			var getSequence = function(str) {
				function getRGBSequence(c1,c2,max) {
					function standardize(color) {
						if (!color) return undefined;
						with (color) if (length == 6) return color;
						else return gsub(man.getRegExp('([a-f/d])'),'#{1}#{1}');
					} var a,b,c,re = man.getRegExp('^#([a-f/d]{3}|[a-f/d]{6})$');
					a = new String(c1).strip().match(re)?(c1 = standardize(RegExp.$1)):null;
					b = new String(c2).strip().match(re)?(c2 = standardize(RegExp.$1)):null;
					if (!a || !b || c1.length+c2.length != 12 || --max < 0) return [];
					for (var c = [],i = 0; i < 6; c.push(parseInt('0x'+(c1+c2).slice(i*2,i++*2+2),16)));
					for (var a = [],i = 0; i <= (parseInt(max) || 1); i++) {
						for (var b = [],j = 0; j < 3; b.push(Math.floor((c[j]*(max-i) + c[(j++)+3]*i)/max)));
						for (var color = '#',j = 0; j < b.length; j++) with ('0123456789abcdef')
							color += charAt(Math.floor(b[j]/16))+charAt(b[j]%16);
						a.push(color);
					} return a;
				};
				function getNumericSequence(n1,n2,max,fnc,m1,m2) {
					function extract(number) {
						new String(number).strip().match(man.getRegExp('^(.*?)(/-?/d+(?:/./d+)?)(.*?)$'));
						pre = pre || RegExp.$1;
						post = post || RegExp.$3;
						return parseFloat(RegExp.$2);
					} var pre,post;
					if (isNaN(n1 = extract(n1)) || isNaN(n2 = extract(n2))) return [];
					if (Object.isString(fnc)) fnc = window[fnc = fnc.strip()] || Math[fnc];
					if (!man.isFunction(fnc)) fnc = Prototype.K;
					m1 = parseFloat(m1) || 1;
					m2 = parseFloat(m2) || 2;
					function getShift(i) {
						return fnc(m1+(m2-m1)*i/(max-1)) || 0;
					} var c = (n2-n1)/(fnc(m2)-fnc(m1) || 1);
					for (var a = [],b,i = 0; i < max; i++) {
						b = Math.abs(getShift(i)-getShift(0));
						a.push(pre+(n1+b*c)+post);
					} return a;
				};
				var a = '(#(?:[a-f/d]{3}|[a-f/d]{6})|(?:.*?/-?/d+(?:/./d+)?.*?))',
					b = '/: (/d+) (?:trend /{ (/d+) > (/d+) /: ([/w/$]+) /})?',
					c = 'get /{ ([^/{/}]*?) /:/: ([a-z/-]*?) /}',
					nodes = rootNode.descendants();
				if (void(nodes.push(rootNode)) || str.gsub(man.getRegExp(c),function(match) {
					with (man.getRegVars()) {
						var obj = getChildren(nodes,$1).first() || documentRoot().select($1).first();
						return obj?obj.getStyle($2):undefined;
					} return undefined;
				}).gsub(man.getRegExp('eval /{ ([^/{/}]*?) /}'),function(match) {
					return Try.these(function() {
						var str = new String(man.getRegVars().$1).gsub(/[^\.\d\+\-\/\*\(\)]/,'');
						new String(eval(str)).match(/^(-?\d*(?:\.\d{1,3})?)\d*?$/);
						return RegExp.$1;
					}) || undefined;
				}).match(man.getRegExp('^',a,'>',a,b,'$'))) with (man.getRegVars()) {
					if ($A(a = getRGBSequence($1,$2,$3)).length) return a;
					else return $A(getNumericSequence($1,$2,$3,$6,$4,$5));
				} else return [];
			};
			this.setFunction = function(obj,drc,fnc) {
				if (obj = getGrName(obj)) (sys.fncs[obj] = sys.fncs[name] || {})[drc] = fnc;
			};
			this.getFunction = function(obj,drc) {
				return (obj = sys.fncs[getGrName(obj)])?obj[drc]:null;
			};
			var fade = function(obj,drc,subnodes,drop,phase) {
				if (man.isUndefined(obj = getDOMElement(obj))) return;
				if (!getHardPause(obj)) {
					var tmr,index = myself.buildIndex(obj,subnodes);
					clearTimeout((tmr = getTimer(obj)).timer);
					var phase = tmr.phase,level = phase.level;
					var drc = man.toBoolean(drc);
					if ((slice = schema[level]) && (index = index[level]))
						slice.styles.each(function(style,i,nodes) {
							if (man.isFunction(style.callback))
								man.execArray([style.callback,obj,phase.i]);
							else index[i].each(function(obj) {
								obj.setStyle(slice.grads[i][phase.i]);
							});
						});
					else return undefined;
					with (phase) if (drc?(++i >= man.max(slice.grads)):(--i < 0))
						if (drc?(++level >= schema.length):(--level < 0)){
							for (buf in phase) phase[buf] = drop?0:(phase[buf]+(drc?-1:+1));
							Object.extend(tmr,{timer:0,phase:phase});
							return void(man.execArray(myself.getFunction(obj,drc)));
						} else i = drc?0:(man.max(schema[level].grads)-1);
				} setTimer(obj,setTimeout(function() {
					fade(obj,drc,subnodes,drop,phase);
				},delay),phase,false);
			};
			var isFirstFrame = function(obj) {
				with (getTimer(obj).phase) return !man.toBoolean(level+i);
			};
			var play = function(obj,drc,subnodes,fnc) {
				if (getPause(obj = getDOMElement(obj))) return;
				if (!schema.length) man.execArray(fnc);
				myself.buildIndex(obj,subnodes);
				drc = man.toBoolean(drc);
				myself.setFunction(obj,drc,fnc);
				if (myself.getLastPlay(obj) === drc) return;
				else myself.setLastPlay(obj,drc);
				if (!drc && isFirstFrame(obj)) {
					var slice = schema[level = schema.length-1];
					i = slice?man.max(slice.grads)-1:0;
				} fade(obj,drc,subnodes,false);
			};
			var documentRoot = function() {
				return $$('html').reduce();
			};
			var setRootNode = function(obj) {
				rootNode = $((man.isElement(obj = $(obj))?obj:rootNode) || documentRoot());
			};
			setRootNode(rootNode);
			sys.grName = man.randName();
			myself.setDelay(delay);
			myself.setSchema(schema,rootNode);
		}
	});
	window.Transform = Class.create({
		initialize:function(str,delay,rootNode) {
			var ref,name,buf,myself = this;
			if (str instanceof Transform) {
				name = str.toString();
				if (man.isUndefined(delay)) delay = str.getDelay();
				ref = getReflection(str.setDelay(delay));
			} else if ((buf = reflections[new String(str)]) instanceof Reflection && (ref = buf))
				name = new String(str).valueOf();
			else ref = reflections[name = man.randName()] = new Reflection(str,delay,$(rootNode));
			this.toString = man.toFunction(name);
			return Object.extend(this,{
				getSchema:function() {
					return ref.getSchema();
				},
				setSchema:function(str,rootNode) {
					ref.setSchema(str,rootNode);
					return myself;
				},
				getDelay:function() {
					return ref.getDelay();
				},
				setDelay:function(msec) {
					ref.setDelay(msec);
					return myself;
				},
				getLastPlay:function(obj) {
					return ref.getLastPlay(obj || rootNode);
				},
				setLastPlay:function(obj,drc) {
					ref.setLastPlay(obj || rootNode,drc);
					return myself;
				},
				getKey:function(obj) {
					return ref.getKey(obj || rootNode);
				}
			});
		}
	});
	window.Fader = Class.create({
		initialize:function(obj,str,delay,subnodes,fncIn,fncOut) {
			var transform = new Transform(str,delay,obj);
			var myself = this,ref = getReflection(transform);
			subnodes = man.isUndefined(subnodes)?true:subnodes;
			ref.buildIndex(obj,subnodes);
			return Object.extend(this,{
				getElement:function() {
					return obj;
				},
				getTransform:function() {
					return transform;
				},
				playForward:function(fnc) {
					ref.playForward(obj,subnodes,fnc || fncIn);
					return myself;
				},
				playBackward:function(fnc) {
					ref.playBackward(obj,subnodes,fnc || fncOut);
					return myself;
				},
				rewind:function() {
					ref.rewind(obj);
					return myself;
				},
				pause:function() {
					ref.pause(obj);
					return myself;
				},
				unpause:function() {
					ref.unpause(obj);
					return myself;
				},
				immediateStop:function() {
					ref.immediateStop(obj);
					return myself;
				},
				zip:function(target,str) {
					ref.zip(obj,target,str);
					return myself;
				},
				unzip:function(target) {
					ref.unzip(obj,target);
					return myself;
				}
			});
		}
	});
	window.Hover = Class.create({
		initialize:function(obj,str,delay,subnodes,fncIn,fncOut) {
			var createHandler = function(ref,obj,drc,subnodes,fnc) {
				var getGrName = ref.getKey;
				ref.setFunction(obj,drc,fnc);
				return (obj = getDOMElement(obj))?function(evt) {
					if (man.isUndefined(evt = evt?evt:(window.event?event:null))) return false;
					var a,b,node = (a = evt.target)?a:((b = evt.srcElement)?b:null);
					b = (drc = man.toBoolean(drc))?evt.fromElement:evt.toElement;
					var from = (a = evt.relatedTarget)?a:(b?b:null);
					from = from?man.isElement(from)?from:from.parentNode:null;
					if (node && (from?(getGrName(from) != getGrName(node)):true))
						with (ref) if (!getTimerObj(obj).pause)
							performRawFade(obj,drc,subnodes);
				}:new Function;
			};
			var transform = new Transform(str,delay,obj);
			var myself = this,ref = getReflection(transform);
			subnodes = man.isUndefined(subnodes)?true:subnodes;
			ref.buildIndex(obj = getDOMElement(obj),subnodes);
			var movr = createHandler(ref,obj,true,subnodes,fncIn);
			var mout = createHandler(ref,obj,false,subnodes,fncOut);
			return Object.extend(this,{
				getElement:function() {
					return obj;
				},
				getTransform:function() {
					return transform;
				},
				playForward:function(fnc) {
					ref.playForward(obj,subnodes,fnc || fncIn);
					return myself;
				},
				playBackward:function(fnc) {
        			ref.playBackward(obj,subnodes,fnc || fncOut);
					return myself;
				},
				enableObserve:function() {
					ref.setObserver(obj,movr,mout,subnodes);
					return myself;
				},
				disableObserve:function() {
					ref.removeObserver(obj);
					return myself;
				},
				immediateStop:function() {
					ref.immediateStop(obj);
					return myself;
				},
				zip:function(target,str) {
					ref.zip(obj,target,str);
					return myself;
				},
				unzip:function(target) {
					ref.unzip(obj,target);
					return myself;
				}
			});
		}
	});
	Element.addMethods({
		attachFader:function(obj,str,delay,subnodes,fncIn,fncOut) {
			return new Fader(obj,str,delay,subnodes,fncIn,fncOut);
		},
		attachHover:function(obj,str,delay,subnodes,fncIn,fncOut) {
			return new Hover(obj,str,delay,subnodes,fncIn,fncOut);
		}
	});
})();