/**
 * 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
 */
 
	/**
	 * A PHP-style method to see if given object is actually an object, and not
	 * an undefined variable.
	 * 
	 * @author R.J.T. de Vries <rdevries@thirdwave.nl>
	 * @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()
	
	/**
	 * Works the same as the PHP equivalent.
	 * 
	 * @author R.J.T. de Vries <rdevries@thirdwave.nl>
	 * @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;
		} // for()
		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).
	 * 
	 * @author R.J.T. de Vries <rdevries@thirdwave.nl>
	 * @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.
	 * 
	 * @author R.J.T. de Vries <rdevries@thirdwave.nl>
	 * @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).
	 *
	 * @author R.J.T. de Vries <rdevries@thirdwave.nl>
	 * @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()
		
	/**
	 * Validate given e-mailaddress based on compilation of characters. No check
	 * is included for validating toplevel domain.
	 *
	 * @author R.J.T. de Vries <rdevries@thirdwave.nl>
	 * @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.
	 * 
	 * @author R.J.T. de Vries <rdevries@thirdwave.nl>
	 * @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;
		} // while()
		if ( !is_object(obj) || !obj.tagName || obj.tagName.toUpperCase() != tag ) {
			return false;
		}
		return obj;
	} // getParentNode()
	
	/**
	 * Get the index number of a childNode in its parentNode
	 * 
	 * @author R.J.T. de Vries <rdevries@thirdwave.nl>
	 * @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 ( !is_object(child) || !is_object(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
	 * 
	 * @author R.J.T. de Vries <rdevries@thirdwave.nl>
	 * @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.
	 * 
	 * @author R.J.T. de Vries <rdevries@thirdwave.nl>
	 * @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'.
	 * 
	 * @author R.J.T. de Vries <rdevries@thirdwave.nl>
	 * @return	string 		name of the attribute to use.
	 */
	function getClassName() {
		return isIE() ? 'className' : 'class';
	} // getClassName()
	
	/**
	 * Get horizontol scrolling of the window in pixels.
	 * 
	 * @author R.J.T. de Vries <rdevries@thirdwave.nl>
	 * @return integer
	 */
	function getScrollX() {
		return document.all ? document.body.scrollLeft : window.scrollX;
	} // getScrollX()
	
	/**
	 * Get vertical scrolling of the window in pixels.
	 * 
	 * @author R.J.T. de Vries <rdevries@thirdwave.nl>
	 * @return integer
	 */
	function getScrollY() {
		return document.all ? document.body.scrollTop : window.scrollY;
	} // getScrollY()
	
	/**
	 * Get vertical scrolling of the window in pixels.
	 * 
	 * @author R.J.T. de Vries <rdevries@thirdwave.nl>
	 * @return integer
	 */
	function getScrollPosY() {
		// all except Explorer
		if ( self.pageYOffset ) {
			return self.pageYOffset;
		}
		// Explorer 6 Strict
		if ( document.documentElement && document.documentElement.scrollTop ) {
			return document.documentElement.scrollTop;
		}
		
		// all other Explorers
		if ( document.body ) {
			return document.body.scrollTop;
		}	
	} // getScrollPosY()

	/**
	 * Get the x position of the given dom object element.
	 * 
	 * @author R.J.T. de Vries <rdevries@thirdwave.nl>
	 * @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.
	 * 
	 * @author R.J.T. de Vries <rdevries@thirdwave.nl>
	 * @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
	 * 
	 * @author R.J.T. de Vries <rdevries@thirdwave.nl>
	 * @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
	 * 
	 * @author R.J.T. de Vries <rdevries@thirdwave.nl>
	 * @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);
		return parseInt(domObj.offsetHeight);
	} // getHeight()

	/**
	 * Get the width of the current browser window in pixels.
	 * 
	 * @author R.J.T. de Vries <rdevries@thirdwave.nl>
	 * @return integer	width of the window in pixels.
	 * @access public
	 */
	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.
	 * 
	 * @author R.J.T. de Vries <rdevries@thirdwave.nl>
	 * @return		integer		height of the window in pixels.
	 * @access public
	 */
	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()
	
	/**
	 * Get the real distance from the given object to the left or topside
	 * (direction) of the window.
	 *  
	 * @author R.J.T. de Vries <rdevries@thirdwave.nl>
	 * @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) {
		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	public/private
	 */
	function getRelPosY(el) {
		var y = 0;
				
		while (el) {
			y += el.offsetTop;
			el = el.offsetParent;
		}
		return y;
	} // getRelPosY()
	
	/**
	 * 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.
	 * 
	 * @author R.J.T. de Vries <rdevries@thirdwave.nl>
	 * @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.
	 * 
	 * @author R.J.T. de Vries <rdevries@thirdwave.nl>
	 * @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.
	 * 
	 * @author R.J.T. de Vries <rdevries@thirdwave.nl>
	 * @param		string		attr		name of the attribute to check.
	 * @param		mixed 		val			value to check for.
	 * @param		string 		tag			limit search to tags with this tagname.
	 * @param		object		parent	parent element from which to start search.
	 * @return	array 		r				array of DOM objects found
	 */
	document.getElementsByAttribute = function(attr, val, tag, parent) {
		if ( !tag ) tag = '*';
		var s = (parent || document).getElementsByTagName(tag), i = s.length, e, r = [];
		
		while ( i-- ) {
			e = s[i];
			if ( e.getAttribute ) {
				// 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 ( e.getAttribute(attr) == val ) r.push(e);
				}
			}
		} // while()
		return r;
	}	// 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()
	
	/**
	 * 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.
	 * 
	 * @author R.J.T. de Vries <rdevries@thirdwave.nl>
	 * @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.
	 * 
	 * @author R.J.T. de Vries <rdevries@thirdwave.nl>
	 * @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.
	 * 
	 * @author R.J.T. de Vries <rdevries@thirdwave.nl>
	 * @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
	 * 
	 * @author R.J.T. de Vries <rdevries@thirdwave.nl>
	 * @see getFrameDocument()
	 */
	function getIFrameDocument(frameId) {
		return getFrameDocument(frameId);
	} // getIFrameDocument()

	/**
	 * Get the window in the iframe with the given ID.
	 * 
	 * @author R.J.T. de Vries <rdevries@thirdwave.nl>
	 * @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.
	 * 
	 * @author R.J.T. de Vries <rdevries@thirdwave.nl>
	 * @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.
	 * 
	 * @author R.J.T. de Vries <rdevries@thirdwave.nl>
	 * @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?
	 * 
	 * @author R.J.T. de Vries <rdevries@thirdwave.nl>
	 * @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.
	 * 
	 * @author R.J.T. de Vries <rdevries@thirdwave.nl>
	 * @return 	object		toplevel-window
	 */
	function getToplevelWindow() {
		var win = window;
		while ( win != win.parent && is_object(win.parent) ) {
			win = win.parent;
		}
		return win;
	} // getToplevelWindow()

	/**
	 * Return number of seconds and milliseconds as a float for current date.
	 * 
	 * @author R.J.T. de Vries <rdevries@thirdwave.nl>
	 * @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'.
	 * 
	 * @author R.J.T. de Vries <rdevries@thirdwave.nl>
	 * @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;
			default:
				return false;
		} // switch()
	} // navChk()
	
	/**
	 * Is the current browser Internet Explorer with a version of 'v' or higher?
	 * 
	 * @author R.J.T. de Vries <rdevries@thirdwave.nl>
	 * @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') );
			default: return false;
		} // switch()
	} // isIE()
	
	/**
	 * Get the CSS rule with the identifier that matches the classname given.
	 * 
	 * @author R.J.T. de Vries <rdevries@thirdwave.nl>
	 * @param 	string 	classname		name of identifier to look for
	 * @return 	mixed								cssRule object if found or false on failure
	 */
	function getCssRule(classname) {
		if ( !document.styleSheets ) return false;
		
		for ( var i = 0; i < document.styleSheets.length; i++ ) {
			var rules = new Array();
			if ( document.styleSheets[i].cssRules ) rules = document.styleSheets[i].cssRules;
			else if (document.styleSheets[i].rules)	rules = document.styleSheets[i].rules;
			else continue;
			
			for ( var j = 0; j < rules.length; j++ ) {
				if ( rules[j].selectorText.toLowerCase().indexOf(classname.toLowerCase()) != -1 ) return rules[j];
			}
		}
			
		return false;
	} // getCssRule()
	
	/**
	 * open a new window with given options
	 * 
	 * @author R.J.T. de Vries <rdevries@thirdwave.nl>
	 * @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 = (screen.availWidth - w) / 2;
		ypos = (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;
		
		var win = window.open(url, winname, winprops);
		
		if ( !is_object(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.
	 * 
	 * @author R.J.T. de Vries <rdevries@thirdwave.nl>
	 * @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) {
		if ( area.value.length > len ) {
			area.value = area.value.substr(0, len);
		}
	} // checkMaxLength()

	/**
	 * Include a new javascript tag in the document body
	 * 
	 * @author R.J.T. de Vries <rdevries@thirdwave.nl>
	 * @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()
	
//-------------------------------------------------
//	End of function-set cms.functions.js
//-------------------------------------------------	
