/**
 * @script: WebDDM
 * @file: WebDDM.js
 * @version: 3.0.3
 * @website: www.jportalhome.com
 * @email: josh@jportalhome.com
 * @copyright: (C) 2004 JPortal
 * @begin: Sun Jul 25 2004
 * @license: Free, Open Source (GPL)
 */

// phpJSObfuscator flags
// ::phpJSO Flag::keep::WebDDM_activateMenu,WebDDM_showMenu,style
// ::phpJSO Flag::force::in_event,WebDDM_mouseout_timeout

//
// ------------------------------
// SECTION: FUNCTION / VARIABLE LIBRARY
// ------------------------------
//
// Browser Detection
a = navigator.userAgent.toLowerCase();
var isOpera = window.opera ? true : false;
var isKonq = navigator.vendor == 'KDE';
var isKhtml = document.childNodes && !document.all && !navigator.taintEnabled;
var isSafari = a.match(/applewebkit/);
var isIE = !isKhtml && !isOpera && a.match(/msie/) && document.all;
var isIE50 = isIE && a.match(/msie 5\.0/) && document.getElementById;
var isIE55 = isIE && a.match(/msie 5\.5/) && document.getElementById;
var isIE5 = isIE50 || isIE55;
var isIE55up = isIE && !isIE50;
var isIE6up = isIE && !isIE55;
var isGecko = navigator.product == 'Gecko';
var dom_canTimeout = !isKonq && !isIE50;
var dom_canFade = isGecko || isIE || isSafari;
var dom_stylePointer = isIE ? 'hand' : 'pointer';

// Other stuff
var dom_scrollbarWidth = 14;
var ddmTT_zIndex = 1000;
var dom_timeoutStateId = 0;
var dom_timeoutStates = {};
var dom_mousePosition = {};
var dom_mouseHeight = 20;
var dom_selectElements;
var dom_eventButton = isIE ? 'button' : 'which';

/**
* Escapes a string so that it can safely be placed inside any other strings.
*
* @param	str String to "escape"
*
* @access	private
*/
function lib_escape (s)
{
	return 'unescape("'+escape(s)+'")';
} // End function lib_escape



/**
* Build one function from the two functions passed. The function parameters of
* the first function take precedence over the parameters of the second function.
*
* @param	obj/str The first function
* @param	obj/str The second function
*
* @access	private
*/
function buildFunction (f1, f2)
{
	func_body = func_params = '';
	if (isset(f1) && (f1+='') && (data=parseFunction(f1)))
	{
		func_body += data[1];
		func_params += data[0];
	}
	if (isset(f2) && (f2+='') && (data=parseFunction(f2)))
	{
		func_body += data[1];
		func_params += (func_params && data[0] ? ',' : '') + data[0];
	}
	eval('retFunc = function ('+func_params+') {'+func_body+'};');
	return retFunc;
} // End function buildFunction

/**
* Parses a function and returns an array of [0]=function parameters, [1]=body
*
* @param	str The function
*
* @access	private
*/
function parseFunction (f)
{
	data = ['',''];
	if (f.match(/\s*function\s*\(([^\)]*)\)\s*\{([^$]*)\}\s*/))
	{
		data[0] = RegExp.$1;
		data[1] = RegExp.$2;
	}
	else
	{
		data[1] = f;
	}
	return data;
} // End function parseFunction

/**
* Merge two arrays. Returns the resulting array. Elements in the second array will NOT
* replace elements in the first array, unless the third argument is "false".
*
* @param	obj The first, "base", array
* @param	obj The second array to merge into the first
* @param	bool Should the second array override the first?
*
* @access	private
*/
function merge (baseArray, mergeArray, override)
{
	for (var i in mergeArray) {
		baseArray[i] = isset(baseArray[i]) && !override
			? baseArray[i]
			: mergeArray[i];
	}
	return baseArray;
} // End function merge

/**
* Checks if a variable is set or not; returns "true" if the variable is non-null
* and not undefined, else false. I copied this from PHP ;)
*
* @param	(any) Variable to check
*
* @access	private
*/
function isset (v)
{
	return v != undefined && v != null;
} // End function isset

/**
* This is a library function from domLib (packaged with domTT) by Dan Allen.
* Basically, it creates functionality of the setTimeout function for all browsers.
*
* @param	obj Function to call
* @param	int Timeout interval
* @param	obj Arguments to pass to function
*
* @access	private
*/
function dom_setTimeout(in_function, in_timeout, in_args)
{
	in_args = in_args || [];

	if (in_timeout < 1)
	{
		in_function(in_args);
		return 0;
	}

	// must make a copy of the arguments so that we release the reference
	var args = merge({}, in_args);

	if (dom_canTimeout)
	{
		return setTimeout(function() { in_function(args); }, in_timeout);
	}
	else
	{
		var id = dom_timeoutStateId++;
		var data = new Array;
		data['function'] = in_function;
		data.args = args;
		dom_timeoutStates[id] = data;

		data.timeoutId = setTimeout('dom_timeoutStates[' + id + '][\'function\'](dom_timeoutStates[' + id + '][\'args\']); delete dom_timeoutStates[' + id + '];', in_timeout);
		return id;
	}
} // End function dom_setTimeout

/**
* This is a library function from domLib (packaged with domTT) by Dan Allen.
* This is the reverse of the dom_setTimeout function. The one argument is the
* return value of the dom_setTimeout function
*
* @param	str Timeout ID
*
* @access	private
*/
function dom_clearTimeout(in_id)
{
	if (dom_canTimeout) {
		clearTimeout(in_id);
	} else if (isset(dom_timeoutStates[in_id])) {
		clearTimeout(dom_timeoutStates[in_id].timeoutId);
		delete dom_timeoutStates[in_id];
	}
} // End function dom_clearTimeout

/**
* This returns the element object with the passed ID.
*
* @param	str Element ID
*
* @access	private
*/
function getEl (el)
{
	return document.all
		? document.all[el]
		: document.getElementById(el);
} // End function getEl

/**
* This function appends the child node (second argument) to the parent node (first arugment)
* in the DOM.
*
* @param	str/obj Parent element/ID
* @param	obj Child element
*
* @access	private
*/
function appendEl (p, c) // parent, child
{
	return (typeof(p) == 'object' ? p : getEl(p)).appendChild(c);
} // End function appendEl

/**
* Creates an element with the specified tag.
*
* @param	str Tag name
*
* @access	private
*/
function createEl (tag)
{
	return document.createElement(tag);
} // End function createEl


/**
* Find needle in array haystack.
* in_array will return the index in the array if the value is found,
* or false if it is not found. Because 0 is a possible return value,
* check the return value like so: alert(in_array(a,b) === false ? 'Not found' : 'Found!');
*
* @param	(any) Needle to search for
* @param	(any) Haystack to search in
*
* @access	private
*/
function in_array (needle, haystack)
{
	// Loop through all elements of the haystack
	for (i in haystack) {
		if (haystack[i] == needle) {
			return i;
		}
	}
	return false;
} // End function in_array

/**
* interCaps a string. This is useful for CSS:
* -moz-opacity = MozOpacity
* background-color = backgroundColor
*
* @param	str String to intercap
*
* @access	private
*/
function interCap (s)
{
	while (s.match(/\-(.)/)) {
		s = s.replace('-'+(c = RegExp.$1), c.toUpperCase());
	}
	return s;
} // End function interCap

/**
* The maximum key of an array.
*
* @param	obj Array to get maxkey of
*
* @access	private
*/
function maxkey (a)
{
	return a.length - 1;
} // End function maxkey

/**
* Takes a CSS string and returns an array of key=>value pairs.
*
* @param	str CSS formatting string.
*
* @access	private
*/
function parseCSS_string (s)
{
	css = {};

	// Split string into chunks
	chunks = s.split(/\s*\;\s*/);
	// Loop through chunks
	for (var k in chunks)
	{
		// v will be something like "border-color: red"
		v = chunks[k];
		if (v.match(/^\s*([a-zA-Z0-9\-]*)\s*\:(.*?)$/)) {
			css[RegExp.$1] = RegExp.$2;
		}
	}
	return css;
} // End function parseCSS_string

//
// ------------------------------
// SECTION: MENU BUILDER
// ------------------------------
//

// Array of menus. This is just an array: menu_name=>>menu_array
var WebDDM_Activated_Menus = {};
// Mouseout timeouts - so that we don't call timeout() right away for an item,
// but wait and make sure that that we're really off, because mouseout() triggers
// sometimes within an item, moving over text or images.
var WebDDM_mouseout_timeout = {}, WebDDM_zIndexes = {};
var WebDDM_default_itemcontainer_attributes = {'max_opacity':100,'fade_delta':0,'fade_in_delay':0,'fade_out_delay':0,'fade':'both'};
WebDDM_onmousemoveSet = false;

