//MooTools, My Object Oriented Javascript Tools. Copyright (c) 2006 Valerio Proietti, <http://mad4milk.net>, MIT Style License.

var Class = function(properties){
    var klass = function(){
        for (var p in this){
            if (this[p]) this[p]._proto_ = this;
        }
        if (arguments[0] != 'noinit' && this.initialize) return this.initialize.apply(this, arguments);
    };
    klass.extend = this.extend;
    klass.implement = this.implement;
    klass.prototype = properties;
    return klass;
};

Class.empty = function(){};

Class.create = function(properties){
    return new Class(properties);
};

Class.prototype = {
    
    extend: function(properties){
        var pr0t0typ3 = new this('noinit');
        for (var property in properties){
            var previous = pr0t0typ3[property];
            var current = properties[property];
            if (previous && previous != current) current = previous.parentize(current) || current;
            pr0t0typ3[property] = current;
        }
        return new Class(pr0t0typ3);
    },
    
    implement: function(properties){
        for (var property in properties) this.prototype[property] = properties[property];
    }
    
};

Object.extend = function(){
    var args = arguments;
    if (args[1]) args = [args[0], args[1]];
    else args = [this, args[0]];
    for (var property in args[1]) args[0][property] = args[1][property];
    return args[0];
};

Object.Native = function(){
    for (var i = 0; i < arguments.length; i++) arguments[i].extend = Class.prototype.implement;
};

new Object.Native(Function, Array, String, Number);

Function.extend({
    
    parentize: function(current){
        var previous = this;
        return function(){
            this.parent = previous;
            return current.apply(this, arguments);
        };
    }
    
});

Function.extend({
    
    pass: function(args, bind){
        var fn = this;
        if ($type(args) != 'array') args = [args];
        return function(){
            return fn.apply(bind || fn._proto_ || fn, args);
        };
    },
    
    bind: function(bind){
        var fn = this;
        return function(){
            return fn.apply(bind, arguments);
        };
    },
    
    bindAsEventListener: function(bind){
        var fn = this;
        return function(event){
            fn.call(bind, event || window.event);
            return false;
        };
    },
    
    delay: function(ms, bind){
        return setTimeout(this.bind(bind || this._proto_ || this), ms);
    },
    
    periodical: function(ms, bind){
        return setInterval(this.bind(bind || this._proto_ || this), ms);
    }
    
});

function $clear(timer){
    clearTimeout(timer);
    clearInterval(timer);
    return null;
};

function $type(obj){
    if (!obj) return false;
    var type = false;
    if (obj instanceof Function) type = 'function';
    else if (obj.nodeName){
        if (obj.nodeType == 3 && !/\S/.test(obj.nodeValue)) type = 'textnode';
        else if (obj.nodeType == 1) type = 'element';
    }
    else if (obj instanceof Array) type = 'array';
    else if (typeof obj == 'object') type = 'object';
    else if (typeof obj == 'string') type = 'string';
    else if (typeof obj == 'number' && isFinite(obj)) type = 'number';
    return type;
};

var Chain = new Class({
    
    chain: function(fn){
        this.chains = this.chains || [];
        this.chains.push(fn);
        return this;
    },
    
    callChain: function(){
        if (this.chains && this.chains.length) this.chains.splice(0, 1)[0].delay(10, this);
    },
    
    clearChain: function(){
        this.chains = [];
    }
    
});

if (!Array.prototype.forEach){
    
    Array.prototype.forEach = function(fn, bind){
        for(var i = 0; i < this.length ; i++) fn.call(bind, this[i], i);
    };
}

Array.extend({
    
    each: Array.prototype.forEach,
    
    copy: function(){
        var newArray = [];
        for (var i = 0; i < this.length; i++) newArray.push(this[i]);
        return newArray;
    },
    
    remove: function(item){
        for (var i = 0; i < this.length; i++){
            if (this[i] == item) this.splice(i, 1);
        }
        return this;
    },
    
    test: function(item){
        for (var i = 0; i < this.length; i++){
            if (this[i] == item) return true;
        };
        return false;
    },
    
    extend: function(newArray){
        for (var i = 0; i < newArray.length; i++) this.push(newArray[i]);
        return this;
    },
    
    associate: function(keys){
        var newArray = [];
        for (var i =0; i < this.length; i++) newArray[keys[i]] = this[i];
        return newArray;
    }
    
});

function $A(array){
    return Array.prototype.copy.call(array);
};

