// {{{ docs <-- this is a VIM (text editor) text fold

/**
 * DOM Tooltip 0.5.1
 *
 * Summary: Allows developers to add custom tooltips to the webpages.  Tooltips
 *          are controlled through three style class definitions.  This library
 *          also detects collisions against native widgets in the browser that
 *          cannot handle the zIndex property.  But this library is even more than
 *          that...with all the features it has, it has the potential to replace
 *          the need for popups entirely as it can embed just about any html inside
 *          the tooltip, leading to the possibility of having whole forms or iframes
 *          right inside the tip...even other programs!!!
 *
 * Maintainer: Dan Allen <dan@mojavelinux.com>
 *
 * License: LGPL - however, if you use this library, please post to my forum where you
 *          use it so that I get a chance to see my baby in action.  If you are doing
 *          this for commercial work perhaps you could send me a few Starbucks Coffee
 *          gift dollars to encourage future developement (NOT REQUIRED).  E-mail me
 *          for and address.
 *
 * Homepage: http://www.mojavelinux.com/forum/viewtopic.php?t=127
 *
 * Freshmeat Project: http://freshmeat.net/projects/domtt/?topic_id=92
 *
 * Updated: 2002/12/19
 *
 * Supported Browsers: Mozilla (Gecko), IE 5.5+, Konqueror, Opera 7
 *
 * Usage: All this is required is to put the function call in the event tag
 *        for an html element. The status option (for changing the status bar
 *        text) is only available through all events, but when used with 'onmouseover'
 *        you have to return true so that the browser does not display the link text
 *        in the status bar.  To do this, wrap the domTT_activate call in the function
 *        domTT_true(), which will just return true, and then prefix it with a 'return'
 *
 * Example: <a href="index.html" onmouseover="return domTT_true(domTT_activate(this, event, 'caption', 'Help', 'content', 'This is a link with a tooltip', 'status', 'Link'));">click me</a>
 *
 * Options: Each option is followed by the value for that option.  The variable event
 *          must be the first parameter, as shown above.  The options avaiable are:
 *            predefined (optional, must be first item if used, loads default values)
 *            caption (optional)
 *            content (required)
 *            closeLink (optional, defaults to domTT_closeLink global setting variable)
 *            status (optional, if used with mouseover must wrap call in 'return domTT_true()')
 *            type (optional, defaults to 'greasy' but can be 'sticky' or 'velcro')
 *            prefix (optional, defaults to 'domTT', for changing style class)
 *            delay (optional, defaults to global delay value domTT_activateDelay)
 *            parent (optional, defaults to document.body)
 *            onClose (optional, defaults to global domTT_onClose, either 'hide' or 'remove')
**/

// }}}
// {{{ Settings (editable)

/**
 * Settings (editable)
 */
var domTT_offsetX = 0;
var domTT_offsetY = 2;
var domTT_direction = 'southeast';
var domTT_mouseHeight = 20;
var domTT_closeLink = 'X';
var domTT_screenEdgePadding = 5;
var domTT_activateDelay = 500;
var domTT_maxWidth = 600;
var domTT_dragStickyTips = true;
var domTT_useGlobalMousePosition = true;
var domTT_prefix = 'domTT';
var domTT_fade = 'neither';
var domTT_lifetime = 0;
var domTT_grid = 0;
var domTT_onClose = 'hide';

// }}}
// {{{ Global constants

/**
 * Global constants (DO NOT EDIT)
 */
var domTT_userAgent = navigator.userAgent.toLowerCase();
var domTT_isOpera = domTT_userAgent.indexOf('opera 7') != -1 ? 1 : 0;
var domTT_isKonq = domTT_userAgent.indexOf('konq') != -1 ? 1 : 0;
var domTT_isIE = !domTT_isKonq && !domTT_isOpera && domTT_userAgent.indexOf('msie 5.0') == -1 && document.all ? 1 : 0;
var domTT_isIE5 = domTT_isIE && domTT_userAgent.indexOf('msie 5.') != -1;
var domTT_isGecko = domTT_userAgent.indexOf('gecko') != -1 ? 1 : 0;
var domTT_useLibrary = domTT_isOpera || domTT_isKonq || domTT_isIE || domTT_isGecko ? 1 : 0;
var domTT_autoId = 1;
var domTT_zIndex = 100;
var domTT_scrollbarWidth = 14;
var domTT_hidePosition = '-1000px';
var domTT_cssFloat = domTT_isIE ? 'float' : 'cssFloat';
var domTT_eventTarget = domTT_isIE ? 'srcElement' : 'currentTarget';
var domTT_eventButton = domTT_isIE ? 'button' : 'which';
var domTT_eventTo = domTT_isIE ? 'toElement' : 'relatedTarget';
var domTT_styleOpacity = domTT_isIE ? 'filter' : 'MozOpacity';
var domTT_stylePointer = domTT_isIE ? 'hand' : 'pointer';
// ** bug in Opera that it can't set maxWidth to 'none' **
var domTT_styleNoMaxWidth = domTT_isOpera ? '10000px' : 'none';
var domTT_predefined = new domTT_Hash();
var domTT_selectElements;
var domTT_timeoutState = new domTT_Hash();
var domTT_activateTimeouts = new domTT_Hash();
var domTT_currentMousePosition = new domTT_Hash();
var domTT_dragMouseDown;
var domTT_dragOffsetLeft;
var domTT_dragOffsetTop;
var domTT_currentDragTarget;
var domTT_fadeInterval = domTT_isIE ? 10 : 40;
var domTT_fadeTimeouts = new domTT_Hash();

