if (typeof AJS == "undefined" && typeof YAHOO != "undefined") {
    var AJS = (function () {
        var bindings = {click: {}},
            initFunctions = [],
            included = [],
            isInitialised = false;
        return {
            params: {},
            /**
            * Returns an HTMLElement reference.
            * @method $
            * @param {String | HTMLElement |Array} el Accepts a string to use as an ID for getting a DOM reference, an actual DOM reference, or an Array of IDs and/or HTMLElements.
            * @return {HTMLElement | Array} A DOM reference to an HTML element or an array of HTMLElements.
            */
            $: YAHOO.util.Dom.get,
            /**
            * Returns a array of HTMLElements with the given class.
            * For optimized performance, include a root node when possible.
            * @method $$
            * @param {String} className The class name to match against
            * @param {String | HTMLElement} root (optional) The HTMLElement or an ID to use as the starting point 
            * @param {Function} apply (optional) A function to apply to each element when found 
            * @return {Array} An array of elements that have the given class name
            */
            $$: function (className, root, apply) {
                return YAHOO.util.Dom.getElementsByClassName(className, null, root, apply);
            },
            /**
            * Returns a array of HTMLElements that pass the test applied by supplied boolean method.
            * For optimized performance, include a root node when possible.
            * @method $$$
            * @param {Function} method - A boolean method for testing elements which receives the element as its only argument.
            * @param {String | HTMLElement} root (optional) The HTMLElement or an ID to use as the starting point 
            * @param {Function} apply (optional) A function to apply to each element when found 
            * @return {Array} Array of HTMLElements
            */
            $$$: function (method, root, apply) {
                return YAHOO.util.Dom.getElementsBy(method, null, root, apply);
            },
            Event: YAHOO.util.Event,
            Dom: YAHOO.util.Dom,
            include: function (url) {
                if (!included.contains(url)) {
                    included.push(url);
                    var s = document.createElement("script");
                    s.src = url;
                    document.getElementsByTagName("body")[0].appendChild(s);
                }
            },
            /**
            * Shortcut function to toggle class name of an element.
            * @method toggleClassName
            * @param {String | HTMLElement} element The HTMLElement or an ID to toggle class name on.
            * @param {String} className The class name to remove or add.
            */
            toggleClassName: function (element, className) {
                if (!(element = this.$(element))) return;
                if (this.Dom.hasClass(element, className))
                    this.Dom.removeClass(element, className);
                else
                    this.Dom.addClass(element, className);
            },
            /**
            * Runs functions from list (@see toInit) and attach binded funtions (@see bind)
            * @method init
            */
            init: function (){
                var params = this.$("parameters");
                if (params) {
                    var inputs = params.getElementsByTagName("input");
                    for (var i = 0, ii = inputs.length; i < ii; i++) {
                        var value = inputs[i].value;
                        this.params[inputs[i].id] = (value.match(/^(tru|fals)e$/i)?value.toLowerCase() == "true":value);
                    }
                }
                isInitialised = true;
                AJS.initFunctions = initFunctions;
                for (var i = 0, ii = initFunctions.length; i < ii; i++) {
                    if (typeof initFunctions[i] == "function") {
                        initFunctions[i]();
                    }
                }
                var all = document.getElementsByTagName("*");
                for (handler in bindings) {
                    for (className in bindings[handler]) {
                        if (className.charAt(0) == "#") {
                            for (var j = 0, jj = bindings[handler][className].length; j < jj; j++) {
                                this.Event.addListener(this.$(className.substring(1)), handler, bindings[handler][className][j]);
                            }
                            delete bindings[handler][className];
                        } else {
                            var ajs = this;
                            for (var j = 0, jj = bindings[handler][className].length; j < jj; j++) {
                                this.$$(className, null, function (el) {
                                    ajs.Event.addListener(el, handler, bindings[handler][className][j]);
                                });
                            }
                            delete bindings[handler][className];
                        }
                    }
                }
            },
            /**
            * Adds functions to the list of methods to be run on initialisation.
            * @method toInit
            * @param {Function} func Function to be call on initialisation.
            * @return AJS object.
            */
            toInit: function (func) {
                initFunctions.push(func);
                return this;
            },
            /**
            * Utility method returns scrolling position.
            * @method getScrollPosition
            * @return object with left and top properties presenting scrolling offset.
            */
            getScrollPosition: function () {
                return {left:this.Dom.getDocumentScrollLeft(), top:this.Dom.getDocumentScrollTop()};
            },
            /**
            * Binds given function to some object or set of objects as event handlers by class name or id.
            * @method bind
            * @param {String} reference Element or name of the element class. Put "#" in the beginning od the string to use it as id.
            * @param {String} handlerName (optional) Name of the event i.e. "click", "mouseover", etc.
            * @param {Function} func Function to be attached.
            * @return AJS object.
            */
            bind: function () {
                if (arguments.length < 2) {
                    throw new Error("Not enough parameters for bind function");
                    return false;
                }
                var reference = arguments[0],
                    handlerName = "click",
                    func = arguments[arguments.length - 1];
                if (arguments.length > 2) {
                    handlerName = arguments[1];
                }
                if (typeof func != "function") {
                    throw new Error("Can not bind non function object");
                    return false;
                }
                var handler = (typeof handlerName == "string")?handlerName.toLowerCase():"click";
                if (isInitialised) {
                    if (typeof reference == "object") {
                        AJS.Event.addListener(reference, handler, func);
                    } else {
                        if (reference.charAt(0) == "#") {
                            AJS.Event.addListener(AJS.$(reference.substring(1)), handler, func);
                        } else {
                            AJS.$$(reference, null, function (el) {
                                AJS.Event.addListener(el, handler, func);
                            });
                        }
                    }
                } else {
                    if (typeof bindings[handler] != "object") {
                        bindings[handler] = {};
                        bindings[handler][reference] = [];
                    }
                    if (typeof bindings[handler][reference] != "object") {
                        bindings[handler][reference] = [func];
                    } else {
                        bindings[handler][reference].push(func);
                    }
                }
                return this;
            }
        };
    })();

    AJS.Event.onDOMReady(function () {AJS.init();});


    /**
    * Utility method for global Array object. Finds the index of an element in the array.
    * @method indexOf
    * @param item Array element which will be searched.
    * @param fromIndex (optional) the index from which the item will be searched. Negative values will search from the
    * end of the array.
    * @return a zero based index of the element.
    */
    Array.prototype.indexOf = function (item, fromIndex) {
        var length = this.length;
        if (fromIndex == null) {
          fromIndex = 0;
        } else {
            if (fromIndex < 0) {
              fromIndex = Math.max(0, length + fromIndex);
            }
        }
        for (var i = fromIndex; i < length; i++) {
          if (this[i] === item) return i;
        }
        return -1;
    };

    /**
    * Utility method for global Array object. Looks for an element inside the array.
    * @method contains
    * @param item Array element which will be searched.
    * @return {Boolean} Is element in array.
    */
    Array.prototype.contains = function (item) {
        return this.indexOf(item) > -1;
    };

    /**
    * Utility method for global Array object. Removes an element from the array.
    * @method remove
    * @param item Array element which will be removed.
    * @return An element which was removed.
    */
    Array.prototype.remove = function (item) {
        var index = this.indexOf(item);
        if (index > -1) {
            this.splice(index, 1);
        }
        return item;
    };

    /**
    * Utility method for global Array object. Runs a function across each element in the array.
    * @method each
    * @param {Function} fn function to run.
    * @param {Array | variable} param (optional) Additional parameters for function.
    * @param {Object} scope (optional) The scope of function.
    * @return An element which was removed.
    */
    Array.prototype.each = function (fn, param, scope)
    {
        if (typeof fn == "function") {
            for (var i = 0, ii = this.length; i < ii; i++) {
                var newParam = [this[i], i];
                if (typeof param == "object" && param instanceof Array) {
                    newParam = param.splice(0, 0, this[i], i);
                } else {
                    newParam.push(param);
                }
                if (scope != null) {
                    fn.apply(scope, newParam);
                } else {
                    fn.apply(window, newParam);
                }
            }
        } else {
            throw new Error("Array.each - first parameter not a function");
        }
    };
}

