/* ----------------------------------------------------------------

    IRSA hybrid CSS/JavaScript UI script. Features:

        - multi-level pop-up menus with hide/show/switch delay

        - menu built at load time from clean markup (nested
          unordered lists). Should degrade semi-gracefully
          on crap browsers, won't confuse web-spiders, and in theory
          could be quite accessible (the appropriate onfocus/onblur
          event handlers would need to be implemented)

        - Can be replaced with a pure CSS fallback without changing
          the markup at all (IE prior to 7 requires JScript no matter
          what since it can't deal with :hover on elements other than
          anchors). Simply uncomment the relevant section in ui.css
          (and use conditional comments to include IE workarounds).

        - if the URL of current page is found in a menu anchor, the
          anchor is marked with a configureable CSS id

        - anchors that have popped up a submenu are marked with
          a configureable CSS class.

    NOTES:

        - This script will totally destruct NS4 and IE 5.x Mac.
          Screen for incoherent browsers using the isFUBAR var.

        - This file is the editing/development version of the script.
          The production script (ui.js) is to be derived from this one
          using the Dojo toolkit JavaScript compression system:

          http://dojotoolkit.org/docs/compressor_system.html

   ---------------------------------------------------------------- */

/* ----------------------------------------------------------------
    First things first: the following most excellent event handler
    registration code is written by Dean Edwards, with tweaks from
    Tino Zijdel. (this hides IEs non standard event model from the
    rest of the script)

    For gory details, see
        http://therealcrisp.xs4all.nl/upload/addEvent_dean.html
        http://dean.edwards.name/weblog/2005/10/add-event/
   ---------------------------------------------------------------- */

function addEvent(element, type, handler)
{
    if (element.addEventListener)
        element.addEventListener(type, handler, false);
    else
    {
        if (!handler.$$guid) handler.$$guid = addEvent.guid++;
        if (!element.events) element.events = {};
        var handlers = element.events[type];
        if (!handlers)
        {
            handlers = element.events[type] = {};
            if (element['on' + type]) handlers[0] = element['on' + type];
            element['on' + type] = handleEvent;
        }

        handlers[handler.$$guid] = handler;
    }
}
addEvent.guid = 1;

function removeEvent(element, type, handler)
{
    if (element.removeEventListener)
        element.removeEventListener(type, handler, false);
    else if (element.events && element.events[type] && handler.$$guid)
        delete element.events[type][handler.$$guid];
}

function handleEvent(event)
{
    event = event || fixEvent(window.event);
    var returnValue = true;
    var handlers = this.events[event.type];

    for (var i in handlers)
    {
        if (!Object.prototype[i])
        {
            this.$$handler = handlers[i];
            if (this.$$handler(event) === false) returnValue = false;
        }
    }

    if (this.$$handler) this.$$handler = null;

    return returnValue;
}

function fixEvent(event)
{
    event.preventDefault = fixEvent.preventDefault;
    event.stopPropagation = fixEvent.stopPropagation;
    return event;
}
fixEvent.preventDefault = function()
{
    this.returnValue = false;
}
fixEvent.stopPropagation = function()
{
    this.cancelBubble = true;
}

if (!window.addEventListener)
{
    document.onreadystatechange = function()
    {
        if (window.onload && window.onload != handleEvent)
        {
            addEvent(window, 'load', window.onload);
            window.onload = handleEvent;
        }
    }
}

/* ----------------------------------------------------------------
    Utility functions/vars
   ---------------------------------------------------------------- */

// Is the browser a worthless POS?
var isFUBAR = ( !document.getElementsByTagName || !(document.getElementById || document.all) );

if (navigator.userAgent.indexOf('MSIE 5') != -1 &&
    navigator.userAgent.indexOf('Mac')    != -1)
    isFUBAR = true;

