/* $Id: bibtrad.js 1542 2011-09-11 18:22:08Z kstephensop $ */

/**
 * @fileOverview Functions and classes needed by all pages for the Bible in
 *     Its Traditions.
 */

/** @namespace */
var bib = bib || {};

/**
 * Location of CSS files, images, etc. Defined in bib.initializeSkinsDir(),
 * called from the window domready event.
 *
 * @type String
 */
bib.skins_dir = null;

/**
 * Reference to a real console object or a pseudo console in the browser (IE).
 * @type Object
 */
bib.console = window.console || {
    /**
     * The div element containing the IE pseudo console.
     * @type HTMLElement
     * @memberOf bib.console
     */
    console_div: null,

    /**
     * Log a message to the pseudo console in IE.
     * @param {String} text The message to log.
     * @memberOf bib.console
     */
    log: function (text) {
        if (! this.console_div) {
            this.console_div = document.createElement('div');
            this.console_div.className = 'console';
            document.getElementsByTagName('body')[0].appendChild(this.console_div);
        }
        var pre = document.createElement('pre');
        pre.className = 'console_message';
        pre.appendChild(document.createTextNode(text));
        this.console_div.appendChild(pre);
    }
};

/**
 * Information on HTML elements retrieved asynchronously. The ID of the HTML
 * element to which to attach the new element and a function to wire the new
 * element are provided in the constructor.
 *
 * @constructor
 * @param {String} parent_id The ID of the parent HTML element for the
 *     asynchronous element.
 * @param {function(HTMLElement)} wiring_function A function to peform any
 *     JavaScript initialization of the element after it is retrieved and
 *     before it is displayed.
 */
bib.AsyncElementInformation = function (parent_id, wiring_function) {
    this.parent_id = parent_id;
    this.wiring_function = wiring_function;
};

/**
 * A method to call the wiring function of an HTML element received
 * asynchronously.
 *
 * @param {HTMLElement} element The HTML element received asynchronously.
 */
bib.AsyncElementInformation.prototype.wire = function (element) {
    if (this.wiring_function) {
        this.wiring_function(element);
    }
};

/**
 * Setup the bible navigation drop-down menu.
 * @param {HTMLElement} section The HTML element which contains the navigation
 *     menu with books, chapters, or pericopes.
 */
bib.wireBibleNavigation = function (section) {
    // Make links in the navigation menu load a new menu.
    section.getElements('.async_link').each(function (link) {
        var href = link.getProperty('href');
        if (href == '#') {
            return;
        }
        link.addEvent('click', function (event) {
            event.stop();
            bib.replace_html_element_request.send({
                'url': href
            });
        });
    });
    // Make sure that if a user clicks within the sub_nav div, it stays in
    // place.
    if (section.get('id') == 'sub_nav') {
        bib.bibleNavigationShow(section);
        section.addEvent('click', function (event) {
            event.stopPropagation();
        });
        section.getElements('.async_link').each(function (link) {
            link.addEvent('click', function (event) {
                // This will also dispose of the element.
                bib.bibleNavigationHide(section);
            });
        });
    }
};

/**
 * Determine where images and CSS files are located and set bib.skins_dir to
 * this value by looking for main.css among the loaded stylesheets.
 */
bib.initializeSkinsDir = function () {
    var styleSheets = document.styleSheets,
        i, j;

    bib.skins_dir = null;
    for (i = 0; i < styleSheets.length && bib.skins_dir === null; i++) {
        bib.skins_dir = styleSheets[i].href;
        j = bib.skins_dir.lastIndexOf('/main.css');
        if (j != -1) {
            bib.skins_dir = bib.skins_dir.substring(0, j);
        } else {
            bib.skins_dir = null;
        }
    }
};

/**
 * Initialize the rollover for biblical passage searches. This must be called
 * after the skins directory has been initialized.
 */
bib.initializeBibleSearchRollover = function () {
    var rollover = $('bible_passage_go');

    if (rollover) {
        rollover.addEvents({
            'mouseenter': function (event) {
                event.stop();
                rollover.set('src', bib.skins_dir + '/right-hover.png');
            },
            'mouseleave': function (event) {
                event.stop();
                rollover.set('src', bib.skins_dir + '/right.png');
            }
        });
    }
};

/**
 * For any element with the CSS class 'overtext', create a MooTools OverText
 * object.
 */
bib.initializeOverText = function () {
    var o;
    document.getElements('.overtext').each(function (item) {
        o = new OverText(item);
    });
};

