/**
 * Global namespace for all random things
 */
var Operator11 = (undefined !== Operator11) ? Operator11 : {};
var EpisodePage = (undefined !== EpisodePage) ? EpisodePage : {};

Event.KEY_SPACE = 32; // why doesn't Prototype provide this one?
Event.code = function(event) {
    return event.charCode || event.keyCode;
}

/**
 * Hijack an HTML link, do an Ajax request to its href, update the link 
 * container with the response, and do a "blue-fade" on it.
 *
 * @param Event event The event object
 * @return false We don't want it following the html link, do we?
 */
var Behaviors = {
    update: function(event) {
        var element = this.up();
        element.addClassName("busy");
        new Ajax.Updater(element, this.readAttribute("href"), {
            evalScripts: true,
            onComplete: function(request) {
                element.removeClassName("busy").addClassName("response");
                new Effect.Highlight(element, { startcolor: "#c1e8f1" });
            }
        });
        return false;
    }
};

/**
 * Substitutes an element's title by a Prototip
 */
Prototipper = Behavior.create({
    initialize: function() {
        new Tip(this.element, this.element.title);
        this.element.title = "";
    }
});

/**
 * Activate inputs onclick or onfocus
 */
ActivatableInput = Behavior.create({
    onclick: function() { this.element.activate() },
    onfocus: function() { this.element.activate() }
});

/**
 * X-browser window scroll and viewport measurements
 */
document.viewport = {
    getScrollY: function() {
        return (document.documentElement && document.documentElement.scrollTop) 
            || (document.body && document.body.scrollTop) 
            || (window.pageYOffset) 
            || (window.scrollY);
    },
    getHeight: function() {
        return window.innerHeight || document.documentElement.clientHeight;
    }
};
Object.extend(window, document.viewport); // backwards compatibility

/**
 * auto-sticky floating menu
 */
StickableMenu = Behavior.create({
    initialize: function() {
      // badabing!
      this.magicNumber = 36;
      
      this.maxTop = Position.cumulativeOffset(this.element)[1];
      this.nextItem = this.element.next(".main_content");
      this.maxBottom = Position.cumulativeOffset(this.nextItem)[1] + this.nextItem.getHeight() - this.element.getHeight() - this.magicNumber - 8;
      this.element.setStyle({position: 'relative', height: (this.nextItem.getHeight() - this.magicNumber) + "px"});
      Event.observe(window, 'scroll', this.onWindowScroll.bind(this));
    },
    onWindowScroll: function() {
      if (window.getScrollY() > this.maxTop) {
        this.element.addClassName('floating');
      } else {
        this.element.removeClassName('floating');
      }
      
      if (window.getScrollY() > this.maxBottom) {
        this.element.addClassName('parked');
      } else {
        this.element.removeClassName('parked');
      }
    }
});

/**
 * Kindof behavior to control paginating galleries (only in the profile now), needs
 * to go away. fast.
 */
var Paginator = {
    browseGallery: function(element) {
        var element = $(element);
        var container = element.up(".gallery-container");
        container.down("h2").addClassName("busy");
        new Ajax.Updater(container, element.href, { evalScripts: true }); 
        return false; 
    }
};

/**
 * Add a default value to a textfield when it's empty and without focus
 */
FocusTextField = Behavior.create({
    initialize: function(value) {
        this.defaultValue = value || "search";
        this.element.value = this.element.value || this.defaultValue;
        if (this.element.value == this.defaultValue)
            this.element.addClassName("default");
    },
    onfocus: function() {
        if (this.element.hasClassName("default")) {
            this.element.removeClassName("default");
            this.element.value = "";
        }
    },
    onblur: function() {
        if (!this.element.hasClassName("default") && this.element.value == "") {
            this.element.value = this.defaultValue;
            this.element.addClassName("default");
        }
    }
});

/**
 * Check availability of login name
 */
