
		// <!-- 
		// favbutton.js
function createXMLHttpRequest() {
	if (window.XMLHttpRequest) {
		return new XMLHttpRequest();
	} else if (window.ActiveXObject) {
		return new ActiveXObject("Microsoft.XMLHTTP");
	}
}

function favorite_rateIt(element, object_str, newImage) {
	var ajaxURL = "../raterservlet?value=" + escape("10");
	
	ajaxURL += "&titem=" + escape(object_str);

	var request = createXMLHttpRequest();
	request.open("GET", ajaxURL, true);
	request.send(null);
	
	element.src = newImage; 
}			
	

	// favdialog.js
function createXMLHttpRequest() {
	if (window.XMLHttpRequest) {
		return new XMLHttpRequest();
	} else if (window.ActiveXObject) {
		return new ActiveXObject("Microsoft.XMLHTTP");
	}
}

function askForMakingFavorite(dialogId) {
	var element = document.getElementById(dialogId); 
	element.className = "favdialog_visible";
}

function hideDialog(dialogId) {
	var element = document.getElementById(dialogId); 
	element.className = "favdialog_invisible";
}

function hideDialogAndNavigate(dialogId, pageUrl) {
	var element = document.getElementById(dialogId); 
	element.className = "favdialog_invisible";
	location.href = pageUrl;
}

function rateAsFavourite(itemId) {
	var ajaxURL = "../raterservlet?value=" + escape("10");
	
	ajaxURL += "&titem=" + escape(itemId);

	var request = createXMLHttpRequest();
	request.open("GET", ajaxURL, true);
	request.send(null);
}

function rateAsMinimal(itemId) {
	var ajaxURL = "../raterservlet?value=" + escape("1");
	
	ajaxURL += "&titem=" + escape(itemId);

	var request = createXMLHttpRequest();
	request.open("GET", ajaxURL, true);
	request.send(null);
}

function storeAsAnswered(itemId, userAnswer) {
	var ajaxURL = "../answerstore?for=" + escape(itemId) +"&answer=" + escape(userAnswer);
	
	var request = createXMLHttpRequest();
	request.open("GET", ajaxURL, true);
	request.send(null);	
}

function makeAsFavourite(element, itemId, newImage) {
	var ajaxURL = "../answerstore?for=" + escape(itemId) +"&answer=True";
	
	var request = createXMLHttpRequest();
	request.open("GET", ajaxURL, true);
	request.send(null);	

	element.src = newImage;
	
	rateAsFavourite(itemId); 
}			


	// utilities.js


UTILITIES_VERSION = 0.1;


/**
 * I'm nothing more than a collection of static utility methods for
 * doing various things.
 */
function Utilities() {}



/**
 * I remove all the children of the specified parent node.
 *
 * @param parent The node whose children should be removed
 */
Utilities.removeChildren = function(parent) {
	for (var i = parent.childNodes.length - 1; i >= 0;) {
		parent.removeChild(parent.childNodes[i]);
		
		i = i-1;
	}
}



/**
 * I return an object with an x and y fields, indicating the object's
 * offset from the top left corner of the document.  The 'offsets'
 * argument is optional; if not provided, one will be initialized and
 * used.  It is exposed as a parameter because it can be useful for
 * computing deltas.  The 'object' parameter can be either an actual
 * document element, or the ID of one.
 *
 * @param object The object to compute the offset of.  May be an object
 *		or the ID of one.
 * @param offsets The starting offsets to calculate from.  In almost
 *		all cases, this should be omitted.
 * @return An offsets object with x and y fields, indicating the
 *		computed offsets for the object.  If an offsets object is
 *		passed, that will be the object returned, though the values
 *		will have been changed.
 */
Utilities.getOffsets = function(object, offsets) {
	if (! offsets) {
		offsets = new Object();
		offsets.x = offsets.y = 0;
	}
	if (typeof object == "string")
		object = document.getElementById(object);
	offsets.x += object.offsetLeft;
	offsets.y += object.offsetTop;
	do {
		object = object.offsetParent;
		if (! object)
			break;
		offsets.x += object.offsetLeft;
		offsets.y += object.offsetTop;
	} while(object.tagName.toUpperCase() != "BODY");
	return offsets;
}



/**
 *
 */
Utilities.listAppend = function(list, value, delimiter) {
	if (typeof delimiter == "undefined")
		delimiter = ",";
	if (list == "")
		return value;
	else
		return list + delimiter + value;
}	

	
	// cssquery2-p.js
