<!--
function MM_swapImgRestore() { //v3.0
  var i,x,a=document.MM_sr; for(i=0;a&&i<a.length&&(x=a[i])&&x.oSrc;i++) x.src=x.oSrc;
}
function MM_preloadImages() { //v3.0
  var d=document; if(d.images){ if(!d.MM_p) d.MM_p=new Array();
    var i,j=d.MM_p.length,a=MM_preloadImages.arguments; for(i=0; i<a.length; i++)
    if (a[i].indexOf("#")!=0){ d.MM_p[j]=new Image; d.MM_p[j++].src=a[i];}}
}

function MM_findObj(n, d) { //v4.01
  var p,i,x;  if(!d) d=document; if((p=n.indexOf("?"))>0&&parent.frames.length) {
    d=parent.frames[n.substring(p+1)].document; n=n.substring(0,p);}
  if(!(x=d[n])&&d.all) x=d.all[n]; for (i=0;!x&&i<d.forms.length;i++) x=d.forms[i][n];
  for(i=0;!x&&d.layers&&i<d.layers.length;i++) x=MM_findObj(n,d.layers[i].document);
  if(!x && d.getElementById) x=d.getElementById(n); return x;
}

function MM_swapImage() { //v3.0
  var i,j=0,x,a=MM_swapImage.arguments; document.MM_sr=new Array; for(i=0;i<(a.length-2);i+=3)
   if ((x=MM_findObj(a[i]))!=null){document.MM_sr[j++]=x; if(!x.oSrc) x.oSrc=x.src; x.src=a[i+2];}
}
//-->
/*	sIFR 2.0.2
	Copyright 2004 - 2006 Mike Davidson, Shaun Inman, Tomas Jogin and Mark Wubben

	This software is licensed under the CC-GNU LGPL <http://creativecommons.org/licenses/LGPL/2.1/>
*/

var hasFlash=function()
{
	var a=6;	
	
	
	if(navigator.appVersion.indexOf("MSIE")!=-1&&navigator.appVersion.indexOf("Windows")>-1)
	{
		document.write('<script language="VBScript"\> \non error resume next \nhasFlash = (IsObject(CreateObject("ShockwaveFlash.ShockwaveFlash." & '+a+'))) \n</script\> \n');
		
		if(window.hasFlash!=null)return window.hasFlash
	}
	
	if(navigator.mimeTypes&&navigator.mimeTypes["application/x-shockwave-flash"]&&navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin)
	{		
		var b=(navigator.plugins["Shockwave Flash 2.0"]||navigator.plugins["Shockwave Flash"]).description;
		return parseInt(b.charAt(b.indexOf(".")-1))>=a
	}return false}();
	
	String.prototype.normalize=function()
	{
		return this.replace(/\s+/g," ")
	};
	
	if(Array.prototype.push==null)
	{
		
		Array.prototype.push=function()
		{
			var i=0,a=this.length,b=arguments.length;
			while(i<b){this[a++]=arguments[i++]}return this.length
		}
	}
	
	if(!Function.prototype.apply)
	{
		Function.prototype.apply=function(a,b)
		{
			var c=[];var d,e;if(!a)a=window;if(!b)b=[];
			
			for(var i=0;i<b.length;i++)
			{
				c[i]="b["+i+"]"
			}e="a.__applyTemp__("+c.join(",")+");";
			
			a.__applyTemp__=this;			
			d=eval(e);a.__applyTemp__=null;return d
		}
	}
	
	function named(a)
	{		
		return new named.Arguments(a)
	}
	
	named.Arguments=function(a)
	{
		
		this.oArgs=a
	};
	
	
	
	named.Arguments.prototype.constructor=named.Arguments;named.extract=function(a,b){var c,d;var i=a.length;while(i--){d=a[i];if(d!=null&&d.constructor!=null&&d.constructor==named.Arguments){c=a[i].oArgs;break}}if(c==null)return;for(e in c)if(b[e]!=null)b[e](c[e]);return};var parseSelector=function(){var a=/^([^#.>`]*)(#|\.|\>|\`)(.+)$/;function r(s,t){var u=s.split(/\s*\,\s*/);var v=[];for(var i=0;i<u.length;i++)v=v.concat(b(u[i],t));return v}function b(c,d,e){c=c.normalize().replace(" ","`");var f=c.match(a);var g,h,i,j,k,n;var l=[];if(f==null)f=[c,c];if(f[1]=="")f[1]="*";if(e==null)e="`";if(d==null)d=document;switch(f[2]){case "#":k=f[3].match(a);if(k==null)k=[null,f[3]];g=document.getElementById(k[1]);if(g==null||(f[1]!="*"&&!o(g,f[1])))return l;if(k.length==2){l.push(g);return l}return b(k[3],g,k[2]);case ".":if(e!=">")h=m(d,f[1]);else h=d.childNodes;for(i=0,n=h.length;i<n;i++){g=h[i];if(g.nodeType!=1)continue;k=f[3].match(a);if(k!=null){if(g.className==null||g.className.match("(\\s|^)"+k[1]+"(\\s|$)")==null)continue;j=b(k[3],g,k[2]);l=l.concat(j)}else if(g.className!=null&&g.className.match("(\\s|^)"+f[3]+"(\\s|$)")!=null)l.push(g)}return l;case ">":if(e!=">")h=m(d,f[1]);else h=d.childNodes;for(i=0,n=h.length;i<n;i++){g=h[i];if(g.nodeType!=1)continue;if(!o(g,f[1]))continue;j=b(f[3],g,">");l=l.concat(j)}return l;case "`":h=m(d,f[1]);for(i=0,n=h.length;i<n;i++){g=h[i];j=b(f[3],g,"`");l=l.concat(j)}return l;default:if(e!=">")h=m(d,f[1]);else h=d.childNodes;for(i=0,n=h.length;i<n;i++){g=h[i];if(g.nodeType!=1)continue;if(!o(g,f[1]))continue;l.push(g)}return l}}function m(d,o){if(o=="*"&&d.all!=null)return d.all;return d.getElementsByTagName(o)}function o(p,q){return q=="*"?true:p.nodeName.toLowerCase().replace("html:", "")==q.toLowerCase()}return r}();var sIFR=function(){var a="http://www.w3.org/1999/xhtml";var b=false;var c=false;var d;var ah=[];var al=document;var ak=al.documentElement;var am=window;var au=al.addEventListener;var av=am.addEventListener;var f=function(){var g=navigator.userAgent.toLowerCase();var f={a:g.indexOf("applewebkit")>-1,b:g.indexOf("safari")>-1,c:navigator.product!=null&&navigator.product.toLowerCase().indexOf("konqueror")>-1,d:g.indexOf("opera")>-1,e:al.contentType!=null&&al.contentType.indexOf("xml")>-1,f:true,g:true,h:null,i:null,j:null,k:null}; f.l=f.a||f.c;f.m=!f.a&&navigator.product!=null&&navigator.product.toLowerCase()=="gecko";if(f.m&&g.match(/.*gecko\/(\d{8}).*/))f.j=new Number(g.match(/.*gecko\/(\d{8}).*/)[1]);f.n=g.indexOf("msie")>-1&&!f.d&&!f.l&&!f.m;f.o=f.n&&g.match(/.*mac.*/)!=null;if(f.d&&g.match(/.*opera(\s|\/)(\d+\.\d+)/))f.i=new Number(g.match(/.*opera(\s|\/)(\d+\.\d+)/)[2]);if(f.n||(f.d&&f.i<7.6))f.g=false;if(f.a&&g.match(/.*applewebkit\/(\d+).*/))f.k=new Number(g.match(/.*applewebkit\/(\d+).*/)[1]);if(am.hasFlash&&(!f.n||f.o)){var aj=(navigator.plugins["Shockwave Flash 2.0"]||navigator.plugins["Shockwave Flash"]).description;f.h=parseInt(aj.charAt(aj.indexOf(".")-1))}if(g.match(/.*(windows|mac).*/)==null||f.o||f.c||(f.d&&(g.match(/.*mac.*/)!=null||f.i<7.6))||(f.b&&f.h<7)||(!f.b&&f.a&&f.k<312)||(f.m&&f.j<20020523))f.f=false;if(!f.o&&!f.m&&al.createElementNS)try{al.createElementNS(a,"i").innerHTML=""}catch(e){f.e=true}f.p=f.c||(f.a&&f.k<312);return f}();function at(){return{bIsWebKit:f.a,bIsSafari:f.b,bIsKonq:f.c,bIsOpera:f.d,bIsXML:f.e,bHasTransparencySupport:f.f,bUseDOM:f.g,nFlashVersion:f.h,nOperaVersion:f.i,nGeckoBuildDate:f.j,nWebKitVersion:f.k,bIsKHTML:f.l,bIsGecko:f.m,bIsIE:f.n,bIsIEMac:f.o,bUseInnerHTMLHack:f.p}}if(am.hasFlash==false||!al.getElementsByTagName||!al.getElementById||(f.e&&(f.p||f.n)))return{UA:at()};function af(e){if((!k.bAutoInit&&(am.event||e)!=null)||!l(e))return;b=true;for(var i=0,h=ah.length;i<h;i++)j.apply(null,ah[i]);ah=[]}var k=af;function l(e){if(c==false||k.bIsDisabled==true||((f.e&&f.m||f.l)&&e==null&&b==false)||(al.body==null||al.getElementsByTagName("body").length==0))return false;return true}function m(n){if(f.n)return n.replace(new RegExp("%\d{0}","g"),"%25");return n.replace(new RegExp("%(?!\d)","g"),"%25")}function as(p,q){return q=="*"?true:p.nodeName.toLowerCase().replace("html:", "")==q.toLowerCase()}function o(p,q,r,s,t){var u="";var v=p.firstChild;var w,x,y,z;if(s==null)s=0;if(t==null)t="";while(v){if(v.nodeType==3){z=v.nodeValue.replace("<","&lt;");switch(r){case "lower":u+=z.toLowerCase();break;case "upper":u+=z.toUpperCase();break;default:u+=z}}else if(v.nodeType==1){if(as(v,"a")&&!v.getAttribute("href")==false){if(v.getAttribute("target"))t+="&sifr_url_"+s+"_target="+v.getAttribute("target");t+="&sifr_url_"+s+"="+m(v.getAttribute("href")).replace(/&/g,"%26");u+='<a href="asfunction:_root.launchURL,'+s+'">';s++}else if(as(v,"br"))u+="<br/>";if(v.hasChildNodes()){y=o(v,null,r,s,t);u+=y.u;s=y.s;t=y.t}if(as(v,"a"))u+="</a>"}w=v;v=v.nextSibling;if(q!=null){x=w.parentNode.removeChild(w);q.appendChild(x)}}return{"u":u,"s":s,"t":t}}function A(B){if(al.createElementNS&&f.g)return al.createElementNS(a,B);return al.createElement(B)}function C(D,E,z){var p=A("param");p.setAttribute("name",E);p.setAttribute("value",z);D.appendChild(p)}function F(p,G){var H=p.className;if(H==null)H=G;else H=H.normalize()+(H==""?"":" ")+G;p.className=H}function aq(ar){var a=ak;if(k.bHideBrowserText==false)a=al.getElementsByTagName("body")[0];if((k.bHideBrowserText==false||ar)&&a)if(a.className==null||a.className.match(/\bsIFR\-hasFlash\b/)==null)F(a, "sIFR-hasFlash")}function j(I,J,K,L,M,N,O,P,Q,R,S,r,T){if(!l())return ah.push(arguments);aq();named.extract(arguments,{sSelector:function(ap){I=ap},sFlashSrc:function(ap){J=ap},sColor:function(ap){K=ap},sLinkColor:function(ap){L=ap},sHoverColor:function(ap){M=ap},sBgColor:function(ap){N=ap},nPaddingTop:function(ap){O=ap},nPaddingRight:function(ap){P=ap},nPaddingBottom:function(ap){Q=ap},nPaddingLeft:function(ap){R=ap},sFlashVars:function(ap){S=ap},sCase:function(ap){r=ap},sWmode:function(ap){T=ap}});var U=parseSelector(I);if(U.length==0)return false;if(S!=null)S="&"+S.normalize();else S="";if(K!=null)S+="&textcolor="+K;if(M!=null)S+="&hovercolor="+M;if(M!=null||L!=null)S+="&linkcolor="+(L||K);if(O==null)O=0;if(P==null)P=0;if(Q==null)Q=0;if(R==null)R=0;if(N==null){N="#FFFFFF";}if(T=="transparent")if(!f.f)T="opaque";else N="transparent";if(T==null)T="transparent";var p,V,W,X,Y,Z,aa,ab,ac;var ad=null;for(var i=0,h=U.length;i<h;i++){p=U[i];if(p.className!=null&&p.className.match(/\bsIFR\-replaced\b/)!=null)continue;V=p.offsetWidth-R-P;W=p.offsetHeight-O-Q;aa=A("span");aa.className="sIFR-alternate";ac=o(p,aa,r);Z="txt="+m(ac.u).replace(/\+/g,"%2B").replace(/&/g,"%26").replace(/\"/g, "%22").normalize() + S + "&w=" + V + "&h=" + W + ac.t;F(p,"sIFR-replaced");if(ad==null||!f.g){if(!f.g){if(!f.n)p.innerHTML=['<embed class="sIFR-flash" type="application/x-shockwave-flash" src="',J,'" quality="best" wmode="',T,'" bgcolor="',N,'" flashvars="',Z,'" width="',V,'" height="',W,'" wmode="transparent" sifr="true"></embed>'].join("");else p.innerHTML=['<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" sifr="true" width="',V,'" height="',W,'" class="sIFR-flash"><param name="movie" value="',J,"?",Z,'"></param><param name="quality" value="best"></param><param name="wmode" value="transparent"></param><param name="bgcolor" value="',N,'"></param> </object>'].join('')}else{if(f.d){ab=A("object");ab.setAttribute("data",J);C(ab,"quality","best");C(ab,"wmode",T);C(ab,"bgcolor",N)}else{ab=A("embed");ab.setAttribute("src",J);ab.setAttribute("quality","best");ab.setAttribute("flashvars",Z);ab.setAttribute("wmode",T);ab.setAttribute("bgcolor",N)}ab.setAttribute("sifr","true");ab.setAttribute("type","application/x-shockwave-flash");ab.className="sIFR-flash";if(!f.l||!f.e)ad=ab.cloneNode(true)}}else ab=ad.cloneNode(true);if(f.g){if(f.d)C(ab,"flashvars",Z);else ab.setAttribute("flashvars",Z);ab.setAttribute("width",V);ab.setAttribute("height",W);ab.style.width=V+"px";ab.style.height=W+"px";p.appendChild(ab)}p.appendChild(aa);if(f.p)p.innerHTML+=""}if(f.n&&k.bFixFragIdBug)setTimeout(function(){al.title=d},0)}function ai(){d=al.title}function ae(){if(k.bIsDisabled==true)return;c=true;if(k.bHideBrowserText)aq(true);if(am.attachEvent)am.attachEvent("onload",af);else if(!f.c&&(al.addEventListener||am.addEventListener)){if(f.a&&f.k>=132&&am.addEventListener)am.addEventListener("load",function(){setTimeout("sIFR({})",1)},false);else{if(al.addEventListener)al.addEventListener("load",af,false);if(am.addEventListener)am.addEventListener("load",af,false)}}else if(typeof am.onload=="function"){var ag=am.onload;am.onload=function(){ag();af()}}else am.onload=af;if(!f.n||am.location.hash=="")k.bFixFragIdBug=false;else ai()}k.UA=at();k.bAutoInit=true;k.bFixFragIdBug=true;k.replaceElement=j;k.updateDocumentTitle=ai;k.appendToClassName=F;k.setup=ae;k.debug=function(){aq(true)};k.debug.replaceNow=function(){ae();k()};k.bIsDisabled=false;k.bHideBrowserText=true;return k}();

	
if(typeof sIFR == "function" && !sIFR.UA.bIsIEMac){
sIFR.setup();
};
/*	sIFR 2.0.1 Official Add-ons 1.2
	Copyright 2005 Mark Wubben

	This software is licensed under the CC-GNU LGPL <http://creativecommons.org/licenses/LGPL/2.1/>
*/

if(typeof sIFR=="function")(function(){var j=document;var h=j.documentElement;sIFR.removeDecoyClasses=function(){function a(b){if(b&&b.className!=null)b.className=b.className.replace(/\bsIFR-hasFlash\b/,"")}return function(){a(h);a(j.getElementsByTagName("body")[0])}}();sIFR.preferenceManager={storage:{sCookieId:"sifr",set:function(a){var b=new Date();b.setFullYear(b.getFullYear()+3);j.cookie=[this.sCookieId,"=",a,";expires=",b.toGMTString(),";path=/"].join("")},get:function(){var a=j.cookie.match(new RegExp(";?"+this.sCookieId+"=([^;]+);?"));if(a!=null&&a[1]=="false")return false;else return true},reset:function(){var a=new Date();a.setFullYear(a.getFullYear()-1);j.cookie=[this.sCookieId,"=true;expires=",a.toGMTString(),";path=/"].join("")}},disable:function(){this.storage.set(false)},enable:function(){this.storage.set(true)},test:function(){return this.storage.get()}};if(sIFR.preferenceManager.test()==false){sIFR.bIsDisabled=true;sIFR.removeDecoyClasses()}sIFR.rollback=function(){function a(b){var c,d,e,f,g,h;var l=parseSelector(b);var i=l.length-1;var m=false;while(i>=0){c=l[i];l.length--;d=c.parentNode;if(c.getAttribute("sifr")=="true"){h=0;while(h<d.childNodes.length){c=d.childNodes[h];if(c.className=="sIFR-alternate"){e=c;h++;continue}d.removeChild(c)}if(e!=null){f=e.firstChild;while(f!=null){g=f.nextSibling;d.appendChild(e.removeChild(f));f=g}d.removeChild(e)}if(!sIFR.UA.bIsXML&&sIFR.UA.bUseInnerHTMLHack)d.innerHTML+="";d.className=d.className.replace(/\bsIFR\-replaced\b/,"")};m=true;i--}return m}return function(k){named.extract(arguments,{sSelector:function(a){k=a}});if(k==null)k="";else k+=">";sIFR.removeDecoyClasses();sIFR.bHideBrowserText=false;if(a(k+"embed")==false)a(k+"object")}}()})()
sfHover = function() {
	var sfEls = document.getElementById("nav");
	if (sfEls != null)
	{
	    sfEls = sfEls.getElementsByTagName("LI");
	    for (var i=0; i<sfEls.length; i++) {
		    sfEls[i].onmouseover=function() {
			    this.className+=" over";
		    }
		    sfEls[i].onmouseout=function() {
			    this.className=this.className.replace(new RegExp(" over\\b"), "");
		    }
	    }
	}
}
if (window.attachEvent) window.attachEvent("onload", sfHover);
//MooTools, <http://mootools.net>, My Object Oriented (JavaScript) Tools. Copyright (c) 2006-2009 Valerio Proietti, <http://mad4milk.net>, MIT Style License.
//MooTools More, <http://mootools.net/more>. Copyright (c) 2006-2009 Aaron Newton <http://clientcide.com/>, Valerio Proietti <http://mad4milk.net> & the MooTools team <http://mootools.net/developers>, MIT Style License.

//Contents: Core, Browser, Array, Function, Number, String, Hash, Event, Class, Class.Extras, Element, Element.Event, Element.Style, Element.Dimensions, Selectors, DomReady, JSON, Cookie, Swiff, Fx, Fx.CSS, Fx.Tween, Fx.Morph, Fx.Transitions, Request, Request.HTML, Request.JSON, More, Fx.Elements, Fx.Accordion, Fx.Scroll, Fx.Slide, Fx.SmoothScroll, Drag, Drag.Move, Class.Binds, Element.Measure, Slider, Sortables, Color, Group, Hash.Cookie, Scroller, Tips

/*
---

script: Core.js

description: The core of MooTools, contains all the base functions and the Native and Hash implementations. Required by all the other scripts.

license: MIT-style license.

copyright: Copyright (c) 2006-2008 [Valerio Proietti](http://mad4milk.net/).

authors: The MooTools production team (http://mootools.net/developers/)

inspiration:
- Class implementation inspired by [Base.js](http://dean.edwards.name/weblog/2006/03/base/) Copyright (c) 2006 Dean Edwards, [GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)
- Some functionality inspired by [Prototype.js](http://prototypejs.org) Copyright (c) 2005-2007 Sam Stephenson, [MIT License](http://opensource.org/licenses/mit-license.php)

provides: [Mootools, Native, Hash.base, Array.each, $util]

...
*/

var MooTools = {
    'version': '1.2.4',
    'build': '0d9113241a90b9cd5643b926795852a2026710d4'
};

var Native = function(options) {
    options = options || {};
    var name = options.name;
    var legacy = options.legacy;
    var protect = options.protect;
    var methods = options.implement;
    var generics = options.generics;
    var initialize = options.initialize;
    var afterImplement = options.afterImplement || function() { };
    var object = initialize || legacy;
    generics = generics !== false;

    object.constructor = Native;
    object.$family = { name: 'native' };
    if (legacy && initialize) object.prototype = legacy.prototype;
    object.prototype.constructor = object;

    if (name) {
        var family = name.toLowerCase();
        object.prototype.$family = { name: family };
        Native.typize(object, family);
    }

    var add = function(obj, name, method, force) {
        if (!protect || force || !obj.prototype[name]) obj.prototype[name] = method;
        if (generics) Native.genericize(obj, name, protect);
        afterImplement.call(obj, name, method);
        return obj;
    };

    object.alias = function(a1, a2, a3) {
        if (typeof a1 == 'string') {
            var pa1 = this.prototype[a1];
            if ((a1 = pa1)) return add(this, a2, a1, a3);
        }
        for (var a in a1) this.alias(a, a1[a], a2);
        return this;
    };

    object.implement = function(a1, a2, a3) {
        if (typeof a1 == 'string') return add(this, a1, a2, a3);
        for (var p in a1) add(this, p, a1[p], a2);
        return this;
    };

    if (methods) object.implement(methods);

    return object;
};

Native.genericize = function(object, property, check) {
    if ((!check || !object[property]) && typeof object.prototype[property] == 'function') object[property] = function() {
        var args = Array.prototype.slice.call(arguments);
        return object.prototype[property].apply(args.shift(), args);
    };
};

Native.implement = function(objects, properties) {
    for (var i = 0, l = objects.length; i < l; i++) objects[i].implement(properties);
};

Native.typize = function(object, family) {
    if (!object.type) object.type = function(item) {
        return ($type(item) === family);
    };
};

(function() {
    var natives = { 'Array': Array, 'Date': Date, 'Function': Function, 'Number': Number, 'RegExp': RegExp, 'String': String };
    for (var n in natives) new Native({ name: n, initialize: natives[n], protect: true });

    var types = { 'boolean': Boolean, 'native': Native, 'object': Object };
    for (var t in types) Native.typize(types[t], t);

    var generics = {
        'Array': ["concat", "indexOf", "join", "lastIndexOf", "pop", "push", "reverse", "shift", "slice", "sort", "splice", "toString", "unshift", "valueOf"],
        'String': ["charAt", "charCodeAt", "concat", "indexOf", "lastIndexOf", "match", "replace", "search", "slice", "split", "substr", "substring", "toLowerCase", "toUpperCase", "valueOf"]
    };
    for (var g in generics) {
        for (var i = generics[g].length; i--; ) Native.genericize(natives[g], generics[g][i], true);
    }
})();

var Hash = new Native({

    name: 'Hash',

    initialize: function(object) {
        if ($type(object) == 'hash') object = $unlink(object.getClean());
        for (var key in object) this[key] = object[key];
        return this;
    }

});

Hash.implement({

    forEach: function(fn, bind) {
        for (var key in this) {
            if (this.hasOwnProperty(key)) fn.call(bind, this[key], key, this);
        }
    },

    getClean: function() {
        var clean = {};
        for (var key in this) {
            if (this.hasOwnProperty(key)) clean[key] = this[key];
        }
        return clean;
    },

    getLength: function() {
        var length = 0;
        for (var key in this) {
            if (this.hasOwnProperty(key)) length++;
        }
        return length;
    }

});

Hash.alias('forEach', 'each');

Array.implement({

    forEach: function(fn, bind) {
        for (var i = 0, l = this.length; i < l; i++) fn.call(bind, this[i], i, this);
    }

});

Array.alias('forEach', 'each');

function $A(iterable) {
    if (iterable.item) {
        var l = iterable.length, array = new Array(l);
        while (l--) array[l] = iterable[l];
        return array;
    }
    return Array.prototype.slice.call(iterable);
};

function $arguments(i) {
    return function() {
        return arguments[i];
    };
};

function $chk(obj) {
    return !!(obj || obj === 0);
};

function $clear(timer) {
    clearTimeout(timer);
    clearInterval(timer);
    return null;
};

function $defined(obj) {
    return (obj != undefined);
};

function $each(iterable, fn, bind) {
    var type = $type(iterable);
    ((type == 'arguments' || type == 'collection' || type == 'array') ? Array : Hash).each(iterable, fn, bind);
};

function $empty() { };

function $extend(original, extended) {
    for (var key in (extended || {})) original[key] = extended[key];
    return original;
};

function $H(object) {
    return new Hash(object);
};

function $lambda(value) {
    return ($type(value) == 'function') ? value : function() {
        return value;
    };
};

function $merge() {
    var args = Array.slice(arguments);
    args.unshift({});
    return $mixin.apply(null, args);
};

function $mixin(mix) {
    for (var i = 1, l = arguments.length; i < l; i++) {
        var object = arguments[i];
        if ($type(object) != 'object') continue;
        for (var key in object) {
            var op = object[key], mp = mix[key];
            mix[key] = (mp && $type(op) == 'object' && $type(mp) == 'object') ? $mixin(mp, op) : $unlink(op);
        }
    }
    return mix;
};

function $pick() {
    for (var i = 0, l = arguments.length; i < l; i++) {
        if (arguments[i] != undefined) return arguments[i];
    }
    return null;
};

function $random(min, max) {
    return Math.floor(Math.random() * (max - min + 1) + min);
};

function $splat(obj) {
    var type = $type(obj);
    return (type) ? ((type != 'array' && type != 'arguments') ? [obj] : obj) : [];
};

var $time = Date.now || function() {
    return +new Date;
};

function $try() {
    for (var i = 0, l = arguments.length; i < l; i++) {
        try {
            return arguments[i]();
        } catch (e) { }
    }
    return null;
};

function $type(obj) {
    if (obj == undefined) return false;
    if (obj.$family) return (obj.$family.name == 'number' && !isFinite(obj)) ? false : obj.$family.name;
    if (obj.nodeName) {
        switch (obj.nodeType) {
            case 1: return 'element';
            case 3: return (/\S/).test(obj.nodeValue) ? 'textnode' : 'whitespace';
        }
    } else if (typeof obj.length == 'number') {
        if (obj.callee) return 'arguments';
        else if (obj.item) return 'collection';
    }
    return typeof obj;
};

function $unlink(object) {
    var unlinked;
    switch ($type(object)) {
        case 'object':
            unlinked = {};
            for (var p in object) unlinked[p] = $unlink(object[p]);
            break;
        case 'hash':
            unlinked = new Hash(object);
            break;
        case 'array':
            unlinked = [];
            for (var i = 0, l = object.length; i < l; i++) unlinked[i] = $unlink(object[i]);
            break;
        default: return object;
    }
    return unlinked;
};
/*
---

script: Browser.js

description: The Browser Core. Contains Browser initialization, Window and Document, and the Browser Hash.

license: MIT-style license.

requires: 
- /Native
- /$util

provides: [Browser, Window, Document, $exec]

...
*/

var Browser = $merge({

    Engine: { name: 'unknown', version: 0 },

    Platform: { name: (window.orientation != undefined) ? 'ipod' : (navigator.platform.match(/mac|win|linux/i) || ['other'])[0].toLowerCase() },

    Features: { xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector) },

    Plugins: {},

    Engines: {

        presto: function() {
            return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925));
        },

        trident: function() {
            return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? ((document.querySelectorAll) ? 6 : 5) : 4);
        },

        webkit: function() {
            return (navigator.taintEnabled) ? false : ((Browser.Features.xpath) ? ((Browser.Features.query) ? 525 : 420) : 419);
        },

        gecko: function() {
            return (!document.getBoxObjectFor && window.mozInnerScreenX == null) ? false : ((document.getElementsByClassName) ? 19 : 18);
        }

    }

}, Browser || {});

Browser.Platform[Browser.Platform.name] = true;