String.extend({
    
    test: function(regex, params){
        return this.match(new RegExp(regex, params));
    },
    toInt: function(){
        return parseInt(this);
    },
    
    camelCase: function(){
        return this.replace(/-\D/gi, function(match){
            return match.charAt(match.length - 1).toUpperCase();
        });
    },
    capitalize: function(){
        return this.toLowerCase().replace(/\b[a-z]/g, function(match){
            return match.toUpperCase();
        });
    },
    
    trim: function(){
        return this.replace(/^\s*|\s*$/g, '');
    },
    
    clean: function(){
        return this.replace(/\s\s/g, ' ').trim();
    },
    
    rgbToHex: function(array){
        var rgb = this.test('([\\d]{1,3})', 'g');
        if (rgb[3] == 0) return 'transparent';
        var hex = [];
        for (var i = 0; i < 3; i++){
            var bit = (rgb[i]-0).toString(16);
            hex.push(bit.length == 1 ? '0'+bit : bit);
        }
        var hexText = '#'+hex.join('');
        if (array) return hex;
        else return hexText;
    },
    
    hexToRgb: function(array){
        var hex = this.test('^[#]{0,1}([\\w]{1,2})([\\w]{1,2})([\\w]{1,2})$');
        var rgb = [];
        for (var i = 1; i < hex.length; i++){
            if (hex[i].length == 1) hex[i] += hex[i];
            rgb.push(parseInt(hex[i], 16));
        }
        var rgbText = 'rgb('+rgb.join(',')+')';
        if (array) return rgb;
        else return rgbText;
    }
    
});

Number.extend({
    
    toInt: function(){
        return this;
    }
    
});

var Element = new Class({
    
    initialize: function(el){
        if ($type(el) == 'string') el = document.createElement(el);
        return $(el);
    },
    
    inject: function(el, where){
        el = $(el) || new Element(el);
        switch(where){
            case "before": $(el.parentNode).insertBefore(this, el); break;
            case "after": {
                if (!el.getNext()) $(el.parentNode).appendChild(this);
                else $(el.parentNode).insertBefore(this, el.getNext());
            } break;
            case "inside": el.appendChild(this); break;
        }
        return this;
    },
    
    injectBefore: function(el){
        return this.inject(el, 'before');
    },
    
    injectAfter: function(el){
        return this.inject(el, 'after');
    },
    
    injectInside: function(el){
        return this.inject(el, 'inside');
    },
    
    adopt: function(el){
        this.appendChild($(el) || new Element(el));
        return this;
    },
    
    remove: function(){
        this.parentNode.removeChild(this);
    },
    
    clone: function(contents){
        return $(this.cloneNode(contents || true));
    },
    
    replaceWith: function(el){
        var el = $(el) || new Element(el);
        this.parentNode.replaceChild(el, this);
        return el;
    },
    
    appendText: function(text){
        if (this.getTag() == 'style' && window.ActiveXObject) this.styleSheet.cssText = text;
        else this.appendChild(document.createTextNode(text));
        return this;
    },
    
    hasClass: function(className){
        return !!this.className.test("\\b"+className+"\\b");
    },
    
    addClass: function(className){
        if (!this.hasClass(className)) this.className = (this.className+' '+className.trim()).clean();
        return this;
    },
    
    removeClass: function(className){
        if (this.hasClass(className)) this.className = this.className.replace(className.trim(), '').clean();
        return this;
    },
    
    toggleClass: function(className){
        if (this.hasClass(className)) return this.removeClass(className);
        else return this.addClass(className);
    },
    
    setStyle: function(property, value){
        if (property == 'opacity') this.setOpacity(parseFloat(value));
        else this.style[property.camelCase()] = value;
        return this;
    },
    
    setStyles: function(source){
        if ($type(source) == 'object') {
            for (var property in source) this.setStyle(property, source[property]);
        } else if ($type(source) == 'string') {
            if (window.ActiveXObject) this.cssText = source;
            else this.setAttribute('style', source);
        }
        return this;
    },
    
    setOpacity: function(opacity){
        if (opacity == 0){
            if(this.style.visibility != "hidden") this.style.visibility = "hidden";
        } else {
            if(this.style.visibility != "visible") this.style.visibility = "visible";
        }
        if (window.ActiveXObject) this.style.filter = "alpha(opacity=" + opacity*100 + ")";
        this.style.opacity = opacity;
        return this;
    },
    
    getStyle: function(property){
        var proPerty = property.camelCase();
        var style = this.style[proPerty] || false;
        if (!style) {
            if (document.defaultView) style = document.defaultView.getComputedStyle(this,null).getPropertyValue(property);
            else if (this.currentStyle) style = this.currentStyle[proPerty];
        }
        if (style && ['color', 'backgroundColor', 'borderColor'].test(proPerty) && style.test('rgb')) style = style.rgbToHex();
        return style;
    },
    
    addEvent: function(action, fn){
        this[action+fn] = fn.bind(this);
        if (this.addEventListener) this.addEventListener(action, fn, false);
        else this.attachEvent('on'+action, this[action+fn]);
        var el = this;
        if (this != window) Unload.functions.push(function(){
            el.removeEvent(action, fn);
            el[action+fn] = null;
        });
        return this;
    },
    
    removeEvent: function(action, fn){
        if (this.removeEventListener) this.removeEventListener(action, fn, false);
        else this.detachEvent('on'+action, this[action+fn]);
        return this;
    },
    
    getBrother: function(what){
        var el = this[what+'Sibling'];
        while ($type(el) == 'textnode') el = el[what+'Sibling'];
        return $(el);
    },
    
    getPrevious: function(){
        return this.getBrother('previous');
    },
    
    getNext: function(){
        return this.getBrother('next');
    },
    
    getFirst: function(){
        var el = this.firstChild;
        while ($type(el) == 'textnode') el = el.nextSibling;
        return $(el);
    },
    
    getLast: function(){
        var el = this.lastChild;
        while ($type(el) == 'textnode')
            el = el.previousSibling;
        return $(el);
    },
    
    setProperty: function(property, value){
        var el = false;
        switch(property){
            case 'class': this.className = value; break;
            case 'style': this.setStyles(value); break;
            case 'name': if (window.ActiveXObject && this.getTag() == 'input'){
                el = $(document.createElement('<input name="'+value+'" />'));
                $A(this.attributes).each(function(attribute){
                    if (attribute.name != 'name') el.setProperty(attribute.name, attribute.value);
                    
                });
                if (this.parentNode) this.replaceWith(el);
            };
            default: this.setAttribute(property, value);
        }
        return el || this;
    },
    
    setProperties: function(source){
        for (var property in source) this.setProperty(property, source[property]);
        return this;
    },
    
    setHTML: function(html){
        this.innerHTML = html;
        return this;
    },
    
    getProperty: function(property){
        return this.getAttribute(property);
    },
    
    getTag: function(){
        return this.tagName.toLowerCase();
    },
    
    getOffset: function(what){
        what = what.capitalize();
        var el = this;
        var offset = 0;
        do {
            offset += el['offset'+what] || 0;
            el = el.offsetParent;
        } while (el);
        return offset;
    },
    
    getTop: function(){
        return this.getOffset('top');
    },
    
    getLeft: function(){
        return this.getOffset('left');
    },
    
    getValue: function(){
        var value = false;
        switch(this.getTag()){
            case 'select': value = this.getElementsByTagName('option')[this.selectedIndex].value; break;
            case 'input': if ( (this.checked && ['checkbox', 'radio'].test(this.type)) || (['hidden', 'text', 'password'].test(this.type)) ) 
                value = this.value; break;
            case 'textarea': value = this.value;
        }
        return value;
    }
    
});