// }}}
// {{{ Global onmousemove

if (domTT_useLibrary && domTT_useGlobalMousePosition) {
    document.onmousemove = function(in_event) {
        var eventObj = domTT_isIE ? event : in_event;
        domTT_currentMousePosition = domTT_getEventPosition(eventObj);    
        domTT_dragUpdate(in_event);
    }
}

// }}}
// {{{ class domTT_Hash()

function domTT_Hash()
{
    this.length = 0;
    this.items = new Array();
    for (var i = 0; i < arguments.length; i += 2) {
        if (typeof(arguments[i + 1]) != 'undefined') {
            this.items[arguments[i]] = arguments[i + 1];
            this.length++;
        }
    }

    this.getItem = function(in_key)
    {
        return this.items[in_key];
    }

    this.removeItem = function(in_key)
    {
        var tmp_value;
        if (typeof(this.items[in_key]) != 'undefined') {
            this.length--;
            tmp_value = this.items[in_key];
            delete this.items[in_key];
        }
        
        return tmp_value;
    }

    this.setItem = function(in_key, in_value)
    {
        if (typeof(in_value) != 'undefined') {
            if (typeof(this.items[in_key]) == 'undefined') {
                this.length++;
            }
            
            return this.items[in_key] = in_value;
        }
        
        return false;
    }
    
    this.hasItem = function(in_key)
    {
        return typeof(this.items[in_key]) != 'undefined';
    }
}

// }}}
// {{{ domTT_callTimeout()

function domTT_callTimeout(in_function, in_timeout, in_args)
{
    if (!in_timeout) {
        in_function(in_args);
        return 0;
    }

    // must make a copy of the arguments so that we release the reference
    var tmp_args = new Array();
    for (var i in in_args) {
        tmp_args[i] = in_args[i];
    }

    if (!domTT_isKonq) {
        return setTimeout(function() { in_function(tmp_args); }, in_timeout);
    }
    else {
        var tmp_id = domTT_timeoutState.length;
        var tmp_data = new Array();
        tmp_data['function'] = in_function;
        tmp_data['args'] = tmp_args;
        domTT_timeoutState.setItem(tmp_id, tmp_data);

        domTT_timeoutState.items[tmp_id]['timeout'] = setTimeout("domTT_timeoutState.items[" + tmp_id + "]['function'](domTT_timeoutState.items[" + tmp_id + "]['args']); domTT_timeoutState.setItem(" + tmp_id + ", false);", in_timeout);
        return domTT_timeoutState.items[tmp_id]['timeout'];
    } 
}

// }}}
// {{{ domTT_mouseout()

function domTT_mouseout(in_triggerObj, in_this, in_event, in_tipId)
{
    if (typeof(in_this) != 'undefined') {
        var triggerObj = domTT_isIE || !in_triggerObj ? in_this : in_event.currentTarget;
        var eventRelatedTarget = domTT_isIE ? event.toElement : in_event.relatedTarget;
        var tipObj = domTT_isActive(in_tipId);
        // deactive tip if not yet active or if target we are entering is not 
        // a descendant of the trigger element
        if (!tipObj || !domTT_isDescendantOf(eventRelatedTarget, triggerObj)) {
            domTT_deactivate(in_tipId, tipObj);
        }
    }
    
    if (in_triggerObj && in_triggerObj.domTT_mouseoutSaved) {
        in_triggerObj.domTT_mouseoutRun = function(in_event) { 
            // we have to do this so that the user can use event in the handler
            if (domTT_isGecko) { 
                event = in_event; 
            } 

            in_triggerObj.domTT_mouseoutSaved(in_event);
        }

        in_triggerObj.domTT_mouseoutRun(in_event);
    }
    
    try {
        window.status = window.defaultStatus;
    }
    // no permissions
    catch(e) {}
}

// }}}
// {{{ domTT_activate()