/*
cssQuery, version 2.0.2 (2005-08-19)
Copyright: 2004-2005, Dean Edwards (http://dean.edwards.name/)
License: http://creativecommons.org/licenses/LGPL/2.1/
Packed by Steffen Rusitschka; included modules: css level2 and standard.
*/
eval(function(p,a,c,k,e,d){e=function(c){return(c<a?"":e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c){c=c-1;d[e(c)]=k[c]||e(c)}k=[(function(e){return d[e]})];e=(function(){return'\\w+'});c=1};while(c){c=c-1; if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('6 B=7(){6 1j="2.0.2ruzee";6 $23=/\\s*,\\s*/;6 B=7($9,$$a){6 $c=[];6 $1h=o.2q.1Q&&!$$a;6 $1k=($$a)?($$a.2g==1t)?$$a:[$$a]:[z];6 $$u=14($9).1w($23),i;m(i=0;i<$$u.p;i++){$9=1q($$u[i]);b(P&&$9.Z(0,3).1R("")==" *#"){$9=$9.Z(2);$$a=24([],$1k,$9[1])}1s $$a=$1k;6 j=0,$I,$x,$o,$M="";H(j<$9.p){$I=$9[j++];$x=$9[j++];$M+=$I+$x;$o="";b($9[j]=="("){H($9[j++]!=")"&&j<$9.p){$o+=$9[j]}$o=$o.Z(0,-1);$M+="("+$o+")"}$$a=($1h&&O[$M])?O[$M]:1u($$a,$I,$x,$o);b($1h)O[$M]=$$a}$c=$c.2i($$a)}1S B.2k;8 $c};B.1B=7(){8"7 B() {\\n  [1j "+1j+"]\\n}"};6 O={};B.1Q=R;B.2l=7($9){b($9){$9=1q($9).1R("");1S O[$9]}1s O={}};6 28={};6 1g=R;B.2m=7($J,$15){b(1g)1f("$15="+21($15));28[$J]=17 $15()};B.2n=7($F){8 $F?1f($F):l};6 u={};6 L={};6 t={c:/\\[([\\w-]+(\\|[\\w-]+)?)\\s*(\\W?=)?\\s*([^\\]]*)\\]/};6 E=[];u[" "]=7($f,$a,$k,$h){6 $5,i,j;m(i=0;i<$a.p;i++){6 $10=T($a[i],$k,$h);m(j=0;($5=$10[j]);j++){b(D($5)&&16($5,$h))$f.q($5)}}};u["#"]=7($f,$a,$v){6 $5,j;m(j=0;($5=$a[j]);j++)b($5.v==$v)$f.q($5)};u["."]=7($f,$a,$N){$N=17 1M("(^|\\\\s)"+$N+"(\\\\s|$)");6 $5,i;m(i=0;($5=$a[i]);i++)b($N.d($5.N))$f.q($5)};u[":"]=7($f,$a,$1V,$o){6 $d=L[$1V],$5,i;b($d)m(i=0;($5=$a[i]);i++)b($d($5,$o))$f.q($5)};L["2p"]=7($5){6 $z=X($5);b($z.1m)m(6 i=0;i<$z.1m.p;i++){b($z.1m[i]==$5)8 18}};L["2B"]=7($5){};6 D=7($5){8($5&&$5.1v==1&&$5.k!="!")?$5:1T};6 1d=7($5){H($5&&($5=$5.2s)&&!D($5))1W;8 $5};6 12=7($5){H($5&&($5=$5.2t)&&!D($5))1W;8 $5};6 1Z=7($5){8 D($5.1Y)||12($5.1Y)};6 2v=7($5){8 D($5.1X)||1d($5.1X)};6 U=7($5){6 $U=[];$5=1Z($5);H($5){$U.q($5);$5=12($5)}8 $U};6 P=18;6 1o=7($5){6 $z=X($5);8(2y $z.20=="2A")?/\\.26$/i.d($z.2C):2D($z.20=="2F 2G")};6 X=7($5){8 $5.2H||$5.z};6 T=7($5,$k){8($k=="*"&&$5.1l)?$5.1l:$5.T($k)};6 1b=7($5,$k,$h){b($k=="*")8 D($5);b(!16($5,$h))8 R;b(!1o($5))$k=$k.2J();8 $5.k==$k};6 16=7($5,$h){8!$h||($h=="*")||($5.2K==$h)};6 1J=7($5){8 $5.1K};7 24($f,$a,v){6 $c,i,j;m(i=0;i<$a.p;i++){b($c=$a[i].1l.2L(v)){b($c.v==v)$f.q($c);1s b($c.p!=1T){m(j=0;j<$c.p;j++){b($c[j].v==v)$f.q($c[j])}}}}8 $f};b(![].q)1t.29.q=7(){m(6 i=0;i<o.p;i++){l[l.p]=o[i]}8 l.p};6 $19=/\\|/;7 1u($$a,$I,$x,$o){b($19.d($x)){$x=$x.1w($19);$o=$x[0];$x=$x[1]}6 $f=[];b(u[$I]){u[$I]($f,$$a,$x,$o)}8 $f};6 $1y=/^[^\\s>+~]/;6 $$1z=/[\\s#.:>+~()@]|[^\\s#.:>+~()@]+/g;7 1q($9){b($1y.d($9))$9=" "+$9;8 $9.c($$1z)||[]};6 $1A=/\\s*([\\s>+~(),]|^|$)\\s*/g;6 $22=/([\\s>+~,]|[^(]\\+|^)([#.:@])/g;6 14=7($9){8 $9.C($1A,"$1").C($22,"$1*$2")};6 1p={1B:7(){8"\'"},c:/^(\'[^\']*\')|("[^"]*")$/,d:7($y){8 l.c.d($y)},1D:7($y){8 l.d($y)?$y:l+$y+l},1C:7($y){8 l.d($y)?$y.Z(1,-1):$y}};6 1U=7($1F){8 1p.1C($1F)};6 $1G=/([\\/()[\\]?{}|*+-])/g;7 1a($y){8 $y.C($1G,"\\\\$1")};u[">"]=7($f,$a,$k,$h){6 $5,i,j;m(i=0;i<$a.p;i++){6 $10=U($a[i]);m(j=0;($5=$10[j]);j++)b(1b($5,$k,$h))$f.q($5)}};u["+"]=7($f,$a,$k,$h){m(6 i=0;i<$a.p;i++){6 $5=12($a[i]);b($5&&1b($5,$k,$h))$f.q($5)}};u["@"]=7($f,$a,$1H){6 $d=E[$1H].d;6 $5,i;m(i=0;($5=$a[i]);i++)b($d($5))$f.q($5)};L["2c-2d"]=7($5){8!1d($5)};L["1e"]=7($5,$F){$F=17 1M("^"+$F,"i");H($5&&!$5.Q("1e"))$5=$5.2e;8 $5&&$F.d($5.Q("1e"))};t.1O=/\\\\:/g;t.1i="@";t.K={};t.C=7($c,$r,$h,$1N,$A){6 $13=l.1i+$c;b(!E[$13]){$r=l.1P($r,$1N||"",$A||"");E[$13]=$r;E.q($r)}8 E[$13].v};t.1L=7($9){$9=$9.C(l.1O,"|");6 $c;H($c=$9.c(l.c)){6 $C=l.C($c[0],$c[1],$c[2],$c[3],$c[4]);$9=$9.C(l.c,$C)}8 $9};t.1P=7($1n,$d,$A){6 $S={};$S.v=l.1i+E.p;$S.J=$1n;$d=l.K[$d];$d=$d?$d(l.Q($1n),1U($A)):R;$S.d=17 2r("e","8 "+$d);8 $S};t.Q=7($J){27($J.2u()){G"v":8"e.v";G"2z":8"e.N";G"m":8"e.2E";G"25":b(P){8"21((e.2I.c(/25=\\\\1r?([^\\\\s\\\\1r]*)\\\\1r?/)||[])[1]||\'\')"}}8"e.Q(\'"+$J.C($19,":")+"\')"};t.K[""]=7($r){8 $r};t.K["="]=7($r,$A){8 $r+"=="+1p.1D($A)};t.K["~="]=7($r,$A){8"/(^| )"+1a($A)+"( |$)/.d("+$r+")"};t.K["|="]=7($r,$A){8"/^"+1a($A)+"(-|$)/.d("+$r+")"};6 1I=14;14=7($9){8 1I(t.1L($9))};P=1f("R;/*@2f@b(@\\2h)P=18@2j@*/");b(!P){T=7($5,$k,$h){8 $h?$5.2o("*",$k):$5.T($k)};16=7($5,$h){8!$h||($h=="*")||($5.2w==$h)};1o=z.1x?7($5){8/26/i.d(X($5).1x)}:7($5){8 X($5).2a.k!="2b"};1J=7($5){8 $5.Y||$5.1K||1c($5)};7 1c($5){6 $Y="",$V,i;m(i=0;($V=$5.2x[i]);i++){27($V.1v){G 11:G 1:$Y+=1c($V);1E;G 3:$Y+=$V.2M;1E}}8 $Y}}1g=18;8 B}();',62,173,'|||||element|var|function|return|selector|from|if|match|test||results||namespace|||tagName|this|for||arguments|length|push|attribute||AttributeSelector|selectors|id||filter|string|document|value|cssQuery|replace|thisElement|attributeSelectors|code|case|while|token|name|tests|pseudoClasses|cacheSelector|className|cache|isMSIE|getAttribute|false|attributeSelector|getElementsByTagName|childElements|node||getDocument|textContent|slice|subset||nextElementSibling|key|parseSelector|script|compareNamespace|new|true|NAMESPACE|regEscape|compareTagName|_getTextContent|previousElementSibling|lang|eval|loaded|useCache|PREFIX|version|base|all|links|propertyName|isXML|Quote|_toStream|x22|else|Array|select|nodeType|split|contentType|STANDARD_SELECT|STREAM|WHITESPACE|toString|remove|add|break|text|ESCAPE|attributeSelectorID|_parseSelector|getTextContent|innerText|parse|RegExp|compare|NS_IE|create|caching|join|delete|null|getText|pseudoClass|continue|lastChild|firstChild|firstElementChild|mimeType|String|IMPLIED_ALL|COMMA|_msie_selectById|href|xml|switch|modules|prototype|documentElement|HTML|first|child|parentNode|cc_on|constructor|x5fwin32|concat|end|error|clearCache|addModule|valueOf|getElementsByTagNameNS|link|callee|Function|previousSibling|nextSibling|toLowerCase|lastElementChild|prefix|childNodes|typeof|class|unknown|visited|URL|Boolean|htmlFor|XML|Document|ownerDocument|outerHTML|toUpperCase|scopeName|item|nodeValue'.split('|'),0,{}))


	// stars.js
var starImageOffLeft = new Image();
starImageOffLeft.src = "../images/small_greyStar_left.gif";
var starImageOffRight = new Image();
starImageOffRight.src = "../images/small_greyStar_right.gif";

var starImageOrangeLeft = new Image();
starImageOrangeLeft.src = "../images/small_orangeStar_left.gif";
var starImageOrangeRight = new Image();
starImageOrangeRight.src = "../images/small_orangeStar_right.gif";

var starImageGreenLeft = new Image();
starImageGreenLeft.src = "../images/small_greenStar_left.gif";
var starImageGreenRight = new Image();
starImageGreenRight.src = "../images/small_greenStar_right.gif";


function createXMLHttpRequest() {
	if (window.XMLHttpRequest) {
		return new XMLHttpRequest();
	} else if (window.ActiveXObject) {
		return new ActiveXObject("Microsoft.XMLHTTP");
	}
}
function setRatingValue(id, rating) {
	var fullValueElementID = id + "_value";
	var valueElement = document.getElementById(fullValueElementID);
	valueElement.value = rating;


	var fullTargetItemElementID = id + "_titem";
	var targetItemElement = document.getElementById(fullTargetItemElementID);
	// AKA - It doesn't make sence to send request if item id is not specified
	if (targetItemElement != null) {
		var ajaxURL = "../raterservlet?value=" + escape(rating);
	
		ajaxURL += "&titem=" + escape(targetItemElement.value);
	
		var request = createXMLHttpRequest();
		request.open("GET", ajaxURL, true);
		request.send(null);
	}
}

/** By some reasons  - unknown I cannot pass i into setRatingValue
 from rater_rateIt function, so, I should pass posinter to element
 and calculate i in loop here
 */
function setRatingValueToElement(toElement) {
	//get id from toElement id
	var id = toElement.id.substring(0, toElement.id.lastIndexOf("_"));
	
	var rateItElementID = id + "_rateit";
	var rateItElement = document.getElementById(rateItElementID);
	rateItElement.innerHTML = "STORED";
	
	for (var i = 1; i <= 10; i++) {
		var fullElementID = id + "_img" + formatTwoDigitNumber(i);
		var element = document.getElementById(fullElementID);
		
		if (element.id == toElement.id) {
			setRatingValue(id,i);
			return;
		}
	}
	
	/*var rateItElementID = id + "_rateit";
	var rateItElement = document.getElementById(rateItElementID);
	
	if(document.all){
		// for IE
    	rateItElement.innerText = "Rating Stored";
	} else{
		// for Firefox
    	rateItElement.textContent = "Rating Stored";
	}*/	
}

function ratingMouseOut(id) {
	var fullElementID = id + "_value";
	var element = document.getElementById(fullElementID);
	if (element.value == "") {
		setRatingDisplay(id, 0);
	} else {
		setRatingDisplay(id, parseInt(element.value, 10));
	}
}
function setRatingDisplay(id, rating) {
	var starImageOnRight = starImageOrangeRight;
	var starImageOnLeft = starImageOrangeLeft;
	
	// get scheme
	var schemeElementID = id + "_scheme";
	var schemeElement = document.getElementById(schemeElementID);
	var scheme = "average";
	
	if (schemeElement != null) {
		scheme = schemeElement.value;
		
		if (scheme == "personal") {
			starImageOnRight = starImageGreenRight;
			starImageOnLeft = starImageGreenLeft;
		}
	}
	
	if( rating == 0 ) { // if user click on 'rate it' then minimal rating is 1
		rating = 1;
	} 
	
	for (var i = 1; i <= 10; i++) {
		var imageToUse;
		if ((i % 2 == 0) && (rating >= i)) {
			imageToUse = starImageOnRight;
		} else if ((i % 2 == 0) && (rating < i)) {
			imageToUse = starImageOffRight;
		} else if ((i % 2 == 1) && (rating >= i)) {
			imageToUse = starImageOnLeft;
		} else if ((i % 2 == 1) && (rating < i)) {
			imageToUse = starImageOffLeft;
		}
		var fullElementID = id + "_img" + formatTwoDigitNumber(i);
		var element = document.getElementById(fullElementID);
		element.src = imageToUse.src;
	}
}

function setRatingDisplayToElement(toElement) {
	//get id from toElement id
	var id = toElement.id.substring(0, toElement.id.lastIndexOf("_"));

	var starImageOnRight = starImageOrangeRight;
	var starImageOnLeft = starImageOrangeLeft;
	
	// get scheme
	var schemeElementID = id + "_scheme";
	var schemeElement = document.getElementById(schemeElementID);
	var scheme = "average";
	
	if (schemeElement != null) {
		scheme = schemeElement.value;
		
		if (scheme == "personal") {
			starImageOnRight = starImageGreenRight;
			starImageOnLeft = starImageGreenLeft;
		}
	}
	var meetRating = false;
	
	for (var i = 1; i <= 10; i++) {
		var imageToUse;
		if ((i % 2 == 0) && (meetRating == false)) {
			imageToUse = starImageOnRight;
		} else if ((i % 2 == 0) && (meetRating == true)) {
			imageToUse = starImageOffRight;
		} else if ((i % 2 == 1) && (meetRating == false)) {
			imageToUse = starImageOnLeft;
		} else if ((i % 2 == 1) && (meetRating == true)) {
			imageToUse = starImageOffLeft;
		}
		var fullElementID = id + "_img" + formatTwoDigitNumber(i);
		var element = document.getElementById(fullElementID);
		element.src = imageToUse.src;
		
		if (element.id == toElement.id) {
			meetRating = true;
		}
	}
}

function formatTwoDigitNumber(number) {
	if (number < 10) {
		return "0" + number;
	} else {
		return "" + number;
	}
}

function rater_rateIt(id) {
	var schemeElementID = id + "_scheme";
	var schemeElement = document.getElementById(schemeElementID);
	schemeElement.value="personal";

	var rateItElementID = id + "_rateit";
	var rateItElement = document.getElementById(rateItElementID);
	rateItElement.innerHTML = "CLICK STARS";
	
	setRatingValue( id, 1 ); // default rating value: like one half star	


	//rateItElement.className = "invisible";
	
	// set events for stars
	for (var i = 1; i <= 10; i++) {
		var fullElementID = id + "_img" + formatTwoDigitNumber(i);
		var element = document.getElementById(fullElementID);

				
//		if (document.all) { //IE
			element.onmouseover=function() { setRatingDisplayToElement(this); };
			element.onmouseout=function() { ratingMouseOut(id); };
			element.onclick=function() { setRatingValueToElement(this); };
/*		} else {
			element.setAttribute("onmouseover", "setRatingDisplay('" + id + "', " + i + ");");
			element.setAttribute("onmouseout", "ratingMouseOut('" + id + ");");
			element.setAttribute("onclick", "setRatingValue('" + id + "', " + i + ");");
		}	
*/
		element.alt = "click to rate";
		
		var fullElementID = id + "_img" + formatTwoDigitNumber(i);
		var element = document.getElementById(fullElementID);
		element.src = starImageOffRight.src;
		
	}
		
	setRatingDisplay(id, 0);	
}


	// waitingbox.js			
function display_waiting_box() {
		element = document.getElementById("popup-area");
		if (element.className == "popup") {
			element.className = "popup-hidden";
		} else if (element.className == "popup-hidden") {
			element.className = "popup";
		}
		
		setTimeout('document.images["popup-img"].src = "../images/starRotating.gif"', 200);
}

function hidePopup(element) {
	element.className = "popup-hidden";
}
	


// ruzeeborders.js
//
/**
 * RuzeeBorders 0.12
 * (c) 2006 Steffen Rusitschka <steffen@rusitschka.de>
 *
 * RuzeeBorders is freely distributable under the terms of an MIT-style license.
 * For details, see http://www.ruzee.com/
 */

var RUZEE=window.RUZEE||{};
RUZEE.userAgent=navigator.userAgent.toLowerCase();
RUZEE.isIE=typeof window.RUZEE.isIE != 'undefined'
  ?window.RUZEE.isIE
  :RUZEE.userAgent.indexOf('msie')>=0
    && RUZEE.userAgent.indexOf('opera')==-1;
RUZEE.isStrict=typeof window.RUZEE.isStrict != 'undefined'
  ?window.RUZEE.isStrict
  :(document.compatMode?document.compatMode!='BackCompat':RUZEE.userAgent.indexOf('safari')==-1?false:true);

RUZEE.Borders={

  /**
   * Set to false to not draw the borders automatically on
   * domload when RUZEE.Events are available.
   */
  autoRender:true,

  /** Add mapping rules to be executed on render(). */
  add:function(mappings){
    for(rule in mappings){
      var rules=rule.split(',');
      for(var i=0; i<rules.length; ++i){
        var r=rules[i].replace(/^\s+|\s+$/,'');
        var ms=RUZEE.Borders.mappings[r]||{};
        for (m in mappings[rule]) ms[m]=mappings[rule][m];
        RUZEE.Borders.mappings[r]=ms;
      }
    }
  },

  /**
   * Render all added mapping rules into the DOM
   * If RUZEE.Events is not available, this method MUST be called in the
   * window.onload method (or with a similar technique)!
   * Note: Since v0.12, this method is asynchronous! If you need to do
   * stuff AFTER the rendering finished, do it inside the function you passed
   * in via the onfinished parameter.
   */
  render:function(onfinished){
    if(onfinished) RUZEE.Borders.onfinished=onfinished;
    var start=new Date().getTime();
    for(rule in RUZEE.Borders.mappings){
      var e=RUZEE.Borders.cssQuery(rule);
      var b=new RUZEE.Borders.Border(RUZEE.Borders.mappings[rule])
      delete RUZEE.Borders.mappings[rule];
      b.calc(e);
      // if we are rendering for more than 3 seconds, give Firefox some time to get
      // rid of the "unresponsive script" message.
      if(new Date().getTime()-start>3000){
        setTimeout('RUZEE.Borders.render()',0);
        return;
      }
    }
    RUZEE.Borders.renderCalcs();
    if(RUZEE.Borders.onfinished) RUZEE.Borders.onfinished();
  },

  /** The Border class constructor */
  Border:function(d){
    var rad=d.cornerRadius||8;
    this.shadowShift=0;
    this.setEdges(d.edges||'lrtb');
    this.height=d.height||0;
    var b=null;
    switch(d.borderType){
    case 'simple':
      this.cornerRadius=this.shadowRadius=this.shadowPadding=rad;
      this.coShadowS='000';
      break;
    case 'shadow':
      var sw=d.shadowWidth||8;
      this.cornerRadius=rad;
      this.shadowRadius=rad+sw*2;
      this.shadowPadding=rad+sw;
      this.shadowShift=Math.round(sw/2);
      this.coShadowS=d.shadowColor||'000';
      break;
    case 'fade':
      this.cornerRadius=this.shadowPadding=1;
      this.shadowRadius=rad;
      this.coShadowS='.fade';
      break;
    case 'glow':
      this.cornerRadius=this.shadowPadding=rad;
      this.shadowRadius=rad+(d.glowWidth||rad);
      this.coShadowS=d.glowColor||'fff';
      break;
    default:
      alert('Unknown borderType: '+d.borderType);
    }
  },

  // ---- internal fields and methods ----

  /** the mappings: 'CSS rule' -> Border */
  mappings:{},

  /** The corner cache */
  cache:{},

  /** The completed calulations to render */
  calcs:[],

  /** if Dean Edward's cssQuery is available, use it */
  cssQuery:function(s){
    var c=s.charAt(0);
    if(c=='#'&&!(/\s/.test(s))) return [ document.getElementById(s.substr(1)) ];
    if(window.cssQuery) return window.cssQuery(s);

    alert("Don't know what to do with '"+s+"' Did you forget to include cssquery?");
    return [];
  },

  /** Add a completed calculation */
  addCalc:function(calc){
    RUZEE.Borders.calcs.push(calc);
  },

  renderCalcs:function(){
    for(var i=0; i<RUZEE.Borders.calcs.length; ++i){
      RUZEE.Borders.calcs[i]();
    }
    RUZEE.Borders.calcs=[];
  }
};

/** The Border class */
RUZEE.Borders.Border.prototype={

  /** Set the background image for element e to position x,y */
  setBgImg:function(e,x,y){
    if(!this.imgBgInURL) return;
    e.style.backgroundImage=this.imgBgInURL;
    x=-x;y=-y;
    e.style.backgroundPosition=x+'px '+y+'px';
    if(this.imgBgInRepeat) e.style.backgroundRepeat=this.imgBgInRepeat;
  },

  /** Create a DIV with width w, height h, background color bg, overflow o */
  crDiv:function(w,h,bg,o){
    var d=RUZEE.isXHTML
      ?document.createElementNS('http://www.w3.org/1999/xhtml','div')
      :document.createElement('div');
    d.style.padding=d.style.margin='0px';
    d.style.display='block';
    d.style.border='none';
    d.style.width=w?w:'auto';
    if(h) { d.style.height=h; d.style.fontSize=h; }
    if(!bg) bg='transparent';
    d.style.background=bg;
    if(o) d.style.overflow=o;
    return d;
  },

  /** Create wrapper DIV around element c */
  addLR:function(c,co,w,h,bgx,bgy){
    var e=this.crDiv(null,h,co);
    if(typeof bgx!='undefined') this.setBgImg(e,bgx,bgy);
    if(!w) w='1px';
    c.style.margin='0px '+(this.isR?w:'0px')+' 0px '+(this.isL?w:'0px');
    e.appendChild(c);
    return e;
  },

  /** Create the top (top==true) or bottom (top==false) of the border */
  crTB:function(top){
    var ca=RUZEE.Borders.cache[this.cacheID+'.'+top];
    if(ca){
      if(top){
        this.psT=ca.ps;
        this.inSh=ca.inSh;
      }else{
        this.psB=ca.ps;
      }
      return ca.el.cloneNode(true);
    }
    var sh=top?-this.shadowShift:this.shadowShift;
    var cxc=this.shadowPadding-this.cornerRadius-1;
    var cxb=cxc;
    var cxe=cxc+this.cornerRadius;
    var exb=0;
    var exe=cxc-1;
    var syc=this.cornerRadius-this.shadowPadding+sh+1;
    var yb,ye;
    if(top){
      if(!this.isT){
        this.psT=0;
        return;
      }
      yb=syc+this.shadowRadius-1;
      ye=syc-1;
      yi=-1;
      this.inSh=syc-1;
      this.psT=yb-ye;
    }else{
      if(!this.isB) {
        this.psB=0;
        return;
      }
      yb=syc<0?syc:0;
      ye=syc+this.shadowRadius;
      yi=1;
      this.psB=ye-yb;
    }
    var cwb=this.wBorder;
    if(cwb==0) cwb=1;

    var e=this.crDiv(null, Math.abs(yb-ye)+'px',null,'hidden');
    for(var y=yb; y!=ye; y+=yi){
      var co;
      if(y<=this.cornerRadius-cwb){
        co=this.coBgIn;
      }else if(y<=this.cornerRadius){
        co=this.coBorder;
      }else if(y-syc<0){
        co=this.coShadow;
      }else{
        co=rzBlend(this.coShadow,this.coBgOut,(y-syc)/this.shadowRadius);
      }
      var line=this.crDiv(null,'1px',rzC2S(co),'hidden');
      var fstLine=line;
      var xbg=null;
      for(var x=0; x<this.shadowRadius; ++x){
        var isIn=false, setBgImg=false;
        var sd, out=0;
        if(y<syc){
          sd=x;
        }else{
          sd=Math.sqrt(Math.sqr(x)+Math.sqr(y-syc));
        }
        if(this.shadowRadius>this.cornerRadius && sd<=this.shadowRadius){
          co=rzBlend(this.coShadow, this.coBgOut, sd/this.shadowRadius);
        }else{
          co=this.coBgOut;
          out++;
        }
        if(y<=this.cornerRadius){
          if(x>=exb && x<=exe){
            if(y>this.cornerRadius-cwb){
              co=this.coBorder;
            }else{
              isIn=true;
            }
          }else if(x>=cxb && x<=cxe){
            var cd=Math.sqrt(Math.sqr(x-cxc)+Math.sqr(y))-this.cornerRadius;
            if(y<0){
              if(x-cxc>this.cornerRadius-this.wBorder){
                co=this.coBorder;
              }else{ 
                isIn=true;
              }
            }else if(cd<-cwb){
              isIn=true;
            }else if(cd<-cwb+1){
              // first on border! do bgimg
              if(top&&this.imgBgInURL){
                setBgImg=true;
              }else
                co=rzBlend(this.coBgIn,this.coBorder,cd+cwb);
            }else if(cd<0){
              co=this.coBorder;
            }else if(cd<=1){
              co=rzBlend(this.coBorder,co,cd);
            }else{
              out++;
            }
          }
        }else{
          out++;
        }
        if(!isIn&&line==fstLine&&y<=this.cornerRadius-cwb&&top){
          this.setBgImg(fstLine,this.shadowRadius-x,yb-y);
        }
        if(out>1){
          line=this.addLR(line,'transparent',(this.shadowRadius-x)+'px');
          x=this.shadowRadius; // done
        }else{
          if(!isIn){
            // fix a strange IE bug where the 12ths recursion seems to get lost...
            if(RUZEE.isIE&&x==this.shadowRadius-12) line=this.addLR(line);
            line=this.addLR(line,rzC2S(co));
          }
          if(setBgImg) this.setBgImg(line,this.shadowRadius-x,yb-y+1);
        }
      }
      e.appendChild(line);
    }
    var ce={ el:e, ps:top?this.psT:this.psB };
    if(top) ce.inSh=this.inSh;
    RUZEE.Borders.cache[this.cacheID+'.'+top]=ce;
    return e;
  },

  /** Create the left and right of the border */
  crLR:function(e){
    var coBgInS=rzC2S(this.coBgIn);
    var coBS=rzC2S(this.coBorder);
    if(this.wBorder>0) e=this.addLR(e,coBS,this.wBorder+'px');
    for(var x=this.shadowPadding; x<this.shadowRadius; ++x){
      coS=rzC2S(rzBlend(this.coShadow,this.coBgOut,x/this.shadowRadius));
      e=this.addLR(e,coS);
    }
    return e;
  },

  setEdges:function(ed){
    ed=ed?ed.toLowerCase():'lrtb';
    this.isL=ed.indexOf('l')>=0;
    this.isR=ed.indexOf('r')>=0;
    this.isT=ed.indexOf('t')>=0;
    this.isB=ed.indexOf('b')>=0;
  },

  /** Calculate the border around e */
  calc:function(e){
    RUZEE.isXHTML=typeof window.RUZEE.isXHTML != 'undefined'
      ?window.RUZEE.isXHTML
      :(/html\:/.test(document.getElementsByTagName('body')[0].nodeName));

    if(!e) return;
    if(e.constructor==Array){
      for(var i=0; i<e.length; ++i) this.calc(e[i]);
      return;
    }
    this.inSh=0;

    // Get the bg image
    this.imgBgInURL=rzGetStyle(e,'background-image',false,null);
    if(this.imgBgInURL&&this.imgBgInURL=='none') this.imgBgInURL=null;
    if(this.imgBgInURL){
      this.imgBgInRepeat=rzGetStyle(e,'background-repeat',false,null);
    }
    this.coBgIn=rzS2C(rzGetStyle(e,'background-color'),'#ffffff');
    this.coBgOut=rzS2C(rzGetStyle(e.parentNode,'background-color'),'#ffffff');
    var borderCSS='border-'+(this.isT?'top-':'bottom-');
    var bs=rzGetStyle(e,borderCSS+'style',false,'none');
    if(bs && bs!='' && bs!='none' && bs!='hidden'){
      this.coBorder=rzS2C(rzGetStyle(e,borderCSS+'color',false,'black'));
      this.wBorder=rzPX2I(rzGetStyle(e,borderCSS+'width',false,'1px'));
    }else{
      this.coBorder=this.coBgIn;
      this.wBorder=0;
    }
    this.coShadow=this.coShadowS=='.fade'?this.coBorder:rzS2C(this.coShadowS);

    this.cacheID=
      rzC2S(this.coBgIn)+'.'+rzC2S(this.coBgOut)+'.'+
      rzC2S(this.coBorder)+'.'+rzC2S(this.coShadow)+'.'+
      this.wBorder+'.'+this.isL+this.isR+this.isT+this.isB+'.'+
      this.cornerRadius+'.'+this.shadowRadius+'.'+
      this.shadowPadding+'.'+this.shadowShift+'.'+
      this.imgBgInURL+'.'+this.imgBgInRepeat;

    var wr=this.crDiv();
    var cwr=this.crDiv();

    this.psT=0;
    this.psB=0;
    if(this.isT) wr.appendChild(this.crTB(true));
    wr.appendChild(this.crLR(cwr));
    if(this.isB) wr.appendChild(this.crTB(false));
    var psLR=this.shadowRadius-this.shadowPadding+this.wBorder;
    var psL=this.isL?psLR:0;
    var psR=this.isR?psLR:0;
    var isTB=this.isT&&this.isB;
    if(!isTB)this.inSh=0;
    var psT=isTB?Math.floor((this.psT+this.psB+this.inSh)/2):this.psT+Math.floor(this.inSh/2);
    var psB=this.psB+this.psT+this.inSh-psT;

    var cwrbg=cwr;
    // lift the inner div up if necessary
    if(this.inSh!=0){
      var up1=this.crDiv(); cwr.appendChild(up1);
      var up2=this.crDiv(); up1.appendChild(up2);
      cwr.style.position=up1.style.position='relative';
      up1.style.top=up2.style.marginBottom=this.inSh+'px';
      if(RUZEE.isIE) cwr.style.height='1%';
      cwrbg=up1; cwr=up2;
    }

    this.setBgImg(cwrbg,psL,this.psT+this.inSh);
    cwrbg.style.backgroundColor=rzC2S(this.coBgIn);

    if(RUZEE.isIE){
      e.style.height=cwr.style.height='1%'; // fix IE 3px jog when floated
    }else{
      // work around for other browsers for sebs problem
      var end=this.crDiv(null,'1px');
      end.style.marginBottom='-1px';
      e.appendChild(end);
      cwr.appendChild(end.cloneNode(true));
    }
    if(this.height>0) cwr.style.height=(RUZEE.isStrict?this.height:(this.height-this.psB-this.psT))+'px';

    var funcs=[
      rzUpdatePad(e,wr,cwr,'top',psT),
      rzUpdatePad(e,wr,cwr,'bottom',psB),
      rzUpdatePad(e,wr,cwr,'left',psL),
      rzUpdatePad(e,wr,cwr,'right',psR)];

    RUZEE.Borders.addCalc(function(){
      for(var i=0; i<funcs.length; ++i) funcs[i]();
      e.style.background='transparent';
      e.style.backgroundImage='none';
      e.appendChild(wr);
      while (e.childNodes.length>1){
        cwr.appendChild(e.removeChild(e.childNodes[0]));
      }
    });
  },

  /** Render the border around e */
  render:function(e){
    this.calc(e);
    RUZEE.Borders.renderCalcs();
  },

  // DEPRECATED STUFF - WILL BE REMOVED IN ONE OF THE NEXT RELEASES!
  draw:function(e,edges){
    this.setEdges(edges?edges.toLowerCase():'lrtb');
    if(typeof e=='string'){
      if(e.charAt(0)!='.') e='#'+e;
      e=RUZEE.Borders.cssQuery(e);
    }
    this.render(e);
  }
}; // of Border prototype

// add an event handler for render() if RUZEE.Events are available
if(RUZEE.Events){
  RUZEE.Events.add(window,'domload',function(){
    if(RUZEE.Borders.autoRender) RUZEE.Borders.render();
  });
}

// internal tools

Math.sqr=function(x){
  return x*x;
};

function rzCC(s){
  for(var exp=/-([a-z])/; exp.test(s); s=s.replace(exp,RegExp.$1.toUpperCase()));
  return s;
};

function rzGetStyle(e,a,transOk,d){
  if(e==null) return d;
  if(typeof e=='string') e=document.getElementById(e);
  var v=null;
  if(document.defaultView){
    var cs=document.defaultView.getComputedStyle(e,null);
    if (!cs && window.getComputedStyle) cs=window.getComputedStyle(e,null);
    if(cs){
      v=cs.getPropertyValue(a);
      if(!v && cs.getPropertyCSSValue){
        v=cs.getPropertyCSSValue(a);
        if(v) v=v.getStringValue();
      }
    }
  }

  if(!v && e.currentStyle){
    v=e.currentStyle[rzCC(a)];
    if (!v) v=e.currentStyle[a];
  }

  if(!v && e.style) v=e.style[rzCC(a)];
  // KHTML bug fix: transparent is #000000 - if you want black, use #010101 in your CSS.
  // Safari work around: transparent is 'rgba(0, 0, 0, 0)'
  if(!transOk && v && (v.toLowerCase()=='transparent' || v=='#000000' || v=='rgba(0, 0, 0, 0)')) v=null;
  return v?v:d?d:e==document.body?d:rzGetStyle(e.parentNode,a);
};

function rzPX2I(px){
  if(!px) return 0;
  var p=/\s*(\d\d*)px/.exec(px);
  if(p) return parseInt(p[1]);
  return 0;
};

  /** Update the padding of s depending of the setting of d and subtract subPx */
function rzUpdatePad(org,newo,newi,l,subPx,isSet){
  var padL='padding-'+l; var padCC=rzCC(padL);
  var marL='margin-'+l; var marCC=rzCC(marL);
  var borL='border-'+l+'-width'; var borCC=rzCC(borL);

  var pad=rzGetStyle(org,padL);
  var bor=rzGetStyle(org,borL);
  var r=rzPX2I(pad)+rzPX2I(bor);
  var v=r-subPx;
  v=(v<0?0:v)+'px';

  if(RUZEE.isStrict){
    newo.style[marCC]=(-r)+'px';
    newi.style[padCC]=v;
    return function(){
      org.style[borCC]='0px';
      org.style[padCC]=r+'px';
    };
  }else{
    newi.style[padCC]=v;
    return function(){
      org.style[borCC]=org.style[padCC]='0px';
    };
  }
};

function rzS2C(s,d){
    if (!s) return d?rzS2C(d):[0,0,0,0];
    if (s.charAt(0)=='#') s=s.substr(1,6);
    s=s.replace(/ /g,'').toLowerCase();
    // The CSS 2.1 colors
    var COLORS = {
         aqua:'00ffff', black:'000000', blue:'0000ff', fuchsia:'ff00ff',
         gray:'808080', green:'008000', lime:'00ff00', maroon:'800000',
         navy:'000080', olive:'808000', orange:'ffa500', purple:'800080',
         red:'ff0000', silver:'c0c0c0', teal:'008080', white:'ffffff',
         yellow:'ffff00'
    };
    for (var key in COLORS) if (s==key) s=COLORS[key];

    var p=/^rgba\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/.exec(s);
    if(p) return [parseInt(p[1]),parseInt(p[2]),parseInt(p[3]),parseInt(p[4])];
    var p=/^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/.exec(s);
    if(p) return [parseInt(p[1]),parseInt(p[2]),parseInt(p[3]),255];
    p=/^(\w{2})(\w{2})(\w{2})$/.exec(s);
    if(p) return [parseInt(p[1],16),parseInt(p[2],16),parseInt(p[3],16),255];
    p=/^(\w{1})(\w{1})(\w{1})$/.exec(s);
    if(p) return [parseInt(p[1]+p[1],16),parseInt(p[2]+p[2],16),parseInt(p[3]+p[3],16),255];
    return d?rzS2C(d):[0,0,0,0];
};

function rzC2S(c){
  if(typeof c=='string') return c;
  r='0'+c[0].toString(16);
  g='0'+c[1].toString(16);
  b='0'+c[2].toString(16);
  return '#'
    +r.substring(r.length-2)
    +g.substring(g.length-2)
    +b.substring(b.length-2);
};

function rzBlend(a,b,w){
  return Array(
    Math.round(a[0]+(b[0]-a[0])*w),
    Math.round(a[1]+(b[1]-a[1])*w),
    Math.round(a[2]+(b[2]-a[2])*w),
    Math.round(a[3]+(b[3]-a[3])*w));
};

// DEPRECATED STUFF - WILL BE REMOVED IN ONE OF THE NEXT RELEASES!
function rzCrSimpleBorder(rad){
  return new RUZEE.Borders.Border({ borderType:'simple', cornerRadius:rad });
};

function rzCrShadowBorder(rad,smar,coShadowS){
  return new RUZEE.Borders.Border({
    borderType:'shadow', cornerRadius:rad, shadowWidth:smar, shadowColor:coShadowS });
};

function rzCrFadeBorder(rad){
  return new RUZEE.Borders.Border({ borderType:'fade', cornerRadius:rad });
};

function rzCrGlowBorder(rad,gmar,coGlowS){
  return new RUZEE.Borders.Border({ borderType:'glow', cornerRadius:rad, glowWidth:gmar, glowColor:coGlowS });
};

function rzGetElementsByClass(c,n,t) {
  return RUZEE.getElementsByClass(c,t);
};




//addNewRating.js
//
function deactivateButton(buttonElementId) {
	var element = document.getElementById(buttonElementId); 
	element.disabled = true;
}

function activateButton(buttonElementId) {
	var element = document.getElementById(buttonElementId); 
	element.disabled = false;
}

function showRatingBox(ratingBoxId) {
	var element = document.getElementById(ratingBoxId); 
	element.className = "rbox_visible";	
}

function hideRatingBoxImpl(ratingBoxId) {
	var element = document.getElementById(ratingBoxId); 
	element.className = "rbox_invisible";	
}

function showWaitMessage(messageDivId) {
	var messageDiv = document.getElementById(messageDivId);
	if( messageDiv ) {
		messageDiv.innerHTML = 'Please wait while the rating is being stored';
	} 
}

function hideRatingBox( ratingBoxId, messageDivId ) {
	hideRatingBoxImpl( ratingBoxId );
	showWaitMessage( messageDivId );
} 



//autocomplete.js
//
/* This file contains javascript code for autocomplete input-box implementation */
function autocomplete_init(elementId, useRequestFeature, objectType) {
	// initialize the combobox widget, and pass in the callback function.
	var config = new Object();
	config["useRequestFeature"] = useRequestFeature;
	config["objectType"] = objectType;
	
	var cb = new ComboBox(elementId, ajaxCallback, config);
	document.getElementById(elementId).setAttribute("autocomplete", "off");
}

function ajaxResponseHadler(request, combobox) {
	if (request.readyState == 4 && request.status == 200) {
	
       	var items = request.responseText.split("\n");
     
     	// first item in response - is item exist or not
     	// check it - if item is not exists - set flag "displayRequestAdd"
     	if (items.length > 0) {
     		combobox.setItemExists(items[0]);
     		items.shift();
     	}
     	
       	combobox.setItems(items);
    }
}

/** Method for getting suggested values for entered text via Ajax
*/
function ajaxCallback(value, combobox) {
	if (value != "") {
		var ajaxURL = "../NameSuggestion?value=" + escape(value);
		if (typeof combobox.getConfigParam("objectType") != "undefined") {
			ajaxURL += "&type=" + escape(combobox.getConfigParam("objectType"));
		}
		
		var request = createXMLHttpRequest();
		request.onreadystatechange = function() { ajaxResponseHadler(request, combobox); };
		request.open("GET", ajaxURL, true);
		request.send(null);
	} else {
		return new Array();
	}
}



//calendar1.js
Calendar = function (firstDayOfWeek, dateStr, onSelected, onClose) {
	// member variables
	this.activeDiv = null;
	this.currentDateEl = null;
	this.getDateStatus = null;
	this.timeout = null;
	this.onSelected = onSelected || null;
	this.onClose = onClose || null;
	this.dragging = false;
	this.hidden = false;
	this.minYear = 1970;
	this.maxYear = 2050;
	this.dateFormat = Calendar._TT["DEF_DATE_FORMAT"];
	this.ttDateFormat = Calendar._TT["TT_DATE_FORMAT"];
	this.isPopup = true;
	this.weekNumbers = true;
	this.firstDayOfWeek = firstDayOfWeek; // 0 for Sunday, 1 for Monday, etc.
	this.showsOtherMonths = false;
	this.dateStr = dateStr;
	this.ar_days = null;
	this.showsTime = false;
	this.time24 = true;
	this.yearStep = 2;
	// HTML elements
	this.table = null;
	this.element = null;
	this.tbody = null;
	this.firstdayname = null;
	// Combo boxes
	this.monthsCombo = null;
	this.yearsCombo = null;
	this.hilitedMonth = null;
	this.activeMonth = null;
	this.hilitedYear = null;
	this.activeYear = null;
	// Information
	this.dateClicked = false;

	// one-time initializations
	if (typeof Calendar._SDN == "undefined") {
		// table of short day names
		if (typeof Calendar._SDN_len == "undefined")
			Calendar._SDN_len = 3;
		var ar = new Array();
		for (var i = 8; i > 0;) {
			ar[--i] = Calendar._DN[i].substr(0, Calendar._SDN_len);
		}
		Calendar._SDN = ar;
		// table of short month names
		if (typeof Calendar._SMN_len == "undefined")
			Calendar._SMN_len = 3;
		ar = new Array();
		for (var i = 12; i > 0;) {
			ar[--i] = Calendar._MN[i].substr(0, Calendar._SMN_len);
		}
		Calendar._SMN = ar;
	}
};

// ** constants

/// "static", needed for event handlers.
Calendar._C = null;

/// detect a special case of "web browser"
Calendar.is_ie = ( /msie/i.test(navigator.userAgent) &&
		   !/opera/i.test(navigator.userAgent) );

Calendar.is_ie5 = ( Calendar.is_ie && /msie 5\.0/i.test(navigator.userAgent) );

/// detect Opera browser
Calendar.is_opera = /opera/i.test(navigator.userAgent);

/// detect KHTML-based browsers
Calendar.is_khtml = /Konqueror|Safari|KHTML/i.test(navigator.userAgent);

// BEGIN: UTILITY FUNCTIONS; beware that these might be moved into a separate
//        library, at some point.

Calendar.getAbsolutePos = function(el) {
	var SL = 0, ST = 0;
	var is_div = /^div$/i.test(el.tagName);
	if (is_div && el.scrollLeft)
		SL = el.scrollLeft;
	if (is_div && el.scrollTop)
		ST = el.scrollTop;
	var r = { x: el.offsetLeft - SL, y: el.offsetTop - ST };
	if (el.offsetParent) {
		var tmp = this.getAbsolutePos(el.offsetParent);
		r.x += tmp.x;
		r.y += tmp.y;
	}
	return r;
};

Calendar.isRelated = function (el, evt) {
	var related = evt.relatedTarget;
	if (!related) {
		var type = evt.type;
		if (type == "mouseover") {
			related = evt.fromElement;
		} else if (type == "mouseout") {
			related = evt.toElement;
		}
	}
	while (related) {
		if (related == el) {
			return true;
		}
		related = related.parentNode;
	}
	return false;
};

Calendar.removeClass = function(el, className) {
	if (!(el && el.className)) {
		return;
	}
	var cls = el.className.split(" ");
	var ar = new Array();
	for (var i = cls.length; i > 0;) {
		if (cls[--i] != className) {
			ar[ar.length] = cls[i];
		}
	}
	el.className = ar.join(" ");
};

Calendar.addClass = function(el, className) {
	Calendar.removeClass(el, className);
	el.className += " " + className;
};

Calendar.getElement = function(ev) {
	if (Calendar.is_ie) {
		return window.event.srcElement;
	} else {
		return ev.currentTarget;
	}
};

Calendar.getTargetElement = function(ev) {
	if (Calendar.is_ie) {
		return window.event.srcElement;
	} else {
		return ev.target;
	}
};

Calendar.stopEvent = function(ev) {
	ev || (ev = window.event);
	if (Calendar.is_ie) {
		ev.cancelBubble = true;
		ev.returnValue = false;
	} else {
		ev.preventDefault();
		ev.stopPropagation();
	}
	return false;
};

Calendar.addEvent = function(el, evname, func) {
	if (el.attachEvent) { // IE
		el.attachEvent("on" + evname, func);
	} else if (el.addEventListener) { // Gecko / W3C
		el.addEventListener(evname, func, true);
	} else {
		el["on" + evname] = func;
	}
};

Calendar.removeEvent = function(el, evname, func) {
	if (el.detachEvent) { // IE
		el.detachEvent("on" + evname, func);
	} else if (el.removeEventListener) { // Gecko / W3C
		el.removeEventListener(evname, func, true);
	} else {
		el["on" + evname] = null;
	}
};

Calendar.createElement = function(type, parent) {
	var el = null;
	if (document.createElementNS) {
		// use the XHTML namespace; IE won't normally get here unless
		// _they_ "fix" the DOM2 implementation.
		el = document.createElementNS("http://www.w3.org/1999/xhtml", type);
	} else {
		el = document.createElement(type);
	}
	if (typeof parent != "undefined") {
		parent.appendChild(el);
	}
	return el;
};

// END: UTILITY FUNCTIONS

// BEGIN: CALENDAR STATIC FUNCTIONS

/** Internal -- adds a set of events to make some element behave like a button. */
Calendar._add_evs = function(el) {
	with (Calendar) {
		addEvent(el, "mouseover", dayMouseOver);
		addEvent(el, "mousedown", dayMouseDown);
		addEvent(el, "mouseout", dayMouseOut);
		if (is_ie) {
			addEvent(el, "dblclick", dayMouseDblClick);
			el.setAttribute("unselectable", true);
		}
	}
};

Calendar.findMonth = function(el) {
	if (typeof el.month != "undefined") {
		return el;
	} else if (typeof el.parentNode.month != "undefined") {
		return el.parentNode;
	}
	return null;
};

Calendar.findYear = function(el) {
	if (typeof el.year != "undefined") {
		return el;
	} else if (typeof el.parentNode.year != "undefined") {
		return el.parentNode;
	}
	return null;
};

Calendar.showMonthsCombo = function () {
	var cal = Calendar._C;
	if (!cal) {
		return false;
	}
	var cal = cal;
	var cd = cal.activeDiv;
	var mc = cal.monthsCombo;
	if (cal.hilitedMonth) {
		Calendar.removeClass(cal.hilitedMonth, "hilite");
	}
	if (cal.activeMonth) {
		Calendar.removeClass(cal.activeMonth, "active");
	}
	var mon = cal.monthsCombo.getElementsByTagName("div")[cal.date.getMonth()];
	Calendar.addClass(mon, "active");
	cal.activeMonth = mon;
	var s = mc.style;
	s.display = "block";
	if (cd.navtype < 0)
		s.left = cd.offsetLeft + "px";
	else {
		var mcw = mc.offsetWidth;
		if (typeof mcw == "undefined")
			// Konqueror brain-dead techniques
			mcw = 50;
		s.left = (cd.offsetLeft + cd.offsetWidth - mcw) + "px";
	}
	s.top = (cd.offsetTop + cd.offsetHeight) + "px";
};

Calendar.showYearsCombo = function (fwd) {
	var cal = Calendar._C;
	if (!cal) {
		return false;
	}
	var cal = cal;
	var cd = cal.activeDiv;
	var yc = cal.yearsCombo;
	if (cal.hilitedYear) {
		Calendar.removeClass(cal.hilitedYear, "hilite");
	}
	if (cal.activeYear) {
		Calendar.removeClass(cal.activeYear, "active");
	}
	cal.activeYear = null;
	var Y = cal.date.getFullYear() + (fwd ? 1 : -1);
	var yr = yc.firstChild;
	var show = false;
	for (var i = 12; i > 0; --i) {
		if (Y >= cal.minYear && Y <= cal.maxYear) {
			yr.firstChild.data = Y;
			yr.year = Y;
			yr.style.display = "block";
			show = true;
		} else {
			yr.style.display = "none";
		}
		yr = yr.nextSibling;
		Y += fwd ? cal.yearStep : -cal.yearStep;
	}
	if (show) {
		var s = yc.style;
		s.display = "block";
		if (cd.navtype < 0)
			s.left = cd.offsetLeft + "px";
		else {
			var ycw = yc.offsetWidth;
			if (typeof ycw == "undefined")
				// Konqueror brain-dead techniques
				ycw = 50;
			s.left = (cd.offsetLeft + cd.offsetWidth - ycw) + "px";
		}
		s.top = (cd.offsetTop + cd.offsetHeight) + "px";
	}
};

// event handlers

Calendar.tableMouseUp = function(ev) {
	var cal = Calendar._C;
	if (!cal) {
		return false;
	}
	if (cal.timeout) {
		clearTimeout(cal.timeout);
	}
	var el = cal.activeDiv;
	if (!el) {
		return false;
	}
	var target = Calendar.getTargetElement(ev);
	ev || (ev = window.event);
	Calendar.removeClass(el, "active");
	if (target == el || target.parentNode == el) {
		Calendar.cellClick(el, ev);
	}
	var mon = Calendar.findMonth(target);
	var date = null;
	if (mon) {
		date = new Date(cal.date);
		if (mon.month != date.getMonth()) {
			date.setMonth(mon.month);
			cal.setDate(date);
			cal.dateClicked = false;
			cal.callHandler();
		}
	} else {
		var year = Calendar.findYear(target);
		if (year) {
			date = new Date(cal.date);
			if (year.year != date.getFullYear()) {
				date.setFullYear(year.year);
				cal.setDate(date);
				cal.dateClicked = false;
				cal.callHandler();
			}
		}
	}
	with (Calendar) {
		removeEvent(document, "mouseup", tableMouseUp);
		removeEvent(document, "mouseover", tableMouseOver);
		removeEvent(document, "mousemove", tableMouseOver);
		cal._hideCombos();
		_C = null;
		return stopEvent(ev);
	}
};

Calendar.tableMouseOver = function (ev) {
	var cal = Calendar._C;
	if (!cal) {
		return;
	}
	var el = cal.activeDiv;
	var target = Calendar.getTargetElement(ev);
	if (target == el || target.parentNode == el) {
		Calendar.addClass(el, "hilite active");
		Calendar.addClass(el.parentNode, "rowhilite");
	} else {
		if (typeof el.navtype == "undefined" || (el.navtype != 50 && (el.navtype == 0 || Math.abs(el.navtype) > 2)))
			Calendar.removeClass(el, "active");
		Calendar.removeClass(el, "hilite");
		Calendar.removeClass(el.parentNode, "rowhilite");
	}
	ev || (ev = window.event);
	if (el.navtype == 50 && target != el) {
		var pos = Calendar.getAbsolutePos(el);
		var w = el.offsetWidth;
		var x = ev.clientX;
		var dx;
		var decrease = true;
		if (x > pos.x + w) {
			dx = x - pos.x - w;
			decrease = false;
		} else
			dx = pos.x - x;

		if (dx < 0) dx = 0;
		var range = el._range;
		var current = el._current;
		var count = Math.floor(dx / 10) % range.length;
		for (var i = range.length; --i >= 0;)
			if (range[i] == current)
				break;
		while (count-- > 0)
			if (decrease) {
				if (--i < 0)
					i = range.length - 1;
			} else if ( ++i >= range.length )
				i = 0;
		var newval = range[i];
		el.firstChild.data = newval;

		cal.onUpdateTime();
	}
	var mon = Calendar.findMonth(target);
	if (mon) {
		if (mon.month != cal.date.getMonth()) {
			if (cal.hilitedMonth) {
				Calendar.removeClass(cal.hilitedMonth, "hilite");
			}
			Calendar.addClass(mon, "hilite");
			cal.hilitedMonth = mon;
		} else if (cal.hilitedMonth) {
			Calendar.removeClass(cal.hilitedMonth, "hilite");
		}
	} else {
		if (cal.hilitedMonth) {
			Calendar.removeClass(cal.hilitedMonth, "hilite");
		}
		var year = Calendar.findYear(target);
		if (year) {
			if (year.year != cal.date.getFullYear()) {
				if (cal.hilitedYear) {
					Calendar.removeClass(cal.hilitedYear, "hilite");
				}
				Calendar.addClass(year, "hilite");
				cal.hilitedYear = year;
			} else if (cal.hilitedYear) {
				Calendar.removeClass(cal.hilitedYear, "hilite");
			}
		} else if (cal.hilitedYear) {
			Calendar.removeClass(cal.hilitedYear, "hilite");
		}
	}
	return Calendar.stopEvent(ev);
};

Calendar.tableMouseDown = function (ev) {
	if (Calendar.getTargetElement(ev) == Calendar.getElement(ev)) {
		return Calendar.stopEvent(ev);
	}
};

Calendar.calDragIt = function (ev) {
	var cal = Calendar._C;
	if (!(cal && cal.dragging)) {
		return false;
	}
	var posX;
	var posY;
	if (Calendar.is_ie) {
		posY = window.event.clientY + document.body.scrollTop;
		posX = window.event.clientX + document.body.scrollLeft;
	} else {
		posX = ev.pageX;
		posY = ev.pageY;
	}
	cal.hideShowCovered();
	var st = cal.element.style;
	st.left = (posX - cal.xOffs) + "px";
	st.top = (posY - cal.yOffs) + "px";
	return Calendar.stopEvent(ev);
};

Calendar.calDragEnd = function (ev) {
	var cal = Calendar._C;
	if (!cal) {
		return false;
	}
	cal.dragging = false;
	with (Calendar) {
		removeEvent(document, "mousemove", calDragIt);
		removeEvent(document, "mouseup", calDragEnd);
		tableMouseUp(ev);
	}
	cal.hideShowCovered();
};

Calendar.dayMouseDown = function(ev) {
	var el = Calendar.getElement(ev);
	if (el.disabled) {
		return false;
	}
	var cal = el.calendar;
	cal.activeDiv = el;
	Calendar._C = cal;
	if (el.navtype != 300) with (Calendar) {
		if (el.navtype == 50) {
			el._current = el.firstChild.data;
			addEvent(document, "mousemove", tableMouseOver);
		} else
			addEvent(document, Calendar.is_ie5 ? "mousemove" : "mouseover", tableMouseOver);
		addClass(el, "hilite active");
		addEvent(document, "mouseup", tableMouseUp);
	} else if (cal.isPopup) {
		cal._dragStart(ev);
	}
	if (el.navtype == -1 || el.navtype == 1) {
		if (cal.timeout) clearTimeout(cal.timeout);
		cal.timeout = setTimeout("Calendar.showMonthsCombo()", 250);
	} else if (el.navtype == -2 || el.navtype == 2) {
		if (cal.timeout) clearTimeout(cal.timeout);
		cal.timeout = setTimeout((el.navtype > 0) ? "Calendar.showYearsCombo(true)" : "Calendar.showYearsCombo(false)", 250);
	} else {
		cal.timeout = null;
	}
	return Calendar.stopEvent(ev);
};

Calendar.dayMouseDblClick = function(ev) {
	Calendar.cellClick(Calendar.getElement(ev), ev || window.event);
	if (Calendar.is_ie) {
		document.selection.empty();
	}
};

Calendar.dayMouseOver = function(ev) {
	var el = Calendar.getElement(ev);
	if (Calendar.isRelated(el, ev) || Calendar._C || el.disabled) {
		return false;
	}
	if (el.ttip) {
		if (el.ttip.substr(0, 1) == "_") {
			el.ttip = el.caldate.print(el.calendar.ttDateFormat) + el.ttip.substr(1);
		}
		el.calendar.tooltips.firstChild.data = el.ttip;
	}
	if (el.navtype != 300) {
		Calendar.addClass(el, "hilite");
		if (el.caldate) {
			Calendar.addClass(el.parentNode, "rowhilite");
		}
	}
	return Calendar.stopEvent(ev);
};

Calendar.dayMouseOut = function(ev) {
	with (Calendar) {
		var el = getElement(ev);
		if (isRelated(el, ev) || _C || el.disabled) {
			return false;
		}
		removeClass(el, "hilite");
		if (el.caldate) {
			removeClass(el.parentNode, "rowhilite");
		}
		el.calendar.tooltips.firstChild.data = _TT["SEL_DATE"];
		return stopEvent(ev);
	}
};

/**
 *  A generic "click" handler :) handles all types of buttons defined in this
 *  calendar.
 */
Calendar.cellClick = function(el, ev) {
	var cal = el.calendar;
	var closing = false;
	var newdate = false;
	var date = null;
	if (typeof el.navtype == "undefined") {
		Calendar.removeClass(cal.currentDateEl, "selected");
		Calendar.addClass(el, "selected");
		closing = (cal.currentDateEl == el);
		if (!closing) {
			cal.currentDateEl = el;
		}
		cal.date = new Date(el.caldate);
		date = cal.date;
		newdate = true;
		// a date was clicked
		if (!(cal.dateClicked = !el.otherMonth))
			cal._init(cal.firstDayOfWeek, date);
	} else {
		if (el.navtype == 200) {
			Calendar.removeClass(el, "hilite");
			cal.callCloseHandler();
			return;
		}
		date = (el.navtype == 0) ? new Date() : new Date(cal.date);
		// unless "today" was clicked, we assume no date was clicked so
		// the selected handler will know not to close the calenar when
		// in single-click mode.
		// cal.dateClicked = (el.navtype == 0);
		cal.dateClicked = false;
		var year = date.getFullYear();
		var mon = date.getMonth();
		function setMonth(m) {
			var day = date.getDate();
			var max = date.getMonthDays(m);
			if (day > max) {
				date.setDate(max);
			}
			date.setMonth(m);
		};
		switch (el.navtype) {
		    case 400:
			Calendar.removeClass(el, "hilite");
			var text = Calendar._TT["ABOUT"];
			if (typeof text != "undefined") {
				text += cal.showsTime ? Calendar._TT["ABOUT_TIME"] : "";
			} else {
				// FIXME: this should be removed as soon as lang files get updated!
				text = "Help and about box text is not translated into this language.\n" +
					"If you know this language and you feel generous please update\n" +
					"the corresponding file in \"lang\" subdir to match calendar-en.js\n" +
					"and send it back to <mishoo@infoiasi.ro> to get it into the distribution  ;-)\n\n" +
					"Thank you!\n" +
					"http://dynarch.com/mishoo/calendar.epl\n";
			}
			alert(text);
			return;
		    case -2:
			if (year > cal.minYear) {
				date.setFullYear(year - 1);
			}
			break;
		    case -1:
			if (mon > 0) {
				setMonth(mon - 1);
			} else if (year-- > cal.minYear) {
				date.setFullYear(year);
				setMonth(11);
			}
			break;
		    case 1:
			if (mon < 11) {
				setMonth(mon + 1);
			} else if (year < cal.maxYear) {
				date.setFullYear(year + 1);
				setMonth(0);
			}
			break;
		    case 2:
			if (year < cal.maxYear) {
				date.setFullYear(year + 1);
			}
			break;
		    case 100:
			cal.setFirstDayOfWeek(el.fdow);
			return;
		    case 50:
			var range = el._range;
			var current = el.firstChild.data;
			for (var i = range.length; --i >= 0;)
				if (range[i] == current)
					break;
			if (ev && ev.shiftKey) {
				if (--i < 0)
					i = range.length - 1;
			} else if ( ++i >= range.length )
				i = 0;
			var newval = range[i];
			el.firstChild.data = newval;
			cal.onUpdateTime();
			return;
		    case 0:
			// TODAY will bring us here
			if ((typeof cal.getDateStatus == "function") && cal.getDateStatus(date, date.getFullYear(), date.getMonth(), date.getDate())) {
				// remember, "date" was previously set to new
				// Date() if TODAY was clicked; thus, it
				// contains today date.
				return false;
			}
			break;
		}
		if (!date.equalsTo(cal.date)) {
			cal.setDate(date);
			newdate = true;
		}
	}
	if (newdate) {
		cal.callHandler();
	}
	if (closing) {
		Calendar.removeClass(el, "hilite");
		cal.callCloseHandler();
	}
};

// END: CALENDAR STATIC FUNCTIONS

// BEGIN: CALENDAR OBJECT FUNCTIONS

/**
 *  This function creates the calendar inside the given parent.  If _par is
 *  null than it creates a popup calendar inside the BODY element.  If _par is
 *  an element, be it BODY, then it creates a non-popup calendar (still
 *  hidden).  Some properties need to be set before calling this function.
 */
Calendar.prototype.create = function (_par) {
	var parent = null;
	if (! _par) {
		// default parent is the document body, in which case we create
		// a popup calendar.
		parent = document.getElementsByTagName("body")[0];
		this.isPopup = true;
	} else {
		parent = _par;
		this.isPopup = false;
	}
	this.date = this.dateStr ? new Date(this.dateStr) : new Date();

	var table = Calendar.createElement("table");
	this.table = table;
	table.cellSpacing = 0;
	table.cellPadding = 0;
	table.calendar = this;
	Calendar.addEvent(table, "mousedown", Calendar.tableMouseDown);

	var div = Calendar.createElement("div");
	this.element = div;
	div.className = "calendar";
	if (this.isPopup) {
		div.style.position = "absolute";
		div.style.display = "none";
	}
	div.appendChild(table);

	var thead = Calendar.createElement("thead", table);
	var cell = null;
	var row = null;

	var cal = this;
	var hh = function (text, cs, navtype) {
		cell = Calendar.createElement("td", row);
		cell.colSpan = cs;
		cell.className = "button";
		if (navtype != 0 && Math.abs(navtype) <= 2)
			cell.className += " nav";
		Calendar._add_evs(cell);
		cell.calendar = cal;
		cell.navtype = navtype;
		if (text.substr(0, 1) != "&") {
			cell.appendChild(document.createTextNode(text));
		}
		else {
			// FIXME: dirty hack for entities
			cell.innerHTML = text;
		}
		return cell;
	};

	row = Calendar.createElement("tr", thead);
	var title_length = 6;
	(this.isPopup) && --title_length;
	(this.weekNumbers) && ++title_length;

	hh("?", 1, 400).ttip = Calendar._TT["INFO"];
	this.title = hh("", title_length, 300);
	this.title.className = "title";
	if (this.isPopup) {
		this.title.ttip = Calendar._TT["DRAG_TO_MOVE"];
		this.title.style.cursor = "move";
		hh("&#x00d7;", 1, 200).ttip = Calendar._TT["CLOSE"];
	}

	row = Calendar.createElement("tr", thead);
	row.className = "headrow";

	this._nav_py = hh("&#x00ab;", 1, -2);
	this._nav_py.ttip = Calendar._TT["PREV_YEAR"];

	this._nav_pm = hh("&#x2039;", 1, -1);
	this._nav_pm.ttip = Calendar._TT["PREV_MONTH"];

	this._nav_now = hh(Calendar._TT["TODAY"], this.weekNumbers ? 4 : 3, 0);
	this._nav_now.ttip = Calendar._TT["GO_TODAY"];

	this._nav_nm = hh("&#x203a;", 1, 1);
	this._nav_nm.ttip = Calendar._TT["NEXT_MONTH"];

	this._nav_ny = hh("&#x00bb;", 1, 2);
	this._nav_ny.ttip = Calendar._TT["NEXT_YEAR"];

	// day names
	row = Calendar.createElement("tr", thead);
	row.className = "daynames";
	if (this.weekNumbers) {
		cell = Calendar.createElement("td", row);
		cell.className = "name wn";
		cell.appendChild(document.createTextNode(Calendar._TT["WK"]));
	}
	for (var i = 7; i > 0; --i) {
		cell = Calendar.createElement("td", row);
		cell.appendChild(document.createTextNode(""));
		if (!i) {
			cell.navtype = 100;
			cell.calendar = this;
			Calendar._add_evs(cell);
		}
	}
	this.firstdayname = (this.weekNumbers) ? row.firstChild.nextSibling : row.firstChild;
	this._displayWeekdays();

	var tbody = Calendar.createElement("tbody", table);
	this.tbody = tbody;

	for (i = 6; i > 0; --i) {
		row = Calendar.createElement("tr", tbody);
		if (this.weekNumbers) {
			cell = Calendar.createElement("td", row);
			cell.appendChild(document.createTextNode(""));
		}
		for (var j = 7; j > 0; --j) {
			cell = Calendar.createElement("td", row);
			cell.appendChild(document.createTextNode(""));
			cell.calendar = this;
			Calendar._add_evs(cell);
		}
	}

	if (this.showsTime) {
		row = Calendar.createElement("tr", tbody);
		row.className = "time";

		cell = Calendar.createElement("td", row);
		cell.className = "time";
		cell.colSpan = 2;
		cell.innerHTML = Calendar._TT["TIME"] || "&nbsp;";

		cell = Calendar.createElement("td", row);
		cell.className = "time";
		cell.colSpan = this.weekNumbers ? 4 : 3;

		(function(){
			function makeTimePart(className, init, range_start, range_end) {
				var part = Calendar.createElement("span", cell);
				part.className = className;
				part.appendChild(document.createTextNode(init));
				part.calendar = cal;
				part.ttip = Calendar._TT["TIME_PART"];
				part.navtype = 50;
				part._range = [];
				if (typeof range_start != "number")
					part._range = range_start;
				else {
					for (var i = range_start; i <= range_end; ++i) {
						var txt;
						if (i < 10 && range_end >= 10) txt = '0' + i;
						else txt = '' + i;
						part._range[part._range.length] = txt;
					}
				}
				Calendar._add_evs(part);
				return part;
			};
			var hrs = cal.date.getHours();
			var mins = cal.date.getMinutes();
			var t12 = !cal.time24;
			var pm = (hrs > 12);
			if (t12 && pm) hrs -= 12;
			var H = makeTimePart("hour", hrs, t12 ? 1 : 0, t12 ? 12 : 23);
			var span = Calendar.createElement("span", cell);
			span.appendChild(document.createTextNode(":"));
			span.className = "colon";
			var M = makeTimePart("minute", mins, 0, 59);
			var AP = null;
			cell = Calendar.createElement("td", row);
			cell.className = "time";
			cell.colSpan = 2;
			if (t12)
				AP = makeTimePart("ampm", pm ? "pm" : "am", ["am", "pm"]);
			else
				cell.innerHTML = "&nbsp;";

			cal.onSetTime = function() {
				var hrs = this.date.getHours();
				var mins = this.date.getMinutes();
				var pm = (hrs > 12);
				if (pm && t12) hrs -= 12;
				H.firstChild.data = (hrs < 10) ? ("0" + hrs) : hrs;
				M.firstChild.data = (mins < 10) ? ("0" + mins) : mins;
				if (t12)
					AP.firstChild.data = pm ? "pm" : "am";
			};

			cal.onUpdateTime = function() {
				var date = this.date;
				var h = parseInt(H.firstChild.data, 10);
				if (t12) {
					if (/pm/i.test(AP.firstChild.data) && h < 12)
						h += 12;
					else if (/am/i.test(AP.firstChild.data) && h == 12)
						h = 0;
				}
				var d = date.getDate();
				var m = date.getMonth();
				var y = date.getFullYear();
				date.setHours(h);
				date.setMinutes(parseInt(M.firstChild.data, 10));
				date.setFullYear(y);
				date.setMonth(m);
				date.setDate(d);
				this.dateClicked = false;
				this.callHandler();
			};
		})();
	} else {
		this.onSetTime = this.onUpdateTime = function() {};
	}

	var tfoot = Calendar.createElement("tfoot", table);

	row = Calendar.createElement("tr", tfoot);
	row.className = "footrow";

	cell = hh(Calendar._TT["SEL_DATE"], this.weekNumbers ? 8 : 7, 300);
	cell.className = "ttip";
	if (this.isPopup) {
		cell.ttip = Calendar._TT["DRAG_TO_MOVE"];
		cell.style.cursor = "move";
	}
	this.tooltips = cell;

	div = Calendar.createElement("div", this.element);
	this.monthsCombo = div;
	div.className = "combo";
	for (i = 0; i < Calendar._MN.length; ++i) {
		var mn = Calendar.createElement("div");
		mn.className = Calendar.is_ie ? "label-IEfix" : "label";
		mn.month = i;
		mn.appendChild(document.createTextNode(Calendar._SMN[i]));
		div.appendChild(mn);
	}

	div = Calendar.createElement("div", this.element);
	this.yearsCombo = div;
	div.className = "combo";
	for (i = 12; i > 0; --i) {
		var yr = Calendar.createElement("div");
		yr.className = Calendar.is_ie ? "label-IEfix" : "label";
		yr.appendChild(document.createTextNode(""));
		div.appendChild(yr);
	}

	this._init(this.firstDayOfWeek, this.date);
	parent.appendChild(this.element);
};

/** keyboard navigation, only for popup calendars */
Calendar._keyEvent = function(ev) {
	if (!window.calendar) {
		return false;
	}
	(Calendar.is_ie) && (ev = window.event);
	var cal = window.calendar;
	var act = (Calendar.is_ie || ev.type == "keypress");
	if (ev.ctrlKey) {
		switch (ev.keyCode) {
		    case 37: // KEY left
			act && Calendar.cellClick(cal._nav_pm);
			break;
		    case 38: // KEY up
			act && Calendar.cellClick(cal._nav_py);
			break;
		    case 39: // KEY right
			act && Calendar.cellClick(cal._nav_nm);
			break;
		    case 40: // KEY down
			act && Calendar.cellClick(cal._nav_ny);
			break;
		    default:
			return false;
		}
	} else switch (ev.keyCode) {
	    case 32: // KEY space (now)
		Calendar.cellClick(cal._nav_now);
		break;
	    case 27: // KEY esc
		act && cal.callCloseHandler();
		break;
	    case 37: // KEY left
	    case 38: // KEY up
	    case 39: // KEY right
	    case 40: // KEY down
		if (act) {
			var date = cal.date.getDate() - 1;
			var el = cal.currentDateEl;
			var ne = null;
			var prev = (ev.keyCode == 37) || (ev.keyCode == 38);
			switch (ev.keyCode) {
			    case 37: // KEY left
				(--date >= 0) && (ne = cal.ar_days[date]);
				break;
			    case 38: // KEY up
				date -= 7;
				(date >= 0) && (ne = cal.ar_days[date]);
				break;
			    case 39: // KEY right
				(++date < cal.ar_days.length) && (ne = cal.ar_days[date]);
				break;
			    case 40: // KEY down
				date += 7;
				(date < cal.ar_days.length) && (ne = cal.ar_days[date]);
				break;
			}
			if (!ne) {
				if (prev) {
					Calendar.cellClick(cal._nav_pm);
				} else {
					Calendar.cellClick(cal._nav_nm);
				}
				date = (prev) ? cal.date.getMonthDays() : 1;
				el = cal.currentDateEl;
				ne = cal.ar_days[date - 1];
			}
			Calendar.removeClass(el, "selected");
			Calendar.addClass(ne, "selected");
			cal.date = new Date(ne.caldate);
			cal.callHandler();
			cal.currentDateEl = ne;
		}
		break;
	    case 13: // KEY enter
		if (act) {
			cal.callHandler();
			cal.hide();
		}
		break;
	    default:
		return false;
	}
	return Calendar.stopEvent(ev);
};

/**
 *  (RE)Initializes the calendar to the given date and firstDayOfWeek
 */
Calendar.prototype._init = function (firstDayOfWeek, date) {
	var today = new Date();
	this.table.style.visibility = "hidden";
	var year = date.getFullYear();
	if (year < this.minYear) {
		year = this.minYear;
		date.setFullYear(year);
	} else if (year > this.maxYear) {
		year = this.maxYear;
		date.setFullYear(year);
	}
	this.firstDayOfWeek = firstDayOfWeek;
	this.date = new Date(date);
	var month = date.getMonth();
	var mday = date.getDate();
	var no_days = date.getMonthDays();

	// calendar voodoo for computing the first day that would actually be
	// displayed in the calendar, even if it's from the previous month.
	// WARNING: this is magic. ;-)
	date.setDate(1);
	var day1 = (date.getDay() - this.firstDayOfWeek) % 7;
	if (day1 < 0)
		day1 += 7;
	date.setDate(-day1);
	date.setDate(date.getDate() + 1);

	var row = this.tbody.firstChild;
	var MN = Calendar._SMN[month];
	var ar_days = new Array();
	var weekend = Calendar._TT["WEEKEND"];
	for (var i = 0; i < 6; ++i, row = row.nextSibling) {
		var cell = row.firstChild;
		if (this.weekNumbers) {
			cell.className = "day wn";
			cell.firstChild.data = date.getWeekNumber();
			cell = cell.nextSibling;
		}
		row.className = "daysrow";
		var hasdays = false;
		for (var j = 0; j < 7; ++j, cell = cell.nextSibling, date.setDate(date.getDate() + 1)) {
			var iday = date.getDate();
			var wday = date.getDay();
			cell.className = "day";
			var current_month = (date.getMonth() == month);
			if (!current_month) {
				if (this.showsOtherMonths) {
					cell.className += " othermonth";
					cell.otherMonth = true;
				} else {
					cell.className = "emptycell";
					cell.innerHTML = "&nbsp;";
					cell.disabled = true;
					continue;
				}
			} else {
				cell.otherMonth = false;
				hasdays = true;
			}
			cell.disabled = false;
			cell.firstChild.data = iday;
			if (typeof this.getDateStatus == "function") {
				var status = this.getDateStatus(date, year, month, iday);
				if (status === true) {
					cell.className += " disabled";
					cell.disabled = true;
				} else {
					if (/disabled/i.test(status))
						cell.disabled = true;
					cell.className += " " + status;
				}
			}
			if (!cell.disabled) {
				ar_days[ar_days.length] = cell;
				cell.caldate = new Date(date);
				cell.ttip = "_";
				if (current_month && iday == mday) {
					cell.className += " selected";
					this.currentDateEl = cell;
				}
				if (date.getFullYear() == today.getFullYear() &&
				    date.getMonth() == today.getMonth() &&
				    iday == today.getDate()) {
					cell.className += " today";
					cell.ttip += Calendar._TT["PART_TODAY"];
				}
				if (weekend.indexOf(wday.toString()) != -1) {
					cell.className += cell.otherMonth ? " oweekend" : " weekend";
				}
			}
		}
		if (!(hasdays || this.showsOtherMonths))
			row.className = "emptyrow";
	}
	this.ar_days = ar_days;
	this.title.firstChild.data = Calendar._MN[month] + ", " + year;
	this.onSetTime();
	this.table.style.visibility = "visible";
	// PROFILE
	// this.tooltips.firstChild.data = "Generated in " + ((new Date()) - today) + " ms";
};

/**
 *  Calls _init function above for going to a certain date (but only if the
 *  date is different than the currently selected one).
 */
Calendar.prototype.setDate = function (date) {
	if (!date.equalsTo(this.date)) {
		this._init(this.firstDayOfWeek, date);
	}
};

/**
 *  Refreshes the calendar.  Useful if the "disabledHandler" function is
 *  dynamic, meaning that the list of disabled date can change at runtime.
 *  Just * call this function if you think that the list of disabled dates
 *  should * change.
 */
Calendar.prototype.refresh = function () {
	this._init(this.firstDayOfWeek, this.date);
};

/** Modifies the "firstDayOfWeek" parameter (pass 0 for Synday, 1 for Monday, etc.). */
Calendar.prototype.setFirstDayOfWeek = function (firstDayOfWeek) {
	this._init(firstDayOfWeek, this.date);
	this._displayWeekdays();
};

/**
 *  Allows customization of what dates are enabled.  The "unaryFunction"
 *  parameter must be a function object that receives the date (as a JS Date
 *  object) and returns a boolean value.  If the returned value is true then
 *  the passed date will be marked as disabled.
 */
Calendar.prototype.setDateStatusHandler = Calendar.prototype.setDisabledHandler = function (unaryFunction) {
	this.getDateStatus = unaryFunction;
};

/** Customization of allowed year range for the calendar. */
Calendar.prototype.setRange = function (a, z) {
	this.minYear = a;
	this.maxYear = z;
};

/** Calls the first user handler (selectedHandler). */
Calendar.prototype.callHandler = function () {
	if (this.onSelected) {
		this.onSelected(this, this.date.print(this.dateFormat));
	}
};

/** Calls the second user handler (closeHandler). */
Calendar.prototype.callCloseHandler = function () {
	if (this.onClose) {
		this.onClose(this);
	}
	this.hideShowCovered();
};

/** Removes the calendar object from the DOM tree and destroys it. */
Calendar.prototype.destroy = function () {
	var el = this.element.parentNode;
	el.removeChild(this.element);
	Calendar._C = null;
	window.calendar = null;
};

/**
 *  Moves the calendar element to a different section in the DOM tree (changes
 *  its parent).
 */
Calendar.prototype.reparent = function (new_parent) {
	var el = this.element;
	el.parentNode.removeChild(el);
	new_parent.appendChild(el);
};

// This gets called when the user presses a mouse button anywhere in the
// document, if the calendar is shown.  If the click was outside the open
// calendar this function closes it.
Calendar._checkCalendar = function(ev) {
	if (!window.calendar) {
		return false;
	}
	var el = Calendar.is_ie ? Calendar.getElement(ev) : Calendar.getTargetElement(ev);
	for (; el != null && el != calendar.element; el = el.parentNode);
	if (el == null) {
		// calls closeHandler which should hide the calendar.
		window.calendar.callCloseHandler();
		return Calendar.stopEvent(ev);
	}
};

/** Shows the calendar. */
Calendar.prototype.show = function () {
	var rows = this.table.getElementsByTagName("tr");
	for (var i = rows.length; i > 0;) {
		var row = rows[--i];
		Calendar.removeClass(row, "rowhilite");
		var cells = row.getElementsByTagName("td");
		for (var j = cells.length; j > 0;) {
			var cell = cells[--j];
			Calendar.removeClass(cell, "hilite");
			Calendar.removeClass(cell, "active");
		}
	}
	this.element.style.display = "block";
	this.hidden = false;
	if (this.isPopup) {
		window.calendar = this;
		Calendar.addEvent(document, "keydown", Calendar._keyEvent);
		Calendar.addEvent(document, "keypress", Calendar._keyEvent);
		Calendar.addEvent(document, "mousedown", Calendar._checkCalendar);
	}
	this.hideShowCovered();
};

/**
 *  Hides the calendar.  Also removes any "hilite" from the class of any TD
 *  element.
 */
Calendar.prototype.hide = function () {
	if (this.isPopup) {
		Calendar.removeEvent(document, "keydown", Calendar._keyEvent);
		Calendar.removeEvent(document, "keypress", Calendar._keyEvent);
		Calendar.removeEvent(document, "mousedown", Calendar._checkCalendar);
	}
	this.element.style.display = "none";
	this.hidden = true;
	this.hideShowCovered();
};

/**
 *  Shows the calendar at a given absolute position (beware that, depending on
 *  the calendar element style -- position property -- this might be relative
 *  to the parent's containing rectangle).
 */
Calendar.prototype.showAt = function (x, y) {
	var s = this.element.style;
	s.left = x + "px";
	s.top = y + "px";
	this.show();
};

/** Shows the calendar near a given element. */
Calendar.prototype.showAtElement = function (el, opts) {
	var self = this;
	var p = Calendar.getAbsolutePos(el);
	if (!opts || typeof opts != "string") {
		this.showAt(p.x, p.y + el.offsetHeight);
		return true;
	}
	function fixPosition(box) {
		if (box.x < 0)
			box.x = 0;
		if (box.y < 0)
			box.y = 0;
		var cp = document.createElement("div");
		var s = cp.style;
		s.position = "absolute";
		s.right = s.bottom = s.width = s.height = "0px";
		document.body.appendChild(cp);
		var br = Calendar.getAbsolutePos(cp);
		document.body.removeChild(cp);
		if (Calendar.is_ie) {
			br.y += document.body.scrollTop;
			br.x += document.body.scrollLeft;
		} else {
			br.y += window.scrollY;
			br.x += window.scrollX;
		}
		var tmp = box.x + box.width - br.x;
		if (tmp > 0) box.x -= tmp;
		tmp = box.y + box.height - br.y;
		if (tmp > 0) box.y -= tmp;
	};
	this.element.style.display = "block";
	Calendar.continuation_for_the_fucking_khtml_browser = function() {
		var w = self.element.offsetWidth;
		var h = self.element.offsetHeight;
		self.element.style.display = "none";
		var valign = opts.substr(0, 1);
		var halign = "l";
		if (opts.length > 1) {
			halign = opts.substr(1, 1);
		}
		// vertical alignment
		switch (valign) {
		    case "T": p.y -= h; break;
		    case "B": p.y += el.offsetHeight; break;
		    case "C": p.y += (el.offsetHeight - h) / 2; break;
		    case "t": p.y += el.offsetHeight - h; break;
		    case "b": break; // already there
		}
		// horizontal alignment
		switch (halign) {
		    case "L": p.x -= w; break;
		    case "R": p.x += el.offsetWidth; break;
		    case "C": p.x += (el.offsetWidth - w) / 2; break;
		    case "r": p.x += el.offsetWidth - w; break;
		    case "l": break; // already there
		}
		p.width = w;
		p.height = h + 40;
		self.monthsCombo.style.display = "none";
		fixPosition(p);
		self.showAt(p.x, p.y);
	};
	if (Calendar.is_khtml)
		setTimeout("Calendar.continuation_for_the_fucking_khtml_browser()", 10);
	else
		Calendar.continuation_for_the_fucking_khtml_browser();
};

/** Customizes the date format. */
Calendar.prototype.setDateFormat = function (str) {
	this.dateFormat = str;
};

/** Customizes the tooltip date format. */
Calendar.prototype.setTtDateFormat = function (str) {
	this.ttDateFormat = str;
};

/**
 *  Tries to identify the date represented in a string.  If successful it also
 *  calls this.setDate which moves the calendar to the given date.
 */
Calendar.prototype.parseDate = function (str, fmt) {
	var y = 0;
	var m = -1;
	var d = 0;
	var a = str.split(/\W+/);
	if (!fmt) {
		fmt = this.dateFormat;
	}
	var b = fmt.match(/%./g);
	var i = 0, j = 0;
	var hr = 0;
	var min = 0;
	for (i = 0; i < a.length; ++i) {
		if (!a[i])
			continue;
		switch (b[i]) {
		    case "%d":
		    case "%e":
			d = parseInt(a[i], 10);
			break;

		    case "%m":
			m = parseInt(a[i], 10) - 1;
			break;

		    case "%Y":
		    case "%y":
			y = parseInt(a[i], 10);
			(y < 100) && (y += (y > 29) ? 1900 : 2000);
			break;

		    case "%b":
		    case "%B":
			for (j = 0; j < 12; ++j) {
				if (Calendar._MN[j].substr(0, a[i].length).toLowerCase() == a[i].toLowerCase()) { m = j; break; }
			}
			break;

		    case "%H":
		    case "%I":
		    case "%k":
		    case "%l":
			hr = parseInt(a[i], 10);
			break;

		    case "%P":
		    case "%p":
			if (/pm/i.test(a[i]) && hr < 12)
				hr += 12;
			break;

		    case "%M":
			min = parseInt(a[i], 10);
			break;
		}
	}
	if (y != 0 && m != -1 && d != 0) {
		this.setDate(new Date(y, m, d, hr, min, 0));
		return;
	}
	y = 0; m = -1; d = 0;
	for (i = 0; i < a.length; ++i) {
		if (a[i].search(/[a-zA-Z]+/) != -1) {
			var t = -1;
			for (j = 0; j < 12; ++j) {
				if (Calendar._MN[j].substr(0, a[i].length).toLowerCase() == a[i].toLowerCase()) { t = j; break; }
			}
			if (t != -1) {
				if (m != -1) {
					d = m+1;
				}
				m = t;
			}
		} else if (parseInt(a[i], 10) <= 12 && m == -1) {
			m = a[i]-1;
		} else if (parseInt(a[i], 10) > 31 && y == 0) {
			y = parseInt(a[i], 10);
			(y < 100) && (y += (y > 29) ? 1900 : 2000);
		} else if (d == 0) {
			d = a[i];
		}
	}
	if (y == 0) {
		var today = new Date();
		y = today.getFullYear();
	}
	if (m != -1 && d != 0) {
		this.setDate(new Date(y, m, d, hr, min, 0));
	}
};

Calendar.prototype.hideShowCovered = function () {
	var self = this;
	Calendar.continuation_for_the_fucking_khtml_browser = function() {
		function getVisib(obj){
			var value = obj.style.visibility;
			if (!value) {
				if (document.defaultView && typeof (document.defaultView.getComputedStyle) == "function") { // Gecko, W3C
					if (!Calendar.is_khtml)
						value = document.defaultView.
							getComputedStyle(obj, "").getPropertyValue("visibility");
					else
						value = '';
				} else if (obj.currentStyle) { // IE
					value = obj.currentStyle.visibility;
				} else
					value = '';
			}
			return value;
		};

		var tags = new Array("applet", "iframe", "select");
		var el = self.element;

		var p = Calendar.getAbsolutePos(el);
		var EX1 = p.x;
		var EX2 = el.offsetWidth + EX1;
		var EY1 = p.y;
		var EY2 = el.offsetHeight + EY1;

		for (var k = tags.length; k > 0; ) {
			var ar = document.getElementsByTagName(tags[--k]);
			var cc = null;

			for (var i = ar.length; i > 0;) {
				cc = ar[--i];

				p = Calendar.getAbsolutePos(cc);
				var CX1 = p.x;
				var CX2 = cc.offsetWidth + CX1;
				var CY1 = p.y;
				var CY2 = cc.offsetHeight + CY1;

				if (self.hidden || (CX1 > EX2) || (CX2 < EX1) || (CY1 > EY2) || (CY2 < EY1)) {
					if (!cc.__msh_save_visibility) {
						cc.__msh_save_visibility = getVisib(cc);
					}
					cc.style.visibility = cc.__msh_save_visibility;
				} else {
					if (!cc.__msh_save_visibility) {
						cc.__msh_save_visibility = getVisib(cc);
					}
					cc.style.visibility = "hidden";
				}
			}
		}
	};
	if (Calendar.is_khtml)
		setTimeout("Calendar.continuation_for_the_fucking_khtml_browser()", 10);
	else
		Calendar.continuation_for_the_fucking_khtml_browser();
};

/** Internal function; it displays the bar with the names of the weekday. */
Calendar.prototype._displayWeekdays = function () {
	var fdow = this.firstDayOfWeek;
	var cell = this.firstdayname;
	var weekend = Calendar._TT["WEEKEND"];
	for (var i = 0; i < 7; ++i) {
		cell.className = "day name";
		var realday = (i + fdow) % 7;
		if (i) {
			cell.ttip = Calendar._TT["DAY_FIRST"].replace("%s", Calendar._DN[realday]);
			cell.navtype = 100;
			cell.calendar = this;
			cell.fdow = realday;
			Calendar._add_evs(cell);
		}
		if (weekend.indexOf(realday.toString()) != -1) {
			Calendar.addClass(cell, "weekend");
		}
		cell.firstChild.data = Calendar._SDN[(i + fdow) % 7];
		cell = cell.nextSibling;
	}
};

/** Internal function.  Hides all combo boxes that might be displayed. */
Calendar.prototype._hideCombos = function () {
	this.monthsCombo.style.display = "none";
	this.yearsCombo.style.display = "none";
};

/** Internal function.  Starts dragging the element. */
Calendar.prototype._dragStart = function (ev) {
	if (this.dragging) {
		return;
	}
	this.dragging = true;
	var posX;
	var posY;
	if (Calendar.is_ie) {
		posY = window.event.clientY + document.body.scrollTop;
		posX = window.event.clientX + document.body.scrollLeft;
	} else {
		posY = ev.clientY + window.scrollY;
		posX = ev.clientX + window.scrollX;
	}
	var st = this.element.style;
	this.xOffs = posX - parseInt(st.left);
	this.yOffs = posY - parseInt(st.top);
	with (Calendar) {
		addEvent(document, "mousemove", calDragIt);
		addEvent(document, "mouseup", calDragEnd);
	}
};

// BEGIN: DATE OBJECT PATCHES

/** Adds the number of days array to the Date object. */
Date._MD = new Array(31,28,31,30,31,30,31,31,30,31,30,31);

/** Constants used for time computations */
Date.SECOND = 1000 /* milliseconds */;
Date.MINUTE = 60 * Date.SECOND;
Date.HOUR   = 60 * Date.MINUTE;
Date.DAY    = 24 * Date.HOUR;
Date.WEEK   =  7 * Date.DAY;

/** Returns the number of days in the current month */
Date.prototype.getMonthDays = function(month) {
	var year = this.getFullYear();
	if (typeof month == "undefined") {
		month = this.getMonth();
	}
	if (((0 == (year%4)) && ( (0 != (year%100)) || (0 == (year%400)))) && month == 1) {
		return 29;
	} else {
		return Date._MD[month];
	}
};

/** Returns the number of day in the year. */
Date.prototype.getDayOfYear = function() {
	var now = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0);
	var then = new Date(this.getFullYear(), 0, 0, 0, 0, 0);
	var time = now - then;
	return Math.floor(time / Date.DAY);
};

/** Returns the number of the week in year, as defined in ISO 8601. */
Date.prototype.getWeekNumber = function() {
	var d = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0);
	var DoW = d.getDay();
	d.setDate(d.getDate() - (DoW + 6) % 7 + 3); // Nearest Thu
	var ms = d.valueOf(); // GMT
	d.setMonth(0);
	d.setDate(4); // Thu in Week 1
	return Math.round((ms - d.valueOf()) / (7 * 864e5)) + 1;
};

