	
	function htmlspecialchars ( p_string )
	{
		return p_string
			. replace ( /&/g, '&amp;' )
			. replace ( /</g, '&lt;' )
			. replace ( />/g, '&gt;' )
			. replace ( /"/g, '&quot;' )
//			. replace ( /'/g, '&#039;' )
			;
	};
	
	function setEvent ( object, eventType, handler )
	{
		if ( object.addEventListener )
		{
			object.addEventListener ( eventType, handler, false );
			return true;
		}
		
		if ( object.attachEvent )
			return object.attachEvent ( "on" + eventType, handler );

		return false;
	}

	RegExp.escape = function ( text )
	{
		if ( ! arguments.callee.sRE )
		{
			var specials = [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\' ];
			arguments.callee.sRE = new RegExp( '(\\' + specials.join('|\\') + ')', 'g' );
		}
	
		return text.replace ( arguments.callee.sRE, '\\$1' );
	};

	function getScrollTop()
	{
		// Netscape compliant
		if( typeof( window.pageYOffset ) == 'number' )
			return window.pageYOffset;
		
	    // DOM compliant
		if ( document.body && ( document.body.scrollLeft || document.body.scrollTop ) )
			return document.body.scrollTop;
		
		// IE6 standards compliant mode
		if ( document.documentElement && ( document.documentElement.scrollLeft || document.documentElement.scrollTop ) )
			return document.documentElement.scrollTop;
		
		return null;
	}

	function getScrollLeft()
	{
		// Netscape compliant
		if( typeof( window.pageXOffset ) == 'number' )
			return window.pageXOffset;
		
	    // DOM compliant
		if ( document.body && ( document.body.scrollLeft || document.body.scrollTop ) )
			return document.body.scrollLeft;
		
		// IE6 standards compliant mode
		if ( document.documentElement && ( document.documentElement.scrollLeft || document.documentElement.scrollTop ) )
			return document.documentElement.scrollLeft;
		
		return null;
	}

	function removeNodes( el )
	{
		while ( el.hasChildNodes() )
			el.removeChild ( el.lastChild );
	}
	
	function elementCoordinates ( e )
	{
		var curLeft = curTop = 0;
		
		if ( e.offsetParent )
			do
			{
				curLeft	+= e.offsetLeft;
				curTop	+= e.offsetTop;
			}
			while ( e = e.offsetParent );
			
		return [curLeft,curTop];
	}
	
	function mouseCoordinates( e )
	{
		if (!e)
			var e = window.event;
		
		if ( e.pageX )
			return [ e.pageX, e.pageY ];
		
		if ( e.clientX )
			return [ e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft,
			         e.clientY + document.body.scrollTop + document.documentElement.scrollTop ];
		
		return 0;
	}
	
	function eventElement ( e )
	{
		var targ;
		
		if (!e)
			var e = window.event;
		
		if (e.target)
			targ = e.target;
		else if (e.srcElement)
			targ = e.srcElement;
		
		// defeat Safari bug
		if (targ.nodeType == 3)
			targ = targ.parentNode;
		
		return targ;
	}
	
	function displayTooltip( el, message, time )
	{
		if ( ! time )
			time = 500;
		
		var timeOut = window.setTimeout(
			function()
			{
				var elCoords	= elementCoordinates ( el );
				var newEl		= document.createElement ( 'div' );
				
				// append it
				document.body.appendChild ( newEl );
				
				// set the content
				newEl.className = 'toolTip';
				newEl.innerHTML = message;
				
				// set position
				newEl.style.left = ( elCoords[0] + newEl.offsetWidth - getScrollLeft() > document.documentElement.clientWidth )
					? elCoords[0] - newEl.offsetWidth + 'px'
					: elCoords[0] + el.offsetWidth + 'px';
					
				newEl.style.top = ( elCoords[1] + newEl.offsetHeight - getScrollTop() > document.documentElement.clientHeight )
					? elCoords[1] - newEl.offsetHeight + 'px'
					: elCoords[1] + el.offsetHeight + 'px';
					
				el.onmouseout = function() { el.onmouseout = null; newEl.parentNode.removeChild(newEl); return false; };
			},
			300
			);
		
		el.onmouseout = function() { window.clearTimeout ( timeOut ); return false; };
	}
	
	function cleanUpContent ( el )
	{
		while ( el.hasChildNodes() )
		{
			el.onclick = null;
			cleanUpContent( el.lastChild );
			el.removeChild ( el.lastChild );
		}
	}
	
	function validateEmail ( emailStr )
	{
		var knownDomsPat	= /^(com|net|org|edu|int|mil|gov|arpa|biz|aero|name|coop|info|pro|museum)$/;
		var emailPat		= /^(.+)@(.+)$/;
		var specialChars	= "\\(\\)><@,;:\\\\\\\"\\.\\[\\]";
		var validChars		= "\[^\\s" + specialChars + "\]";
		var quotedUser		= "(\"[^\"]*\")";
		var ipDomainPat		= /^\[(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\]$/;
		var atom			= validChars + '+';
		var word			= "(" + atom + "|" + quotedUser + ")";
		var userPat			= new RegExp("^" + word + "(\\." + word + ")*$");
		var domainPat		= new RegExp("^" + atom + "(\\." + atom +")*$");
		var matchArray		= emailStr.match ( emailPat );

		if ( matchArray == null )
			return false;
		
		var user	= matchArray[1];
		var domain	= matchArray[2];
		var i;
		
		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;

		var IPArray = domain.match ( ipDomainPat );
		if ( IPArray != null )
		{
			for ( i=1; i <= 4; i++ )
				if ( IPArray[i] > 255 )
					return false;
			
			return true;
		}

		var atomPat = new RegExp("^" + atom + "$");
		var domArr	= domain.split(".");
		var len		= domArr.length;
	
		for ( i = 0; i < len; i++ )
			if ( domArr[i].search(atomPat) == -1 )
				return false;

		if ( ( domArr[domArr.length-1].length != 2 ) && ( domArr[domArr.length-1].search(knownDomsPat) == -1 ) )
			return false;

		if ( len < 2 )
			return false;

		return true;
	}
	
	function validateEmailElement ( element, allowEmpty, validClass, invalidClass )
	{
		element.className = ( allowEmpty && ( element.value == '' ) ) || ( validateEmail ( element.value ) )
			? validClass : invalidClass;
	}
	
	function setSelectValue ( selectEl, value )
	{
		var i;
		for ( i = 0; i < selectEl.length; i++ )
			if ( selectEl[i].value == value )
			{
				selectEl.selectedIndex = i;
				return true;
			}
		
		return false;
	}
	
	function createIntInputRestrictionHandler ( element, onFalseHandler )
	{
		createInputRestrictionHandler ( element, /[1234567890]/g, onFalseHandler );
	}
	
	function createInputRestrictionHandler ( element, restrictionType, onFalseHandler )
	{
		element.onkeydown = function noNumbers ( e )
		{
			var e		= e || window.event;
			var code	= e.which || e.keyCode;
			var keychar	= String.fromCharCode ( code );

			if ( code <= 46 )
				return true;
			
			if ( keychar.match ( restrictionType ) )
				return true;
			
			if ( onFalseHandler )
				onFalseHandler ( element );
			
			return false;
		};
	}
	
	function number_format( number, decimals, dec_point, thousands_sep )
	{
		var n			= number;
		var prec		= decimals;
		var toFixedFix	= function ( n, prec )
		{
			var k = Math.pow(10,prec);
			return (Math.round(n*k)/k).toString();
		};

		n		= ! isFinite ( +n ) ? 0 : +n;
		prec	= ! isFinite ( +prec ) ? 0 : Math.abs ( prec );
		var sep	= ( typeof thousands_sep === 'undefined' ) ? ',' : thousands_sep;
		var dec	= ( typeof dec_point === 'undefined' ) ? '.' : dec_point;
		var s	= ( prec > 0 ) ? toFixedFix(n, prec) : toFixedFix ( Math.round(n), prec ); //fix for IE parseFloat(0.55).toFixed(0) = 0;
		var abs	= toFixedFix ( Math.abs(n), prec );

		var _, i;

		if ( abs >= 1000 )
		{
			_		= abs.split(/\D/);
			i		= _[0].length % 3 || 3;
			_[0]	= s.slice(0,i + (n < 0)) + _[0].slice(i).replace(/(\d{3})/g, sep+'$1');
			s		= _.join(dec);
		}
		else
			s = s.replace('.', dec);

		if ( ( s.indexOf ( dec ) === -1 ) && ( prec > 1 ) )
			s += dec + new Array(prec) . join(0) + '0';

		return s;
	}
	
	/******************************* string extensions **********************************/

	String.prototype.trim = function()
	{
		return this.replace(/^\s+|\s+$/g,"");
	};
	
	String.prototype.ltrim = function()
	{
		return this.replace(/^\s+/,"");
	};
	
	String.prototype.rtrim = function()
	{
		return this.replace(/\s+$/,"");
	};

	String.prototype.isFloatString = function()
	{
		return this.match( /^((\s*\d+\s*)*[\.,])?(\s*\d+\s*)+$/ );
	};
	
	String.prototype.iqParseFloat = function()
	{
		return parseFloat ( this.replace(/[^\d,\.]/,'').replace(/,/,'.') );
	};
	
	String.prototype.iqParseInt = function()
	{
		if ( this.trim() == '' )
			return 0;
		
		return parseInt ( this.replace(/[^\d]/,'' ), 10 );
	};
	
	String.prototype.hasPrefix = function( string )
	{
		return this.substring( 0, string.length ) == string;
	};

	String.prototype.trimPrefix = function ( string )
	{
		if ( ! this.hasPrefix ( string ) )
			return false;
	
		return this.substring ( string.length );
	};
	
	String.prototype.escapeRegEx = function()
	{
		return this.replace ( /([\\\^\$*+[\]?{}.=!:(|)])/g, "\\$1" );
	};

	/******************************** date extensions ***********************************/

	Date.prototype.getUTCDaysInMonth = function()
	{
		var nextMonthDate = new Date ( this.getTime() );
		
		nextMonthDate.setUTCFullYear ( this.getUTCFullYear() + ( this.getUTCMonth() == 11 ? 1 : 0 ), ( this.getUTCMonth() + 1 ) % 12, 1 );
		nextMonthDate.setUTCHours ( 0, 0, 0, 0 );
		
		nextMonthDate.setTime ( nextMonthDate.getTime() - 60 * 60 );
		
		return nextMonthDate.getUTCDate();
	};

	Date.prototype.increaseDay = function()
	{
		var hours	= this.getHours();
		var minutes	= this.getMinutes();
		var seconds	= this.getSeconds();
		
		this.setTime ( this.getTime() + 86400000 );
		
		// correct time this to solve the DST problem
		this.setHours ( hours );
		this.setMinutes ( minutes );
		this.setSeconds ( seconds );
	};

	Date.prototype.decreaseDay = function()
	{
		var hours	= this.getHours();
		var minutes	= this.getMinutes();
		var seconds	= this.getSeconds();
		
		this.setTime ( this.getTime() - 86400000 );
		
		// correct time to original values to solve the DST problem
		this.setHours ( hours );
		this.setMinutes ( minutes );
		this.setSeconds ( seconds );
	};

	Date.prototype.addDays = function ( days )
	{
		if ( days < 0 )
			while ( days++ < 0 )
				this.decreaseDay();
		
		else if ( days > 0 )
			while ( days-- > 0 )
				this.increaseDay();
	};
	
	Date.prototype.getUTCFirstOfMonth = function()
	{
		var date = new Date();
		
		date.setUTCFullYear ( this.getUTCFullYear(), this.getUTCMonth(), 1 );
		date.setUTCHours ( 0, 0, 0, 0 );
		
		return date;
	};
	
	Date.prototype.getShortDayNameByNumber = function( number )
	{
		switch ( number )
		{
			default: return undefined;
			case  1: return 'Po';
			case  2: return 'Út';
			case  3: return 'St';
			case  4: return 'Čt';
			case  5: return 'Pá';
			case  6: return 'So';
			case  7: return 'Ne';
		}
	};
	
	Date.prototype.getMonthNameByNumber = function( number )
	{
		switch ( number )
		{
			default: return undefined;
			case  1: return 'Leden';
			case  2: return 'Únor';
			case  3: return 'Březen';
			case  4: return 'Duben';
			case  5: return 'Květen';
			case  6: return 'Červen';
			case  7: return 'Červenec';
			case  8: return 'Srpen';
			case  9: return 'Září';
			case 10: return 'Říjen';
			case 11: return 'Listopad';
			case 12: return 'Prosinec';
		}
	};
	
	Date.prototype.getUTCMonthName = function()
	{
		return this.getMonthNameByNumber( this.getUTCMonth() + 1 );
	};
	
	Date.prototype.setUTCFullYearSafeDay = function ( year, month, day )
	{
		this.setUTCFullYear ( year, month, 1 );
		this.setUTCDate ( Math.min ( this.getUTCDaysInMonth(), day ) );
	};
	
	/**
	 * Following does not have to be both UTC & local!
	 * It does not matter whether we are subtracting a day/year/month in UTC mode or in current time zone; the result is the same.
	 */
	
	Date.prototype.getPreviousMonth = function()
	{
		var date = new Date ( this.getTime() );
		date.setUTCFullYearSafeDay ( this.getUTCFullYear() - ( this.getUTCMonth() == 0 ? 1 : 0 ), ( this.getUTCMonth() + 11 ) % 12, this.getUTCDate() );
		
		return date;
	};
	
	Date.prototype.getNextMonth = function()
	{
		var date = new Date ( this.getTime() );
		date.setUTCFullYearSafeDay ( this.getUTCFullYear() + ( this.getUTCMonth() == 11 ? 1 : 0 ), ( this.getUTCMonth() + 1 ) % 12, this.getUTCDate() );
		
		return date;
	};
	
	Date.prototype.getPreviousYear = function()
	{
		var date = new Date ( this.getTime() );
		date.setUTCFullYearSafeDay ( this.getUTCFullYear() - 1, this.getUTCMonth(), this.getUTCDate() );
		
		return date;
	};
	
	Date.prototype.getNextYear = function()
	{
		var date = new Date ( this.getTime() );
		date.setUTCFullYearSafeDay ( this.getUTCFullYear() + 1, this.getUTCMonth(), this.getUTCDate() );
		
		return date;
	};
	