// ==================
// = Drop-down menu =
// ==================
AJS.toInit(function () {
    AJS.$$("ajs-menu-bar", document, function (el) {
        AJS.$$("ajs-menu-item", el, function (it) {
            var dd = AJS.$$("ajs-drop-down", it);
            if (dd.length) {
                var a = AJS.$$("trigger", it);
                dd = dd[0];
                dd.hidden = true;
                dd.focused = -1;
                dd.hide = function () {
                    AJS.toggleClassName(it, "opened");
                    var as = this.getElementsByTagName("a");
                    AJS.toggleClassName(this, "hidden");
                    this.hidden = true;
                    AJS.Event.removeListener(document, "click", this.hide);
                    AJS.Event.removeListener(document, "keydown", this.movefocus);
                    AJS.Event.removeListener(document, "keypress", this.blocker);
                    if (this.focused + 1) {
                        AJS.Dom.removeClass(as[this.focused], "active");
                    }
                    this.focused = -1;
                };
                dd.show = function () {
                    AJS.toggleClassName(this, "hidden");
                    AJS.toggleClassName(it, "opened");
                    this.hidden = false;
                    var dd = this;
                    this.timer = setTimeout(function () {AJS.Event.addListener(document, "click", dd.hide, dd, true);}, 1);
                    AJS.Event.addListener(document, "keydown", this.movefocus, this, true);
                    AJS.Event.addListener(document, "keypress", this.blocker, this, true);
                    var as = dd.getElementsByTagName("a");
                    for (var i = 0, ii = as.length; i < ii; i++) {
                        AJS.Event.addListener(as[i], "mouseover", (function (j) {
                            return function () {
                                if (this.parentNode.parentNode.focused + 1) {
                                    AJS.Dom.removeClass(as[this.parentNode.parentNode.focused].parentNode, "active");
                                }
                                AJS.Dom.addClass(this.parentNode, "active");
                                this.parentNode.parentNode.focused = j;
                            };
                        })(i));
                        AJS.Event.addListener(as[i], "mouseout", function () {
                                if (this.parentNode.parentNode.focused + 1) {
                                    AJS.Dom.removeClass(as[this.parentNode.parentNode.focused].parentNode, "active");
                                }
                                this.parentNode.parentNode.focused = -1;
                            });
                    }
                };
                dd.blocker = function (e) {
                    var c = AJS.Event.getCharCode(e);
                    if (c == 40 || c == 38) {
                        AJS.Event.stopEvent(e);
                    }
                };
                dd.movefocus = function (e) {
                    var c = AJS.Event.getCharCode(e),
                        a = this.getElementsByTagName("a");
                    if (this.focused + 1) {
                        AJS.Dom.removeClass(a[this.focused].parentNode, "active");
                    }
                    switch (c) {
                        case 40:
                        case 9: {
                            this.focused++;
                            break;
                        }
                        case 38: {
                            this.focused--;
                            break;
                        }
                        case 27: {
                            this.hide();
                            return false;
                        }
                        default: {
                            return true;
                        }
                    }
                    if (this.focused < 0) {
                        this.focused = a.length - 1;
                    }
                    if (this.focused > a.length - 1) {
                        this.focused = 0;
                    }
                    a[this.focused].focus();
                    AJS.Dom.addClass(a[this.focused].parentNode, "active");
                    AJS.Event.stopEvent(e);
                };
                dd.show();
                clearTimeout(dd.timer);
                dd.region = AJS.Dom.getRegion(dd);
                dd.hide();
                if (dd.region.right > AJS.Dom.getViewportWidth()) {
                    var it_region = AJS.Dom.getRegion(it);
                    dd.style.marginLeft = "-" + ((dd.region.right - dd.region.left) - (it_region.right - it_region.left)) + "px";
                }
                if (a) {
                    AJS.Event.addListener(a, "click", function (e) {
                        if (dd.hidden) {
                            dd.show();
                        } else {
                            dd.hide();
                        }
                        AJS.Event.preventDefault(e);
                    });
                }
            }
        });
    });
});


// ============================
// = Search field placeholder =
// ============================
AJS.toInit(function () {
    var search = AJS.$("quick-search-query");
    // TODO fix i18n
    search.placeholder = "Search";
    search.placeholded = true;
    search.value = search.placeholder;
    AJS.toggleClassName(search, "placeholded");

    if (!(navigator.vendor && navigator.vendor.indexOf("Apple") + 1)) {
        AJS.bind("#quick-search-query", "focus", function () {
            if (this.placeholded) {
                this.placeholded = false;
                this.value = "";
                AJS.toggleClassName(this, "placeholded");
            }
        });

        AJS.bind("#quick-search-query", "blur", function () {
            if (this.placeholder && (/^\s*$/).test(this.value)) {
                this.value = this.placeholder;
                this.placeholded = true;
                AJS.toggleClassName(this, "placeholded");
            }
        });
    } else {
        search.type = "search";
        search.setAttribute("results", 10);
        search.setAttribute("placeholder", "Search");
        search.value = "";
    }
});