// This variable turns on positioning special cases and runs a cleanup function
// when the page unloads (necessary for IE). IE7 fixes GC problems, and hopefully
// z-index problems. Once the final version is out and is confirmed to work, it
// should be tested for non-borkedness...
var isBorked = document.all && !window.opera;


// Retrieve the object with the given id
function getRef(name)
{
    if (document.getElementById)
        return document.getElementById(name);
    else if (document.all)
        return document.all[name];
    else
        return null;
}


//  Returns an associative array with attributes:
//      left   - horizontal offset of object from page, pixels
//      top    - vertical offset of object from page, pixels
//      width  - width of object, pixels
//      height - height of object, pixels
function getPos(obj)
{
    var left   = 0;
    var top    = 0;
    var width  = 0;
    var height = 0;

    if (obj.offsetParent)
    {
        left = obj.offsetLeft;
        top  = obj.offsetTop;
        var o = obj.offsetParent;
        while (o != null)
        {
            left += o.offsetLeft;
            top  += o.offsetTop;
            o = o.offsetParent;
        }
    }
    else if (obj.x)
    {
        left = obj.x;
        top  = obj.y;
    }
    if (obj.offsetWidth)
        width = obj.offsetWidth;
    if (obj.offsetHeight)
        height = obj.offsetHeight;

    return { 'left':left, 'top':top, 'width':width, 'height':height };
}


//  Returns an associative array with attributes :
//      left   - horizontal offset of window from page, pixels
//      top    - vertical offset of window from page, pixels
//      width  - width of window, pixels
//      height - height of window, pixels
function getWinPos()
{
    var left   = 0;
    var top    = 0;
    var width  = 0;
    var height = 0;

    if (typeof(window.pageXOffset) == 'number')
    {
        left = window.pageXOffset;
        top  = window.pageYOffset;
    }
    else if (document.body && (document.body.scrollLeft || document.body.scrollTop))
    {
        left = document.body.scrollLeft;
        top  = document.body.scrollTop;
    }
    else if (document.documentElement && (document.documentElement.scrollLeft || document.documentElement.scrollTop))
    {
        left = document.documentElement.scrollLeft;
        top  = document.documentElement.scrollTop;
    }
    if (typeof(window.innerWidth) == 'number')
    {
        width = window.innerWidth;
        height = window.innerHeight;
    }
    else if (document.documentElement && (document.documentElement.clientWidth || document.documentElement.clientHeight))
    {
        width = document.documentElement.clientWidth;
        height = document.documentElement.clientHeight;
    }
    else if (document.body && (document.body.clientWidth || document.body.clientHeight))
    {
        width = document.body.clientWidth;
        height = document.body.clientHeight;
    }
    return { 'left':left, 'top':top, 'width':width, 'height':height };
}


// Adds/removes a class from the given element
function toggleClass(elt, cls, on)
{
    if (!elt || !cls)
        return;
    var n = elt.className ? elt.className : '';
    if (on)
    {
        // append the class name (if it hasn't been appended already)
        if (n.length == 0)
            elt.className = cls;
        else if (n.length < cls.length || n.substring(n.length - cls.length, n.length) != cls)
            elt.className += ' ' + cls;
    }
    else
    {
        // remove a trailing occurence of the class name
        if (n.length >= cls.length && n.substring(n.length - cls.length, n.length) == cls)
            elt.className = n.substring(0, n.length - cls.length);
    }
}


/* ----------------------------------------------------------------
    Menu implementation
   ---------------------------------------------------------------- */

function MenuMgr(rootId)
{
    this.mgrId       = rootId;  // ID of menu container
    this.showDelay   = 0;       // Delay before a menu appears (milliseconds)
    this.switchDelay = 0;       // Delay before a child menu is replaced by another (milliseconds)
    this.hideDelay   = 0;       // Delay before a menu subtree disappears (milliseconds)
    this.openClass   = null;    // Class name to assign to anchors that have opened a menu
    this.yahId       = null;    // ID to give to the anchor with href identical to the current URL
    this.menus       = [];      // array of menu nodes

    // maintains a mapping between MenuMgr and id's for lookup/cleanup
    MenuMgr.instances[rootId] = this;
}