/**
* Activates a menu with the ID being the first argument (containerID).
* The menu is built according to the information found in the second argument,
* menu_array. The menu is placed in the DIV container with the ID being the
* first argument passed (containerID). Example:
*   <div id="menu"></div>
*   <script>WebDDM_activateMenu('menu', AlreadyDefinedArray)</script>
*
* @param	menu_id Name of the DIV container that will hold the menu, and
*		the menu_id will also be the ID of the menu. The DIV container
*		must be declared before WebDDM_activateMenu is called.
* @param	menu_array Array of the menu to be built
*
* @return	bool false on error, true on success
*
* @access	public
*/
function WebDDM_activateMenu (menu_id, menu_array)
{
	// Initialize variables
	var container = getEl(menu_id);
	if (!container)
	{
		container = createEl('div');
		container.id = menu_id;
		appendEl(document.body, container);
	}

	// Get base zIndex
	WebDDM_zIndexes[menu_id] = (isset(menu_array.zIndex) ? menu_array.zIndex : 200);

	//
	// Build base menu
	//

	// Set container styles
	for (var k in {'position':0, 'top':0, 'left':0, 'width':0, 'height':0})
	{
		// Set CSS style
		v = menu_array[k];
		container.style[k] = v + (parseInt(v) == v ? 'px' : '');
	}
	container.style.backgroundColor = 'transparent';
	container.style.visibility = container.style.overflow = 'visible';
	container.style.zIndex = WebDDM_zIndexes[container.id]++;

	// Set menu as activated
	WebDDM_Activated_Menus[container.id] = menu_array;

	// Is this a floating menu?
	if (menu_array['float'])
	{
		floatAPI_floating_elements[floatAPI_floating_elements.length] = container.id;
		floatAPI_controlFloaters();
		menu_array.float_top = menu_array.top;
		menu_array.float_left = menu_array.left;
	}

	// Is this a context/click menu?
	if (isset(menu_array.expand_menu) && menu_array.expand_menu.match(/context|click/))
	{
		menu_array.dynamic_pos = true;
		evt = 'document.on' + (menu_array.expand_menu.match('context') ? 'contextmenu' : 'click');
		eval(evt+' = buildFunction(function () {' +
				'WebDDM_setAtCursor("'+container.id+'", '+menu_array.top+', '+menu_array.left+');' +
				'WebDDM_showMenu("'+container.id+'");' +
				'return false;' +
			'}, '+evt+');');
		container.style.top = container.style.left = '-1000px';

		// Make sure menu position is absolute - things can get messed up otherwise
		menu_array.position = container.style.position = 'absolute';
	}
	
	// Set hide_all() callers
	// When this document is clicked or minimized, hide all menus
	hideAll_eval = 'WebDDM_hideAll("'+container.id+'");';
	document.onclick = buildFunction(hideAll_eval, document.onclick);
	document.onblur = buildFunction(hideAll_eval, document.oblur);

	// Set document.onmousemove event handler
	// This gets the mouse coordinates and updates any domTT tooltip dragging.
	if (!WebDDM_onmousemoveSet)
	{
		WebDDM_onmousemoveSet = true;
		document.onmousemove = buildFunction(function (in_event)
		{
			dom_mousePosition = dom_getEventPosition((in_event = isset(in_event) ? in_event : event));
			if (ddmTT_dragMouseDown)
			{
				ddmTT_dragUpdate(in_event);
			}
		}, document.onmousemove);
	}

	// Automatically show menu (if needed)
	if (menu_array.expand_menu == 'auto')
	{
		WebDDM_showMenu(menu_id);
	}

	// Return true since everything went OK!
	return true;
} // End function WebDDM_activateMenu()


/**
* Builds a menu and processes offsets, etc. Returns new menu
* array of processed menu on success or false on error.
*
* @param	menu_array Array() of the menu
* @param	container Container object of the menu
* @param	item_id of the current item being processed. Should be '' if it is the base.
*
* @return	mixed false on error, object Array() of menu on success.
*
* @access	private
*/
function WebDDM_buildMenu (menu_array)
{
	// Get item ID
	container_id = menu_array.container_id;
	item_id = menu_array.item_id || container_id;

	// Create the HTML container
	HTML = '';

	// Merge default item array options into item array
	menu_array.items = merge(menu_array.items,
		WebDDM_default_itemcontainer_attributes,
		false);

	// Set 'fade_in_delay' and 'fade_out_delay' if 'fade_delay' is set
	if (isset(menu_array.items.fade_delay))
	{
		menu_array.items.fade_in_delay = menu_array.items.fade_out_delay = menu_array.items.fade_delay;
	}

	// Get items array
	items_array = menu_array.items;
	
	// Initialize arrays
	actions_array = [];

	// Loop through all the items in the array of items
	for (var item_index = 1; isset(items_array[item_index]); item_index++)
	{
		// Get item ID
		var this_item_id = item_id + '_' + item_index;
		// Get item array
		var item_array = items_array[item_index];
		// Set item ID
		item_array.item_id = this_item_id;

		// Initialize actions for this item
		actions = {'mouseover':'','mouseout':'','click':''};

		//
		// ------------------------------------------------------
		// Do preprocessing
		// ------------------------------------------------------
		//

		// Set defaults for this item array
		item_array.expand_menu = item_array.expand_menu || 'rollover';
		item_array.contract_menu = item_array.contract_menu || 'rollout';

		// Set *_menuopen_out and *_menuopen_rollover vars
		// If rollover is set and menuopen is not, set menuopen_rollover
		// If out is set and menuopen is not, set menuopen_out
		// "*" stands for: content, class, or css
		vars = ['content','class','css'];
		for (var k in vars)
		{
			eval('if (!isset(item_array.'+(a = vars[k])+'_menuopen)) {' +
				'if (isset(item_array.'+a+'_rollover)) {' +
					'item_array.'+a+'_menuopen_rollover=item_array.'+a+'_rollover;' +
				'}' +
				'if (isset(item_array["'+a+'"])) {' +
					'item_array.'+a+'_menuopen_out = item_array["'+a+'"];' +
				'}' +
			'}');
		}


		//
		// ------------------------------------------------------
		// Set actions
		// ------------------------------------------------------
		//

		// Build basic code vars that are repeated several times
		var p2 = '("' + container_id + '", "' + this_item_id + '");';
		var show_menu_code_eval = 'WebDDM_showMenu' + p2;
		var hide_menu_code_eval = 'WebDDM_hideMenu' + p2;

		var tooltip_close_code_eval = isset(item_array.tooltip) ? 'ddmTT_mouseout(getEl("'+this_item_id+'_item_container"));' : '';
		var menu_open_bool_eval = 'WebDDM_menuOpen("'+this_item_id+'")';

		var p1_eval = 'WebDDM_itemStyle("'+container_id+'","'+this_item_id+'","';
		var set_style_out = p1_eval + '");';
		var set_style_rollover = p1_eval + '_rollover");';
		var set_style_menuopen_out = p1_eval + '_menuopen_out");';
		var set_style_menuopen_rollover = p1_eval + '_menuopen_rollover");';

		var p1_eval = 'WebDDM_mouseoverStatus("'+this_item_id+'"';
		var set_mouseover_true = p1_eval+',true);';
		var set_mouseover_false = p1_eval+',false);';
		var get_mouseover = p1_eval+')';

		//
		// Set expand/contract actions
		//
		if (item_array.items)
		{
			// Are we opening the menu on onclick?
			if (item_array.expand_menu.match('click')) {
				// This menu should open on onclick
				// So, let's do it :D
				actions.click += set_mouseover_true +
					'if ('+menu_open_bool_eval+') {' +
						// The menu is open, and we close the menu
						// when it's clicked on. So, close it.
						(item_array.contract_menu.match('click') ? hide_menu_code_eval : '') +
					'} else {' +
						// The menu is not open; open it
						show_menu_code_eval +
					'}';
				// When the mouse rolls over the menu,
				// check if the currently opened menu opens with a click.
				// If it does, close that one and open this one.
				actions.mouseover += 'WebDDM_checkClickMenu("'+container_id+'","'+this_item_id+'");';
			} else if (item_array.contract_menu.match('click')) {
				// The menu does not open on onclick, but it does
				// close on onclick
				actions.click += 'if ('+menu_open_bool_eval+') {' + hide_menu_code_eval + '}';
			}

			// Are we opening the menu on rollover?
			if (item_array.expand_menu.match('rollover')) {
				// We are opening the menu on rollover
				actions.mouseover += show_menu_code_eval;
			}
		} // End if item array has subitems
		else if (!isset(item_array.isContainerItem))
		{
			actions.mouseover += show_menu_code_eval;
		}

		// Cancel the event bubble so a click does not register to document.onclick
		actions.click += 'in_event.cancelBubble = true;';

		// Set status text
		if (item_array.status_text)
		{
			w = 'window.status';
			actions.mouseover += w+'='+lib_escape(item_array.status_text)+';';
			actions.mouseout += w+'=window.defaultStatus;';
		}

		//
		// Build item link
		//
		if (url = item_array.url)
		{
			actions.click += (url.match(/^javascript\:/)
				? 'eval('+lib_escape(url)+')'
				: 'window.open('+lib_escape(url)+', "'+(item_array.target || '_self')+'")') + ';';
		}

		// Set tooltip code
		tooltip_open_code = '';
		if (item_array.tooltip) {
			ttcode = '';
			for (var key in item_array.tooltip) {
				value = item_array.tooltip[key];
				value = typeof(value) == 'string'
					? lib_escape(item_array.tooltip[key])
					: value;
				ttcode += ',"'+key+'",'+value;
			}
			tooltip_open_code = 'ddmTT_activate(this,in_event'+ttcode+');';
			actions.mousemove = 'ddmTT_mousemove(this,in_event);';
		}


		//
		// Set global actions used by every item
		//
		actions.mouseover += 'if (!'+get_mouseover+') {'
				+ set_mouseover_true + tooltip_open_code +
				'if (!'+menu_open_bool_eval+') {' +
					set_style_rollover +
				'} else {' +
					set_style_menuopen_rollover +
				'}' +
			'}';
		actions.mouseout += set_mouseover_false + 'if (isNaN(in_event)) {' +
				'WebDDM_mouseout_timeout["'+this_item_id+'"] = dom_setTimeout(function () {' +
					'if (!'+get_mouseover+') {if ('+menu_open_bool_eval+') {' + set_style_menuopen_out + '} else {' + set_style_out + '}' +
					tooltip_close_code_eval + '}' +
				'},10);' +
			'} else {' +
				'if ('+menu_open_bool_eval+') {' + set_style_menuopen_out + '} else {' + set_style_out + '}' +
				tooltip_close_code_eval +
			'}';

		// Add custom user actions to the vars
		for (var k in (item_array.actions || {}))
		{
			k = k.replace(/^on/, '');
			actions[k] = (actions[k] || '') + item_array.actions[k];
		}

		//
		// ------------------------------------------------------
		// Build item
		// ------------------------------------------------------
		//

		// Get IE CSS filters
		filter = item_array.filter && isIE ? 'filter:'+item_array.filter+';' : '';

		// Get item class
		itemclass = (item_array['class'] ? 'class="'+item_array['class']+'"' : '');

		// Get cursor and correct it
		// If it is hand or pointer, make sure that in IE it's 'hand' and in
		// gecko browsers it's 'pointer'
		cursor = (isset(cursor = item_array.cursor)
			? (cursor.match(/hand|pointer/)
				? dom_stylePointer
				: cursor)
			: 'default');

		// Get item CSS
		css = 'cursor:'+cursor+';' + item_array.css;

		HTML += // Build item container
			'<div id="'+this_item_id+'_item_container" ' +
			'style="visibility:inherit;position:absolute;' +
			'top:'+item_array.top+'px;left:'+item_array.left+'px;'+
			'margin:0px;z-index:'+(WebDDM_zIndexes[container_id]++)+';' +
			'width:'+item_array.width+'px;height:'+item_array.height+'px;'+filter+';">' +
			// Build item
			'<table style="width:100%;height:100%;background-color:transparent;" cellpadding="0" cellspacing="0"><tbody><tr><td id="'+this_item_id+'_item" '+itemclass+' style="width:'+item_array.width+';height:'+item_array.height+';'+css+'">'+item_array.content+'</td></tr></tbody></table></div>';

		// Set final actions in the actions array
		actions_array[item_index] = actions;
	} // End for items

	// Create the item container
	items_container = appendEl(container_id, createEl('div'));
	items_container.id = item_id + '_subitems';
	items_container.innerHTML = HTML;
	eval('items_container.onmouseover = function () { WebDDM_mouseoverStatus("'+item_id+'subitems", true, true); };');
	eval('items_container.onmouseout = function () { WebDDM_mouseoverStatus("'+item_id+'subitems", false, true); };');
	ic_style = items_container.style;
	ic_style.position = 'absolute';
	ic_style.top = items_array.top +
		(menu_array.parent_id
			? WebDDM_getItemArray(container_id, menu_array.parent_id).items.top
			: 0) + 'px';
	ic_style.left = items_array.left +
		(menu_array.parent_id
			? WebDDM_getItemArray(container_id, menu_array.parent_id).items.left
			: 0) + 'px';
	ic_style.height = items_array.height + 'px';
	ic_style.width = items_array.width + 'px';
	ic_style.visibility = ic_style.overflow = 'hidden';
	ic_style.zIndex = (WebDDM_zIndexes[container_id]++) - item_index;
	ic_style.borderStyle = 'none';
	ic_style.backgroundImage = 'url(-)'; // This is a fake image. This is needed for IE to execute the onmouseover/onmouseout code!
	ic_style.opacity = ic_style.MozOpacity = ic_style.KhtmlOpacity = (items_array.max_opacity / 100);
	if (isIE) {
		ic_style.filter = 'alpha(opacity='+items_array.max_opacity+') ' + (items_array.filter || '');
	}

	// Loop through actions array and set final actions for items
	for (var i in actions_array)
	{
		o = getEl(item_id+'_'+i+'_item_container');
		for (var action_name in actions_array[i])
		{
			eval('o.on'+action_name+' = function(in_event){in_event=isset(in_event)?in_event:event;'+actions_array[i][action_name]+'};');
		}
	}
} // End function WebDDM_buildMenu