new Object.Native(Element);

Element.extend({
    hasClassName: Element.prototype.hasClass,
    addClassName: Element.prototype.addClass,
    removeClassName: Element.prototype.removeClass,
    toggleClassName: Element.prototype.toggleClass
});

function $Element(el, method, args){
    if ($type(args) != 'array') args = [args];
    return Element.prototype[method].apply(el, args);
};

function $(el){
    if ($type(el) == 'string') el = document.getElementById(el);
    if ($type(el) == 'element'){
        if (!el.extend){
            Unload.elements.push(el);
            el.extend = Object.extend;
            el.extend(Element.prototype);
        }
        return el;
    } else return false;
};

window.addEvent = document.addEvent = Element.prototype.addEvent;
window.removeEvent = document.removeEvent = Element.prototype.removeEvent;

var Unload = {
    
    elements: [], functions: [], vars: [],
    
    unload: function(){
        Unload.functions.each(function(fn){
            fn();
        });
        
        window.removeEvent('unload', window.removeFunction);
        
        Unload.elements.each(function(el){
            for(var p in Element.prototype){
                window[p] = null;
                document[p] = null;
                el[p] = null;
            }
            el.extend = null;
        });
    }
    
};

window.removeFunction = Unload.unload;
window.addEvent('unload', window.removeFunction);
var Fx = fx = {};

Fx.Base = new Class({
    
    setOptions: function(options){
        this.options = Object.extend({
            onStart: Class.empty,
            onComplete: Class.empty,
            transition: Fx.Transitions.sineInOut,
            duration: 500,
            unit: 'px',
            wait: true,
            fps: 50
        }, options || {});
    },
    
    step: function(){
        var time = new Date().getTime();
        if (time < this.time + this.options.duration){
            this.cTime = time - this.time;
            this.setNow();
        } else {
            this.options.onComplete.pass(this.element, this).delay(10);
            this.clearTimer();
            this.callChain();
            this.now = this.to;
        }
        this.increase();
    },
    
    set: function(to){
        this.now = to;
        this.increase();
        return this;
    },
    
    setNow: function(){
        this.now = this.compute(this.from, this.to);
    },
    
    compute: function(from, to){
        return this.options.transition(this.cTime, from, (to - from), this.options.duration);
    },
    
    custom: function(from, to){
        if (!this.options.wait) this.clearTimer();
        if (this.timer) return;
        this.options.onStart.pass(this.element, this).delay(10);
        this.from = from;
        this.to = to;
        this.time = new Date().getTime();
        this.timer = this.step.periodical(Math.round(1000/this.options.fps), this);
        return this;
    },
    clearTimer: function(){
        this.timer = $clear(this.timer);
        return this;
    },
    
    setStyle: function(element, property, value){
        element.setStyle(property, value + this.options.unit);
    }
    
});

Fx.Base.implement(new Chain);