CheckLoginAvailability = Behavior.create({
    initialize: function() {
        this.container = $("check-availability");
        this.button = this.container.down("button");
        this.toggle();
        this.previousValue = null;
        new Form.Element.Observer(this.element, 0.2, this.checkValue.bind(this));
        this.button.observe("click", this.update.bind(this));

        this.container.setStyle({ visibility: "visible" });
    },
    checkValue: function(element, value) {
        if (value == this.previousValue) return;

        if (!this.button.visible()) {
            this.container.down("span") && this.container.down("span").remove();
            this.button.show();
        }
        this.toggle();
        this.previousValue = value;
    },
    update: function() {
        if (!$F(this.element)) return;

        new Ajax.Updater(this.container, CheckLoginAvailability.url, {
            insertion: "bottom",
            parameters: { login: $F(this.element) },
            onCreate: function() {
                this.button.hide();
                this.container.addClassName("busy");
            }.bind(this),
            onComplete: function() {
                this.container.removeClassName("busy");
            }.bind(this)
        });
    },
    toggle: function() {
        this[$F(this.element) ? "enable" : "disable"]();
    },
    disable: function() {
        this.button.setStyle({ opacity: 0.6, cursor: "default" });
    },
    enable: function() {
        this.button.setStyle({ opacity: 1, cursor: "pointer" });
    }
});

/**
 * Toolbar with "send by mail", embed links, flag as inappropriate and 
 * chat transcripts on the epi page.
 */