//
// ------------------------------
// SECTION: MENU CONTROLS
// ------------------------------
//

/**
* Returns the item array of the specified item.
*
* @param	str container_id
* @param	str item_id
*
* @return	array
*
* @access	private
*/
function WebDDM_getItemArray (container_id, item_id)
{
	// Get item ID
	item_id = container_id == item_id ? '' : item_id;

	// Remove container ID from item ID
	bareitem_id = item_id.replace(container_id+'_', '');

	if (!isset(WebDDM_Activated_Menus[container_id][bareitem_id]))
	{
		evalcode = 'item_array = WebDDM_Activated_Menus[container_id]';
		if (item_id != '')
		{
			var chunks = bareitem_id.split('_');
			for (var i = 0; i < chunks.length; i++)
			{
				evalcode += '.items['+chunks[i]+']';
			}
		}
		evalcode += ';';
		eval(WebDDM_Activated_Menus[container_id][bareitem_id] = evalcode);
	}
	else
	{
		eval(WebDDM_Activated_Menus[container_id][bareitem_id]);
	}
	return item_array;
}

/**
* IF you pass the second argument, then the mouseover
* status of the item will be set to that. ELSE, the
* current mouseover status (true or false) will be
* returned.
*
* @param	str item_id
* @param	bool status If this is not set, the current mouseover status of the item will be returned.
* @param	bool doNotStoreAsLast If this is set to TRUE, this item_id will not be stored in the WebDDM_last_mouseover variable
*
* @return	bool
*
* @access	private
*/
var WebDDM_mouseoverStatuses = {}, WebDDM_last_mouseover;
function WebDDM_mouseoverStatus (item_id, status, doNotStoreAsLast)
{
	// If no mouseover status was provided, return current value
	if (!isset(status)) { return WebDDM_mouseoverStatuses[item_id] || false; }

	// Set last mouseover to "false" since if we're over a different item,
	// we're not over the last one now. Also execute it's onmouseout code.
	if (!doNotStoreAsLast)
	{
		if (WebDDM_last_mouseover && WebDDM_last_mouseover != item_id)
		{
			WebDDM_mouseoverStatuses[WebDDM_last_mouseover] = false;
			getEl(WebDDM_last_mouseover + '_item_container').onmouseout(0);
		}

		// Set last mouseover ID
		WebDDM_last_mouseover = item_id;
	}

	// Set current mouseover status
	WebDDM_mouseoverStatuses[item_id] = status;
}


/**
* Returns true if the item's submenus can be closed, false if not.
*
* @param	obj Item_Array
*
* @return	bool
*
* @access	private
*/
function WebDDM_canHide (Item_Array)
{
	// If the item ID is false, that means that this item
	// has likely not been built, so we can close it
	if (!(item_id = Item_Array.item_id || Item_Array.container_id))
	{
		return true;
	}

	// Can the menu be closed at all?
	// It cannot if contract_menu is not set, set to none, or set to click;
	// and it cannot if the mouse is over this item.
	var contract = Item_Array.contract_menu;
	if ((!isset(contract) ||
		!contract.match(/rollout/)) ||
		WebDDM_mouseoverStatus(item_id) ||
		WebDDM_mouseoverStatus(item_id + 'subitems'))
	{
		return false;
	}
	// See if any subitems can be hidden
	if (isset(Item_Array.items))
	{
		for (var i = 1; isset(Item_Array.items[i]); i++)
		{
			if (!WebDDM_canHide(Item_Array.items[i]))
			{
				return false;
			}
		}
	}

	// Subitems can be closed
	return true;
}

/**
* Returns true if the item's submenu is open or false if it is not.
*
* @param	str item_id
*
* @return	bool
*
* @access	private
*/
function WebDDM_menuOpen (item_id)
{
	return (!getEl(item_id + '_subitems') || (isset(alphaAPIobj = WebDDM_alphaAPI_objects[item_id+'_subitems']) && alphaAPIobj.started && alphaAPIobj.startAlpha > alphaAPIobj.stopAlpha)) ? false : getEl(item_id + '_subitems').style.visibility == 'visible';
}

/**
* Sets the item's styles to '' (out), 'rollover', 'open', 'menuopen_out', or 'menuopen_rollover'
*
* @param	str container_id
* @param	str item_id
* @param	str style
*
* @access	private
*/
var WebDDM_style = {};
function WebDDM_itemStyle (container_id, item_id, style)
{
	// Make sure we "save" the style for this item
	// This is so we don't change the style to the same style multiple times
	IDs = container_id+'_'+item_id;
	if (isset(WebDDM_style[IDs]) && WebDDM_style[IDs] == style)
	{
		return;
	}
	WebDDM_style[IDs] = style;

	// Get item array
	item_array = WebDDM_getItemArray(container_id, item_id);

	// Get element
	element = getEl(item_id+'_item');

	// Change class
	if (isset(item_array['class'+style]))
	{
		element.className = item_array['class'+style];
	}

	// Change CSS
	// First, get an array from the CSS string
	if (item_array['css'+style])
	{
		css = parseCSS_string(item_array['css'+style]);
		for (var k in css)
		{
			try {
			    	element.style[interCap(k)] = css[k];
			} catch (e) { }
		}
	}

	// Change content
	if (isset(item_array['content'+style]))
	{
		element.innerHTML = item_array['content'+style];
	}
} // End function WebDDM_itemStyle