function domTT_activate(in_this, in_event)
{
    if (!domTT_useLibrary) { return true; }

    // get the unified event object and necessary event variables
    // since IE does not support the currentTarget, we have to use the 'this' reference
    var eventObj = domTT_isIE ? event : in_event;
    var eventType = eventObj.type;
    // we have a non-mouse event, such as an onload
    if (!eventObj[domTT_eventTarget]) {
        eventType = '';
        var triggerObj = document.body;
    }
    else {
        var triggerObj = domTT_isIE ? in_this : eventObj.currentTarget;
        // make sure we have nothing higher than document.body
        if (triggerObj.nodeType == 9 || typeof(triggerObj.nodeType) == 'undefined') {
            triggerObj = document.body;
        }
    }

    // preserve the original mouseout event for this target object since we are overwriting it
    if (!triggerObj.getAttribute('domTT_mouseoutChecked') && typeof(triggerObj.domTT_mouseoutSaved) == 'undefined') {
        triggerObj.domTT_mouseoutSaved = triggerObj.onmouseout;
        triggerObj.setAttribute('domTT_mouseoutChecked', 1);
    }

    var activeTip = triggerObj.getAttribute('domTT_activeTip');

    // if the tip is already active and this is a mouseover, don't react
    if (eventType == 'mouseover' && activeTip == 'greasy') {
        return true;
    }

    // setup the options hash from the arguments
    var options = new domTT_Hash(
        'caption',    '',
        'content',    '',
        'closeLink',  domTT_closeLink,
        'parent',     document.body,
        'position',   'absolute',
        'type',       'greasy',
        'direction',  domTT_direction,
        'delay',      domTT_activateDelay,
        'prefix',     domTT_prefix,
        'onClose',    domTT_onClose,
        'lifetime',   domTT_lifetime,
        'grid',       domTT_grid,
        'fade',       domTT_fade
    );

    // load in the predefined options if specified
    if (arguments[2] == 'predefined' && domTT_predefined.hasItem(arguments[3])) {
        var predefinedOptions = domTT_predefined.items[arguments[3]];
        for (var i in predefinedOptions.items) {
            options.setItem(i, predefinedOptions.items[i]);
        }
    }

    // load in the options from the function call
    for (var i = 2; i < arguments.length; i += 2) {
        options.setItem(arguments[i], arguments[i + 1]);
    }

    // add the event items into the hash
    options.setItem('triggerObj', triggerObj);
    options.setItem('eventType', eventType);

    // don't do anything if sticky tip is active and this is not a sticky tip
    // or if this is a greasy tip and another tip (like velcro) is active
    if ((activeTip == 'sticky' && options.items['type'] != 'sticky') ||
        (activeTip && activeTip != 'greasy' && options.items['type'] == 'greasy')) {
        return true;
    }

    // immediately set the status text if provided
    if (options.hasItem('status')) {
        try {
            window.status = options.items['status'];
        }
        // no permissions
        catch(e) {}
    }

    // if we didn't give content...assume we just wanted to change the status and return
    if (options.items['content'] == '') {
        triggerObj.onmouseout = function() { domTT_mouseout(triggerObj); };
        return true;
    }
    

    //CHANGE BY SCOTT----------------------------------------------------
    if (options.items['content'].indexOf(",") >= 0) {
        imgArr = options.items['content'].split(",");
        if (imgArr.length==3)
            options.items['content'] = "<img style='border: 1px #000000 solid' src='"+imgArr[0]+"' width="+imgArr[1]+"'' height='"+imgArr[2]+"'>";
	  else
		options.items['content'] = "<img style='border: 1px #000000 solid' src='"+imgArr[0]+"' width="+imgArr[1]+"'>";
    }
    else {
        options.items['content'] = "<img style='border: 1px #000000 solid' src='"+options.items['content']+"'>";
    }
    //-------------------------------------------------------------------



    // make sure we have a unique id, and handle case when there isn't one
    var tmp_uniqueId;
    if (!options.hasItem('id')) {
        if (!(tmp_uniqueId = triggerObj.id)) {
            triggerObj.id = tmp_uniqueId = 'domTT__id' + domTT_autoId++;
        }
    }
    else {
        tmp_uniqueId = options.items['id'];
    }

    // set the id option in the hash which will be used to label our tip
    var tmp_prefix = 'domTT:' + options.items['type'] + ':';
    options.setItem('id', tmp_prefix + tmp_uniqueId);

    // check for a cached tooltips on this object (can't have two at a time)
    var tipIsActive = (activeTip == options.items['type']);
    var tipObj = document.getElementById(options.items['id']);
    var altTipIsActive = (!tipIsActive && activeTip);
    var altTipId = altTipIsActive ? 'domTT:' + activeTip + ':' + tmp_uniqueId : false;

    // if there is a delay to create, we have to make sure we cancel that tip creation
    // if we mouseout before it happens **this must be here**
    triggerObj.onmouseout = function(in_event) { domTT_mouseout(triggerObj, this, in_event, options.items['id']); };

    // set the update delay, which is 0 if tip exists or this is a mouse button event
    options.items['delay'] = (tipIsActive || eventType.match(/click|mousedown|contextmenu/i)) ? 0 : parseInt(options.items['delay']);

    // get the mouse x and y coordinates as the bottom right edge of target for Konq
    var eventPositionSet = false;
    if (options.items['position'] == 'absolute') {
        if (options.hasItem('x') && options.hasItem('y')) {
            eventPositionSet = true;
            var mouse_x = parseInt(options.items['x']);
            var mouse_y = parseInt(options.items['y']);
        }
        // get the event location unless mouseover, then later
        // ** the delay will always be 0 for konqueror **
        else if (options.items['delay'] == 0 || !domTT_useGlobalMousePosition) {
            var eventPosition = domTT_getEventPosition(eventObj);
            eventPositionSet = true;
            var mouse_x = eventPosition.items['x'];
            var mouse_y = eventPosition.items['y'];
        }
    }
    // we don't need a mouse_x, mouse_y for relative positioning
    else {
        eventPositionSet = true;
        var mouse_x;
        var mouse_y;
    }

    // {{{ create tip if not exists

    // either there is no tooltip for this id, or we are changing our tooltip type
    // and we are going to need to recreate the tooltip with the new type
    if (!tipObj) {
        // we check if tipObj already exists and hide it in the case where we are changing type
        clearTimeout(domTT_activateTimeouts.items[options.items['id']]);
        domTT_activateTimeouts.setItem(options.items['id'], domTT_callTimeout(function(argv) { 
            // destroy alt tip (greasy if this is sticky)
            domTT_deactivate(argv[4]);
            // if this is a delay, get the current mouse position
            if (!argv[0]) {
                argv[1] = domTT_currentMousePosition.items['x'];
                argv[2] = domTT_currentMousePosition.items['y'];
            }
            domTT_create(argv[1], argv[2], argv[3]); 
        }, options.items['delay'], new Array(eventPositionSet, mouse_x, mouse_y, options, altTipId)));

        return options.items['id'];
    }

    // }}}
    // {{{ show tip if exists

    clearTimeout(domTT_activateTimeouts.items[options.items['id']]);
    domTT_activateTimeouts.setItem(options.items['id'], domTT_callTimeout(function(argv) {
        domTT_deactivate(argv[4]);
        // if this is a delay, get the coordinates after the delay
        if (!argv[0]) {
            argv[1] = domTT_currentMousePosition.items['x'];
            argv[2] = domTT_currentMousePosition.items['y'];
        }
        domTT_show(argv[1], argv[2], argv[3], argv[5], argv[6]);
    }, options.items['delay'], new Array(eventPositionSet, mouse_x, mouse_y, options, altTipId, tipObj, tipIsActive)));
    
    return options.items['id'];
    
    // }}}
}