/** Checks dates equality (ignores time) */
Date.prototype.equalsTo = function(date) {
	return ((this.getFullYear() == date.getFullYear()) &&
		(this.getMonth() == date.getMonth()) &&
		(this.getDate() == date.getDate()) &&
		(this.getHours() == date.getHours()) &&
		(this.getMinutes() == date.getMinutes()));
};

/** Prints the date in a string according to the given format. */
Date.prototype.print = function (str) {
	var m = this.getMonth();
	var d = this.getDate();
	var y = this.getFullYear();
	var wn = this.getWeekNumber();
	var w = this.getDay();
	var s = {};
	var hr = this.getHours();
	var pm = (hr >= 12);
	var ir = (pm) ? (hr - 12) : hr;
	var dy = this.getDayOfYear();
	if (ir == 0)
		ir = 12;
	var min = this.getMinutes();
	var sec = this.getSeconds();
	s["%a"] = Calendar._SDN[w]; // abbreviated weekday name [FIXME: I18N]
	s["%A"] = Calendar._DN[w]; // full weekday name
	s["%b"] = Calendar._SMN[m]; // abbreviated month name [FIXME: I18N]
	s["%B"] = Calendar._MN[m]; // full month name
	// FIXME: %c : preferred date and time representation for the current locale
	s["%C"] = 1 + Math.floor(y / 100); // the century number
	s["%d"] = (d < 10) ? ("0" + d) : d; // the day of the month (range 01 to 31)
	s["%e"] = d; // the day of the month (range 1 to 31)
	// FIXME: %D : american date style: %m/%d/%y
	// FIXME: %E, %F, %G, %g, %h (man strftime)
	s["%H"] = (hr < 10) ? ("0" + hr) : hr; // hour, range 00 to 23 (24h format)
	s["%I"] = (ir < 10) ? ("0" + ir) : ir; // hour, range 01 to 12 (12h format)
	s["%j"] = (dy < 100) ? ((dy < 10) ? ("00" + dy) : ("0" + dy)) : dy; // day of the year (range 001 to 366)
	s["%k"] = hr;		// hour, range 0 to 23 (24h format)
	s["%l"] = ir;		// hour, range 1 to 12 (12h format)
	s["%m"] = (m < 9) ? ("0" + (1+m)) : (1+m); // month, range 01 to 12
	s["%M"] = (min < 10) ? ("0" + min) : min; // minute, range 00 to 59
	s["%n"] = "\n";		// a newline character
	s["%p"] = pm ? "PM" : "AM";
	s["%P"] = pm ? "pm" : "am";
	// FIXME: %r : the time in am/pm notation %I:%M:%S %p
	// FIXME: %R : the time in 24-hour notation %H:%M
	s["%s"] = Math.floor(this.getTime() / 1000);
	s["%S"] = (sec < 10) ? ("0" + sec) : sec; // seconds, range 00 to 59
	s["%t"] = "\t";		// a tab character
	// FIXME: %T : the time in 24-hour notation (%H:%M:%S)
	s["%U"] = s["%W"] = s["%V"] = (wn < 10) ? ("0" + wn) : wn;
	s["%u"] = w + 1;	// the day of the week (range 1 to 7, 1 = MON)
	s["%w"] = w;		// the day of the week (range 0 to 6, 0 = SUN)
	// FIXME: %x : preferred date representation for the current locale without the time
	// FIXME: %X : preferred time representation for the current locale without the date
	s["%y"] = ('' + y).substr(2, 2); // year without the century (range 00 to 99)
	s["%Y"] = y;		// year with the century
	s["%%"] = "%";		// a literal '%' character

	var re = /%./g;
	if (!Calendar.is_ie5)
		return str.replace(re, function (par) { return s[par] || par; });

	var a = str.match(re);
	for (var i = 0; i < a.length; i++) {
		var tmp = s[a[i]];
		if (tmp) {
			re = new RegExp(a[i], 'g');
			str = str.replace(re, tmp);
		}
	}

	return str;
};