Fx.Style = Fx.Base.extend({
    
    initialize: function(el, property, options){
        this.element = $(el);
        this.setOptions(options);
        this.property = property.camelCase();
    },
    
    hide: function(){
        return this.set(0);
    },
    
    goTo: function(val){
        return this.custom(this.now || 0, val);
    },
    
    increase: function(){
        this.setStyle(this.element, this.property, this.now);
    }
    
});

Fx.Styles = Fx.Base.extend({
    
    initialize: function(el, options){
        this.element = $(el);
        this.setOptions(options);
        this.now = {};
    },
    
    setNow: function(){
        for (var p in this.from) this.now[p] = this.compute(this.from[p], this.to[p]);
    },
    
    custom: function(objFromTo){
        if (this.timer && this.options.wait) return;
        var from = {};
        var to = {};
        for (var p in objFromTo){
            from[p] = objFromTo[p][0];
            to[p] = objFromTo[p][1];
        }
        return this.parent(from, to);
    },
    
    increase: function(){
        for (var p in this.now) this.setStyle(this.element, p, this.now[p]);
    }
    
});

Element.extend({
    
    effect: function(property, options){
        return new Fx.Style(this, property, options);
    },
    
    effects: function(options){
        return new Fx.Styles(this, options);
    }
    
});

Fx.Transitions = {
    linear: function(t, b, c, d){
        return c*t/d + b;
    },
    sineInOut: function(t, b, c, d){
        return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
    }
    
};

function $S(){
    var els = [];
    $A(arguments).each(function(sel){
        if ($type(sel) == 'string') els.extend(document.getElementsBySelector(sel));
        else if ($type(sel) == 'element') els.push($(sel));
    });
    return $Elements(els);
};

var $$ = $S;

function $E(selector, filter){
    return ($(filter) || document).getElement(selector);
};

function $ES(selector, filter){
    return ($(filter) || document).getElementsBySelector(selector);
};

function $Elements(elements){
    return Object.extend(elements, new Elements);
};

Element.extend({
    
    getElements: function(selector){
        var filters = [];
        selector.clean().split(' ').each(function(sel, i){
            var bits = sel.test('^(\\w*|\\*)(?:#([\\w_-]+)|\\.([\\w_-]+))?(?:\\[["\']?(\\w+)["\']?(?:([\\*\\^\\$]?=)["\']?(\\w*)["\']?)?\\])?$');
            if (!bits) return;
            if (!bits[1]) bits[1] = '*';
            var param = bits.remove(bits[0]).associate(['tag', 'id', 'class', 'attribute', 'operator', 'value']);
            if (i == 0){
                if (param['id']){
                    var el = this.getElementById(param['id']);
                    if (!el || (param['tag'] != '*' && $(el).getTag() != param['tag'])) return false;
                    filters = [el];
                } else {
                    filters = $A(this.getElementsByTagName(param['tag']));
                }
            } else {
                if (param['id']) filters = $Elements(filters).filterById(param['id']);
                filters = $Elements(filters).filterByTagName(param['tag']);
            }
            if (param['class']) filters = $Elements(filters).filterByClassName(param['class']);
            if (param['attribute']) filters = $Elements(filters).filterByAttribute(param['attribute'], param['value'], param['operator']);
            
        }, this);
        filters.each(function(el){
            $(el);
        });
        return $Elements(filters);
    },
    
    getElement: function(selector){
        return this.getElementsBySelector(selector)[0];
    },
    
    getElementsBySelector: function(selector){
        var els = [];
        selector.split(',').each(function(sel){
            els.extend(this.getElements(sel));
        }, this);
        return $Elements(els);
    }
    
});

document.extend = Object.extend;

document.extend({
    getElementsByClassName: function(className){
        return document.getElements('.'+className);
    },
    getElement: Element.prototype.getElement,
    getElements: Element.prototype.getElements,
    getElementsBySelector: Element.prototype.getElementsBySelector
    
});

var Elements = new Class({
    
    action: function(actions){
        this.each(function(el){
            el = $(el);
            if (actions.initialize) actions.initialize.apply(el);
            for(var action in actions){
                var evt = false;
                if (action.test('^on[\\w]{1,}')) el[action] = actions[action];
                else if (evt = action.test('([\\w-]{1,})event$')) el.addEvent(evt[1], actions[action]);
            }
        });
    },
    
    //internal methods
    
    filterById: function(id){
        var found = [];
        this.each(function(el){
            if (el.id == id) found.push(el);
        });
        return found;
    },
    
    filterByClassName: function(className){
        var found = [];
        this.each(function(el){
            if ($Element(el, 'hasClass', className)) found.push(el);
        });
        return found;
    },
    
    filterByTagName: function(tagName){
        var found = [];
        this.each(function(el){
            found.extend($A(el.getElementsByTagName(tagName)));
        });
        return found;
    },
    
    filterByAttribute: function(name, value, operator){
        var found = [];
        this.each(function(el){
            var att = el.getAttribute(name);
            if(!att) return;
            if (!operator) return found.push(el);
            
            switch(operator){
                case '*=': if (att.test(value)) found.push(el); break;
                case '=': if (att == value) found.push(el); break;
                case '^=': if (att.test('^'+value)) found.push(el); break;
                case '$=': if (att.test(value+'$')) found.push(el);
            }
            
        });
        return found;
    }
    
});