/**
* Places an item on the cursor.
*
* @param	str element ID
* @param	int top
* @param	int left
*
* @access	private
*/
function WebDDM_setAtCursor (elID, top, left)
{
	// Calculate mouse positions
	var t = dom_mousePosition.y + top;
	var l = dom_mousePosition.x + left;

	// Set element position
	(element = getEl(elID).style).top = t + 'px';
	element.left = l + 'px';

	// Reset item array offsets
	if (isset(WebDDM_Activated_Menus[elID]['float']) && WebDDM_Activated_Menus[elID]['float'] == true)
	{
		// Set floating offsets, minus the page offsets
		WebDDM_Activated_Menus[elID].float_top = t - (document.body.scrollTop || window.pageYOffset);
		WebDDM_Activated_Menus[elID].float_left = l- (document.body.scrollLeft || window.pageXOffset);
	}

	// Stop floating
	if (floatAPI_floaters[elID])
	{
		dom_clearTimeout(floatAPI_floaters[elID]);
		delete floatAPI_floaters[elID];
	}
}

/**
* This code is called when the mouse rolls over a menu that expands
* with a click. Check if any menus are open on the same level as this
* item; if there are, and if they too open with click, close them and
* open this one.
*
* @param	str container_id
* @param	str item_id
*/
function WebDDM_checkClickMenu(container_id, item_id)
{
	// Get item array
	var item_array = WebDDM_getItemArray(container_id, item_id);

	// Make sure menu level is present
	if (!isset(item_array.menu_level))
	{
		// Get menu level
		item_array.menu_level = bareitem_id ? bareitem_id.split('_').length : 0;
	}

	// Is there a menu open on this level?
	// Close all menus on/above this level
	if (item_array.menu_level > 0)
	{
		for (var i = maxkey(open_menu_data[container_id]); i > 0; i--)
		{
			data = open_menu_data[container_id][i];
			if (data.menu_level == item_array.menu_level && data.item_id != item_id)
			{
				// There is a menu open on this level. Does it expand
				// with onclick?
				if (data.expand_menu.match('click'))
				{
					if (data.contract_menu.match('click'))
					{
						// Open the menu of the item that was just rolled over
						// This will also close any menus on or above this level
						WebDDM_showMenu(container_id, item_id);
						// Break loop
						break;
					}
					else
					{
						// Close all menus on/above this level
						WebDDM_closeMenusOnLevel(container_id, item_array);
					}
				} // End if menu on this level expands with click
			} // End if menu_level above this menu_level
		} // End for array_keys
	} // End if menu_level above 0
} // End function WebDDM_checkClickMenu

/**
* Closes all the menus on/above a level, unless they match the item
* ID in the item array passed.
*
* @param	str container_id
* @param	obj item_array
*
*/
function WebDDM_closeMenusOnLevel (container_id, item_array)
{
	if (item_array.menu_level > 0)
	{
		for (var i = maxkey(open_menu_data[container_id]); i > 0; i--)
		{
			data = open_menu_data[container_id][i];
			if (data.menu_level >= item_array.menu_level && data.item_id != item_array.item_id)
			{
				WebDDM_hideMenu(container_id, data.item_id);
			} // End if menu_level above this menu_level
		} // End for array_keys
	} // End if menu_level above
} // End function WebDDM_closeMenusOnLevel


/**
* Opens an item's submenu.
*
* @param	str container_id
* @param	str item_id
*
* @access	private
*/
var open_menu_data = {}, WebDDM_cleaning_menus = {};
function WebDDM_showMenu(container_id, item_id)
{
	// Make sure array keys and open_menu_data arrays are present
	if (!isset(open_menu_data[container_id]))
	{
		open_menu_data[container_id] = new Array;
	}

	// Get item array and make sure Item ID is present
	var item_array = WebDDM_getItemArray(container_id, item_id = item_id || '');

	if (!isset(item_array.max_subitem_id))
	{
		// Get menu level
		item_array.menu_level = bareitem_id ? bareitem_id.split('_').length : 0;
		// Get max subitem ID
		item_array.max_subitem_id = 0;
		if (isset(item_array.items))
		{
			while (isset(item_array.items[(item_array.max_subitem_id+1)]))
			{
				item_array.max_subitem_id++;
			}
		}
		// Get parent ID
		item_array.parent_id = item_id.match(/^(.*?)\_[0-9]+$/) ? RegExp.$1 : '';
		// Set item and container IDs in the item array
		item_array.item_id = item_id;
		item_array.container_id = container_id;
	}

	// Close all menus on/above this level
	WebDDM_closeMenusOnLevel(container_id, item_array);

	// If there are no subitems, stop now
	if (item_array.max_subitem_id == 0)
	{
		return;
	}

	// Get key for this array
	var this_menu_open = false;
	var this_array_id;
	for (var i = 1; i <=  maxkey(open_menu_data[container_id]); i++)
	{
		// This menu is already open;
		// Make sure we do not add the menu ID to the array a second time
		if (open_menu_data[container_id][i].item_id == item_id)
		{
			this_array_id = i;
			this_menu_open = true;
			break;
		}
	}

	// Get Item Or Container ID
	var IOCID = item_id || container_id;

	// Build this set of items if needed
	if (!getEl(IOCID+'_subitems'))
	{
		WebDDM_buildMenu(item_array);
	}

	// The menu has not been opened yet.
	// Assign it a new array key.
	if (this_menu_open == false)
	{
		this_array_id = open_menu_data[container_id].length;

		// Change style to open
		if (item_id)
		{
			WebDDM_itemStyle(container_id, item_id, '_menuopen');
		}
		else if (item_array.contract_menu && item_array.contract_menu.match('rollout'))
		{
			// There is no item_id, meaning that this is the base menu
			// Set the mouseover status to true
			// The menu closes on rollout, meaning that
			// (a) the menu is a context menu or (b) the user
			// is a crackhead. Assume A - the first item
			// will likely be placed directly under the cursor,
			// and the onmouseover code will not be executed
			// right away - so the mouseover status will be
			// FALSE, and the menu will be closed right away.
			WebDDM_mouseoverStatus(container_id + '_1', true);
		}
	}

	// Put this menu ID in the open menu data array
	open_menu_data[container_id][this_array_id] = item_array;

	// Show subitems
	WebDDM_toggleVisibility(item_array.items, item_array.items.max_opacity, IOCID + '_subitems', true);
	
	// If there are any subitems that have "expand_menu" set to "auto",
	// open them now. Originally I did this in WebDDM_buildMenu, but that
	// was buggy because then the CHILD array would be placed in the open_menu_data
	// array before the parent. The child would never be able to be hidden
	// because it would be hidden by the parent.
	if (item_array.items)
	{
		for (i in item_array.items)
		{
			// Check if the expand_menu of the subitem is set to auto
			expand_menu = item_array.items[i].expand_menu;
			if (expand_menu && expand_menu.match('auto'))
			{
				// Get subitem ID
				subitem_id = (item_id || container_id) + '_' + i;
				// Show subitem's subitems
				WebDDM_showMenu(container_id, subitem_id);
				// Set mouseover status of subitem to true if 
				// the subitem's subitems close with rollout.
				contract_menu = item_array.items[i].contract_menu;
				if (contract_menu && contract_menu.match('rollout'))
				{
					WebDDM_mouseoverStatus(subitem_id, true);					
				}
			} // End if expand_menu contains 'auto'
		} // End for loop through item_array.items
	} // End if subitems exist

	// Start checking if there are any menus that can be closed
	dom_setTimeout(function () {WebDDM_cleanMenus(container_id);}, 100);
} // End function WebDDM_showMenu

/**
* Closes an item's submenu if it's possible.
*
* @param	str container_id
*
* @access	private
*/
function WebDDM_cleanMenus (container_id)
{
	// Stop cleaning timer
	dom_clearTimeout(WebDDM_cleaning_menus[container_id]);
	// Get open menu data and max array key
	timeoutDelay = 1000;
	if (data = open_menu_data[container_id][maxkey(open_menu_data[container_id])])
	{
		// Try to close the menu last opened
		// If the menu can be closed in 1 millisecond... close it!
		dom_setTimeout(function ()
		{
			if (WebDDM_canHide(data))
			{
				WebDDM_hideMenu(container_id, data.item_id);
			}
		}, timeoutDelay = 1);
	}

	// If the first menu can be closed, close all menus above it
	// First get the first ID of this menu. In most cases, except
	// right-click context menus, it will be 2.
	if (first_el = open_menu_data[container_id][1])
	{
		var first_id = !isset(first_el.contract_menu) || first_el.contract_menu.match(/none|click/) ? 2 : 1;
		if (isset(first_id_data = open_menu_data[container_id][first_id]) && WebDDM_canHide(first_id_data))
		{
			WebDDM_hideAll(container_id, first_id);
		}
	}

	// Recurse
	WebDDM_cleaning_menus[container_id] = dom_setTimeout(function(){WebDDM_cleanMenus(container_id);}, timeoutDelay);
}