Date.prototype.__msh_oldSetFullYear = Date.prototype.setFullYear;
Date.prototype.setFullYear = function(y) {
	var d = new Date(this);
	d.__msh_oldSetFullYear(y);
	if (d.getMonth() != this.getMonth())
		this.setDate(28);
	this.__msh_oldSetFullYear(y);
};

// END: DATE OBJECT PATCHES


// global object that remembers the calendar
window.calendar = null;


// ** I18N

// Calendar EN language
// Author: Mihai Bazon, <mishoo@infoiasi.ro>
// Encoding: any
// Distributed under the same terms as the calendar itself.

// For translators: please use UTF-8 if possible.  We strongly believe that
// Unicode is the answer to a real internationalized world.  Also please
// include your contact information in the header, as can be seen above.

// full day names
Calendar._DN = new Array
("Sunday",
 "Monday",
 "Tuesday",
 "Wednesday",
 "Thursday",
 "Friday",
 "Saturday",
 "Sunday");

// Please note that the following array of short day names (and the same goes
// for short month names, _SMN) isn't absolutely necessary.  We give it here
// for exemplification on how one can customize the short day names, but if
// they are simply the first N letters of the full name you can simply say:
//
//   Calendar._SDN_len = N; // short day name length
//   Calendar._SMN_len = N; // short month name length
//
// If N = 3 then this is not needed either since we assume a value of 3 if not
// present, to be compatible with translation files that were written before
// this feature.