// }}}
// {{{ domTT_create()

function domTT_create(in_x, in_y, in_options)
{
    var tipObj = document.createElement('div');
    tipObj.className = in_options.items['prefix'];
    tipObj.id = in_options.items['id'];
    tipObj.style.position = in_options.items['position'];
    // hide the tooltip so that we can get dimensions off of it without showing it
    tipObj.style.visibility = 'hidden';
    // give tooltip plenty of room to render if we are absolutely placing it
    if (in_options.items['position'] == 'absolute') {
        tipObj.style.left = 0;
        tipObj.style.top = 0;
    }

    tipObj.setAttribute('domTT_onClose', in_options.items['onClose']);
    tipObj.setAttribute('domTT_eventX', in_x);
    tipObj.setAttribute('domTT_eventY', in_y);
    tipObj.setAttribute('domTT_fade', in_options.items['fade']);
    tipObj.setAttribute('domTT_triggerId', in_options.items['triggerObj'].id);
    if (in_options.items['caption'] || (in_options.items['type'] == 'sticky' && in_options.items['caption'] !== false)) {
        var tmp_useTitleBar = true;
        var tmp_captionBox = document.createElement('div');
        tmp_captionBox.className = in_options.items['prefix'] + 'Caption';
        var tmp_caption = document.createElement('span');
        if (in_options.items['type'] == 'sticky') {
            tmp_caption.style[domTT_cssFloat] = 'left';
        }

        tmp_caption.appendChild(document.createTextNode(in_options.items['caption']));
        tmp_captionBox.appendChild(tmp_caption);
        if (in_options.items['type'] == 'sticky') {
            tmp_close = document.createElement('span');
            if (!domTT_isIE) {
                tmp_close.style[domTT_cssFloat] = 'right';
            }

            tmp_close.style.cursor = domTT_stylePointer;
            if (typeof(in_options.items['closeLink']) == 'string') {
                tmp_close.innerHTML = in_options.items['closeLink'];
            }
            else {
                tmp_close.appendChild(in_options.items['closeLink'].cloneNode(1));
            }

            tmp_close = tmp_captionBox.appendChild(tmp_close);
            // this will get lost in the IE hack, so don't waste time here
            if (!domTT_isIE) {
                tmp_close.onclick = function() { domTT_deactivate(in_options.items['id'], tipObj); };
                tmp_close.onmousedown = function(in_event) { in_event.cancelBubble = true; };
            }

            tmp_break = document.createElement('br');
            tmp_break.style.clear = 'both';
            tmp_captionBox.appendChild(tmp_break);
        }
        tipObj.appendChild(tmp_captionBox);
    }
    else {
        var tmp_useTitleBar = false;
    }

    var tmp_content = document.createElement('div');
    if (in_options.items['content'].nodeType) {
        tmp_content.appendChild(in_options.items['content']);
    }
    else {
        tmp_content.innerHTML = in_options.items['content'];
    }

    tmp_content.className = in_options.items['prefix'] + 'Content';
    tipObj.appendChild(tmp_content);
    tipObj = in_options.items['parent'].appendChild(tipObj);

    // {{{ IE workarounds for float

    // ** nasty hack for IE to get the float working right **
    if (domTT_isIE && tmp_useTitleBar) {
        var tmp_oldWidth = tipObj.offsetWidth;
        // standards compliance, we must account for the width of the borders
        if (!domTT_isIE5) {
            tmp_oldWidth -= parseInt(tipObj.currentStyle.borderLeftWidth) + parseInt(tipObj.currentStyle.borderRightWidth);
        }

        if (in_options.items['type'] == 'sticky') {
            tipObj.firstChild.firstChild.nextSibling.style[domTT_cssFloat] = 'right';
        }

        tipObj.innerHTML = tipObj.innerHTML;
        tipObj.style.width = tmp_oldWidth + 'px';
        if (in_options.items['type'] == 'sticky') {
            var tmp_close = tipObj.firstChild.firstChild.nextSibling;
            tmp_close.onclick = function() { domTT_deactivate(in_options.items['id'], tipObj); };
            tmp_close.onmousedown = function() { event.cancelBubble = true; };
        }
    }

    // }}}

    // adjust the width if specified
    if (in_options.hasItem('width')) {
        tipObj.style.width = parseInt(in_options.items['width']) + 'px';
    }

    // check if we are overridding the maxWidth
    // if the browser supports maxWidth, the global setting will be ignored (assume stylesheet)
    var tmp_maxWidth = domTT_maxWidth;
    if (in_options.hasItem('maxWidth')) {
        if ((tmp_maxWidth = in_options.items['maxWidth']) === false) {
            tipObj.style.maxWidth = domTT_styleNoMaxWidth;
        }
        else {
            tipObj.style.maxWidth = tmp_maxWidth = parseInt(in_options.items['maxWidth']) + 'px';
        }
    }

    // ** fix lack of maxWidth in CSS for Konq and IE **
    if (tmp_maxWidth !== false && (domTT_isIE || domTT_isKonq) && tipObj.offsetWidth > tmp_maxWidth) {
        tipObj.style.width = tmp_maxWidth + 'px';
    }
    
    // ** hack for IE to make the tip object have a 'layout' so it can fade **
    if (domTT_isIE && in_options.items['position'] == 'relative' && in_options.items['fade'] != 'neither') {
        tipObj.style.height = (tipObj.offsetHeight - parseInt(tipObj.currentStyle.borderTopWidth) - parseInt(tipObj.currentStyle.borderBottomWidth)) + 'px';
    }

    if (in_options.items['type'] == 'sticky') {
        if (in_options.items['position'] == 'absolute' && domTT_dragStickyTips) {
            if (domTT_isIE) {
                tipObj.firstChild.onselectstart = function() { return false; };
            }

            // setup drag
            tipObj.firstChild.onmousedown = function(in_event) { domTT_dragStart(this, in_event);  };
            tipObj.firstChild.onmousemove = function(in_event) { domTT_dragUpdate(in_event); };
            tipObj.firstChild.onmouseup = function(in_event) { domTT_dragStop(in_event); };
        }
    }
    else if (in_options.items['type'] == 'velcro') {
        tipObj.onmouseout = function(in_event) { domTT_mouseout(false, tipObj, in_event, in_options.items['id']); };
    }
    
    domTT_show(in_x, in_y, in_options, tipObj, false);
}