new Object.Native(Elements);
var Drag = {};

Drag.Base = new Class({
    
    setOptions: function(options){
        this.options = Object.extend({
            handle: false,
            unit: 'px', 
            onStart: Class.empty, 
            onComplete: Class.empty, 
            onDrag: Class.empty,
            xMax: false,
            xMin: false,
            yMax: false,
            yMin: false
        }, options || {});
    },
    
    initialize: function(el, xModifier, yModifier, options){
        this.setOptions(options);
        this.element = $(el);
        this.handle = $(this.options.handle) || this.element;
        if (xModifier) this.xp = xModifier.camelCase();
        if (yModifier) this.yp = yModifier.camelCase();
        this.handle.onmousedown = this.start.bind(this);
    },
    
    start: function(evt){
        evt = evt || window.event;
        this.startX = evt.clientX;
        this.startY = evt.clientY;
        
        this.handleX = this.startX - this.handle.getLeft();
        this.handleY = this.startY - this.handle.getTop();
        
        this.set(evt);
        this.options.onStart.pass(this.element, this).delay(10);
        document.onmousemove = this.drag.bind(this);
        document.onmouseup = this.end.bind(this);
        return false;
    },
    
    addStyles: function(x, y){
        if (this.xp){
            var stylex = this.element.getStyle(this.xp).toInt();
            
            var movex = function(val){
                this.element.setStyle(this.xp, val+this.options.unit);
            }.bind(this);
            
            if (this.options.xMax && stylex >= this.options.xMax){
                if (this.clientX <= this.handleX+this.handle.getLeft()) movex(stylex+x);
                if (stylex > this.options.xMax) movex(this.options.xMax);
            } else if(this.options.xMin && stylex <= this.options.xMin){
                if (this.clientX >= this.handleX+this.handle.getLeft()) movex(stylex+x);
                if (stylex < this.options.xMin) movex(this.options.xMin);
            } else movex(stylex+x);
        }
        if (this.yp){
            var styley = this.element.getStyle(this.yp).toInt();
            
            var movey = function(val){
                this.element.setStyle(this.yp, val+this.options.unit);
            }.bind(this);
            
            if (this.options.yMax && styley >= this.options.yMax){
                if (this.clientY <= this.handleY+this.handle.getTop()) movey(styley+y);
                if (styley > this.options.yMax) movey(this.options.yMax);
            } else if(this.options.yMin && styley <= this.options.yMin){
                if (this.clientY >= this.handleY+this.handle.getTop()) movey(styley+y);
                if (styley < this.options.yMin) movey(this.options.yMin);
            } else movey(styley+y);
        }
    },
    
    drag: function(evt){
        evt = evt || window.event;
        this.clientX = evt.clientX;
        this.clientY = evt.clientY;
        this.options.onDrag.pass(this.element, this).delay(5);
        this.addStyles((this.clientX-this.lastMouseX), (this.clientY-this.lastMouseY));
        this.set(evt);
        return false;
    },
    
    set: function(evt){
        this.lastMouseX = evt.clientX;
        this.lastMouseY = evt.clientY;
        return false;
    },
    
    end: function(){
        document.onmousemove = null;
        document.onmouseup = null;
        this.options.onComplete.pass(this.element, this).delay(10);
    }
    
});

Drag.Move = Drag.Base.extend({
    
    extendOptions: function(options){
        this.options = Object.extend(this.options || {}, Object.extend({
            onSnap: Class.empty,
            droppables: [],
            snapDistance: 8,
            snap: true,
            xModifier: 'left',
            yModifier: 'top',
            container: false
        }, options || {}));
    },
    
    initialize: function(el, options){
        this.extendOptions(options);
        this.container = $(this.options.container);
        this.parent(el, this.options.xModifier, this.options.yModifier, this.options);
    },
    
    start: function(evt){
        if (this.options.container) {
            var cont = $(this.options.container).getPosition();
            Object.extend(this.options, {
                xMax: cont.right-this.element.offsetWidth,
                xMin: cont.left,
                yMax: cont.bottom-this.element.offsetHeight,
                yMin: cont.top
            });
        }
        this.parent(evt);
        if (this.options.snap) document.onmousemove = this.checkAndDrag.bind(this);
        return false;
    },
    
    drag: function(evt){
        this.parent(evt);
        this.options.droppables.each(function(drop){
            if (this.checkAgainst(drop)){
                if (drop.onOver && !drop.dropping) drop.onOver.pass([this.element, this], drop).delay(10);
                drop.dropping = true;
            } else {
                if (drop.onLeave && drop.dropping) drop.onLeave.pass([this.element, this], drop).delay(10);
                drop.dropping = false;
            }
        }, this);
        return false;
    },
    
    checkAndDrag: function(evt){
        evt = evt || window.event;
        var distance = Math.round(Math.sqrt(Math.pow(evt.clientX - this.startX, 2)+Math.pow(evt.clientY - this.startY, 2)));
        if (distance > this.options.snapDistance){
            this.set(evt);
            this.options.onSnap.pass(this.element, this).delay(10);
            document.onmousemove = this.drag.bind(this);
            this.addStyles(-(this.startX-evt.clientX), -(this.startY-evt.clientY));
        }
        return false;
    },
    
    checkAgainst: function(el){
        x = this.clientX+Window.getScrollLeft();
        y = this.clientY+Window.getScrollTop();
        var el = $(el).getPosition();
        return (x > el.left && x < el.right && y < el.bottom && y > el.top);
    },
    
    end: function(){
        this.parent();
        this.options.droppables.each(function(drop){
            if (drop.onDrop && this.checkAgainst(drop)) drop.onDrop.pass([this.element, this], drop).delay(10);
        }, this);
    }
    
});