/**
* Closes an item's submenu.
*
* @param	str container_id
* @param	str item_id
*
* @access	private
*/
function WebDDM_hideMenu(container_id, item_id)
{
	// Since the menu we're closing should ALWAYS be the last in the open
	// menu data array, don't loop through the array and look for the item_id
	// (as we did in WebDDM 2). Just close the last menu.


	// Get item array
	if (item_array = open_menu_data[container_id][maxkey(open_menu_data[container_id])])
	{
		// Make sure passed item_id matches the item ID in the item array
		if (item_array.item_id != item_id)
		{
			return;
		}

		// Change styles
		if (item_array.item_id)
		{
			WebDDM_itemStyle(container_id, item_array.item_id, WebDDM_mouseoverStatus(item_array.item_id)
				? '_rollover'
				: '');
		}

		// Hide all the items
		WebDDM_toggleVisibility(item_array.items, item_array.items.max_opacity,
			(item_array.item_id || container_id) + '_subitems', false);

		open_menu_data[container_id].pop();
	}
}

/**
* Closes all subitems.
*
* @param	str container_id
* @access	private
*/
function WebDDM_hideAll (container_id, first_id)
{
	// Get open menu data
	var data = open_menu_data[container_id];

	// Prevent errors
	if (!data || !data[first_id || 1]) {
		return;
	}

	// Get first ID
	// If the first menu can be closed, close all menus above it
	// First get the first ID of this menu. In most cases, except
	// right-click context menus, it will be 2.
	var first_id = first_id || (!isset(data[1].contract_menu) || data[1].contract_menu.match('none') ? 2 : 1);

	// Hide all items
	for (var i = maxkey(open_menu_data[container_id]); i >= first_id; i--)
	{
		// Set mouseover status to false, since we know we're in the document,
		// not the item. Also, there are some cases where the mouseover status
		// is set to TRUE automatically and the mouse may not necessarily be oevr
		// the item. This fixes any possible bugs.
		WebDDM_mouseoverStatus(data[i].item_id, false);
		
		// Hide menu
		WebDDM_hideMenu(container_id, data[i].item_id);
	}
} // End function WebDDM_hideAll

/**
* Toggles the visibility of an element. Fades in/out if needed.
*
* @param	Array item_array
* @param	int max_opacity
* @param	str element_id
* @param	bool is_visible
*
* @access	private
*/
var WebDDM_alphaAPI_objects = {};
function WebDDM_toggleVisibility(item_array, max_opacity, element_id, is_visible)
{
	element = getEl(element_id);
	fade_obj = WebDDM_alphaAPI_objects[element_id];

	// Detect collisions with select objects
	dom_detectCollisions(getEl(element_id), !is_visible);

	// Can we fade?
	fade = item_array.fade;
	if (dom_canFade && item_array.fade_delta && ((is_visible && item_array.fade_in_delay && fade.match(/in|both/)) || (!is_visible && item_array.fade_out_delay && fade.match(/out|both/))))
	{
		// Initialize fader object if needed
		if (isset(fade_obj))
		{
			fade_obj.pause();
		}
		else
		{
			fade_obj = WebDDM_alphaAPI_objects[element_id] = new alphaAPI(element, item_array.fade_in_delay, item_array.fade_out_delay, 0, 0, 0, item_array.fade_delta, true);
			fade_obj.setAlpha(0);
		}

		var current_alpha = fade_obj.getAlpha();
		if (is_visible)
		{
			// Fade this item in
			fade_obj.startAlpha = current_alpha <= max_opacity ? current_alpha : 0;
			fade_obj.stopAlpha = max_opacity;
		}
		else
		{
			// Fade this item out
			fade_obj.startAlpha = current_alpha;
			fade_obj.stopAlpha = 0;
		}
		fade_obj.start();
	}
	// Do not fade
	else
	{
		element.style.visibility = is_visible ? "visible" : "hidden";
		if (isset(fade_obj))
		{
			fade_obj.stop();
			fade_obj.setAlpha(is_visible ? max_opacity : 0);
		}
	}
} // End WebDDM_toggleVisibility()


//
// ------------------------------
// SECTION: MENU FLOATING
// floatAPI - Created by JPortal for WebDDM
// ------------------------------
//

/**
* Updates positions of floating menus.
*
* @access	public
*/
var last_pageYOffset, last_pageXOffset;
var floatAPI_floating_elements = new Array;
function floatAPI_controlFloaters ()
{
	// Get page offsets
	var pageYOffset = document.body.scrollTop || window.pageYOffset;
	var pageXOffset = document.body.scrollLeft || window.pageXOffset;

	// Compare current page offsets to last page offsets
	if (last_pageYOffset != pageYOffset || last_pageXOffset != pageXOffset)
	{
		// Window has scrolled! Update positions
		// Loop through all floating menus
		for (var i = 0; i < floatAPI_floating_elements.length; i++)
		{
			// Get vars
			var container_id = floatAPI_floating_elements[i];
			var menu_array = WebDDM_Activated_Menus[container_id];

			// Float menu back into view
			if (!floatAPI_floaters[container_id])
			{
				floatAPI_slideFloater(container_id, menu_array.float_top, menu_array.float_left, menu_array.float_speed, menu_array.float_smoothness, true, false);
			}
		}
	}

	// Update last positions
	last_pageYOffset = pageYOffset;
	last_pageXOffset = pageXOffset;

	// Recurse
	dom_setTimeout(function () {floatAPI_controlFloaters()}, 500);
}


/**
* Slides floating menus.
*
* @param	str element_id ID of the element to float.
* @param	int offset_top Offset TOP from the default position
* @param	int offset_left Offset LEFT from the default position
* @param	int speed Speed to float at
* @param	int smoothness Smoothness of float. Usually 5-10 is good, anymore makes floating too slow.
* @param	bool useScreenOffset Use the screen offset for the default position? If TRUE, the default position will ALWAYS be the upper left hand corner of the screen.
* @param	bool useMousePosition Use the mouse position for the default position?
*
* @access	private
*/
var floatAPI_floaters = {};
function floatAPI_slideFloater (element_id, offset_top, offset_left, speed, smoothness, useScreenOffset, useMousePosition)
{
	// Stop the timeout right away if we can
	dom_clearTimeout(floatAPI_floaters[element_id]);
	delete floatAPI_floaters[element_id];

	// Get page offsets
	var pageYOffset = isIE ? document.body.scrollTop : window.pageYOffset;
	var pageXOffset = isIE ? document.body.scrollLeft : window.pageXOffset;

	// Get current positions of menu container
	var currentContainerPosTop = parseInt(getEl(element_id).style.top);
	var currentContainerPosLeft = parseInt(getEl(element_id).style.left);

	// Get the position that the container SHOULD be at
	var shouldbeContainerPosTop = (useScreenOffset ? pageYOffset : (useMousePosition ? dom_mousePosition.y : 0)) + offset_top;
	var shouldbeContainerPosLeft = (useScreenOffset ? pageXOffset : (useMousePosition ? dom_mousePosition.x : 0)) + offset_left;

	//
	// Get differences between positions
	//
	if (smoothness > 1)
	{
		if (currentContainerPosTop > shouldbeContainerPosTop)
		{
			var differenceTop = currentContainerPosTop - shouldbeContainerPosTop;
			var posTopNow = differenceTop > 5 ? currentContainerPosTop - (differenceTop / smoothness) - 1 : shouldbeContainerPosTop;
		}
		else
		{
			var differenceTop = shouldbeContainerPosTop - currentContainerPosTop;
			var posTopNow = differenceTop > 5 ? currentContainerPosTop + (differenceTop / smoothness) + 1 : shouldbeContainerPosTop;
		}
		if (currentContainerPosLeft > shouldbeContainerPosLeft)
		{
			var differenceLeft = currentContainerPosLeft - shouldbeContainerPosLeft;
			var posLeftNow = differenceLeft > 5 ? currentContainerPosLeft - (differenceLeft / smoothness) - 1 : shouldbeContainerPosLeft;
		}
		else
		{
			var differenceLeft = shouldbeContainerPosLeft - currentContainerPosLeft;
			var posLeftNow = differenceLeft > 5 ? currentContainerPosLeft + (differenceLeft / smoothness) + 1 : shouldbeContainerPosLeft;
		}
	}
	else
	{
		posTopNow = shouldbeContainerPosTop;
		posLeftNow = shouldbeContainerPosLeft;
		// Slide more
		floatAPI_floaters[element_id] = dom_setTimeout(function ()
		{
			floatAPI_slideFloater(element_id, offset_top, offset_left, speed, smoothness, useScreenOffset, useMousePosition);
		}, speed);
	}

	// Set positions
	var element = getEl(element_id);
	element.style.top = posTopNow + 'px';
	element.style.left = posLeftNow + 'px';

	// Set new positions
	// Only update if there is a difference between where
	// the container is and where it should be
	if (differenceLeft > 0 || differenceTop > 0)
	{
		// Get total difference
		var differenceTotal = differenceLeft + differenceTop;

		// Slide more
		floatAPI_floaters[element_id] = dom_setTimeout(function ()
		{
			floatAPI_slideFloater(element_id, offset_top, offset_left, speed, smoothness, useScreenOffset, useMousePosition);
		}, differenceTotal > 150 ? speed / 12 : (differenceTotal > 50 ? speed / 6 : speed));
	}
}