// }}}
// {{{ domTT_show()

function domTT_show(in_x, in_y, in_options, in_tipObj, in_active)
{
    // if the tip is visible and we are using the grid, then make sure we have moved to
    // the next grid slot before we move the tooltip
    if (!in_active || !in_options.items['grid'] || Math.abs(in_tipObj.getAttribute('domTT_eventX') - in_x) > in_options.items['grid'] || Math.abs(in_tipObj.getAttribute('domTT_eventY') - in_y) > in_options.items['grid']) {
        in_tipObj.setAttribute('domTT_eventX', in_x);
        in_tipObj.setAttribute('domTT_eventY', in_y);
    }
    else {
        return;
    }

    // increase the tip zIndex so it goes over previously shown tips
    in_tipObj.style.zIndex = domTT_zIndex++;

    // if not yet active, we need to grab dimensions off the tooltip without showing it
    if (!in_active) {
        in_tipObj.style.visibility = 'hidden';
        in_tipObj.style.display = '';
    }

    // ** sometimes the offsetWidth/offsetHeight in konqueror are undefined **
    if (in_options.items['position'] == 'absolute') {
        // place the tip in the direction relative to the pointer
        switch (in_options.items['direction']) {
            case 'northeast':
                var tip_x = in_x + domTT_offsetX;
                var tip_y = in_y - in_tipObj.offsetHeight - domTT_offsetY;
            break;
            case 'northwest':
                var tip_x = in_x - in_tipObj.offsetWidth - domTT_offsetX;
                var tip_y = in_y - in_tipObj.offsetHeight - domTT_offsetY;
            break;
            case 'southwest':
                var tip_x = in_x - in_tipObj.offsetWidth - domTT_offsetX;
                var tip_y = in_y + domTT_mouseHeight + domTT_offsetY;
            break;
            case 'southeast':
                var tip_x = in_x + domTT_offsetX;
                var tip_y = in_y + domTT_mouseHeight + domTT_offsetY;
            break;
        }

        var tipCoordinates = domTT_correctEdgeBleed(in_tipObj.offsetWidth, in_tipObj.offsetHeight, tip_x, tip_y, domTT_offsetX, domTT_offsetY, in_options.items['type']);

        // update the position
        in_tipObj.style.left = tipCoordinates[0] + 'px';
        in_tipObj.style.top = tipCoordinates[1] + 'px';
    }

    // if tip is not active, active it now and check for a fade in
    if (!in_active) {
        in_options.items['triggerObj'].setAttribute('domTT_activeTip', in_options.items['type']);
        // unhide the tooltip
        in_tipObj.style.visibility = 'visible';

        // [!] what happens if it is fading in and an event fires again? [!]
        // check to see if we are fading the tip
        if (domTT_isIE || domTT_isGecko) {
            var tmp_fade = in_options.items['fade'];
            if (tmp_fade == 'out' || tmp_fade == 'both') {
                clearTimeout(domTT_fadeTimeouts.items[in_options.items['id']]);
            }

            if (tmp_fade == 'in' || tmp_fade == 'both') {
                if (!in_active) {
                    in_tipObj.style[domTT_styleOpacity] = domTT_isIE ? 'alpha(opacity=0)' : '0%';
                }

                domTT_fadeTimeouts.setItem(in_options.items['id'], setTimeout(function() { domTT_doFade(in_tipObj, 'in'); }, domTT_fadeInterval));
            }
            else {
                in_tipObj.style[domTT_styleOpacity] = domTT_isIE ? 'alpha(opacity=100)' : '100%';
            }
        }
    }

    // update the mouseout for sticky tips
    if (in_options.items['type'] == 'sticky') {
        in_options.items['triggerObj'].onmouseout = function() { domTT_mouseout(in_options.items['triggerObj']); };
    }
    else if (in_options.items['type'] == 'velcro') {
        in_options.items['triggerObj'].onmouseout = function() { domTT_mouseout(in_options.items['triggerObj']); };
    }
    else {
        in_options.items['triggerObj'].onmouseout = function(in_event) { domTT_mouseout(in_options.items['triggerObj'], this, in_event, in_options.items['id']); };
        // set the tip lifetime
        if (in_options.items['lifetime']) {
            clearTimeout(in_tipObj.getAttribute('domTT_lifetime'));
            in_tipObj.setAttribute('domTT_lifetime', domTT_callTimeout(function(argv) { domTT_deactivate(argv[0], argv[1]); }, in_options.items['lifetime'], new Array(in_options.items['id'], in_tipObj)));
        }
    }

    if (in_options.items['position'] == 'absolute') {
        domTT_detectCollisions(in_tipObj);
    }
}