Element.extend({
    
    makeDraggable: function(options){
        return new Drag.Move(this, options);
    },
    
    makeResizable: function(options){
        return new Drag.Base(this, 'width', 'height', options);
    },
    
    getPosition: function(){
        var obj = {};
        obj.width = this.offsetWidth;
        obj.height = this.offsetHeight;
        obj.left = this.getLeft();
        obj.top = this.getTop();
        obj.right = obj.left + obj.width;
        obj.bottom = obj.top + obj.height;
        return obj;
    }
    
});

var Window = {
    
    disableImageCache: function(){
        if (window.ActiveXObject) document.execCommand("BackgroundImageCache", false, true);
    },
    
    extend: Object.extend,
    
    getWidth: function(){
        return window.innerWidth || document.documentElement.clientWidth || 0;
    },
    
    getHeight: function(){
        return window.innerHeight || document.documentElement.clientHeight || 0;
    },
    
    getScrollHeight: function(){
        return document.documentElement.scrollHeight;
    },
    
    getScrollWidth: function(){
        return document.documentElement.scrollWidth;
    },
    
    getScrollTop: function(){
        return document.documentElement.scrollTop || window.pageYOffset || 0;
    },
    
    getScrollLeft: function(){
        return document.documentElement.scrollLeft || window.pageXOffset || 0;
    },
    
    onDomReady: function(init){
        var state = document.readyState;
        if (state && document.childNodes && !document.all && !navigator.taintEnabled){ //khtml
            if (state.test(/loaded|complete/)) return init();
            else return Window.onDomReady.pass(init).delay(100);
        } else if (state && window.ActiveXObject){ //ie
            var script = $('_ie_ready_');
            if (!script) document.write("<script id='_ie_ready_' defer='true' src='://'></script>");
            $('_ie_ready_').addEvent('readystatechange', function(){
                if (this.readyState == 'complete') init();
            });
            return;
        } else { //others
            var myInit = function() {
                if (arguments.callee.done) return;
                arguments.callee.done = true;
                init();
            };
            window.addEvent("load", myInit);
            document.addEvent("DOMContentLoaded", myInit);
        }
    }
    
};

Fx.Scroll = Fx.Base.extend({
    
    initialize: function(el, options) {
        this.element = $(el);
        this.setOptions(options);
    },
    
    down: function(){
        return this.custom(this.element.scrollTop, this.element.scrollHeight-this.element.offsetHeight);
    },
    
    up: function(){
        return this.custom(this.element.scrollTop, 0);
    },
    
    increase: function(){
        this.element.scrollTop = this.now;
    }
});

Fx.Slide = Fx.Base.extend({
    
    initialize: function(el, options){
        this.element = $(el);
        this.wrapper = new Element('div').injectAfter(this.element).setStyle('overflow', 'hidden').adopt(this.element);
        this.setOptions(options);
        if (!this.options.mode) this.options.mode = 'vertical';
        this.now = [];
    },
    
    setNow: function(){
        [0,1].each(function(i){
            this.now[i] = this.compute(this.from[i], this.to[i]);
        }, this);
    },
    
    vertical: function(){
        this.margin = 'top';
        this.layout = 'height';
        this.startPosition = [this.element.scrollHeight, '0'];
        this.endPosition = ['0', -this.element.scrollHeight];
        return this;
    },
    
    horizontal: function(){
        this.margin = 'left';
        this.layout = 'width';
        this.startPosition = [this.element.scrollWidth, '0'];
        this.endPosition = ['0', -this.element.scrollWidth];
        return this;
    },
    
    hide: function(){
        this[this.options.mode]();
        this.wrapper.setStyle(this.layout, '0');
        this.element.setStyle('margin-'+this.margin, -this.element['scroll'+this.layout.capitalize()]+this.options.unit);
        return this;
    },
    
    show: function(){
        this[this.options.mode]();
        this.wrapper.setStyle(this.layout, this.element['scroll'+this.layout.capitalize()]+this.options.unit);
        this.element.setStyle('margin-'+this.margin, '0');
        return this;
    },
    
    toggle: function(mode){
        this[this.options.mode]();
        if (this.wrapper['offset'+this.layout.capitalize()] > 0) return this.custom(this.startPosition, this.endPosition);
        else return this.custom(this.endPosition, this.startPosition);
    },
    
    increase: function(){	
        this.wrapper.setStyle(this.layout, this.now[0]+this.options.unit);
        this.element.setStyle('margin-'+this.margin, this.now[1]+this.options.unit);
    }
    
});

