/**
 * cms.functions.js
 * 
 * A set of javascript functions that are so common, that we will most always
 * need them when scripting in js.
 *
 * @author R.J.T. de Vries <rdevries@thirdwave.nl>
 * @version 2.00, 03/02/2006 - Rewritten and regrouped most functions.
 * @version 2.10, 07/27/2007 - Added function String.prototype.capitalize().
 * @package CMS
 */
 
	/**
	 * Set all the preset select-values in given document.
	 * 
	 * @param		object		[doc]		document in which to preset select values.
	 * @return	boolean						true on success, false on failure.
	 * @access	global
	 */
	function set_select_values(doc) {
		var selects,		// collection of select elements.
				opts,				// option elements in a select.
				i, j;				// iterators.
				
		if ( !doc || !doc.getElementsByTagName ) doc = document;
		selects = doc.getElementsByTagName('select');
		for ( i = 0; i < selects.length; i++ ) {
			if ( selects[i].getAttribute('val') ) {
				selects[i].value = selects[i].getAttribute('val');
			}
		}
		
		return true;
	} // set_select_values()
	
	/**
	 * JavaScript trim function.
	 * 
	 * @param		string		value		string to trim.
	 * @return	string		value		trimmed string.
	 * @access	public/private
	 */
	function strtrim(value) {
		value = value.replace('&nbsp', ' ');
		value = value.replace(/^\s+/,'');
		value = value.replace(/\s+$/,'');
		return value;
	} // strtrim()
 
	/**
	 * A PHP-style method to see if given object is actually an object, and not
	 * an undefined variable.
	 * 
	 * @param 	mixed		o		potential object to check
	 * @return 	boolean			true if object, false if not
	 */
	function is_object(o) {
		if ( typeof(o) != 'undefined' && o !== 'null' && o !== null ) return true;
		return false;
	} // is_object()
	
	/**
	 * A function to get a named element from given form. Normally, one would just
	 * use form.elements[name] to retrieve the form element, but Internet
	 * Explorer 8 does not support this (thank you for that!). Therefore, if the
	 * normal way doesn't work, then loop the elements array and return the
	 * element if found. If not, return null.
	 * 
	 * @param		object		form		<form> element in which to look.
	 * @param		string		name		name of the form element to find.
	 * @return	mixed							form element found or null if not found.
	 * @access	public/private
	 */
	function getFormElementByName(form, name) {
		var i;
		
		if ( form.elements[name] ) return form.elements[name];		// default way.
		for ( i = 0; i < form.elements.length; i++ ) {
			if ( form.elements[i].name == name ) return form.elements[i];
		}
		return null;
	} // getFormElementByName()
	
	/**
	 * Converts HTML entities in given string back to (JavaScript) re-usable text.
	 * 
	 * @param		string		str		string to convert.
	 * @return	string					converted string.
	 * @access	global
	 */
	function html_entity_decode(str) {
		var tmp,		// temporary element to use in conversion.
				ret,		// converted string.
				i;			// iterator.
				
		tmp = document.createElement('span');
		str = str.replace(/<br.*?>/gi, '##br##');
		str = str.replace('<', '&lt;');
		str = str.replace('>', '&gt;');
		tmp.innerHTML = str;
		ret = tmp.firstChild ? tmp.firstChild.nodeValue : '';
		/*
		for ( i = 0; i < tmp.childNodes.length; i++ ) {
			if ( tmp.childNodes[i].nodeType == 1 ) {
				ret += '<' + tmp.childNodes[i].tagName + '>';
			} else if ( tmp.childNodes[i].nodeType == 3 ) {
				ret += tmp.childNodes[i].nodeValue;
			}
		}
		*/
		delete(tmp);
		return ret;
	} // html_entity_decode()
	
	/**
	 * Works the same as the PHP equivalent.
	 * 
	 * @param		mixed			needle
	 * @param		array			haystack
	 * @return	boolean		true if found, false if not.
	 * @access public
	 */
	function in_array(needle, haystack) {
		if ( !is_object(haystack) || !haystack.length ) return false;
		for ( var i = 0; i < haystack.length; i++ ) {
			if ( haystack[i] == needle ) return true;
		}
		return false;
	} // in_array()
	
	/**
	 * Generate a random integer
	 * 
	 * int rand ( [int min, int max] )
	 * If you want a random number between 5 and 15 (inclusive), for example,
	 * use rand (5, 15).
	 * 
	 * @param 	integer		iMin
	 * @param		integer		iMax
	 * @return 	integer		rnd
	 */
	function rand(iMin, iMax) {
		var rnd = iMin - 1, maxtime = 100, d = new Date(), tstart = d.getTime(), tend;
	
		while ( rnd < iMin || rnd > iMax ) {
			rnd = parseInt(Math.random() * 10);
			d = new Date(); tend = d.getTime();
			if ( (tend - tstart) > maxtime ) return false;
		} // while()
		return rnd;
	} // rand()

	/**
	 * Does the same as PHP equivalent.
	 * 
	 * @param 	array 	arr		array to get values of
	 * @return 	array		ret		array with all the keys being incrementing integers
	 */
	function array_values(arr) {
		var j = 0, ret = [];
		for (var i = 0; i < arr.length; i++ ) if ( arr[i] ) ret[j++] = arr[i];
		return ret;
	} // array_values()
	
	/**
	 * Does the same as PHP equivalent.
	 *
	 * Returns the rounded value of val to specified precision (number of digits
	 * after the decimal point). Precision can also be negative or zero (default).
	 *
	 * @param 	float		val						value to round.
	 * @param		integer	[precision]		number of digits after the decimal point.
	 * @return 	mixed									rounded value.
	 */
	function round(val, precision) {
		var factor;			// factor used for precision.
	
		if ( !precision ) precision = 0;
		factor = Math.pow(10, precision);
		
		val *= factor;
		val = Math.round(val);
		val /= factor;
		return val;
	} // round()
	
	/**
	 * Returns the highest number of all parameters given.
	 * 
	 * @param		number
	 * @return	number		highest of the numbers given.
	 */
	function max() {
		var i, max = 0;
		
		for ( i = 0; i < arguments.length; i++ ) {
			if ( parseFloat(arguments[i], 10) > max ) max = parseFloat(arguments[i]);
		}
		return max;
	} // max()
	
	/**
	 * Equivalent of PHP's trim function.
	 * 
	 * @param		string		string to strip whitespaces of.
	 * @return	string		trimmed string.
	 */
	function trim(str) {
		str = str.replace(/^\s+/, '');
		str = str.replace(/\s+$/, '');
		return str;
	} // trim()
	
	/**
	 * Equivalent of PHP's UpperCase first function.
	 * 
	 * @param		string		string to perform action on.
	 * @return	string		capitalized string.
	 */
	function ucfirst(str) {
		return str.replace(/\w+/g, function(a) {
			return a.charAt(0).toUpperCase() + a.substr(1).toLowerCase();
		});
	} // ucfirst()
		
	/**
	 * Validate given e-mailaddress based on compilation of characters. No check
	 * is included for validating toplevel domain.
	 *
	 * @param 	string		address		e-mail address to validate.
	 * @return 	boolean							true if valid, false if not.
	 */
	function is_valid_email(address) {
		var checkTLD = 1,		// check toplevel domain?
				knownDomsPat,		// list of known TLDs that an e-mail address must end with.
				emailPat,				// Pattern to check if address fits user@domain format.
												// Also used to separate the username from the domain.
				specialChars,		// String representing pattern for matching all special characters.
												// We don't want to allow special characters in the address. 
				validChars,			// Pattern representing range of characters allowed in username or domain.
												// It really states which chars aren't allowed.
				quotedUser,			// Pattern applies if the "user" is a quoted string (in which case, there are
												// no rules about which characters are allowed and which aren't.
												// E.g. "jiminy cricket"@disney.com is a legal e-mail address.
				ipDomainPat,		// Pattern applies for domains that are IP addresses, rather than symbolic names.
				atom,						// string represents an atom (basically a series of non-special characters).
				word,						// String represents one word in the typical username.
												// For example, in john.doe@somewhere.com, john and doe are words.
												// Basically, a word is either an atom or quoted string.
				userPat,				// Pattern describes the structure of the user.
				domainPat,			// Pattern describes the structure of a normal symbolic
												// domain, as opposed to ipDomainPat, shown above.
				matchArray,			// coarse pattern to simply break up user@domain into
												// different pieces that are easy to analyze.
				user,						// user part of address.
				domain,					// domain part of address.
				i,							// iterator
				IPArray,				// if domain is an IP address make sure the IP address is valid.
				atomPat,
				domArr,
				len;
				
		
		knownDomsPat = /^(com|net|org|edu|int|mil|gov|arpa|biz|aero|name|coop|info|pro|museum)$/;
		emailPat = /^(.+)@(.+)$/;
		specialChars = "\\(\\)><@,;:\\\\\\\"\\.\\[\\]";
		validChars = "\[^\\s" + specialChars + "\]";
		quotedUser = "(\"[^\"]*\")";
		ipDomainPat = /^\[(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\]$/;
		atom = validChars + '+';
		word = "(" + atom + "|" + quotedUser + ")";
		userPat = new RegExp("^" + word + "(\\." + word + ")*$");
		domainPat = new RegExp("^" + atom + "(\\." + atom +")*$");

		/* VALIDATION PHASE */

		matchArray = address.match(emailPat);
		if ( matchArray == null ) return false;
		user = matchArray[1];
		domain = matchArray[2];

		// Start by checking that only basic ASCII characters are in the strings (0-127).
		for ( i = 0; i < user.length; i++ ) {
			if ( user.charCodeAt(i) > 127 ) return false;
		}

		for ( i = 0; i < domain.length; i++ ) {
			if ( domain.charCodeAt(i) > 127 ) return false;
		}

		if ( user.match(userPat) == null ) return false;

		IPArray = domain.match(ipDomainPat);
		if ( IPArray != null ) {
			for ( i = 1; i <= 4; i++ ) {
				if ( IPArray[i] > 255 ) return false;
			}
			return true;
		}

		// Domain is symbolic name.  Check if it's valid.
		atomPat = new RegExp("^" + atom + "$");
		domArr = domain.split(".");
		len = domArr.length;
		for ( i = 0; i < len; i++ ) {
			if ( domArr[i].search(atomPat)==-1 ) return false;
		} // for()

		if ( checkTLD && domArr[domArr.length-1].length != 2 && 
				domArr[domArr.length-1].search(knownDomsPat) == -1 ) {
			return false;
		}

		if ( len < 2 ) return false;
		return true;
	} // is_valid_email()
	
	/**
	 * Get the first parentNode of the given object that has the given tagname.
	 * 
	 * @param		string		tagname		tagname we want the parentNode to have.
	 * @param		object 		obj				object we want to have the parentNode of.
	 * @return	mixed								parentnode with the given tagname or false on
	 *															failure
	 */
	function getParentNode(tag, obj) {
		tag = tag.toUpperCase();
		while ( is_object(obj.parentNode) && obj.tagName.toUpperCase() != tag ) {
			obj = obj.parentNode;
		}
		if ( !is_object(obj) || !obj.tagName || obj.tagName.toUpperCase() != tag ) {
			return false;
		}
		return obj;
	} // getParentNode()
	
	/**
	 * Get the first parentNode of given object that has any one of the attributes
	 * in given attributelist set.
	 * 
	 * @param		string		attributelist		attributes to look for, delimited by ||
	 * @param		object		obj							DOM element from which to start looking.
	 * @return	mixed											element found or false if none found.
	 */
	function getParentWithAttribute(attributelist, obj) {
		var attrs,							// array with attributes to look for.
				attrFound = false,	// flag to indicate whether attribute has been found.
				arr,								// key/value pair.
				i;									// iterator.
	
		if ( !attributelist || !attributelist.split || !obj || !obj.getAttribute ) {
			return false;
		}
		attrs = attributelist.split('||');
		for ( i = 0; i < attrs.length; i++ ) {
			if ( attrs[i].indexOf('=') != -1 ) {
				arr = attrs[i].split('=');
				delete(attrs[i]);
				attrs[arr[0]] = arr[1];
			}
		}
		
		while ( obj.parentNode ) {
			for ( i in attrs ) {
				if ( !isNaN(i) && obj.getAttribute(attrs[i]) ) attrFound = true;
				else if ( isNaN(i) && i == 'className' && obj.className
						&& obj.className.indexOf(attrs[i]) != -1 ) attrFound = true;
				else if ( isNaN(i) && obj.getAttribute(i)
						&& obj.getAttribute(i) == attrs[i] ) attrFound = true;
			}
			if ( attrFound ) break;
			obj = obj.parentNode;
		}
		
		return attrFound ? obj : false;
	} // getParentWithAttribute()
	
	/**
	 * Get the index number of a childNode in its parentNode
	 * 
	 * @param 	object	child								dom object representing child.
	 * @param		boolean	excludeTextNodes		exclude text nodes in the calculation.
	 * @return 	mixed						(int) index or false on failure
	 */
	function getChildIndex(child, excludeTextNodes) {
		var j = 0, i = 0;
		
		if ( !child || !child.parentNode ) return false;
		for ( i = 0; i < child.parentNode.childNodes.length; i++ ) {
			if ( excludeTextNodes && child.parentNode.childNodes[i].nodeType == 3 ) continue;
			if ( child == child.parentNode.childNodes[i] ) return j;
			j++;
		} // for()
		
		return 0;
	} // getChildIndex()
	
	/**
	 * Get all childNodes of given object that have nodeType = 1
	 * 
	 * @param 	object		obj		object to get childNodes of.
	 * @return 	array			ret		array of childNodes with nodeType=1
	 */
	function getType1ChildNodes(obj) {
		var ret = [];
		if ( !is_object(obj) || !obj.childNodes.length ) return ret;
		for ( var i = 0; i < obj.childNodes.length; i++ ) {
			if ( obj.childNodes[i].nodeType == 1 ) ret[ret.length] = obj.childNodes[i];
		} // for
		return ret;
	} // getType1ChildNodes()
	
	/**
	 * Set the given value to the given style on all childNodes of the given
	 * table row.
	 * 
	 * Sometimes, a css style may not be applied to a TR tag (such as border),
	 * so it has to be applied to each of the childNodes of the row.
	 * 
	 * @param 	object		row			DOM object representing table row (TR tag)
	 * @param 	string		attr		css style to set on row (i.e. 'borderTop')
	 * @param 	string		value		value for the given style
	 * @return 	boolean						true on success, false on failure
	 */
	function setRowStyleOnTDs(row, attr, value) {
		var cell, evalstr;
	
		if ( !is_object(row) || !row.style || !row.childNodes.length ) return false;
		for ( var i = 0; i < row.childNodes.length; i++ ) {
			cell = row.childNodes[i];
			evalstr = 'cell.style.' + attr + '= "' + value + '";';
			eval(evalstr);
		}
	} // setRowStyleOnTDs()
	
	/**
	 * For some strange reason, when setting the 'class' property of an HTML
	 * object via JavaScript, then IE only accepts 'className' as the name of
	 * the attribute, whilst gecko only accepts 'class'.
	 * 
	 * @return	string 		name of the attribute to use.
	 */
	function getClassName() {
		return isIE() ? 'className' : 'class';
	} // getClassName()
	
	/**
	 * Get horizontol scrolling of the window in pixels.
	 * 
	 * @return integer
	 */
	function getScrollX() {
		return document.all ? document.body.scrollLeft : window.scrollX;
	} // getScrollX()
	
	/**
	 * Get vertical scrolling of the window in pixels.
	 * 
	 * @return integer
	 */
	function getScrollY() {
		return document.all ? document.body.scrollTop : window.scrollY;
	} // getScrollY()
	
	/**
	 * Get vertical scrolling of the window in pixels.
	 * 
	 * @return integer
	 */
	function getScrollPosY() {
		if ( self.pageYOffset ) return self.pageYOffset;			// all except Explorer
		if ( document.documentElement && document.documentElement.scrollTop ) {
			return document.documentElement.scrollTop;					// Explorer 6 Strict
		}
		if ( document.body ) return document.body.scrollTop;	// all other Explorers
	} // getScrollPosY()

	/**
	 * Get vertical scrolling of the window in pixels.
	 * 
	 * @return integer
	 */
	function getScrollPosX() {
		if ( self.pageXOffset ) return self.pageXOffset;			// all except Explorer
		if ( document.documentElement && document.documentElement.scrollLeft ) {
			return document.documentElement.scrollleft;					// Explorer 6 Strict
		}
		if ( document.body ) return document.body.scrollLeft;	// all other Explorers
	} // getScrollPosX()

	/**
	 * Get the x position of the given dom object element.
	 * 
	 * @param 	object	obj		dom object to get x-pos for
	 * @return 	integer	x			x-pos of given dom object
	 */
	function getPosX(obj) {
		var curleft = 0;
		
		if (obj.offsetParent) {
			while (obj.offsetParent) {
				curleft += obj.offsetLeft;
				obj = obj.offsetParent;
			} // while()
		} else if (obj.x) curleft += obj.x;                              
		return curleft;
	} // getPosX()

	/**
	 * Get the y position of the given dom object element.
	 * 
	 * @param 	object	obj		dom object to get y-pos for
	 * @return 	integer	y			y-pos of given dom object
	 */
	function getPosY(obj) {
		var curtop = 0;
		
		if (obj.offsetParent) {
			while (obj.offsetParent) {
				curtop += obj.offsetTop;
				obj = obj.offsetParent;
			} // while()
		} else if (obj.y) curtop += obj.y;
		return curtop;
	} // getPosY()
	
	/**
	 * Return the width of given dom object
	 * 
	 * @param 	object	domObj		dom object to return width of
	 * @return 	integer						width of dom object
	 */
	function getWidth(domObj) {
		return parseInt(domObj.offsetWidth);
	} // getWidth()
	
	/**
	 * Return the height of given dom object
	 * 
	 * @param 	object	domObj		dom object to return height of
	 * @return 	integer						height of dom object
	 */
	function getHeight(domObj) {
		if ( domObj.clientHeight ) return parseInt(domObj.clientHeight, 10);
		return parseInt(domObj.offsetHeight, 10);
	} // getHeight()

	/**
	 * Get the width of the current browser window in pixels.
	 * 
	 * @return	integer		width of the window in pixels.
	 * @access	global
	 */
	function getWindowWidth() {
		var w = 0;
		if( typeof( window.innerWidth ) == 'number' ) {
			w = window.innerWidth;
		} else if ( document.documentElement && document.documentElement.clientWidth ) {
			w = document.documentElement.clientWidth;
		} else if ( document.body && document.body.clientWidth ) {
			w = document.body.clientWidth;
		}
		return parseInt(w);
	} // getWindowWidth()
	
	/**
	 * Get the height of the current browser window in pixels.
	 * 
	 * @return	integer		height of the window in pixels.
	 * @access	global
	 */
	function getWindowHeight(win) {
		if ( !win ) win = window;
		var h = 0;
		if( typeof( win.innerHeight ) == 'number' ) {
			h = win.innerHeight;
		} else if ( win.document.documentElement && win.document.documentElement.clientHeight ) {
			h = win.document.documentElement.clientHeight;
		} else if ( win.document.body && win.document.body.clientHeight ) {
			h = win.document.body.clientHeight;
		}
		return parseInt(h);
	} // getWindowHeight()
	
	/**
	 * Returns available width in viewport.
	 * 
	 * @return	mixed		(int) available width or false on failure.
	 * @access	global
	 */
	function getAvailWidth() {
		// the more standards compliant browsers (mozilla/netscape/opera/IE7) use
		// window.innerWidth and window.innerHeight
		if ( typeof window.innerWidth != 'undefined' ) {
			return window.innerWidth
		}
		 
		// IE6 in standards compliant mode (i.e. with a valid doctype as the first
		// line in the document)
		else if (typeof document.documentElement != 'undefined'
				&& typeof document.documentElement.clientWidth != 'undefined'
				&& document.documentElement.clientWidth != 0) {
			return document.documentElement.clientWidth
		}

		// older versions of IE.
		else {
			return document.getElementsByTagName('body')[0].clientWidth
		}
	
		// The way we used to solve this:
		if ( document && document.body && document.body.clientWidth ) {
			return document.body.clientWidth;
		} else if ( window && window.innerWidth ) {
			return window.innerWidth;
		}
		return false;
	} // getAvailWidth()

	/**
	 * Returns available width in viewport.
	 * 
	 * @return	mixed		(int) available width or false on failure.
	 * @access	global
	 */
	function getAvailHeight() {
		// the more standards compliant browsers (mozilla/netscape/opera/IE7) use window.innerWidth and window.innerHeight
		if ( typeof window.innerHeight != 'undefined' ) {
			return window.innerHeight
		}
		 
		// IE6 in standards compliant mode (i.e. with a valid doctype as the first line in the document)
		else if (typeof document.documentElement != 'undefined'
				&& typeof document.documentElement.clientHeight != 'undefined'
				&& document.documentElement.clientHeight != 0) {
			return document.documentElement.clientHeight
		}

		// older versions of IE
		else {
			return document.getElementsByTagName('body')[0].clientHeight
		}
	
		// The way we used to solve this:
		if ( document && document.body && document.body.clientHeight ) {
			return document.body.clientHeight;
		} else if ( window && window.innerHeight ) {
			return window.innerHeight;
		}
		return false;
	} // getAvailWidth()
	
	/**
	 * Get the real distance from the given object to the left or topside
	 * (direction) of the window.
	 *  
	 * @param object		obj					DOM object to get the real distance of.
	 * @param integer 	x						distance
	 * @param string 		direction		'Left' for the distance to the left,
	 *															'Top' for the distance to the top.
	 * @return integer x						distance found.
	 */
	function getRealDistance(obj, x, direction) {
		var extra = 0;
		if ( !eval('obj.offset' + direction) ) return x;
			
		try {
			eval ('extra = obj.offset' + direction);
			x += extra;
			try				{	return getRealDistance(obj.offsetParent, x, direction); }
			catch(f)	{	return x; }
		} catch(e) {
			return x;
		}
	} // getRealDistance()

	/**
	 * Returns the top position of given element relative to the entire window.
	 * 
	 * @param		object		el		DOM element to get relative top position for.
	 * @return	integer		y			y position of given element.
	 * @access	public/private
	 */
	function getRelPosX(el, rel) {
		var x = 0;
				
		while (el) {
			x += el.offsetLeft;
			el = el.offsetParent;
		}
		return x;
	} // getRelPosX()
	
	/**
	 * Returns the top position of given element relative to the entire window.
	 * 
	 * @param		object		el		DOM element to get relative top position for.
	 * @return	integer		y			y position of given element.
	 * @access	global
	 */
	function getRelPosY(el) {
		var y = 0;
				
		while (el) {
			y += el.offsetTop;
			//y -= el.scrollTop;
			el = el.offsetParent;
		}
		return y;
	} // getRelPosY()
	
	/**
	 * Center given element relative to rel. If rel is not given, use document
	 * body as rel.
	 * 
	 * @param		object		el							html element to position.
	 * @param		object		[rel]						html element in which to center el.
	 * @param		boolean		[incScrollPos]	include scroll position?
	 * @return	boolean										true on success, false on failure.
	 * @access	global
	 */
	function centerElement(el, rel, incScrollPos) {
		var availHeight = getWindowHeight(),
				availWidth = getWindowWidth(),
				scrollY = incScrollPos ? getScrollPosY() : 0,
				scrollX = incScrollPos ? getScrollPosX() : 0;
				
		if ( !el || !el.style ) return false;
		if ( rel ) {
			availHeight = getHeight(rel);
			availWidth = getWidth(rel);
		}
		
		var top = parseInt((availHeight / 2) - (getHeight(el) / 2) + scrollY);
		if ( top < 5 ) top = 5;
		var lft = parseInt((availWidth / 2) - (getWidth(el) / 2) + scrollX);
		
		el.style.left = lft + 'px';
		el.style.top = top + 'px';
	} // centerElement()
	
	/**
	 * As there is only a document.getElementById function, there is not a
	 * standard possibility to get a set of elements by their attribute-name.
	 * 
	 * As such as function would be highly appropriate, I have decided to write
	 * one myself. what it actually does is go through all the childNodes of the
	 * 'element' given, and check whether the given attribute name has the given
	 * attribute value for the current element object. If so, it is added to the
	 * returnMe array. When no start-point (element) is given, we start at the
	 * document.body element.
	 * 
	 * @param		string	attr			name of the attribute to check.
	 * @param		mixed		val				value to check for.
	 * @param		object 	element		element to start your search from. If not given,
	 *														this object defaults to document.body.
	 * @param		array		returnMe	array to add the found items to.
	 *							 							defaults to empty array.
	 * @return	array		returnMe	array with the items that were found.
	 */
	function getElementBy(attr, val, element, returnMe) {
		if (!i) i = 0;
		
		i++;

		if ( document.all && (val == 'true'	|| val == 'false') ) val = eval(val);
		if ( !element )  var element = document.body;
		if ( !returnMe ) var returnMe = new Array();
		if ( element.getAttribute && element.getAttribute(attr) == val ) {
			returnMe[returnMe.length] = element;
		}
		
		if ( element.childNodes && element.childNodes.length ) {
			for ( var i = 0; i < element.childNodes.length; i++ ) {
				returnMe = getElementBy( attr, val, element.childNodes[i], returnMe);
			} // for()
		}
		return returnMe;
	} // getElementBy()
	
	/**
	 * Because the getElementBy function is too slow for processing large amounts
	 * of objects this function should save some time.
	 *
	 * It still returns items that match the given attribute criteria. This
	 * function searches through a specified array of objects though. This could
	 * be a collection of objects that were fetched by their tagName. Hopefully,
	 * this makes the whole a little leaner.
	 * 
	 * @param		string		attr			name of the attribute to check.
	 * @param 	mixed 		val				value to check for.
	 * @param		array			arr				array to search through.
	 * @return	array 		returnMe	array with the items that were found.
	 */
	function getElementFromArrayBy(attr, val, arr) {
		if ( !arr.length ) return false;
		
		// IE uses boolean values different from Mozilla ...
		if ( document.all && (val == 'true'	|| val == 'false') ) val = eval(val);
		
		// Initiate the return array
		var returnMe = new Array();
		
		// Iterate through the given array and check each object for the given
		// arguments.
		for ( var i = 0; i < arr.length; i++ ) {
			if ( arr[i].getAttribute && arr[i].getAttribute(attr) == val ) {
				returnMe[returnMe.length] = arr[i];
			}
		} // for()
		if ( !returnMe.length ) return false;
		else										return returnMe;
	} // getElementFromArrayBy()
	
	/**
	 * Get elements by their attribute value.
	 * 
	 * Taken from Gathering of Tweakers. This function may speed up the process
	 * of finding DOM objects based on given attribute-name and value pair.
	 * 
	 * @param		string		attr		name of the attribute to check.
	 * @param		mixed 		val			value to check for; if set to (bool) false,
	 *														then elements that just have the given attribute
	 *														set are returned.
	 * @param		string 		tag			limit search to tags with this tagname.
	 * @param		object		parent	parent element from which to start search.
	 * @param		boolean		allowpm	allow a Partial Match for the given attribute, 
	 *														meaning that if the first X characters of the
	 *														given attribute match the length of the value,
	 *														a match is made.
	 * @return	array 		r				array of DOM objects found
	 */
	document.getElementsByAttribute = function(attr, val, tag, parent, allowpm) {
		if ( !tag ) tag = '*';
		
		var s = (parent || document).getElementsByTagName(tag), i = s.length, e, r = [];
		
		while ( i-- ) {
			e = s[i];
			
			// Changed if ( e && e.getAttribute ) into below.
			if ( e && typeof(e.getAttribute) != 'undefined' ) {
				// is val an array or a string/integer
				if (val instanceof Array) {
					if ( in_array(e.getAttribute(attr), val) ) r.push(e);
				} else {
					if ( attr == 'className' && e.className == val ) r.push(e);
					else if ( attr == 'className' && val.indexOf('%') != -1 && e.className.indexOf(val.replace('%', '')) != -1 ) r.push(e);
					else if ( val && val.indexOf && e.getAttribute(attr) && val.indexOf('%') != -1 && e.getAttribute(attr).toString().length && e.getAttribute(attr).toString().indexOf(val.replace('%', '')) != -1 ) r.push(e);
					else if ( e.getAttribute(attr) == val ) r.push(e);
					else if ( e.getAttribute(attr) && val === false ) r.push(e);
					else if ( allowpm && e.getAttribute(attr) && e.getAttribute(attr).substr(0, val.toString().length) == val ) r.push(e);
				}
			}
		} // while()
		return r;
	};	// document.getElementsByAttribute()
	
	/**
	 * Get elements by their attribute value.
	 * 
	 * Taken from Gathering of Tweakers. This function may speed up the process
	 * of finding DOM objects based on given attribute-name and value pair.
	 * 
	 * @param		string		attr		name of the attribute to check.
	 * @param		mixed 		val			value to check for; if set to (bool) false,
	 *														then elements that just have the given attribute
	 *														set are returned.
	 * @param		string 		tag			limit search to tags with this tagname.
	 * @param		object		parent	parent element from which to start search.
	 * @param		boolean		allowpm	allow a Partial Match for the given attribute, 
	 *														meaning that if the first X characters of the
	 *														given attribute match the length of the value,
	 *														a match is made.
	 * @return	array 		r				array of DOM objects found
	 */
	document.getElementsByAttribute2 = function(attr, val, tag, parent, allowpm) {
		if ( !tag ) tag = '*';
		var s = (parent || document).getElementsByTagName(tag), i = s.length, e, r = [];
		
		while ( i-- ) {
			e = s[i];
			if ( e && e.getAttribute ) {
				// is val an array or a string/integer
				if (val instanceof Array) {
					if ( in_array(e.getAttribute(attr), val) ) r.unshift(e);
				} else {
					if ( attr == 'className' && e.className == val ) r.unshift(e);
					else if ( e.getAttribute(attr) == val ) r.unshift(e);
					else if ( e.getAttribute(attr) && val === false ) r.unshift(e);
					else if ( allowpm && e.getAttribute(attr) && e.getAttribute(attr).substr(0, val.toString().length) == val ) r.unshift(e);
				}
			}
		} // while()
		return r;
	};	// document.getElementsByAttribute2()
	
	/**
	 * Alias of document.getElementsByAttribute for retrieving only a single
	 * element.
	 * 
	 * @see document.getElementsByAttribute()
	 */
	document.getElementByAttribute = function(attr, val, tag, parent) {
		var els = document.getElementsByAttribute(attr, val, tag, parent);
		if ( !els || !els.length ) return false;
		return els.length == 1 ? els[0] : false;
	}; // document.getElementsByAttribute()
	
	/**
	 * Function to extend possibilities of standard getElementsByTagName function.
	 * This function accepts a DOM element as first argument. That is the element
	 * on which the getElementsByTagName function will executed. The second
	 * argument is a string with tagnames, seperated by a pipe symbol.
	 *
	 * Please note that the getElementsByTagName function returns a special type
	 * of array, called an HTMLCollection, which is read only. This function 
	 * combines several of those collections in a normal array. In that way, the
	 * result of this function is somewhat different than the original function.
	 *
	 * The return value for this function is always an array, even if no elements
	 * were found (in which case an empty array is returned).
	 * 
	 * @param		object	parent		dom element for which to get child-elements.
	 * @param		string	tags			tagnames, seperated by | (pipe symbol).
	 * @return	array							array of elements found.
	 * @access	public/private
	 */
	function getElementsByTagNames(parent, tags) {
		var ret = [],		// return array.
				els,				// elements found for each of the tags in given tags arg.
				i, j;				// iterators.
	
		if ( !is_object(parent) || !parent.getElementsByTagName ) return ret;
		
		tags = tags.split('|');
		for ( i = 0; i < tags.length; i++ ) {
			els = parent.getElementsByTagName(tags[i]);
			for ( j = 0; j < els.length; j++ ) {
				ret[ret.length] = els[j];
			}
		}
		return ret;
	} // getElementsByTagNames()
	
	document.getElementByTagName = function(tag, parent) {
		var els = parent.getElementsByTagName(tag);
		if ( !els || !els.length ) return false;
		return els[0];
	}; // document.getElementByTagName()
	
	/**
	 * Add capitalize function to string prototype.
	 * 
	 * @return	string		string with first letter in uppercase.
	 */
	String.prototype.capitalize = function(){ //v1.0
		return this.replace(/\w+/g, function(a){
			return a.charAt(0).toUpperCase() + a.substr(1).toLowerCase();
		});
	}; // String.prototype.capitalize()

	/**
	 * Due to differences between Mozilla and IE, we use this function to get
	 * the parentWindow property (IE) or defaultView property (Mozilla) for the
	 * document on which this method is invoked.
	 * 
	 * @return 	object		parentWindow/defaultView property
	 * @access public
	 */
	document.getParentWindow = function() {
		if ( is_object(this.parentWindow) ) return this.parentWindow;
		if ( is_object(this.defaultView) ) return this.defaultView;
		return false;
	}; // document.getParentWindow()
	
	function getParentWindow(doc) {
		if ( is_object(doc.parentWindow) ) return doc.parentWindow;
		if ( is_object(doc.defaultView) )	 return doc.defaultView;
		return null;
	} // getParentWindow()
	
	/**
	 * As versions of Explorer < 6.0 do not support the .ownerDocument property,
	 * this function supplies a work-around for those explorer versions.
	 * 
	 * @param		object		obj 	object of which to get ownerDocument.
	 * @return 	mixed						false on failure, document object on success.
	 */
	function getOwnerDocument(obj) {
		if ( !is_object(obj) )										return false;
		else if ( !is_object(obj.ownerDocument) ) return obj.document;
		else																			return obj.ownerDocument;
	} // getOwnerDocument()
	
	/**
	 * Get the document in the iframe with the given ID.
	 * 
	 * @param		string		frameId 	id of frame to return document of OR frame
	 *															element itself.
	 * @return	object							document in the frame requested or false on
	 *															failure.
	 */
	function getFrameDocument(frameId) {
		var frame;

		if ( frameId.tagName && frameId.tagName.toLowerCase().indexOf('frame') != -1 ) {
			frame = frameId;
		} else {
			frame = document.getElementById(frameId);
		}
		
		if ( !is_object(frame) )		return false;
		if ( frame.contentWindow )	return frame.contentWindow.document;
		else												return frame.document;
	} // getFrameDocument()
	
	/**
	 * Alias for getFrameDocument
	 * 
	 * @see getFrameDocument()
	 */
	function getIFrameDocument(frameId) {
		return getFrameDocument(frameId);
	} // getIFrameDocument()

	/**
	 * Get the window in the iframe with the given ID.
	 * 
	 * @param 	string		aID 		id of the iframe that we want the window of.
	 * @return	object						window object in the iframe requested.
	 */
	function getFrameWindow(frameId) {
		var frame;
		
		if ( frameId.tagName && frameId.tagName.toLowerCase().indexOf('frame') != -1 ) {
			frame = frameId;
		} else {
			frame = document.getElementById(frameId);
		}
		if ( is_object(frame) ) return frame.contentWindow;
	} // getFrameWindow()
	
	/**
	 * Get the frame DOM object (tag) of the current window.
	 * 
	 * @return 	object		DOM object for current frame/iframe tag.
	 */
	function getFrameTag() {
		return this.frameElement;
	} // getFrameTag()
	
	/**
	 * Get the value of an attribute that was set on the frame or iframe tag
	 * that forms the basis of this document.
	 * 
	 * @param 	string	attr		attribute name
	 * @return 	mixed						value of attribute or false on failure
	 */
	function getFrameAttribute(attr) {
		if ( !is_object( getFrameTag() ) ) return;
		return getFrameTag().getAttribute(attr);
	} // getFrameAttribute()
	
	/**
	 * Is the given frame operation allowed, based on what was set in the 
	 * frame tag?
	 * 
	 * @param 	string	attr	attribute to check
	 * @return 	boolean				true if allowed, false if not
	 */
	function isAllowed(attr) {
		var operation = getFrameAttribute(attr);
		if ( !is_object(operation) || !operation || !operation.toLowerCase ) return false;
		return ( operation.toLowerCase() != 'yes' ) ? false : true;
	} // isAllowed()
	
	/**
	 * Get the highest window in the order of windows.
	 * 
	 * @return 	object		toplevel-window
	 */
	function getToplevelWindow() {
		var win = window;
		while ( win != win.parent && is_object(win.parent) ) {
			win = win.parent;
		}
		return win;
	} // getToplevelWindow()
	
	/**
	 * Set opacity of given object to given value, respecting browsers.
	 *
	 * @param 	object		obj
	 * @param		integer		opacity
	 * @return	void
	 */
	function setOpacity(obj, opacity) {
		switch ( true ) {
			case is_object(obj.style.opacity):
				obj.style.opacity = opacity / 100;
				break;
			case is_object(obj.style.MozOpacity):
				obj.style.MozOpacity = opacity / 100;
				break;
			case is_object(obj.parentNode) && is_object(obj.filters) && is_object(obj.filters.alpha):
				obj.filters.alpha.opacity = opacity;
				break;
			case is_object(obj.style):
				obj.style['filter'] = 'alpha(opacity=' + opacity + ')';
				break;
			default:
				return 0;
		}
		return true;
	} // setOpacity()
	
	/**
	 * Returns opacity of given element, respecting browsers.
	 * 
	 * @param		object		obj		element to return opacity of.
	 * @return	integer					current opacity.
	 */
	function getOpacity(obj) {
		switch ( true ) {
			case is_object(obj.style.opacity):
				return obj.style.opacity * 100;
			case is_object(obj.style.MozOpacity):
				return obj.style.MozOpacity * 100;
			case is_object(obj.parentNode) && is_object(obj.filters) && is_object(obj.filters.alpha):
				return obj.filters.alpha.opacity;
			default:
				return 0;
		}
	} // getOpacity()
	
	/**
	 * Return number of seconds and milliseconds as a float for current date.
	 * 
	 * @return float		epoch time, including milliseconds
	 */
	function microtime() {
		var d = new Date;
		return d.getTime();
	} // microtime()
	
	/**
	 * Sleep for given number of milliseconds.
	 * 
	 * @param		integer		msecs
	 * @return	void
	 */
	function sleep(msecs) {
		var start, end;
		
		start = microtime();
		while ( microtime() - start < msecs ) { }
		return;		
	} // sleep()

	/**
	 * A general function for some browser-checks. There are also some specific
	 * browser-checks, which use this function as there 'big daddy'.
	 * 
	 * @param 	string 	what		What do you want to know about the browser?
	 * @return	boolean					true if the navigator IS what, false if NOT
	 * @access public
	 */
	function navChk(what) {
		switch ( what.toUpperCase() ) {
			case 'MAC':
				return (navigator.appVersion.indexOf("Mac")!=-1) ? true : false;
			case 'IEMAC':
				return ((document.all)&&(isMac)) ? true : false;
			case 'IE4':
				return ((document.all)&&(navigator.appVersion.indexOf("MSIE 4.")!=-1)) ? true : false;
			case 'IE5':
				return ((document.all)&&(navigator.appVersion.indexOf("MSIE 5.")!=-1)) ? true : false;
			case 'IE6':
				return ((document.all)&&(navigator.appVersion.indexOf("MSIE 6.")!=-1)) ? true : false;
			case 'IE7':
				return ((document.all)&&(navigator.appVersion.indexOf("MSIE 7.")!=-1)) ? true : false;
			case 'IE8':
				return ((document.all)&&(navigator.appVersion.indexOf("MSIE 8.")!=-1)) ? true : false;
			default:
				return false;
		} // switch()
	} // navChk()
	
	/**
	 * Is the current browser Internet Explorer with a version of 'v' or higher?
	 * 
	 * @param		integer		v		The minimum version number of IE that we want.
	 * @return	boolean				true if current browser IS IEv+, false if not.
	 * @see this.navChk()
	 * @access public
	 */
	function isIE(v) {
		if ( !v ) var v = 5;

		switch (v) {
			case 4: return ( this.navChk('IE4') || this.navChk('IE5') || this.navChk('IE6') || this.navChk('IE7') );
			case 5: return ( this.navChk('IE5') || this.navChk('IE6') ) || this.navChk('IE7');
			case 6: return ( this.navChk('IE6') );
			case 8: return ( this.navChk('IE8') );
			default: return false;
		} // switch()
	} // isIE()
	
	/**
	 * Get the CSS rule with the identifier that matches the classname given.
	 * 
	 * @param 	string 	classname		name of identifier to look for
	 * @param		boolean	returnIndex	return the index-number of the rule instead of the rule itself.
	 * @return 	mixed								cssRule object if found or false on failure
	 */
	function getCssRule(classname, returnIndex) {
		var i, j,		// iterators.
				rules;	// style rules array.
		
		if ( !document.styleSheets ) return false;
		
		for ( i = 0; i < document.styleSheets.length; i++ ) {
			rules = [];
			if ( document.styleSheets[i].cssRules ) rules = document.styleSheets[i].cssRules;
			else if (document.styleSheets[i].rules)	rules = document.styleSheets[i].rules;
			else continue;
			
			for ( j = 0; j < rules.length; j++ ) {
				if ( rules[j].selectorText.toLowerCase().indexOf(classname.toLowerCase()) != -1 ) {
					return returnIndex ? j : rules[j];
				}
			}
		}
			
		return false;
	} // getCssRule()
	
	/**
	 * open a new window with given options
	 * 
	 * @param		string		url 			url to load in new window
	 * @param		integer		w 				width of new window
	 * @param		integer		h 				height of new window
	 * @param		string		winname 	name of new window
	 * @return	void
	 */
	function newwin(url, w, h, winname, scrollbars, resizable, menubar) {
		if ( !scrollbars ) scrollbars = 'yes';
		if ( !resizable  ) resizable = 'yes';
		menubar = !menubar ? 'no' : 'yes';
		if ( !winname ) {
			var d = new Date;
			winname = 'newwin_' + d.getTime();
		}
		
		xpos = Math.round((screen.availWidth - w) / 2);
		ypos = Math.round((screen.availHeight - h) / 2);
		winprops = "toolbar=no, location=no, directories=no, status=yes";
		winprops += ", menubar="+menubar+", scrollbars="+scrollbars;
		winprops += ", resizable="+resizable+", width="+w+", height="+h;
		winprops += ", left="+xpos+", top="+ypos;
		
		// alert('winname: ' + winname + "\nwinprops:" + winprops + "\nurl:" + url);
		var win = window.open(url, winname, winprops);
		
		if ( !win ) {
			var str = "Het nieuwe venster kon niet worden geopend.\n\n";
			str += "Waarschijnlijk is het nieuwe venster geblokkeerd door een\n";
			str += "'pop-up blocker'.\n\n";
			str += "Stel uw 'pop-up blocker' zo in, dat deze het openen van\n";
			str += "nieuwe vensters voor deze pagina toestaat.";
			alert(str);
		} else {
			win.focus();
		}
		
		return win;
	} // newwin()
	
	/**
	 * Called on change event in TextArea. Makes sure that the maximum number of
	 * characters in the textarea do not exceed the given limit.
	 * 
	 * @param object	area	textarea to which limit applies.
	 * @param integer len 	maximum number of characters in textarea.
	 * @return void
	 * @access public
	 */
	function checkMaxLength(area, len) {
		var e, shortmax;
	
		if ( !area || !area.getAttribute || !area.tagName ) {
			var e = !area ? window.event : area;
			if ( (area = getEventSrc(e)) && area.getAttribute('shortmax') ) {
				len = parseInt(area.getAttribute('shortmax')) || 150;
			}
		}
	
		if ( area.value.length > len ) {
			area.value = area.value.substr(0, len);
		}
	} // checkMaxLength()

	/**
	 * Include a new javascript tag in the document body
	 * 
	 * @param 	string		scriptsrc		location of the script to load
	 * @return 	boolean								true on success, false on failure
	 */
	function includeJS(id, scriptsrc) {
		var oldscript = document.getElementById(id);
		
		newscript = document.createElement('script');
		newscript.setAttribute('type', 'text/javascript');
		newscript.id = id;
		
		if ( is_object(oldscript) ) {
			oldscript.parentNode.replaceChild(newscript, oldscript);
		} else {
			document.body.appendChild(newscript);
		}
	
		newscript.setAttribute('src', scriptsrc);
	} // includeJS()
	
	/**
	 * Redirects the browser to page
	 * 
	 * @param 	page	The page to redirect the browser to.
	 * @return	void
	 */
	function redirectBrowser(page) {
		if ( typeof page == 'object' ) {
			page = page.firstChild.nodeValue;
		}
		
		window.location = page;
	} // redirectBrowser();
	
//-------------------------------------------------
//	End of function-set cms.functions.js
//-------------------------------------------------	

