function Tabber(tabs, links, options) {
    return this.init(tabs, links, options);
}

Tabber.prototype = {
    options: {
        crossfadeDuration: 500,
        autochangeInterval: 0,
        autochangeResume: false,
        autochangeDuration: 500,
        highlighter: null,
        highlighterTransition: Fx.Transitions.linear,
        cycles: null
    },
    curr: 0,
    steps: [],
    highlighter: null,
    autochanger: null,
    paused: false,

    init: function(tabs, links, options) {
        Object.extend(this.options, options);

        var slist = $(links);
        var sholder = $(tabs);
        this.steps = [];

        if (slist) {
            if (this.options.highlighter) {
                this.highlighter = $(this.options.highlighter);
                if (this.highlighter) {
                    this.highlighter._fx = new Fx.Style(this.highlighter, 'top', { duration: this.options.autochangeDuration, transition: this.options.highlighterTransition, wait: false, onComplete: this.makeActive.bind(this) });
                    this.highlighter._fx.set(0);
                }
            }

            var links = slist.select("a");
            links.each((function(link, i) {
                var tabber = this;
                var c = document.getElementById(link.rel);
                if (c) {
                    link._tab = c;
                    link._index = this.steps.length;
                    link._tab._fx = new Fx.Style(link._tab, 'opacity', { duration: this.options.crossfadeDuration, wait: false });
                    link._tab._fx.set(i == 0 ? 1 : 0);
                    this.steps.push(link);

                    link.onmouseover = function() {
                        tabber.paused = true;
                        if (!tabber.options.autochangeResume && tabber.autochanger) {
                            tabber.autochanger.stop()
                            tabber.autochanger = null;
                        }
                        tabber.xfade.bind(tabber)(this._index);
                        tabber.highlight(this._index);
                        return false;
                    };
                    link.onmouseout = function() {
                        tabber.paused = !tabber.options.autochangeResume;
                    };
                }
            }).bind(this));

            if (this.steps.length > 0 && this.options.autochangeInterval > 0)
                this.autochanger = new PeriodicalExecuter(this.cycle.bind(this), this.options.autochangeInterval);
        }

        if (sholder) sholder.className = sholder.className.replace("loader", "");
    },

    cycle: function() {
        if (!this.paused) {
            var next = (this.curr + 1) % this.steps.length;
            this.xfade(next);
            this.highlight(next, true);
            if (this.options.cycles && next == 0) {
                if (--this.options.cycles == 0) {
                    this.paused = true;
                    if (this.autochanger) {
                        this.autochanger.stop();
                        this.autochanger = null;
                    }
                }
            }
        }
    },

    highlight: function(index, animate) {
        for (i = 0; i < this.steps.length; i++)
            this.steps[i].parentNode.className = this.steps[i].parentNode.className.replace("active", "");

        if (this.highlighter) {
            if (animate) {
                this.highlighter._fx.start((index * 42));
            } else {
                this.highlighter._fx.set((index * 42));
                this.makeActive();
            }
        } else {
            this.makeActive();
        }
    },

    makeActive: function() {
        this.steps[this.curr].parentNode.className += " active";
    },

    xfade: function(next) {
        var curr = this.curr;
        var steps = this.steps;

        if (next != curr) {
            if (steps[next]._tab.src != steps[curr]._tab.src) {
                steps[next]._tab._fx.start(1);
                steps[curr]._tab._fx.start(0);
            } else {
                steps[next]._tab._fx.set(1);
                steps[curr]._tab._fx.set(0);
            }
            this.curr = next;
        }
    }
}