// }}}
// {{{ domTT_doFade()

function domTT_doFade(in_object, in_direction) {
    var matches = in_object.style[domTT_styleOpacity].match(/[0-9]+/);
    var opacity = new Number(matches[0]);
    if (in_direction == 'in') {
        if (opacity < 100) {
            var nextOpacity = Math.min(100, opacity + 10);
        }
        else {
            return;
        }
    }
    else {
        if (opacity > 0) {
            var nextOpacity = Math.max(0, opacity - 10);
        }
        else {
            in_object.style.display = 'none';
            return;
        }
    }

    in_object.style[domTT_styleOpacity] = domTT_isIE ? 'alpha(opacity=' + nextOpacity + ')' : nextOpacity + '%';
    domTT_fadeTimeouts.setItem(in_object.id, setTimeout(function() { domTT_doFade(in_object, in_direction); }, domTT_fadeInterval));
}

// }}}
// {{{ domTT_deactivate()

function domTT_deactivate(in_tipId, in_tipObj)
{
    if (!in_tipId) {
        return;
    }

    // cancel the creation of this tip if it is still pending
    // ** in konqueror, cancelling timeout id that doesn't exist freezes everything **
    if (domTT_isKonq) {
        // [!] assuming no quotes here [!]
        setTimeout('clearTimeout(domTT_activateTimeouts.items[\'' + in_tipId + '\']);', 0);
    }
    else {
        clearTimeout(domTT_activateTimeouts.items[in_tipId]);
    }

    var tipObj = in_tipObj ? in_tipObj : domTT_isActive(in_tipId);
    if (tipObj) {

        // [!] hack to disable IFRAME content in opera, since it won't hide it [!]
        if (domTT_isOpera && tipObj.lastChild.firstChild.tagName == 'IFRAME') {
            tipObj.style.top = domTT_hidePosition;
        }

        // clear the activeTip type if triggerObj reference is used
        var triggerObj = document.getElementById(tipObj.getAttribute('domTT_triggerId'))
        if (triggerObj) {
            triggerObj.setAttribute('domTT_activeTip', '');
        }

        if (tipObj.getAttribute('domTT_onClose') == 'hide') {
            if ((domTT_isIE || domTT_isGecko) && (tipObj.getAttribute('domTT_fade') == 'out' || tipObj.getAttribute('domTT_fade') == 'both')) {
                clearTimeout(domTT_fadeTimeouts.items[tipObj.id]);
                domTT_fadeTimeouts.setItem(tipObj.id, setTimeout(function() { domTT_doFade(tipObj, 'out'); }, domTT_fadeInterval));
            }
            else {
                tipObj.style.display = 'none';
            }
        }
        else {
            // guard against a fluke here
            if (tipObj.parentNode) {
                tipObj.parentNode.removeChild(tipObj);
            }
        }
        
        // unhide all of the selects that are owned by this object
        domTT_detectCollisions(tipObj, true); 
    }
}

// }}}
// {{{ domTT_isActive()

function domTT_isActive(in_id)
{
    if (!in_id) {
        return false;
    }

    var tooltipObj = typeof(in_id) == 'object' ? in_id : document.getElementById(in_id);
    if (tooltipObj && tooltipObj.style.display != 'none') {
        return tooltipObj;
    }
    else {
        return false;
    }
}

// }}}
// {{{ domTT_addPredefined()

function domTT_addPredefined(in_id)
{
    var options = new domTT_Hash();
    for (var i = 1; i < arguments.length; i += 2) {
        options.setItem(arguments[i], arguments[i + 1]);
    }
    
    // [!] possiblity add here the check for document.body and Gecko and use the
    // createContextualFragment to cache the parsing of the html for content [!]

    domTT_predefined.setItem(in_id, options);
}

// }}}
// {{{ domTT_correctEdgeBleed()

