//MooTools More, <http://mootools.net/more>. Copyright (c) 2006-2008 Valerio Proietti, <http://mad4milk.net>, MIT Style License.
Fx.Slide = new Class({
    Extends: Fx,
    options: {
        mode: "vertical"
    },
    initialize: function(B, A) {
        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 = $(B);
        this.parent(A);
        var C = this.element.retrieve("wrapper");
        this.wrapper = C || new Element("div", {
            styles: $extend(this.element.getStyles("margin", "position"), {
                overflow: "hidden"
            })
        }).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(A) {
        this.element.setStyle(this.margin, A[0]);
        this.wrapper.setStyle(this.layout, A[1]);
        return this;
    },
    compute: function(E, D, C) {
        var B = [];
        var A = 2;
        A.times(function(F) {
            B[F] = Fx.compute(E[F], D[F], C);
        });
        return B;
    },
    start: function(B, E) {
        if (!this.check(arguments.callee, B, E)) {
            return this;
        }
        this[E || this.options.mode]();
        var D = this.element.getStyle(this.margin).toInt();
        var C = this.wrapper.getStyle(this.layout).toInt();
        var A = [[D, C], [0, this.offset]];
        var G = [[D, C], [ - this.offset, 0]];
        var F;
        switch (B) {
        case "in":
            F = A;
            break;
        case "out":
            F = G;
            break;
        case "toggle":
            F = (this.wrapper["offset" + this.layout.capitalize()] == 0) ? A: G;
        }
        return this.parent(F[0], F[1]);
    },
    slideIn: function(A) {
        return this.start("in", A);
    },
    slideOut: function(A) {
        return this.start("out", A);
    },
    hide: function(A) {
        this[A || this.options.mode]();
        this.open = false;
        return this.set([ - this.offset, 0]);
    },
    show: function(A) {
        this[A || this.options.mode]();
        this.open = true;
        return this.set([0, this.offset]);
    },
    toggle: function(A) {
        return this.start("toggle", A);
    }
});
Element.Properties.slide = {
    set: function(B) {
        var A = this.retrieve("slide");
        if (A) {
            A.cancel();
        }
        return this.eliminate("slide").store("slide:options", $extend({
            link: "cancel"
        },
        B));
    },
    get: function(A) {
        if (A || !this.retrieve("slide")) {
            if (A || !this.retrieve("slide:options")) {
                this.set("slide", A);
            }
            this.store("slide", new Fx.Slide(this, this.retrieve("slide:options")));
        }
        return this.retrieve("slide");
    }
};
Element.implement({
    slide: function(D, E) {
        D = D || "toggle";
        var B = this.get("slide"),
        A;
        switch (D) {
        case "hide":
            B.hide(E);
            break;
        case "show":
            B.show(E);
            break;
        case "toggle":
            var C = this.retrieve("slide:flag", B.open);
            B[(C) ? "slideOut": "slideIn"](E);
            this.store("slide:flag", !C);
            A = true;
            break;
        default:
            B.start(D, E);
        }
        if (!A) {
            this.eliminate("slide:flag");
        }
        return this;
    }
});
Fx.Scroll = new Class({
    Extends: Fx,
    options: {
        offset: {
            x: 0,
            y: 0
        },
        wheelStops: true
    },
    initialize: function(B, A) {
        this.element = this.subject = $(B);
        this.parent(A);
        var D = this.cancel.bind(this, false);
        if ($type(this.element) != "element") {
            this.element = $(this.element.getDocument().body);
        }
        var C = this.element;
        if (this.options.wheelStops) {
            this.addEvent("start",
            function() {
                C.addEvent("mousewheel", D);
            },
            true);
            this.addEvent("complete",
            function() {
                C.removeEvent("mousewheel", D);
            },
            true);
        }
    },
    set: function() {
        var A = Array.flatten(arguments);
        this.element.scrollTo(A[0], A[1]);
    },
    compute: function(E, D, C) {
        var B = [];
        var A = 2;
        A.times(function(F) {
            B.push(Fx.compute(E[F], D[F], C));
        });
        return B;
    },
    start: function(C, H) {
        if (!this.check(arguments.callee, C, H)) {
            return this;
        }
        var E = this.element.getSize(),
        F = this.element.getScrollSize();
        var B = this.element.getScroll(),
        D = {
            x: C,
            y: H
        };
        for (var G in D) {
            var A = F[G] - E[G];
            if ($chk(D[G])) {
                D[G] = ($type(D[G]) == "number") ? D[G].limit(0, A) : A;
            } else {
                D[G] = B[G];
            }
            D[G] += this.options.offset[G];
        }
        return this.parent([B.x, B.y], [D.x, D.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(B) {
        var A = $(B).getPosition(this.element);
        return this.start(A.x, A.y);
    }
});
Fx.Elements = new Class({
    Extends: Fx.CSS,
    initialize: function(B, A) {
        this.elements = this.subject = $$(B);
        this.parent(A);
    },
    compute: function(G, H, I) {
        var C = {};
        for (var D in G) {
            var A = G[D],
            E = H[D],
            F = C[D] = {};
            for (var B in A) {
                F[B] = this.parent(A[B], E[B], I);
            }
        }
        return C;
    },
    set: function(B) {
        for (var C in B) {
            var A = B[C];
            for (var D in A) {
                this.render(this.elements[C], D, A[D], this.options.unit);
            }
        }
        return this;
    },
    start: function(C) {
        if (!this.check(arguments.callee, C)) {
            return this;
        }
        var H = {},
        I = {};
        for (var D in C) {
            var F = C[D],
            A = H[D] = {},
            G = I[D] = {};
            for (var B in F) {
                var E = this.prepare(this.elements[D], B, F[B]);
                A[B] = E.from;
                G[B] = E.to;
            }
        }
        return this.parent(H, I);
    }
});
var Drag = new Class({
    Implements: [Events, Options],
    options: {
        snap: 6,
        unit: "px",
        grid: false,
        style: true,
        limit: false,
        handle: false,
        invert: false,
        preventDefault: false,
        modifiers: {
            x: "left",
            y: "top"
        }
    },
    initialize: function() {
        var B = Array.link(arguments, {
            options: Object.type,
            element: $defined
        });
        this.element = $(B.element);
        this.document = this.element.getDocument();
        this.setOptions(B.options || {});
        var A = $type(this.options.handle);
        this.handles = (A == "array" || A == "collection") ? $$(this.options.handle) : $(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(C) {
        if (this.options.preventDefault) {
            C.preventDefault();
        }
        this.fireEvent("beforeStart", this.element);
        this.mouse.start = C.page;
        var A = this.options.limit;
        this.limit = {
            x: [],
            y: []
        };
        for (var D in this.options.modifiers) {
            if (!this.options.modifiers[D]) {
                continue;
            }
            if (this.options.style) {
                this.value.now[D] = this.element.getStyle(this.options.modifiers[D]).toInt();
            } else {
                this.value.now[D] = this.element[this.options.modifiers[D]];
            }
            if (this.options.invert) {
                this.value.now[D] *= -1;
            }
            this.mouse.pos[D] = C.page[D] - this.value.now[D];
            if (A && A[D]) {
                for (var B = 2; B--; B) {
                    if ($chk(A[D][B])) {
                        this.limit[D][B] = $lambda(A[D][B])();
                    }
                }
            }
        }
        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(A) {
        if (this.options.preventDefault) {
            A.preventDefault();
        }
        var B = Math.round(Math.sqrt(Math.pow(A.page.x - this.mouse.start.x, 2) + Math.pow(A.page.y - this.mouse.start.y, 2)));
        if (B > this.options.snap) {
            this.cancel();
            this.document.addEvents({
                mousemove: this.bound.drag,
                mouseup: this.bound.stop
            });
            this.fireEvent("start", this.element).fireEvent("snap", this.element);
        }
    },
    drag: function(A) {
        if (this.options.preventDefault) {
            A.preventDefault();
        }
        this.mouse.now = A.page;
        for (var B in this.options.modifiers) {
            if (!this.options.modifiers[B]) {
                continue;
            }
            this.value.now[B] = this.mouse.now[B] - this.mouse.pos[B];
            if (this.options.invert) {
                this.value.now[B] *= -1;
            }
            if (this.options.limit && this.limit[B]) {
                if ($chk(this.limit[B][1]) && (this.value.now[B] > this.limit[B][1])) {
                    this.value.now[B] = this.limit[B][1];
                } else {
                    if ($chk(this.limit[B][0]) && (this.value.now[B] < this.limit[B][0])) {
                        this.value.now[B] = this.limit[B][0];
                    }
                }
            }
            if (this.options.grid[B]) {
                this.value.now[B] -= (this.value.now[B] % this.options.grid[B]);
            }
            if (this.options.style) {
                this.element.setStyle(this.options.modifiers[B], this.value.now[B] + this.options.unit);
            } else {
                this.element[this.options.modifiers[B]] = this.value.now[B];
            }
        }
        this.fireEvent("drag", this.element);
    },
    cancel: function(A) {
        this.document.removeEvent("mousemove", this.bound.check);
        this.document.removeEvent("mouseup", this.bound.cancel);
        if (A) {
            this.document.removeEvent(this.selection, this.bound.eventStop);
            this.fireEvent("cancel", this.element);
        }
    },
    stop: function(A) {
        this.document.removeEvent(this.selection, this.bound.eventStop);
        this.document.removeEvent("mousemove", this.bound.drag);
        this.document.removeEvent("mouseup", this.bound.stop);
        if (A) {
            this.fireEvent("complete", this.element);
        }
    }
});
Element.implement({
    makeResizable: function(A) {
        return new Drag(this, $merge({
            modifiers: {
                x: "width",
                y: "height"
            }
        },
        A));
    }
});
Drag.Move = new Class({
    Extends: Drag,
    options: {
        droppables: [],
        container: false
    },
    initialize: function(C, B) {
        this.parent(C, B);
        this.droppables = $$(this.options.droppables);
        this.container = $(this.options.container);
        if (this.container && $type(this.container) != "element") {
            this.container = $(this.container.getDocument().body);
        }
        C = this.element;
        var D = C.getStyle("position");
        var A = (D != "static") ? D: "absolute";
        if (C.getStyle("left") == "auto" || C.getStyle("top") == "auto") {
            C.position(C.getPosition(C.offsetParent));
        }
        C.setStyle("position", A);
        this.addEvent("start",
        function() {
            this.checkDroppables();
        },
        true);
    },
    start: function(B) {
        if (this.container) {
            var D = this.element,
            J = this.container,
            E = J.getCoordinates(D.offsetParent),
            F = {},
            A = {};
            ["top", "right", "bottom", "left"].each(function(K) {
                F[K] = J.getStyle("padding-" + K).toInt();
                A[K] = D.getStyle("margin-" + K).toInt();
            },
            this);
            var C = D.offsetWidth + A.left + A.right,
            I = D.offsetHeight + A.top + A.bottom;
            var H = [E.left + F.left, E.right - F.right - C];
            var G = [E.top + F.top, E.bottom - F.bottom - I];
            this.options.limit = {
                x: H,
                y: G
            };
        }
        this.parent(B);
    },
    checkAgainst: function(B) {
        B = B.getCoordinates();
        var A = this.mouse.now;
        return (A.x > B.left && A.x < B.right && A.y < B.bottom && A.y > B.top);
    },
    checkDroppables: function() {
        var A = this.droppables.filter(this.checkAgainst, this).getLast();
        if (this.overed != A) {
            if (this.overed) {
                this.fireEvent("leave", [this.element, this.overed]);
            }
            if (A) {
                this.overed = A;
                this.fireEvent("enter", [this.element, A]);
            } else {
                this.overed = null;
            }
        }
    },
    drag: function(A) {
        this.parent(A);
        if (this.droppables.length) {
            this.checkDroppables();
        }
    },
    stop: function(A) {
        this.checkDroppables();
        this.fireEvent("drop", [this.element, this.overed]);
        this.overed = null;
        return this.parent(A);
    }
});
Element.implement({
    makeDraggable: function(A) {
        return new Drag.Move(this, A);
    }
});
Hash.Cookie = new Class({
    Extends: Cookie,
    options: {
        autoSave: true
    },
    initialize: function(B, A) {
        this.parent(B, A);
        this.load();
    },
    save: function() {
        var A = JSON.encode(this.hash);
        if (!A || A.length > 4096) {
            return false;
        }
        if (A == "{}") {
            this.dispose();
        } else {
            this.write(A);
        }
        return true;
    },
    load: function() {
        this.hash = new Hash(JSON.decode(this.read(), true));
        return this;
    }
});
Hash.Cookie.implement((function() {
    var A = {};
    Hash.each(Hash.prototype,
    function(C, B) {
        A[B] = function() {
            var D = C.apply(this.hash, arguments);
            if (this.options.autoSave) {
                this.save();
            }
            return D;
        };
    });
    return A;
})());
var Color = new Native({
    initialize: function(B, C) {
        if (arguments.length >= 3) {
            C = "rgb";
            B = Array.slice(arguments, 0, 3);
        } else {
            if (typeof B == "string") {
                if (B.match(/rgb/)) {
                    B = B.rgbToHex().hexToRgb(true);
                } else {
                    if (B.match(/hsb/)) {
                        B = B.hsbToRgb();
                    } else {
                        B = B.hexToRgb(true);
                    }
                }
            }
        }
        C = C || "rgb";
        switch (C) {
        case "hsb":
            var A = B;
            B = B.hsbToRgb();
            B.hsb = A;
            break;
        case "hex":
            B = B.hexToRgb(true);
            break;
        }
        B.rgb = B.slice(0, 3);
        B.hsb = B.hsb || B.rgbToHsb();
        B.hex = B.rgbToHex();
        return $extend(B, this);
    }
});
Color.implement({
    mix: function() {
        var A = Array.slice(arguments);
        var C = ($type(A.getLast()) == "number") ? A.pop() : 50;
        var B = this.slice();
        A.each(function(D) {
            D = new Color(D);
            for (var E = 0; E < 3; E++) {
                B[E] = Math.round((B[E] / 100 * (100 - C)) + (D[E] / 100 * C));
            }
        });
        return new Color(B, "rgb");
    },
    invert: function() {
        return new Color(this.map(function(A) {
            return 255 - A;
        }));
    },
    setHue: function(A) {
        return new Color([A, this.hsb[1], this.hsb[2]], "hsb");
    },
    setSaturation: function(A) {
        return new Color([this.hsb[0], A, this.hsb[2]], "hsb");
    },
    setBrightness: function(A) {
        return new Color([this.hsb[0], this.hsb[1], A], "hsb");
    }
});
function $RGB(C, B, A) {
    return new Color([C, B, A], "rgb");
}
function $HSB(C, B, A) {
    return new Color([C, B, A], "hsb");
}
function $HEX(A) {
    return new Color(A, "hex");
}
Array.implement({
    rgbToHsb: function() {
        var B = this[0],
        C = this[1],
        J = this[2];
        var G,
        F,
        H;
        var I = Math.max(B, C, J),
        E = Math.min(B, C, J);
        var K = I - E;
        H = I / 255;
        F = (I != 0) ? K / I: 0;
        if (F == 0) {
            G = 0;
        } else {
            var D = (I - B) / K;
            var A = (I - C) / K;
            var L = (I - J) / K;
            if (B == I) {
                G = L - A;
            } else {
                if (C == I) {
                    G = 2 + D - L;
                } else {
                    G = 4 + A - D;
                }
            }
            G /= 6;
            if (G < 0) {
                G++;
            }
        }
        return [Math.round(G * 360), Math.round(F * 100), Math.round(H * 100)];
    },
    hsbToRgb: function() {
        var C = Math.round(this[2] / 100 * 255);
        if (this[1] == 0) {
            return [C, C, C];
        } else {
            var A = this[0] % 360;
            var E = A % 60;
            var F = Math.round((this[2] * (100 - this[1])) / 10000 * 255);
            var D = Math.round((this[2] * (6000 - this[1] * E)) / 600000 * 255);
            var B = Math.round((this[2] * (6000 - this[1] * (60 - E))) / 600000 * 255);
            switch (Math.floor(A / 60)) {
            case 0:
                return [C, B, F];
            case 1:
                return [D, C, F];
            case 2:
                return [F, C, B];
            case 3:
                return [F, D, C];
            case 4:
                return [B, F, C];
            case 5:
                return [C, F, D];
            }
        }
        return false;
    }
});
String.implement({
    rgbToHsb: function() {
        var A = this.match(/\d{1,3}/g);
        return (A) ? hsb.rgbToHsb() : null;
    },
    hsbToRgb: function() {
        var A = this.match(/\d{1,3}/g);
        return (A) ? A.hsbToRgb() : null;
    }
});
var Group = new Class({
    initialize: function() {
        this.instances = Array.flatten(arguments);
        this.events = {};
        this.checker = {};
    },
    addEvent: function(B, A) {
        this.checker[B] = this.checker[B] || {};
        this.events[B] = this.events[B] || [];
        if (this.events[B].contains(A)) {
            return false;
        } else {
            this.events[B].push(A);
        }
        this.instances.each(function(C, D) {
            C.addEvent(B, this.check.bind(this, [B, C, D]));
        },
        this);
        return this;
    },
    check: function(C, A, B) {
        this.checker[C][B] = true;
        var D = this.instances.every(function(F, E) {
            return this.checker[C][E] || false;
        },
        this);
        if (!D) {
            return;
        }
        this.checker[C] = {};
        this.events[C].each(function(E) {
            E.call(this, this.instances, A);
        },
        this);
    }
});
var Asset = new Hash({
    javascript: function(F, D) {
        D = $extend({
            onload: $empty,
            document: document,
            check: $lambda(true)
        },
        D);
        var B = new Element("script", {
            src: F,
            type: "text/javascript"
        });
        var E = D.onload.bind(B),
        A = D.check,
        G = D.document;
        delete D.onload;
        delete D.check;
        delete D.document;
        B.addEvents({
            load: E,
            readystatechange: function() {
                if (["loaded", "complete"].contains(this.readyState)) {
                    E();
                }
            }
        }).setProperties(D);
        if (Browser.Engine.webkit419) {
            var C = (function() {
                if (!$try(A)) {
                    return;
                }
                $clear(C);
                E();
            }).periodical(50);
        }
        return B.inject(G.head);
    },
    css: function(B, A) {
        return new Element("link", $merge({
            rel: "stylesheet",
            media: "screen",
            type: "text/css",
            href: B
        },
        A)).inject(document.head);
    },
    image: function(C, B) {
        B = $merge({
            onload: $empty,
            onabort: $empty,
            onerror: $empty
        },
        B);
        var D = new Image();
        var A = $(D) || new Element("img"); ["load", "abort", "error"].each(function(E) {
            var F = "on" + E;
            var G = B[F];
            delete B[F];
            D[F] = function() {
                if (!D) {
                    return;
                }
                if (!A.parentNode) {
                    A.width = D.width;
                    A.height = D.height;
                }
                D = D.onload = D.onabort = D.onerror = null;
                G.delay(1, A, A);
                A.fireEvent(E, A, 1);
            };
        });
        D.src = A.src = C;
        if (D && D.complete) {
            D.onload.delay(1);
        }
        return A.setProperties(B);
    },
    images: function(D, C) {
        C = $merge({
            onComplete: $empty,
            onProgress: $empty
        },
        C);
        if (!D.push) {
            D = [D];
        }
        var A = [];
        var B = 0;
        D.each(function(F) {
            var E = new Asset.image(F, {
                onload: function() {
                    C.onProgress.call(this, B, D.indexOf(F));
                    B++;
                    if (B == D.length) {
                        C.onComplete();
                    }
                }
            });
            A.push(E);
        });
        return new Elements(A);
    }
});
var Sortables = new Class({
    Implements: [Events, Options],
    options: {
        snap: 4,
        opacity: 1,
        clone: false,
        revert: false,
        handle: false,
        constrain: false
    },
    initialize: function(A, B) {
        this.setOptions(B);
        this.elements = [];
        this.lists = [];
        this.idle = true;
        this.addLists($$($(A) || A));
        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(A) {
            this.elements.push(A);
            var B = A.retrieve("sortables:start", this.start.bindWithEvent(this, A)); (this.options.handle ? A.getElement(this.options.handle) || A: A).addEvent("mousedown", B);
        },
        this);
        return this;
    },
    addLists: function() {
        Array.flatten(arguments).each(function(A) {
            this.lists.push(A);
            this.addItems(A.getChildren());
        },
        this);
        return this;
    },
    removeItems: function() {
        var A = [];
        Array.flatten(arguments).each(function(B) {
            A.push(B);
            this.elements.erase(B);
            var C = B.retrieve("sortables:start"); (this.options.handle ? B.getElement(this.options.handle) || B: B).removeEvent("mousedown", C);
        },
        this);
        return $$(A);
    },
    removeLists: function() {
        var A = [];
        Array.flatten(arguments).each(function(B) {
            A.push(B);
            this.lists.erase(B);
            this.removeItems(B.getChildren());
        },
        this);
        return $$(A);
    },
    getClone: function(B, A) {
        if (!this.options.clone) {
            return new Element("div").inject(document.body);
        }
        if ($type(this.options.clone) == "function") {
            return this.options.clone.call(this, B, A, this.list);
        }
        return A.clone(true).setStyles({
            margin: "0px",
            position: "absolute",
            visibility: "hidden",
            width: A.getStyle("width")
        }).inject(this.list).position(A.getPosition(A.getOffsetParent()));
    },
    getDroppables: function() {
        var A = this.list.getChildren();
        if (!this.options.constrain) {
            A = this.lists.concat(A).erase(this.list);
        }
        return A.erase(this.clone).erase(this.element);
    },
    insert: function(C, B) {
        var A = "inside";
        if (this.lists.contains(B)) {
            this.list = B;
            this.drag.droppables = this.getDroppables();
        } else {
            A = this.element.getAllPrevious().contains(B) ? "before": "after";
        }
        this.element.inject(B, A);
        this.fireEvent("sort", [this.element, this.clone]);
    },
    start: function(B, A) {
        if (!this.idle) {
            return;
        }
        this.idle = false;
        this.element = A;
        this.opacity = A.get("opacity");
        this.list = A.getParent();
        this.clone = this.getClone(B, A);
        this.drag = new Drag.Move(this.clone, {
            snap: this.options.snap,
            container: this.options.constrain && this.element.getParent(),
            droppables: this.getDroppables(),
            onSnap: function() {
                B.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(B);
    },
    end: function() {
        this.drag.detach();
        this.element.set("opacity", this.opacity);
        if (this.effect) {
            var A = this.element.getStyles("width", "height");
            var B = this.clone.computePosition(this.element.getPosition(this.clone.offsetParent));
            this.effect.element = this.clone;
            this.effect.start({
                top: B.top,
                left: B.left,
                width: A.width,
                height: A.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 C = Array.link(arguments, {
            modifier: Function.type,
            index: $defined
        });
        var B = this.lists.map(function(D) {
            return D.getChildren().map(C.modifier ||
            function(E) {
                return E.get("id");
            },
            this);
        },
        this);
        var A = C.index;
        if (this.lists.length == 1) {
            A = 0;
        }
        return $chk(A) && A >= 0 && A < this.lists.length ? B[A] : B;
    }
});
var Tips = new Class({
    Implements: [Events, Options],
    options: {
        onShow: function(A) {
            A.setStyle("visibility", "visible");
        },
        onHide: function(A) {
            A.setStyle("visibility", "hidden");
        },
        showDelay: 100,
        hideDelay: 100,
        className: null,
        offsets: {
            x: 16,
            y: 16
        },
        fixed: false
    },
    initialize: function() {
        var C = Array.link(arguments, {
            options: Object.type,
            elements: $defined
        });
        this.setOptions(C.options || null);
        this.tip = new Element("div").inject(document.body);
        if (this.options.className) {
            this.tip.addClass(this.options.className);
        }
        var B = new Element("div", {
            "class": "tip-top"
        }).inject(this.tip);
        this.container = new Element("div", {
            "class": "tip"
        }).inject(this.tip);
        var A = new Element("div", {
            "class": "tip-bottom"
        }).inject(this.tip);
        this.tip.setStyles({
            position: "absolute",
            top: 0,
            left: 0,
            visibility: "hidden"
        });
        if (C.elements) {
            this.attach(C.elements);
        }
    },
    attach: function(A) {
        $$(A).each(function(D) {
            var G = D.retrieve("tip:title", D.get("title"));
            var F = D.retrieve("tip:text", D.get("rel") || D.get("href"));
            var E = D.retrieve("tip:enter", this.elementEnter.bindWithEvent(this, D));
            var C = D.retrieve("tip:leave", this.elementLeave.bindWithEvent(this, D));
            D.addEvents({
                mouseenter: E,
                mouseleave: C
            });
            if (!this.options.fixed) {
                var B = D.retrieve("tip:move", this.elementMove.bindWithEvent(this, D));
                D.addEvent("mousemove", B);
            }
            D.store("tip:native", D.get("title"));
            D.erase("title");
        },
        this);
        return this;
    },
    detach: function(A) {
        $$(A).each(function(C) {
            C.removeEvent("mouseenter", C.retrieve("tip:enter") || $empty);
            C.removeEvent("mouseleave", C.retrieve("tip:leave") || $empty);
            C.removeEvent("mousemove", C.retrieve("tip:move") || $empty);
            C.eliminate("tip:enter").eliminate("tip:leave").eliminate("tip:move");
            var B = C.retrieve("tip:native");
            if (B) {
                C.set("title", B);
            }
        });
        return this;
    },
    elementEnter: function(B, A) {
        $A(this.container.childNodes).each(Element.dispose);
        var D = A.retrieve("tip:title");
        if (D) {
            this.titleElement = new Element("div", {
                "class": "tip-title"
            }).inject(this.container);
            this.fill(this.titleElement, D);
        }
        var C = A.retrieve("tip:text");
        if (C) {
            this.textElement = new Element("div", {
                "class": "tip-text"
            }).inject(this.container);
            this.fill(this.textElement, C);
        }
        this.timer = $clear(this.timer);
        this.timer = this.show.delay(this.options.showDelay, this);
        this.position((!this.options.fixed) ? B: {
            page: A.getPosition()
        });
    },
    elementLeave: function(A) {
        $clear(this.timer);
        this.timer = this.hide.delay(this.options.hideDelay, this);
    },
    elementMove: function(A) {
        this.position(A);
    },
    position: function(D) {
        var B = window.getSize(),
        A = window.getScroll();
        var E = {
            x: this.tip.offsetWidth,
            y: this.tip.offsetHeight
        };
        var C = {
            x: "left",
            y: "top"
        };
        for (var F in C) {
            var G = D.page[F] + this.options.offsets[F];
            if ((G + E[F] - A[F]) > B[F]) {
                G = D.page[F] - this.options.offsets[F] - E[F];
            }
            this.tip.setStyle(C[F], G);
        }
    },
    fill: function(A, B) { (typeof B == "string") ? A.set("html", B) : A.adopt(B);
    },
    show: function() {
        this.fireEvent("show", this.tip);
    },
    hide: function() {
        this.fireEvent("hide", this.tip);
    }
});
var SmoothScroll = new Class({
    Extends: Fx.Scroll,
    initialize: function(B, C) {
        C = C || document;
        var E = C.getDocument(),
        D = C.getWindow();
        this.parent(E, B);
        this.links = (this.options.links) ? $$(this.options.links) : $$(E.links);
        var A = D.location.href.match(/^[^#]*/)[0] + "#";
        this.links.each(function(G) {
            if (G.href.indexOf(A) != 0) {
                return;
            }
            var F = G.href.substr(A.length);
            if (F && $(F)) {
                this.useLink(G, F);
            }
        },
        this);
        if (!Browser.Engine.webkit419) {
            this.addEvent("complete",
            function() {
                D.location.hash = this.anchor;
            },
            true);
        }
    },
    useLink: function(B, A) {
        B.addEvent("click",
        function(C) {
            this.anchor = A;
            this.toElement(A);
            C.stop();
        }.bind(this));
    }
});
var Slider = new Class({
    Implements: [Events, Options],
    options: {
        onTick: function(A) {
            if (this.options.snap) {
                A = this.toPosition(this.step);
            }
            this.knob.setStyle(this.property, A);
        },
        snap: false,
        offset: 0,
        range: false,
        wheel: false,
        steps: 100,
        mode: "horizontal"
    },
    initialize: function(E, A, D) {
        this.setOptions(D);
        this.element = $(E);
        this.knob = $(A);
        this.previousChange = this.previousEnd = this.step = -1;
        this.element.addEvent("mousedown", this.clickedElement.bind(this));
        if (this.options.wheel) {
            this.element.addEvent("mousewheel", this.scrolledElement.bindWithEvent(this));
        }
        var F,
        B = {},
        C = {
            x: false,
            y: false
        };
        switch (this.options.mode) {
        case "vertical":
            this.axis = "y";
            this.property = "top";
            F = "offsetHeight";
            break;
        case "horizontal":
            this.axis = "x";
            this.property = "left";
            F = "offsetWidth";
        }
        this.half = this.knob[F] / 2;
        this.full = this.element[F] - this.knob[F] + (this.options.offset * 2);
        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.offset);
        C[this.axis] = this.property;
        B[this.axis] = [ - this.options.offset, this.full - this.options.offset];
        this.drag = new Drag(this.knob, {
            snap: 0,
            limit: B,
            modifiers: C,
            onDrag: this.draggedKnob.bind(this),
            onStart: this.draggedKnob.bind(this),
            onComplete: function() {
                this.draggedKnob();
                this.end();
            }.bind(this)
        });
        if (this.options.snap) {
            this.drag.options.grid = Math.ceil(this.stepWidth);
            this.drag.options.limit[this.axis][1] = this.full;
        }
    },
    set: function(A) {
        if (! ((this.range > 0) ^ (A < this.min))) {
            A = this.min;
        }
        if (! ((this.range > 0) ^ (A > this.max))) {
            A = this.max;
        }
        this.step = Math.round(A);
        this.checkStep();
        this.end();
        this.fireEvent("tick", this.toPosition(this.step));
        return this;
    },
    clickedElement: function(C) {
        var B = this.range < 0 ? -1: 1;
        var A = C.page[this.axis] - this.element.getPosition()[this.axis] - this.half;
        A = A.limit( - this.options.offset, this.full - this.options.offset);
        this.step = Math.round(this.min + B * this.toStep(A));
        this.checkStep();
        this.end();
        this.fireEvent("tick", A);
    },
    scrolledElement: function(A) {
        var B = (this.options.mode == "horizontal") ? (A.wheel < 0) : (A.wheel > 0);
        this.set(B ? this.step - this.stepSize: this.step + this.stepSize);
        A.stop();
    },
    draggedKnob: function() {
        var B = this.range < 0 ? -1: 1;
        var A = this.drag.value.now[this.axis];
        A = A.limit( - this.options.offset, this.full - this.options.offset);
        this.step = Math.round(this.min + B * this.toStep(A));
        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(A) {
        var B = (A + this.options.offset) * this.stepSize / this.full * this.steps;
        return this.options.steps ? Math.round(B -= B % this.stepSize) : B;
    },
    toPosition: function(A) {
        return (this.full * Math.abs(this.min - A)) / (this.steps * this.stepSize) - this.options.offset;
    }
});
var Scroller = new Class({
    Implements: [Events, Options],
    options: {
        area: 20,
        velocity: 1,
        onChange: function(A, B) {
            this.element.scrollTo(A, B);
        }
    },
    initialize: function(B, A) {
        this.setOptions(A);
        this.element = $(B);
        this.listener = ($type(this.element) != "element") ? $(this.element.getDocument().body) : this.element;
        this.timer = null;
        this.coord = this.getCoords.bind(this);
    },
    start: function() {
        this.listener.addEvent("mousemove", this.coord);
    },
    stop: function() {
        this.listener.removeEvent("mousemove", this.coord);
        this.timer = $clear(this.timer);
    },
    getCoords: function(A) {
        this.page = (this.listener.get("tag") == "body") ? A.client: A.page;
        if (!this.timer) {
            this.timer = this.scroll.periodical(50, this);
        }
    },
    scroll: function() {
        var B = this.element.getSize(),
        A = this.element.getScroll(),
        E = this.element.getPosition(),
        D = {
            x: 0,
            y: 0
        };
        for (var C in this.page) {
            if (this.page[C] < (this.options.area + E[C]) && A[C] != 0) {
                D[C] = (this.page[C] - this.options.area - E[C]) * this.options.velocity;
            } else {
                if (this.page[C] + this.options.area > (B[C] + E[C]) && B[C] + B[C] != A[C]) {
                    D[C] = (this.page[C] - B[C] + this.options.area - E[C]) * this.options.velocity;
                }
            }
        }
        if (D.y || D.x) {
            this.fireEvent("change", [A.x + D.x, A.y + D.y]);
        }
    }
});
var Accordion = new Class({
    Extends: Fx.Elements,
    options: {
        display: 0,
        show: false,
        height: true,
        width: false,
        opacity: true,
        fixedHeight: false,
        fixedWidth: false,
        wait: false,
        alwaysHide: false
    },
    initialize: function() {
        var C = Array.link(arguments, {
            container: Element.type,
            options: Object.type,
            togglers: $defined,
            elements: $defined
        });
        this.parent(C.elements, C.options);
        this.togglers = $$(C.togglers);
        this.container = $(C.container);
        this.previous = -1;
        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 B = 0, A = this.togglers.length; B < A; B++) {
            this.addSection(this.togglers[B], this.elements[B]);
        }
        this.elements.each(function(E, D) {
            if (this.options.show === D) {
                this.fireEvent("active", [this.togglers[D], E]);
            } else {
                for (var F in this.effects) {
                    E.setStyle(F, 0);
                }
            }
        },
        this);
        if ($chk(this.options.display)) {
            this.display(this.options.display);
        }
    },
    addSection: function(E, C, G) {
        E = $(E);
        C = $(C);
        var F = this.togglers.contains(E);
        var B = this.togglers.length;
        this.togglers.include(E);
        this.elements.include(C);
        if (B && (!F || G)) {
            G = $pick(G, B - 1);
            E.inject(this.togglers[G], "before");
            C.inject(E, "after");
        } else {
            if (this.container && !F) {
                E.inject(this.container);
                C.inject(this.container);
            }
        }
        var A = this.togglers.indexOf(E);
        E.addEvent("click", this.display.bind(this, A));
        if (this.options.height) {
            C.setStyles({
                "padding-top": 0,
                "border-top": "none",
                "padding-bottom": 0,
                "border-bottom": "none"
            });
        }
        if (this.options.width) {
            C.setStyles({
                "padding-left": 0,
                "border-left": "none",
                "padding-right": 0,
                "border-right": "none"
            });
        }
        C.fullOpacity = 1;
        if (this.options.fixedWidth) {
            C.fullWidth = this.options.fixedWidth;
        }
        if (this.options.fixedHeight) {
            C.fullHeight = this.options.fixedHeight;
        }
        C.setStyle("overflow", "hidden");
        if (!F) {
            for (var D in this.effects) {
                C.setStyle(D, 0);
            }
        }
        return this;
    },
    display: function(A) {
        A = ($type(A) == "element") ? this.elements.indexOf(A) : A;
        if ((this.timer && this.options.wait) || (A === this.previous && !this.options.alwaysHide)) {
            return this;
        }
        this.previous = A;
        var B = {};
        this.elements.each(function(E, D) {
            B[D] = {};
            var C = (D != A) || (this.options.alwaysHide && (E.offsetHeight > 0));
            this.fireEvent(C ? "background": "active", [this.togglers[D], E]);
            for (var F in this.effects) {
                B[D][F] = C ? 0: E[this.effects[F]];
            }
        },
        this);
        return this.start(B);
    }
});