//
// ------------------------------
// SECTION: FADING
// alphaAPI - Original Author: chrisken
// Original Url: http://www.cs.utexas.edu/users/chrisken/alphaapi.html
// ------------------------------
//
function alphaAPI(element, fadeInDelay, fadeOutDelay, startAlpha, stopAlpha, offsetTime, deltaAlpha)
{
	// {{{ properties
	this.element = typeof(element) == 'object' ? element : getEl(element);
	this.fadeInDelay = fadeInDelay || 40;
	this.fadeOutDelay = fadeOutDelay || this.fadeInDelay;
	this.startAlpha = startAlpha;
	this.stopAlpha = stopAlpha;
	// make sure a filter exists so an error is not thrown
	if (typeof(this.element.filters) == 'object' && !isset(this.element.filters.alpha))
	{
		this.element.style.filter += 'alpha(opacity=100)';
	}
	this.offsetTime = (offsetTime || 0) * 1000;
	this.deltaAlpha = deltaAlpha || 10;
	this.timer = this.paused = this.started = false;
	// }}}
	// {{{ setAlphaBy()
	this.setAlphaBy = function(deltaAlpha)
	{
		this.setAlpha(this.getAlpha() + deltaAlpha);
	};
	// }}}
	// {{{ SetTimeout()
	this.SetTimeout = function(command, delay)
	{
		if (!this.timer)
		{
			this.timer = dom_setTimeout(command, delay);
		}
	};
	// }}}
	// {{{ setAlpha()
	this.setAlpha = function(opacity)
	{
		if (isIE)
		{
			this.element.filters.alpha.opacity = opacity; // Supported by IE4+
		}
		else
		{
			opacity = (opacity < 100 ? opacity : 99) / 100; // For Mozilla < 1.7/FireFox < 0.9 bug
			this.element.style.MozOpacity = // Supported by Gecko browsers
			this.element.style.KhtmlOpacity = // Supported by Safari 1.1
			this.element.style.opacity = opacity; // Supported by FireFox 0.9+/Mozilla 1.7+/Safari 1.2+/CSS3
		}
	};
	// }}}
	// {{{ getAlpha()
	this.getAlpha = function()
	{
		return isIE ? this.element.filters.alpha.opacity : this.element.style.opacity*100;
	};
	// }}}
	// {{{ start()
	this.start = function()
	{
		this.stopTimer();
		this.setAlpha(this.startAlpha);
		var instance = this;
		this.started = true;
		// determine direction
		if (this.startAlpha > this.stopAlpha)
		{
			this.SetTimeout(function() { instance.fadeOut(); }, this.offsetTime);
		}
		else
		{
			this.SetTimeout(function() { instance.fadeIn(); }, this.offsetTime);
		}
	};
	// }}}
	// {{{ stop()
	this.stop = function()
	{
		this.started = false;
		this.setAlpha(this.stopAlpha);
		this.stopTimer();
	};
	// }}}
	// {{{ pause()
	this.pause = function()
	{
		this.paused = true;
		this.stopTimer();
	};
	// }}}
	// {{{ stopTimer()
	this.stopTimer = function()
	{
		if (this.timer)
		{
			dom_clearTimeout(this.timer);
			this.timer = null;
		}
	};
	// }}}
	// {{{ fadeOut()
	this.fadeOut = function()
	{
		this.stopTimer();
		if (this.getAlpha() > this.stopAlpha)
		{
			this.setAlphaBy(-1 * this.deltaAlpha);
			var instance = this;
			this.SetTimeout(function() { instance.fadeOut(); }, this.fadeOutDelay);
		}
		else
		{
			this.element.style.visibility = this.getAlpha() < 1 ? 'hidden' : 'visible';
			this.started = false;
		}
	};
	// }}}
	// {{{ fadeIn()
	this.fadeIn = function()
	{
		this.stopTimer();
		if (this.getAlpha() < this.stopAlpha || this.getAlpha() < this.startAlpha)
		{
			this.element.style.visibility = 'visible';
			this.setAlphaBy(this.deltaAlpha);
			var instance = this;
			this.SetTimeout(function() { instance.fadeIn(); }, this.fadeInDelay);
		}
		else
		{
			this.started = false;
		}
	};
	// }}}
	return this;
}

//
// ------------------------------
// SECTION: TOOLTIP
// domTT - Original Author: Dan Allen
// Original Url: www.mojavelinux.com
// DEPENDENCIES: Function/variable library, domTT_drag, alphaAPI
// ------------------------------
//


// {{{ Settings
var ddmTT_default_options = {'caption':'','content':'','closeLink':'X',
	'type':'greasy','drag':false,'direction':'southeast',
	'delay':500,'classPrefix':'','lifetime':0,'grid':0,'fade':'neither',
	'fadeDelay':50,'fadeDelta':10,'maxOpacity':100,'trail':false,
	'offsetY':2,'offsetX':0,'screenEdgePadding':5,'maxWidth':300,

	// Non options
	'lastX':0,'lastY':0
};

// }}}
// {{{ Global constants

/**
 * Global constants
 */
var ddmTT_tooltips = {};