function domTT_correctEdgeBleed(in_width, in_height, in_x, in_y, in_offsetX, in_offsetY, in_type)
{
    var bleedRight;
    var bleedBottom;
    if (domTT_isIE && !domTT_isIE5) {
        var pageHeight = document.documentElement.clientHeight;
    }
    else if (!domTT_isKonq) {
        var pageHeight = document.body.clientHeight;
    }
    else {
        var pageHeight = window.innerHeight;
    }

    var pageYOffset = domTT_isIE ? document.body.scrollTop : window.pageYOffset;
    var pageXOffset = domTT_isIE ? document.body.scrollLeft : window.pageXOffset;
    // we are bleeding off the right, move tip over to stay on page
    if ((bleedRight = (in_x - pageXOffset) + in_width - (document.body.clientWidth - domTT_screenEdgePadding)) > 0) {
        in_x -= bleedRight;
    }

    // we are bleeding to the left, move tip over to stay on page
    // we don't want an 'else if' here, because if it doesn't fit we will bleed off the right
    if ((in_x - pageXOffset) < domTT_screenEdgePadding) {
        in_x = domTT_screenEdgePadding + pageXOffset;
    }
    
    // ** top/bottom corrections depends on type, because we can't end up with the mouse over
    // the tip if this is a greasy **
    // if we are bleeding off the bottom, flip to north
    if ((bleedBottom = (in_y - pageYOffset) + in_height - (pageHeight - domTT_screenEdgePadding)) > 0) {
        if (in_type == 'sticky') {
            in_y -= bleedBottom;
        }
        else {
            in_y -= in_height + (2 * in_offsetY) + domTT_mouseHeight;
        }
    }

    // if we are bleeding off the top, flip to south
    // we don't want an 'else if' here, because if we just can't fit it, bleed off the bottom
    if ((in_y - pageYOffset) < domTT_screenEdgePadding) {
        if (in_type == 'sticky') {
            in_y = domTT_screenEdgePadding + pageYOffset;
        }
        else {
            in_y += in_height + (2 * in_offsetY) + domTT_mouseHeight;
        }
    }
    
    return new Array(in_x, in_y);
}

// }}}
// {{{ domTT_detectCollisions()

function domTT_detectCollisions(in_tipObj, in_recover)
{
    // no need to do anything for opera
    if (domTT_isOpera) {
        return;
    }

    if (typeof(domTT_selectElements) == 'undefined') {
        domTT_selectElements = document.getElementsByTagName('select');
    }
    
    // if we don't have a tip, then unhide selects
    if (in_recover) {
        for (var cnt = 0; cnt < domTT_selectElements.length; cnt++) {
            if (domTT_isGecko && domTT_selectElements[cnt].size <= 1 && !domTT_selectElements[cnt].multiple) {
                continue;
            }

            var thisSelect = domTT_selectElements[cnt];
            thisSelect.hideList.removeItem(in_tipObj.id);
            if (!thisSelect.hideList.length) {
                domTT_selectElements[cnt].style.visibility = 'visible';
            }
        }
        
        return;
    }

    // okay, we have a tip, so hunt and destroy
    var tipOffsets = domTT_getOffsets(in_tipObj);

    for (var cnt = 0; cnt < domTT_selectElements.length; cnt++) {
        var thisSelect = domTT_selectElements[cnt];

        // mozilla doesn't have a problem with regular selects
        if (domTT_isGecko && thisSelect.size <= 1 && !thisSelect.multiple) {
            continue;
        }

        if (!thisSelect.hideList) {
            thisSelect.hideList = new domTT_Hash();
        }

        var selectOffsets = domTT_getOffsets(thisSelect); 
        // for mozilla we only have to worry about the scrollbar itself
        if (domTT_isGecko) {
            selectOffsets.setItem('left', selectOffsets.items['left'] + thisSelect.offsetWidth - domTT_scrollbarWidth);
            selectOffsets.setItem('leftCenter', selectOffsets.items['left'] + domTT_scrollbarWidth/2);
            selectOffsets.setItem('radius', Math.max(thisSelect.offsetHeight, domTT_scrollbarWidth/2));
        }

        var center2centerDistance = Math.sqrt(Math.pow(selectOffsets.items['leftCenter'] - tipOffsets.items['leftCenter'], 2) + Math.pow(selectOffsets.items['topCenter'] - tipOffsets.items['topCenter'], 2));
        var radiusSum = selectOffsets.items['radius'] + tipOffsets.items['radius'];
        // the encompassing circles are overlapping, get in for a closer look
        if (center2centerDistance < radiusSum) {
            // tip is left of select
            if ((tipOffsets.items['leftCenter'] <= selectOffsets.items['leftCenter'] && tipOffsets.items['right'] < selectOffsets.items['left']) ||
            // tip is right of select
                (tipOffsets.items['leftCenter'] > selectOffsets.items['leftCenter'] && tipOffsets.items['left'] > selectOffsets.items['right']) ||
            // tip is above select
                (tipOffsets.items['topCenter'] <= selectOffsets.items['topCenter'] && tipOffsets.items['bottom'] < selectOffsets.items['top']) ||
            // tip is below select
                (tipOffsets.items['topCenter'] > selectOffsets.items['topCenter'] && tipOffsets.items['top'] > selectOffsets.items['bottom'])) {
                thisSelect.hideList.removeItem(in_tipObj.id);
                if (!thisSelect.hideList.length) {
                    thisSelect.style.visibility = 'visible';
                }
            }
            else {
                thisSelect.hideList.setItem(in_tipObj.id, true);
                thisSelect.style.visibility = 'hidden';
            }
        }
    }
}