Fx.Color = Fx.Base.extend({
    
    initialize: function(el, property, options){
        this.element = $(el);
        this.setOptions(options);
        this.property = property;
        this.now = [];
    },
    
    custom: function(from, to){
        return this.parent(from.hexToRgb(true), to.hexToRgb(true));
    },
    
    setNow: function(){
        [0,1,2].each(function(i){
            this.now[i] = Math.round(this.compute(this.from[i], this.to[i]));
        }, this);
    },
    
    increase: function(){
        this.element.setStyle(this.property, "rgb("+this.now[0]+","+this.now[1]+","+this.now[2]+")");
    },
    
    fromColor: function(color){
        return this.custom(color, this.element.getStyle(this.property));
    },
    
    toColor: function(color){
        return this.custom(this.element.getStyle(this.property), color);
    }
    
});

Fx.Transitions = {
    linear: function(t, b, c, d){
        return c*t/d + b;
    },
    quadIn: function(t, b, c, d){
        return c*(t/=d)*t + b;
    },
    quadOut: function(t, b, c, d){
        return -c *(t/=d)*(t-2) + b;
    },
    quadInOut: function(t, b, c, d){
        if ((t/=d/2) < 1) return c/2*t*t + b;
        return -c/2 * ((--t)*(t-2) - 1) + b;
    },
    cubicIn: function(t, b, c, d){
        return c*(t/=d)*t*t + b;
    },
    cubicOut: function(t, b, c, d){
        return c*((t=t/d-1)*t*t + 1) + b;
    },
    cubicInOut: function(t, b, c, d){
        if ((t/=d/2) < 1) return c/2*t*t*t + b;
        return c/2*((t-=2)*t*t + 2) + b;
    },
    quartIn: function(t, b, c, d){
        return c*(t/=d)*t*t*t + b;
    },
    quartOut: function(t, b, c, d){
        return -c * ((t=t/d-1)*t*t*t - 1) + b;
    },
    quartInOut: function(t, b, c, d){
        if ((t/=d/2) < 1) return c/2*t*t*t*t + b;
        return -c/2 * ((t-=2)*t*t*t - 2) + b;
    },
    quintIn: function(t, b, c, d){
        return c*(t/=d)*t*t*t*t + b;
    },
    quintOut: function(t, b, c, d){
        return c*((t=t/d-1)*t*t*t*t + 1) + b;
    },
    quintInOut: function(t, b, c, d){
        if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b;
        return c/2*((t-=2)*t*t*t*t + 2) + b;
    },
    sineIn: function(t, b, c, d){
        return -c * Math.cos(t/d * (Math.PI/2)) + c + b;
    },
    sineOut: function(t, b, c, d){
        return c * Math.sin(t/d * (Math.PI/2)) + b;
    },
    sineInOut: function(t, b, c, d){
        return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
    },
    expoIn: function(t, b, c, d){
        return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
    },
    expoOut: function(t, b, c, d){
        return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
    },
    expoInOut: function(t, b, c, d){
        if (t==0) return b;
        if (t==d) return b+c;
        if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
        return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
    },
    circIn: function(t, b, c, d){
        return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b;
    },
    circOut: function(t, b, c, d){
        return c * Math.sqrt(1 - (t=t/d-1)*t) + b;
    },
    circInOut: function(t, b, c, d){
        if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b;
        return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b;
    },
    elasticIn: function(t, b, c, d, a, p){
        if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3; if (!a) a = 1;
        if (a < Math.abs(c)){ a=c; var s=p/4; }
        else var s = p/(2*Math.PI) * Math.asin(c/a);
        return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
    },
    elasticOut: function(t, b, c, d, a, p){
        if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3; if (!a) a = 1;
        if (a < Math.abs(c)){ a=c; var s=p/4; }
        else var s = p/(2*Math.PI) * Math.asin(c/a);
        return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b;
    },
    elasticInOut: function(t, b, c, d, a, p){
        if (t==0) return b;  if ((t/=d/2)==2) return b+c;  if (!p) p=d*(.3*1.5); if (!a) a = 1;
        if (a < Math.abs(c)){ a=c; var s=p/4; }
        else var s = p/(2*Math.PI) * Math.asin(c/a);
        if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
        return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b;
    },
    backIn: function(t, b, c, d, s){
        if (!s) s = 1.70158;
        return c*(t/=d)*t*((s+1)*t - s) + b;
    },
    backOut: function(t, b, c, d, s){
        if (!s) s = 1.70158;
        return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
    },
    backInOut: function(t, b, c, d, s){
        if (!s) s = 1.70158;
        if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
        return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
    },
    bounceIn: function(t, b, c, d){
        return c - Fx.Transitions.bounceOut(d-t, 0, c, d) + b;
    },
    bounceOut: function(t, b, c, d){
        if ((t/=d) < (1/2.75)){
            return c*(7.5625*t*t) + b;
        } else if (t < (2/2.75)){
            return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
        } else if (t < (2.5/2.75)){
            return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
        } else {
            return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
        }
    },
    bounceInOut: function(t, b, c, d){
        if (t < d/2) return Fx.Transitions.bounceIn(t*2, 0, c, d) * .5 + b;
        return Fx.Transitions.bounceOut(t*2-d, 0, c, d) * .5 + c*.5 + b;
    }
    
};

