/**
 *	Checks if the classname needle is in the string haystack.
 *	@param needle string the classname to look for
 *	@param haystack string the string with one or more classnames
 *	@return boolean true if the classname is in haystack, otherwise false
 */
function matchClassName(needle, haystack) {
	var search1 = eval('/^' + needle + '$/');
	var search2 = eval('/\ ' + needle + '$/');
	var search3 = eval('/^' + needle + '\ /');
	var search4 = eval('/\ ' + needle + '\ /');
	
	return haystack.match(search1) || haystack.match(search2) || haystack.match(search3) || haystack.match(search4);
}

/**
 *	Returns the first child from parent of the type nodeName and with the class 
 *	className. The nodeName and the className can be null, if they doesn't 
 *	matter. This function is not recursive, so only direct child nodes will 
 *	be find.
 *	@param parent object|node the node to start the search
 *	@param nodeName string|uppercase the nodeName like A, EM, DIV or null
 *	@param className string|casesensitive the className or null
 *	@return object|node the first child of parent with the given attributes or 
 *			null
 */
function findFirstSub(parent, nodeName, className) {
	var child = parent.firstChild;
	while (child && ((nodeName!=null && child.nodeName != nodeName) || (className != null && !matchClassName(className, child.className)))) {
		child = child.nextSibling;
	}
	return child;
}

/**
 *	Returns the next (right) sibling from previousSub of the type nodeName and
 *	with the class className. The nodeName and the className can be null, if
 *	they doesn't matter.
 *	@param previousSub object|node the node to start the search
 *	@param nodeName string|uppercase the nodeName like A, EM, DIV or null
 *	@param className string|casesensitive the className or null
 *	@return object|node next sibling of previousSub with the given attributes 
 *			or null
 */
function findNextSub(previousSub, nodeName, className) {
	var child = previousSub.nextSibling;
	while (child && ((nodeName!=null && child.nodeName != nodeName) || (className != null && !matchClassName(className, child.className)))) {
		child = child.nextSibling;
	}
	return child;
}

/**
 *	Returns true if node is a child (defined recursively) from a node of a 
 *	parent defined by parentNodeName and parentClassName.
 *	@param node object|node the node to start the search
 *	@param parentNodeName string|uppercase the nodeName like A, EM, DIV or null
 *	@param parentClassName string|casesensitive the className or null
 *	@return boolean true, if node has such a parent
 */
function hasParent(node, parentNodeName, parentClassName) {
	var parent = node.parentNode;
	var hasOne = false;
	if (parent && ((parentNodeName==null || parent.nodeName == parentNodeName) && (parentClassName == null || matchClassName(parentClassName, parent.className)))) {
		hasOne = true;
	}
	while (!hasOne && parent && ((parentNodeName!=null && parent.nodeName != parentNodeName) || (parentClassName != null && !matchClassName(parentClassName, parent.className)))) {
		parent = parent.parentNode;
		if (parent && ((parentNodeName==null || parent.nodeName == parentNodeName) && (parentClassName == null || matchClassName(parentClassName, parent.className)))) {
			hasOne = true;
		}
	}
	return hasOne;
}

/**
 *	Returns the parent node, if node is a child (defined recursively) from a 
 *	node of a parent defined by parentNodeName and parentClassName.
 *	@param node object|node the node to start the search
 *	@param parentNodeName string|uppercase the nodeName like A, EM, DIV or null
 *	@param parentClassName string|casesensitive the className or null
 *	@return object|node the parent or null if no such parent exists
 */
function getHighParent(node, parentNodeName, parentClassName) {
	var parent = node.parentNode;
	var hasOne = false;
	if (parent && ((parentNodeName==null || parent.nodeName == parentNodeName) && (parentClassName == null || matchClassName(parentClassName, parent.className)))) {
		hasOne = true;
	}
	while (!hasOne && parent && ((parentNodeName!=null && parent.nodeName != parentNodeName) || (parentClassName != null && !matchClassName(parentClassName, parent.className)))) {
		parent = parent.parentNode;
		if (parent && ((parentNodeName==null || parent.nodeName == parentNodeName) && (parentClassName == null || matchClassName(parentClassName, parent.className)))) {
			hasOne = true;
		}
	}
	return parent;
}

/**
 *	Returns true, if node has (recursively) a child defined by 
 *	subNodeName and subClassName.
 *	@param node object|node the node to start the search
 *	@param subNodeName string|uppercase the nodeName like A, EM, DIV or null
 *	@param subClassName string|casesensitive the className or null
 *	@return boolean true, if node has such a child
 */
function hasDeepSub(node, subNodeName, subClassName) {
	return ((getDeepSub(node, subNodeName, subClassName))?true:false);
}

/**
 *	Returns the child node, if node has (recursively) a child defined by 
 *	subNodeName and subClassName.
 *	@param node object|node the node to start the search
 *	@param subNodeName string|uppercase the nodeName like A, EM, DIV or null
 *	@param subClassName string|casesensitive the className or null
 *	@return object|node the (first) child or null if no such child exists
 */
function getDeepSub(node, subNodeName, subClassName, layer) {
	var sub = node.firstChild;
	var hasOne = false;
	if (sub && ((subNodeName==null || sub.nodeName == subNodeName) && (subClassName == null || matchClassName(subClassName, sub.className)))) {
		hasOne = true;
	}
	while (!hasOne && sub && ((subNodeName!=null && sub.nodeName != subNodeName) || (subClassName != null && !matchClassName(subClassName, sub.className)))) {
		if (sub && ((subNodeName==null || sub.nodeName == subNodeName) && (subClassName == null || matchClassName(subClassName, sub.className)))) {
			hasOne = true;
		}
		if (!hasOne) {
			var lowerSub = getDeepSub(sub, subNodeName, subClassName, layer + 1);
			if (lowerSub && lowerSub != false) {
				hasOne = true;
				sub = lowerSub;
			} else {
				sub = sub.nextSibling;
			}
		}
	}
	if (sub && ((subNodeName==null || sub.nodeName == subNodeName) && (subClassName == null || matchClassName(subClassName, sub.className)))) {
		hasOne = true;
	}
	if (hasOne != false) {
		return sub;
	} else {
		return false;
	}
}

/**
 *	Adds a className to the nodes class name.
 *	@param node object|node the node to add the class name
 *	@param className string casesensitive the class name to add
 *	@return void
 */
function addClass(node, className) {
	node.className = node.className + ' ' + className;
}

/**
 *	Removes a className from the nodes class name.
 *	@param node object|node the node to remove the class name from
 *	@param className string casesensitive the class name to remove
 *	@return void
 */
function removeClass(node, className) {
	var search1 = eval('/^' + className + '$/');
	var search2 = eval('/\ ' + className + '$/');
	var search3 = eval('/^' + className + '\ /');
	var search4 = eval('/\ ' + className + '\ /');
	
	node.className = node.className.replace(search1, '');
	node.className = node.className.replace(search2, '');
	node.className = node.className.replace(search3, '');
	node.className = node.className.replace(search4, '');
}
