
var registeredEventHandlers = [];

// Compatibility function to add an event handler, as IE uses a proprietry
// event model.
function addEvent(objObject, strEventName, fnHandler) {
	var safetyNet = function(codeToRun) { codeToRun(); };
	//var safetyNet = function(codeToRun) { try { codeToRun(); } catch (ex) { errorHandler(ex); } };

	// DOM-compliant way to add an event listener
	if (objObject.addEventListener)
		objObject.addEventListener(strEventName, function(e) { safetyNet(function() { fnHandler(e); }); }, true);
	// IE/windows way to add an event listener
	else if (objObject.attachEvent) {
		var ieHandler = function() { safetyNet(function() { fnHandler(fetchEventObject(objObject)); }); }
		objObject.attachEvent("on" + strEventName, ieHandler);
		registeredEventHandlers[registeredEventHandlers.length] = {
			o: objObject, e: strEventName, h: ieHandler
		};
	}
}

// IE leaks like a very leaky thing if you don't detach all the event
// handlers you've previously attached when you leave the page. And we
// don't want that.
function clearEvents() {
	for (var i = registeredEventHandlers.length - 1; i >= 0; i--) {
		registeredEventHandlers[i].o.detachEvent("on" + registeredEventHandlers[i].e, registeredEventHandlers[i].h);
		registeredEventHandlers.length = i;
	}
}
addEvent(window, 'unload', clearEvents);

// Compatibility function for IE's proprietry event model... returns a W3C
// event model-compatible event object, using the information in window.event.
function fetchEventObject(currentTarget) {
	var e = window.event;
	if (!e.currentTarget) e.currentTarget = currentTarget;
	if (e.srcElement && !e.target) e.target = e.srcElement;
	if (e.keyCode && !e.charCode) e.charCode = e.keyCode;
	if (!e.preventDefault) e.preventDefault = function() { this.returnValue = false; }
	if (!e.stopPropagation) e.stopPropagation = function() { this.cancelBubble = true; }
	if (!e.pageX) e.pageX = e.clientX + (document.body.scrollLeft || document.documentElement.scrollLeft);
	if (!e.pageY) e.pageY = e.clientY + (document.body.scrollTop || document.documentElement.scrollTop);
	return e;
}

// Utility function to find the innermost parent of a given element with the
// requested tag name. If checkInitial is true, it will check the given
// element for a match first (this functionality is more useful in an event
// handler).
function findContainer(currentElement, tagName, checkInitial) {
	if (checkInitial && currentElement.tagName.toLowerCase() == tagName)
		return currentElement;
	while (currentElement = currentElement.parentNode)
		if (currentElement.tagName && currentElement.tagName.toLowerCase() == tagName)
			return currentElement;
}
function findContainerWithClass(currentNode, searchClass, checkInitial) {
	if (checkInitial && hasClass(currentNode, searchClass))
		return currentNode;
   while (currentNode = currentNode.parentNode)
      if (hasClass(currentNode, searchClass))
         return currentNode;
}
function previousRealSibling(o) {
	o = o.previousSibling;
	while (o && o.nodeType != 1 && o.nodeType != 9)
		o = o.previousSibling;
	return o;
}

function hasClass(element, className) {
	return (' '+(element.className||'')+' ').indexOf(' '+className+' ') > -1;
}
function hasExtendedClass(element, className, searchValue) {
	var values = extendedClassValues(element, className);
	if (!searchValue) return values.length > 0;
	for (var i = 0; i < values.length; i++)
		if (values[i] == searchValue)
			return true;
}
function extendedClassValues(element, className) {
	var c = (element.className||'').split(/ +/);
	var o = [];
	for (var i = 0; i < c.length; i++) {
		if (c[i].substr(0, className.length + 1) == className + ':')
			o[o.length] = c[i].substr(className.length + 1);
	}
	return o;
}
function addClass(element, className, extendedValue) {
	if (extendedValue) className += ':' + encodeURI(extendedValue);
	removeClass(element, className);
	element.className += ' ' + className;
}
function removeClass(element, className, extendedValue) {
	if (extendedValue) className += ':' + encodeURI(extendedValue);
	element.className = (' '+(element.className||'')+' ').replace(' '+className+' ', ' ');
}
function toggleClass(element, className) {
	if (hasClass(element, className)) {
		removeClass(element, className);
	} else {
		addClass(element, className);
	}
}


// A magical function used to retrieve the full set of options 
// originally available in a select box. After the first call, it uses
// an internal cache to supply the list. Thus, we can (a) get the list,
// (b) remove everything from the select box, (c) re-add those items 
// from the list that we don't want to filter out.
function getOptionList(o) {
	if (!o.fullOptionList) {
		o.fullOptionList = [];
		var a = o.childNodes;
		for (var i = 0; i < a.length; i++)
			if (a[i].tagName && (a[i].tagName.toUpperCase() == 'OPTGROUP' || a[i].tagName.toUpperCase() == 'OPTION'))
				o.fullOptionList[o.fullOptionList.length] = a[i];
	}
	return o.fullOptionList;
}