// }}}
// {{{ ddmTT_activate()
function ddmTT_activate(owner, in_event)
{
	var tooltip = ddmTT_tooltips[owner.id];
	if (tooltip)
	{
		if (tooltip.eventType != in_event.type)
		{
			if (tooltip.type == 'greasy')
			{
				ddmTT_deactivate(owner.id);
			}
			else if (tooltip.status != 'inactive')
			{
				return owner.id;
			}
		}
		else
		{
			if (tooltip.status == 'inactive')
			{
				tooltip.status = 'pending';
				tooltip.activateTimeout = dom_setTimeout(function(argv) {
					ddmTT_show(argv[0], argv[1]);
				}, tooltip.delay, [owner.id, in_event.type]);

				return owner.id;
			}
			// either pending or active, let it be
			else
			{
				return owner.id;
			}
		}
	}

	// setup the default options array
	var options = merge({}, ddmTT_default_options);
	options.parent = document.body;

	for (var i = 2; i < arguments.length; i += 2)
	{
		// set option
		options[arguments[i]] = (typeof(s = arguments[i + 1]) == 'string' ? unescape(s) : s);
	}

	options.eventType = in_event.type;

	options.owner = owner;
	options.id = '[domTT]' + owner.id;
	ddmTT_create(options);
	// determine the show delay
	options.delay = parseInt(options.delay);
	ddmTT_tooltips[owner.id] = options;
	options.status = 'pending';
	options.activateTimeout = dom_setTimeout(function(argv) {
		ddmTT_show(argv[0], argv[1]);
	}, options.delay, [owner.id, in_event.type]);

	return owner.id;
}
// }}}
// {{{ ddmTT_create()
function ddmTT_create(in_options)
{
	var owner = in_options.owner;

	// create the tooltip and hide it
	var tipObj = appendEl(document.body, createEl('div'));
	tipObj.style.position = 'absolute';
	tipObj.style.left = '0px';
	tipObj.style.top = '0px';
	tipObj.style.visibility = 'hidden';
	tipObj.id = in_options.id;
	tipObj.className = in_options.classPrefix + 'Container';

	// Get some IDs
	var tipContentID = in_options.id + 'Content';
	var tipCaptionRowID = in_options.id + 'CaptionRow';
	var tipCloseLinkID = in_options.id + 'CloseLink';

	if (in_options.caption || (in_options.type == 'sticky' && in_options.caption !== false))
	{
		// For Konqueror...
		var cellSpacing = isKonq ? ' cellspacing="0"' : '';

		// Layout the tip with a hidden formatting table
		var tipLayoutTableBegin = '<table style="border-collapse:collapse;"'+cellSpacing+'>' +
			'<tbody>';
		var tipLayoutTableEnd = '</tbody></table>';

		// Get captionRow HTML
		var numCaptionCells = 0;
		var captionExtraCSS = '';
		var captionRow = '';
		var captionRowClass = ' class="'+in_options.classPrefix+'Caption"';
		if (in_options.type == 'sticky')
		{
			numCaptionCells = 2;

			// Append close link to caption row
			captionRow = '<td style="padding:0px;"><div id="'+tipCloseLinkID+'"'+captionRowClass+
				' style="height:100%;text-align:right;cursor:'+dom_stylePointer+';border-left-width:0px;padding-left:0px;margin-left:0px;">' +
			in_options.closeLink +
			'</div></td>';
			captionExtraCSS = 'border-right-width:0px;padding-right:0px;margin-right:0px;';
		}
		captionRow = '<tr id="'+tipCaptionRowID+'"><td style="padding:0px;"><div'+captionRowClass+' style="height:100%;'+captionExtraCSS+'">'+in_options.caption+'</div></td>'+captionRow+'</tr>';

		// Build content row
		var colSpan = numCaptionCells ? ' colspan="'+numCaptionCells+'"' : '';
		var contentRow = '<tr><td style="padding:0px;"'+colSpan+'><div id="'+tipContentID+'" style="height:100%;">' +
			'</td></tr>';

		// Set HTML into tipObj
		tipObj.innerHTML = tipLayoutTableBegin + captionRow + contentRow + tipLayoutTableEnd;

		// Get objects by their IDs
		content = getEl(tipContentID);
		captionRow = getEl(tipCaptionRowID);

		// Set closeLink events
		if (closeLink = getEl(tipCloseLinkID))
		{
			closeLink.onclick = function(in_event) { in_event = isset(in_event) ? in_event : event; ddmTT_deactivate(owner.id); in_event.cancelBubble = true; };
			closeLink.onmousedown = function(in_event) { in_event = isset(in_event) ? in_event : event; in_event.cancelBubble = true; };
		}
	}
	else
	{
		var content = appendEl(tipObj, createEl('div'));
	}

	content.className = in_options.classPrefix + 'Content';
	content.onclick = function (in_event) { in_event = isset(in_event) ? in_event : event; in_event.cancelBubble = true; };
	content.innerHTML = in_options.content;

	// adjust the width if specified
	if (isset(in_options.width))
	{
		tipObj.style.width = parseInt(in_options.width) + 'px';
	}

	// check if we are overridding the maxWidth
	if (in_options.maxWidth === false)
	{
		tipObj.style.maxWidth = isOpera ? '10000px' : 'none';
	}
	else
	{
		maxWidthStr = (isIE50 || isKonq ? 'w' : 'maxW') + 'idth'; 
		tipObj.style[maxWidthStr] = in_options.maxWidth + 'px';
	}

	// tooltip floats
	var offset_x, offset_y;
	if (!(isset(in_options.x) && isset(in_options.y)))
	{
		// determine the offset relative to the pointer
		switch (in_options.direction)
		{
		case 'northeast':
			offset_x = in_options.offsetX;
			offset_y = 0 - tipObj.offsetHeight - in_options.offsetY;
			break;
		case 'northwest':
			offset_x = 0 - tipObj.offsetWidth - in_options.offsetX;
			offset_y = 0 - tipObj.offsetHeight - in_options.offsetY;
			break;
		case 'southwest':
			offset_x = 0 - tipObj.offsetWidth - in_options.offsetX;
			offset_y = dom_mouseHeight + in_options.offsetY;
			break;
		case 'southeast':
			offset_x = in_options.offsetX;
			offset_y = dom_mouseHeight + in_options.offsetY;
		}
	}
	// tooltip is fixed
	else
	{
		offset_x = offset_y = 0;
		in_options.trail = false;
	}

	// Set offset X and offset Y
	in_options.offsetX = offset_x;
	in_options.offsetY = offset_y;

	// Set offset width and height
	in_options.offsetWidth = tipObj.offsetWidth;
	in_options.offsetHeight = tipObj.offsetHeight;

	if (dom_canFade)
	{
		if (in_options.fade != 'neither')
		{
			// Max opacity: Argument 4
			// Fade delay in: Argument 2
			// Fade delay out: Argument 3
			// Fade jump (delta): Argument 7
			var fadeHandler = new alphaAPI(tipObj, in_options.fadeDelay,
				in_options.fadeDelay, in_options.maxOpacity, 0,
				null, in_options.fadeDelta);
			fadeHandler.setAlpha(0);
			in_options.fadeHandler = fadeHandler;
		}
	}
	else
	{
		in_options.fade = 'neither';
	}

	if (in_options.type == 'sticky')
	{
		if (in_options.drag)
		{
			// For IE only, but other browsers won't whine if we set this
			captionRow.onselectstart = function() { return false; };

			// setup drag
			captionRow.onmousedown = function(in_event) { in_event = isset(in_event) ? in_event : event; ddmTT_dragStart(tipObj, in_event); };
			captionRow.onmousemove = function(in_event) { in_event = isset(in_event) ? in_event : event; ddmTT_dragUpdate(in_event); };
			captionRow.onmouseup = function() { ddmTT_dragStop(); };
			captionRow.onclick = function (in_event) { in_event = isset(in_event) ? in_event : event; in_event.cancelBubble = true; };
		}
	}
	else if (in_options.type == 'velcro')
	{
		tipObj.onmouseout = function(in_event) { in_event = isset(in_event) ? in_event : event; if (!dom_isDescendantOf(in_event[isIE ? 'toElement' : 'relatedTarget'], tipObj)) { ddmTT_deactivate(owner.id); }};
	}

	appendEl(in_options.parent, tipObj);

	in_options.node = tipObj;
	in_options.status = 'inactive';
}
// }}}
// {{{ ddmTT_show()
function ddmTT_show(in_ownerId, in_eventType)
{
	// should always find one since this call would be cancelled if tip was killed
	var tooltip = ddmTT_tooltips[in_ownerId];
	var status = tooltip.status;
	var tipObj = tooltip.node;

	var mouse_x = (isset(tooltip.x) ? tooltip.x : dom_mousePosition.x);
	var mouse_y = (isset(tooltip.y) ? tooltip.y : dom_mousePosition.y);

	// we are using a grid for updates
	if (tooltip.grid)
	{
		// if this is not a mousemove event or it is a mousemove event on an active tip and
		// the movement is bigger than the grid
		if (in_eventType != 'mousemove' || (status == 'active' && (Math.abs(tooltip.lastX - mouse_x) > tooltip.grid || Math.abs(tooltip.lastY - mouse_y) > tooltip.grid)))
		{
			tooltip.lastX = mouse_x;
			tooltip.lastY = mouse_y;
		}
		// did not satisfy the grid movement requirement
		else
		{
			return false;
		}
	}

	var coordinates = ddmTT_correctEdgeBleed(tooltip.offsetWidth, tooltip.offsetHeight, mouse_x + tooltip.offsetX, mouse_y + tooltip.offsetY, tooltip.offsetX, tooltip.offsetY, tooltip.type, tooltip);

	// update the position
	tipObj.style.left = coordinates.x+'px';
	tipObj.style.top = coordinates.y+'px';

	// increase the tip zIndex so it goes over previously shown tips
	tipObj.style.zIndex = ddmTT_zIndex++;

	// if tip is not active, active it now and check for a fade in
	if (status == 'pending')
	{
		// unhide the tooltip
		tooltip.status = 'active';
		tipObj.style.display = '';
		tipObj.style.visibility = 'visible';

		var fade = tooltip.fade;
		if (fade != 'neither')
		{
			var fadeHandler = tooltip.fadeHandler;
			if (fade == 'out' || fade == 'both')
			{
				fadeHandler.pause();
				if (fade == 'out')
				{
					fadeHandler.reset();
				}
			}

			if (fade == 'in' || fade == 'both')
			{
				fadeHandler.fadeIn();
			}
		}

		// If the tooltip type is greasy, and tooltip lifetime is non-zero,
		// then set timeout for the tooltip to close itself.
		if (tooltip.type == 'greasy' && tooltip.lifetime)
		{
			tooltip.lifetimeTimeout = dom_setTimeout(function(argv) { ddmTT_deactivate(argv[0]); }, tooltip.lifetime, [in_ownerId]);
		}
	}

	dom_detectCollisions(tipObj);
}
// }}}
// {{{ ddmTT_deactivate()
function ddmTT_deactivate(in_ownerId)
{
	if (tooltip = ddmTT_tooltips[in_ownerId])
	{
		var status = tooltip.status;
		if (status == 'pending')
		{
			// cancel the creation of this tip if it is still pending
			dom_clearTimeout(tooltip.activateTimeout);
			tooltip.status = 'inactive';
		}
		else if (status == 'active')
		{
			if (tooltip.lifetime)
			{
				dom_clearTimeout(tooltip.lifetimeTimeout);
			}

			var tipObj = tooltip.node;
			var fade = tooltip.fade;
			if (fade != 'neither')
			{
				var fadeHandler = tooltip.fadeHandler;
				if (fade == 'out' || fade == 'both')
				{
					fadeHandler.pause();
					fadeHandler.fadeOut();
				}
				else
				{
					fadeHandler.stop();
				}
			}
			else
			{
				tipObj.style.display = 'none';
			}

			tooltip.status = 'inactive';
			// unhide all of the selects that are owned by this object
			dom_detectCollisions(tipObj, true);

			// Hide the tooltip by setting the position far off
			// If the tooltip is fading out we have to wait for it
			dom_setTimeout(function () { tipObj.style.left = -1000; tipObj.style.top = -1000; },
				fade == 'out' || fade == 'both' ? tooltip.fadeDelay * (100 / tooltip.fadeDelta) : 0);
		}
	}
}
// }}}
// {{{ ddmTT_mouseout()
function ddmTT_mouseout(in_owner)
{
	// Get tooltip and owner
	// Then if tooltip is valid, and the tooltip is greasy or inactive, deactive the tooltip
	if ((tooltip = ddmTT_tooltips[in_owner.id]) && (tooltip.type == 'greasy' || tooltip.status != 'active'))
	{
		ddmTT_deactivate(in_owner.id);
	}
}
// }}}
// {{{ ddmTT_mousemove()
function ddmTT_mousemove(in_owner, in_event)
{
	// Get tooltip object
	tooltip = ddmTT_tooltips[in_owner.id];
	if (tooltip && tooltip.trail && tooltip.status == 'active')
	{
		ddmTT_show(in_owner.id, (isset(in_event) ? in_event : event).type);
	}
}
// }}}
// {{{ ddmTT_correctEdgeBleed()
function ddmTT_correctEdgeBleed(in_width, in_height, in_x, in_y, in_offsetX, in_offsetY, in_type, in_options)
{
	var bleedRight, bleedBottom;
	var pageYOffset = isIE ? document.body.scrollTop : window.pageYOffset;
	var pageXOffset = isIE ? document.body.scrollLeft : window.pageXOffset;
	var pageHeight = document.documentElement.clientHeight || window.innerHeight || document.body.clientHeight;
	var pageWidth = document.documentElement.clientWidth || window.innerWidth || document.body.clientWidth;

	// we are bleeding off the right, move tip over to stay on page
	if ((bleedRight = (in_x - pageXOffset) + in_width - (pageWidth - in_options.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) < in_options.screenEdgePadding)
	{
		in_x = in_options.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 - in_options.screenEdgePadding)) > 0)
	{                                                                      	i
		in_y -= in_type == 'sticky' ? bleedBottom : in_height + (2 * in_offsetY) + dom_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) < in_options.screenEdgePadding)
	{
		in_y = in_type == 'sticky' ? in_options.screenEdgePadding + pageYOffset : in_y += in_height + (2 * in_offsetY) + dom_mouseHeight;
	}

	return {'x': in_x, 'y': in_y};
}
// }}}
// {{{ ddmTT_isActive()
function ddmTT_isActive(in_ownerId)
{
	return (tooltip = ddmTT_tooltips[in_ownerId]) && tooltip.status == 'active';
}
// }}}
// {{{ dom_detectCollisions()
// :WARNING: hideList is being used as an object property and is not a string
function dom_detectCollisions(in_object, in_recover)
{
	// no need to do anything for opera
	if (isOpera)
	{
		return;
	}

	if (!isset(dom_selectElements))
	{
		dom_selectElements = document.getElementsByTagName('select');
	}

	// if we don't have a tip, then unhide selects
	if (in_recover)
	{
		for (var cnt = 0; cnt < dom_selectElements.length; cnt++)
		{
			var thisSelect = dom_selectElements[cnt];

			thisSelect.hideList = thisSelect.hideList || {};

			// if this is mozilla and it is a regular select or it is multiple and the
			// size is not set, then we don't need to unhide
			if (isGecko && (!thisSelect.multiple || thisSelect.size < 0))
			{
				continue;
			}

			if (in_array(in_object.id, thisSelect.hideList) !== false)
			{
				delete thisSelect.hideList[in_array(in_object.id, thisSelect.hideList)];
			}
			if (!thisSelect.hideList.length)
			{
				dom_selectElements[cnt].style.visibility = 'visible';
			}
		}

		return;
	}

	// okay, we have a tip, so hunt and destroy
	var objectOffsets = dom_getOffsets(in_object);

	for (var cnt = 0; cnt < dom_selectElements.length; cnt++)
	{
		var thisSelect = dom_selectElements[cnt];

		// if this is mozilla and not a multiple-select or the multiple select size
		// is not defined, then continue since mozilla does not have an issue
		if (isGecko && (!thisSelect.multiple || thisSelect.size < 0))
		{
			continue;
		}

		// if the select is in the tip, then skip it
		// :WARNING: is this too costly?
		if (dom_isDescendantOf(thisSelect, in_object))
		{
			continue;
		}

		thisSelect.hideList = thisSelect.hideList || {};

		var selectOffsets = dom_getOffsets(thisSelect);
		// for mozilla we only have to worry about the scrollbar itself
		if (isGecko)
		{
			selectOffsets.left = selectOffsets.left + thisSelect.offsetWidth - dom_scrollbarWidth;
			selectOffsets.leftCenter = selectOffsets.left + dom_scrollbarWidth/2;
			selectOffsets.radius = Math.max(thisSelect.offsetHeight, dom_scrollbarWidth/2);
		}

		var center2centerDistance = Math.sqrt(Math.pow(selectOffsets.leftCenter - objectOffsets.leftCenter, 2) + Math.pow(selectOffsets.topCenter - objectOffsets.topCenter, 2));
		var radiusSum = selectOffsets.radius + objectOffsets.radius;
		// the encompassing circles are overlapping, get in for a closer look
		if (center2centerDistance < radiusSum)
		{
			// tip is left of select
			if ((objectOffsets.leftCenter <= selectOffsets.leftCenter && objectOffsets.right < selectOffsets.left) ||
			// tip is right of select
				(objectOffsets.leftCenter > selectOffsets.leftCenter && objectOffsets.left > selectOffsets.right) ||
			// tip is above select
				(objectOffsets.topCenter <= selectOffsets.topCenter && objectOffsets.bottom < selectOffsets.top) ||
			// tip is below select
				(objectOffsets.topCenter > selectOffsets.topCenter && objectOffsets.top > selectOffsets.bottom))
			{
				if (in_array(in_object.id, thisSelect.hideList) !== false)
				{
					delete thisSelect.hideList[in_array(in_object.id, thisSelect.hideList)];
				}
				if (!thisSelect.hideList.length)
				{
					thisSelect.style.visibility = 'visible';
				}
			}
			else
			{
				thisSelect.hideList[thisSelect.hideList.length] = in_object.id;
				thisSelect.style.visibility = 'hidden';
			}
		}
	}
}
// }}}
// {{{ dom_getOffsets()
function dom_getOffsets(in_object)
{
	var originalObject = in_object;
	var originalWidth = in_object.offsetWidth;
	var originalHeight = in_object.offsetHeight;
	var offsetLeft = 0, offsetTop = 0;

	while (in_object)
	{
		offsetLeft += in_object.offsetLeft;
		offsetTop += in_object.offsetTop;
		in_object = in_object.offsetParent;
	}

	return {
		'left':			offsetLeft,
		'top':			offsetTop,
		'right':		offsetLeft + originalWidth,
		'bottom':		offsetTop + originalHeight,
		'leftCenter':	offsetLeft + originalWidth/2,
		'topCenter':	offsetTop + originalHeight/2,
		'radius':		Math.max(originalWidth, originalHeight)
	};
}
// }}}
// {{{ dom_isDescendantOf()
function dom_isDescendantOf(in_object, in_ancestor)
{
	if (in_object == in_ancestor)
	{
		return true;
	}

	while (in_object != document.documentElement)
	{
		// There used to be a try {} block here,
		// If we have problems, just add it again...
		if (((tmp_object = in_object.offsetParent) && tmp_object == in_ancestor) || (tmp_object = in_object.parentNode) == in_ancestor)
		{
			return true;
		}
		else
		{
			in_object = tmp_object;
		}
	}

	return false;
}
// }}}
// {{{ dom_getEventPosition()
function dom_getEventPosition(in_eventObj)
{
	var eventPosition = {};
	// As far as I know, only Konqueror supports the "x" and "y"
	// objects. IE does too, but not in the desired way so we have
	// to explicitly check for Konqueror here.
	if (isKonq)
	{
		eventPosition.x = in_eventObj.x;
		eventPosition.y = in_eventObj.y;
	}
	// I think only IE supports these attributes,
	// however, as some people have stated, "it is arrogant
	// to assume that you know how all browsers will perform".
	// For this reason, we can simply check for the object here.
	else if (isset(in_eventObj.clientX))
	{
		if (document.documentElement.clientHeight)
		{
			eventPosition.x = in_eventObj.clientX + document.documentElement.scrollLeft;
			eventPosition.y = in_eventObj.clientY + document.documentElement.scrollTop;
		}
		// :WARNING: consider case where document.body doesn't yet exist for IE
		else
		{
			eventPosition.x = in_eventObj.clientX + document.body.scrollLeft;
			eventPosition.y = in_eventObj.clientY + document.body.scrollTop;
		}
	}
	// Any other browsers probably support these objects.
	else
	{
		eventPosition.x = in_eventObj.pageX;
		eventPosition.y = in_eventObj.pageY;
	}

	return eventPosition;
}
// }}}