Calendar._SDN = new Array("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun");
Calendar._MN = new Array("January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December");
Calendar._SMN = new Array("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");

// tooltips
Calendar._TT = {};
Calendar._TT["INFO"] = "About the calendar";

Calendar._TT["ABOUT"] =
"Date selection:\n" +
"- Use the \xab, \xbb buttons to select year\n" +
"- Use the " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " buttons to select month\n" +
"- Hold mouse button on any of the above buttons for faster selection.";
Calendar._TT["ABOUT_TIME"] = "\n\n" +
"Time selection:\n" +
"- Click on any of the time parts to increase it\n" +
"- or Shift-click to decrease it\n" +
"- or click and drag for faster selection.";

Calendar._TT["PREV_YEAR"] = "Prev. year (hold for menu)";
Calendar._TT["PREV_MONTH"] = "Prev. month (hold for menu)";
Calendar._TT["GO_TODAY"] = "Go Today";
Calendar._TT["NEXT_MONTH"] = "Next month (hold for menu)";
Calendar._TT["NEXT_YEAR"] = "Next year (hold for menu)";
Calendar._TT["SEL_DATE"] = "Select date";
Calendar._TT["DRAG_TO_MOVE"] = "Drag to move";
Calendar._TT["PART_TODAY"] = " (today)";