EpisodePage.Toolbar = {
    hideAll: function() {
        $$(".toolbar-pane").invoke("hide");
    }
};
EpisodePage.Toolbar.Hidden = Behavior.create({
    initialize: function() {
        Event.observe(window, "load", function() {
            this.element.removeAttribute("onclick");
            this.target = $(this.element.readAttribute("href").split(/#/).last());
        }.bind(this));
    },
    onclick: function() {
        var visible = $(this.target).visible();
        this.hideAll();
        visible || $(this.target).show();
        return false;
    },
    hideAll: EpisodePage.Toolbar.hideAll
});
EpisodePage.Toolbar.Remote = Behavior.create({
    initialize: function() {
        this.element.removeAttribute("onclick");
        this.loading = false;
        this.target = null;
    },
    onclick: function() {
        var visible = this.target && $(this.target) && $(this.target).visible();
        this.hideAll();
        visible || this.load();
        return false;
    },
    load: function() {
        if (this.target && $(this.target)) return $(this.target).show();
        if (this.loading) return;

        this.loading = true;
        this.element.addClassName("busy");
        new Ajax.Updater("toolbar", this.element.href, {
            insertion: "after",
            method: "get",
            parameters: { t: new Date().getTime() },
            evalScripts: true,
            onComplete: this.afterLoad.bind(this)
        });
    },
    afterLoad: function() {
        this.loading = false;
        this.element.removeClassName("busy");
        this.target = $("toolbar").next().id;
        $(this.target).addClassName("toolbar-pane");

        this.hideAll();
        $(this.target).show();
    },
    hideAll: EpisodePage.Toolbar.hideAll
});
EpisodePage.Toolbar.ClosePane = Behavior.create({
    onclick: function() {
        this.element.up(".toolbar-pane").hide();
        return false;
    }
});

/**
 * Gets a flash movie by name (IE object) or id (embed)
 */
var $M = (Prototype.Browser.IE)
    ? function(movieName) { return window[movieName]; } 
    : $;

/**
 * Update the live show count every 1 minute
 */
new Ajax.PeriodicalUpdater("shows-live-count", "/wheel/spin", { method: "get", frequency: (1).minute() });

/**
 * Toggle the login form
 */
Operator11.toggleLogin = (function() {
    var form, overlay;

    Event.onReady(function() {
        form = $("login-form");
        if (!form) return;

        form.insert({ bottom: new Element("div", { className: "overlay" }) });
        var children = form.immediateDescendants()
        overlay = children.pop().absolutize().setStyle({ left: 0, top: 0, width: "100%", height: "100%", zIndex: 1, background: "#83bf09", opacity: 0 });
        children.invoke("setStyle", { zIndex: 2 });
    });

    function hideForm() {
        new Effect.BlindUp(form.addClassName("animating"), { 
            duration: 0.2, 
            afterFinish: function(effect) { 
                effect.element.removeClassName("animating"); 
            } 
        });
    }

    function showForm() {
        new Effect.BlindDown(form.addClassName("animating"), { 
            duration: 0.2, 
            afterFinish: function(effect) { 
                effect.element.removeClassName("animating").focusFirstElement(); 
                highlightForm();
            } 
        });
    }

    function highlightForm() {
        new Effect.Opacity(overlay.show(), {
            duration: 0.2, to: 0.5,
            afterFinish: function(effect) { 
                new Effect.Opacity(overlay, {
                    duration: 0.2, from: 0.5, to: 0.0,
                    afterFinish: function(effect) { effect.element.hide(); }
                });
            }
        });
    }

    return function() {
        form.visible() && !form.hasClassName("animating") ? hideForm() : showForm();
        return false;
    };
})();

/**
 * Back to top link
 */
BackToTop = {
    initialize: function() {
        document.body.appendChild(
            BackToTop.link = new Element('span', {id: 'back-to-top'})
                .update("back to top")
                .setOpacity(0.6)
        );
        BackToTop.link.observe("click", BackToTop.onLinkClick);
        Event.observe(window, "scroll", BackToTop.onWindowScroll);
    },
    onLinkClick: function() {
        if (window.location.hash) {
            window.location.hash = "o11-com";
        } else {
            window.scrollTo(0, 0);
        }
    },
    onWindowScroll: function() {
        if (document.viewport.getScrollY() > (document.viewport.getHeight() * 0.8)) {
            BackToTop.link.addClassName('visible');
        } else {
            BackToTop.link.removeClassName('visible');
        }
    }
};
Event.observe(window, 'load', BackToTop.initialize);

/**
 * Delegate arrow keys and space bar to home page flash piece for navigation
 */
(function() {
    var movie, listenToEvents = true;
    
    Event.onReady(function() {
        movie = $M("billboard");
        if (!movie) return;

        Event.observe(document.documentElement, "keyup",   keyHandler("up"));
        Event.observe(document.documentElement, "keydown", keyHandler("down"));
        Event.observe(document.documentElement, "keydown", ignoreSpace);

        $$("input").invoke("observe", "focus", function() { listenToEvents = false });
        $$("input").invoke("observe", "blur",  function() { listenToEvents = true });
    });

    var keyHandler = function(direction) {
        return function(event) {
            if (!listenToEvents) return true;
            try {
                movie[mapping(event, direction)]();
            } catch(e) { return true; }
        }
    }

    var ignoreSpace = function(event) {
        if (!listenToEvents || Event.code(event) != Event.KEY_SPACE) return true;
        Event.stop(event);
        return false;
    }

    var mapping = function(event, direction) {
        var direction = direction.match(/down/i) ? "Down" : "";
        switch (Event.code(event)) {
            case Event.KEY_LEFT:  return "_previous" + direction;
            case Event.KEY_RIGHT: return "_next" + direction;
            case Event.KEY_SPACE: return "_toggle" + direction;
        }
        throw new Error("wrong key, pal");
    }
})();

Operator11.currentBrowser = (function() {
	var IE6 = false /*@cc_on || @_jscript_version < 5.7 @*/;
	if (IE6) return Object.extend("IE", { ie6: true });

    var isBrowser = function(browser) {
        if (Prototype.Browser[browser]) {
            var name = browser.toLowerCase();
            return function() { return name; };
        } else return function() { throw new Error("cuack!"); }
    }
    return Try.these(isBrowser("IE"), isBrowser("Gecko"), isBrowser("Opera"), isBrowser("WebKit"));
})();

Event.addBehavior({
    ".prototippable": Prototipper,
    "#header-search-query": FocusTextField,
    "#random_menu_thingy": StickableMenu,
    "#banner span": function() { this.setOpacity(0.85); },
    "#episode-page #toolbar .links .hidden a": EpisodePage.Toolbar.Hidden,
    "#episode-page #toolbar .links .remote a": EpisodePage.Toolbar.Remote,
    "#episode-page .toolbar-pane a.cancel":    EpisodePage.Toolbar.ClosePane,
    "input[readonly]": ActivatableInput,
    "body": function() {
        $(this).addClassName(Operator11.currentBrowser);
    }
});

/**
 * Make labels work in safari as in all other browsers
 */
if (Operator11.currentBrowser == "webkit") {
    Event.addBehavior({
        "label:click": function() {
            var element = $(label.readAttribute("for")).activate();
            if ($w("radio checkbox").include(element.type)) element.checked = true;
        }
    });
}