Fx.Elements = Fx.Base.extend({
    
    initialize: function(elements, options){
        this.elements = [];
        elements.each(function(el){
            this.elements.push($(el));
        }, this);
        this.setOptions(options);
        this.now = {};
    },
    
    setNow: function(){
        for (var i in this.from){
            var iFrom = this.from[i];
            var iTo = this.to[i];
            var iNow = this.now[i] = {};
            for (var p in iFrom) iNow[p] = this.compute(iFrom[p], iTo[p]);
        }
    },
    
    custom: function(objObjs){
        if (this.timer && this.options.wait) return;
        var from = {};
        var to = {};
        for (var i in objObjs){
            var iProps = objObjs[i];
            var iFrom = from[i] = {};
            var iTo = to[i] = {};
            for (var prop in iProps){
                iFrom[prop] = iProps[prop][0];
                iTo[prop] = iProps[prop][1];
            }
        }
        return this.parent(from, to);
    },
    
    increase: function(){
        for (var i in this.now){
            var iNow = this.now[i];
            for (var p in iNow) this.setStyle(this.elements[i.toInt()], p, iNow[p]);
        }
    }
    
});

Fx.Accordion = Fx.Elements.extend({
    
    extendOptions: function(options){
        Object.extend(this.options, Object.extend({
            start: 'open-first',
            fixedHeight: false,
            fixedWidth: false,
            alwaysHide: false,
            wait: false,
            onActive: Class.empty,
            onBackground: Class.empty,
            height: true,
            opacity: true,
            width: false
        }, options || {}));
    },
    
    initialize: function(togglers, elements, options){
        this.parent(elements, options);
        this.extendOptions(options);
        this.previousClick = 'nan';
        togglers.each(function(tog, i){
            $(tog).addEvent('click', function(){this.showThisHideOpen(i)}.bind(this));
        }, this);
        this.togglers = togglers;
        this.h = {}; this.w = {}; this.o = {};
        this.elements.each(function(el, i){
            this.now[i] = {};
            $(el).setStyles({'height': 0, 'overflow': 'hidden'});
        }, this);
        switch(this.options.start){
            case 'first-open': this.elements[0].setStyle('height', this.elements[0].scrollHeight+this.options.unit); break;
            case 'open-first': this.showThisHideOpen(0); break;
        }
    },
    
    hideThis: function(i){
        if (this.options.height) this.h = {'height': [this.elements[i].offsetHeight, 0]};
        if (this.options.width) this.w = {'width': [this.elements[i].offsetWidth, 0]};
        if (this.options.opacity) this.o = {'opacity': [this.now[i]['opacity'] || 1, 0]};
    },
    
    showThis: function(i){
        if (this.options.height) this.h = {'height': [this.elements[i].offsetHeight, this.options.fixedHeight || this.elements[i].scrollHeight]};
        if (this.options.width) this.w = {'width': [this.elements[i].offsetWidth, this.options.fixedWidth || this.elements[i].scrollWidth]};
        if (this.options.opacity) this.o = {'opacity': [this.now[i]['opacity'] || 0, 1]};
    },
    
    showThisHideOpen: function(iToShow){
        if (iToShow != this.previousClick || this.options.alwaysHide){
            this.previousClick = iToShow;
            var objObjs = {};
            var err = false;
            var madeInactive = false;
            this.elements.each(function(el, i){
                this.now[i] = this.now[i] || {};
                if (i != iToShow){
                    this.hideThis(i);
                } else if (this.options.alwaysHide){
                    if (el.offsetHeight == el.scrollHeight){
                        this.hideThis(i);
                        madeInactive = true;
                    } else if (el.offsetHeight == 0){
                        this.showThis(i);
                    } else {
                        err = true;
                    }
                } else if (this.options.wait && this.timer){
                    this.previousClick = 'nan';
                    err = true;
                } else {
                    this.showThis(i);
                }
                objObjs[i] = Object.extend(this.h, Object.extend(this.o, this.w));
            }, this);
            if (err) return;
            if (!madeInactive) this.options.onActive.call(this, this.togglers[iToShow], iToShow);
            this.togglers.each(function(tog, i){
                if (i != iToShow || madeInactive) this.options.onBackground.call(this, tog, i);
            }, this);
            return this.custom(objObjs);
        }
    }
    
});