// the following is to inform that "%s" is to be the first day of week
// %s will be replaced with the day name.
Calendar._TT["DAY_FIRST"] = "Display %s first";

// This may be locale-dependent.  It specifies the week-end days, as an array
// of comma-separated numbers.  The numbers are from 0 to 6: 0 means Sunday, 1
// means Monday, etc.
Calendar._TT["WEEKEND"] = "0,6";

Calendar._TT["CLOSE"] = "Close";
Calendar._TT["TODAY"] = "Today";
Calendar._TT["TIME_PART"] = "(Shift-)Click or drag to change value";

// date formats
Calendar._TT["DEF_DATE_FORMAT"] = "%Y-%m-%d";
Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e";

Calendar._TT["WK"] = "wk";
Calendar._TT["TIME"] = "Time:";


Calendar.setup = function (params) {
	function param_default(pname, def) { if (typeof params[pname] == "undefined") { params[pname] = def; } };

	param_default("inputField",     null);
	param_default("displayArea",    null);
	param_default("button",         null);
	param_default("eventName",      "click");
	param_default("ifFormat",       "%Y/%m/%d");
	param_default("daFormat",       "%Y/%m/%d");
	param_default("singleClick",    true);
	param_default("disableFunc",    null);
	param_default("dateStatusFunc", params["disableFunc"]);	// takes precedence if both are defined
	param_default("firstDay",       0); // defaults to "Sunday" first
	param_default("align",          "Br");
	param_default("range",          [1900, 2999]);
	param_default("weekNumbers",    true);
	param_default("flat",           null);
	param_default("flatCallback",   null);
	param_default("onSelect",       null);
	param_default("onClose",        null);
	param_default("onUpdate",       null);
	param_default("date",           null);
	param_default("showsTime",      false);
	param_default("timeFormat",     "24");
	param_default("electric",       true);
	param_default("step",           2);
	param_default("position",       null);
	param_default("cache",          false);
	param_default("showOthers",     false);

	var tmp = ["inputField", "displayArea", "button"];
	for (var i in tmp) {
		if (typeof params[tmp[i]] == "string") {
			params[tmp[i]] = document.getElementById(params[tmp[i]]);
		}
	}
	if (!(params.flat || params.inputField || params.displayArea || params.button)) {
		alert("Calendar.setup:\n  Nothing to setup (no fields found).  Please check your code");
		return false;
	}

	function onSelect(cal) {
		var p = cal.params;
		var update = (cal.dateClicked || p.electric);
		if (update && p.flat) {
			if (typeof p.flatCallback == "function")
				p.flatCallback(cal);
			else
				alert("No flatCallback given -- doing nothing.");
			return false;
		}
		if (update && p.inputField) {
			p.inputField.value = cal.date.print(p.ifFormat);
			if (typeof p.inputField.onchange == "function")
				p.inputField.onchange();
		}
		if (update && p.displayArea)
			p.displayArea.innerHTML = cal.date.print(p.daFormat);
		if (update && p.singleClick && cal.dateClicked)
			cal.callCloseHandler();
		if (update && typeof p.onUpdate == "function")
			p.onUpdate(cal);
	};

	if (params.flat != null) {
		if (typeof params.flat == "string")
			params.flat = document.getElementById(params.flat);
		if (!params.flat) {
			alert("Calendar.setup:\n  Flat specified but can't find parent.");
			return false;
		}
		var cal = new Calendar(params.firstDay, params.date, params.onSelect || onSelect);
		cal.showsTime = params.showsTime;
		cal.time24 = (params.timeFormat == "24");
		cal.params = params;
		cal.weekNumbers = params.weekNumbers;
		cal.setRange(params.range[0], params.range[1]);
		cal.setDateStatusHandler(params.dateStatusFunc);
		cal.create(params.flat);
		cal.show();
		return false;
	}

	var triggerEl = params.button || params.displayArea || params.inputField;
	triggerEl["on" + params.eventName] = function() {
		var dateEl = params.inputField || params.displayArea;
		var dateFmt = params.inputField ? params.ifFormat : params.daFormat;
		var mustCreate = false;
		var cal = window.calendar;
		if (!(cal && params.cache)) {
			window.calendar = cal = new Calendar(params.firstDay,
							     params.date,
							     params.onSelect || onSelect,
							     params.onClose || function(cal) { cal.hide(); });
			cal.showsTime = params.showsTime;
			cal.time24 = (params.timeFormat == "24");
			cal.weekNumbers = params.weekNumbers;
			mustCreate = true;
		} else {
			if (params.date)
				cal.setDate(params.date);
			cal.hide();
		}
		cal.showsOtherMonths = params.showOthers;
		cal.yearStep = params.step;
		cal.setRange(params.range[0], params.range[1]);
		cal.params = params;
		cal.setDateStatusHandler(params.dateStatusFunc);
		cal.setDateFormat(dateFmt);
		if (mustCreate)
			cal.create();
		cal.parseDate(dateEl.value || dateEl.innerHTML);
		cal.refresh();
		if (!params.position)
			cal.showAtElement(params.button || params.displayArea || params.inputField, params.align);
		else
			cal.showAt(params.position[0], params.position[1]);
		return false;
	};
};