/**
 * Make biblical book navigation asynchronous.
 */
bib.initializeBibleNavigation = function () {
    var main_nav = $('main_nav');

    if (main_nav) {
        bib.wireBibleNavigation(main_nav);
        // If a user clicks anywhere outside a sub_nav div, make the div goes
        // away.
        document.addEvent('click', function (event) {
            var sub_nav = $('sub_nav');
            if (sub_nav) {
                // This will also dispose of the element
                bib.bibleNavigationHide(sub_nav);
            }
        });
    }
};

/**
 * Pin the main navigation div to the top of the window.
 */
bib.initializeMainNavigation = function () {
    var main_nav = $('main_nav'),
        main_nav_bar = $('main_nav_bar'),
        header = $('header');

    if (main_nav && main_nav_bar && header) {
        header.setStyles({
            'margin-top': main_nav_bar.getSize().y,
            background: 'transparent'
        });
        main_nav.setStyles({
            position: 'fixed',
            top: 0
        });
        main_nav.grab(new Element('div', {
            'class': 'main_nav_shadow',
            styles: {
                top: main_nav_bar.getSize().y
            }
        }), 'after');
    }
};

/**
 * Initialization common to all pages of the Bible in Its Traditions.
 */
//#mark domready
window.addEvent('domready', function () {
    bib.initializeSkinsDir();
    bib.initializeBibleSearchRollover();
    bib.initializeOverText();
    bib.initializeBibleNavigation();
    bib.initializeMainNavigation();
});

/**
 * Replace or insert element(s) on the page with new content. If the new
 * content has the same ID as existing content, the existing content will
 * be replaced with the new content. If there is an async_element_info
 * entry for the ID of the new content and it has a parent ID, add the
 * new content to the parent. If there is an async_element_info entry for
 * the ID of the new content which contains a wiring function, call the
 * wiring function.
 *
 * @param {HTMLElement[]} html An array of HTML elements to process.
 */
bib.replaceHtmlElement = function (html) {
    var new_element, old_element, element_id, async_element_info, i;
    for (i = 0; i < html.length; i++) {
        new_element = html.item(i);
        if (new_element.nodeType != 1) {
            continue;
        }
        element_id = new_element.get('id');
        if (element_id === null) {
            continue;
        }
        if (element_id == 'sub_nav') {
            bib.addBibleNavigation(new_element);
            continue;
        }
        async_element_info = bib.async_elements.get(element_id);
        old_element = $(element_id);
        if (old_element) {
            new_element.replaces(old_element);
        } else {
            // Add element in appropriate place if it is new
            if (async_element_info && async_element_info.parent_id) {
                $(async_element_info.parent_id).grab(new_element);
            }
        }
        if (async_element_info) {
            async_element_info.wire(new_element);
        }
    }
};

/**
 * A shared HTML request for retrieving elements asynchronously.
 * @type Request.HTML
 */
bib.replace_html_element_request = new Request.HTML({
    method: 'get',
    onSuccess: bib.replaceHtmlElement
});

/**
 * Add a new bible navigation menu to the window. If a navigation menu is
 * currently displayed, the new menu will replace the current menu.
 *
 * @param {HTMLElement} new_element The new navigation menu.
 */
bib.addBibleNavigation = function (new_element) {
    var element_id = 'sub_nav',
        async_element_info = bib.async_elements.get(element_id),
        old_element = $(element_id);
    if (old_element) {
        // This will also dispose of the old element.
        bib.bibleNavigationHide(old_element);
    }
    // Add element in appropriate place if it is new
    $(async_element_info.parent_id).grab(new_element);
    async_element_info.wire(new_element);
};

/**
 * Display the bible navigation menu. Does nothing in this implementation,
 * but a particular skin can redefine this function for special effects.
 * @param {HTMLElement} element The navigation menu to show.
 */
bib.bibleNavigationShow = function (element) {
};

/**
 * Remove the bible navigation menu from the document. A particular skin can
 * redefine this function for special effects.
 * @param {HTMLElement} element The navigation menu to hide.
 */
bib.bibleNavigationHide = function (element) {
    element.dispose();
};

/**
 * An object with properties corresponding to the IDs of HTML elements which
 * may be retrieved asynchronously. The value corresponding to each property
 * is a bib.AsyncElementInformation object, possibly with a parent ID and a
 * wiring function.
 *
 * @type Hash
 */
bib.async_elements = new Hash({
    sub_nav: new bib.AsyncElementInformation('main_nav', bib.wireBibleNavigation)
});

