/*

Author:      John Carney
Date:        24 June 2005
Version:     1.0
Description: Cross-browser javascript library
URL:         http://innercurmudgeon.com/xbj

Copyright:	 2005, John Carney (http://innercurmudgeon.com/)
License:     http://creativecommons.org/licenses/LGPL/2.1/

You may use this software freely for personal or commercial purposes. If you would like to
support future development of this software and similar, then please consider contributing a
donation at http://innercurmudgeon.com/xbj

*/


function xbj () {
}

/**
 * Returns the target element for the given event object. Note that this is intended to
 * be used only within the context of an event handler.
 *
 * @param event The event object.
 *
 * @returns The event target element.
 */
xbj.EventTarget = function (event) {
	if (!event) {
		event = window.event ;
	}

	if (event.srcElement) {
		return event.srcElement ;
	} else if (xbj.Defined (event.currentTarget)) {
		return event.currentTarget ;
	}

	return window ;
}

/**
 * Adds an event listener object to an element
 *
 * @param element	The target element
 * @param eventType	The type of event to capture (click, mouseover, etc.)
 * @param listener	A function object to invoke
 * @param capture	????
 */
xbj.AddEventListener = function (element, eventType, handler) {
	var	EventType ;
	var	capture ;

	//
	// If 4th parameter is supplied, it is to select the event phase - capture or bubble.
	//
	if (arguments.length > 3) {
		capture = arguments[3] ? true : false ;
	} else {
		capture = false ;
	}

	//
	// Get a capitalized version of the event type.
	//
	EventType = eventType.substr (0, 1).toUpperCase () + eventType.substr (1) ;

	//
	// DOM event model
	//
	if (element.addEventListener) {
		//
		// If handler is an object with a handleEvent method, use a closure
		// shim as a listener.
		//
		if (typeof (handler['handle' + EventType]) == 'function') {
			element.addEventListener (eventType, function (eventobj) {handler['handle' + EventType] (eventobj) ;}, capture) ;
		} else {
			element.addEventListener (eventType, handler, capture) ;
		}
	//
	// IE event model
	//
	} else if (element.attachEvent) {
		//
		// If handler is an object with a handleEvent method, use a closure
		// shim as a listener.
		//
		if (typeof (handler['handle' + EventType]) == 'function') {
			element.attachEvent ('on' + eventType, function () {handler['handle' + EventType] (window.event) ;}) ;
		} else {
			element.attachEvent ('on' + eventType, function () {handler (window.event) ;}) ;
		}
	//
	// Default event model
	//
	} else {
		//
		// If no handler is present, just add ours in
		//
		if (typeof (element['on' + eventType]) != 'function') {
			//
			// If handler is an object with a handleEvent method, use a closure
			// shim as a listener.
			//
			if (typeof (handler['handle' + EventType]) == 'function') {
				element['on' + eventType] = function (eventobj) {handler['handle' + EventType] (eventobj ? eventobj : window.event) ;}
			} else {
				element['on' + eventType] = function (eventobj) {handler (eventobj ? eventobj : window.event) ;}
			}
		//
		// If a handler is present, chain it using a closure
		//
		} else {
			var	oldlistener ;

			oldlistener = element['on' + eventType] ;

			//
			// If handler is an object with a handleEvent method, use a closure
			// shim as a listener.
			//
			if (typeof (handler['handle' + EventType]) == 'function') {
				element['on' + eventType] = function (event) {oldlistener (eventobj) ; handler['handle' + EventType] (eventobj ? eventobj : window.event) ;}
			} else {
				element['on' + eventType] = function (event) {oldlistener (eventobj) ; handler (eventobj ? eventobj : window.event) ;}
			}
		}
	}
}

/**
 * Determine whether a given variable is defined or not.
 *
 * @param x The variable to test
 *
 * @returns TRUE if the variable is defined, FALSE otherwise.
 */
xbj.Defined = function (x) {
	return !((typeof (x) == '') || (typeof (x) == 'undefined'))
}

/**
 * Returns the element with the specified ID
 *
 * @param id The ID to search for
 *
 * @returns The requested element, if it exists. NULL otherwise.
 */
xbj.ElementByID = function (id) {
	if (document.getElementById) {
		return document.getElementById (id) ;
	} else if (document.all) {
		return document.all[id] ;
	}

	return null ;
}