Browser.detect = function() {

    for (var engine in this.Engines) {
        var version = this.Engines[engine]();
        if (version) {
            this.Engine = { name: engine, version: version };
            this.Engine[engine] = this.Engine[engine + version] = true;
            break;
        }
    }

    return { name: engine, version: version };

};

Browser.detect();

Browser.Request = function() {
    return $try(function() {
        return new XMLHttpRequest();
    }, function() {
        return new ActiveXObject('MSXML2.XMLHTTP');
    }, function() {
        return new ActiveXObject('Microsoft.XMLHTTP');
    });
};

Browser.Features.xhr = !!(Browser.Request());

Browser.Plugins.Flash = (function() {
    var version = ($try(function() {
        return navigator.plugins['Shockwave Flash'].description;
    }, function() {
        return new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
    }) || '0 r0').match(/\d+/g);
    return { version: parseInt(version[0] || 0 + '.' + version[1], 10) || 0, build: parseInt(version[2], 10) || 0 };
})();

function $exec(text) {
    if (!text) return text;
    if (window.execScript) {
        window.execScript(text);
    } else {
        var script = document.createElement('script');
        script.setAttribute('type', 'text/javascript');
        script[(Browser.Engine.webkit && Browser.Engine.version < 420) ? 'innerText' : 'text'] = text;
        document.head.appendChild(script);
        document.head.removeChild(script);
    }
    return text;
};

Native.UID = 1;

var $uid = (Browser.Engine.trident) ? function(item) {
    return (item.uid || (item.uid = [Native.UID++]))[0];
} : function(item) {
    return item.uid || (item.uid = Native.UID++);
};

var Window = new Native({

    name: 'Window',

    legacy: (Browser.Engine.trident) ? null : window.Window,

    initialize: function(win) {
        $uid(win);
        if (!win.Element) {
            win.Element = $empty;
            if (Browser.Engine.webkit) win.document.createElement("iframe"); //fixes safari 2
            win.Element.prototype = (Browser.Engine.webkit) ? window["[[DOMElement.prototype]]"] : {};
        }
        win.document.window = win;
        return $extend(win, Window.Prototype);
    },

    afterImplement: function(property, value) {
        window[property] = Window.Prototype[property] = value;
    }

});

Window.Prototype = { $family: { name: 'window'} };

new Window(window);

var Document = new Native({

    name: 'Document',

    legacy: (Browser.Engine.trident) ? null : window.Document,

    initialize: function(doc) {
        $uid(doc);
        doc.head = doc.getElementsByTagName('head')[0];
        doc.html = doc.getElementsByTagName('html')[0];
        if (Browser.Engine.trident && Browser.Engine.version <= 4) $try(function() {
            doc.execCommand("BackgroundImageCache", false, true);
        });
        if (Browser.Engine.trident) doc.window.attachEvent('onunload', function() {
            doc.window.detachEvent('onunload', arguments.callee);
            doc.head = doc.html = doc.window = null;
        });
        return $extend(doc, Document.Prototype);
    },

    afterImplement: function(property, value) {
        document[property] = Document.Prototype[property] = value;
    }

});

Document.Prototype = { $family: { name: 'document'} };

new Document(document);
/*
---

script: Array.js

description: Contains Array Prototypes like each, contains, and erase.

license: MIT-style license.

requires:
- /$util
- /Array.each

provides: [Array]

...
*/

Array.implement({

    every: function(fn, bind) {
        for (var i = 0, l = this.length; i < l; i++) {
            if (!fn.call(bind, this[i], i, this)) return false;
        }
        return true;
    },

    filter: function(fn, bind) {
        var results = [];
        for (var i = 0, l = this.length; i < l; i++) {
            if (fn.call(bind, this[i], i, this)) results.push(this[i]);
        }
        return results;
    },

    clean: function() {
        return this.filter($defined);
    },

    indexOf: function(item, from) {
        var len = this.length;
        for (var i = (from < 0) ? Math.max(0, len + from) : from || 0; i < len; i++) {
            if (this[i] === item) return i;
        }
        return -1;
    },

    map: function(fn, bind) {
        var results = [];
        for (var i = 0, l = this.length; i < l; i++) results[i] = fn.call(bind, this[i], i, this);
        return results;
    },

    some: function(fn, bind) {
        for (var i = 0, l = this.length; i < l; i++) {
            if (fn.call(bind, this[i], i, this)) return true;
        }
        return false;
    },

    associate: function(keys) {
        var obj = {}, length = Math.min(this.length, keys.length);
        for (var i = 0; i < length; i++) obj[keys[i]] = this[i];
        return obj;
    },

    link: function(object) {
        var result = {};
        for (var i = 0, l = this.length; i < l; i++) {
            for (var key in object) {
                if (object[key](this[i])) {
                    result[key] = this[i];
                    delete object[key];
                    break;
                }
            }
        }
        return result;
    },

    contains: function(item, from) {
        return this.indexOf(item, from) != -1;
    },

    extend: function(array) {
        for (var i = 0, j = array.length; i < j; i++) this.push(array[i]);
        return this;
    },

    getLast: function() {
        return (this.length) ? this[this.length - 1] : null;
    },

    getRandom: function() {
        return (this.length) ? this[$random(0, this.length - 1)] : null;
    },

    include: function(item) {
        if (!this.contains(item)) this.push(item);
        return this;
    },

    combine: function(array) {
        for (var i = 0, l = array.length; i < l; i++) this.include(array[i]);
        return this;
    },

    erase: function(item) {
        for (var i = this.length; i--; i) {
            if (this[i] === item) this.splice(i, 1);
        }
        return this;
    },

    empty: function() {
        this.length = 0;
        return this;
    },

    flatten: function() {
        var array = [];
        for (var i = 0, l = this.length; i < l; i++) {
            var type = $type(this[i]);
            if (!type) continue;
            array = array.concat((type == 'array' || type == 'collection' || type == 'arguments') ? Array.flatten(this[i]) : this[i]);
        }
        return array;
    },

    hexToRgb: function(array) {
        if (this.length != 3) return null;
        var rgb = this.map(function(value) {
            if (value.length == 1) value += value;
            return value.toInt(16);
        });
        return (array) ? rgb : 'rgb(' + rgb + ')';
    },

    rgbToHex: function(array) {
        if (this.length < 3) return null;
        if (this.length == 4 && this[3] == 0 && !array) return 'transparent';
        var hex = [];
        for (var i = 0; i < 3; i++) {
            var bit = (this[i] - 0).toString(16);
            hex.push((bit.length == 1) ? '0' + bit : bit);
        }
        return (array) ? hex : '#' + hex.join('');
    }

});
/*
---

script: Function.js

description: Contains Function Prototypes like create, bind, pass, and delay.

license: MIT-style license.

requires:
- /Native
- /$util

provides: [Function]

...
*/

Function.implement({

    extend: function(properties) {
        for (var property in properties) this[property] = properties[property];
        return this;
    },

    create: function(options) {
        var self = this;
        options = options || {};
        return function(event) {
            var args = options.arguments;
            args = (args != undefined) ? $splat(args) : Array.slice(arguments, (options.event) ? 1 : 0);
            if (options.event) args = [event || window.event].extend(args);
            var returns = function() {
                return self.apply(options.bind || null, args);
            };
            if (options.delay) return setTimeout(returns, options.delay);
            if (options.periodical) return setInterval(returns, options.periodical);
            if (options.attempt) return $try(returns);
            return returns();
        };
    },

    run: function(args, bind) {
        return this.apply(bind, $splat(args));
    },

    pass: function(args, bind) {
        return this.create({ bind: bind, arguments: args });
    },

    bind: function(bind, args) {
        return this.create({ bind: bind, arguments: args });
    },

    bindWithEvent: function(bind, args) {
        return this.create({ bind: bind, arguments: args, event: true });
    },

    attempt: function(args, bind) {
        return this.create({ bind: bind, arguments: args, attempt: true })();
    },

    delay: function(delay, bind, args) {
        return this.create({ bind: bind, arguments: args, delay: delay })();
    },

    periodical: function(periodical, bind, args) {
        return this.create({ bind: bind, arguments: args, periodical: periodical })();
    }

});
/*
---

script: Number.js

description: Contains Number Prototypes like limit, round, times, and ceil.

license: MIT-style license.

requires:
- /Native
- /$util

provides: [Number]

...
*/

Number.implement({

    limit: function(min, max) {
        return Math.min(max, Math.max(min, this));
    },

    round: function(precision) {
        precision = Math.pow(10, precision || 0);
        return Math.round(this * precision) / precision;
    },

    times: function(fn, bind) {
        for (var i = 0; i < this; i++) fn.call(bind, i, this);
    },

    toFloat: function() {
        return parseFloat(this);
    },

    toInt: function(base) {
        return parseInt(this, base || 10);
    }

});

Number.alias('times', 'each');

(function(math) {
    var methods = {};
    math.each(function(name) {
        if (!Number[name]) methods[name] = function() {
            return Math[name].apply(null, [this].concat($A(arguments)));
        };
    });
    Number.implement(methods);
})(['abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp', 'floor', 'log', 'max', 'min', 'pow', 'sin', 'sqrt', 'tan']);
/*
---

script: String.js

description: Contains String Prototypes like camelCase, capitalize, test, and toInt.

license: MIT-style license.

requires:
- /Native

provides: [String]

...
*/

String.implement({

    test: function(regex, params) {
        return ((typeof regex == 'string') ? new RegExp(regex, params) : regex).test(this);
    },

    contains: function(string, separator) {
        return (separator) ? (separator + this + separator).indexOf(separator + string + separator) > -1 : this.indexOf(string) > -1;
    },

    trim: function() {
        return this.replace(/^\s+|\s+$/g, '');
    },

    clean: function() {
        return this.replace(/\s+/g, ' ').trim();
    },

    camelCase: function() {
        return this.replace(/-\D/g, function(match) {
            return match.charAt(1).toUpperCase();
        });
    },

    hyphenate: function() {
        return this.replace(/[A-Z]/g, function(match) {
            return ('-' + match.charAt(0).toLowerCase());
        });
    },

    capitalize: function() {
        return this.replace(/\b[a-z]/g, function(match) {
            return match.toUpperCase();
        });
    },

    escapeRegExp: function() {
        return this.replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1');
    },

    toInt: function(base) {
        return parseInt(this, base || 10);
    },

    toFloat: function() {
        return parseFloat(this);
    },

    hexToRgb: function(array) {
        var hex = this.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);
        return (hex) ? hex.slice(1).hexToRgb(array) : null;
    },

    rgbToHex: function(array) {
        var rgb = this.match(/\d{1,3}/g);
        return (rgb) ? rgb.rgbToHex(array) : null;
    },

    stripScripts: function(option) {
        var scripts = '';
        var text = this.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi, function() {
            scripts += arguments[1] + '\n';
            return '';
        });
        if (option === true) $exec(scripts);
        else if ($type(option) == 'function') option(scripts, text);
        return text;
    },

    substitute: function(object, regexp) {
        return this.replace(regexp || (/\\?\{([^{}]+)\}/g), function(match, name) {
            if (match.charAt(0) == '\\') return match.slice(1);
            return (object[name] != undefined) ? object[name] : '';
        });
    }

});
/*
---

script: Hash.js

description: Contains Hash Prototypes. Provides a means for overcoming the JavaScript practical impossibility of extending native Objects.

license: MIT-style license.

requires:
- /Hash.base

provides: [Hash]

...
*/

Hash.implement({

    has: Object.prototype.hasOwnProperty,

    keyOf: function(value) {
        for (var key in this) {
            if (this.hasOwnProperty(key) && this[key] === value) return key;
        }
        return null;
    },

    hasValue: function(value) {
        return (Hash.keyOf(this, value) !== null);
    },

    extend: function(properties) {
        Hash.each(properties || {}, function(value, key) {
            Hash.set(this, key, value);
        }, this);
        return this;
    },

    combine: function(properties) {
        Hash.each(properties || {}, function(value, key) {
            Hash.include(this, key, value);
        }, this);
        return this;
    },

    erase: function(key) {
        if (this.hasOwnProperty(key)) delete this[key];
        return this;
    },

    get: function(key) {
        return (this.hasOwnProperty(key)) ? this[key] : null;
    },

    set: function(key, value) {
        if (!this[key] || this.hasOwnProperty(key)) this[key] = value;
        return this;
    },

    empty: function() {
        Hash.each(this, function(value, key) {
            delete this[key];
        }, this);
        return this;
    },

    include: function(key, value) {
        if (this[key] == undefined) this[key] = value;
        return this;
    },

    map: function(fn, bind) {
        var results = new Hash;
        Hash.each(this, function(value, key) {
            results.set(key, fn.call(bind, value, key, this));
        }, this);
        return results;
    },

    filter: function(fn, bind) {
        var results = new Hash;
        Hash.each(this, function(value, key) {
            if (fn.call(bind, value, key, this)) results.set(key, value);
        }, this);
        return results;
    },

    every: function(fn, bind) {
        for (var key in this) {
            if (this.hasOwnProperty(key) && !fn.call(bind, this[key], key)) return false;
        }
        return true;
    },

    some: function(fn, bind) {
        for (var key in this) {
            if (this.hasOwnProperty(key) && fn.call(bind, this[key], key)) return true;
        }
        return false;
    },

    getKeys: function() {
        var keys = [];
        Hash.each(this, function(value, key) {
            keys.push(key);
        });
        return keys;
    },

    getValues: function() {
        var values = [];
        Hash.each(this, function(value) {
            values.push(value);
        });
        return values;
    },

    toQueryString: function(base) {
        var queryString = [];
        Hash.each(this, function(value, key) {
            if (base) key = base + '[' + key + ']';
            var result;
            switch ($type(value)) {
                case 'object': result = Hash.toQueryString(value, key); break;
                case 'array':
                    var qs = {};
                    value.each(function(val, i) {
                        qs[i] = val;
                    });
                    result = Hash.toQueryString(qs, key);
                    break;
                default: result = key + '=' + encodeURIComponent(value);
            }
            if (value != undefined) queryString.push(result);
        });

        return queryString.join('&');
    }

});

Hash.alias({ keyOf: 'indexOf', hasValue: 'contains' });
/*
---

script: Event.js

description: Contains the Event Class, to make the event object cross-browser.

license: MIT-style license.

requires:
- /Window
- /Document
- /Hash
- /Array
- /Function
- /String

provides: [Event]

...
*/

