	/**
	 * Utility functions
	 * @author Tom Hendrikx <tom@whyscream.net>
	 * @version $Id: util.js,v 1.8.6.1 2011/08/26 09:29:33 michel Exp $
	 */

	/**
	 * Create a debug window in the page, and display the debug messages.
	 */
	function debug(str) {
		if(str == null) {
			str = 'null';
		}
		if(str.type != 'string') {
			str = str.toString();
		}
		var bugwin = document.createElement('div');
		bugwin.style.fontFamily = 'monospace';
		bugwin.style.backgroundColor = '#ff0000';
		str = str.replace(/\n/, '<br/>');
		bugwin.innerHTML = str;
		document.getElementsByTagName('body')[0].appendChild(bugwin);

	}

	/**
	 * Get the horizontal position of an object.
	 * @param object obj The object to calculate upon.
	 * @return integer The horizontal offset in px.
	 */
	function getPosX(obj) {
		var pos = 0;
		if(obj.offsetParent) {
			while(obj) {
				// offsetLeft
				pos += obj.offsetLeft;
				if(obj.clientLeft && obj.nodeName != 'TABLE' && obj.nodeName != 'BODY') {
					// clientLeft for MSIE
					pos += obj.clientLeft;
				}
				obj = obj.offsetParent;
			}
		}
		return pos;
	}

	/**
	 * Get the vertical position of an object.
	 * @param object obj The object to calculate upon.
	 * @return integer The vertical offset in px.
	 */
	function getPosY(obj) {
		var pos = 0;
		var orig = obj;
		if(obj.offsetParent) {
			while(obj) {
				// offsetTop
				pos += obj.offsetTop;
				if(obj.clientTop && obj.nodeName != 'TABLE' && obj.nodeName  != 'BODY') {
					// clientTop for MSIE
					pos += obj.clientTop;
				}
				obj = obj.offsetParent;
			}
		}
		return pos;
	}

	/**
	 * Get the next element in the page hierarchy.
	 * @param Object obj The current element.
	 */
	function getNextElement(obj) {

		if(obj.firstChild) {
			obj = obj.firstChild;
		} else if (obj.nextSibling) {
			obj = obj.nextSibling;
		} else {
			while(obj.parentNode) {
				obj = obj.parentNode;
				if(obj.nextSibling) {
					obj = obj.nextSibling;
					break;
				}
			}
		}

		if(obj.nodeType != 1)	{ // no element
			// keep searching
			obj = getNextElement(obj);
		}
		return obj;
	}

	/**
	 * Get the previous element in the page hierarchy.
	 * @param Object obj The current element.
	 */
	function getPrevElement(obj) {

		if(obj.previousSibling) {
			obj = obj.previousSibling;
			if(obj.lastChild) {
				while(obj.lastChild) {
					obj = obj.lastChild;
				}
			}
		} else if(obj.parentNode) {
			obj = obj.parentNode;
		}

		if(obj.nodeType != 1)	{ // no element
			// keep searching
			obj = getPrevElement(obj);
		}
		return obj;
	}

	/**
	 * Output extensive information about the given element.
	 * @param Object obj The target element.
	 */
	function inspectElement(obj) {
		debug(('* Inspecting Element: '+ obj.nodeName + ((obj.nodeType == 1 && obj.id) ? '['+ obj.id +']' : '')).bold());

		var properties = new Array();
		var methods = new Array();
		for(i in obj) {
			if(typeof obj[i] == 'function') {
				methods.push(i +'()'+ (obj[i].arity ? ' ('+ obj[i].arity +' args)' : ''));
			} else {
				properties.push(i +' ('+ typeof obj[i] +'): '+ obj[i]);
			}
		}

		var styles = new Array();
		if(window.getComputedStyle) {	// gecko
			var compStyle = window.getComputedStyle(obj, null);
			for(i in compStyle) {
				styles.push(i +' ('+ typeof compStyle[i] +'): '+ compStyle[i]);
			}
		} else if(obj.currentStyle) {
			for(i in obj.currentStyle) {
				styles.push(i +' ('+ typeof obj.currentStyle[i] +'): '+ obj.currentStyle[i]);
			}
		}

		properties.sort();
		debug('* Element properties:'.bold());
		for(i in properties) {
			debug(properties[i]);
		}
		methods.sort();
		debug('* Element methods:'.bold());
		for(i in methods) {
			debug(methods[i]);
		}
		styles.sort();
		debug('* Element style properties:'.bold());
		for(i in styles) {
			debug(styles[i]);
		}
	}

	/**
	 * Return a style property as rendered by the user-agent.
	 * @param object obj The object which style is considered.
	 * @param string property The name of the style property.
	 * @return string The style property, if any.
	 */
	function getStyle(obj, property) {	  
		if(obj.currentStyle && obj.currentStyle[ property ]) {
			return obj.currentStyle[ property ];
		} else if(window.getComputedStyle) {
			return document.defaultView.getComputedStyle(obj, null)[ property ];
		}
	}

	/**
	 * Generic event handler addition.
	 * Adds an event to an object. Code shamelessly copied from:
	 * http://ejohn.org/projects/flexible-javascript-events/
	 * references: http://www.quirksmode.org/blog/archives/2005/09/addevent_recodi.html
	 * @param object The object to consider.
	 * @param string The type of the event (f.i. 'click', 'load')
	 * @param object function The function to execute.
	 */
	function addEvent( obj, type, fn ) {
	  if ( obj.attachEvent ) {
      obj['e'+type+fn] = fn;
      obj[type+fn] = function(){obj['e'+type+fn]( window.event );}
      obj.attachEvent( 'on'+type, obj[type+fn] );
    } else
      obj.addEventListener( type, fn, false );
    }

	/**
	 * Generic event handler removal.
	 * Remove an earlier specified event from an object. Code shamelessly 
	 * copied from: http://ejohn.org/projects/flexible-javascript-events/
	 * references: http://www.quirksmode.org/blog/archives/2005/09/addevent_recodi.html
	 * @param object The object to consider.
	 * @param string The type of the event (f.i. 'click', 'load')
	 * @param object function The function to execute.
	 */
  function removeEvent( obj, type, fn ) {
    if ( obj.detachEvent ) {
      obj.detachEvent( 'on'+type, obj[type+fn] );
      obj[type+fn] = null;
    } else
      obj.removeEventListener( type, fn, false );
  }


  /**
   * Generic image swap function.
   * Generic function for image rollovers and other image replacements.
   * @param string image_path The path to the new image file.
   * @param object $image The object to work on, for the current image, use 'this'.
   */
  function replaceImage(image_path, image) {
    if (image.tagName != 'IMG') {
       return false;
    }

    image.src = image_path;
  }

  /**
   * Javascript equivalent of strpos.   
   * @param string haystack The string to search in
   * @param string needle The string to search for
   * @param int offset The index to start the search at
   */
  function strpos(haystack, needle, offset){
    var i = haystack.indexOf( needle, offset );
    return i >= 0 ? i : false;
  }

  /**
   * Javascript equivalent of str_replace.   
   * @param string search The string to search for
   * @param string replace The string to replace it with
   * @param string subject The string to search in
   */
  function str_replace(search, replace, subject) { 
    var result = ''; 
    var oldi = 0; 
    for (i = subject.indexOf (search); i > -1; i = subject.indexOf (search, i)){ 
      result += subject.substring (oldi, i); 
      result += replace; 
      i += search.length; 
      oldi = i; 
    }
    return result + subject.substring (oldi, subject.length); 
  }

  /**
   * Javascript equivalent of number_format.   
   * @param string number        The number to format
   * @param string decimals      Sets the number of decimal points. 
   * @param string dec_point     Sets the separator for the decimal point.
   * @param string thousands_sep Sets the thousands separator.
	 * @return string              The formatted number.
   */
  function number_format(number, decimals, dec_point ,thousands_sep ) { 
    number += '';
    if(!decimals) {
      var decimals = 0;
    }
    if(!dec_point) {
      var dec_point = ".";
    }
    if(!thousands_sep) {
      var thousands_sep = ",";
    }
    x = number.split('.');
    x1 = x[0];
    if(!x[1]){var dec = "";}else{dec = x[1];}
    while (dec.length < decimals) {
      dec = dec+"0";
    }
    x2 = dec.length > 0 ? dec_point + dec : '';
    var rgx = /(\d+)(\d{3})/;
    while (rgx.test(x1)) {
    x1 = x1.replace(rgx, '$1' + thousands_sep + '$2');
    }
    return x1 + x2;
  }