MenuMgr.instances = [];     // Create list of menu managers


function Menu(mgr, id, pid, a, ul)
{
    this.mgr    = mgr;      // menu manager
    this.timer  = null;     // timer for visibility changes
    this.parent = (pid >= 0) ? mgr.menus[pid] : null; // parent menu
    this.child  = null;     // child menu
    this.pop    = a;        // anchor which pops up this menu
    this.cont   = ul;       // container for this popup
    this.id     = id;       // menu id
    this.isopen = (id == 0);// is the menu open (visible)?
}


Menu.prototype.over = function()
{
    // Clear out a pending menu close
    clearTimeout(this.timer);
    this.timer = null;

    // WTF: an invisible node got a mouseover event
    if (this.parent && !this.isopen)
        this.open(); // show ourselves to the world!

    // ensure state of anchor that popped up this menu is open
    toggleClass(this.pop, this.mgr.openClass, true);

    if (this.todo)
    {
        var ch = this.mgr.menus[this.todo];
        clearTimeout(ch.timer);
        ch.timer = null;
        if (ch == this.child)
        {
            this.todo = null;
            return;
        }
        var t = this.child ? this.mgr.switchDelay : this.mgr.showDelay;
        if (t <= 0)
            ch.open();
        else
            ch.timer = setTimeout('MenuMgr.instances["'
                + this.mgr.mgrId + '"].menus["' + ch.id + '"].open()', t);
    }
    this.todo = null;
};


Menu.prototype.open = function()
{
    // top-level menus are always open
    if (!this.cont || !this.parent)
        return;

    // if parent has a different menu open, close it
    if (this.parent.child && this.parent.child != this)
        this.parent.child.close();
    this.parent.child = this;

    // ensure state of anchor that popped up this menu is open
    toggleClass(this.pop, this.mgr.openClass, true);

    if (!this.isopen)
    {
        var m, li, lp, wp, top, left, width, height;

        // compute appropriate position, taking screen into account.
        // The menu ul is absolutely positioned relative to the containing
        // block (established by a relatively positioned li). So, get the li
        li = this.pop.parentNode;
        while (li && li.nodeName != 'LI')
            li = li.parentNode;
        if (!li)
            return;

        // Get position of li and the window.
        lp = getPos(li);
        wp = getWinPos();

        // standard positioning is top: 0, left: lp.width
        m = this.cont;
        width = m.offsetWidth ? m.offsetWidth : 0;
        height = m.offsetHeight ? m.offsetHeight : 0;

        // A 25pixel pad is included in both top/left computations since some browsers
        // don't include scroll bars width/height in their window width computation.

        // Compute value of left: try to avoid overflowing right window edge,
        // never overflow left edge.
        //
        // On borked browsers (IE) we overflow the screen rather than overlapping
        // popups because despite assigning proper z-indexes, the underlying popup
        // shows through (text, border) and popup on top somehow "leaks" events
        // to the one underneath...
        left = lp.left + lp.width + width - wp.left - wp.width;
        if (isBorked || left <= -25)
            left = lp.width;
        else
        {
            left = lp.width - left - 25;
            if (lp.left + left <= wp.left)
                left = wp.left - lp.left + 25;
        }

        // Compute value of top: try to avoid overflowing bottom window edge,
        // never overflow top edge.
        top = wp.top + wp.height - lp.top - height;
        if (top >= 25)
            top = 0;
        else
        {
            top -= 25;
            if (top < wp.top - lp.top)
                top = wp.top - lp.top + 25;
        }

        m.style.top = top + 'px';
        m.style.left = left + 'px';
        m.style.visibility = 'visible';
        this.isopen = true;
    }
};