var Event = new Native({

    name: 'Event',

    initialize: function(event, win) {
        win = win || window;
        var doc = win.document;
        event = event || win.event;
        if (event.$extended) return event;
        this.$extended = true;
        var type = event.type;
        var target = event.target || event.srcElement;
        while (target && target.nodeType == 3) target = target.parentNode;

        if (type.test(/key/)) {
            var code = event.which || event.keyCode;
            var key = Event.Keys.keyOf(code);
            if (type == 'keydown') {
                var fKey = code - 111;
                if (fKey > 0 && fKey < 13) key = 'f' + fKey;
            }
            key = key || String.fromCharCode(code).toLowerCase();
        } else if (type.match(/(click|mouse|menu)/i)) {
            doc = (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
            var page = {
                x: event.pageX || event.clientX + doc.scrollLeft,
                y: event.pageY || event.clientY + doc.scrollTop
            };
            var client = {
                x: (event.pageX) ? event.pageX - win.pageXOffset : event.clientX,
                y: (event.pageY) ? event.pageY - win.pageYOffset : event.clientY
            };
            if (type.match(/DOMMouseScroll|mousewheel/)) {
                var wheel = (event.wheelDelta) ? event.wheelDelta / 120 : -(event.detail || 0) / 3;
            }
            var rightClick = (event.which == 3) || (event.button == 2);
            var related = null;
            if (type.match(/over|out/)) {
                switch (type) {
                    case 'mouseover': related = event.relatedTarget || event.fromElement; break;
                    case 'mouseout': related = event.relatedTarget || event.toElement;
                }
                if (!(function() {
                    while (related && related.nodeType == 3) related = related.parentNode;
                    return true;
                }).create({ attempt: Browser.Engine.gecko })()) related = false;
            }
        }

        return $extend(this, {
            event: event,
            type: type,

            page: page,
            client: client,
            rightClick: rightClick,

            wheel: wheel,

            relatedTarget: related,
            target: target,

            code: code,
            key: key,

            shift: event.shiftKey,
            control: event.ctrlKey,
            alt: event.altKey,
            meta: event.metaKey
        });
    }

});

Event.Keys = new Hash({
    'enter': 13,
    'up': 38,
    'down': 40,
    'left': 37,
    'right': 39,
    'esc': 27,
    'space': 32,
    'backspace': 8,
    'tab': 9,
    'delete': 46
});

Event.implement({

    stop: function() {
        return this.stopPropagation().preventDefault();
    },

    stopPropagation: function() {
        if (this.event.stopPropagation) this.event.stopPropagation();
        else this.event.cancelBubble = true;
        return this;
    },

    preventDefault: function() {
        if (this.event.preventDefault) this.event.preventDefault();
        else this.event.returnValue = false;
        return this;
    }

});
/*
---

script: Class.js

description: Contains the Class Function for easily creating, extending, and implementing reusable Classes.

license: MIT-style license.

requires:
- /$util
- /Native
- /Array
- /String
- /Function
- /Number
- /Hash

provides: [Class]

...
*/

function Class(params) {

    if (params instanceof Function) params = { initialize: params };

    var newClass = function() {
        Object.reset(this);
        if (newClass._prototyping) return this;
        this._current = $empty;
        var value = (this.initialize) ? this.initialize.apply(this, arguments) : this;
        delete this._current; delete this.caller;
        return value;
    } .extend(this);

    newClass.implement(params);

    newClass.constructor = Class;
    newClass.prototype.constructor = newClass;

    return newClass;

};

Function.prototype.protect = function() {
    this._protected = true;
    return this;
};

Object.reset = function(object, key) {

    if (key == null) {
        for (var p in object) Object.reset(object, p);
        return object;
    }

    delete object[key];

    switch ($type(object[key])) {
        case 'object':
            var F = function() { };
            F.prototype = object[key];
            var i = new F;
            object[key] = Object.reset(i);
            break;
        case 'array': object[key] = $unlink(object[key]); break;
    }

    return object;

};

new Native({ name: 'Class', initialize: Class }).extend({

    instantiate: function(F) {
        F._prototyping = true;
        var proto = new F;
        delete F._prototyping;
        return proto;
    },

    wrap: function(self, key, method) {
        if (method._origin) method = method._origin;

        return function() {
            if (method._protected && this._current == null) throw new Error('The method "' + key + '" cannot be called.');
            var caller = this.caller, current = this._current;
            this.caller = current; this._current = arguments.callee;
            var result = method.apply(this, arguments);
            this._current = current; this.caller = caller;
            return result;
        } .extend({ _owner: self, _origin: method, _name: key });

    }

});

Class.implement({

    implement: function(key, value) {

        if ($type(key) == 'object') {
            for (var p in key) this.implement(p, key[p]);
            return this;
        }

        var mutator = Class.Mutators[key];

        if (mutator) {
            value = mutator.call(this, value);
            if (value == null) return this;
        }

        var proto = this.prototype;

        switch ($type(value)) {

            case 'function':
                if (value._hidden) return this;
                proto[key] = Class.wrap(this, key, value);
                break;

            case 'object':
                var previous = proto[key];
                if ($type(previous) == 'object') $mixin(previous, value);
                else proto[key] = $unlink(value);
                break;

            case 'array':
                proto[key] = $unlink(value);
                break;

            default: proto[key] = value;

        }

        return this;

    }

});

Class.Mutators = {

    Extends: function(parent) {

        this.parent = parent;
        this.prototype = Class.instantiate(parent);

        this.implement('parent', function() {
            var name = this.caller._name, previous = this.caller._owner.parent.prototype[name];
            if (!previous) throw new Error('The method "' + name + '" has no parent.');
            return previous.apply(this, arguments);
        } .protect());

    },

    Implements: function(items) {
        $splat(items).each(function(item) {
            if (item instanceof Function) item = Class.instantiate(item);
            this.implement(item);
        }, this);

    }

};
/*
---

script: Class.Extras.js

description: Contains Utility Classes that can be implemented into your own Classes to ease the execution of many common tasks.

license: MIT-style license.

requires:
- /Class

provides: [Chain, Events, Options]

...
*/

var Chain = new Class({

    $chain: [],

    chain: function() {
        this.$chain.extend(Array.flatten(arguments));
        return this;
    },

    callChain: function() {
        return (this.$chain.length) ? this.$chain.shift().apply(this, arguments) : false;
    },

    clearChain: function() {
        this.$chain.empty();
        return this;
    }

});

var Events = new Class({

    $events: {},

    addEvent: function(type, fn, internal) {
        type = Events.removeOn(type);
        if (fn != $empty) {
            this.$events[type] = this.$events[type] || [];
            this.$events[type].include(fn);
            if (internal) fn.internal = true;
        }
        return this;
    },

    addEvents: function(events) {
        for (var type in events) this.addEvent(type, events[type]);
        return this;
    },

    fireEvent: function(type, args, delay) {
        type = Events.removeOn(type);
        if (!this.$events || !this.$events[type]) return this;
        this.$events[type].each(function(fn) {
            fn.create({ 'bind': this, 'delay': delay, 'arguments': args })();
        }, this);
        return this;
    },

    removeEvent: function(type, fn) {
        type = Events.removeOn(type);
        if (!this.$events[type]) return this;
        if (!fn.internal) this.$events[type].erase(fn);
        return this;
    },

    removeEvents: function(events) {
        var type;
        if ($type(events) == 'object') {
            for (type in events) this.removeEvent(type, events[type]);
            return this;
        }
        if (events) events = Events.removeOn(events);
        for (type in this.$events) {
            if (events && events != type) continue;
            var fns = this.$events[type];
            for (var i = fns.length; i--; i) this.removeEvent(type, fns[i]);
        }
        return this;
    }

});

Events.removeOn = function(string) {
    return string.replace(/^on([A-Z])/, function(full, first) {
        return first.toLowerCase();
    });
};

var Options = new Class({

    setOptions: function() {
        this.options = $merge.run([this.options].extend(arguments));
        if (!this.addEvent) return this;
        for (var option in this.options) {
            if ($type(this.options[option]) != 'function' || !(/^on[A-Z]/).test(option)) continue;
            this.addEvent(option, this.options[option]);
            delete this.options[option];
        }
        return this;
    }

});
/*
---

script: Element.js

description: One of the most important items in MooTools. Contains the dollar function, the dollars function, and an handful of cross-browser, time-saver methods to let you easily work with HTML Elements.

license: MIT-style license.

requires:
- /Window
- /Document
- /Array
- /String
- /Function
- /Number
- /Hash

provides: [Element, Elements, $, $$, Iframe]

...
*/

var Element = new Native({

    name: 'Element',

    legacy: window.Element,

    initialize: function(tag, props) {
        var konstructor = Element.Constructors.get(tag);
        if (konstructor) return konstructor(props);
        if (typeof tag == 'string') return document.newElement(tag, props);
        return document.id(tag).set(props);
    },

    afterImplement: function(key, value) {
        Element.Prototype[key] = value;
        if (Array[key]) return;
        Elements.implement(key, function() {
            var items = [], elements = true;
            for (var i = 0, j = this.length; i < j; i++) {
                var returns = this[i][key].apply(this[i], arguments);
                items.push(returns);
                if (elements) elements = ($type(returns) == 'element');
            }
            return (elements) ? new Elements(items) : items;
        });
    }

});

Element.Prototype = { $family: { name: 'element'} };

Element.Constructors = new Hash;

var IFrame = new Native({

    name: 'IFrame',

    generics: false,

    initialize: function() {
        var params = Array.link(arguments, { properties: Object.type, iframe: $defined });
        var props = params.properties || {};
        var iframe = document.id(params.iframe);
        var onload = props.onload || $empty;
        delete props.onload;
        props.id = props.name = $pick(props.id, props.name, iframe ? (iframe.id || iframe.name) : 'IFrame_' + $time());
        iframe = new Element(iframe || 'iframe', props);
        var onFrameLoad = function() {
            var host = $try(function() {
                return iframe.contentWindow.location.host;
            });
            if (!host || host == window.location.host) {
                var win = new Window(iframe.contentWindow);
                new Document(iframe.contentWindow.document);
                $extend(win.Element.prototype, Element.Prototype);
            }
            onload.call(iframe.contentWindow, iframe.contentWindow.document);
        };
        var contentWindow = $try(function() {
            return iframe.contentWindow;
        });
        ((contentWindow && contentWindow.document.body) || window.frames[props.id]) ? onFrameLoad() : iframe.addListener('load', onFrameLoad);
        return iframe;
    }

});

var Elements = new Native({

    initialize: function(elements, options) {
        options = $extend({ ddup: true, cash: true }, options);
        elements = elements || [];
        if (options.ddup || options.cash) {
            var uniques = {}, returned = [];
            for (var i = 0, l = elements.length; i < l; i++) {
                var el = document.id(elements[i], !options.cash);
                if (options.ddup) {
                    if (uniques[el.uid]) continue;
                    uniques[el.uid] = true;
                }
                if (el) returned.push(el);
            }
            elements = returned;
        }
        return (options.cash) ? $extend(elements, this) : elements;
    }

});

Elements.implement({

    filter: function(filter, bind) {
        if (!filter) return this;
        return new Elements(Array.filter(this, (typeof filter == 'string') ? function(item) {
            return item.match(filter);
        } : filter, bind));
    }

});

Document.implement({

    newElement: function(tag, props) {
        if (Browser.Engine.trident && props) {
            ['name', 'type', 'checked'].each(function(attribute) {
                if (!props[attribute]) return;
                tag += ' ' + attribute + '="' + props[attribute] + '"';
                if (attribute != 'checked') delete props[attribute];
            });
            tag = '<' + tag + '>';
        }
        return document.id(this.createElement(tag)).set(props);
    },

    newTextNode: function(text) {
        return this.createTextNode(text);
    },

    getDocument: function() {
        return this;
    },

    getWindow: function() {
        return this.window;
    },

    id: (function() {

        var types = {

            string: function(id, nocash, doc) {
                id = doc.getElementById(id);
                return (id) ? types.element(id, nocash) : null;
            },

            element: function(el, nocash) {
                $uid(el);
                if (!nocash && !el.$family && !(/^object|embed$/i).test(el.tagName)) {
                    var proto = Element.Prototype;
                    for (var p in proto) el[p] = proto[p];
                };
                return el;
            },

            object: function(obj, nocash, doc) {
                if (obj.toElement) return types.element(obj.toElement(doc), nocash);
                return null;
            }

        };

        types.textnode = types.whitespace = types.window = types.document = $arguments(0);

        return function(el, nocash, doc) {
            if (el && el.$family && el.uid) return el;
            var type = $type(el);
            return (types[type]) ? types[type](el, nocash, doc || document) : null;
        };

    })()

});

if (window.$ == null) Window.implement({
    $: function(el, nc) {
        return document.id(el, nc, this.document);
    }
});

Window.implement({

    $$: function(selector) {
        if (arguments.length == 1 && typeof selector == 'string') return this.document.getElements(selector);
        var elements = [];
        var args = Array.flatten(arguments);
        for (var i = 0, l = args.length; i < l; i++) {
            var item = args[i];
            switch ($type(item)) {
                case 'element': elements.push(item); break;
                case 'string': elements.extend(this.document.getElements(item, true));
            }
        }
        return new Elements(elements);
    },

    getDocument: function() {
        return this.document;
    },

    getWindow: function() {
        return this;
    }

});

Native.implement([Element, Document], {

    getElement: function(selector, nocash) {
        return document.id(this.getElements(selector, true)[0] || null, nocash);
    },

    getElements: function(tags, nocash) {
        tags = tags.split(',');
        var elements = [];
        var ddup = (tags.length > 1);
        tags.each(function(tag) {
            var partial = this.getElementsByTagName(tag.trim());
            (ddup) ? elements.extend(partial) : elements = partial;
        }, this);
        return new Elements(elements, { ddup: ddup, cash: !nocash });
    }

});

(function() {

    var collected = {}, storage = {};
    var props = { input: 'checked', option: 'selected', textarea: (Browser.Engine.webkit && Browser.Engine.version < 420) ? 'innerHTML' : 'value' };

    var get = function(uid) {
        return (storage[uid] || (storage[uid] = {}));
    };

    var clean = function(item, retain) {
        if (!item) return;
        var uid = item.uid;
        if (Browser.Engine.trident) {
            if (item.clearAttributes) {
                var clone = retain && item.cloneNode(false);
                item.clearAttributes();
                if (clone) item.mergeAttributes(clone);
            } else if (item.removeEvents) {
                item.removeEvents();
            }
            if ((/object/i).test(item.tagName)) {
                for (var p in item) {
                    if (typeof item[p] == 'function') item[p] = $empty;
                }
                Element.dispose(item);
            }
        }
        if (!uid) return;
        collected[uid] = storage[uid] = null;
    };

    var purge = function() {
        Hash.each(collected, clean);
        if (Browser.Engine.trident) $A(document.getElementsByTagName('object')).each(clean);
        if (window.CollectGarbage) CollectGarbage();
        collected = storage = null;
    };

    var walk = function(element, walk, start, match, all, nocash) {
        var el = element[start || walk];
        var elements = [];
        while (el) {
            if (el.nodeType == 1 && (!match || Element.match(el, match))) {
                if (!all) return document.id(el, nocash);
                elements.push(el);
            }
            el = el[walk];
        }
        return (all) ? new Elements(elements, { ddup: false, cash: !nocash }) : null;
    };

    var attributes = {
        'html': 'innerHTML',
        'class': 'className',
        'for': 'htmlFor',
        'defaultValue': 'defaultValue',
        'text': (Browser.Engine.trident || (Browser.Engine.webkit && Browser.Engine.version < 420)) ? 'innerText' : 'textContent'
    };
    var bools = ['compact', 'nowrap', 'ismap', 'declare', 'noshade', 'checked', 'disabled', 'readonly', 'multiple', 'selected', 'noresize', 'defer'];
    var camels = ['value', 'type', 'defaultValue', 'accessKey', 'cellPadding', 'cellSpacing', 'colSpan', 'frameBorder', 'maxLength', 'readOnly', 'rowSpan', 'tabIndex', 'useMap'];

    bools = bools.associate(bools);

    Hash.extend(attributes, bools);
    Hash.extend(attributes, camels.associate(camels.map(String.toLowerCase)));

    var inserters = {

        before: function(context, element) {
            if (element.parentNode) element.parentNode.insertBefore(context, element);
        },

        after: function(context, element) {
            if (!element.parentNode) return;
            var next = element.nextSibling;
            (next) ? element.parentNode.insertBefore(context, next) : element.parentNode.appendChild(context);
        },

        bottom: function(context, element) {
            element.appendChild(context);
        },

        top: function(context, element) {
            var first = element.firstChild;
            (first) ? element.insertBefore(context, first) : element.appendChild(context);
        }

    };

    inserters.inside = inserters.bottom;

    Hash.each(inserters, function(inserter, where) {

        where = where.capitalize();

        Element.implement('inject' + where, function(el) {
            inserter(this, document.id(el, true));
            return this;
        });

        Element.implement('grab' + where, function(el) {
            inserter(document.id(el, true), this);
            return this;
        });

    });

    Element.implement({

        set: function(prop, value) {
            switch ($type(prop)) {
                case 'object':
                    for (var p in prop) this.set(p, prop[p]);
                    break;
                case 'string':
                    var property = Element.Properties.get(prop);
                    (property && property.set) ? property.set.apply(this, Array.slice(arguments, 1)) : this.setProperty(prop, value);
            }
            return this;
        },

        get: function(prop) {
            var property = Element.Properties.get(prop);
            return (property && property.get) ? property.get.apply(this, Array.slice(arguments, 1)) : this.getProperty(prop);
        },

        erase: function(prop) {
            var property = Element.Properties.get(prop);
            (property && property.erase) ? property.erase.apply(this) : this.removeProperty(prop);
            return this;
        },

        setProperty: function(attribute, value) {
            var key = attributes[attribute];
            if (value == undefined) return this.removeProperty(attribute);
            if (key && bools[attribute]) value = !!value;
            (key) ? this[key] = value : this.setAttribute(attribute, '' + value);
            return this;
        },

        setProperties: function(attributes) {
            for (var attribute in attributes) this.setProperty(attribute, attributes[attribute]);
            return this;
        },

        getProperty: function(attribute) {
            var key = attributes[attribute];
            var value = (key) ? this[key] : this.getAttribute(attribute, 2);
            return (bools[attribute]) ? !!value : (key) ? value : value || null;
        },

        getProperties: function() {
            var args = $A(arguments);
            return args.map(this.getProperty, this).associate(args);
        },

        removeProperty: function(attribute) {
            var key = attributes[attribute];
            (key) ? this[key] = (key && bools[attribute]) ? false : '' : this.removeAttribute(attribute);
            return this;
        },

        removeProperties: function() {
            Array.each(arguments, this.removeProperty, this);
            return this;
        },

        hasClass: function(className) {
            return this.className.contains(className, ' ');
        },

        addClass: function(className) {
            if (!this.hasClass(className)) this.className = (this.className + ' ' + className).clean();
            return this;
        },

        removeClass: function(className) {
            this.className = this.className.replace(new RegExp('(^|\\s)' + className + '(?:\\s|$)'), '$1');
            return this;
        },

        toggleClass: function(className) {
            return this.hasClass(className) ? this.removeClass(className) : this.addClass(className);
        },

        adopt: function() {
            Array.flatten(arguments).each(function(element) {
                element = document.id(element, true);
                if (element) this.appendChild(element);
            }, this);
            return this;
        },

        appendText: function(text, where) {
            return this.grab(this.getDocument().newTextNode(text), where);
        },

        grab: function(el, where) {
            inserters[where || 'bottom'](document.id(el, true), this);
            return this;
        },

        inject: function(el, where) {
            inserters[where || 'bottom'](this, document.id(el, true));
            return this;
        },

        replaces: function(el) {
            el = document.id(el, true);
            el.parentNode.replaceChild(this, el);
            return this;
        },

        wraps: function(el, where) {
            el = document.id(el, true);
            return this.replaces(el).grab(el, where);
        },

        getPrevious: function(match, nocash) {
            return walk(this, 'previousSibling', null, match, false, nocash);
        },

        getAllPrevious: function(match, nocash) {
            return walk(this, 'previousSibling', null, match, true, nocash);
        },

        getNext: function(match, nocash) {
            return walk(this, 'nextSibling', null, match, false, nocash);
        },

        getAllNext: function(match, nocash) {
            return walk(this, 'nextSibling', null, match, true, nocash);
        },

        getFirst: function(match, nocash) {
            return walk(this, 'nextSibling', 'firstChild', match, false, nocash);
        },

        getLast: function(match, nocash) {
            return walk(this, 'previousSibling', 'lastChild', match, false, nocash);
        },

        getParent: function(match, nocash) {
            return walk(this, 'parentNode', null, match, false, nocash);
        },

        getParents: function(match, nocash) {
            return walk(this, 'parentNode', null, match, true, nocash);
        },

        getSiblings: function(match, nocash) {
            return this.getParent().getChildren(match, nocash).erase(this);
        },

        getChildren: function(match, nocash) {
            return walk(this, 'nextSibling', 'firstChild', match, true, nocash);
        },

        getWindow: function() {
            return this.ownerDocument.window;
        },

        getDocument: function() {
            return this.ownerDocument;
        },

        getElementById: function(id, nocash) {
            var el = this.ownerDocument.getElementById(id);
            if (!el) return null;
            for (var parent = el.parentNode; parent != this; parent = parent.parentNode) {
                if (!parent) return null;
            }
            return document.id(el, nocash);
        },

        getSelected: function() {
            return new Elements($A(this.options).filter(function(option) {
                return option.selected;
            }));
        },

        getComputedStyle: function(property) {
            if (this.currentStyle) return this.currentStyle[property.camelCase()];
            var computed = this.getDocument().defaultView.getComputedStyle(this, null);
            return (computed) ? computed.getPropertyValue([property.hyphenate()]) : null;
        },

        toQueryString: function() {
            var queryString = [];
            this.getElements('input, select, textarea', true).each(function(el) {
                if (!el.name || el.disabled || el.type == 'submit' || el.type == 'reset' || el.type == 'file') return;
                var value = (el.tagName.toLowerCase() == 'select') ? Element.getSelected(el).map(function(opt) {
                    return opt.value;
                }) : ((el.type == 'radio' || el.type == 'checkbox') && !el.checked) ? null : el.value;
                $splat(value).each(function(val) {
                    if (typeof val != 'undefined') queryString.push(el.name + '=' + encodeURIComponent(val));
                });
            });
            return queryString.join('&');
        },

        clone: function(contents, keepid) {
            contents = contents !== false;
            var clone = this.cloneNode(contents);
            var clean = function(node, element) {
                if (!keepid) node.removeAttribute('id');
                if (Browser.Engine.trident) {
                    node.clearAttributes();
                    node.mergeAttributes(element);
                    node.removeAttribute('uid');
                    if (node.options) {
                        var no = node.options, eo = element.options;
                        for (var j = no.length; j--; ) no[j].selected = eo[j].selected;
                    }
                }
                var prop = props[element.tagName.toLowerCase()];
                if (prop && element[prop]) node[prop] = element[prop];
            };

            if (contents) {
                var ce = clone.getElementsByTagName('*'), te = this.getElementsByTagName('*');
                for (var i = ce.length; i--; ) clean(ce[i], te[i]);
            }

            clean(clone, this);
            return document.id(clone);
        },

        destroy: function() {
            Element.empty(this);
            Element.dispose(this);
            clean(this, true);
            return null;
        },

        empty: function() {
            $A(this.childNodes).each(function(node) {
                Element.destroy(node);
            });
            return this;
        },

        dispose: function() {
            return (this.parentNode) ? this.parentNode.removeChild(this) : this;
        },

        hasChild: function(el) {
            el = document.id(el, true);
            if (!el) return false;
            if (Browser.Engine.webkit && Browser.Engine.version < 420) return $A(this.getElementsByTagName(el.tagName)).contains(el);
            return (this.contains) ? (this != el && this.contains(el)) : !!(this.compareDocumentPosition(el) & 16);
        },

        match: function(tag) {
            return (!tag || (tag == this) || (Element.get(this, 'tag') == tag));
        }

    });

    Native.implement([Element, Window, Document], {

        addListener: function(type, fn) {
            if (type == 'unload') {
                var old = fn, self = this;
                fn = function() {
                    self.removeListener('unload', fn);
                    old();
                };
            } else {
                collected[this.uid] = this;
            }
            if (this.addEventListener) this.addEventListener(type, fn, false);
            else this.attachEvent('on' + type, fn);
            return this;
        },

        removeListener: function(type, fn) {
            if (this.removeEventListener) this.removeEventListener(type, fn, false);
            else this.detachEvent('on' + type, fn);
            return this;
        },

        retrieve: function(property, dflt) {
            var storage = get(this.uid), prop = storage[property];
            if (dflt != undefined && prop == undefined) prop = storage[property] = dflt;
            return $pick(prop);
        },

        store: function(property, value) {
            var storage = get(this.uid);
            storage[property] = value;
            return this;
        },

        eliminate: function(property) {
            var storage = get(this.uid);
            delete storage[property];
            return this;
        }

    });

    window.addListener('unload', purge);

})();

Element.Properties = new Hash;

Element.Properties.style = {

    set: function(style) {
        this.style.cssText = style;
    },

    get: function() {
        return this.style.cssText;
    },

    erase: function() {
        this.style.cssText = '';
    }

};

Element.Properties.tag = {

    get: function() {
        return this.tagName.toLowerCase();
    }

};

Element.Properties.html = (function() {
    var wrapper = document.createElement('div');

    var translations = {
        table: [1, '<table>', '</table>'],
        select: [1, '<select>', '</select>'],
        tbody: [2, '<table><tbody>', '</tbody></table>'],
        tr: [3, '<table><tbody><tr>', '</tr></tbody></table>']
    };
    translations.thead = translations.tfoot = translations.tbody;

    var html = {
        set: function() {
            var html = Array.flatten(arguments).join('');
            var wrap = Browser.Engine.trident && translations[this.get('tag')];
            if (wrap) {
                var first = wrapper;
                first.innerHTML = wrap[1] + html + wrap[2];
                for (var i = wrap[0]; i--; ) first = first.firstChild;
                this.empty().adopt(first.childNodes);
            } else {
                this.innerHTML = html;
            }
        }
    };

    html.erase = html.set;

    return html;
})();

if (Browser.Engine.webkit && Browser.Engine.version < 420) Element.Properties.text = {
    get: function() {
        if (this.innerText) return this.innerText;
        var temp = this.ownerDocument.newElement('div', { html: this.innerHTML }).inject(this.ownerDocument.body);
        var text = temp.innerText;
        temp.destroy();
        return text;
    }
};
/*
---

script: Element.Event.js

description: Contains Element methods for dealing with events. This file also includes mouseenter and mouseleave custom Element Events.

license: MIT-style license.

requires: 
- /Element
- /Event

provides: [Element.Event]

...
*/

Element.Properties.events = { set: function(events) {
    this.addEvents(events);
} 
};

Native.implement([Element, Window, Document], {

    addEvent: function(type, fn) {
        var events = this.retrieve('events', {});
        events[type] = events[type] || { 'keys': [], 'values': [] };
        if (events[type].keys.contains(fn)) return this;
        events[type].keys.push(fn);
        var realType = type, custom = Element.Events.get(type), condition = fn, self = this;
        if (custom) {
            if (custom.onAdd) custom.onAdd.call(this, fn);
            if (custom.condition) {
                condition = function(event) {
                    if (custom.condition.call(this, event)) return fn.call(this, event);
                    return true;
                };
            }
            realType = custom.base || realType;
        }
        var defn = function() {
            return fn.call(self);
        };
        var nativeEvent = Element.NativeEvents[realType];
        if (nativeEvent) {
            if (nativeEvent == 2) {
                defn = function(event) {
                    event = new Event(event, self.getWindow());
                    if (condition.call(self, event) === false) event.stop();
                };
            }
            this.addListener(realType, defn);
        }
        events[type].values.push(defn);
        return this;
    },

    removeEvent: function(type, fn) {
        var events = this.retrieve('events');
        if (!events || !events[type]) return this;
        var pos = events[type].keys.indexOf(fn);
        if (pos == -1) return this;
        events[type].keys.splice(pos, 1);
        var value = events[type].values.splice(pos, 1)[0];
        var custom = Element.Events.get(type);
        if (custom) {
            if (custom.onRemove) custom.onRemove.call(this, fn);
            type = custom.base || type;
        }
        return (Element.NativeEvents[type]) ? this.removeListener(type, value) : this;
    },

    addEvents: function(events) {
        for (var event in events) this.addEvent(event, events[event]);
        return this;
    },

    removeEvents: function(events) {
        var type;
        if ($type(events) == 'object') {
            for (type in events) this.removeEvent(type, events[type]);
            return this;
        }
        var attached = this.retrieve('events');
        if (!attached) return this;
        if (!events) {
            for (type in attached) this.removeEvents(type);
            this.eliminate('events');
        } else if (attached[events]) {
            while (attached[events].keys[0]) this.removeEvent(events, attached[events].keys[0]);
            attached[events] = null;
        }
        return this;
    },

    fireEvent: function(type, args, delay) {
        var events = this.retrieve('events');
        if (!events || !events[type]) return this;
        events[type].keys.each(function(fn) {
            fn.create({ 'bind': this, 'delay': delay, 'arguments': args })();
        }, this);
        return this;
    },

    cloneEvents: function(from, type) {
        from = document.id(from);
        var fevents = from.retrieve('events');
        if (!fevents) return this;
        if (!type) {
            for (var evType in fevents) this.cloneEvents(from, evType);
        } else if (fevents[type]) {
            fevents[type].keys.each(function(fn) {
                this.addEvent(type, fn);
            }, this);
        }
        return this;
    }

});

Element.NativeEvents = {
    click: 2, dblclick: 2, mouseup: 2, mousedown: 2, contextmenu: 2, //mouse buttons
    mousewheel: 2, DOMMouseScroll: 2, //mouse wheel
    mouseover: 2, mouseout: 2, mousemove: 2, selectstart: 2, selectend: 2, //mouse movement
    keydown: 2, keypress: 2, keyup: 2, //keyboard
    focus: 2, blur: 2, change: 2, reset: 2, select: 2, submit: 2, //form elements
    load: 1, unload: 1, beforeunload: 2, resize: 1, move: 1, DOMContentLoaded: 1, readystatechange: 1, //window
    error: 1, abort: 1, scroll: 1 //misc
};

(function() {

    var $check = function(event) {
        var related = event.relatedTarget;
        if (related == undefined) return true;
        if (related === false) return false;
        return ($type(this) != 'document' && related != this && related.prefix != 'xul' && !this.hasChild(related));
    };

    Element.Events = new Hash({

        mouseenter: {
            base: 'mouseover',
            condition: $check
        },

        mouseleave: {
            base: 'mouseout',
            condition: $check
        },

        mousewheel: {
            base: (Browser.Engine.gecko) ? 'DOMMouseScroll' : 'mousewheel'
        }

    });

})();
/*
---

script: Element.Style.js

description: Contains methods for interacting with the styles of Elements in a fashionable way.

license: MIT-style license.

requires:
- /Element

provides: [Element.Style]

...
*/

Element.Properties.styles = { set: function(styles) {
    this.setStyles(styles);
} 
};

Element.Properties.opacity = {

    set: function(opacity, novisibility) {
        if (!novisibility) {
            if (opacity == 0) {
                if (this.style.visibility != 'hidden') this.style.visibility = 'hidden';
            } else {
                if (this.style.visibility != 'visible') this.style.visibility = 'visible';
            }
        }
        if (!this.currentStyle || !this.currentStyle.hasLayout) this.style.zoom = 1;
        if (Browser.Engine.trident) this.style.filter = (opacity == 1) ? '' : 'alpha(opacity=' + opacity * 100 + ')';
        this.style.opacity = opacity;
        this.store('opacity', opacity);
    },

    get: function() {
        return this.retrieve('opacity', 1);
    }

};

Element.implement({

    setOpacity: function(value) {
        return this.set('opacity', value, true);
    },

    getOpacity: function() {
        return this.get('opacity');
    },

    setStyle: function(property, value) {
        switch (property) {
            case 'opacity': return this.set('opacity', parseFloat(value));
            case 'float': property = (Browser.Engine.trident) ? 'styleFloat' : 'cssFloat';
        }
        property = property.camelCase();
        if ($type(value) != 'string') {
            var map = (Element.Styles.get(property) || '@').split(' ');
            value = $splat(value).map(function(val, i) {
                if (!map[i]) return '';
                return ($type(val) == 'number') ? map[i].replace('@', Math.round(val)) : val;
            }).join(' ');
        } else if (value == String(Number(value))) {
            value = Math.round(value);
        }
        this.style[property] = value;
        return this;
    },

    getStyle: function(property) {
        switch (property) {
            case 'opacity': return this.get('opacity');
            case 'float': property = (Browser.Engine.trident) ? 'styleFloat' : 'cssFloat';
        }
        property = property.camelCase();
        var result = this.style[property];
        if (!$chk(result)) {
            result = [];
            for (var style in Element.ShortStyles) {
                if (property != style) continue;
                for (var s in Element.ShortStyles[style]) result.push(this.getStyle(s));
                return result.join(' ');
            }
            result = this.getComputedStyle(property);
        }
        if (result) {
            result = String(result);
            var color = result.match(/rgba?\([\d\s,]+\)/);
            if (color) result = result.replace(color[0], color[0].rgbToHex());
        }
        if (Browser.Engine.presto || (Browser.Engine.trident && !$chk(parseInt(result, 10)))) {
            if (property.test(/^(height|width)$/)) {
                var values = (property == 'width') ? ['left', 'right'] : ['top', 'bottom'], size = 0;
                values.each(function(value) {
                    size += this.getStyle('border-' + value + '-width').toInt() + this.getStyle('padding-' + value).toInt();
                }, this);
                return this['offset' + property.capitalize()] - size + 'px';
            }
            if ((Browser.Engine.presto) && String(result).test('px')) return result;
            if (property.test(/(border(.+)Width|margin|padding)/)) return '0px';
        }
        return result;
    },

    setStyles: function(styles) {
        for (var style in styles) this.setStyle(style, styles[style]);
        return this;
    },

    getStyles: function() {
        var result = {};
        Array.flatten(arguments).each(function(key) {
            result[key] = this.getStyle(key);
        }, this);
        return result;
    }

});

Element.Styles = new Hash({
    left: '@px', top: '@px', bottom: '@px', right: '@px',
    width: '@px', height: '@px', maxWidth: '@px', maxHeight: '@px', minWidth: '@px', minHeight: '@px',
    backgroundColor: 'rgb(@, @, @)', backgroundPosition: '@px @px', color: 'rgb(@, @, @)',
    fontSize: '@px', letterSpacing: '@px', lineHeight: '@px', clip: 'rect(@px @px @px @px)',
    margin: '@px @px @px @px', padding: '@px @px @px @px', border: '@px @ rgb(@, @, @) @px @ rgb(@, @, @) @px @ rgb(@, @, @)',
    borderWidth: '@px @px @px @px', borderStyle: '@ @ @ @', borderColor: 'rgb(@, @, @) rgb(@, @, @) rgb(@, @, @) rgb(@, @, @)',
    zIndex: '@', 'zoom': '@', fontWeight: '@', textIndent: '@px', opacity: '@'
});

Element.ShortStyles = { margin: {}, padding: {}, border: {}, borderWidth: {}, borderStyle: {}, borderColor: {} };

['Top', 'Right', 'Bottom', 'Left'].each(function(direction) {
    var Short = Element.ShortStyles;
    var All = Element.Styles;
    ['margin', 'padding'].each(function(style) {
        var sd = style + direction;
        Short[style][sd] = All[sd] = '@px';
    });
    var bd = 'border' + direction;
    Short.border[bd] = All[bd] = '@px @ rgb(@, @, @)';
    var bdw = bd + 'Width', bds = bd + 'Style', bdc = bd + 'Color';
    Short[bd] = {};
    Short.borderWidth[bdw] = Short[bd][bdw] = All[bdw] = '@px';
    Short.borderStyle[bds] = Short[bd][bds] = All[bds] = '@';
    Short.borderColor[bdc] = Short[bd][bdc] = All[bdc] = 'rgb(@, @, @)';
});
/*
---

script: Element.Dimensions.js

description: Contains methods to work with size, scroll, or positioning of Elements and the window object.

license: MIT-style license.

credits:
- Element positioning based on the [qooxdoo](http://qooxdoo.org/) code and smart browser fixes, [LGPL License](http://www.gnu.org/licenses/lgpl.html).
- Viewport dimensions based on [YUI](http://developer.yahoo.com/yui/) code, [BSD License](http://developer.yahoo.com/yui/license.html).

requires:
- /Element

provides: [Element.Dimensions]

...
*/

(function() {

    Element.implement({

        scrollTo: function(x, y) {
            if (isBody(this)) {
                this.getWindow().scrollTo(x, y);
            } else {
                this.scrollLeft = x;
                this.scrollTop = y;
            }
            return this;
        },

        getSize: function() {
            if (isBody(this)) return this.getWindow().getSize();
            return { x: this.offsetWidth, y: this.offsetHeight };
        },

        getScrollSize: function() {
            if (isBody(this)) return this.getWindow().getScrollSize();
            return { x: this.scrollWidth, y: this.scrollHeight };
        },

        getScroll: function() {
            if (isBody(this)) return this.getWindow().getScroll();
            return { x: this.scrollLeft, y: this.scrollTop };
        },

        getScrolls: function() {
            var element = this, position = { x: 0, y: 0 };
            while (element && !isBody(element)) {
                position.x += element.scrollLeft;
                position.y += element.scrollTop;
                element = element.parentNode;
            }
            return position;
        },

        getOffsetParent: function() {
            var element = this;
            if (isBody(element)) return null;
            if (!Browser.Engine.trident) return element.offsetParent;
            while ((element = element.parentNode) && !isBody(element)) {
                if (styleString(element, 'position') != 'static') return element;
            }
            return null;
        },

        getOffsets: function() {
            if (this.getBoundingClientRect) {
                var bound = this.getBoundingClientRect(),
				html = document.id(this.getDocument().documentElement),
				htmlScroll = html.getScroll(),
				elemScrolls = this.getScrolls(),
				elemScroll = this.getScroll(),
				isFixed = (styleString(this, 'position') == 'fixed');

                return {
                    x: bound.left.toInt() + elemScrolls.x - elemScroll.x + ((isFixed) ? 0 : htmlScroll.x) - html.clientLeft,
                    y: bound.top.toInt() + elemScrolls.y - elemScroll.y + ((isFixed) ? 0 : htmlScroll.y) - html.clientTop
                };
            }

            var element = this, position = { x: 0, y: 0 };
            if (isBody(this)) return position;

            while (element && !isBody(element)) {
                position.x += element.offsetLeft;
                position.y += element.offsetTop;

                if (Browser.Engine.gecko) {
                    if (!borderBox(element)) {
                        position.x += leftBorder(element);
                        position.y += topBorder(element);
                    }
                    var parent = element.parentNode;
                    if (parent && styleString(parent, 'overflow') != 'visible') {
                        position.x += leftBorder(parent);
                        position.y += topBorder(parent);
                    }
                } else if (element != this && Browser.Engine.webkit) {
                    position.x += leftBorder(element);
                    position.y += topBorder(element);
                }

                element = element.offsetParent;
            }
            if (Browser.Engine.gecko && !borderBox(this)) {
                position.x -= leftBorder(this);
                position.y -= topBorder(this);
            }
            return position;
        },

        getPosition: function(relative) {
            if (isBody(this)) return { x: 0, y: 0 };
            var offset = this.getOffsets(),
				scroll = this.getScrolls();
            var position = {
                x: offset.x - scroll.x,
                y: offset.y - scroll.y
            };
            var relativePosition = (relative && (relative = document.id(relative))) ? relative.getPosition() : { x: 0, y: 0 };
            return { x: position.x - relativePosition.x, y: position.y - relativePosition.y };
        },

        getCoordinates: function(element) {
            if (isBody(this)) return this.getWindow().getCoordinates();
            var position = this.getPosition(element),
				size = this.getSize();
            var obj = {
                left: position.x,
                top: position.y,
                width: size.x,
                height: size.y
            };
            obj.right = obj.left + obj.width;
            obj.bottom = obj.top + obj.height;
            return obj;
        },

        computePosition: function(obj) {
            return {
                left: obj.x - styleNumber(this, 'margin-left'),
                top: obj.y - styleNumber(this, 'margin-top')
            };
        },

        setPosition: function(obj) {
            return this.setStyles(this.computePosition(obj));
        }

    });


    Native.implement([Document, Window], {

        getSize: function() {
            if (Browser.Engine.presto || Browser.Engine.webkit) {
                var win = this.getWindow();
                return { x: win.innerWidth, y: win.innerHeight };
            }
            var doc = getCompatElement(this);
            return { x: doc.clientWidth, y: doc.clientHeight };
        },

        getScroll: function() {
            var win = this.getWindow(), doc = getCompatElement(this);
            return { x: win.pageXOffset || doc.scrollLeft, y: win.pageYOffset || doc.scrollTop };
        },

        getScrollSize: function() {
            var doc = getCompatElement(this), min = this.getSize();
            return { x: Math.max(doc.scrollWidth, min.x), y: Math.max(doc.scrollHeight, min.y) };
        },

        getPosition: function() {
            return { x: 0, y: 0 };
        },

        getCoordinates: function() {
            var size = this.getSize();
            return { top: 0, left: 0, bottom: size.y, right: size.x, height: size.y, width: size.x };
        }

    });

    // private methods

    var styleString = Element.getComputedStyle;

    function styleNumber(element, style) {
        return styleString(element, style).toInt() || 0;
    };

    function borderBox(element) {
        return styleString(element, '-moz-box-sizing') == 'border-box';
    };

    function topBorder(element) {
        return styleNumber(element, 'border-top-width');
    };

    function leftBorder(element) {
        return styleNumber(element, 'border-left-width');
    };

    function isBody(element) {
        return (/^(?:body|html)$/i).test(element.tagName);
    };

    function getCompatElement(element) {
        var doc = element.getDocument();
        return (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
    };

})();

//aliases
Element.alias('setPosition', 'position'); //compatability

Native.implement([Window, Document, Element], {

    getHeight: function() {
        return this.getSize().y;
    },

    getWidth: function() {
        return this.getSize().x;
    },

    getScrollTop: function() {
        return this.getScroll().y;
    },

    getScrollLeft: function() {
        return this.getScroll().x;
    },

    getScrollHeight: function() {
        return this.getScrollSize().y;
    },

    getScrollWidth: function() {
        return this.getScrollSize().x;
    },

    getTop: function() {
        return this.getPosition().y;
    },

    getLeft: function() {
        return this.getPosition().x;
    }

});
/*
---

script: Selectors.js

description: Adds advanced CSS-style querying capabilities for targeting HTML Elements. Includes pseudo selectors.

license: MIT-style license.

requires:
- /Element

provides: [Selectors]

...
*/

Native.implement([Document, Element], {

    getElements: function(expression, nocash) {
        expression = expression.split(',');
        var items, local = {};
        for (var i = 0, l = expression.length; i < l; i++) {
            var selector = expression[i], elements = Selectors.Utils.search(this, selector, local);
            if (i != 0 && elements.item) elements = $A(elements);
            items = (i == 0) ? elements : (items.item) ? $A(items).concat(elements) : items.concat(elements);
        }
        return new Elements(items, { ddup: (expression.length > 1), cash: !nocash });
    }

});

Element.implement({

    match: function(selector) {
        if (!selector || (selector == this)) return true;
        var tagid = Selectors.Utils.parseTagAndID(selector);
        var tag = tagid[0], id = tagid[1];
        if (!Selectors.Filters.byID(this, id) || !Selectors.Filters.byTag(this, tag)) return false;
        var parsed = Selectors.Utils.parseSelector(selector);
        return (parsed) ? Selectors.Utils.filter(this, parsed, {}) : true;
    }

});

var Selectors = { Cache: { nth: {}, parsed: {}} };

Selectors.RegExps = {
    id: (/#([\w-]+)/),
    tag: (/^(\w+|\*)/),
    quick: (/^(\w+|\*)$/),
    splitter: (/\s*([+>~\s])\s*([a-zA-Z#.*:\[])/g),
    combined: (/\.([\w-]+)|\[(\w+)(?:([!*^$~|]?=)(["']?)([^\4]*?)\4)?\]|:([\w-]+)(?:\(["']?(.*?)?["']?\)|$)/g)
};

Selectors.Utils = {

    chk: function(item, uniques) {
        if (!uniques) return true;
        var uid = $uid(item);
        if (!uniques[uid]) return uniques[uid] = true;
        return false;
    },

    parseNthArgument: function(argument) {
        if (Selectors.Cache.nth[argument]) return Selectors.Cache.nth[argument];
        var parsed = argument.match(/^([+-]?\d*)?([a-z]+)?([+-]?\d*)?$/);
        if (!parsed) return false;
        var inta = parseInt(parsed[1], 10);
        var a = (inta || inta === 0) ? inta : 1;
        var special = parsed[2] || false;
        var b = parseInt(parsed[3], 10) || 0;
        if (a != 0) {
            b--;
            while (b < 1) b += a;
            while (b >= a) b -= a;
        } else {
            a = b;
            special = 'index';
        }
        switch (special) {
            case 'n': parsed = { a: a, b: b, special: 'n' }; break;
            case 'odd': parsed = { a: 2, b: 0, special: 'n' }; break;
            case 'even': parsed = { a: 2, b: 1, special: 'n' }; break;
            case 'first': parsed = { a: 0, special: 'index' }; break;
            case 'last': parsed = { special: 'last-child' }; break;
            case 'only': parsed = { special: 'only-child' }; break;
            default: parsed = { a: (a - 1), special: 'index' };
        }

        return Selectors.Cache.nth[argument] = parsed;
    },

    parseSelector: function(selector) {
        if (Selectors.Cache.parsed[selector]) return Selectors.Cache.parsed[selector];
        var m, parsed = { classes: [], pseudos: [], attributes: [] };
        while ((m = Selectors.RegExps.combined.exec(selector))) {
            var cn = m[1], an = m[2], ao = m[3], av = m[5], pn = m[6], pa = m[7];
            if (cn) {
                parsed.classes.push(cn);
            } else if (pn) {
                var parser = Selectors.Pseudo.get(pn);
                if (parser) parsed.pseudos.push({ parser: parser, argument: pa });
                else parsed.attributes.push({ name: pn, operator: '=', value: pa });
            } else if (an) {
                parsed.attributes.push({ name: an, operator: ao, value: av });
            }
        }
        if (!parsed.classes.length) delete parsed.classes;
        if (!parsed.attributes.length) delete parsed.attributes;
        if (!parsed.pseudos.length) delete parsed.pseudos;
        if (!parsed.classes && !parsed.attributes && !parsed.pseudos) parsed = null;
        return Selectors.Cache.parsed[selector] = parsed;
    },

    parseTagAndID: function(selector) {
        var tag = selector.match(Selectors.RegExps.tag);
        var id = selector.match(Selectors.RegExps.id);
        return [(tag) ? tag[1] : '*', (id) ? id[1] : false];
    },

    filter: function(item, parsed, local) {
        var i;
        if (parsed.classes) {
            for (i = parsed.classes.length; i--; i) {
                var cn = parsed.classes[i];
                if (!Selectors.Filters.byClass(item, cn)) return false;
            }
        }
        if (parsed.attributes) {
            for (i = parsed.attributes.length; i--; i) {
                var att = parsed.attributes[i];
                if (!Selectors.Filters.byAttribute(item, att.name, att.operator, att.value)) return false;
            }
        }
        if (parsed.pseudos) {
            for (i = parsed.pseudos.length; i--; i) {
                var psd = parsed.pseudos[i];
                if (!Selectors.Filters.byPseudo(item, psd.parser, psd.argument, local)) return false;
            }
        }
        return true;
    },

    getByTagAndID: function(ctx, tag, id) {
        if (id) {
            var item = (ctx.getElementById) ? ctx.getElementById(id, true) : Element.getElementById(ctx, id, true);
            return (item && Selectors.Filters.byTag(item, tag)) ? [item] : [];
        } else {
            return ctx.getElementsByTagName(tag);
        }
    },

    search: function(self, expression, local) {
        var splitters = [];

        var selectors = expression.trim().replace(Selectors.RegExps.splitter, function(m0, m1, m2) {
            splitters.push(m1);
            return ':)' + m2;
        }).split(':)');

        var items, filtered, item;

        for (var i = 0, l = selectors.length; i < l; i++) {

            var selector = selectors[i];

            if (i == 0 && Selectors.RegExps.quick.test(selector)) {
                items = self.getElementsByTagName(selector);
                continue;
            }

            var splitter = splitters[i - 1];

            var tagid = Selectors.Utils.parseTagAndID(selector);
            var tag = tagid[0], id = tagid[1];

            if (i == 0) {
                items = Selectors.Utils.getByTagAndID(self, tag, id);
            } else {
                var uniques = {}, found = [];
                for (var j = 0, k = items.length; j < k; j++) found = Selectors.Getters[splitter](found, items[j], tag, id, uniques);
                items = found;
            }

            var parsed = Selectors.Utils.parseSelector(selector);

            if (parsed) {
                filtered = [];
                for (var m = 0, n = items.length; m < n; m++) {
                    item = items[m];
                    if (Selectors.Utils.filter(item, parsed, local)) filtered.push(item);
                }
                items = filtered;
            }

        }

        return items;

    }

};

Selectors.Getters = {

    ' ': function(found, self, tag, id, uniques) {
        var items = Selectors.Utils.getByTagAndID(self, tag, id);
        for (var i = 0, l = items.length; i < l; i++) {
            var item = items[i];
            if (Selectors.Utils.chk(item, uniques)) found.push(item);
        }
        return found;
    },

    '>': function(found, self, tag, id, uniques) {
        var children = Selectors.Utils.getByTagAndID(self, tag, id);
        for (var i = 0, l = children.length; i < l; i++) {
            var child = children[i];
            if (child.parentNode == self && Selectors.Utils.chk(child, uniques)) found.push(child);
        }
        return found;
    },

    '+': function(found, self, tag, id, uniques) {
        while ((self = self.nextSibling)) {
            if (self.nodeType == 1) {
                if (Selectors.Utils.chk(self, uniques) && Selectors.Filters.byTag(self, tag) && Selectors.Filters.byID(self, id)) found.push(self);
                break;
            }
        }
        return found;
    },

    '~': function(found, self, tag, id, uniques) {
        while ((self = self.nextSibling)) {
            if (self.nodeType == 1) {
                if (!Selectors.Utils.chk(self, uniques)) break;
                if (Selectors.Filters.byTag(self, tag) && Selectors.Filters.byID(self, id)) found.push(self);
            }
        }
        return found;
    }

};

Selectors.Filters = {

    byTag: function(self, tag) {
        return (tag == '*' || (self.tagName && self.tagName.toLowerCase() == tag));
    },

    byID: function(self, id) {
        return (!id || (self.id && self.id == id));
    },

    byClass: function(self, klass) {
        return (self.className && self.className.contains && self.className.contains(klass, ' '));
    },

    byPseudo: function(self, parser, argument, local) {
        return parser.call(self, argument, local);
    },

    byAttribute: function(self, name, operator, value) {
        var result = Element.prototype.getProperty.call(self, name);
        if (!result) return (operator == '!=');
        if (!operator || value == undefined) return true;
        switch (operator) {
            case '=': return (result == value);
            case '*=': return (result.contains(value));
            case '^=': return (result.substr(0, value.length) == value);
            case '$=': return (result.substr(result.length - value.length) == value);
            case '!=': return (result != value);
            case '~=': return result.contains(value, ' ');
            case '|=': return result.contains(value, '-');
        }
        return false;
    }

};

Selectors.Pseudo = new Hash({

    // w3c pseudo selectors

    checked: function() {
        return this.checked;
    },

    empty: function() {
        return !(this.innerText || this.textContent || '').length;
    },

    not: function(selector) {
        return !Element.match(this, selector);
    },

    contains: function(text) {
        return (this.innerText || this.textContent || '').contains(text);
    },

    'first-child': function() {
        return Selectors.Pseudo.index.call(this, 0);
    },

    'last-child': function() {
        var element = this;
        while ((element = element.nextSibling)) {
            if (element.nodeType == 1) return false;
        }
        return true;
    },

    'only-child': function() {
        var prev = this;
        while ((prev = prev.previousSibling)) {
            if (prev.nodeType == 1) return false;
        }
        var next = this;
        while ((next = next.nextSibling)) {
            if (next.nodeType == 1) return false;
        }
        return true;
    },

    'nth-child': function(argument, local) {
        argument = (argument == undefined) ? 'n' : argument;
        var parsed = Selectors.Utils.parseNthArgument(argument);
        if (parsed.special != 'n') return Selectors.Pseudo[parsed.special].call(this, parsed.a, local);
        var count = 0;
        local.positions = local.positions || {};
        var uid = $uid(this);
        if (!local.positions[uid]) {
            var self = this;
            while ((self = self.previousSibling)) {
                if (self.nodeType != 1) continue;
                count++;
                var position = local.positions[$uid(self)];
                if (position != undefined) {
                    count = position + count;
                    break;
                }
            }
            local.positions[uid] = count;
        }
        return (local.positions[uid] % parsed.a == parsed.b);
    },

    // custom pseudo selectors

    index: function(index) {
        var element = this, count = 0;
        while ((element = element.previousSibling)) {
            if (element.nodeType == 1 && ++count > index) return false;
        }
        return (count == index);
    },

    even: function(argument, local) {
        return Selectors.Pseudo['nth-child'].call(this, '2n+1', local);
    },

    odd: function(argument, local) {
        return Selectors.Pseudo['nth-child'].call(this, '2n', local);
    },

    selected: function() {
        return this.selected;
    },

    enabled: function() {
        return (this.disabled === false);
    }

});
/*
---

script: DomReady.js

description: Contains the custom event domready.

license: MIT-style license.

requires:
- /Element.Event

provides: [DomReady]

...
*/

Element.Events.domready = {

    onAdd: function(fn) {
        if (Browser.loaded) fn.call(this);
    }

};

(function() {

    var domready = function() {
        if (Browser.loaded) return;
        Browser.loaded = true;
        window.fireEvent('domready');
        document.fireEvent('domready');
    };

    window.addEvent('load', domready);

    if (Browser.Engine.trident) {
        var temp = document.createElement('div');
        (function() {
            ($try(function() {
                temp.doScroll(); // Technique by Diego Perini
                return document.id(temp).inject(document.body).set('html', 'temp').dispose();
            })) ? domready() : arguments.callee.delay(50);
        })();
    } else if (Browser.Engine.webkit && Browser.Engine.version < 525) {
        (function() {
            (['loaded', 'complete'].contains(document.readyState)) ? domready() : arguments.callee.delay(50);
        })();
    } else {
        document.addEvent('DOMContentLoaded', domready);
    }

})();
/*
---

script: JSON.js

description: JSON encoder and decoder.

license: MIT-style license.

See Also: <http://www.json.org/>

requires:
- /Array
- /String
- /Number
- /Function
- /Hash

provides: [JSON]

...
*/

var JSON = new Hash(this.JSON && {
    stringify: JSON.stringify,
    parse: JSON.parse
}).extend({

    $specialChars: { '\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"': '\\"', '\\': '\\\\' },

    $replaceChars: function(chr) {
        return JSON.$specialChars[chr] || '\\u00' + Math.floor(chr.charCodeAt() / 16).toString(16) + (chr.charCodeAt() % 16).toString(16);
    },

    encode: function(obj) {
        switch ($type(obj)) {
            case 'string':
                return '"' + obj.replace(/[\x00-\x1f\\"]/g, JSON.$replaceChars) + '"';
            case 'array':
                return '[' + String(obj.map(JSON.encode).clean()) + ']';
            case 'object': case 'hash':
                var string = [];
                Hash.each(obj, function(value, key) {
                    var json = JSON.encode(value);
                    if (json) string.push(JSON.encode(key) + ':' + json);
                });
                return '{' + string + '}';
            case 'number': case 'boolean': return String(obj);
            case false: return 'null';
        }
        return null;
    },

    decode: function(string, secure) {
        if ($type(string) != 'string' || !string.length) return null;
        if (secure && !(/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(string.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, ''))) return null;
        return eval('(' + string + ')');
    }

});

Native.implement([Hash, Array, String, Number], {

    toJSON: function() {
        return JSON.encode(this);
    }

});
/*
---

script: Cookie.js

description: Class for creating, reading, and deleting browser Cookies.

license: MIT-style license.

credits:
- Based on the functions by Peter-Paul Koch (http://quirksmode.org).

requires:
- /Options

provides: [Cookie]

...
*/

var Cookie = new Class({

    Implements: Options,

    options: {
        path: false,
        domain: false,
        duration: false,
        secure: false,
        document: document
    },

    initialize: function(key, options) {
        this.key = key;
        this.setOptions(options);
    },

    write: function(value) {
        value = encodeURIComponent(value);
        if (this.options.domain) value += '; domain=' + this.options.domain;
        if (this.options.path) value += '; path=' + this.options.path;
        if (this.options.duration) {
            var date = new Date();
            date.setTime(date.getTime() + this.options.duration * 24 * 60 * 60 * 1000);
            value += '; expires=' + date.toGMTString();
        }
        if (this.options.secure) value += '; secure';
        this.options.document.cookie = this.key + '=' + value;
        return this;
    },

    read: function() {
        var value = this.options.document.cookie.match('(?:^|;)\\s*' + this.key.escapeRegExp() + '=([^;]*)');
        return (value) ? decodeURIComponent(value[1]) : null;
    },

    dispose: function() {
        new Cookie(this.key, $merge(this.options, { duration: -1 })).write('');
        return this;
    }

});

Cookie.write = function(key, value, options) {
    return new Cookie(key, options).write(value);
};

Cookie.read = function(key) {
    return new Cookie(key).read();
};

Cookie.dispose = function(key, options) {
    return new Cookie(key, options).dispose();
};
/*
---

script: Swiff.js

description: Wrapper for embedding SWF movies. Supports External Interface Communication.

license: MIT-style license.

credits: 
- Flash detection & Internet Explorer + Flash Player 9 fix inspired by SWFObject.

requires:
- /Options
- /$util

provides: [Swiff]

...
*/

var Swiff = new Class({

    Implements: [Options],

    options: {
        id: null,
        height: 1,
        width: 1,
        container: null,
        properties: {},
        params: {
            quality: 'high',
            allowScriptAccess: 'always',
            wMode: 'transparent',
            swLiveConnect: true
        },
        callBacks: {},
        vars: {}
    },

    toElement: function() {
        return this.object;
    },

    initialize: function(path, options) {
        this.instance = 'Swiff_' + $time();

        this.setOptions(options);
        options = this.options;
        var id = this.id = options.id || this.instance;
        var container = document.id(options.container);

        Swiff.CallBacks[this.instance] = {};

        var params = options.params, vars = options.vars, callBacks = options.callBacks;
        var properties = $extend({ height: options.height, width: options.width }, options.properties);

        var self = this;

        for (var callBack in callBacks) {
            Swiff.CallBacks[this.instance][callBack] = (function(option) {
                return function() {
                    return option.apply(self.object, arguments);
                };
            })(callBacks[callBack]);
            vars[callBack] = 'Swiff.CallBacks.' + this.instance + '.' + callBack;
        }

        params.flashVars = Hash.toQueryString(vars);
        if (Browser.Engine.trident) {
            properties.classid = 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000';
            params.movie = path;
        } else {
            properties.type = 'application/x-shockwave-flash';
            properties.data = path;
        }
        var build = '<object id="' + id + '"';
        for (var property in properties) build += ' ' + property + '="' + properties[property] + '"';
        build += '>';
        for (var param in params) {
            if (params[param]) build += '<param name="' + param + '" value="' + params[param] + '" />';
        }
        build += '</object>';
        this.object = ((container) ? container.empty() : new Element('div')).set('html', build).firstChild;
    },

    replaces: function(element) {
        element = document.id(element, true);
        element.parentNode.replaceChild(this.toElement(), element);
        return this;
    },

    inject: function(element) {
        document.id(element, true).appendChild(this.toElement());
        return this;
    },

    remote: function() {
        return Swiff.remote.apply(Swiff, [this.toElement()].extend(arguments));
    }

});

Swiff.CallBacks = {};

Swiff.remote = function(obj, fn) {
    var rs = obj.CallFunction('<invoke name="' + fn + '" returntype="javascript">' + __flash__argumentsToXML(arguments, 2) + '</invoke>');
    return eval(rs);
};
/*
---

script: Fx.js

description: Contains the basic animation logic to be extended by all other Fx Classes.

license: MIT-style license.

requires:
- /Chain
- /Events
- /Options

provides: [Fx]

...
*/

var Fx = new Class({

    Implements: [Chain, Events, Options],

    options: {
        /*
        onStart: $empty,
        onCancel: $empty,
        onComplete: $empty,
        */
        fps: 50,
        unit: false,
        duration: 500,
        link: 'ignore'
    },

    initialize: function(options) {
        this.subject = this.subject || this;
        this.setOptions(options);
        this.options.duration = Fx.Durations[this.options.duration] || this.options.duration.toInt();
        var wait = this.options.wait;
        if (wait === false) this.options.link = 'cancel';
    },

    getTransition: function() {
        return function(p) {
            return -(Math.cos(Math.PI * p) - 1) / 2;
        };
    },

    step: function() {
        var time = $time();
        if (time < this.time + this.options.duration) {
            var delta = this.transition((time - this.time) / this.options.duration);
            this.set(this.compute(this.from, this.to, delta));
        } else {
            this.set(this.compute(this.from, this.to, 1));
            this.complete();
        }
    },

    set: function(now) {
        return now;
    },

    compute: function(from, to, delta) {
        return Fx.compute(from, to, delta);
    },

    check: function() {
        if (!this.timer) return true;
        switch (this.options.link) {
            case 'cancel': this.cancel(); return true;
            case 'chain': this.chain(this.caller.bind(this, arguments)); return false;
        }
        return false;
    },

    start: function(from, to) {
        if (!this.check(from, to)) return this;
        this.from = from;
        this.to = to;
        this.time = 0;
        this.transition = this.getTransition();
        this.startTimer();
        this.onStart();
        return this;
    },

    complete: function() {
        if (this.stopTimer()) this.onComplete();
        return this;
    },

    cancel: function() {
        if (this.stopTimer()) this.onCancel();
        return this;
    },

    onStart: function() {
        this.fireEvent('start', this.subject);
    },

    onComplete: function() {
        this.fireEvent('complete', this.subject);
        if (!this.callChain()) this.fireEvent('chainComplete', this.subject);
    },

    onCancel: function() {
        this.fireEvent('cancel', this.subject).clearChain();
    },

    pause: function() {
        this.stopTimer();
        return this;
    },

    resume: function() {
        this.startTimer();
        return this;
    },

    stopTimer: function() {
        if (!this.timer) return false;
        this.time = $time() - this.time;
        this.timer = $clear(this.timer);
        return true;
    },

    startTimer: function() {
        if (this.timer) return false;
        this.time = $time() - this.time;
        this.timer = this.step.periodical(Math.round(1000 / this.options.fps), this);
        return true;
    }

});

Fx.compute = function(from, to, delta) {
    return (to - from) * delta + from;
};

Fx.Durations = { 'short': 250, 'normal': 500, 'long': 1000 };
/*
---

script: Fx.CSS.js

description: Contains the CSS animation logic. Used by Fx.Tween, Fx.Morph, Fx.Elements.

license: MIT-style license.

requires:
- /Fx
- /Element.Style

provides: [Fx.CSS]

...
*/

Fx.CSS = new Class({

    Extends: Fx,

    //prepares the base from/to object

    prepare: function(element, property, values) {
        values = $splat(values);
        var values1 = values[1];
        if (!$chk(values1)) {
            values[1] = values[0];
            values[0] = element.getStyle(property);
        }
        var parsed = values.map(this.parse);
        return { from: parsed[0], to: parsed[1] };
    },

    //parses a value into an array

    parse: function(value) {
        value = $lambda(value)();
        value = (typeof value == 'string') ? value.split(' ') : $splat(value);
        return value.map(function(val) {
            val = String(val);
            var found = false;
            Fx.CSS.Parsers.each(function(parser, key) {
                if (found) return;
                var parsed = parser.parse(val);
                if ($chk(parsed)) found = { value: parsed, parser: parser };
            });
            found = found || { value: val, parser: Fx.CSS.Parsers.String };
            return found;
        });
    },

    //computes by a from and to prepared objects, using their parsers.

    compute: function(from, to, delta) {
        var computed = [];
        (Math.min(from.length, to.length)).times(function(i) {
            computed.push({ value: from[i].parser.compute(from[i].value, to[i].value, delta), parser: from[i].parser });
        });
        computed.$family = { name: 'fx:css:value' };
        return computed;
    },

    //serves the value as settable

    serve: function(value, unit) {
        if ($type(value) != 'fx:css:value') value = this.parse(value);
        var returned = [];
        value.each(function(bit) {
            returned = returned.concat(bit.parser.serve(bit.value, unit));
        });
        return returned;
    },

    //renders the change to an element

    render: function(element, property, value, unit) {
        element.setStyle(property, this.serve(value, unit));
    },

    //searches inside the page css to find the values for a selector

    search: function(selector) {
        if (Fx.CSS.Cache[selector]) return Fx.CSS.Cache[selector];
        var to = {};
        Array.each(document.styleSheets, function(sheet, j) {
            var href = sheet.href;
            if (href && href.contains('://') && !href.contains(document.domain)) return;
            var rules = sheet.rules || sheet.cssRules;
            Array.each(rules, function(rule, i) {
                if (!rule.style) return;
                var selectorText = (rule.selectorText) ? rule.selectorText.replace(/^\w+/, function(m) {
                    return m.toLowerCase();
                }) : null;
                if (!selectorText || !selectorText.test('^' + selector + '$')) return;
                Element.Styles.each(function(value, style) {
                    if (!rule.style[style] || Element.ShortStyles[style]) return;
                    value = String(rule.style[style]);
                    to[style] = (value.test(/^rgb/)) ? value.rgbToHex() : value;
                });
            });
        });
        return Fx.CSS.Cache[selector] = to;
    }

});

Fx.CSS.Cache = {};

Fx.CSS.Parsers = new Hash({

    Color: {
        parse: function(value) {
            if (value.match(/^#[0-9a-f]{3,6}$/i)) return value.hexToRgb(true);
            return ((value = value.match(/(\d+),\s*(\d+),\s*(\d+)/))) ? [value[1], value[2], value[3]] : false;
        },
        compute: function(from, to, delta) {
            return from.map(function(value, i) {
                return Math.round(Fx.compute(from[i], to[i], delta));
            });
        },
        serve: function(value) {
            return value.map(Number);
        }
    },

    Number: {
        parse: parseFloat,
        compute: Fx.compute,
        serve: function(value, unit) {
            return (unit) ? value + unit : value;
        }
    },

    String: {
        parse: $lambda(false),
        compute: $arguments(1),
        serve: $arguments(0)
    }

});
/*
---

script: Fx.Tween.js

description: Formerly Fx.Style, effect to transition any CSS property for an element.

license: MIT-style license.

requires: 
- /Fx.CSS

provides: [Fx.Tween, Element.fade, Element.highlight]

...
*/

Fx.Tween = new Class({

    Extends: Fx.CSS,

    initialize: function(element, options) {
        this.element = this.subject = document.id(element);
        this.parent(options);
    },

    set: function(property, now) {
        if (arguments.length == 1) {
            now = property;
            property = this.property || this.options.property;
        }
        this.render(this.element, property, now, this.options.unit);
        return this;
    },

    start: function(property, from, to) {
        if (!this.check(property, from, to)) return this;
        var args = Array.flatten(arguments);
        this.property = this.options.property || args.shift();
        var parsed = this.prepare(this.element, this.property, args);
        return this.parent(parsed.from, parsed.to);
    }

});

Element.Properties.tween = {

    set: function(options) {
        var tween = this.retrieve('tween');
        if (tween) tween.cancel();
        return this.eliminate('tween').store('tween:options', $extend({ link: 'cancel' }, options));
    },

    get: function(options) {
        if (options || !this.retrieve('tween')) {
            if (options || !this.retrieve('tween:options')) this.set('tween', options);
            this.store('tween', new Fx.Tween(this, this.retrieve('tween:options')));
        }
        return this.retrieve('tween');
    }

};

Element.implement({

    tween: function(property, from, to) {
        this.get('tween').start(arguments);
        return this;
    },

    fade: function(how) {
        var fade = this.get('tween'), o = 'opacity', toggle;
        how = $pick(how, 'toggle');
        switch (how) {
            case 'in': fade.start(o, 1); break;
            case 'out': fade.start(o, 0); break;
            case 'show': fade.set(o, 1); break;
            case 'hide': fade.set(o, 0); break;
            case 'toggle':
                var flag = this.retrieve('fade:flag', this.get('opacity') == 1);
                fade.start(o, (flag) ? 0 : 1);
                this.store('fade:flag', !flag);
                toggle = true;
                break;
            default: fade.start(o, arguments);
        }
        if (!toggle) this.eliminate('fade:flag');
        return this;
    },

    highlight: function(start, end) {
        if (!end) {
            end = this.retrieve('highlight:original', this.getStyle('background-color'));
            end = (end == 'transparent') ? '#fff' : end;
        }
        var tween = this.get('tween');
        tween.start('background-color', start || '#ffff88', end).chain(function() {
            this.setStyle('background-color', this.retrieve('highlight:original'));
            tween.callChain();
        } .bind(this));
        return this;
    }

});
/*
---

script: Fx.Morph.js

description: Formerly Fx.Styles, effect to transition any number of CSS properties for an element using an object of rules, or CSS based selector rules.

license: MIT-style license.

requires:
- /Fx.CSS

provides: [Fx.Morph]

...
*/

Fx.Morph = new Class({

    Extends: Fx.CSS,

    initialize: function(element, options) {
        this.element = this.subject = document.id(element);
        this.parent(options);
    },

    set: function(now) {
        if (typeof now == 'string') now = this.search(now);
        for (var p in now) this.render(this.element, p, now[p], this.options.unit);
        return this;
    },

    compute: function(from, to, delta) {
        var now = {};
        for (var p in from) now[p] = this.parent(from[p], to[p], delta);
        return now;
    },

    start: function(properties) {
        if (!this.check(properties)) return this;
        if (typeof properties == 'string') properties = this.search(properties);
        var from = {}, to = {};
        for (var p in properties) {
            var parsed = this.prepare(this.element, p, properties[p]);
            from[p] = parsed.from;
            to[p] = parsed.to;
        }
        return this.parent(from, to);
    }

});

Element.Properties.morph = {

    set: function(options) {
        var morph = this.retrieve('morph');
        if (morph) morph.cancel();
        return this.eliminate('morph').store('morph:options', $extend({ link: 'cancel' }, options));
    },

    get: function(options) {
        if (options || !this.retrieve('morph')) {
            if (options || !this.retrieve('morph:options')) this.set('morph', options);
            this.store('morph', new Fx.Morph(this, this.retrieve('morph:options')));
        }
        return this.retrieve('morph');
    }

};

Element.implement({

    morph: function(props) {
        this.get('morph').start(props);
        return this;
    }

});
/*
---

script: Fx.Transitions.js

description: Contains a set of advanced transitions to be used with any of the Fx Classes.

license: MIT-style license.

credits:
- Easing Equations by Robert Penner, <http://www.robertpenner.com/easing/>, modified and optimized to be used with MooTools.

requires:
- /Fx

provides: [Fx.Transitions]

...
*/

Fx.implement({

    getTransition: function() {
        var trans = this.options.transition || Fx.Transitions.Sine.easeInOut;
        if (typeof trans == 'string') {
            var data = trans.split(':');
            trans = Fx.Transitions;
            trans = trans[data[0]] || trans[data[0].capitalize()];
            if (data[1]) trans = trans['ease' + data[1].capitalize() + (data[2] ? data[2].capitalize() : '')];
        }
        return trans;
    }

});

Fx.Transition = function(transition, params) {
    params = $splat(params);
    return $extend(transition, {
        easeIn: function(pos) {
            return transition(pos, params);
        },
        easeOut: function(pos) {
            return 1 - transition(1 - pos, params);
        },
        easeInOut: function(pos) {
            return (pos <= 0.5) ? transition(2 * pos, params) / 2 : (2 - transition(2 * (1 - pos), params)) / 2;
        }
    });
};

Fx.Transitions = new Hash({

    linear: $arguments(0)

});

Fx.Transitions.extend = function(transitions) {
    for (var transition in transitions) Fx.Transitions[transition] = new Fx.Transition(transitions[transition]);
};

Fx.Transitions.extend({

    Pow: function(p, x) {
        return Math.pow(p, x[0] || 6);
    },

    Expo: function(p) {
        return Math.pow(2, 8 * (p - 1));
    },

    Circ: function(p) {
        return 1 - Math.sin(Math.acos(p));
    },

    Sine: function(p) {
        return 1 - Math.sin((1 - p) * Math.PI / 2);
    },

    Back: function(p, x) {
        x = x[0] || 1.618;
        return Math.pow(p, 2) * ((x + 1) * p - x);
    },

    Bounce: function(p) {
        var value;
        for (var a = 0, b = 1; 1; a += b, b /= 2) {
            if (p >= (7 - 4 * a) / 11) {
                value = b * b - Math.pow((11 - 6 * a - 11 * p) / 4, 2);
                break;
            }
        }
        return value;
    },

    Elastic: function(p, x) {
        return Math.pow(2, 10 * --p) * Math.cos(20 * p * Math.PI * (x[0] || 1) / 3);
    }

});

['Quad', 'Cubic', 'Quart', 'Quint'].each(function(transition, i) {
    Fx.Transitions[transition] = new Fx.Transition(function(p) {
        return Math.pow(p, [i + 2]);
    });
});
/*
---

script: Request.js

description: Powerful all purpose Request Class. Uses XMLHTTPRequest.

license: MIT-style license.

requires:
- /Element
- /Chain
- /Events
- /Options
- /Browser

provides: [Request]

...
*/

var Request = new Class({

    Implements: [Chain, Events, Options],

    options: {/*
		onRequest: $empty,
		onComplete: $empty,
		onCancel: $empty,
		onSuccess: $empty,
		onFailure: $empty,
		onException: $empty,*/
        url: '',
        data: '',
        headers: {
            'X-Requested-With': 'XMLHttpRequest',
            'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
        },
        async: true,
        format: false,
        method: 'post',
        link: 'ignore',
        isSuccess: null,
        emulation: true,
        urlEncoded: true,
        encoding: 'utf-8',
        evalScripts: false,
        evalResponse: false,
        noCache: false
    },

    initialize: function(options) {
        this.xhr = new Browser.Request();
        this.setOptions(options);
        this.options.isSuccess = this.options.isSuccess || this.isSuccess;
        this.headers = new Hash(this.options.headers);
    },

    onStateChange: function() {
        if (this.xhr.readyState != 4 || !this.running) return;
        this.running = false;
        this.status = 0;
        $try(function() {
            this.status = this.xhr.status;
        } .bind(this));
        this.xhr.onreadystatechange = $empty;
        if (this.options.isSuccess.call(this, this.status)) {
            this.response = { text: this.xhr.responseText, xml: this.xhr.responseXML };
            this.success(this.response.text, this.response.xml);
        } else {
            this.response = { text: null, xml: null };
            this.failure();
        }
    },

    isSuccess: function() {
        return ((this.status >= 200) && (this.status < 300));
    },

    processScripts: function(text) {
        if (this.options.evalResponse || (/(ecma|java)script/).test(this.getHeader('Content-type'))) return $exec(text);
        return text.stripScripts(this.options.evalScripts);
    },

    success: function(text, xml) {
        this.onSuccess(this.processScripts(text), xml);
    },

    onSuccess: function() {
        this.fireEvent('complete', arguments).fireEvent('success', arguments).callChain();
    },

    failure: function() {
        this.onFailure();
    },

    onFailure: function() {
        this.fireEvent('complete').fireEvent('failure', this.xhr);
    },

    setHeader: function(name, value) {
        this.headers.set(name, value);
        return this;
    },

    getHeader: function(name) {
        return $try(function() {
            return this.xhr.getResponseHeader(name);
        } .bind(this));
    },

    check: function() {
        if (!this.running) return true;
        switch (this.options.link) {
            case 'cancel': this.cancel(); return true;
            case 'chain': this.chain(this.caller.bind(this, arguments)); return false;
        }
        return false;
    },

    send: function(options) {
        if (!this.check(options)) return this;
        this.running = true;

        var type = $type(options);
        if (type == 'string' || type == 'element') options = { data: options };

        var old = this.options;
        options = $extend({ data: old.data, url: old.url, method: old.method }, options);
        var data = options.data, url = String(options.url), method = options.method.toLowerCase();

        switch ($type(data)) {
            case 'element': data = document.id(data).toQueryString(); break;
            case 'object': case 'hash': data = Hash.toQueryString(data);
        }

        if (this.options.format) {
            var format = 'format=' + this.options.format;
            data = (data) ? format + '&' + data : format;
        }

        if (this.options.emulation && !['get', 'post'].contains(method)) {
            var _method = '_method=' + method;
            data = (data) ? _method + '&' + data : _method;
            method = 'post';
        }

        if (this.options.urlEncoded && method == 'post') {
            var encoding = (this.options.encoding) ? '; charset=' + this.options.encoding : '';
            this.headers.set('Content-type', 'application/x-www-form-urlencoded' + encoding);
        }

        if (this.options.noCache) {
            var noCache = 'noCache=' + new Date().getTime();
            data = (data) ? noCache + '&' + data : noCache;
        }

        var trimPosition = url.lastIndexOf('/');
        if (trimPosition > -1 && (trimPosition = url.indexOf('#')) > -1) url = url.substr(0, trimPosition);

        if (data && method == 'get') {
            url = url + (url.contains('?') ? '&' : '?') + data;
            data = null;
        }

        this.xhr.open(method.toUpperCase(), url, this.options.async);

        this.xhr.onreadystatechange = this.onStateChange.bind(this);

        this.headers.each(function(value, key) {
            try {
                this.xhr.setRequestHeader(key, value);
            } catch (e) {
                this.fireEvent('exception', [key, value]);
            }
        }, this);

        this.fireEvent('request');
        this.xhr.send(data);
        if (!this.options.async) this.onStateChange();
        return this;
    },

    cancel: function() {
        if (!this.running) return this;
        this.running = false;
        this.xhr.abort();
        this.xhr.onreadystatechange = $empty;
        this.xhr = new Browser.Request();
        this.fireEvent('cancel');
        return this;
    }

});

(function() {

    var methods = {};
    ['get', 'post', 'put', 'delete', 'GET', 'POST', 'PUT', 'DELETE'].each(function(method) {
        methods[method] = function() {
            var params = Array.link(arguments, { url: String.type, data: $defined });
            return this.send($extend(params, { method: method }));
        };
    });

    Request.implement(methods);

})();

Element.Properties.send = {

    set: function(options) {
        var send = this.retrieve('send');
        if (send) send.cancel();
        return this.eliminate('send').store('send:options', $extend({
            data: this, link: 'cancel', method: this.get('method') || 'post', url: this.get('action')
        }, options));
    },

    get: function(options) {
        if (options || !this.retrieve('send')) {
            if (options || !this.retrieve('send:options')) this.set('send', options);
            this.store('send', new Request(this.retrieve('send:options')));
        }
        return this.retrieve('send');
    }

};

Element.implement({

    send: function(url) {
        var sender = this.get('send');
        sender.send({ data: this, url: url || sender.options.url });
        return this;
    }

});
/*
---

script: Request.HTML.js

description: Extends the basic Request Class with additional methods for interacting with HTML responses.

license: MIT-style license.

requires:
- /Request
- /Element

provides: [Request.HTML]

...
*/

Request.HTML = new Class({

    Extends: Request,

    options: {
        update: false,
        append: false,
        evalScripts: true,
        filter: false
    },

    processHTML: function(text) {
        var match = text.match(/<body[^>]*>([\s\S]*?)<\/body>/i);
        text = (match) ? match[1] : text;

        var container = new Element('div');

        return $try(function() {
            var root = '<root>' + text + '</root>', doc;
            if (Browser.Engine.trident) {
                doc = new ActiveXObject('Microsoft.XMLDOM');
                doc.async = false;
                doc.loadXML(root);
            } else {
                doc = new DOMParser().parseFromString(root, 'text/xml');
            }
            root = doc.getElementsByTagName('root')[0];
            if (!root) return null;
            for (var i = 0, k = root.childNodes.length; i < k; i++) {
                var child = Element.clone(root.childNodes[i], true, true);
                if (child) container.grab(child);
            }
            return container;
        }) || container.set('html', text);
    },

    success: function(text) {
        var options = this.options, response = this.response;

        response.html = text.stripScripts(function(script) {
            response.javascript = script;
        });

        var temp = this.processHTML(response.html);

        response.tree = temp.childNodes;
        response.elements = temp.getElements('*');

        if (options.filter) response.tree = response.elements.filter(options.filter);
        if (options.update) document.id(options.update).empty().set('html', response.html);
        else if (options.append) document.id(options.append).adopt(temp.getChildren());
        if (options.evalScripts) $exec(response.javascript);

        this.onSuccess(response.tree, response.elements, response.html, response.javascript);
    }

});

Element.Properties.load = {

    set: function(options) {
        var load = this.retrieve('load');
        if (load) load.cancel();
        return this.eliminate('load').store('load:options', $extend({ data: this, link: 'cancel', update: this, method: 'get' }, options));
    },

    get: function(options) {
        if (options || !this.retrieve('load')) {
            if (options || !this.retrieve('load:options')) this.set('load', options);
            this.store('load', new Request.HTML(this.retrieve('load:options')));
        }
        return this.retrieve('load');
    }

};

Element.implement({

    load: function() {
        this.get('load').send(Array.link(arguments, { data: Object.type, url: String.type }));
        return this;
    }

});
/*
---

script: Request.JSON.js

description: Extends the basic Request Class with additional methods for sending and receiving JSON data.

license: MIT-style license.

requires:
- /Request JSON

provides: [Request.HTML]

...
*/

Request.JSON = new Class({

    Extends: Request,

    options: {
        secure: true
    },

    initialize: function(options) {
        this.parent(options);
        this.headers.extend({ 'Accept': 'application/json', 'X-Request': 'JSON' });
    },

    success: function(text) {
        this.response.json = JSON.decode(text, this.options.secure);
        this.onSuccess(this.response.json, text);
    }

});
/*
---

script: More.js

description: MooTools More

license: MIT-style license

authors:
- Guillermo Rauch
- Thomas Aylott
- Scott Kyle

requires:
- core:1.2.4/MooTools

provides: [MooTools.More]

...
*/

MooTools.More = {
    'version': '1.2.4.2',
    'build': 'bd5a93c0913cce25917c48cbdacde568e15e02ef'
}; /*
---

script: Fx.Elements.js

description: Effect to change any number of CSS properties of any number of Elements.

license: MIT-style license

authors:
- Valerio Proietti

requires:
- core:1.2.4/Fx.CSS
- /MooTools.More

provides: [Fx.Elements]

...
*/

Fx.Elements = new Class({

    Extends: Fx.CSS,

    initialize: function(elements, options) {
        this.elements = this.subject = $$(elements);
        this.parent(options);
    },

    compute: function(from, to, delta) {
        var now = {};
        for (var i in from) {
            var iFrom = from[i], iTo = to[i], iNow = now[i] = {};
            for (var p in iFrom) iNow[p] = this.parent(iFrom[p], iTo[p], delta);
        }
        return now;
    },

    set: function(now) {
        for (var i in now) {
            var iNow = now[i];
            for (var p in iNow) this.render(this.elements[i], p, iNow[p], this.options.unit);
        }
        return this;
    },

    start: function(obj) {
        if (!this.check(obj)) return this;
        var from = {}, to = {};
        for (var i in obj) {
            var iProps = obj[i], iFrom = from[i] = {}, iTo = to[i] = {};
            for (var p in iProps) {
                var parsed = this.prepare(this.elements[i], p, iProps[p]);
                iFrom[p] = parsed.from;
                iTo[p] = parsed.to;
            }
        }
        return this.parent(from, to);
    }

}); /*
---

script: Fx.Accordion.js

description: An Fx.Elements extension which allows you to easily create accordion type controls.

license: MIT-style license

authors:
- Valerio Proietti

requires:
- core:1.2.4/Element.Event
- /Fx.Elements

provides: [Fx.Accordion]

...
*/

var Accordion = Fx.Accordion = new Class({

    Extends: Fx.Elements,

    options: {/*
		onActive: $empty(toggler, section),
		onBackground: $empty(toggler, section),
		fixedHeight: false,
		fixedWidth: false,
		*/
        display: 0,
        show: false,
        height: true,
        width: false,
        opacity: true,
        alwaysHide: false,
        trigger: 'click',
        initialDisplayFx: true,
        returnHeightToAuto: true
    },

    initialize: function() {
        var params = Array.link(arguments, { 'container': Element.type, 'options': Object.type, 'togglers': $defined, 'elements': $defined });
        this.parent(params.elements, params.options);
        this.togglers = $$(params.togglers);
        this.container = document.id(params.container);
        this.previous = -1;
        this.internalChain = new Chain();
        if (this.options.alwaysHide) this.options.wait = true;
        if ($chk(this.options.show)) {
            this.options.display = false;
            this.previous = this.options.show;
        }
        if (this.options.start) {
            this.options.display = false;
            this.options.show = false;
        }
        this.effects = {};
        if (this.options.opacity) this.effects.opacity = 'fullOpacity';
        if (this.options.width) this.effects.width = this.options.fixedWidth ? 'fullWidth' : 'offsetWidth';
        if (this.options.height) this.effects.height = this.options.fixedHeight ? 'fullHeight' : 'scrollHeight';
        for (var i = 0, l = this.togglers.length; i < l; i++) this.addSection(this.togglers[i], this.elements[i]);
        this.elements.each(function(el, i) {
            if (this.options.show === i) {
                this.fireEvent('active', [this.togglers[i], el]);
            } else {
                for (var fx in this.effects) el.setStyle(fx, 0);
            }
        }, this);
        if ($chk(this.options.display)) this.display(this.options.display, this.options.initialDisplayFx);
        this.addEvent('complete', this.internalChain.callChain.bind(this.internalChain));
    },

    addSection: function(toggler, element) {
        toggler = document.id(toggler);
        element = document.id(element);
        var test = this.togglers.contains(toggler);
        this.togglers.include(toggler);
        this.elements.include(element);
        var idx = this.togglers.indexOf(toggler);
        var displayer = this.display.bind(this, idx);
        toggler.store('accordion:display', displayer);
        toggler.addEvent(this.options.trigger, displayer);
        if (this.options.height) element.setStyles({ 'padding-top': 0, 'border-top': 'none', 'padding-bottom': 0, 'border-bottom': 'none' });
        if (this.options.width) element.setStyles({ 'padding-left': 0, 'border-left': 'none', 'padding-right': 0, 'border-right': 'none' });
        element.fullOpacity = 1;
        if (this.options.fixedWidth) element.fullWidth = this.options.fixedWidth;
        if (this.options.fixedHeight) element.fullHeight = this.options.fixedHeight;
        element.setStyle('overflow', 'hidden');
        if (!test) {
            for (var fx in this.effects) element.setStyle(fx, 0);
        }
        return this;
    },

    detach: function() {
        this.togglers.each(function(toggler) {
            toggler.removeEvent(this.options.trigger, toggler.retrieve('accordion:display'));
        }, this);
    },

    display: function(index, useFx) {
        if (!this.check(index, useFx)) return this;
        useFx = $pick(useFx, true);
        if (this.options.returnHeightToAuto) {
            var prev = this.elements[this.previous];
            if (prev && !this.selfHidden) {
                for (var fx in this.effects) {
                    prev.setStyle(fx, prev[this.effects[fx]]);
                }
            }
        }
        index = ($type(index) == 'element') ? this.elements.indexOf(index) : index;
        if ((this.timer && this.options.wait) || (index === this.previous && !this.options.alwaysHide)) return this;
        this.previous = index;
        var obj = {};
        this.elements.each(function(el, i) {
            obj[i] = {};
            var hide;
            if (i != index) {
                hide = true;
            } else if (this.options.alwaysHide && ((el.offsetHeight > 0 && this.options.height) || el.offsetWidth > 0 && this.options.width)) {
                hide = true;
                this.selfHidden = true;
            }
            this.fireEvent(hide ? 'background' : 'active', [this.togglers[i], el]);
            for (var fx in this.effects) obj[i][fx] = hide ? 0 : el[this.effects[fx]];
        }, this);
        this.internalChain.chain(function() {
            if (this.options.returnHeightToAuto && !this.selfHidden) {
                var el = this.elements[index];
                if (el) el.setStyle('height', 'auto');
            };
        } .bind(this));
        return useFx ? this.start(obj) : this.set(obj);
    }

}); /*
---

script: Fx.Scroll.js

description: Effect to smoothly scroll any element, including the window.

license: MIT-style license

authors:
- Valerio Proietti

requires:
- core:1.2.4/Fx
- core:1.2.4/Element.Event
- core:1.2.4/Element.Dimensions
- /MooTools.More

provides: [Fx.Scroll]

...
*/

Fx.Scroll = new Class({

    Extends: Fx,

    options: {
        offset: { x: 0, y: 0 },
        wheelStops: true
    },

    initialize: function(element, options) {
        this.element = this.subject = document.id(element);
        this.parent(options);
        var cancel = this.cancel.bind(this, false);

        if ($type(this.element) != 'element') this.element = document.id(this.element.getDocument().body);

        var stopper = this.element;

        if (this.options.wheelStops) {
            this.addEvent('start', function() {
                stopper.addEvent('mousewheel', cancel);
            }, true);
            this.addEvent('complete', function() {
                stopper.removeEvent('mousewheel', cancel);
            }, true);
        }
    },

    set: function() {
        var now = Array.flatten(arguments);
        if (Browser.Engine.gecko) now = [Math.round(now[0]), Math.round(now[1])];
        this.element.scrollTo(now[0], now[1]);
    },

    compute: function(from, to, delta) {
        return [0, 1].map(function(i) {
            return Fx.compute(from[i], to[i], delta);
        });
    },

    start: function(x, y) {
        if (!this.check(x, y)) return this;
        var scrollSize = this.element.getScrollSize(),
			scroll = this.element.getScroll(),
			values = { x: x, y: y };
        for (var z in values) {
            var max = scrollSize[z];
            if ($chk(values[z])) values[z] = ($type(values[z]) == 'number') ? values[z] : max;
            else values[z] = scroll[z];
            values[z] += this.options.offset[z];
        }
        return this.parent([scroll.x, scroll.y], [values.x, values.y]);
    },

    toTop: function() {
        return this.start(false, 0);
    },

    toLeft: function() {
        return this.start(0, false);
    },

    toRight: function() {
        return this.start('right', false);
    },

    toBottom: function() {
        return this.start(false, 'bottom');
    },

    toElement: function(el) {
        var position = document.id(el).getPosition(this.element);
        return this.start(position.x, position.y);
    },

    scrollIntoView: function(el, axes, offset) {
        axes = axes ? $splat(axes) : ['x', 'y'];
        var to = {};
        el = document.id(el);
        var pos = el.getPosition(this.element);
        var size = el.getSize();
        var scroll = this.element.getScroll();
        var containerSize = this.element.getSize();
        var edge = {
            x: pos.x + size.x,
            y: pos.y + size.y
        };
        ['x', 'y'].each(function(axis) {
            if (axes.contains(axis)) {
                if (edge[axis] > scroll[axis] + containerSize[axis]) to[axis] = edge[axis] - containerSize[axis];
                if (pos[axis] < scroll[axis]) to[axis] = pos[axis];
            }
            if (to[axis] == null) to[axis] = scroll[axis];
            if (offset && offset[axis]) to[axis] = to[axis] + offset[axis];
        }, this);
        if (to.x != scroll.x || to.y != scroll.y) this.start(to.x, to.y);
        return this;
    },

    scrollToCenter: function(el, axes, offset) {
        axes = axes ? $splat(axes) : ['x', 'y'];
        el = $(el);
        var to = {},
			pos = el.getPosition(this.element),
			size = el.getSize(),
			scroll = this.element.getScroll(),
			containerSize = this.element.getSize(),
			edge = {
			    x: pos.x + size.x,
			    y: pos.y + size.y
			};

        ['x', 'y'].each(function(axis) {
            if (axes.contains(axis)) {
                to[axis] = pos[axis] - (containerSize[axis] - size[axis]) / 2;
            }
            if (to[axis] == null) to[axis] = scroll[axis];
            if (offset && offset[axis]) to[axis] = to[axis] + offset[axis];
        }, this);
        if (to.x != scroll.x || to.y != scroll.y) this.start(to.x, to.y);
        return this;
    }

});
/*
---

script: Fx.Slide.js

description: Effect to slide an element in and out of view.

license: MIT-style license

authors:
- Valerio Proietti

requires:
- core:1.2.4/Fx Element.Style
- /MooTools.More

provides: [Fx.Slide]

...
*/

Fx.Slide = new Class({

    Extends: Fx,

    options: {
        mode: 'vertical',
        hideOverflow: true
    },

    initialize: function(element, options) {
        this.addEvent('complete', function() {
            this.open = (this.wrapper['offset' + this.layout.capitalize()] != 0);
            if (this.open && Browser.Engine.webkit419) this.element.dispose().inject(this.wrapper);
        }, true);
        this.element = this.subject = document.id(element);
        this.parent(options);
        var wrapper = this.element.retrieve('wrapper');
        var styles = this.element.getStyles('margin', 'position', 'overflow');
        if (this.options.hideOverflow) styles = $extend(styles, { overflow: 'hidden' });
        this.wrapper = wrapper || new Element('div', {
            styles: styles
        }).wraps(this.element);
        this.element.store('wrapper', this.wrapper).setStyle('margin', 0);
        this.now = [];
        this.open = true;
    },

    vertical: function() {
        this.margin = 'margin-top';
        this.layout = 'height';
        this.offset = this.element.offsetHeight;
    },

    horizontal: function() {
        this.margin = 'margin-left';
        this.layout = 'width';
        this.offset = this.element.offsetWidth;
    },

    set: function(now) {
        this.element.setStyle(this.margin, now[0]);
        this.wrapper.setStyle(this.layout, now[1]);
        return this;
    },

    compute: function(from, to, delta) {
        return [0, 1].map(function(i) {
            return Fx.compute(from[i], to[i], delta);
        });
    },

    start: function(how, mode) {
        if (!this.check(how, mode)) return this;
        this[mode || this.options.mode]();
        var margin = this.element.getStyle(this.margin).toInt();
        var layout = this.wrapper.getStyle(this.layout).toInt();
        var caseIn = [[margin, layout], [0, this.offset]];
        var caseOut = [[margin, layout], [-this.offset, 0]];
        var start;
        switch (how) {
            case 'in': start = caseIn; break;
            case 'out': start = caseOut; break;
            case 'toggle': start = (layout == 0) ? caseIn : caseOut;
        }
        return this.parent(start[0], start[1]);
    },

    slideIn: function(mode) {
        return this.start('in', mode);
    },

    slideOut: function(mode) {
        return this.start('out', mode);
    },

    hide: function(mode) {
        this[mode || this.options.mode]();
        this.open = false;
        return this.set([-this.offset, 0]);
    },

    show: function(mode) {
        this[mode || this.options.mode]();
        this.open = true;
        return this.set([0, this.offset]);
    },

    toggle: function(mode) {
        return this.start('toggle', mode);
    }

});

Element.Properties.slide = {

    set: function(options) {
        var slide = this.retrieve('slide');
        if (slide) slide.cancel();
        return this.eliminate('slide').store('slide:options', $extend({ link: 'cancel' }, options));
    },

    get: function(options) {
        if (options || !this.retrieve('slide')) {
            if (options || !this.retrieve('slide:options')) this.set('slide', options);
            this.store('slide', new Fx.Slide(this, this.retrieve('slide:options')));
        }
        return this.retrieve('slide');
    }

};

Element.implement({

    slide: function(how, mode) {
        how = how || 'toggle';
        var slide = this.get('slide'), toggle;
        switch (how) {
            case 'hide': slide.hide(mode); break;
            case 'show': slide.show(mode); break;
            case 'toggle':
                var flag = this.retrieve('slide:flag', slide.open);
                slide[flag ? 'slideOut' : 'slideIn'](mode);
                this.store('slide:flag', !flag);
                toggle = true;
                break;
            default: slide.start(how, mode);
        }
        if (!toggle) this.eliminate('slide:flag');
        return this;
    }

});
/*
---

script: Fx.SmoothScroll.js

description: Class for creating a smooth scrolling effect to all internal links on the page.

license: MIT-style license

authors:
- Valerio Proietti

requires:
- core:1.2.4/Selectors
- /Fx.Scroll

provides: [Fx.SmoothScroll]

...
*/

var SmoothScroll = Fx.SmoothScroll = new Class({

    Extends: Fx.Scroll,

    initialize: function(options, context) {
        context = context || document;
        this.doc = context.getDocument();
        var win = context.getWindow();
        this.parent(this.doc, options);
        this.links = $$(this.options.links || this.doc.links);
        var location = win.location.href.match(/^[^#]*/)[0] + '#';
        this.links.each(function(link) {
            if (link.href.indexOf(location) != 0) { return; }
            var anchor = link.href.substr(location.length);
            if (anchor) this.useLink(link, anchor);
        }, this);
        if (!Browser.Engine.webkit419) {
            this.addEvent('complete', function() {
                win.location.hash = this.anchor;
            }, true);
        }
    },

    useLink: function(link, anchor) {
        var el;
        link.addEvent('click', function(event) {
            if (el !== false && !el) el = document.id(anchor) || this.doc.getElement('a[name=' + anchor + ']');
            if (el) {
                event.preventDefault();
                this.anchor = anchor;
                this.toElement(el).chain(function() {
                    this.fireEvent('scrolledTo', [link, el]);
                } .bind(this));
                link.blur();
            }
        } .bind(this));
    }
}); /*
---

script: Drag.js

description: The base Drag Class. Can be used to drag and resize Elements using mouse events.

license: MIT-style license

authors:
- Valerio Proietti
- Tom Occhinno
- Jan Kassens

requires:
- core:1.2.4/Events
- core:1.2.4/Options
- core:1.2.4/Element.Event
- core:1.2.4/Element.Style
- /MooTools.More

provides: [Drag]

*/

var Drag = new Class({

    Implements: [Events, Options],

    options: {/*
		onBeforeStart: $empty(thisElement),
		onStart: $empty(thisElement, event),
		onSnap: $empty(thisElement)
		onDrag: $empty(thisElement, event),
		onCancel: $empty(thisElement),
		onComplete: $empty(thisElement, event),*/
        snap: 6,
        unit: 'px',
        grid: false,
        style: true,
        limit: false,
        handle: false,
        invert: false,
        preventDefault: false,
        stopPropagation: false,
        modifiers: { x: 'left', y: 'top' }
    },

    initialize: function() {
        var params = Array.link(arguments, { 'options': Object.type, 'element': $defined });
        this.element = document.id(params.element);
        this.document = this.element.getDocument();
        this.setOptions(params.options || {});
        var htype = $type(this.options.handle);
        this.handles = ((htype == 'array' || htype == 'collection') ? $$(this.options.handle) : document.id(this.options.handle)) || this.element;
        this.mouse = { 'now': {}, 'pos': {} };
        this.value = { 'start': {}, 'now': {} };

        this.selection = (Browser.Engine.trident) ? 'selectstart' : 'mousedown';

        this.bound = {
            start: this.start.bind(this),
            check: this.check.bind(this),
            drag: this.drag.bind(this),
            stop: this.stop.bind(this),
            cancel: this.cancel.bind(this),
            eventStop: $lambda(false)
        };
        this.attach();
    },

    attach: function() {
        this.handles.addEvent('mousedown', this.bound.start);
        return this;
    },

    detach: function() {
        this.handles.removeEvent('mousedown', this.bound.start);
        return this;
    },

    start: function(event) {
        if (event.rightClick) return;
        if (this.options.preventDefault) event.preventDefault();
        if (this.options.stopPropagation) event.stopPropagation();
        this.mouse.start = event.page;
        this.fireEvent('beforeStart', this.element);
        var limit = this.options.limit;
        this.limit = { x: [], y: [] };
        for (var z in this.options.modifiers) {
            if (!this.options.modifiers[z]) continue;
            if (this.options.style) this.value.now[z] = this.element.getStyle(this.options.modifiers[z]).toInt();
            else this.value.now[z] = this.element[this.options.modifiers[z]];
            if (this.options.invert) this.value.now[z] *= -1;
            this.mouse.pos[z] = event.page[z] - this.value.now[z];
            if (limit && limit[z]) {
                for (var i = 2; i--; i) {
                    if ($chk(limit[z][i])) this.limit[z][i] = $lambda(limit[z][i])();
                }
            }
        }
        if ($type(this.options.grid) == 'number') this.options.grid = { x: this.options.grid, y: this.options.grid };
        this.document.addEvents({ mousemove: this.bound.check, mouseup: this.bound.cancel });
        this.document.addEvent(this.selection, this.bound.eventStop);
    },

    check: function(event) {
        if (this.options.preventDefault) event.preventDefault();
        var distance = Math.round(Math.sqrt(Math.pow(event.page.x - this.mouse.start.x, 2) + Math.pow(event.page.y - this.mouse.start.y, 2)));
        if (distance > this.options.snap) {
            this.cancel();
            this.document.addEvents({
                mousemove: this.bound.drag,
                mouseup: this.bound.stop
            });
            this.fireEvent('start', [this.element, event]).fireEvent('snap', this.element);
        }
    },

    drag: function(event) {
        if (this.options.preventDefault) event.preventDefault();
        this.mouse.now = event.page;
        for (var z in this.options.modifiers) {
            if (!this.options.modifiers[z]) continue;
            this.value.now[z] = this.mouse.now[z] - this.mouse.pos[z];
            if (this.options.invert) this.value.now[z] *= -1;
            if (this.options.limit && this.limit[z]) {
                if ($chk(this.limit[z][1]) && (this.value.now[z] > this.limit[z][1])) {
                    this.value.now[z] = this.limit[z][1];
                } else if ($chk(this.limit[z][0]) && (this.value.now[z] < this.limit[z][0])) {
                    this.value.now[z] = this.limit[z][0];
                }
            }
            if (this.options.grid[z]) this.value.now[z] -= ((this.value.now[z] - (this.limit[z][0] || 0)) % this.options.grid[z]);
            if (this.options.style) {
                this.element.setStyle(this.options.modifiers[z], this.value.now[z] + this.options.unit);
            } else {
                this.element[this.options.modifiers[z]] = this.value.now[z];
            }
        }
        this.fireEvent('drag', [this.element, event]);
    },

    cancel: function(event) {
        this.document.removeEvent('mousemove', this.bound.check);
        this.document.removeEvent('mouseup', this.bound.cancel);
        if (event) {
            this.document.removeEvent(this.selection, this.bound.eventStop);
            this.fireEvent('cancel', this.element);
        }
    },

    stop: function(event) {
        this.document.removeEvent(this.selection, this.bound.eventStop);
        this.document.removeEvent('mousemove', this.bound.drag);
        this.document.removeEvent('mouseup', this.bound.stop);
        if (event) this.fireEvent('complete', [this.element, event]);
    }

});

Element.implement({

    makeResizable: function(options) {
        var drag = new Drag(this, $merge({ modifiers: { x: 'width', y: 'height'} }, options));
        this.store('resizer', drag);
        return drag.addEvent('drag', function() {
            this.fireEvent('resize', drag);
        } .bind(this));
    }

});
/*
---

script: Drag.Move.js

description: A Drag extension that provides support for the constraining of draggables to containers and droppables.

license: MIT-style license

authors:
- Valerio Proietti
- Tom Occhinno
- Jan Kassens
- Aaron Newton
- Scott Kyle

requires:
- core:1.2.4/Element.Dimensions
- /Drag

provides: [Drag.Move]

...
*/

Drag.Move = new Class({

    Extends: Drag,

    options: {/*
		onEnter: $empty(thisElement, overed),
		onLeave: $empty(thisElement, overed),
		onDrop: $empty(thisElement, overed, event),*/
        droppables: [],
        container: false,
        precalculate: false,
        includeMargins: true,
        checkDroppables: true
    },

    initialize: function(element, options) {
        this.parent(element, options);
        element = this.element;

        this.droppables = $$(this.options.droppables);
        this.container = document.id(this.options.container);

        if (this.container && $type(this.container) != 'element')
            this.container = document.id(this.container.getDocument().body);

        var styles = element.getStyles('left', 'right', 'position');
        if (styles.left == 'auto' || styles.top == 'auto')
            element.setPosition(element.getPosition(element.getOffsetParent()));

        if (styles.position == 'static')
            element.setStyle('position', 'absolute');

        this.addEvent('start', this.checkDroppables, true);

        this.overed = null;
    },

    start: function(event) {
        if (this.container) this.options.limit = this.calculateLimit();

        if (this.options.precalculate) {
            this.positions = this.droppables.map(function(el) {
                return el.getCoordinates();
            });
        }

        this.parent(event);
    },

    calculateLimit: function() {
        var offsetParent = this.element.getOffsetParent(),
			containerCoordinates = this.container.getCoordinates(offsetParent),
			containerBorder = {},
			elementMargin = {},
			elementBorder = {},
			containerMargin = {},
			offsetParentPadding = {};

        ['top', 'right', 'bottom', 'left'].each(function(pad) {
            containerBorder[pad] = this.container.getStyle('border-' + pad).toInt();
            elementBorder[pad] = this.element.getStyle('border-' + pad).toInt();
            elementMargin[pad] = this.element.getStyle('margin-' + pad).toInt();
            containerMargin[pad] = this.container.getStyle('margin-' + pad).toInt();
            offsetParentPadding[pad] = offsetParent.getStyle('padding-' + pad).toInt();
        }, this);

        var width = this.element.offsetWidth + elementMargin.left + elementMargin.right,
			height = this.element.offsetHeight + elementMargin.top + elementMargin.bottom,
			left = 0,
			top = 0,
			right = containerCoordinates.right - containerBorder.right - width,
			bottom = containerCoordinates.bottom - containerBorder.bottom - height;

        if (this.options.includeMargins) {
            left += elementMargin.left;
            top += elementMargin.top;
        } else {
            right += elementMargin.right;
            bottom += elementMargin.bottom;
        }

        if (this.element.getStyle('position') == 'relative') {
            var coords = this.element.getCoordinates(offsetParent);
            coords.left -= this.element.getStyle('left').toInt();
            coords.top -= this.element.getStyle('top').toInt();

            left += containerBorder.left - coords.left;
            top += containerBorder.top - coords.top;
            right += elementMargin.left - coords.left;
            bottom += elementMargin.top - coords.top;

            if (this.container != offsetParent) {
                left += containerMargin.left + offsetParentPadding.left;
                top += (Browser.Engine.trident4 ? 0 : containerMargin.top) + offsetParentPadding.top;
            }
        } else {
            left -= elementMargin.left;
            top -= elementMargin.top;

            if (this.container == offsetParent) {
                right -= containerBorder.left;
                bottom -= containerBorder.top;
            } else {
                left += containerCoordinates.left + containerBorder.left;
                top += containerCoordinates.top + containerBorder.top;
            }
        }

        return {
            x: [left, right],
            y: [top, bottom]
        };
    },

    checkAgainst: function(el, i) {
        el = (this.positions) ? this.positions[i] : el.getCoordinates();
        var now = this.mouse.now;
        return (now.x > el.left && now.x < el.right && now.y < el.bottom && now.y > el.top);
    },

    checkDroppables: function() {
        var overed = this.droppables.filter(this.checkAgainst, this).getLast();
        if (this.overed != overed) {
            if (this.overed) this.fireEvent('leave', [this.element, this.overed]);
            if (overed) this.fireEvent('enter', [this.element, overed]);
            this.overed = overed;
        }
    },

    drag: function(event) {
        this.parent(event);
        if (this.options.checkDroppables && this.droppables.length) this.checkDroppables();
    },

    stop: function(event) {
        this.checkDroppables();
        this.fireEvent('drop', [this.element, this.overed, event]);
        this.overed = null;
        return this.parent(event);
    }

});

Element.implement({

    makeDraggable: function(options) {
        var drag = new Drag.Move(this, options);
        this.store('dragger', drag);
        return drag;
    }

});
/*
---

script: Class.Binds.js

description: Automagically binds specified methods in a class to the instance of the class.

license: MIT-style license

authors:
- Aaron Newton

requires:
- core:1.2.4/Class
- /MooTools.More

provides: [Class.Binds]

...
*/

Class.Mutators.Binds = function(binds) {
    return binds;
};

Class.Mutators.initialize = function(initialize) {
    return function() {
        $splat(this.Binds).each(function(name) {
            var original = this[name];
            if (original) this[name] = original.bind(this);
        }, this);
        return initialize.apply(this, arguments);
    };
};
/*
---

script: Element.Measure.js

description: Extends the Element native object to include methods useful in measuring dimensions.

credits: "Element.measure / .expose methods by Daniel Steigerwald License: MIT-style license. Copyright: Copyright (c) 2008 Daniel Steigerwald, daniel.steigerwald.cz"

license: MIT-style license

authors:
- Aaron Newton

requires:
- core:1.2.4/Element.Style
- core:1.2.4/Element.Dimensions
- /MooTools.More

provides: [Element.Measure]

...
*/

Element.implement({

    measure: function(fn) {
        var vis = function(el) {
            return !!(!el || el.offsetHeight || el.offsetWidth);
        };
        if (vis(this)) return fn.apply(this);
        var parent = this.getParent(),
			restorers = [],
			toMeasure = [];
        while (!vis(parent) && parent != document.body) {
            toMeasure.push(parent.expose());
            parent = parent.getParent();
        }
        var restore = this.expose();
        var result = fn.apply(this);
        restore();
        toMeasure.each(function(restore) {
            restore();
        });
        return result;
    },

    expose: function() {
        if (this.getStyle('display') != 'none') return $empty;
        var before = this.style.cssText;
        this.setStyles({
            display: 'block',
            position: 'absolute',
            visibility: 'hidden'
        });
        return function() {
            this.style.cssText = before;
        } .bind(this);
    },

    getDimensions: function(options) {
        options = $merge({ computeSize: false }, options);
        var dim = {};
        var getSize = function(el, options) {
            return (options.computeSize) ? el.getComputedSize(options) : el.getSize();
        };
        var parent = this.getParent('body');
        if (parent && this.getStyle('display') == 'none') {
            dim = this.measure(function() {
                return getSize(this, options);
            });
        } else if (parent) {
            try { //safari sometimes crashes here, so catch it
                dim = getSize(this, options);
            } catch (e) { }
        } else {
            dim = { x: 0, y: 0 };
        }
        return $chk(dim.x) ? $extend(dim, { width: dim.x, height: dim.y }) : $extend(dim, { x: dim.width, y: dim.height });
    },

    getComputedSize: function(options) {
        options = $merge({
            styles: ['padding', 'border'],
            plains: {
                height: ['top', 'bottom'],
                width: ['left', 'right']
            },
            mode: 'both'
        }, options);
        var size = { width: 0, height: 0 };
        switch (options.mode) {
            case 'vertical':
                delete size.width;
                delete options.plains.width;
                break;
            case 'horizontal':
                delete size.height;
                delete options.plains.height;
                break;
        }
        var getStyles = [];
        //this function might be useful in other places; perhaps it should be outside this function?
        $each(options.plains, function(plain, key) {
            plain.each(function(edge) {
                options.styles.each(function(style) {
                    getStyles.push((style == 'border') ? style + '-' + edge + '-' + 'width' : style + '-' + edge);
                });
            });
        });
        var styles = {};
        getStyles.each(function(style) { styles[style] = this.getComputedStyle(style); }, this);
        var subtracted = [];
        $each(options.plains, function(plain, key) { //keys: width, height, plains: ['left', 'right'], ['top','bottom']
            var capitalized = key.capitalize();
            size['total' + capitalized] = size['computed' + capitalized] = 0;
            plain.each(function(edge) { //top, left, right, bottom
                size['computed' + edge.capitalize()] = 0;
                getStyles.each(function(style, i) { //padding, border, etc.
                    //'padding-left'.test('left') size['totalWidth'] = size['width'] + [padding-left]
                    if (style.test(edge)) {
                        styles[style] = styles[style].toInt() || 0; //styles['padding-left'] = 5;
                        size['total' + capitalized] = size['total' + capitalized] + styles[style];
                        size['computed' + edge.capitalize()] = size['computed' + edge.capitalize()] + styles[style];
                    }
                    //if width != width (so, padding-left, for instance), then subtract that from the total
                    if (style.test(edge) && key != style &&
						(style.test('border') || style.test('padding')) && !subtracted.contains(style)) {
                        subtracted.push(style);
                        size['computed' + capitalized] = size['computed' + capitalized] - styles[style];
                    }
                });
            });
        });

        ['Width', 'Height'].each(function(value) {
            var lower = value.toLowerCase();
            if (!$chk(size[lower])) return;

            size[lower] = size[lower] + this['offset' + value] + size['computed' + value];
            size['total' + value] = size[lower] + size['total' + value];
            delete size['computed' + value];
        }, this);

        return $extend(styles, size);
    }

}); /*
---

script: Slider.js

description: Class for creating horizontal and vertical slider controls.

license: MIT-style license

authors:
- Valerio Proietti

requires:
- core:1.2.4/Element.Dimensions
- /Class.Binds
- /Drag
- /Element.Dimensions
- /Element.Measure

provides: [Slider]

...
*/

var Slider = new Class({

    Implements: [Events, Options],

    Binds: ['clickedElement', 'draggedKnob', 'scrolledElement'],

    options: {/*
		onTick: $empty(intPosition),
		onChange: $empty(intStep),
		onComplete: $empty(strStep),*/
        onTick: function(position) {
            if (this.options.snap) position = this.toPosition(this.step);
            this.knob.setStyle(this.property, position);
        },
        initialStep: 0,
        snap: false,
        offset: 0,
        range: false,
        wheel: false,
        steps: 100,
        mode: 'horizontal'
    },

    initialize: function(element, knob, options) {
        this.setOptions(options);
        this.element = document.id(element);
        this.knob = document.id(knob);
        this.previousChange = this.previousEnd = this.step = -1;
        var offset, limit = {}, modifiers = { 'x': false, 'y': false };
        switch (this.options.mode) {
            case 'vertical':
                this.axis = 'y';
                this.property = 'top';
                offset = 'offsetHeight';
                break;
            case 'horizontal':
                this.axis = 'x';
                this.property = 'left';
                offset = 'offsetWidth';
        }

        this.full = this.element.measure(function() {
            this.half = this.knob[offset] / 2;
            return this.element[offset] - this.knob[offset] + (this.options.offset * 2);
        } .bind(this));

        this.min = $chk(this.options.range[0]) ? this.options.range[0] : 0;
        this.max = $chk(this.options.range[1]) ? this.options.range[1] : this.options.steps;
        this.range = this.max - this.min;
        this.steps = this.options.steps || this.full;
        this.stepSize = Math.abs(this.range) / this.steps;
        this.stepWidth = this.stepSize * this.full / Math.abs(this.range);

        this.knob.setStyle('position', 'relative').setStyle(this.property, this.options.initialStep ? this.toPosition(this.options.initialStep) : -this.options.offset);
        modifiers[this.axis] = this.property;
        limit[this.axis] = [-this.options.offset, this.full - this.options.offset];

        var dragOptions = {
            snap: 0,
            limit: limit,
            modifiers: modifiers,
            onDrag: this.draggedKnob,
            onStart: this.draggedKnob,
            onBeforeStart: (function() {
                this.isDragging = true;
            }).bind(this),
            onCancel: function() {
                this.isDragging = false;
            } .bind(this),
            onComplete: function() {
                this.isDragging = false;
                this.draggedKnob();
                this.end();
            } .bind(this)
        };
        if (this.options.snap) {
            dragOptions.grid = Math.ceil(this.stepWidth);
            dragOptions.limit[this.axis][1] = this.full;
        }

        this.drag = new Drag(this.knob, dragOptions);
        this.attach();
    },

    attach: function() {
        this.element.addEvent('mousedown', this.clickedElement);
        if (this.options.wheel) this.element.addEvent('mousewheel', this.scrolledElement);
        this.drag.attach();
        return this;
    },

    detach: function() {
        this.element.removeEvent('mousedown', this.clickedElement);
        this.element.removeEvent('mousewheel', this.scrolledElement);
        this.drag.detach();
        return this;
    },

    set: function(step) {
        if (!((this.range > 0) ^ (step < this.min))) step = this.min;
        if (!((this.range > 0) ^ (step > this.max))) step = this.max;

        this.step = Math.round(step);
        this.checkStep();
        this.fireEvent('tick', this.toPosition(this.step));
        this.end();
        return this;
    },

    clickedElement: function(event) {
        if (this.isDragging || event.target == this.knob) return;

        var dir = this.range < 0 ? -1 : 1;
        var position = event.page[this.axis] - this.element.getPosition()[this.axis] - this.half;
        position = position.limit(-this.options.offset, this.full - this.options.offset);

        this.step = Math.round(this.min + dir * this.toStep(position));
        this.checkStep();
        this.fireEvent('tick', position);
        this.end();
    },

    scrolledElement: function(event) {
        var mode = (this.options.mode == 'horizontal') ? (event.wheel < 0) : (event.wheel > 0);
        this.set(mode ? this.step - this.stepSize : this.step + this.stepSize);
        event.stop();
    },

    draggedKnob: function() {
        var dir = this.range < 0 ? -1 : 1;
        var position = this.drag.value.now[this.axis];
        position = position.limit(-this.options.offset, this.full - this.options.offset);
        this.step = Math.round(this.min + dir * this.toStep(position));
        this.checkStep();
    },

    checkStep: function() {
        if (this.previousChange != this.step) {
            this.previousChange = this.step;
            this.fireEvent('change', this.step);
        }
    },

    end: function() {
        if (this.previousEnd !== this.step) {
            this.previousEnd = this.step;
            this.fireEvent('complete', this.step + '');
        }
    },

    toStep: function(position) {
        var step = (position + this.options.offset) * this.stepSize / this.full * this.steps;
        return this.options.steps ? Math.round(step -= step % this.stepSize) : step;
    },

    toPosition: function(step) {
        return (this.full * Math.abs(this.min - step)) / (this.steps * this.stepSize) - this.options.offset;
    }

}); /*
---

script: Sortables.js

description: Class for creating a drag and drop sorting interface for lists of items.

license: MIT-style license

authors:
- Tom Occhino

requires:
- /Drag.Move

provides: [Slider]

...
*/

var Sortables = new Class({

    Implements: [Events, Options],

    options: {/*
		onSort: $empty(element, clone),
		onStart: $empty(element, clone),
		onComplete: $empty(element),*/
        snap: 4,
        opacity: 1,
        clone: false,
        revert: false,
        handle: false,
        constrain: false
    },

    initialize: function(lists, options) {
        this.setOptions(options);
        this.elements = [];
        this.lists = [];
        this.idle = true;

        this.addLists($$(document.id(lists) || lists));
        if (!this.options.clone) this.options.revert = false;
        if (this.options.revert) this.effect = new Fx.Morph(null, $merge({ duration: 250, link: 'cancel' }, this.options.revert));
    },

    attach: function() {
        this.addLists(this.lists);
        return this;
    },

    detach: function() {
        this.lists = this.removeLists(this.lists);
        return this;
    },

    addItems: function() {
        Array.flatten(arguments).each(function(element) {
            this.elements.push(element);
            var start = element.retrieve('sortables:start', this.start.bindWithEvent(this, element));
            (this.options.handle ? element.getElement(this.options.handle) || element : element).addEvent('mousedown', start);
        }, this);
        return this;
    },

    addLists: function() {
        Array.flatten(arguments).each(function(list) {
            this.lists.push(list);
            this.addItems(list.getChildren());
        }, this);
        return this;
    },

    removeItems: function() {
        return $$(Array.flatten(arguments).map(function(element) {
            this.elements.erase(element);
            var start = element.retrieve('sortables:start');
            (this.options.handle ? element.getElement(this.options.handle) || element : element).removeEvent('mousedown', start);

            return element;
        }, this));
    },

    removeLists: function() {
        return $$(Array.flatten(arguments).map(function(list) {
            this.lists.erase(list);
            this.removeItems(list.getChildren());

            return list;
        }, this));
    },

    getClone: function(event, element) {
        if (!this.options.clone) return new Element('div').inject(document.body);
        if ($type(this.options.clone) == 'function') return this.options.clone.call(this, event, element, this.list);
        return element.clone(true).setStyles({
            margin: '0px',
            position: 'absolute',
            visibility: 'hidden',
            'width': element.getStyle('width')
        }).inject(this.list).setPosition(element.getPosition(element.getOffsetParent()));
    },

    getDroppables: function() {
        var droppables = this.list.getChildren();
        if (!this.options.constrain) droppables = this.lists.concat(droppables).erase(this.list);
        return droppables.erase(this.clone).erase(this.element);
    },

    insert: function(dragging, element) {
        var where = 'inside';
        if (this.lists.contains(element)) {
            this.list = element;
            this.drag.droppables = this.getDroppables();
        } else {
            where = this.element.getAllPrevious().contains(element) ? 'before' : 'after';
        }
        this.element.inject(element, where);
        this.fireEvent('sort', [this.element, this.clone]);
    },

    start: function(event, element) {
        if (!this.idle) return;
        this.idle = false;
        this.element = element;
        this.opacity = element.get('opacity');
        this.list = element.getParent();
        this.clone = this.getClone(event, element);

        this.drag = new Drag.Move(this.clone, {
            snap: this.options.snap,
            container: this.options.constrain && this.element.getParent(),
            droppables: this.getDroppables(),
            onSnap: function() {
                event.stop();
                this.clone.setStyle('visibility', 'visible');
                this.element.set('opacity', this.options.opacity || 0);
                this.fireEvent('start', [this.element, this.clone]);
            } .bind(this),
            onEnter: this.insert.bind(this),
            onCancel: this.reset.bind(this),
            onComplete: this.end.bind(this)
        });

        this.clone.inject(this.element, 'before');
        this.drag.start(event);
    },

    end: function() {
        this.drag.detach();
        this.element.set('opacity', this.opacity);
        if (this.effect) {
            var dim = this.element.getStyles('width', 'height');
            var pos = this.clone.computePosition(this.element.getPosition(this.clone.offsetParent));
            this.effect.element = this.clone;
            this.effect.start({
                top: pos.top,
                left: pos.left,
                width: dim.width,
                height: dim.height,
                opacity: 0.25
            }).chain(this.reset.bind(this));
        } else {
            this.reset();
        }
    },

    reset: function() {
        this.idle = true;
        this.clone.destroy();
        this.fireEvent('complete', this.element);
    },

    serialize: function() {
        var params = Array.link(arguments, { modifier: Function.type, index: $defined });
        var serial = this.lists.map(function(list) {
            return list.getChildren().map(params.modifier || function(element) {
                return element.get('id');
            }, this);
        }, this);

        var index = params.index;
        if (this.lists.length == 1) index = 0;
        return $chk(index) && index >= 0 && index < this.lists.length ? serial[index] : serial;
    }

});
/*
---

script: Color.js

description: Class for creating and manipulating colors in JavaScript. Supports HSB -> RGB Conversions and vice versa.

license: MIT-style license

authors:
- Valerio Proietti

requires:
- core:1.2.4/Array
- core:1.2.4/String
- core:1.2.4/Number
- core:1.2.4/Hash
- core:1.2.4/Function
- core:1.2.4/$util

provides: [Color]

...
*/

var Color = new Native({

    initialize: function(color, type) {
        if (arguments.length >= 3) {
            type = 'rgb'; color = Array.slice(arguments, 0, 3);
        } else if (typeof color == 'string') {
            if (color.match(/rgb/)) color = color.rgbToHex().hexToRgb(true);
            else if (color.match(/hsb/)) color = color.hsbToRgb();
            else color = color.hexToRgb(true);
        }
        type = type || 'rgb';
        switch (type) {
            case 'hsb':
                var old = color;
                color = color.hsbToRgb();
                color.hsb = old;
                break;
            case 'hex': color = color.hexToRgb(true); break;
        }
        color.rgb = color.slice(0, 3);
        color.hsb = color.hsb || color.rgbToHsb();
        color.hex = color.rgbToHex();
        return $extend(color, this);
    }

});

Color.implement({

    mix: function() {
        var colors = Array.slice(arguments);
        var alpha = ($type(colors.getLast()) == 'number') ? colors.pop() : 50;
        var rgb = this.slice();
        colors.each(function(color) {
            color = new Color(color);
            for (var i = 0; i < 3; i++) rgb[i] = Math.round((rgb[i] / 100 * (100 - alpha)) + (color[i] / 100 * alpha));
        });
        return new Color(rgb, 'rgb');
    },

    invert: function() {
        return new Color(this.map(function(value) {
            return 255 - value;
        }));
    },

    setHue: function(value) {
        return new Color([value, this.hsb[1], this.hsb[2]], 'hsb');
    },

    setSaturation: function(percent) {
        return new Color([this.hsb[0], percent, this.hsb[2]], 'hsb');
    },

    setBrightness: function(percent) {
        return new Color([this.hsb[0], this.hsb[1], percent], 'hsb');
    }

});

var $RGB = function(r, g, b) {
    return new Color([r, g, b], 'rgb');
};

var $HSB = function(h, s, b) {
    return new Color([h, s, b], 'hsb');
};

var $HEX = function(hex) {
    return new Color(hex, 'hex');
};

Array.implement({

    rgbToHsb: function() {
        var red = this[0],
				green = this[1],
				blue = this[2],
				hue = 0;
        var max = Math.max(red, green, blue),
				min = Math.min(red, green, blue);
        var delta = max - min;
        var brightness = max / 255,
				saturation = (max != 0) ? delta / max : 0;
        if (saturation != 0) {
            var rr = (max - red) / delta;
            var gr = (max - green) / delta;
            var br = (max - blue) / delta;
            if (red == max) hue = br - gr;
            else if (green == max) hue = 2 + rr - br;
            else hue = 4 + gr - rr;
            hue /= 6;
            if (hue < 0) hue++;
        }
        return [Math.round(hue * 360), Math.round(saturation * 100), Math.round(brightness * 100)];
    },

    hsbToRgb: function() {
        var br = Math.round(this[2] / 100 * 255);
        if (this[1] == 0) {
            return [br, br, br];
        } else {
            var hue = this[0] % 360;
            var f = hue % 60;
            var p = Math.round((this[2] * (100 - this[1])) / 10000 * 255);
            var q = Math.round((this[2] * (6000 - this[1] * f)) / 600000 * 255);
            var t = Math.round((this[2] * (6000 - this[1] * (60 - f))) / 600000 * 255);
            switch (Math.floor(hue / 60)) {
                case 0: return [br, t, p];
                case 1: return [q, br, p];
                case 2: return [p, br, t];
                case 3: return [p, q, br];
                case 4: return [t, p, br];
                case 5: return [br, p, q];
            }
        }
        return false;
    }

});

String.implement({

    rgbToHsb: function() {
        var rgb = this.match(/\d{1,3}/g);
        return (rgb) ? rgb.rgbToHsb() : null;
    },

    hsbToRgb: function() {
        var hsb = this.match(/\d{1,3}/g);
        return (hsb) ? hsb.hsbToRgb() : null;
    }

});
/*
---

script: Group.js

description: Class for monitoring collections of events

license: MIT-style license

authors:
- Valerio Proietti

requires:
- core:1.2.4/Events
- /MooTools.More

provides: [Group]

...
*/

var Group = new Class({

    initialize: function() {
        this.instances = Array.flatten(arguments);
        this.events = {};
        this.checker = {};
    },

    addEvent: function(type, fn) {
        this.checker[type] = this.checker[type] || {};
        this.events[type] = this.events[type] || [];
        if (this.events[type].contains(fn)) return false;
        else this.events[type].push(fn);
        this.instances.each(function(instance, i) {
            instance.addEvent(type, this.check.bind(this, [type, instance, i]));
        }, this);
        return this;
    },

    check: function(type, instance, i) {
        this.checker[type][i] = true;
        var every = this.instances.every(function(current, j) {
            return this.checker[type][j] || false;
        }, this);
        if (!every) return;
        this.checker[type] = {};
        this.events[type].each(function(event) {
            event.call(this, this.instances, instance);
        }, this);
    }

});
/*
---

script: Hash.Cookie.js

description: Class for creating, reading, and deleting Cookies in JSON format.

license: MIT-style license

authors:
- Valerio Proietti
- Aaron Newton

requires:
- core:1.2.4/Cookie
- core:1.2.4/JSON
- /MooTools.More

provides: [Hash.Cookie]

...
*/

Hash.Cookie = new Class({

    Extends: Cookie,

    options: {
        autoSave: true
    },

    initialize: function(name, options) {
        this.parent(name, options);
        this.load();
    },

    save: function() {
        var value = JSON.encode(this.hash);
        if (!value || value.length > 4096) return false; //cookie would be truncated!
        if (value == '{}') this.dispose();
        else this.write(value);
        return true;
    },

    load: function() {
        this.hash = new Hash(JSON.decode(this.read(), true));
        return this;
    }

});

Hash.each(Hash.prototype, function(method, name) {
    if (typeof method == 'function') Hash.Cookie.implement(name, function() {
        var value = method.apply(this.hash, arguments);
        if (this.options.autoSave) this.save();
        return value;
    });
}); /*
---

script: Scroller.js

description: Class which scrolls the contents of any Element (including the window) when the mouse reaches the Element's boundaries.

license: MIT-style license

authors:
- Valerio Proietti

requires:
- core:1.2.4/Events
- core:1.2.4/Options
- core:1.2.4/Element.Event
- core:1.2.4/Element.Dimensions

provides: [Scroller]

...
*/

var Scroller = new Class({

    Implements: [Events, Options],

    options: {
        area: 20,
        velocity: 1,
        onChange: function(x, y) {
            this.element.scrollTo(x, y);
        },
        fps: 50
    },

    initialize: function(element, options) {
        this.setOptions(options);
        this.element = document.id(element);
        this.listener = ($type(this.element) != 'element') ? document.id(this.element.getDocument().body) : this.element;
        this.timer = null;
        this.bound = {
            attach: this.attach.bind(this),
            detach: this.detach.bind(this),
            getCoords: this.getCoords.bind(this)
        };
    },

    start: function() {
        this.listener.addEvents({
            mouseover: this.bound.attach,
            mouseout: this.bound.detach
        });
    },

    stop: function() {
        this.listener.removeEvents({
            mouseover: this.bound.attach,
            mouseout: this.bound.detach
        });
        this.detach();
        this.timer = $clear(this.timer);
    },

    attach: function() {
        this.listener.addEvent('mousemove', this.bound.getCoords);
    },

    detach: function() {
        this.listener.removeEvent('mousemove', this.bound.getCoords);
        this.timer = $clear(this.timer);
    },

    getCoords: function(event) {
        this.page = (this.listener.get('tag') == 'body') ? event.client : event.page;
        if (!this.timer) this.timer = this.scroll.periodical(Math.round(1000 / this.options.fps), this);
    },

    scroll: function() {
        var size = this.element.getSize(),
			scroll = this.element.getScroll(),
			pos = this.element.getOffsets(),
			scrollSize = this.element.getScrollSize(),
			change = { x: 0, y: 0 };
        for (var z in this.page) {
            if (this.page[z] < (this.options.area + pos[z]) && scroll[z] != 0)
                change[z] = (this.page[z] - this.options.area - pos[z]) * this.options.velocity;
            else if (this.page[z] + this.options.area > (size[z] + pos[z]) && scroll[z] + size[z] != scrollSize[z])
                change[z] = (this.page[z] - size[z] + this.options.area - pos[z]) * this.options.velocity;
        }
        if (change.y || change.x) this.fireEvent('change', [scroll.x + change.x, scroll.y + change.y]);
    }

}); /*
---

script: Tips.js

description: Class for creating nice tips that follow the mouse cursor when hovering an element.

license: MIT-style license

authors:
- Valerio Proietti
- Christoph Pojer

requires:
- core:1.2.4/Options
- core:1.2.4/Events
- core:1.2.4/Element.Event
- core:1.2.4/Element.Style
- core:1.2.4/Element.Dimensions
- /MooTools.More

provides: [Tips]

...
*/

(function() {

    var read = function(option, element) {
        return (option) ? ($type(option) == 'function' ? option(element) : element.get(option)) : '';
    };

    this.Tips = new Class({

        Implements: [Events, Options],

        options: {
            /*
            onAttach: $empty(element),
            onDetach: $empty(element),
            */
            onShow: function() {
                this.tip.setStyle('display', 'block');
            },
            onHide: function() {
                this.tip.setStyle('display', 'none');
            },
            title: 'title',
            text: function(element) {
                return element.get('rel') || element.get('href');
            },
            showDelay: 100,
            hideDelay: 100,
            className: 'tip-wrap',
            offset: { x: 16, y: 16 },
            fixed: false
        },

        initialize: function() {
            var params = Array.link(arguments, { options: Object.type, elements: $defined });
            this.setOptions(params.options);
            document.id(this);

            if (params.elements) this.attach(params.elements);
        },

        toElement: function() {
            if (this.tip) return this.tip;

            this.container = new Element('div', { 'class': 'tip' });
            return this.tip = new Element('div', {
                'class': this.options.className,
                styles: {
                    position: 'absolute',
                    top: 0,
                    left: 0
                }
            }).adopt(
			new Element('div', { 'class': 'tip-top' }),
			this.container,
			new Element('div', { 'class': 'tip-bottom' })
		).inject(document.body);
        },

        attach: function(elements) {
            $$(elements).each(function(element) {
                var title = read(this.options.title, element),
				text = read(this.options.text, element);

                element.erase('title').store('tip:native', title).retrieve('tip:title', title);
                element.retrieve('tip:text', text);
                this.fireEvent('attach', [element]);

                var events = ['enter', 'leave'];
                if (!this.options.fixed) events.push('move');

                events.each(function(value) {
                    var event = element.retrieve('tip:' + value);
                    if (!event) event = this['element' + value.capitalize()].bindWithEvent(this, element);

                    element.store('tip:' + value, event).addEvent('mouse' + value, event);
                }, this);
            }, this);

            return this;
        },

        detach: function(elements) {
            $$(elements).each(function(element) {
                ['enter', 'leave', 'move'].each(function(value) {
                    element.removeEvent('mouse' + value, element.retrieve('tip:' + value)).eliminate('tip:' + value);
                });

                this.fireEvent('detach', [element]);

                if (this.options.title == 'title') { // This is necessary to check if we can revert the title
                    var original = element.retrieve('tip:native');
                    if (original) element.set('title', original);
                }
            }, this);

            return this;
        },

        elementEnter: function(event, element) {
            this.container.empty();

            ['title', 'text'].each(function(value) {
                var content = element.retrieve('tip:' + value);
                if (content) this.fill(new Element('div', { 'class': 'tip-' + value }).inject(this.container), content);
            }, this);

            $clear(this.timer);
            this.timer = this.show.delay(this.options.showDelay, this, element);
            this.position((this.options.fixed) ? { page: element.getPosition()} : event);
        },

        elementLeave: function(event, element) {
            $clear(this.timer);
            this.timer = this.hide.delay(this.options.hideDelay, this, element);
            this.fireForParent(event, element);
        },

        fireForParent: function(event, element) {
            if (!element) return;
            parentNode = element.getParent();
            if (parentNode == document.body) return;
            if (parentNode.retrieve('tip:enter')) parentNode.fireEvent('mouseenter', event);
            else this.fireForParent(parentNode, event);
        },

        elementMove: function(event, element) {
            this.position(event);
        },

        position: function(event) {
            var size = window.getSize(), scroll = window.getScroll(),
			tip = { x: this.tip.offsetWidth, y: this.tip.offsetHeight },
			props = { x: 'left', y: 'top' },
			obj = {};

            for (var z in props) {
                obj[props[z]] = event.page[z] + this.options.offset[z];
                if ((obj[props[z]] + tip[z] - scroll[z]) > size[z]) obj[props[z]] = event.page[z] - this.options.offset[z] - tip[z];
            }

            this.tip.setStyles(obj);
        },

        fill: function(element, contents) {
            if (typeof contents == 'string') element.set('html', contents);
            else element.adopt(contents);
        },

        show: function(element) {
            this.fireEvent('show', [this.tip, element]);
        },

        hide: function(element) {
            this.fireEvent('hide', [this.tip, element]);
        }

    });

})();


/*
---

script: Assets.js

description: Provides methods to dynamically load JavaScript, CSS, and Image files into the document.

license: MIT-style license

authors:
- Valerio Proietti

requires:
- core:1.2.4/Element.Event
- /MooTools.More

provides: [Assets]

...
*/

var Asset = {

    javascript: function(source, properties) {
        properties = $extend({
            onload: $empty,
            document: document,
            check: $lambda(true)
        }, properties);

        var script = new Element('script', { src: source, type: 'text/javascript' });

        var load = properties.onload.bind(script),
			check = properties.check,
			doc = properties.document;
        delete properties.onload;
        delete properties.check;
        delete properties.document;

        script.addEvents({
            load: load,
            readystatechange: function() {
                if (['loaded', 'complete'].contains(this.readyState)) load();
            }
        }).set(properties);

        if (Browser.Engine.webkit419) var checker = (function() {
            if (!$try(check)) return;
            $clear(checker);
            load();
        }).periodical(50);

        return script.inject(doc.head);
    },

    css: function(source, properties) {
        return new Element('link', $merge({
            rel: 'stylesheet',
            media: 'screen',
            type: 'text/css',
            href: source
        }, properties)).inject(document.head);
    },

    image: function(source, properties) {
        properties = $merge({
            onload: $empty,
            onabort: $empty,
            onerror: $empty
        }, properties);
        var image = new Image();
        var element = document.id(image) || new Element('img');
        ['load', 'abort', 'error'].each(function(name) {
            var type = 'on' + name;
            var event = properties[type];
            delete properties[type];
            image[type] = function() {
                if (!image) return;
                if (!element.parentNode) {
                    element.width = image.width;
                    element.height = image.height;
                }
                image = image.onload = image.onabort = image.onerror = null;
                event.delay(1, element, element);
                element.fireEvent(name, element, 1);
            };
        });
        image.src = element.src = source;
        if (image && image.complete) image.onload.delay(1);
        return element.set(properties);
    },

    images: function(sources, options) {
        options = $merge({
            onComplete: $empty,
            onProgress: $empty,
            onError: $empty,
            properties: {}
        }, options);
        sources = $splat(sources);
        var images = [];
        var counter = 0;
        return new Elements(sources.map(function(source) {
            return Asset.image(source, $extend(options.properties, {
                onload: function() {
                    options.onProgress.call(this, counter, sources.indexOf(source));
                    counter++;
                    if (counter == sources.length) options.onComplete();
                },
                onerror: function() {
                    options.onError.call(this, counter, sources.indexOf(source));
                    counter++;
                    if (counter == sources.length) options.onComplete();
                }
            }));
        }));
    }

};



/* Upgrade helper */

if (!window.console) var console = {};
if (!console.log) console.log = function() { };
if (!console.warn) console.warn = console.log;
if (!console.error) console.error = console.warn;

MooTools.upgradeLog = function() {
    if (console[this.upgradeLogLevel]) console[this.upgradeLogLevel].apply(console, arguments);
};

(function() {
    oldA = $A;
    window.$A = function(iterable, start, length) {
        if (start != undefined && length != undefined) {
            MooTools.upgradeLog('1.1 > 1.2: $A no longer takes start and length arguments.');
            if (Browser.Engine.trident && $type(iterable) == 'collection') {
                start = start || 0;
                if (start < 0) start = iterable.length + start;
                length = length || (iterable.length - start);
                var array = [];
                for (var i = 0; i < length; i++) array[i] = iterable[start++];
                return array;
            }
            start = (start || 0) + ((start < 0) ? iterable.length : 0);
            var end = ((!$chk(length)) ? iterable.length : length) + start;
            return Array.prototype.slice.call(iterable, start, end);
        }
        return oldA(iterable);
    };


    var strs = ['Array', 'Function', 'String', 'RegExp', 'Number', 'Window', 'Document', 'Element', 'Elements'];
    for (var i = 0, l = strs.length; i < l; i++) {
        var type = strs[i];
        var natv = window[type];
        if (natv) {
            var extend = natv.extend;
            natv.extend = function(props) {
                MooTools.upgradeLog('1.1 > 1.2: native types no longer use .extend to add methods to prototypes but instead use .implement. NOTE: YOUR METHODS WERE NOT IMPLEMENTED ON THE NATIVE ' + type.toUpperCase() + ' PROTOTYPE.');
                return extend.apply(this, arguments);
            };
        }
    }
})();

window.onDomReady = Window.onDomReady = function(fn) {
    MooTools.upgradeLog('1.1 > 1.2: window.onDomReady is no longer supported. Use window.addEvent("domready") instead');
    return window.addEvent('domready', fn);
}; if (Browser.__defineGetter__) {
    Browser.__defineGetter__('hasGetter', function() {
        return true;
    });
}

if (Browser.hasGetter) { // webkit, gecko, opera support

    window.__defineGetter__('ie', function() {
        MooTools.upgradeLog('1.1 > 1.2: window.ie is deprecated. Use Browser.Engine.trident');
        return (Browser.Engine.name == 'trident') ? true : false;
    });
    window.__defineGetter__('ie6', function() {
        MooTools.upgradeLog('1.1 > 1.2: window.ie6 is deprecated. Use Browser.Engine.trident and Browser.Engine.version');
        return (Browser.Engine.name == 'trident' && Browser.Engine.version == 4) ? true : false;
    });
    window.__defineGetter__('ie7', function() {
        MooTools.upgradeLog('1.1 > 1.2: window.ie7 is deprecated. Use Browser.Engine.trident and Browser.Engine.version');
        return (Browser.Engine.name == 'trident' && Browser.Engine.version == 5) ? true : false;
    });
    window.__defineGetter__('gecko', function() {
        MooTools.upgradeLog('1.1 > 1.2: window.gecko is deprecated. Use Browser.Engine.gecko');
        return (Browser.Engine.name == 'gecko') ? true : false;
    });
    window.__defineGetter__('webkit', function() {
        MooTools.upgradeLog('1.1 > 1.2: window.webkit is deprecated. Use Browser.Engine.webkit');
        return (Browser.Engine.name == 'webkit') ? true : false;
    });
    window.__defineGetter__('webkit419', function() {
        MooTools.upgradeLog('1.1 > 1.2: window.webkit is deprecated. Use Browser.Engine.webkit and Browser.Engine.version');
        return (Browser.Engine.name == 'webkit' && Browser.Engine.version == 419) ? true : false;
    });
    window.__defineGetter__('webkit420', function() {
        MooTools.upgradeLog('1.1 > 1.2: window.webkit is deprecated. Use Browser.Engine.webkit and Browser.Engine.version');
        return (Browser.Engine.name == 'webkit' && Browser.Engine.version == 420) ? true : false;
    });
    window.__defineGetter__('opera', function() {
        MooTools.upgradeLog('1.1 > 1.2: window.opera is deprecated. Use Browser.Engine.presto');
        return (Browser.Engine.name == 'presto') ? true : false;
    });
} else {
    window[Browser.Engine.name] = window[Browser.Engine.name + Browser.Engine.version] = true;
    window.ie = window.trident;
    window.ie6 = window.trident4;
    window.ie7 = window.trident5;
}
Array.implement({

    copy: function(start, length) {
        MooTools.upgradeLog('1.1 > 1.2: Array.copy is deprecated. Use Array.splice');
        return $A(this, start, length);
    },

    remove: function(item) {
        MooTools.upgradeLog('1.1 > 1.2: Array.remove is deprecated. Use Array.erase');
        return this.erase(item);
    },

    merge: function(array) {
        MooTools.upgradeLog('1.1 > 1.2: Array.merge is deprecated. Use Array.combine');
        return this.combine(array);
    }

});
Function.implement({

    bindAsEventListener: function(bind, args) {
        MooTools.upgradeLog('1.1 > 1.2: Function.bindAsEventListener is deprecated. Use bindWithEvent.');
        return this.bindWithEvent.call(this, bind, args);
    }

});

Function.empty = function() {
    MooTools.upgradeLog('1.1 > 1.2: Function.empty is now just $empty.');
}; Hash.implement({

    keys: function() {
        MooTools.upgradeLog('1.1 > 1.2: Hash.keys is deprecated. Use Hash.getKeys');
        return this.getKeys();
    },

    values: function() {
        MooTools.upgradeLog('1.1 > 1.2: Hash.values is deprecated. Use Hash.getValues');
        return this.getValues();
    },

    hasKey: function(item) {
        MooTools.upgradeLog('1.1 > 1.2: Hash.hasKey is deprecated. Use Hash.has');
        return this.has(item);
    },

    merge: function(properties) {
        MooTools.upgradeLog('1.1 > 1.2: Hash.merge is deprecated. Use Hash.combine');
        return this.extend(properties);
    },

    remove: function(key) {
        MooTools.upgradeLog('1.1 > 1.2: Hash.remove is deprecated. use Hash.erase');
        return this.erase(key);
    }

});

Object.toQueryString = function(obj) {
    MooTools.upgradeLog('1.1 > 1.2: Object.toQueryString() is deprecated. use Hash.toQueryString() instead');
    $H(obj).each(function(item, key) {
        if ($type(item) == 'object' || $type(item) == 'array') {
            obj[key] = item.toString();
        }
    });
    return Hash.toQueryString(obj);
};

var Abstract = function(obj) {
    MooTools.upgradeLog('1.1 > 1.2: Abstract is deprecated. Use Hash');
    return new Hash(obj);
}; Class.empty = function() {
    MooTools.upgradeLog('1.1 > 1.2: replace Class.empty with $empty');
    return $empty;
};

//legacy .extend support

(function() {
    var proto = function(obj) {
        var f = function() {
            return this;
        };
        f.prototype = obj;
        return f;
    };

    Class.prototype.extend = function(properties) {
        MooTools.upgradeLog('1.1 > 1.2: Class.extend is deprecated. See the class Extend mutator.');
        var maker = proto(properties);
        var made = new maker();
        made.Extends = this;
        return new Class(made);
    };

    var __implement = Class.prototype.implement;
    Class.prototype.implement = function() {
        if (arguments.length > 1 && Array.every(arguments, Object.type)) {
            MooTools.upgradeLog('1.1 > 1.2: Class.implement no longer takes more than one thing at a time, either MyClass.implement(key, value) or MyClass.implement(object) but NOT MyClass.implement(new Foo, new Bar, new Baz). See also: the class Implements mutator.');
            Array.each(arguments, function(argument) {
                __implement.call(this, argument);
            }, this);
            return this;
        }
        return __implement.apply(this, arguments);
    };
})(); (function() {

    var getPosition = Element.prototype.getPosition;
    var getCoordinates = Element.prototype.getCoordinates;

    function isBody(element) {
        return (/^(?:body|html)$/i).test(element.tagName);
    };

    var getSize = Element.prototype.getSize;

    Element.implement({

        getSize: function() {
            MooTools.upgradeLog('1.1 > 1.2: NOTE: getSize is different in 1.2; it no longer returns values for size, scroll, and scrollSize, but instead just returns x/y values for the dimensions of the element.');
            var size = getSize.apply(this, arguments);
            return $merge(size, {
                size: size,
                scroll: this.getScroll(),
                scrollSize: this.getScrollSize()
            });
        },

        getPosition: function(relative) {
            if (relative && $type(relative) == "array") {
                MooTools.upgradeLog('1.1 > 1.2: Element.getPosition no longer accepts an array of overflown elements but rather, optionally, a single element to get relative coordinates.');
                relative = null;
            }
            return getPosition.apply(this, [relative]);
        },

        getCoordinates: function(relative) {
            if (relative && $type(relative) == "array") {
                MooTools.upgradeLog('1.1 > 1.2: Element.getCoordinates no longer accepts an array of overflown elements but rather, optionally, a single element to get relative coordinates.');
                relative = null;
            }
            return getCoordinates.apply(this, [relative]);
        }

    });

    Native.implement([Document, Window], {

        getSize: function() {
            MooTools.upgradeLog('1.1 > 1.2: NOTE: getSize is different in 1.2; it no longer returns values for size, scroll, and scrollSize, but instead just returns x/y values for the dimensions of the element.');
            var size;
            var win = this.getWindow();
            var doc = this.getDocument();
            doc = (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
            if (Browser.Engine.presto || Browser.Engine.webkit) {
                size = { x: win.innerWidth, y: win.innerHeight };
            } else {
                size = { x: doc.clientWidth, y: doc.clientHeight };
            }
            return $extend(size, {
                size: size,
                scroll: { x: win.pageXOffset || doc.scrollLeft, y: win.pageYOffset || doc.scrollTop },
                scrollSize: { x: Math.max(doc.scrollWidth, size.x), y: Math.max(doc.scrollHeight, size.y) }
            });
        }

    });

})(); Event.keys = Event.Keys; // TODO
(function() {

    var toQueryString = Element.prototype.toQueryString;

    Element.implement({

        getFormElements: function() {
            MooTools.upgradeLog('1.1 > 1.2: Element.getFormElements is deprecated, use Element.getElements("input, textarea, select");');
            return this.getElements('input, textarea, select');
        },

        replaceWith: function(el) {
            MooTools.upgradeLog('1.1 > 1.2: Element.replaceWith is deprecated, use Element.replaces instead.');
            el = $(el);
            this.parentNode.replaceChild(el, this);
            return el;
        },

        remove: function() {
            MooTools.upgradeLog('1.1 > 1.2: Element.remove is deprecated - use Element.dispose.');
            return this.dispose.apply(this, arguments);
        },

        getText: function() {
            MooTools.upgradeLog('1.1 > 1.2: Element.getText is deprecated - use Element.get("text").');
            return this.get('text');
        },

        setText: function(text) {
            MooTools.upgradeLog('1.1 > 1.2: Element.setText is deprecated - use Element.set("text", text).');
            return this.set('text', text);
        },

        setHTML: function() {
            MooTools.upgradeLog('1.1 > 1.2: Element.setHTML is deprecated - use Element.set("html", HTML).');
            return this.set('html', arguments);
        },

        getHTML: function() {
            MooTools.upgradeLog('1.1 > 1.2: Element.getHTML is deprecated - use Element.get("html").');
            return this.get('html');
        },

        getTag: function() {
            MooTools.upgradeLog('1.1 > 1.2: Element.getTag is deprecated - use Element.get("tag").');
            return this.get('tag');
        },

        getValue: function() {
            MooTools.upgradeLog('1.1 > 1.2: Element.getValue is deprecated - use Element.get("value").');
            switch (this.getTag()) {
                case 'select':
                    var values = [];
                    $each(this.options, function(option) {
                        if (option.selected) values.push($pick(option.value, option.text));
                    });
                    return (this.multiple) ? values : values[0];
                case 'input': if (!(this.checked && ['checkbox', 'radio'].contains(this.type)) && !['hidden', 'text', 'password'].contains(this.type)) break;
                case 'textarea': return this.value;
            }
            return false;
        },

        toQueryString: function() {
            MooTools.upgradeLog('1.1 > 1.2: warning Element.toQueryString is slightly different; inputs without names are excluded, inputs with type == submit, reset, and file are excluded, and inputs with undefined values are excluded.');
            return toQueryString.apply(this, arguments);
        }
    });
})();

Element.Properties.properties = {

    set: function(props) {
        MooTools.upgradeLog('1.1 > 1.2: Element.set({properties: {}}) is deprecated; instead of properties, just name the values at the root of the object (Element.set({src: url})).');
        $H(props).each(function(value, property) {
            this.set(property, value);
        }, this);
    }

};
Element.implement({

    setOpacity: function(op) {
        MooTools.upgradeLog('1.1 > 1.2: Element.setOpacity is deprecated; use Element.setStyle("opacity", value).');
        return this.setStyle('opacity', op);
    }

});

Element.Properties.styles = {

    set: function(styles) {
        MooTools.upgradeLog('1.1 > 1.2: Element.set("styles") no longer accepts a string as an argument. Pass an object instead.');
        if ($type(styles) == 'string') {
            styles.split(";").each(function(style) {
                this.setStyle(style.split(":")[0], style.split(":")[1]);
            }, this);
        } else {
            this.setStyles(styles);
        }
    }

}; Fx.implement({

    custom: function(from, to) {
        MooTools.upgradeLog('1.1 > 1.2: Fx.custom is deprecated. use Fx.start.');
        return this.start(from, to);
    },

    clearTimer: function() {
        MooTools.upgradeLog('1.1 > 1.2: Fx.clearTimer is deprecated. use Fx.cancel.');
        return this.cancel();
    },

    stop: function() {
        MooTools.upgradeLog('1.1 > 1.2: Fx.stop is deprecated. use Fx.cancel.');
        return this.cancel();
    }

});

Fx.Base = new Class({
    Extends: Fx,
    initialize: function() {
        MooTools.upgradeLog('1.1 > 1.2: Fx.Base is deprecated. use Fx.');
        this.parent.apply(this, arguments);
    }
});
Fx.Style = new Class({
    Extends: Fx.Tween,
    initialize: function(element, property, options) {
        MooTools.upgradeLog('1.1 > 1.2: Fx.Style is deprecated. use Fx.Tween.');
        this.property = property;
        this.parent(element, options);
    },

    start: function(from, to) {
        return this.parent(this.property, from, to);
    },

    set: function(to) {
        return this.parent(this.property, to);
    },

    hide: function() {
        MooTools.upgradeLog('1.1 > 1.2: Fx.Style .hide() is deprecated; use Fx.Tween .set(0) instead');
        return this.set(0);
    }

});

Element.implement({

    effect: function(property, options) {
        MooTools.upgradeLog('1.1 > 1.2: Element.effect is deprecated; use Fx.Tween or Element.tween.');
        return new Fx.Style(this, property, options);
    }

});
Fx.Styles = new Class({
    Extends: Fx.Morph,
    initialize: function() {
        MooTools.upgradeLog('1.1 > 1.2: Fx.Styles is deprecated. use Fx.Morph.');
        this.parent.apply(this, arguments);
    }
});

Element.implement({

    effects: function(options) {
        MooTools.upgradeLog('1.1 > 1.2: Element.effects is deprecated; use Fx.Morph or Element.morph.');
        return new Fx.Morph(this, options);
    }

}); Fx.Scroll.implement({

    scrollTo: function(y, x) {
        MooTools.upgradeLog('1.1 > 1.2: Fx.Scroll\'s .scrollTo is deprecated; use .start.');
        return this.start(y, x);
    }

}); Request.implement({
    //1.11 passed along the response text and xml to onComplete
    onStateChange: function() {
        if (this.xhr.readyState != 4 || !this.running) return;
        this.running = false;
        this.status = 0;
        $try(function() {
            this.status = this.xhr.status;
        } .bind(this));
        this.xhr.onreadystatechange = $empty;
        this.response = { text: this.xhr.responseText, xml: this.xhr.responseXML };
        if (this.options.isSuccess.call(this, this.status)) this.success(this.response.text, this.response.xml);
        else this.failure(this.response.text, this.response.xml);
    },

    failure: function() {
        this.onFailure.apply(this, arguments);
    },

    onFailure: function() {
        MooTools.upgradeLog('1.1 > 1.2: Note that onComplete does not receive arguments in 1.2. Also note that onComplete is invoked on BOTH success and failure (while in 1.1 it was only invoked on success). Use the onSuccess event instead if you wish to limit this invocation to success.');
        this.fireEvent('complete', arguments).fireEvent('failure', this.xhr);
    }

});

var XHR = new Class({

    Extends: Request,

    options: {
        update: false
    },

    initialize: function(options) {
        MooTools.upgradeLog('1.1 > 1.2: XHR is deprecated. Use Request.');
        this.parent(options);
        this.transport = this.xhr;
    },

    request: function(data) {
        MooTools.upgradeLog('1.1 > 1.2: XHR.request() is deprecated. Use Request.send() instead.');
        return this.send(this.url, data || this.options.data);
    },

    send: function(url, data) {
        if (!this.check(arguments.callee, url, data)) return this;
        return this.parent({ url: url, data: data });
    },

    success: function(text, xml) {
        text = this.processScripts(text);
        if (this.options.update) $(this.options.update).empty().set('html', text);
        this.onSuccess(text, xml);
    },

    failure: function() {
        this.fireEvent('failure', this.xhr);
    }

});


var Ajax = new Class({

    Extends: XHR,

    initialize: function(url, options) {
        MooTools.upgradeLog('1.1 > 1.2: Ajax is deprecated. Use Request.');
        this.url = url;
        this.parent(options);
    },

    success: function(text, xml) {
        // This version processes scripts *after* the update element is updated, like Mootools 1.1's Ajax class
        // Partially from Remote.Ajax.success
        this.processScripts(text);
        response = this.response;
        response.html = text.stripScripts(function(script) {
            response.javascript = script;
        });
        if (this.options.update) $(this.options.update).empty().set('html', response.html);
        if (this.options.evalScripts) $exec(response.javascript);
        this.onSuccess(text, xml);
    }

});

(function() {
    var send = Element.prototype.send;
    Element.implement({
        send: function(url) {
            if ($type(url) == "string") return send.apply(this, arguments);
            if ($type(url) == "object") {
                MooTools.upgradeLog('1.1 > 1.2: Element.send no longer takes an options argument as its object but rather a url. See docs.');
                this.set('send', url);
                send.call(this);
            }
            return this;
        }
    });
})(); JSON.Remote = new Class({

    options: {
        key: 'json'
    },

    Extends: Request.JSON,

    initialize: function(url, options) {
        MooTools.upgradeLog('JSON.Remote is deprecated. Use Request.JSON');
        this.parent(options);
        this.onComplete = $empty;
        this.url = url;
    },

    send: function(data) {
        if (!this.check(arguments.callee, data)) return this;
        return this.parent({ url: this.url, data: { json: Json.encode(data)} });
    },

    failure: function() {
        this.fireEvent('failure', this.xhr);
    }

});

Cookie.set = function(key, value, options) {
    MooTools.upgradeLog('1.1 > 1.2: Cookie.set is deprecated. Use Cookie.write');
    return new Cookie(key, options).write(value);
};

Cookie.get = function(key) {
    MooTools.upgradeLog('1.1 > 1.2: Cookie.get is deprecated. Use Cookie.read');
    return new Cookie(key).read();
};

Cookie.remove = function(key, options) {
    MooTools.upgradeLog('1.1 > 1.2: Cookie.remove is deprecated. Use Cookie.dispose');
    return new Cookie(key, options).dispose();
};
JSON.toString = function(obj) {
    MooTools.upgradeLog('1.1 > 1.2: JSON.toString is deprecated. Use JSON.encode');
    return JSON.encode(obj);
};
JSON.evaluate = function(str) {
    MooTools.upgradeLog('1.1 > 1.2: JSON.evaluate is deprecated. Use JSON.decode');
    return JSON.decode(str);
};
var Json = JSON;

Native.implement([Element, Document], {

    getElementsByClassName: function(className) {
        MooTools.upgradeLog('1.1 > 1.2: Element.filterByTag is deprecated.');

        return this.getElements('.' + className);
    },

    getElementsBySelector: function(selector) {
        MooTools.upgradeLog('1.1 > 1.2: Element.getElementsBySelector is deprecated. Use getElements()');
        return this.getElements(selector);
    }

});

Elements.implement({

    filterByTag: function(tag) {
        MooTools.upgradeLog('1.1 > 1.2: Elements.filterByTag is deprecated. Use Elements.filter.');
        return this.filter(tag);
    },

    filterByClass: function(className) {
        MooTools.upgradeLog('1.1 > 1.2: Elements.filterByClass is deprecated. Use Elements.filter.');
        return this.filter('.' + className);
    },

    filterById: function(id) {
        MooTools.upgradeLog('1.1 > 1.2: Elements.filterById is deprecated. Use Elements.filter.');
        return this.filter('#' + id);
    },

    filterByAttribute: function(name, operator, value) {
        MooTools.upgradeLog('1.1 > 1.2: Elements.filterByAttribute is deprecated. Use Elements.filter.');
        var filtered = this.filter('[' + name + (operator || '') + (value || '') + ']');
        if (value) filtered = filtered.filter('[' + name + ']');
        return filtered;
    }

});

var $E = function(selector, filter) {
    MooTools.upgradeLog('1.1 > 1.2: $E is deprecated, use document.getElement.');
    return ($(filter) || document).getElement(selector);
};

var $ES = function(selector, filter) {
    MooTools.upgradeLog('1.1 > 1.2: $ES is deprecated. Use $$.');
    return ($(filter) || document).getElements(selector);
}; (function() {
    if (!window.Tips) return;

    Tips.implement({

        initialize: function() {
            MooTools.upgradeLog('1.1 > 1.2: Tips DOM element layout has changed and your CSS classes may need to change.');
            var params = Array.link(arguments, { options: Object.type, elements: $defined });
            this.setOptions(params.options);
            if (this.options.offsets) {
                MooTools.upgradeLog('1.1 > 1.2: Tips no longer have an "offsets" option; use "offset".');
                this.options.offset = this.options.offsets;
            }
            document.id(this);
            this.addEvent('show', function() {
                this.tip.addClass('tool-tip');
                this.tip.getElement('.tip-title').addClass('tool-title');
                this.tip.getElement('.tip-text').addClass('tool-text');
            });
            this.parseTitle(params.elements);
            if (params.elements) this.attach(params.elements);
        },

        parseTitle: function(elements) {
            elements.each(function(element) {
                var title = element.get('title');
                if (title.test('::')) {
                    MooTools.upgradeLog('1.1 > 1.2: Tips no longer parse the title attribute for "::" for title/caption; use title and rel attributes instead.');
                    element.store('tip:title', title.split('::')[0]);
                    element.store('tip:text', title.split('::')[1]);
                    element.set('title', '');
                }
            });
        }

    });

})();
/*	Script: modalizer.js
		Provides functionality to overlay the window contents with a semi-transparent layer that prevents interaction with page content until it is removed.

		Dependencies:
		Mootools - <Moo.js>, <Array.js>, <String.js>, <Function.js>, <Utility.js>, <Dom.js>, <Element.js>, <Window.Size.js>, <Event.js>, <Window.Base.js>

		Author:
		Aaron Newton (aaron [dot] newton [at] cnet [dot] com)

		Class: Modalizer
		Provides functionality to overlay the window contents with a semi-transparent layer that prevents interaction with page content until it is removed. This class is intended
		to be implemented into other classes to provide them access to this functionality.
	*/

var Modalizer = new Class({
	defaultModalStyle: {
		'display':'block',
		'position':'fixed',
		'top':'0px',
		'left':'0px',
		'z-index':5000,
		'background-color':'#333',
		'opacity':.8
	},
/*	Property: setOptions
		Sets the options for the modal overlay.

		Arguments:
		options - an object with name/value definitions

		See <modalShow> for options list.
	*/
	setModalOptions: function(options){
		this.modalOptions = $merge({
			'width':(window.getScrollWidth()+300)+'px',
			'height':(window.getScrollHeight()+300)+'px',
			elementsToHide: 'select',
			onModalHide: Class.empty,
			onModalShow: Class.empty,
			hideOnClick: true,
			modalStyle: {}
		}, this.modalOptions, options || {});
	},
/*	Property: setModalStyle
		Sets the style of the modal overlay to those in the object passed in.

		Arguments:
		styleObject - object with key/value css properties

		Default styleObject:
(start code){
	'display':'block',
	'position':'fixed',
	'top':'0px',
	'left':'0px',
	'width':'100%',
	'height':'100%',
	'z-index':this.modalOptions.zIndex,
	'background-color':this.modalOptions.color,
	'opacity':this.modalOptions.opacity
}(end)

	The object you pass in can contain any portion of this object, and the options you specify will overwrite the defaults; any option you do not specify will remain.
	*/
	setModalStyle: function (styleObject){
		this.modalOptions.modalStyle = styleObject;
		this.modalStyle = $merge(this.defaultModalStyle, {
			'width':this.modalOptions.width,
			'height':this.modalOptions.height
		}, styleObject);
		if($('modalOverlay'))$('modalOverlay').setStyles(this.modalStyle);
		return(this.modalStyle);
	},
/*	Property: modalShow
		Shows the modal window.

		Arguments:
		options - key/value options object

		Options:
		elementsToHide - comma seperated string of selectors to hide when the overlay is applied;
			example: 'select, input, img.someClass'; defaults to 'select'
		modalHide - the funciton that hides the modal window; defaults to
			"function(){if($('modalOverlay'))$('modalOverlay').hide();}"
		modalShow - the function that shows the modal window; defaults to
			"function(){$('modalOverlay').setStyle('display','block');}"
		onModalHide - function to execute when the modal window is removed
		onModalShow - function to execute when the modal window appears
		hideOnClick - allow the user to click anywhere on the modal layer to close it; defaults to true.
		modalStyle - a css style object to apply to the modal overlay. See <setModalStyle>.
	*/
	modalShow: function(options){
		this.setModalOptions(options||{});
		var overlay = null;
		if($('modalOverlay')) overlay = $('modalOverlay');
		if(!overlay) overlay = new Element('div').setProperty('id','modalOverlay').injectInside(document.body);
		overlay.setStyles(this.setModalStyle(this.modalOptions.modalStyle));
		if(window.ie6) overlay.setStyle('position','absolute');
		$('modalOverlay').removeEvents('click').addEvent('click', function(){
			this.modalHide(this.modalOptions.hideOnClick);
		}.bind(this));
		this.modalOptions.onModalShow();
		this.togglePopThroughElements(0);
		overlay.setStyle('display','block');
		return this;
	},
/*	Property: modalHide
		Hides the modal layer.

	*/
	modalHide: function(override){
		if(override === false) return; //this is internal, you don't need to pass in an argument
		this.togglePopThroughElements(1);
		this.modalOptions.onModalHide();
		if($('modalOverlay'))$('modalOverlay').setStyle('display','none');
		return this;
	},
	togglePopThroughElements: function(opacity){
		if((window.ie6 || (window.gecko && navigator.userAgent.test('mac', 'i')))) {
			$$(this.modalOptions.elementsToHide).each(function(sel){
				sel.setStyle('opacity', opacity);
			});
		}
	}
});
//legacy namespace
var modalizer = Modalizer;
/* do not edit below this line */
/* Section: Change Log

$Source: /cvs/main/flatfile/html/rb/js/global/cnet.global.framework/common/js.widgets/modalizer.js,v $
$Log: modalizer.js,v $
Revision 1.17  2007/06/29 21:54:27  newtona
fixed a bug with the hideOnClick option

Revision 1.16  2007/05/29 22:01:53  newtona
Split element.cnet.js into seperate files; updated docs in files to note this
Changed element.visible to element.isVisible (left old namespace for legacy support)
Fixed Element.empty in prototype.compatibility.js
Removed as many dependencies in common code to element.*.js as possible (espeically element.shortcuts.js)

Revision 1.15  2007/05/03 18:24:24  newtona
iframeshim: removed a dbug line
modalizer: only hide select lists for browsers that need it
product picker: added a try/catch, updated cnet api link/code

Revision 1.14  2007/04/13 19:06:11  newtona
dependency update in the docs

Revision 1.13  2007/03/27 16:05:40  newtona
fixed bug where select elements were not being hidden on modal show

Revision 1.12  2007/03/20 20:59:54  newtona
fixed a problem where the modal stickywin didn't close when the modal layer was clicked.

Revision 1.11  2007/03/19 17:34:38  newtona
fixed a bug; modal overlay width/height wasn't being set.

Revision 1.10  2007/03/05 23:33:38  newtona
moved width declarations to setModalOptions function; fixed a bug in opera

Revision 1.9  2007/03/05 19:36:00  newtona
now setModalOptions is always called, even if options are not supplied (so the defaults will be used)

Revision 1.8  2007/02/27 21:46:43  newtona
docs update; fixing references

Revision 1.7  2007/02/21 00:27:28  newtona
better option handling

Revision 1.6  2007/02/07 20:51:41  newtona
implemented Options class
implemented Events class
StickyWin now uses Element.position

Revision 1.5  2007/02/06 18:11:29  newtona
refactored to re-use the overlay div because IE hogs memory for each one. god I hate IE.

Revision 1.4  2007/01/26 05:48:22  newtona
docs update

Revision 1.3  2007/01/22 22:00:15  newtona
numerous bug fixes to modalizer, stickywin, and popupdetails
updated for mootools 1.0
fixed date validation in form.validator

Revision 1.2  2007/01/11 20:55:23  newtona
changed the way options are set, split up stickywin into 4 files, refactored popupdetails to use stickywin and modalizer

Revision 1.1  2007/01/09 02:39:35  newtona
renamed addons directory to "common" directory

Revision 1.3  2007/01/09 01:25:05  newtona
numerous improvements; ability to set individual css styles, some other tweaks

Revision 1.2  2007/01/05 18:31:51  newtona
fixed documentation syntax
added cvs footer


*/

/*
* MooTools
* version 1.11
* documentation at http://docs111.mootools.net
* used for floating layer & on dom ready functions
*/

/*
* Modalizer
* based on MooTools
* documentation at http://clientside.cnet.com/cnet.gf/docs/files3/common/js-widgets/modalizer-js.html
* used for modal floating layer
*/

window.addEvent('domready', function() {
    $$('img.mo').each(function(img) {
        var src = img.getProperty('src');
        var extension = src.substring(src.lastIndexOf('.'), src.length)
        img.addEvent('mouseenter', function() { img.setProperty('src', src.replace(extension, '-o' + extension)); });
        img.addEvent('mouseleave', function() { img.setProperty('src', src); });
    });
    
    if (window.getWidth() >= 1200) {
        ShowBackGround();
    }
});

function ShowBackGround() {
    var background = document.getElementById('WallpaperContainer');
    var pageWrapper = document.getElementById('pagewrapper');
    var mainDiv = document.getElementById('mainDiv');
    var mainBodyDiv = document.getElementById('mainBodyDiv');
    
    if (background) {
        background.className = (background.className == 'hidden') ? 'unhidden' : 'hidden';

        if (mainDiv) {
            mainDiv.className = "main-without-background";
        }

        if (mainBodyDiv) {
            mainBodyDiv.className = "main-without-background";
        }

        if (pageWrapper) {
            pageWrapper.className = "backgroundwrapper_fixed";            
        }    
    }
}

var FloatingLayer = new Class({

    /*
    * @type {Object}
    */
    options: {
        loadingText: 'Loading content...',
        failureText: 'Failed to get content',
        layerColor: '#000000',
        layerOpacity: .5
    },

    /*
    * Constructor
    *
    * @param {Object} options
    */
    initialize: function(options) {
        this.setOptions(options);

        this.xposition = getWidth() / 2;
        this.yposition = getHeight() / 2;
        this.zIndex = '6000';
        this.className = 'floating-layer-wrapper';
        this.popwindow = null;
        this.iframe = null;
        this.hideButton = 'a.close';
        this.maxHeight = 600;
        this.loads = 1;

        // Create base element if it doesn't already exist
        if ($$('div.floating-layer-wrapper').length < 1) {
            this.popwindow = new Element('div', {
                'styles': {
                    'left': this.xposition,
                    'top': this.yposition,
                    'z-index': this.zIndex
                },
                'class': this.className
            }).injectInside(document.body).setOpacity(0);
            this.iframe = new Element('iframe');
            //this.popwindow.adopt(this.iframe);
            this.iframe.setProperties({
                'frameborder': '0',
                'scrolling': 'auto',
                'name': 'fl' + this.xposition
            });
            this.popwindow.adopt(this.iframe);
        } else {
            this.popwindow = $$('div.floating-layer-wrapper')[0];
            this.iframe = $$('iframe')[0];
        }
    },

    /*
    * Will show the popup
    */
    show: function(url, button, maxHeight) {
        this.modalShow({
            modalStyle: {
                'background-color': this.options.layerColor,
                'opacity': this.options.layerOpacity
            },
            hideOnClick: true,
            onModalShow: function() { this.getContent(url, button) } .bind(this),
            onModalHide: function() { this.popwindow.setOpacity(0) } .bind(this)
        });
    },

    /*
    * Will asynchronously get the content for the popup from html file
    */
    getContent: function(url, button) {
        this.iframe.addEvent('load', this.effect.bindAsEventListener(this, [button]));
        this.iframe.contentWindow.location.href = url;
    },

    /*
    * Will apply effect to showing of content (only on first load)
    */
    effect: function(e, button) {

        var effectPopwindow = new Fx.Styles(this.popwindow, { duration: 1000, transition: Fx.Transitions.linear });
        var frameBody = $(this.popwindow.getFirst());

        assignHideButtonFloatingLayer(button);

        if (this.loads == 1) {
            effectPopwindow.start({
                'opacity': [0.2, 1]
            })
        }

        //get the size of the content
        if (this.popwindow.getFirst().contentDocument) { // DOM
            var frameSize = frameBody.contentDocument.body.getFirst().getSize().scrollSize;
            frameSize.y = frameSize.y + 14;
        } else if (this.popwindow.getFirst().contentWindow) { // IE win
            var frameSize = frameBody.contentWindow.document.body.getFirst().getSize().scrollSize;
            frameSize.x = frameSize.x + 14;
            frameSize.y = frameSize.y + 14;
        }

        var newWidth = frameSize.x;
        var newHeight = frameSize.y;

        if (this.maxHeight < frameSize.y) {
            newHeight = this.maxHeight
        }

        //change the size of the floating layer & iframe
        this.popwindow.setStyles({
            'left': (this.xposition.toInt() - (newWidth / 2)),
            'top': (this.yposition.toInt() - (newHeight / 2) + window.getScrollTop()),
            'width': newWidth,
            'height': newHeight,
            'border': 'none'
        })

        frameBody.setStyles({
            'width': newWidth,
            'height': newHeight
        })

        this.loads = this.loads + 1;
    },

    /*
    * Will hide the popup
    */
    hide: function() {
        this.popwindow.setOpacity(0);
        this.modalHide();
    }
});

FloatingLayer.implement(new Options, new Events);
FloatingLayer.implement(new Modalizer);

/* create a floating layer of modal type */
function createFloatingLayer(showButton, hideButton, contentFile, maximumHeight) {
    /*
    * Clear any existing floating layer
    */
    myFloatingLayer = null;

    /*
    * create new floating layer & add show method to click event button
    */
    $E(showButton).removeProperty('href');
    $E(showButton).setStyle('cursor', 'pointer');
    $E(showButton).addEvent('click', function(event) { myFloatingLayer = new FloatingLayer(); myFloatingLayer.show(contentFile, hideButton); return false; });
}

function assignHideButtonFloatingLayer(hideButton) {
    /*
    * add hide method to click event close button
    */
    if ($$('iframe')[0].contentDocument) { // DOM
        var closeButton = $ES(hideButton, $E('iframe').contentDocument.body);
    } else if ($$('iframe')[0].contentWindow) { // IE win
        var closeButton = $ES(hideButton, $E('iframe').contentWindow.document.body);
    }

    for (var i = 0; i < closeButton.length; i++) {
        closeButton[i].addEvent('click', function(event) { myFloatingLayer.hide(); return false; });
    }
}

/* sticking footer at the bottom of the viewport */
window.addEvent('domready', function() {
    if ($chk($E('.footer'))) {
        var heightViewport = window.getHeight();
        var heightContent = $E('.footer').getTop() + $E('.footer').getSize().size.y;
        var marginFooter = $E('.footer').getStyle('margin-top').toInt();
        var fillerFooter = $E('.footer').getStyle('margin-top').toInt() + heightViewport - heightContent;

        if (heightViewport > heightContent) {
            $E('.footer').setStyle('margin-top', ($E('.footer').getStyle('margin-top').toInt() + heightViewport - heightContent))
        };
    };
});

/* hover state of images in big-navigation-shop */
window.addEvent('domready', function() {
    $$('.big-navigation-shop li a').each(function(el) {
        var img;
        var src;
        var extension;

        if ($chk($E('img', el))) {
            img = $E('img', el);
            src = img.getProperty('src');
            extension = src.substring(src.lastIndexOf('.'), src.length)

            if (src.lastIndexOf('-o') < 0) {
                el.addEvent('mouseenter', function() { img.setProperty('src', src.replace(extension, '-o' + extension)); });
                el.addEvent('mouseleave', function() { img.setProperty('src', src); });
            };
        };
    });
});

/* hover state of images in list3-view */
window.addEvent('domready', function() {
    $$('.product-view3-row').each(function(el) {
        var src;
        var srco;
        var extension;

        src = 'url(/images/list3-bg.gif)';
        srco = 'url(/images/list3-bg-o.gif)';

        el.addEvent('mouseenter', function() { el.setStyle('background-image', srco) });
        el.addEvent('mouseleave', function() { el.setStyle('background-image', src) });
    });
});


/* hover state of images in list12-view */
window.addEvent('domready', function() {
    $$('.image-info-content').each(function(el) {
        var src;
        var srco;
        var extension;

        src = 'url(/images/product-lijst-bg.gif)';
        srco = 'url(/images/product-lijst-bg-o.gif)';

        el.addEvent('mouseenter', function() { el.setStyle('background-image', srco) });
        el.addEvent('mouseleave', function() { el.setStyle('background-image', src) });
    });
});

/* hover state of images in list12-view */
window.addEvent('domready', function() {
    $$('.bijpassende-image').each(function(el) {
        var src;
        var srco;
        var extension;

        src = 'url(/images/bijpassende-bg.gif)';
        srco = 'url(/images/bijpassende-bg-o.gif)';

        el.addEvent('mouseenter', function() { el.setStyle('background-image', srco) });
        el.addEvent('mouseleave', function() { el.setStyle('background-image', src) });
    });
});

/* hover state of images in list12-view */
window.addEvent('domready', function() {
    $$('.bijpassende-image2').each(function(el) {
        var src;
        var srco;
        var extension;

        src = 'url(/images/bijpassende-bg.gif)';
        srco = 'url(/images/bijpassende-bg-o.gif)';

        el.addEvent('mouseenter', function() { el.setStyle('background-image', srco) });
        el.addEvent('mouseleave', function() { el.setStyle('background-image', src) });
    });
});

function swapimg(oThis, img)
{
    document.getElementById(oThis).style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src = '" + img + "', sizingMethod='scale')";
}

function closeFloatingLayer()
{
    myFloatingLayer.hide();
    return false;
}

function openFloatingLayer(contentFile, hideButton)
{
    /*
    * create new floating layer
    */
    myFloatingLayer = new FloatingLayer(); myFloatingLayer.show(contentFile, hideButton);
}
function addWatermark(targetControlUniqueID, watermarkText)
{
    var txt = $(targetControlUniqueID);
    
    if( txt != null)
    {    
        txt.addEvents({
	        'focus': function() {
		        if (txt.value.contains(watermarkText)) txt.value = '';
	        },
	        'blur': function() {
	            if( txt.value.trim() == '')
	            {
		            txt.value = watermarkText;
		        }
	        }
        });
    }
}

function submitWatermarkedTextBox(targetControlUniqueID, watermarktText) {

    var txt = $(targetControlUniqueID);

    if (txt != null) {
        if (txt.value == watermarktText) 
        {
            txt.value = "";
        }
    }

}

var messageSlideOut;

function isSizeSelected() {
    var selectedSizeField = $(selectedSizeFieldId);
    return selectedSizeField.value != '';
}

function getSelectedSize() {
    var selectedSizeField = $(selectedSizeFieldId);
    return selectedSizeField.value;
}

function setSelectedSize(value) {
    var selectedSizeField = $(selectedSizeFieldId);
    selectedSizeField.value = value;
}

function isSizeInStock() {
    var stockStatusField = $(stockStatusFieldId);
    return stockStatusField.value == 'true';
}

function openWhereToBuy() {
    if (!isSizeSelected()) {
        messageSlideOut.slideIn();
    }
    else {
        var variantid = getSelectedSize();
        openFloatingLayer('/StoreLocator.aspx?Catalog=' + catalogName + '&ProductId=' + productId + '&VariantId=' + variantid + '&SeachOnPostcode=true', 'a.close');
    }
}

function saveSelectedSizeInCookie(selectedSize) {
    var sizeCookie = Cookie.write("selectedsize", selectedSize);
}

function getSelectedSizeFromCookie() {
    return Cookie.read("selectedsize");
}

function selectNewButton(selectedButton, classname, instock) {

    var selectedButtonElement = $(selectedButton);
    var selectedSize = selectedButtonElement.get('text');
    saveSelectedSizeInCookie(selectedSize);

    var errorMessage = $$('.maat-foutmelding');
    errorMessage[0].setStyle('display', 'block');

    selectedButton.className = classname;

    var stockStatusField = $(stockStatusFieldId);
    var selectedSizeField = $(selectedSizeFieldId);

    var sizeField = selectedButton.childNodes[0];
    if (sizeField != null) {
        setSelectedSize(sizeField.value);
    }
    stockStatusField.value = instock;
}

function SetupButtons(addToBasketEnabled) {

    try {
        var basketButton = $('addToBasket');
        var basketButtonImage = $$('#addToBasket img');
        var addToWishlistButtonImage = $$('#addToWishlist img');
        var whereToBuyButton = $('whereToBuy');
        var whereToBuyImage = $$('#whereToBuy img');
        if (basketButton != null) {
            if (addToBasketEnabled) {
                basketButtonImage.src = '/images/checkoutbox/btn-active-addtobasket.png';
                basketButtonImage[0].src = '/images/checkoutbox/btn-active-addtobasket.png';
            }
            else {
                basketButtonImage[0].src = '/images/checkoutbox/btn-addtobasket.png';
            }
            addToWishlistButtonImage[0].src = '/images/checkoutbox/btn-active-addtowishlist.png';
            whereToBuyImage[0].src = '/images/checkoutbox/btn-active-wheretobuy.png';
        }
    }
    catch (err) {
    }
}

function processSizeClick(element, instock) {
    deselectPreviousSelection();

    switch (instock) {
        case true:
            classname = 'instock';
            break;
        default:
            classname = 'nostock';
    }

    selectNewButton(element, classname, instock);

    SetupButtons(instock);
}

function clickSizeInStock() {
    processSizeClick(this, true);
}

function clickSizeNoStock() {
    processSizeClick(this, false);
}

function deselectPreviousSelection() {

    messageSlideOut.slideOut();

    var prevSelection = $$('div.instock', 'div.nostock');

    if (prevSelection.length > 0) {
        prevSelection.each(function (item) { item.className = item.className + '-default'; });
    }
}

function clickAddToBasket() {
    if (!isSizeSelected() || !isSizeInStock()) {
        messageSlideOut.slideIn();
    }
    else {
        __doPostBack(addToBasketButtonId, '');
    }
}

function clickAddToWishlist() {

    if (!isSizeSelected()) {
        messageSlideOut.slideIn();
    }
    else {
        __doPostBack(addToWishlistButtonId, '');
    }
}

window.addEvent('domready', function () {

    var maatFoutmelding = document.id('maat-foutmelding');

    if (maatFoutmelding != null) {

        messageSlideOut = new Fx.Slide('maat-foutmelding', { duration: 200 }).hide();

        var maatInStock = $$('div.maatselectie li div.instock-default').addEvent('click', clickSizeInStock);
        var maatOutOfStock = $$('div.maatselectie li div.nostock-default').addEvent('click', clickSizeNoStock);
        $$('#addToBasket').addEvent('click', clickAddToBasket);
        $$('#addToWishlist').addEvent('click', clickAddToWishlist);
        $$('#whereToBuy').addEvent('click', openWhereToBuy);

        var selectedSize = getSelectedSizeFromCookie();

        $each(maatInStock, function (element, index) {
            if (element.get('text') == selectedSize) {
                processSizeClick(element, true);
            }
        });

        $each(maatOutOfStock, function (element, index) {
            if (element.get('text') == selectedSize) {
                processSizeClick(element, false);
            }
        });
    }

});