//
// ------------------------------
// SECTION: DRAG TOOLTIP
// ddmTT_drag - Original Author: Dan Allen
// Original Url: www.mojavelinux.com
// ------------------------------
//

var ddmTT_dragMouseDown, ddmTT_dragTarget, ddmTT_dragOffsetLeft, ddmTT_dragOffsetTop;
// {{{ ddmTT_dragStart()
function ddmTT_dragStart(in_this, in_event)
{
	if (in_event[dom_eventButton] != 1 && !isKonq)
	{
		return;
	}

	// Set drag target
	ddmTT_dragTarget = in_this;
	// Get style
	style = in_this.style;
	// Set cursor to "move"
	style.cursor = 'move';
	// Increment the z-index
	style.zIndex = ++ddmTT_zIndex;

	// Get target offsets and set position
	var targetPosition = dom_getOffsets(in_this);
	ddmTT_dragOffsetLeft = dom_mousePosition.x - targetPosition.left;
	ddmTT_dragOffsetTop = dom_mousePosition.y - targetPosition.top;
	ddmTT_dragMouseDown = true;
}
// }}}
// {{{ ddmTT_dragUpdate()
function ddmTT_dragUpdate(in_event)
{
	if (ddmTT_dragMouseDown)
	{
		if (window.getSelection) // if (isGecko)
		{
			window.getSelection().removeAllRanges();
		}

		ddmTT_dragTarget.style.left = (dom_mousePosition.x - ddmTT_dragOffsetLeft) + 'px';
		ddmTT_dragTarget.style.top = (dom_mousePosition.y - ddmTT_dragOffsetTop) + 'px';

		// update the collision detection
		dom_detectCollisions(ddmTT_dragTarget);
	}
}
// }}}
// {{{ ddmTT_dragStop()
function ddmTT_dragStop()
{
	if (ddmTT_dragMouseDown)
	{
		ddmTT_dragMouseDown = false;
		ddmTT_dragTarget.style.cursor = 'default';
		if (window.getSelection) // if (isGecko)
		{
			window.getSelection().removeAllRanges()
		}
	}
}
// }}}