Menu.prototype.out = function()
{
    clearTimeout(this.timer);
    this.timer = null;

    if (this.parent) // don't hide top level menu
    {
        if (this.mgr.hideDelay <= 0)
            this.close();
        else
            this.timer = setTimeout(
                'MenuMgr.instances["' + this.mgr.mgrId + '"].menus["' + this.id + '"].close()',
                this.mgr.hideDelay
            );
    }
};


Menu.prototype.close = function()
{
    // set state of our popup anchor to closed
    toggleClass(this.pop, this.mgr.openClass, false);

    // close all children
    if (this.child)
        this.child.close(); // recurse
    if (this.parent && this.parent.child == this)
        this.parent.child = null;
    clearTimeout(this.timer);
    if (this.parent && this.cont && this.isopen)
    {
        // hide this menu (so long as it isn't the mother of all menus)
        this.cont.style.visibility = 'hidden';
        this.isopen = false;
    }
};


// Mouse handlers for menus
function overMenu(event)
{
    MenuMgr.instances[this.menuMgrId].menus[this.menuId].over();
}

function outMenu(event)
{
    MenuMgr.instances[this.menuMgrId].menus[this.menuId].out();
}

// Mouse handlers for popup anchors
function overLink(event)
{
    var mgr = MenuMgr.instances[this.menuMgrId];
    if (mgr)
    {
        // mark the popup anchor open
        toggleClass(this, mgr.openClass, true);
        var m = mgr.menus[this.menuPid];
        if (m)
        {
            // The mouseover event bubbles up the containment heirarchy to the
            // parent menu (which will schedule the menu in its todo var for
            // display). All we need to do is tell it which menu to schedule.
            m.todo = this.menuId;
        }
    }
}

function outLink(event)
{
    var mgr = MenuMgr.instances[this.menuMgrId];
    if (mgr)
    {
        mgr.menus[this.menuId].out();
        // mark the popup anchor closed
        toggleClass(this, mgr.openClass, false);
    }
}

// Mouse handler for menu links - when a click is detected all menus are hidden.
// This is more or less because if one clicks on a menu link Opera keeps the menu
// open (it's visible when one navigates with the back button).
function clickLinkOrMenu()
{
    var mgr = MenuMgr.instances[this.menuMgrId];
    // close the root menu (which will close all open chilren)
    if (mgr && mgr.menus[0])
        mgr.menus[0].close();
    // Now ok to jump to whatever link was clicked on...
    return true;
}

// Builds the internal representation of the menu by walking the DOM
MenuMgr.prototype.buildMenu = function()
{
    var o = getRef(this.mgrId);
    if (!o)
        return;

    // create the root menu node
    this.menus[0] = new Menu(this, 0, -1, null, o);
    // and attach root mouse handlers
    addEvent(o, 'mouseover', overMenu);
    addEvent(o, 'mouseout', outMenu);
    addEvent(o, 'click', clickLinkOrMenu);
    o.menuMgrId = this.mgrId;
    o.menuId = 0;

    // obtain a list of child ul elements
    var elts, ul, li, pul, a, id, i, j;
    elts = o.getElementsByTagName('UL');
    i = id = 0;
    while (i < elts.length)
    {
        ul = elts[i];
        ++i;

        // Search upwards for the li containing this ul
        li = ul.parentNode;
        while (li && li.nodeName != 'LI') li = li.parentNode;
        if (!li)
            continue;   // there isn't one

        // Find the first anchor in the li
        a = null;
        j = li.childNodes.length;
        while (j > 0)
        {
            --j;
            if (li.childNodes[j].nodeName == 'A')
            {
                a = li.childNodes[j];
                break;
            }
        }
        if (!a)
            continue;  // ul cannot be popped up

        // find the parent ul of the li (if any)
        pul = li.parentNode;
        while (pul && pul.nodeName != 'UL' && pul.id != this.mgrId)
            pul = pul.parentNode;
        // Equip the list and anchor with some knowledge
        // about who they really are in the scheme of things
        a.menuMgrId = ul.menuMgrId = this.mgrId;
        ++id;
        a.menuId = ul.menuId = id;
        a.menuPid = (pul && typeof(pul.menuId) == 'number') ? pul.menuId : 0;
        ul.menuLvl = (pul && typeof(pul.menuLvl) == 'number') ? pul.menuLvl + 100 : 0;
        ul.style.zIndex = 100 + ul.menuLvl;

        // Create a menu object
        this.menus[id] = new Menu(this, id, a.menuPid, a, ul);

        // Equip the list and anchor with mouse handlers
        addEvent(ul, 'mouseover', overMenu);
        addEvent(ul, 'mouseout', outMenu);
        addEvent(ul, 'click', clickLinkOrMenu);
        addEvent(a, 'mouseover', overLink);
        addEvent(a, 'mouseout', outLink);
        addEvent(a, 'click', clickLinkOrMenu);
    }
    // Examine the URL's in the menu
    if (this.yahId)
    {
        elts = o.getElementsByTagName('a');
        for (i = 0; i < elts.length; ++i)
        {
            // does it point to the current page?
            if (elts[i].href == location.href)
            {
                // yes: give the anchor a special id
                elts[i].id = this.yahId;
                break;
            }
        }
    }
};