//combobox.js
//

if (typeof UTILITIES_VERSION == "undefined" || UTILITIES_VERSION < 0.1) {
	alert("A suitable version of the Utilities class is not available");
}

COMBOBOX_VERSION = 0.1;

/*
Sample CSS for the combobox
	.comboBoxList {
		padding: 0px;
		border: 1px solid #999;
		background-color: #f7f7ff;
		overflow: visible;
	}
	.comboBoxItem {
		margin: 0px;
		padding: 2px 5px;
		background-color: inherit;
		cursor: default;
	}
	.comboBoxSelectedItem {
		background-color: #ddf;
	}
*/

/**
 * I create a new combobox, using the specified text input field and
 * population callback.  The item list is styled with three CSS
 * classes: comboBoxList, comboBoxItem, and comboBoxSelectedItem, which
 * are for the containing DIV, the individial item DIVs, and for the
 * currently selected item DIV.  Note that the selected item has both
 * the item and selectedItem classes applied.  Sample CSS is available
 * in a comment at the top of the implementation file.
 * 
 * The 'config' argument allows passing of additional parameters that
 * further govern the behaviour of the combo box.  Supported parameters
 * are listed here:
 * 	allowMultipleValues - whether the form field should allow multiple
 *		values to be provided.  Each individual value will get it's own
 *		separate dropdown with, so a field value such as "dog,ca" would
 *		operate as if the value were just "ca" (i.e. just "ca" would be
 *		passed to the callback, and a selection choice would only
 *		replace the "ca").  Defaults to false.
 *	valueDelimiter - if allowMultipleValues is set to true, this is the
 *		character used to delimit the values.  Defaults to a comma.
 *
 * @param id The ID of the text field the combobox is based around.
 * @param callback The function to call when the typed value changes.
 *		The function will be passed the current value of the field, and
 *		must return an array of values to display in the dropdown.
 * @param config additional config parameters, as explained above.
 * @param config An object containing configuration parameters for the
 *		instance.
 */
function ComboBox(id, callback, config) {
	var self = this;
	// instance variables
	this.config = config || new Object();
	this.callback = callback;
	this.availableItems = new Array();
	this.selectedItemIndex = -1;
	this.id = id;
	this.field = document.getElementById(id);
	
	if (typeof this.field == "undefined")
		alert("You have specified an invalid id for the field you want to turn into a combo box");
	this.dropdown = document.createElement("div");
	this.isDropdownShowing = false;
	
	this.displayRequestAdd = false;
	
	// configure the dropdown div
	this.dropdown.className = "comboBoxList";
	// sometimes, parent of combobox is not completed, so, we will get here wronge coordinates
	// so, I moved this code to later place
	
	// initialize the field
	this.field.comboBox = this;
	this.field.oldValue = this.field.value;
	this.field.onkeyup = ComboBox.onKeyUp;
	this.field.moveCaretToEnd = function() {
		if (this.createTextRange) {
			var range = this.createTextRange();
			range.collapse(false);
			range.select();
		} else if (this.setSelectionRange) {
			this.focus();
			var length = this.value.length;
			this.setSelectionRange(length, length);
		}
	}
	this.field.form.oldonsubmit = this.field.form.onsubmit;
	this.field.onfocus = function() {
		this.form.onsubmit = function() {
			if (this.oldonsubmit) this.oldonsubmit();
			return ! self.isDropdownShowing;
		};
	}
	this.field.onblur = function() {
		var cb = this.comboBox;
		this.hideTimeout = setTimeout(function() { cb.hideDropdown(); }, 100);
		this.form.onsubmit = function() {
			if (this.oldonsubmit) this.oldonsubmit();
			return true;
		};
	}
	
	// privileged methods
	this.getConfigParam = function(name, defVal) {
		return self.config[name] || defVal;
	}
}



/**
 * I am the onKeyDown listener that gets installed on the input field
 * that is the core of the ComboBox.  I handle action operations.
 *
 * @param e The event object on Mozilla browsers, null on IE
 */
ComboBox.onKeyDown = function(e) {
	if (!e) e = window.event;
	var capture = function() {
		e.cancelBubble = true;
		if (e.stopPropagation) e.stopPropagation();
	}
	switch (e.keyCode) {
		case 13: // enter
		case 9: // tab
			this.comboBox.chooseSelection();
			capture();
			return false;
		case 27: // escape
			this.comboBox.hideDropdown();
			capture();
		case 38: // up arrow
			this.comboBox.selectPrevious();
			capture();
			break;
		case 40: // down arrow
			this.comboBox.selectNext();
			capture();
			break;
	}
}



/**
 * I am the onKeyUp listener that gets installed on the input field
 * that is the core of the ComboBox.  I handle value-change operations.
 *
 * @param e The event object on Mozilla browsers, null on IE
 */
ComboBox.onKeyUp = function(e) {
	if (!e) e = window.event;
	var capture = function() {
		e.cancelBubble = true;
		if (e.stopPropagation) e.stopPropagation();
	}
	switch (e.keyCode) {
		case 38: // up arrow
		case 40: // down arrow
			this.moveCaretToEnd();
			capture();
			break;
		default:
			if (this.value != this.oldValue) {
				this.comboBox.valueChanged();
				this.oldValue = this.value;
			}
			capture();
	}
}



/**
 * I am called by the onKeyUp listener when the entered value changes,
 * and am responsible for invoking the application callback function
 * and repopulating the dropdown, if appropriate.  
 */
ComboBox.prototype.valueChanged = function() {
	var value = this.field.value;
	if (this.getConfigParam("allowMultipleValues", false)) {
		value = value.split(this.getConfigParam("valueDelimiter", ","));
		value = value[value.length - 1].replace(/^ +/, "").replace(/ +$/, "");
	}
	var a = this.callback(value, this);
	if (typeof a == "undefined") // to catch null returns
		return;
	this.setItems(a);
}



/**
 * I can be called at any time with a new set of items to display in
 * the dropdown.
 *
 * @param items The array of items that should be used for the dropdown
 *		values.
 */
ComboBox.prototype.setItems = function(items) {
	if (typeof items != "object") {
		alert("setItems wasn't passed a valid array: " + typeof a);
		return;
	}
	this.availableItems = items;
	this.populateDropdown();
}



ComboBox.prototype.setItemExists = function(isItemExists) {
	if (this.getConfigParam("useRequestFeature", false)) {
		if (isItemExists == 0) {
			this.displayRequestAdd = true;
		} else {
			this.displayRequestAdd = false;
		}
	}
}

function requestAddNewBand(value) {
	if (value != "") {
		var ajaxURL = "../RequestAddNewBand?name=" + value; // value is escaped we don't need do it again
		var request = createXMLHttpRequest();
		request.open("GET", ajaxURL, true);
		request.send(null);
	}
}

/**
 * I am called to repopulate the dropdown.  There should never be a
 * need to invoke me externally.
 */
ComboBox.prototype.populateDropdown = function() {
	// moved here from constructor
	if (this.dropdown.style.position != 'absolute') {
		this.dropdown.style.position = 'absolute';
		var offsets = Utilities.getOffsets(this.field);
		this.dropdown.style.top = offsets.y + (this.field.offsetHeight ? this.field.offsetHeight : 22) + "px";
		this.dropdown.style.left = offsets.x + "px";
		this.dropdown.style.width = (this.field.offsetWidth ? this.field.offsetWidth : 100) + "px"

		document.body.appendChild(this.dropdown);
		this.hideDropdown();
	}
	
	if ((this.availableItems.length > 0 || this.displayRequestAdd) &&
			this.field.value != "") {
		Utilities.removeChildren(this.dropdown);
		var i = 0;
		for (i = 0; i < this.availableItems.length; i++) {
			var item = document.createElement("div");;			
			item.className = "comboBoxItem";
			// start parsing
			var fullName = this.availableItems[i];	// get name and type in one string, separated by BANDSEPARATOR		
			var BANDSEPARATOR = "<bvt>"; // declared in NameSuggestionServlet -> doGet(HttpServletRequest, HttpServletResponse)
			var simpleName = fullName.substr(0, fullName.indexOf(BANDSEPARATOR)); // get name of the object
            var bandOrVenue = fullName.substr(fullName.indexOf(BANDSEPARATOR)+BANDSEPARATOR.length); // get Object type            
            // every item in drop-down list is a table
			var tableWithData = '<table style=\"border:0px\" width=\"100%\"><tr><td width=\"70%\" align=\"left\" style=\"\">'+simpleName+'</td><td align=\"right\" style=\"color: #70AF00;\">'+bandOrVenue+'</td></tr></table>';
			//alert(tableWithData);
			// end parsing		
			item.innerHTML = tableWithData;

			item.id = "item_" + simpleName;
			
			// rewrite string with name and type by only name
			this.availableItems[i] = simpleName;

			item.comboBox = this;
			item.comboBoxIndex = i;
			item.onmouseover = function() {this.comboBox.select(this.comboBoxIndex);};
			// [AKA] onclick fired after onblur of parent element, so, with onclick it not always worked
			// I changed it to onmousedown
			item.onmousedown = function() {this.comboBox.choose(this.comboBoxIndex);};
			this.dropdown.appendChild(item);
		}
				
		// [AKA] Display link to request add new band
		if (this.displayRequestAdd) {
			if (i != 0) {
				var hr = document.createElement("hr");
				this.dropdown.appendChild(hr);
			}
			
			var item = document.createElement("div");
			item.className = "comboBoxItem";
			
			var innerHtml = "<a style='color: red;' href='../member/NewBandRequest.faces?newBandName=";
			innerHtml += escape(this.field.value);
			innerHtml += "'>Request To Add New Band</a>";
			item.innerHTML = innerHtml;
			item.comboBox = this;
			item.comboBoxIndex = i++;
			item.onmouseover = function() {this.comboBox.select(this.comboBoxIndex);};
			this.dropdown.appendChild(item);
		}

		// [AKA] Write some text for user
		var hr = document.createElement("hr");
		this.dropdown.appendChild(hr);
	
		var textItem = document.createElement("div");
		textItem.className = "comboBoxItem";
		textItem.innerHTML = "You can choose one of the matched name";
		
		if (this.displayRequestAdd) {
			textItem.innerHTML += "<br/>Or click on the link to request site administrator to add a new band into database";
		}
		
		textItem.comboBox = this;
		textItem.comboBoxIndex = i++;
		this.dropdown.appendChild(textItem);
			
		this.selectedItemIndex = 0;
		this.updateSelection();
		this.showDropdown();
	} else {
		this.selectedItemIndex = -1;
		this.hideDropdown();
	}
}

/**
 * I am called by a mouse listener on the dropdown items to choose a
 * specific item straight away.
 *
 * @param index The index of the item to choose
 */
ComboBox.prototype.choose = function(index) {
	if (this.select(index))
		this.chooseSelection();
}



/**
 * I am called by the onKeyUp listener to indicate that the user wants
 * to use the current selection as the new value of the field.
 */
ComboBox.prototype.chooseSelection = function() {
	var i = this.selectedItemIndex;
	var a = this.availableItems;
	if (i >= 0 && i < a.length) {
		var valueToAdd = a[i].replace(/<[^>]+>/g, "");
		if (this.getConfigParam("allowMultipleValues", false)) {
			var currentValue = "";
			var delim = this.getConfigParam("valueDelimiter", ",");
			values = this.field.value.split(delim);
			for (var j = 0; j < values.length - 1; j++) {
				currentValue = Utilities.listAppend(currentValue, values[j], delim);
			}
			this.field.value = Utilities.listAppend(currentValue, valueToAdd, delim);
		} else {
			this.field.value = valueToAdd;
		}

		this.field.oldValue = this.field.value;
		this.field.focus();
		this.field.moveCaretToEnd();
		this.hideDropdown();
	}
}



/**
 * I am called by a mouse listener on the dropdown items to select a
 * specific item straight away.
 *
 * @param index The index of the item to select
 * @return whether the selection happened (the index was valid)
 */
ComboBox.prototype.select = function(index) {
	if (index < 0 || index >= this.availableItems.length)
		return false;
	this.selectedItemIndex = index;
	this.updateSelection();
	return true;
}



/**
 * I am called by the onKeyUp listener to indicate that the user wants
 * to select the next option in the dropdown.
 */
ComboBox.prototype.selectNext = function() {
	if (this.selectedItemIndex >= this.availableItems.length - 1)
		return false;
	this.selectedItemIndex++;
	this.updateSelection();
	return true;
}



/**
 * I am called by the onKeyUp listener to indicate that the user wants
 * to select the previous option in the dropdown.
 */
ComboBox.prototype.selectPrevious = function() {
	if (this.selectedItemIndex <= 0)
		return false;
	this.selectedItemIndex--;
	this.updateSelection();
}



/**
 * I show the dropdown DIV.
 */
ComboBox.prototype.showDropdown = function() {
	clearTimeout(this.field.hideTimeout);
	this.dropdown.style.display = 'block';
	this.field.onkeydown = ComboBox.onKeyDown;
	this.isDropdownShowing = true;
}



/**
 * I hide the dropdown DIV.
 */
ComboBox.prototype.hideDropdown = function() {
	var self = this;
	setTimeout(function() {self.isDropdownShowing = false;}, 100);
	this.field.onkeydown = null;
	this.dropdown.style.display = 'none';
}



/**
 * I update the dropdown so that the display reflects the internally
 * selected item,
 */
ComboBox.prototype.updateSelection = function() {
	for (var i = 0; i < this.dropdown.childNodes.length; i++) {
		if (i == this.selectedItemIndex) {
			this.dropdown.childNodes[i].className += " comboBoxSelectedItem";
		} else {
			this.dropdown.childNodes[i].className = this.dropdown.childNodes[i].className.replace(/ *comboBoxSelectedItem */g, "");
		}
	}
}



//gmap.js
//
// Author: AKO
// GMap/MapBuilder construction routines

// Careful: GMap must to be created in window.onLoad(),
// and also the previous onLoad() must to be called

// Global variables

var map = ""; // GMap itself

// mapbuilder stuff
var icontiny = "";
var iconbig = "";
var iconcustom = "";

function createGMap() {
    map = new GMap2(document.getElementById("LocationGMap"));
    
    map.addControl(new GSmallMapControl()); // new GLargeMapControl()            
    // map.addControl(new GMapTypeControl());          

    // Add locations
}

function createLocationMarker(point, locationName, locationAddress) {
    var infoHTML = "<div id=\"MapInfo\"><strong>" + locationName + "</strong><br/>" + locationAddress + "</div>";
  
    var marker = new GMarker(point);
    GEvent.addListener(marker, "click", function() {
        marker.openInfoWindowHtml(infoHTML);
    }); // balloon opener
    
	GEvent.addListener(marker, "infowindowclose", function() {
		window.setTimeout(function() {
		    map.panTo(point);
		}, 100); // return map to the location center on pan
    });    
        
    return marker;
}

function locateGMapTo(longitude, latitude, locationName, locationAddress) {

      var locationPoint = new GLatLng(latitude, longitude);
      
      map.setCenter(locationPoint, zoom); 

	  var mapType = map.getMapTypes()[0]; // G_NORMAL_MAP, G_SATELLITE_MAP, G_HYBRID_MAP

     // we must to set center before setting type 
      map.setMapType(mapType);
      
      var marker = createLocationMarker(locationPoint, locationName, locationAddress);
      map.addOverlay(marker);
}



function MainGMapLoad() {
   /* attributes longitude, latitude, zoom, locationName, locationAddress
   came from the covering function */
   createGMap();
   locateGMapTo(longitude, latitude, 
 	                       locationName, locationAddress);
};
  	      
// Main function to call in pages
function constructGMapAndPointTo(longitude, latitude, zoom, locationName, locationAddress) {

    // GMap must to be created in window.onLoad(),
    // and also the previous onLoad() must to be called

    // new onload handling is taken from 
    // http://www.brothercake.com/site/resources/scripts/onload/

	//setup onload function
	if(typeof window.addEventListener != 'undefined') {
		//.. gecko, safari, konqueror and standard
		window.addEventListener('load', MainGMapLoad, false);
	} else if(typeof document.addEventListener != 'undefined') {
		//.. opera 7
		document.addEventListener('load', MainGMapLoad, false);
	} else if(typeof window.attachEvent != 'undefined')	{
		//.. win/ie
		window.attachEvent('onload', MainGMapLoad);
	} else {
	    //** remove this condition to degrade older browsers 
	       
		//.. mac/ie5 and anything else that gets this far
		
		//if there's an existing onload function
		if (typeof window.onload == 'function') {
			//store it
			var existing = onload;
			//add new onload handler
			window.onload = function() {
				//call existing onload function
				existing();
				//call generic onload function
				MainGMapLoad();
			};
		} else {
			//setup onload function
			window.onload = MainGMapLoad;
		}
	}

}




//multifile.js
//
/**
 * Convert a single file-input element into a 'multiple' input list
 *
 * Usage:
 *
 *   1. Create a file input element (no name)
 *      eg. <input type="file" id="first_file_element">
 *
 *   2. Create a DIV for the output to be written to
 *      eg. <div id="files_list"></div>
 *
 *   3. Instantiate a MultiSelector object, passing in the DIV and an (optional) maximum number of files
 *      eg. var multi_selector = new MultiSelector( document.getElementById( 'files_list' ), 3 );
 *
 *   4. Add the first element
 *      eg. multi_selector.addElement( document.getElementById( 'first_file_element' ) );
 *
 *   5. That's it.
 *
 *   You might (will) want to play around with the addListRow() method to make the output prettier.
 *
 *   You might also want to change the line 
 *       element.name = 'file_' + this.count;
 *   ...to a naming convention that makes more sense to you.
 * 
 * Licence:
 *   Use this however/wherever you like, just don't blame me if it breaks anything.
 *
 * Credit:
 *   If you're nice, you'll leave this bit:
 *  
 *   Class by Stickman -- http://www.the-stickman.com
 *      with thanks to:
 *      [for Safari fixes]
 *         Luis Torrefranca -- http://www.law.pitt.edu
 *         and
 *         Shawn Parker & John Pennypacker -- http://www.fuzzycoconut.com
 *      [for duplicate name bug]
 *         'neal'
 */