// }}}
// {{{ domTT_getOffsets()

function domTT_getOffsets(in_object)
{
    var originalObject = in_object;
    var originalWidth = in_object.offsetWidth;
    var originalHeight = in_object.offsetHeight;
    var offsetLeft = 0;
    var offsetTop = 0;

    while (in_object) {
        offsetLeft += in_object.offsetLeft;
        offsetTop += in_object.offsetTop;
        in_object = in_object.offsetParent;
    }
    
    return new domTT_Hash(
        'left',       offsetLeft,
        'top',        offsetTop,
        'right',      offsetLeft + originalWidth,
        'bottom',     offsetTop + originalHeight,
        'leftCenter', offsetLeft + originalWidth/2,
        'topCenter',  offsetTop + originalHeight/2,
        'radius',     Math.max(originalWidth, originalHeight) 
    );
}

// }}}
// {{{ domTT_getEventPosition()

function domTT_getEventPosition(in_eventObj)
{
    var eventPosition = new domTT_Hash();
    if (domTT_isKonq) {
        eventPosition.setItem('x', in_eventObj.x);
        eventPosition.setItem('y', in_eventObj.y);
    }
    else if (domTT_isIE) {
        // consider case where document.body doesn't yet exist for IE
        eventPosition.setItem('x', in_eventObj.clientX + (document.body ? document.body.scrollLeft : 0));
        eventPosition.setItem('y', in_eventObj.clientY + (document.body ? document.body.scrollTop : 0));
    }
    else {
        eventPosition.setItem('x', in_eventObj.pageX);
        eventPosition.setItem('y', in_eventObj.pageY);
    }
    
    return eventPosition;
}

// }}}
// {{{ domTT_isDescendantOf()

function domTT_isDescendantOf(in_object, in_ancestor) {
    if (in_object == in_ancestor) {
        return true;
    }

    while (in_object != document.documentElement) {
        try {
            if ((tmp_object = in_object.offsetParent) && tmp_object == in_ancestor) {
                return true;
            }
            else if ((tmp_object = in_object.parentNode) == in_ancestor) {
                return true;
            }
            else {
                in_object = tmp_object;
            }
        }
        // in case we get some wierd error, just assume we haven't gone out yet
        catch(e) {
            return true;
        }
    }

    return false;
}

// }}}
// {{{ domTT_dragStart()

function domTT_dragStart(in_this, in_event)
{
    var eventObj = domTT_isIE ? event : in_event;
    var eventButton = eventObj[domTT_eventButton];
    if (eventButton != 1 && !domTT_isKonq) {
        return;
    }
    
    var eventTarget = domTT_isIE ? in_this : in_event.currentTarget;
    eventTarget = domTT_currentDragTarget = eventTarget.parentNode;
    eventTarget.style.cursor = 'move';

    // upgrade our z-index
    eventTarget.style.zIndex = ++domTT_zIndex;

    var eventPosition = domTT_getEventPosition(eventObj);

    var targetPosition = domTT_getOffsets(eventTarget);
    domTT_dragOffsetLeft = eventPosition.items['x'] - targetPosition.items['left'];
    domTT_dragOffsetTop = eventPosition.items['y'] - targetPosition.items['top'];
    domTT_dragMouseDown = true;
}

// }}}
// {{{ domTT_dragUpdate()

function domTT_dragUpdate(in_event)
{
    if (domTT_dragMouseDown) {
        if (domTT_isGecko) {
            window.getSelection().removeAllRanges()
        }

        var eventObj = domTT_isIE ? event : in_event;
        var eventTarget = domTT_currentDragTarget;
        var eventPosition = domTT_getEventPosition(eventObj);

        eventTarget.style.left = (eventPosition.items['x'] - domTT_dragOffsetLeft) + 'px';
        eventTarget.style.top = (eventPosition.items['y'] - domTT_dragOffsetTop) + 'px';

        // update the collision detection
        domTT_detectCollisions(eventTarget);
    }
}

// }}}
// {{{ domTT_dragStop()

function domTT_dragStop()
{
    if (domTT_dragMouseDown) {
        domTT_dragMouseDown = false; 
        domTT_currentDragTarget.style.cursor = 'default';
        domTT_currentDragTarget = null;
        if (domTT_isGecko) {
            window.getSelection().removeAllRanges()
        }
    }
}

// }}}
// {{{ domTT_true()

function domTT_true() {
    return true;
}

// }}}
// {{{ domTT_false()

function domTT_false() {
    return false;
}

// }}}




// -----------------------------------------------------
// added by scott 1/7/03 -------------------------------
// -----------------------------------------------------
function showPic(arg1, arg2, arg3) {
	return domTT_true(domTT_activate(arg1, arg2, 'caption', '', 'content', arg3, 'status', 'Image'));
}
/*
function showPic(arg1, arg2, path,w) {
	return domTT_true(domTT_activate(arg1, arg2, 'caption', '', 'content', path+','+w, 'status', 'Image'));
}
function showPic(arg1, arg2, path,w,h) {
	return domTT_true(domTT_activate(arg1, arg2, 'caption', '', 'content', path+','+w+','+h, 'status', 'Image'));
}
*/
// -----------------------------------------------------
// -----------------------------------------------------
// -----------------------------------------------------