// Filter a SELECT list to only show OPTIONs where the [filterField]
// (normally 'caption' or 'value') begins with filterValue followed
// by valueSeparator (and any that don't contain valueSeparator).
// Shows all options if filterValue is an empty string. 'caption' is
// mostly a synonym for innerHTML, but it is able to DTRT when it
// encounters an OPTGROUP (see below).
//
// It supports OPTGROUPs, with two modes of operation, controlled by
// matchGroups. If 'true', it will perform the above-specified match
// test to the group label, and, when true, show the entire group.
// When matchGroups is anything else, it will apply the match rules
// to each of the OPTIONs in the OPTGROUP, and then show the OPTGROUP
// if it contains at least one visible OPTION.
//
function filterSelectList(listBox, filterValue, filterField, valueSeparator, matchGroups) {
	var origFilterField = filterField;
	if (typeof filterField != 'function')
		filterField = function(o, seekValue) { return seekValue == '' || o[origFilterField].indexOf(valueSeparator) < 0 || seekValue == o[origFilterField].substr(0, o[origFilterField].indexOf(valueSeparator)); };
	var selectedValue = listBox.selectedIndex >= 0 ? listBox.options[listBox.selectedIndex].value : null;
	var list = getOptionList(listBox);
	while (listBox.childNodes.length > 0)
		listBox.removeChild(listBox.firstChild);
	var visibleCount = 0; var firstVisible = null;
	var selectedIndex = -1;
	for (var i = 0; i < list.length; i++) {
		var opt = list[i];
		var addItem = false;
		var visibleRows = 1;
		opt.caption = opt.innerHTML;
		switch (opt.tagName.toLowerCase()) {
			case 'optgroup':
				visibleRows = 0; // We don't want to be counted as a visible item, as the selectedIndex ignores us.
				if (matchGroups !== true) {
					var subList = getOptionList(opt);
					while (opt.childNodes.length > 0)
						opt.removeChild(opt.firstChild);
					for (var j = 0; j < subList.length; j++) {
						var subOpt = subList[j];
						subOpt.caption = subOpt.innerHTML;
						if (filterField(opt, filterValue)) {
							opt.appendChild(subOpt);
							visibleRows++;
							if (subOpt.value == selectedValue)
								selectedIndex = visibleCount + visibleRows - 1;
						}
					}
					addItem = opt.childNodes.length > 0;
					break;
				} else {
					for (var j = 0; j < opt.childNodes.length; j++)
						if (opt.childNodes[j].tagName && opt.childNodes[j].tagName.toUpperCase() == 'OPTION') {
							visibleRows++;
							if (opt.childNodes[j].value == selectedValue)
								selectedIndex = visibleCount + visibleRows - 1;
						}
				}
				opt.caption = opt.label;
			case 'option':
				addItem = filterField(opt, filterValue);
		}
		if (addItem) {
			listBox.appendChild(opt);
			visibleCount += visibleRows;
			if (opt.value == selectedValue)
				selectedIndex = visibleCount - 1;
		}
	}

	if (visibleCount == 2)
		selectedIndex = 1;
	if (selectedIndex < 0)
		selectedIndex = 0;

	listBox.selectedIndex = selectedIndex;

	renderBug(listBox);
}

// uc() and lc() are utility functions (named after their perl 
// equivalents) which won't die when given an undefined value.
function uc(s) {
	return s && s.toUpperCase ? s.toUpperCase() : '';
}
function lc(s) {
	return s && s.toLowerCase ? s.toLowerCase() : '';
}

// N.B. isHidden() and isVisible() are not literal opposites:
// isVisible() is true iff this node and all ancestors are not hidden;
// isHidden() represents only whether this particular node has been
// specifically hidden. Generally, you probably want isVisible().
function isHidden(o) {
	return o.style && (lc(o.style.display) == 'none' || lc(o.style.visibility) == 'hidden');
}
function isVisible(o) {
	while (!isHidden(o)) {
		o = o.parentNode;
		if (!o) return true;
	}
}

// FIXME: This is a hack; it should be much more intelligent, calculating
// relative URIs as relative to the current location, etc.
function uriEqual(a, b) {
	return a == b || a.substr(a.length - b.length) == b || b.substr(b.length - a.length) == a;
}

function registerFunction(obj, funcName, funcRef) {
	if (!(obj && funcName && funcRef)) return;
	if (!obj[funcName])
		obj[funcName] = [];
	obj[funcName][obj[funcName].length] = funcRef;
}
function callRegisteredFunction(obj, funcName, thisObj, args) {
	var results = [];
	if (obj[funcName])
		for (var i = 0; i < obj[funcName].length; i++)
			results[results.length] = callFunction(obj[funcName][i], thisObj, args);
	return results;
}
function callFunction(func, thisObj, args) {
	if (typeof func != 'function') return func;
	return func.apply(thisObj, args);
}
function callRegisteredFunctionAny(obj, funcName, thisObj, args) {
	var results = callRegisteredFunction(obj, funcName, thisObj, args);
	for (var i = 0; i < results.length; i++)
		if (results[i])
			return results[i];
}
function callRegisteredFunctionAll(obj, funcName, thisObj, args) {
	var results = callRegisteredFunction(obj, funcName, thisObj, args);
	for (var i = 0; i < results.length; i++)
		if (!results[i])
			return false;
	return true;
}