// Iterate over all MenuMgr objects, removing event handlers
// and DOM node references and just generally nulling out as
// much as possible
function cleanupMenus()
{
    var i, n, o, ob, mg, elts;
    for (n in MenuMgr.instances)
    {
        mg = MenuMgr.instances[n];
        if (!mg)
            continue;
        ob = getRef(mg.mgrId);
        removeEvent(ob, 'mouseover', overMenu);
        removeEvent(ob, 'mouseout', outMenu);
        removeEvent(ob, 'click', clickLinkOrMenu);
        elts = ob.getElementsByTagName('ul');
        for (i = 0; i < elts.length; ++i)
        {
            o = elts[i];
            removeEvent(o, 'mouseover', overMenu);
            removeEvent(o, 'mouseout', outMenu);
            removeEvent(o, 'click', clickLinkOrMenu);
            o.menuId = o.menuMgr = null;
        }
        elts = ob.getElementsByTagName('a');
        for (i = 0; i < elts.length; ++i)
        {
            o = elts[i];
            removeEvent(o, 'mouseover', overLink);
            removeEvent(o, 'mouseout', outLink);
            removeEvent(o, 'click', clickLinkOrMenu);
            o.menuPid = o.menuId = o.menuMgr = null;
        }
        for (i = 0; i < mg.menus.length; ++i)
        {
            o = mg.menus[i];
            o.id = o.menu = o.pop = o.child = o.parent = o.timer = o.mgr = null;
        }
        mg.menus.length = 0;
        mg.mgrId = null;
        MenuMgr.instances[n] = null;
    }
    MenuMgr.instances.length = 0;
}


// Make sure the cleanup function gets called when the window is unloaded
// (IE has some memory leaks). IMPORTANT: don't call this function for Opera,
// because Opera won't fire a load event when the user hits the back button.
// Since an unload event is fired when the user clicks away from the page, the
// script will have destructed itself and will effectively be missing until a
// page refresh occurs. Opera/Mozilla have working GC so it should be OK to skip
// cleanup...
if (isBorked)
    addEvent(window, 'unload', cleanupMenus);

/* ----------------------------------------------------------------
    Praise be the Lord! We're done!
   ---------------------------------------------------------------- */

function setCutoutsRadio(whichradio)
{
  myradios = document.forms[0].outtype;
  len = myradios.length;
  for (var i = 0; i < len; i++)
  {
    if (myradios[i].value == whichradio)
    {
    myradios[i].checked = true;
    }
  }
}