function MultiSelector( list_target, max ){

	// Where to write the list
	this.list_target = list_target;
	// How many elements?
	this.count = 0;
	// How many elements?
	this.id = 0;
	// Is there a maximum?
	if( max ){
		this.max = max;
	} else {
		this.max = -1;
	};
	
	/**
	 * Add a new file input element
	 */
	this.addElement = function( element ){

		// Make sure it's a file input element
		if( element.tagName == 'INPUT' && element.type == 'file' ){

			// Element name -- what number am I?
			element.name = 'file_' + this.id++;

			// Add reference to this object
			element.multi_selector = this;

			// What to do when a file is selected
			element.onchange = function(){

				// New file input
				var new_element = document.createElement( 'input' );
				new_element.type = 'file';

				// Add new element
				this.parentNode.insertBefore( new_element, this );

				// Apply 'update' to element
				this.multi_selector.addElement( new_element );

				// Update list
				this.multi_selector.addListRow( this );

				// Hide this: we can't use display:none because Safari doesn't like it
				this.style.position = 'absolute';
				this.style.left = '-1000px';

			};
			// If we've reached maximum number, disable input element
			if( this.max != -1 && this.count >= this.max ){
				element.disabled = true;
			};

			// File element counter
			this.count++;
			// Most recent element
			this.current_element = element;
			
		} else {
			// This can only be applied to file input elements!
			alert( 'Error: not a file input element' );
		};

	};

	/**
	 * Add a new row to the list of files
	 */
	this.addListRow = function( element ) {

		// Row div
		var new_row = document.createElement( 'tr' );
		var new_col_filename = document.createElement( 'td' );
		var new_col_button = document.createElement( 'td' );
		
		new_col_filename.setAttribute( 'align', 'left');
		new_col_button.setAttribute( 'align', 'right');

		// Delete button
		var new_row_button = document.createElement( 'input' );
		new_row_button.type = 'button';
		new_row_button.value = 'Delete';

		new_row.appendChild( new_col_filename );
		new_row.appendChild( new_col_button );

		// References
		new_row.element = element;

		// Delete function
		new_row_button.onclick= function(){

			// Remove row from table
			this.parentNode.parentNode.parentNode.removeChild(this.parentNode.parentNode);

			// Remove element from form
			this.parentNode.parentNode.element.parentNode.removeChild( this.parentNode.parentNode.element );
			
			// Decrement counter
			this.parentNode.parentNode.element.multi_selector.count--;
			
			// Re-enable input element (if it's disabled)
			this.parentNode.parentNode.element.multi_selector.current_element.disabled = false;
			
			// Appease Safari
			// without it Safari wants to reload the browser window
			// which nixes your already queued uploads
			return false;
		};
		
		// Set row value
		// new_row.innerHTML = element.value;
		
		var c = element.value.lastIndexOf("\/"); //For Unix, etc.
		if (c != -1) {
			var elementValue = element.value.substring(c+1);
		} else {
			var c = element.value.lastIndexOf("\\"); //Win
			if (c != -1) {
				var elementValue = element.value.substring(c+1);
			} else {
				var elementValue = element.value;
			}
		}
		
		new_col_filename.innerHTML = elementValue;
		new_col_button.appendChild( new_row_button );

		// Add it to the list
		this.list_target.appendChild( new_row );
		
	};

};



//multiurls.js
//

function URLMultiSelector( list_target, errorSpan, max ){
	// Where to write the list
	this.list_target = list_target;
	
	this.errorSpan = errorSpan;
	
	// How many elements?
	this.count = 0;
	// How many elements?
	this.id = 0;
	// Is there a maximum?
	if( max ){
		this.max = max;
	} else {
		this.max = -1;
	};
	
	/**
	 * Add a new file input element
	 */
	this.addElement = function( nameElement, urlElement, buttonElement ){
		// Check GUI elements
		if( nameElement.tagName != 'INPUT' && nameElement.type != 'text' ){
			alert( 'Error: not a text input element for name' );
		}
		if( urlElement.tagName != 'INPUT' && urlElement.type != 'text' ){
			alert( 'Error: not a text input element for url' );
		}
		if( buttonElement.tagName != 'INPUT' && buttonElement.type != 'button' ){
			alert( 'Error: not a text input element for add button' );
		}
		
		// UI is OK, proceed...
		
		nameElement.name = "name_" + this.id;
		nameElement.id = "name_" + this.id;
		urlElement.id = "url_" + this.id;
		urlElement.name = "url_" + this.id++;
		buttonElement.multi_selector = this;
				
		buttonElement.onclick = function() {				
			// check for errors and get existing inputs
			var prevElementsId = this.multi_selector.id-1;
			var old_urlElement = document.getElementById("url_" + prevElementsId);
			var old_nameElement = document.getElementById("name_" + prevElementsId);
			
			if( old_urlElement.value == "" || old_nameElement.value == "" ) {
				this.multi_selector.errorSpan.innerHTML = 'Name and URL can not be empty';
				return;
			} else {
				this.multi_selector.errorSpan.innerHTML = ' ';
			}	
				
			// New inputs
			var new_nameElement = document.createElement( 'input' );
			new_nameElement.type = 'text';
			var new_urlElement = document.createElement( 'input' );
			new_urlElement.type = 'text';
			new_urlElement.size = "50";
			
			
			
			// Add new inputs
			var parent = old_nameElement.parentNode;
			parent.insertBefore( new_nameElement, old_nameElement );
			parent = old_urlElement.parentNode;
			parent.insertBefore( new_urlElement, old_urlElement );

			// Apply 'update' to element
			this.multi_selector.addElement( new_nameElement, new_urlElement, this );

			// Update list
			this.multi_selector.addListRow( old_nameElement, old_urlElement, this );

			// Hide old inputs: we can't use display:none because Safari doesn't like it
			old_urlElement.style.position = 'absolute';
			old_urlElement.style.left = '-1000px';
			old_nameElement.style.position = 'absolute';
			old_nameElement.style.left = '-1000px';
		};
		
		// If we've reached maximum number, disable input element
		if( this.max != -1 && this.count >= this.max ){
			buttonElement.disabled = true;
		};

		// File element counter
		this.count++;
		
		// simple button
		this.current_element = buttonElement;
	};

	/**
	 * Add a new row to the list of files
	 */
	this.addListRow = function( nameElement, urlElement, buttonElement ) {

		// Row div
		var new_row = document.createElement( 'tr' );
		var new_col_filename = document.createElement( 'td' );
		var new_col_button = document.createElement( 'td' );
		
		new_col_filename.setAttribute( 'align', 'left');
		new_col_button.setAttribute( 'align', 'right');

		// Delete button
		var new_row_button = document.createElement( 'input' );
		new_row_button.type = 'button';
		new_row_button.value = 'Delete';

		new_row.appendChild( new_col_filename );
		new_row.appendChild( new_col_button );

		// References
		new_row.nameElement = nameElement;
		new_row.urlElement = urlElement;

		// Delete function
		new_row_button.onclick= function(){

			// Remove row from table
			this.parentNode.parentNode.parentNode.removeChild(this.parentNode.parentNode);

			// Remove element from form
			this.parentNode.parentNode.buttonElement.parentNode.removeChild( this.parentNode.parentNode.nameElement );
			this.parentNode.parentNode.buttonElement.parentNode.removeChild( this.parentNode.parentNode.urlElement );
			
			// Decrement counter
			this.parentNode.parentNode.buttonElement.multi_selector.count--;
			
			// Re-enable input element (if it's disabled)
			this.parentNode.parentNode.buttonElement.multi_selector.current_element.disabled = false;
			
			// Appease Safari
			// without it Safari wants to reload the browser window
			// which nixes your already queued uploads
			return false;
		};
		
		new_col_filename.innerHTML = nameElement.value;
		new_col_button.appendChild( new_row_button );

		// Add it to the list
		this.list_target.appendChild( new_row );
	};

};


// Tabs

function showSpecs(whichTab,tabCount)
{
for ( i=1; i<=tabCount; ++i ) {
	document.getElementById('tab' + i).className = 'pd-tab-off';
	document.getElementById('panel' + i).style.display = 'none';
	}
document.getElementById('tab' + whichTab).className = 'pd-tab-on';
document.getElementById('panel' + whichTab).style.display = 'block';
}




var eventStyle = '';
function eventRollover(row, style, eventCurr) {
   row.className=style;
   eventStyle = eventCurr;
}

function eventRollout(row, style) {
   eventStyle = '';
   row.className=style;
}




// pscroller.js
/***********************************************
* REMEMBER DO NOT GET RID OF [1]
* REMEMBER DO NOT GET RID OF [1]
* REMEMBER DO NOT GET RID OF [1]
* REMEMBER DO NOT GET RID OF [1]
* REMEMBER DO NOT GET RID OF [1]
***********************************************/



var pausecontent=new Array();
pausecontent[0]="<table width='100%'><tr><td><a href='http://www.ticketcity.com/Lyle-Lovett-Tickets-Stubbs-BBQ-October-15-7-00PM.html'>Lyle Lovett Tickets, Stubbs BBQ, October 15 7:00PM</a></td></tr></table>";
pausecontent[1]="<table width='100%'><tr><td><a href='http://www.ticketcity.com/Korn-Tickets-Frank-Erwin-Center-October-17-7-00PM.html'>Korn Tickets, Frank Erwin Center, October 17 7:00PM</a></td></tr></table>";
pausecontent[2]="<table width='100%'><tr><td><a href='http://www.ticketcity.com/Chevelle-Tickets-Stubbs-BBQ-October-18-7-00PM.html'>Chevelle Tickets, Stubbs BBQ, October 18 7:00PM</a></td></tr></table>";
pausecontent[3] ="<table width='100%'><tr><td><a href='http://www.ticketcity.com/Peter-Frampton-Tickets-The-Backyard-October-18-8-00PM.html'>Peter Frampton Tickets, The Backyard, October 18 8:00PM</a></td></tr></table>";
pausecontent[4] ="<table width='100%'><tr><td><a href='http://www.ticketcity.com/Tragically-Hip-Tickets-La-Zona-Rosa-October-18-TBA.html'>Tragically Hip Tickets, La Zona Rosa, October 18 TBA</a></td></tr></table>";
pausecontent[5] ="<table width='100%'><tr><td><a href='http://www.ticketcity.com/Newsboys-Tickets-Frank-Erwin-Center-October-20-7-00PM.html'>Newsboys Tickets, Frank Erwin Center, October 20 7:00PM</a></td></tr></table>";
pausecontent[6] ="<table width='100%'><tr><td><a href='http://www.ticketcity.com/Anthony-Bourdain-Tickets-Paramount-Theatre-TX-October-20-8-00PM.html'>Anthony Bourdain Tickets, Paramount Theatre -TX, October 20 8:00PM</a></td></tr></table>";
pausecontent[7] ="<table width='100%'><tr><td><a href='http://www.ticketcity.com/Fall-Out-Boy-Tickets-Frank-Erwin-Center-October-23-7-00PM.html'>Fall Out Boy Tickets, Frank Erwin Center, October 23 7:00PM</a></td></tr></table>";
pausecontent[8] ="<table width='100%'><tr><td><a href='http://www.ticketcity.com/Tegan-and-Sara-Tickets-Stubbs-BBQ-November-7-7-00PM.html'>Tegan and Sara Tickets, Stubbs BBQ, November 7 7:00PM</a></td></tr></table>";
pausecontent[9] ="<table width='100%'><tr><td><a href='http://www.ticketcity.com/Mute-Math-Tickets-La-Zona-Rosa-November-8-TBA.html'>Mute Math Tickets, La Zona Rosa, November 8 TBA</a></td></tr></table>";
pausecontent[10]="<table width='100%'><tr><td><a href='http://www.ticketcity.com/The-Mother-Truckers-Tickets-La-Zona-Rosa-November-9-9-00PM.html'>The Mother Truckers Tickets, La Zona Rosa, November 9 9:00PM</a></td></tr></table>";
pausecontent[11]="<table width='100%'><tr><td><a href='http://www.ticketcity.com/Zappa-Plays-Zappa-Tickets-Hogg-Auditorium-November-13-8-00PM.html'>Zappa Plays Zappa Tickets, Hogg Auditorium, November 13 8:00PM</a></td></tr></table>";
pausecontent[12]="<table width='100%'><tr><td><a href='http://www.ticketcity.com/Brand-New-Tickets-Stubbs-BBQ-November-16-TBA.html'>Brand New Tickets, Stubbs BBQ, November 16 TBA</a></td></tr></table>";
pausecontent[13]="<table width='100%'><tr><td><a href='http://www.ticketcity.com/Blue-October-Tickets-Frank-Erwin-Center-November-17-6-50PM.html'>Blue October Tickets, Frank Erwin Center, November 17 6:50PM</a></td></tr></table>";


function pausescroller(content, divId, divClass, delay){
this.content=content //message array content
this.tickerid=divId //ID of ticker div to display information
this.delay=delay //Delay between msg change, in miliseconds.
this.mouseoverBol=0 //Boolean to indicate whether mouse is currently over scroller (and pause it if it is)
this.hiddendivpointer=1 //index of message array for hidden div
document.write('<div id="'+divId+'" class="'+divClass+'" style="position: relative; overflow: hidden"><div class="innerDiv" style="position: absolute; width: 100%" id="'+divId+'1">'+content[0]+'</div><div class="innerDiv" style="position: absolute; width: 100%; visibility: hidden" id="'+divId+'2">'+content[1]+'</div></div>')
var scrollerinstance=this
if (window.addEventListener) //run onload in DOM2 browsers
window.addEventListener("load", function(){scrollerinstance.initialize()}, false)
else if (window.attachEvent) //run onload in IE5.5+
window.attachEvent("onload", function(){scrollerinstance.initialize()})
else if (document.getElementById) //if legacy DOM browsers, just start scroller after 0.5 sec
setTimeout(function(){scrollerinstance.initialize()}, 500)
}

// -------------------------------------------------------------------
// initialize()- Initialize scroller method.
// -Get div objects, set initial positions, start up down animation
// -------------------------------------------------------------------

pausescroller.prototype.initialize=function(){
this.tickerdiv=document.getElementById(this.tickerid)
this.visiblediv=document.getElementById(this.tickerid+"1")
this.hiddendiv=document.getElementById(this.tickerid+"2")
this.visibledivtop=parseInt(pausescroller.getCSSpadding(this.tickerdiv))
//set width of inner DIVs to outer DIV's width minus padding (padding assumed to be top padding x 2)
this.visiblediv.style.width=this.hiddendiv.style.width=this.tickerdiv.offsetWidth-(this.visibledivtop*2)+"px"
this.getinline(this.visiblediv, this.hiddendiv)
this.hiddendiv.style.visibility="visible"
var scrollerinstance=this
document.getElementById(this.tickerid).onmouseover=function(){scrollerinstance.mouseoverBol=1}
document.getElementById(this.tickerid).onmouseout=function(){scrollerinstance.mouseoverBol=0}
if (window.attachEvent) //Clean up loose references in IE
window.attachEvent("onunload", function(){scrollerinstance.tickerdiv.onmouseover=scrollerinstance.tickerdiv.onmouseout=null})
setTimeout(function(){scrollerinstance.animateup()}, this.delay)
}


// -------------------------------------------------------------------
// animateup()- Move the two inner divs of the scroller up and in sync
// -------------------------------------------------------------------

pausescroller.prototype.animateup=function(){
var scrollerinstance=this
if (parseInt(this.hiddendiv.style.top)>(this.visibledivtop+5)){
this.visiblediv.style.top=parseInt(this.visiblediv.style.top)-5+"px"
this.hiddendiv.style.top=parseInt(this.hiddendiv.style.top)-5+"px"
setTimeout(function(){scrollerinstance.animateup()}, 50)
}
else{
this.getinline(this.hiddendiv, this.visiblediv)
this.swapdivs()
setTimeout(function(){scrollerinstance.setmessage()}, this.delay)
}
}

// -------------------------------------------------------------------
// swapdivs()- Swap between which is the visible and which is the hidden div
// -------------------------------------------------------------------

pausescroller.prototype.swapdivs=function(){
var tempcontainer=this.visiblediv
this.visiblediv=this.hiddendiv
this.hiddendiv=tempcontainer
}

pausescroller.prototype.getinline=function(div1, div2){
div1.style.top=this.visibledivtop+"px"
div2.style.top=Math.max(div1.parentNode.offsetHeight, div1.offsetHeight)+"px"
}

// -------------------------------------------------------------------
// setmessage()- Populate the hidden div with the next message before it's visible
// -------------------------------------------------------------------

pausescroller.prototype.setmessage=function(){
var scrollerinstance=this
if (this.mouseoverBol==1) //if mouse is currently over scoller, do nothing (pause it)
setTimeout(function(){scrollerinstance.setmessage()}, 100)
else{
var i=this.hiddendivpointer
var ceiling=this.content.length
this.hiddendivpointer=(i+1>ceiling-1)? 0 : i+1
this.hiddendiv.innerHTML=this.content[this.hiddendivpointer]
this.animateup()
}
}

pausescroller.getCSSpadding=function(tickerobj){ //get CSS padding value, if any
if (tickerobj.currentStyle)
return tickerobj.currentStyle["paddingTop"]
else if (window.getComputedStyle) //if DOM2
return window.getComputedStyle(tickerobj, "").getPropertyValue("padding-top")
else
return 0
}