// A stupid function to fix a stupider problem -- IE occasionally 
// decides the world would be a better place if things just appeared
// where they were when initially rendered... or something like that.
// We work around the problem by frobbing an objects display to 'none'
// and back a few seconds after our caller did its thing.
function renderBug(o) {
	try {
		// If we don't have an object, or aren't on IE, we don't have 
		// anything to do.
		if (!o || !(true || navigator && navigator.userAgent && navigator.userAgent.toLowerCase().match(/msie/)))
			return;

		// So far, I've seen problems with the date selector, and filtered
		// select boxes.
		if (hasClass(o, 'dateSelector') || o.fullOptionList) {
			if (o.renderBugBusy) return;
			o.renderBugBusy = true;

			o.renderBugVisibility = o.style.visibility;
			o.style.visibility = 'hidden';

			// If someone tries to focus our input while we're hidden (as
			// occurs, for example, when changing tabs and we're the first
			// input on the new tab), things asplode. By replacing the
			// focus method with one that just remembers we need focus, then
			// getting it once we're shown again, all is good. 
			//
			// NOTE: This assumes that when you set focus on something, you
			// mean it... it (currently) doesn't realise that you then set
			// the focus to something else, and wanted it to stay over there.
			// Be warned -- if you do that, this will get busted.
			var inp = o.getElementsByTagName('input')[0] || o;
			inp.renderBugFocus = inp.focus;
			inp.renderBugSetFocus = false;
			inp.focus = function() { inp.renderBugSetFocus = true; }

			setTimeout(function() {
				try {
					o.style.visibility = o.renderBugVisibility;
					inp.focus = inp.renderBugFocus;
					if (inp.renderBugSetFocus) inp.focus();
					o.renderBugBusy = false;
				} catch (ex) {
					errorHandler(ex);
				}
			}, 1);
		} else {
			var spanList = o.getElementsByTagName('span');
			for (var i = 0; i < spanList.length; i++)
				renderBug(spanList[i]);
			var selectList = o.getElementsByTagName('select');
			for (var i = 0; i < selectList.length; i++)
				renderBug(selectList[i]);
		}
	} catch (ex) {
		errorHandler(ex);
	}
}

addEvent(window, 'load', function() { setTimeout( function() { renderBug(document); }, 500); });


function errorHandler(ex) {
	throw(ex);
}

function describeObject(o, recurseLevel) {
	recurseLevel = recurseLevel || 0;
	if (typeof(o) == 'object') {
		if (recurseLevel > 2) 
			return '{ .. }';
		var list = [];
		try {
			for (var m in o) {
				list[list.length] = m.toString() + ": " + describeObject(o[m], recurseLevel + 1);
			}
		} catch(e) {
			return '{ !! }';
		}
		return '{ ' + list.join(', ') + ' }';
	} else if (typeof(o) == 'string') {
		var s = o;
		//if (s.length > 15) s = s.substr(0, 15);
		//s = s.replace("\n", '  ');
		return "'" + s.replace('\\', '\\\\').replace("'", "\\'") + "'";
	} else if (typeof(o) == 'function') {
		return "function() { .. }";
	} else {
		return o.toString();
	}
}

function displayEmail(emailName, emailServer) {
   return emailName + "@" + emailServer;
};
function displayEmailLink(emailName, emailServer, displayText) {
   document.write ("<a href=\"mailto: " + displayEmail(emailName, emailServer) + "\">" + displayText + "</a>");
}

var detect = navigator.userAgent.toLowerCase();
var OS,browser,version,total,thestring;


function getBrowserInfo() {
	if (checkIt('konqueror')) {
		browser = "Konqueror";
		OS = "Linux";
	}
	else if (checkIt('safari')) browser 	= "Safari"
	else if (checkIt('omniweb')) browser 	= "OmniWeb"
	else if (checkIt('opera')) browser 		= "Opera"
	else if (checkIt('webtv')) browser 		= "WebTV";
	else if (checkIt('icab')) browser 		= "iCab"
	else if (checkIt('msie')) browser 		= "Internet Explorer"
	else if (!checkIt('compatible')) {
		browser = "Netscape Navigator"
		version = detect.charAt(8);
	}
	else browser = "An unknown browser";

	if (!version) version = detect.charAt(place + thestring.length);

	if (!OS) {
		if (checkIt('linux')) OS 		= "Linux";
		else if (checkIt('x11')) OS 	= "Unix";
		else if (checkIt('mac')) OS 	= "Mac"
		else if (checkIt('win')) OS 	= "Windows"
		else OS 								= "an unknown operating system";
	}
}

function checkIt(string) {
	place = detect.indexOf(string) + 1;
	thestring = string;
	return place;
}