/**
 * Returns all elements with the requested tag name
 *
 * @param tagName	The tag name to search for. Maybe a string or regular expression.
 * @param rootNode	The node at which to start the search (optional - defaults to the document root).
 *
 * @returns An array of elements with matching tag names.
 */
xbj.ElementsByTagName = function (tagName) {
	var	rootNode ;

	if (arguments.length > 1) {
		rootNode = arguments[1] ;
	} else {
		rootNode = document ;
	}

	if (!tagName) {
		tagName = '*' ;
	}

	if (typeof tagName == 'string') {
		if (xbj.IsIE4 || xbj.IsIE5) {
			if (tagName == '*') {
				return rootNode.all ;
			} else {
				return rootNode.all.tags (tagName) ;
			}
		} else {
			if (rootNode.getElementsByTagName) {
				return rootNode.getElementsByTagName (tagName) ;
			}
		}
	} else if (((typeof tagName == 'function') || (typeof tagName == 'object')) && tagName.test) {
		var	candidates ;
		var	result ;
		var	i ;

		if (xbj.IsIE4 || xbj.IsIE5) {
			candidates = rootNode.all ;
		} else {
			candidates = rootNode.getElementsByTagName ('*') ;
		}

		result = new Array () ;
		for (i = 0 ; i != candidates.length ; i++) {
			if (tagName.test (candidates[i].tagName)) {
				result[result.length] = candidates[i] ;
			}
		}

		return result ;
	}


	return new Array () ;
}

/**
 * Returns all elements with the requested class name
 *
 * @param className	The class name to search for. Maybe a string or regular expression.
 * @param tagName	The tag name to search for. Maybe a string or regular expression (optional - defaults to all).
 * @param rootNode	The node at which to start the search (optional - defaults to the document root).
 *
 * @returns An array of elements with matching class names
 */
xbj.ElementsByClassName = function (className) {
	var	rootNode ;
	var	tagName ;
	var	candidates ;
	var	result ;
	var	i ;

	if (arguments.length > 2) {
		candidates = xbj.ElementsByTagName (arguments[1], arguments[2]) ;
	} else if (arguments.length > 1) {
		candidates = xbj.ElementsByTagName (arguments[1]) ;
	} else {
		candidates = xbj.ElementsByTagName ('*') ;
	}

	result = new Array () ;
	for (i = 0 ; i != candidates.length ; i++) {
		if (typeof className == 'string') {
			if (candidates[i].className == className) {
				result[result.length] = candidates[i] ;
			}
		} else if (((typeof className == 'function') || (typeof className == 'object')) && className.test) {
			if (className.test (candidates[i].className)) {
				result[result.length] = candidates[i] ;
			}
		}
	}

	return result ;
}

/**
 * Returns all elements with the requested name attribute
 *
 * @param name		The name to search for. Maybe a string or regular expression.
 * @param tagName	The tag name to search for. Maybe a string or regular expression (optional - defaults to all).
 * @param rootNode	The node at which to start the search (optional - defaults to the document root).
 *
 * @returns An array of elements with matching names
 */
xbj.ElementsByName = function (name) {
	var	rootNode ;
	var	tagName ;
	var	candidates ;
	var	result ;
	var	i ;

	if (arguments.length > 2) {
		candidates = xbj.ElementsByTagName (arguments[1], arguments[2]) ;
	} else if (arguments.length > 1) {
		candidates = xbj.ElementsByTagName (arguments[1]) ;
	} else {
		candidates = xbj.ElementsByTagName ('*') ;
	}

	result = new Array () ;
	for (i = 0 ; i != candidates.length ; i++) {
		if (typeof name == 'string') {
			if (candidates[i].name == name) {
				result[result.length] = candidates[i] ;
			}
		} else if ((typeof name == 'function') && name.test) {
			if (name.test (candidates[i].name)) {
				result[result.length] = candidates[i] ;
			}
		}
	}

	return result ;
}

/*
 * Basic browser identification.
 */
xbj.IsOpera7		= navigator.userAgent.match (/opera(?:\/|\s)4/i) ;
xbj.IsIE4OrBetter	= navigator.userAgent.match (/msie/i) && (parseInt (navigator.appVersion) >= 4) ;
xbj.IsIE4			= navigator.userAgent.match (/msie\s+4/i) ;
xbj.IsIE5			= navigator.userAgent.match (/msie\s+5/i) ;
xbj.IsMac			= navigator.userAgent.match (/mac/i) ;
xbj.IsNN4			= document.layers ? true : false ;

