/*
*	Constructor for the class.
*
*	@sO {String} form element; not necessary -> default = document.forms[0];
*	@tDiv {String} div name for messages; not necessary, domestos will create one, if missing.
*/

function Domestos(sO){
	var me = this;
	// Check, if browser supports xml requests.
	if (!this._getXMLHTTPRequest()) return false;
	this._uid = new Date().getTime().toString();
	if(sO!=undefined) this._targetObject = sO;
	this._addEvent(window,"load",function(){me._register()}); //Wait for document to load completely before attaching events.
	return false;
};

/*** Public properties ***/
Domestos.prototype.statusDiv = ""; //ID for the html div we will create to display the data
Domestos.prototype.waitImg = false; // "/images/wait.gif";
Domestos.prototype.waitMsg = "";//"Bitte warten ...";
Domestos.prototype.callFunc = false;
Domestos.prototype.warnMsg = "Der Formularinhalt ist nicht gespeichert. \nAlle Bearbeitungen gehen verloren, wenn Sie jetzt diese Seite verlassen.";
Domestos.prototype.bubbleOffsetLeft = 0;	// If the bubble has a specific style, its position relative to the target element can be finetuned with these parameters.
Domestos.prototype.bubbleOffsetTop  = 0;
Domestos.prototype.validate = false;		// If true, form element is validated in the background after changing the value.
Domestos.prototype.bubbleValues = false;	// If true, the success result will show in a bubble, if no callback function is defined.
Domestos.prototype.searchHintText = "Meinen Sie vielleicht:";
Domestos.prototype.minSearchStart = 1; // Min. characters to start the search;
Domestos.prototype.toolTipAutoHide = true;
Domestos.prototype.closeImg = false;

/* style definitions for the bubble */
Domestos.prototype.cssBubble = "";
Domestos.prototype.cssBubbleTL = "bubbleTL";
Domestos.prototype.cssBubbleTR = "bubbleTR";
Domestos.prototype.cssBubbleBL = "bubbleBL";
Domestos.prototype.cssBubbleBR = "bubbleBR";
Domestos.prototype.cssBubbleText = "bubbleText";
Domestos.prototype.cssSearchDivBack = "searchDivBack";
Domestos.prototype.cssSearchDivHead = "searchDivHead";
Domestos.prototype.cssSearchDivList = "searchDivList";
Domestos.prototype.searchDivHeadText = "Suggestions";
Domestos.prototype.bubbleMinHeight = "42px";
Domestos.prototype.bubbleMaxWidth = "280px";
Domestos.prototype.bubbleBorderRight = "18px";
Domestos.prototype.bubbleBorderBottom = "18px";
Domestos.prototype.styleOptionList = " style=\"height:180px;overflow:auto;background-color:#ffffff;border:1px solid #dddddd;\"";
Domestos.prototype.styleOptionListItem = " style=\"text-indent:-20px;padding-left:25px;\"";
Domestos.prototype.styleOptionListLink = " style=\"cursor:pointer\"";
Domestos.prototype.overRideFileWarning = false;
Domestos.prototype._markedStyleClass = "marked";
Domestos.prototype._unmarkedStyleClass = "unmarked";
Domestos.prototype._outputDiv = false;
Domestos.prototype.submitButton = "";

/*** Private properties ***/
	Domestos.prototype._uid = 0;
	Domestos.prototype._targetObject = undefined;
	Domestos.prototype._dataURL = ""; //where to load the data from
	Domestos.prototype._searchURL = "";  // where to load data from for searches while typing.
	Domestos.prototype._catchForm = false;
	Domestos.prototype._form = false;
	Domestos.prototype._request = undefined; //var to hold an instance of the XMLHTTPRequest object
	Domestos.prototype._defStatusDiv = "domestosstatus";
	Domestos.prototype._reqMethod = "post";
	Domestos.prototype._regWarnExit = false;
	Domestos.prototype._formHasChanged = false;
	Domestos.prototype._allowBubbleHide = false;
	Domestos.prototype._doSearch = false;
	Domestos.prototype._doValidation = false;
	Domestos.prototype._validationError = false; 
	Domestos.prototype._currentEl = false;
	Domestos.prototype._SearchResultsActive = false;
	Domestos.prototype._bubble = "bubble";
	Domestos.prototype._bubbleText = "bubbleText";
	Domestos.prototype._tooltipElementClass = "tooltip";
	Domestos.prototype._tooltipURL = "";
	Domestos.prototype._lastSearchResult = "";
	Domestos.prototype._SuggestionsVisible = false;
	Domestos.prototype._lastSearchString = "";
	Domestos.prototype._ListPosition = -1;
	Domestos.prototype._outputSearchDiv = "domestossuggestions";

/**************** Public APIs **********************/


/**
*	Tells the class to load its data and render the results.
*/
Domestos.prototype.load = function(e){
	if (this._SuggestionsVisible) {
		this._SuggestionsVisible = false;
		if (e && e.preventDefault) e.preventDefault(); // DOM style
		return false;
	}

	this._doValidation = false;

	/*if (outDiv!=undefined && this._$(outDiv)) {
		// No form processing, just a simple background load.
		this._outputDiv = outDiv;
	}
	else {
	*/
	
	//}
	
	
	//get a new XMLHTTPRequest and store it in an instance var.
	this._request = this._getXMLHTTPRequest();
	if(this._request){	
		//set the var so we can scope the callback
		var me = this;
		//callback will be an anonymous function that calls back into our class
		//this allows the call back in which we handle the response _onData to have the correct scope.
		this._request.onreadystatechange = function(){me._onData();};
		// Read all elements and values from the form.
		var params="";
		if(this._form) {params=this._readForm();}
		params += "&" + this.submitButton;
		// Send the request.
		//if(url!=undefined) {return this._sendRequest(params,url);}
		//else {
			this._sendRequest(params);
			if (e && e.preventDefault) e.preventDefault(); // DOM style

			// Show wait msg.
			var t = "";
			if (this.waitImg) {
				t+="<img src=\""+this.waitImg+"\" alt=\"\" align=absmiddle> ";
			}
			t += this.waitMsg;
			if (t.length>0) this._render(t);
		
			// Position bubble.
			this._hideBubble();
			this._posBubble(this._form);
			
			return false;

		//}
	} else {
		if (e && e.preventDefault) e.preventDefault(); // DOM style
		return false;
	}
};

Domestos.prototype.warnExit = function(warnMsg){
	this._regWarnExit = true;
	if (warnMsg!=undefined) {this.warnMsg=warnMsg;}
	return false;
};

Domestos.prototype.search = function(searchURL,outDiv){
	if (outDiv!=undefined) {
		this._outputSearchDiv = outDiv;
	}
	this._doSearch = true;
	if (searchURL!=undefined) {this._searchURL=searchURL;}
	return false;
};

Domestos.prototype.toolTip = function(url,cs) {
	if (url==undefined) {return false;}
	else {
		this._tooltipURL = url;
		if (cs!=undefined) {this._tooltipElementClass = cs;}
	}
	return false;
};

/***************Private Rendering APIs ********************/

// Renders the bubble.
Domestos.prototype._createBubble = function() {
	this._bubble = this._uid+this._bubble;
	this._bubbleText = this._uid+this._bubbleText;
	while (this._$(this._bubble)) this._bubble+="_";
	while (this._$(this._bubbleText)) this._bubbleText+="_";
	
	var b = document.createElement("div");
	b.id = this._bubble;
	b.style.position = "absolute";
	b.style.display = "none";
	b.style.zIndex = 98;
	b.style.top = "300px";
	b.style.left = "100px";
	b.className = this.cssBubble;
	document.body.appendChild(b);
	
	var t = document.createElement("table");
	t.style.borderCollapse = "collapse";
	b.appendChild(t);
	var tBody = document.createElement("tbody");
	t.appendChild(tBody);
	var tr = document.createElement("tr");
	tBody.appendChild(tr);
	
	var td = document.createElement("td");
	td.className = this.cssBubbleTL;
	td.style.backgroundRepeat = "no-repeat";
	td.style.backgroundPosition = "top left";
	tr.appendChild(td);
	
	var d = document.createElement("div");
	d.className = this.cssBubbleText;
	d.id = this._bubbleText;
	d.style.minHeight = this.bubbleMinHeight;
	d.style.maxWidth = this.bubbleMaxWidth;
	d.innerHTML = "&nbsp;";
	td.appendChild(d);
	
	td = document.createElement("td");
	td.className = this.cssBubbleTR;
	td.style.backgroundRepeat = "no-repeat";
	td.style.backgroundPosition = "top right";
	td.style.lineHeight = "12px";
	td.style.fontSize = "1pt";
	td.innerHTML = "&nbsp;";
	td.width = this.bubbleBorderRight;
	tr.appendChild(td);
	
	tr = document.createElement("tr");
	tBody.appendChild(tr);
	
	td = document.createElement("td");
	td.className = this.cssBubbleBL;
	td.style.backgroundRepeat = "no-repeat";
	td.style.backgroundPosition = "bottom left";
	td.style.lineHeight = "12px";
	td.style.fontSize = "1pt";
	tr.appendChild(td);
	
	d = document.createElement("div");
	d.style.height = this.bubbleBorderBottom;
	td.appendChild(d);
	
	td = document.createElement("td");
	td.className = this.cssBubbleBR;
	td.style.backgroundRepeat = "no-repeat";
	td.style.backgroundPosition = "bottom right";
	td.style.lineHeight = "12px";
	td.style.fontSize = "1pt";
	tr.appendChild(td);
	
	d = document.createElement("div");
	d.style.height = this.bubbleBorderBottom;
	td.appendChild(d);

	return false;
};

Domestos.prototype._createSearchDiv = function() {
/*
<div id="suggestions">
	<div id="suggestions_back">
		<div id="suggestions_head">Suggestions</div>
		<div id="suggestions_list">
*/

	var b = document.createElement("div");
	b.id = this._outputSearchDiv;
	b.style.position = "absolute";
	b.style.display = "none";
	b.style.zIndex = 130;
	b.style.top = "300px";
	b.style.left = "100px";
	b.style.width = "100px";
	b.style.height = "100px";
	document.body.appendChild(b);

	var d = document.createElement("div");
	d.id = this._outputSearchDiv+"_back";
	d.className = this.cssSearchDivBack;
	b.appendChild(d);
	
	var h = document.createElement("div");
	h.id = this._outputSearchDiv+"_head";
	h.className = this.cssSearchDivHead;
	h.innerHTML = this.searchDivHeadText;
	d.appendChild(h);
	
	h = document.createElement("div");
	h.id = this._outputSearchDiv+"_list";
	h.className = this.cssSearchDivList;
	d.appendChild(h);

};

//renders the text inside the bubble.
Domestos.prototype._render = function(t){
	if(t.length>0){
		if (this.statusDiv.length==0 || !this._$(this.statusDiv)){
			this._createStatus();
		}
		this._$(this.statusDiv).innerHTML=t;
	}
	return false;
};

Domestos.prototype._setSubmitButton = function(e) {
	var el = this._getEventElement(e);
	this.submitButton = el.name + "=" + el.value;
}

/***************Private Data Loading Handlers*******************/

// Register the form elements with Domestos functions, add events etc.
Domestos.prototype._register = function() {
	var me = this;
	//window.onkeypress = function(evt) { var e = (evt ? evt : window.event); return (e.keyCode ? e.keyCode : e.charCode ? e.charCode : e.which) != 13; };
	this._registerTooltip();
	if(this._targetObject==undefined) this._targetObject = 0;
	if(document.forms[this._targetObject]){
		this._form=document.forms[this._targetObject];
		for (i = 0; i < this._form.elements.length; i++) {
			switch (this._form.elements[i].type.toLowerCase()) {
				case "file":
					if (!this.overRideFileWarning) {
						alert("Domestos does not support file uploads!");
						return false;
					}
					break;
				case "submit":
					this._addEvent(this._form.elements[i],"click", function(e){me._setSubmitButton(e);});
					break;
			}
		}
		//this._form.onsubmit=function(e){return me.load(e);}; // stop normal page submit.
		this._addEvent(this._form,"submit",function(e){return me.load(e);});
		this._dataURL=this._form.action;
		if (this._doSearch) if (this._searchURL.length==0) this._searchURL = this._dataURL;
		this._reqMethod=this._form.method;
		this._catchForm=true;
		if (this._regWarnExit) this._warnExit();
		if (this._doSearch) {
			for (i=0;i<this._form.elements.length;i++){
				switch (this._form.elements[i].type) {
					case "button":
						if (this.validate) this._addEvent(this._form.elements[i],"focus",function(e){me._onFocus(e);});
						break;
					case "text":
					//case "textarea":
						this._form.elements[i].setAttribute("autocomplete", "off");
						if (this.validate) {
							this._addEvent(this._form.elements[i],"change", function(e){me._validate(e);});
							this._addEvent(this._form.elements[i], 'blur', 	 	function(e) {me._validate(e); me._hideSuggestions(e);});
							this._addEvent(this._form.elements[i], 'focus', function(e) {me._positionSuggestions(e);});//me._onFocus(e);});
						} else {
							this._addEvent(this._form.elements[i], 'focus', function(e) {me._positionSuggestions(e);});
							this._addEvent(this._form.elements[i], 'blur', 	 	function(e) {me._hideSuggestions(e);});
						}
						this._addEvent(this._form.elements[i], 'keypress', function(e) {me._checkKeycode(e);});
						this._addEvent(this._form.elements[i], 'keydown',  function(e) {me._checkKeydown(e);});
						this._addEvent(this._form.elements[i], 'keyup',		function(e) {me._search(e);});
						break;
					default:
						this._addEvent(this._form.elements[i],"change",function(e){me._validate(e);});
						this._addEvent(this._form.elements[i],"focus",function(e){me._onFocus(e);});
						break;
				}
			}
		} else {
			if (this.validate) {
				for (i=0;i<this._form.elements.length;i++){
					if (this._form.elements[i].type!="button") {
						this._addEvent(this._form.elements[i],"change",function(e){me._validate(e);});
					}
					this._addEvent(this._form.elements[i],"focus",function(e){me._onFocus(e);});
				}
			}
		}
	}
	else {
		if (this._regWarnExit || this.validate || this_doSearch) {
			alert("No form in document. Domestos needs a form for validation / inline search / exit warning!");
		}
	}
	// Check for status div; if it does not exist, create a unique div.
	//if (this.statusDiv.length==0 || !this._$(this.statusDiv)){
	//	this._createStatus();
	//}
	// Create bubble.
	this._createBubble();
	this._createSearchDiv();
	
	if (document.forms[0]) {
		var f = document.forms[0];
			if (f.elements[0].type.toLowerCase()!='hidden'){
				f.elements[0].focus();
				return true;
			}
	}
	return false;
};

Domestos.prototype._registerTooltip = function() {
	var me = this;
	if (this._tooltipURL.length>0) {
		for (i=0; i<document.getElementsByTagName("A").length;i++) {
			var el = document.getElementsByTagName("A")[i];
			var elClass = " "+el.className+" ";
			if (elClass.indexOf(this._tooltipElementClass)>-1) {
				this._addEvent(el,"mouseover",function(e){me._onMouseOver(e);});
				this._addEvent(el,"mouseout",function(e){me._onMouseOut(e);});
			}
		}
	}
	return false;
}
/*
Domestos.prototype.registerTooltip = function(el) {
	this._addEvent(el,"mouseover",function(e){me._onMouseOver(e);});
	this._addEvent(el,"mouseout",function(e){me._onMouseOut(e);});
}
*/

Domestos.prototype._createStatus = function() {

		this.statusDiv = this._uid+this._defStatusDiv;
		while (this._$(this.statusDiv)) {this.statusDiv += "_";}
		var st = document.createElement("DIV");
		st.id = this.statusDiv;
		st.innerHTML = "&nbsp;";
		if (this._form) this._form.appendChild(st);
		else document.body.appendChild(st);
};

// Warn before leaving page without sending (saving).
Domestos.prototype._warnExit = function(){
	if(this._form){
		var me = this;
		this._addEvent(window,"beforeunload",function(oEvent){if(!oEvent){oEvent = window.event;}if(me._formHasChanged==true){oEvent.returnValue=me.warnMsg;}});
		for (i=0;i<this._form.elements.length;i++){
			if (this._form.elements[i].type=="button") this._addEvent(this._form.elements[i],"click",function(){me._onChange();});
			else this._addEvent(this._form.elements[i],"change",function(){me._onChange();});
		}
	}
};


Domestos.prototype._onFocus = function(e) {
	this._hideBubble();
	var el = this._getEventElement(e);
	if (this._currentEl != el) {
		if (this._SearchResultsActive) {
			//this._hideBubble();
		}
		if (this._currentEl.type!="button") this._validateMe(this._currentEl);
	}
	this._currentEl = el;
};

// Remember form changes.
Domestos.prototype._onChange = function() {
	this._formHasChanged = true;
};

// Sends the form content for validation after a form element has changed.
Domestos.prototype._validate = function(e) {
	//if(!this._SearchResultsActive && !this._request) {
	//if(!this._request) {
		var me = this;
		var el = this._getEventElement(e);
		this._currentEl = el;
		this._validateMe(el);
		//this._$("status").innerHTML = new Date;
	//}
};

Domestos.prototype._validateMe = function(el) {
	var me = this;
	switch(el.type) {
		case "text":
		case "textarea":
			el.value = this._trim(el.value);
			break;
	}
	this._posBubble(el);
	this._validationError = true;
	if (this._request != undefined) {
		delete this._request;
	}
		// Only validate, if no other XMLHTTPRequest is running!
		this._request = this._getXMLHTTPRequest();
		if(this._request){	
			this._doValidation = true;
			this._request.onreadystatechange = function(){me._onValidationResult();}; 
			var params="";
			params=this._readForm();
			params+="&DomestosValidate="+el.name;
			this._sendRequest(params);
		}
	//}
	
};


// Returns the result values as specified.
Domestos.prototype._returnData = function(val){
	this._formHasChanged = false;
	if (this._outputDiv) {
		this._$(this._outputDiv).innerHTML = val;
		//this._outputDiv = false;
	}
	else if (this.callFunc) {
		// Send values to function.
		this._render("&nbsp;"); // Turn off wait msg div.
		eval(this.callFunc+"(val);");
	} 
	else if (this.bubbleValues || this._doValidation) {
		// Show values in bubble.
		this._render("&nbsp;"); // Turn off wait msg div.
		this._updateBubble(val);
	} 
	else {
		// Show values in status div.
		this._render(val);
	}
};

// Returns the result values as specified.
Domestos.prototype._returnValidation = function(val){
	this._formHasChanged = false;
	this._render("&nbsp;"); // Turn off wait msg div.
	this._updateBubble(val);
};

Domestos.prototype._search = function(e) {
	//if (this._validationError == false) {
		var el = this._getEventElement(e);
		if (this._currentEl==false || this._currentEl==el) {
			if (this._lastSearchString!=this._trim(el.value)) {
				this._lastSearchString=this._trim(el.value);
				if (this._lastSearchString.length>=this.minSearchStart) {
					//this._render("&nbsp;");
					//this._posBubble(el);
					this._request = this._getXMLHTTPRequest();
					if(this._request){	
						var me = this;
						this._request.onreadystatechange = function(){me._onSearchResult()};	
						var params="";
						params=this._readForm();
						params+="&DomestosSearch="+el.name;
						this._sendRequest(params, this._searchURL);
					}
				} else {
					//this._hideBubble();
					if (this._outputSearchDiv!=undefined) {
						this._$(this._outputSearchDiv).style.display = "none";
					}
				}
			}
		}
		this._currentEl = el;
	//}
};

Domestos.prototype._onMouseOver = function(e) {
	var el = this._getEventElement(e);
	this._posBubble(el);
	this._request = this._getXMLHTTPRequest();
	if(this._request){	
		var me = this;
		this._request.onreadystatechange = function(){me._onTooltipResult()};	
		var params="url=";
		params=params.concat(encodeURIComponent(el.href)).concat("&width="+this.bubbleMaxWidth).concat("&DomestosPreview=1");
		this._sendRequest(params, this._tooltipURL);
	}
};

Domestos.prototype._onMouseOut = function(e) {
	if (this.toolTipAutoHide) this._hideBubble();
};

Domestos.prototype._onSearchResult = function(){
	if(this._request && this._request.readyState==4) {
		//this._$("debug").innerHTML = new Date();
		if(this._request.status=="200") {
			var ret = this._request.responseXML;
			if (ret==null || ret.getElementsByTagName("option")==null || ret.getElementsByTagName("option").item(0)==null) {
				// No xml received.
				var retVal = this._request.responseText;
				if (this._outputSearchDiv==undefined) {
					this._returnData(retVal,true);
				} else {
					if (retVal==this._lastSearchResult) return false;
					else {
						this._lastSearchResult = retVal;
						if (retVal.length > 10) {
							this._SuggestionsVisible = true;
							this._positionSuggestionsDiv(this._currentEl);
							this._$(this._outputSearchDiv+"_list").innerHTML = retVal;
							this._$(this._outputSearchDiv).style.display = "block";
							this._hideBubble();
							this._addClickEvent2Suggestions();
						} else {
							this._$(this._outputSearchDiv+"_list").innerHTML = "&nbsp;";
							this._$(this._outputSearchDiv).style.display = "none";
							this._SuggestionsVisible = false;
						}
					}
				}
			} else {
				var el = ret.getElementsByTagName("option").item(0);
				var msg = "";
				var ds = "";
				if (el.childNodes.length > 8) ds = this.styleOptionList;
				for (i=0; i<el.childNodes.length; i++) {
					this._render(i);
					msg += "<p"+this.styleOptionListItem+"><a"+this.styleOptionListLink+">"+el.childNodes[i].firstChild.data+"</a></p>";
				}
				if (msg.length>0) {
					var ln = "optionlist";
					while(this._$(ln)){ln+="_";}
					msg = this.searchHintText+"<div id=\""+ln+"\""+ds+">"+msg+"</div>";
					this._SearchResultsActive = true;
					this._updateBubble(msg);
					this._allowBubbleHide = false;
					var l = this._$(ln);
					var me = this;
					for(i=0;i<l.childNodes.length;i++){
						addEvent(l.childNodes[i].childNodes[0],"click",function(e){me._insertSearchResult(e);});
					}
				} else {
					this._updateBubble(" ");
				}
			} 
			if(this.onDraw != undefined){this.onDraw();}
		}
		//clean up
		delete this._request;
	}
};

Domestos.prototype._onTooltipResult = function() {
	var me = this;
	if(this._request && this._request.readyState==4) {
		if(this._request.status=="200") {
			var msg = "";
			var ln = "bubbleTooltip";
			while(this._$(ln)){ln+="_";}
			if (!this.toolTipAutoHide) {
				msg += "<div align=\"right\" style=\"margin-bottom:5px\"><a id=\""+ln+"\" style=\"cursor:pointer\">";
				if (this.closeImg) msg += "<img src=\""+this.closeImg+"\" border=0 alt=\"\">";
				else msg+= "x";
				msg += "</a></div>";
			}
			msg += "<div style=\"max-width:"+this.bubbleMaxWidth+";max-height:"+this.bubbleMaxWidth+";overflow:auto\">"+this._request.responseText+"</div>";
			this._updateBubble(msg);
			if (!this.toolTipAutoHide) addEvent(this._$(ln),"click",function(){me._hideBubble();});
		}
		delete this._request;
	}
};

Domestos.prototype._insertSearchResult = function(e) {
	this._SearchResultsActive = true;
	var el = this._getEventElement(e);
	if (this._currentEl) {
		this._currentEl.value = el.innerHTML.replace('&amp;','&');
		this._hideBubble();
		this._currentEl.focus();
	} 
};

//callback for when the data is loaded from the server
Domestos.prototype._onData = function(){
	if(this._request && this._request.readyState == 4){
		if(this._request.status == "200"){
			var ret = this._request.responseXML;
			if (ret==null || ret.getElementsByTagName("error")==null || ret.getElementsByTagName("error").item(0)==null) {
				// No xml received.
				var retVal = this._request.responseText;
				this._returnData(retVal);
			} else {
				// Analyze return error value.
				var error = ret.getElementsByTagName("error").item(0).firstChild.data;
				switch(error) {
					default: // No error -> return result values!
						var retVal = ret.getElementsByTagName("return").item(0).firstChild.data;
						this._returnData(retVal);
						break;
					case "1": // validation error.
						this._validationError = true;
						if (ret.getElementsByTagName("element") && ret.getElementsByTagName("element").item(0)) {
							var el  = ret.getElementsByTagName("element").item(0).firstChild.data;
							if (el) {
								for (i=0;i<this._form.elements.length;i++) {
									if (this._form.elements[i].name==el) {
										this._posBubble(this._form.elements[i]);
										break;
									}
								}
							} else {
								this._posBubble(this._form);
							}
						}
						if (ret.getElementsByTagName("msg") && ret.getElementsByTagName("msg").item(0)) {
							var msg = ret.getElementsByTagName("msg").item(0).firstChild.data;
							var t = this._uid+"domestos_bubble_div";
							while (this._$(t)!=undefined) { t+="_";}
							this._updateBubble("<div id=\""+t+"\" style=\"cursor:pointer\">"+msg+"</div>",true);
							var me = this;
							this._addEvent(this._$(t),"click",function(){me._hideBubble();});
						} else {
							alert("Unknown error occurred, error message is missing!");
						}
						this._render("&nbsp;");
						break;
				}
			}
			//if the onDraw callback has been defined
			//call it to let the listener know
			//that we are done creating the list
			if(this.onDraw != undefined){this.onDraw();}
		}


		else{	
			//check if an error callback handler has been defined
			if(this.onError != undefined){
				//pass an object to the callback handler with info
				//about the error
				this.onError({status:this_request.status,statusText:this._request.statusText});
				this._render("&nbsp;");
			}
		}
		//clean up
		delete this._request;
	}
};

//callback for when the data is loaded from the server
Domestos.prototype._onValidationResult = function(){
	if(this._request && this._request.readyState == 4){
		if(this._request.status == "200"){
			var ret = this._request.responseXML;
			if (ret==null || ret.getElementsByTagName("error")==null || ret.getElementsByTagName("error").item(0)==null) {
				// No xml received.
				/*var retVal = this._request.responseText;
				this._returnValidation(retVal);*/
				var msg = this._trim(this._request.responseText);
				if (msg.length>0) {
					//alert("'"+msg+"'");
					var t = this._uid+"domestos_bubble_div";
					while (this._$(t)!=undefined) { t+="_";}
					this._updateBubble("<div id=\""+t+"\" style=\"cursor:pointer\">"+msg+"</div>",true);
					var me = this;
					this._addEvent(this._$(t),"click",function(){me._hideBubble();});
				} else {
					this._validationError = false;
					//this._hideBubble();
				}
			} else {
				// Analyze return error value.
				var error = ret.getElementsByTagName("error").item(0).firstChild.data;
				switch(error) {
					default: // No error -> return result values!
						var retVal = ret.getElementsByTagName("return").item(0).firstChild.data;
						this._returnValidation(retVal);
						break;
					case "1": // validation error.
						this._validationError = true;
						if (ret.getElementsByTagName("element") && ret.getElementsByTagName("element").item(0)) {
							var el  = ret.getElementsByTagName("element").item(0).firstChild.data;
							if (el) {
								for (i=0;i<this._form.elements.length;i++) {
									if (this._form.elements[i].name==el) {
										this._posBubble(this._form.elements[i]);
										break;
									}
								}
							} else {
								this._posBubble(this._form);
							}
						}
						if (ret.getElementsByTagName("msg") && ret.getElementsByTagName("msg").item(0)) {
							var msg = ret.getElementsByTagName("msg").item(0).firstChild.data;
							var t = this._uid+"domestos_bubble_div";
							while (this._$(t)!=undefined) { t+="_";}
							this._updateBubble("<div id=\""+t+"\" style=\"cursor:pointer\">"+msg+"</div>",true);
							var me = this;
							this._addEvent(this._$(t),"click",function(){me._hideBubble();});
						} else {
							alert("Unknown error occurred, error message is missing!");
						}
						this._render("&nbsp;");
						break;
				}
			}
		}
		else{	
			//check if an error callback handler has been defined
			if(this.onError != undefined){
				//pass an object to the callback handler with info
				//about the error
				this.onError({status:this_request.status,statusText:this._request.statusText});
				this._render("&nbsp;");
			}
		}
		//clean up
		delete this._request;
	}
};

/*************** Suggestion functions **************************/


Domestos.prototype._registerElement = function(el) {
	var me = this;
	el.setAttribute("autocomplete", "off");
	this._addEvent(el, 'focus', 	 function(e) {me._positionSuggestions(e);});
	this._addEvent(el, 'keypress', function(e) {me._checkKeycode(e);});
	this._addEvent(el, 'keydown',  function(e) {me._checkKeydown(e);});
	this._addEvent(el, 'blur', 	 function(e) {me._hideSuggestions(e);});
};

Domestos.prototype._positionSuggestions = function(evt) {
	this._ListPosition = -1;
	var e = evt || window.event;
	var t = e.target || e.srcElement;
	var el = this._$(this._outputSearchDiv);
	this._positionSuggestionsDiv(t);
	this._$(this._outputSearchDiv+"_list").innerHTML = "&nbsp;";
};

Domestos.prototype._positionSuggestionsDiv = function(t) {
	t.setAttribute("autocomplete", "off");
	var el = this._$(this._outputSearchDiv);
	if (t.offsetWidth > 100) el.style.width = t.offsetWidth+"px";
	else el.style.width = "100px";
	el.style.top = (this._absTop(t)+t.offsetHeight)+"px";
	el.style.left = this._absLeft(t)+"px";
	this._resetSuggestions();
};

Domestos.prototype._checkKeycode = function(evt) {
	var e = evt || window.event;
	var keycode = e.keyCode || e.which || 0;
	var t = e.target || e.srcElement;
	var hs = false;
	switch (keycode) {
		case 27:
			hs = true;
			this._hideSuggestions();
			break;
		case 9:
			if (this._$(this._outputSearchDiv).style.display=="block") {
				this._insertSuggestion(t);
			}
			break;
		case 13:
			hs = true;
			if (this._$(this._outputSearchDiv).style.display=="block") {
				this._insertSuggestion(t);
			}
			break;
	}
	hs=true;
	if (hs == false) {
		var items = this._$(this._outputSearchDiv+"_list").getElementsByTagName("div");
		if (items.length==0) {
			//this._$(this._outputSearchDiv).style.display = "none";
		}
		else {
			this._$(this._outputSearchDiv).style.display = "block";
		}
	}
};

Domestos.prototype._checkKeydown = function(evt){
	var e = evt || window.event;
	var keycode = e.keyCode || e.which || 0;
	var t = e.target || e.srcElement;
	var items = this._$(this._outputSearchDiv+"_list").getElementsByTagName("div");
	var hs = false;
	switch (keycode) {
		case 38:
		case 57385:
			if (items.length>0) this._$(this._outputSearchDiv).style.display = "block";
			this._markSuggestion(-1);
			break;
		case 40:
		case 57386:
			if (items.length>0) this._$(this._outputSearchDiv).style.display = "block";
			this._markSuggestion(1);
			break;
		case 27:
			this._SuggestionsVisible = false;
			this._hideSuggestions();
			break;
		case 9:
			this._insertSuggestion(t);
			this._SuggestionsVisible = false;
			//this._stopEvent(evt);
			break;
	}
};

Domestos.prototype._markSuggestion = function(s) {
	var items = this._$(this._outputSearchDiv+"_list").getElementsByTagName("div");
	this._ListPosition += s;
	if (this._ListPosition < -1) this._ListPosition = -1;
	if (this._ListPosition > items.length - 1) this._ListPosition = items.length - 1;
	for (i=0; i < items.length; i++) {
		if (i == this._ListPosition) {
			items[i].className = 'marked';
		} else {
			items[i].className = 'unmarked';
		}
	}
};

Domestos.prototype._resetSuggestions = function() {
	var items = this._$(this._outputSearchDiv+"_list").getElementsByTagName("div");
	for (i=0; i < items.length; i++) {
		items[i].className = 'unmarked';
	}
	this._ListPosition = -1;
};

Domestos.prototype._addClickEvent2Suggestions = function() {
	var me = this;
	var items = this._$(this._outputSearchDiv+"_list").getElementsByTagName("div");
	for (i=0; i < items.length; i++) {
		this._addEvent(items[i], 'mousedown', function(e) {me._suggestionClicked(e);});
		this._addEvent(items[i], 'mouseover', function(e) {me._suggestionMouseOver(e);});
	}
};

Domestos.prototype._suggestionClicked = function(evt){
	var e = evt || window.event;
	var t = e.target || e.srcElement;
	var items = this._$(this._outputSearchDiv+"_list").getElementsByTagName("div");
	for (i=0; i < items.length; i++) {
		if (items[i].id==t.parentNode.parentNode.parentNode.parentNode.id) {
			this._ListPosition = i;
			break;
		}
	}
	this._insertSuggestion(this._currentEl);
	this._SuggestionsVisible = false;
};

Domestos.prototype._suggestionMouseOver = function(evt) {
	var e = evt || window.event;
	var t = e.target || e.srcElement;
	var items = this._$(this._outputSearchDiv+"_list").getElementsByTagName("div");
	for (i=0; i < items.length; i++) {
		if (items[i].id==t.parentNode.parentNode.parentNode.parentNode.id) {
			items[i].className = 'marked';
		} else {
			items[i].className = 'unmarked';
		}
	}
};

Domestos.prototype._hideSuggestions = function() {
	this._$(this._outputSearchDiv).style.display = "none";
	this._ListPosition = -1;
	this._SuggestionsVisible = false;
};

Domestos.prototype._insertSuggestion = function(el) {
	if (this._ListPosition != -1) {
		var items = this._$(this._outputSearchDiv+"_list").getElementsByTagName("div");
		var tt = items[this._ListPosition].getElementsByTagName("td");
		if (typeof tt[0].textContent != "undefined") {
			el.value=tt[0].textContent;
		} else {
			el.value=tt[0].innerText;
		}
	}
	this._hideSuggestions();
};

/* */
/***************Private Data Util Functions ********************/

//returns an XMLHTTPRequest instance (based on browser)
Domestos.prototype._getXMLHTTPRequest = function(){
	var xhr;
	if(window.XMLHttpRequest){xhr = new XMLHttpRequest;}
	else if(window.ActiveXObject){
		try{xhr=new ActiveXObject("Msxml2.XMLHttp")}
		catch(e){
			try{xhr=new ActiveXObject("Microsoft.XMLHttp");}
			catch(e2){}
		}
	}
	return xhr;
};

Domestos.prototype._sendRequest = function(params,url) {
	// Send the form.
	if (url==undefined) url=this._dataURL;
	if (this._reqMethod.toLowerCase()=="post") {
		this._request.open("POST", url, true);
		this._request.setRequestHeader("Content-Length", params.length);
		this._request.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
		this._request.send(params);
	}
	else {
		if(params.length>0){params="?"+params;}
		this._request.open("GET", url+params,true);
		this._request.send(null);
	}	
	return false;
};

Domestos.prototype._readForm = function(){
    var strSubmit = "";
    var formElem;
    for (i = 0; i < this._form.elements.length; i++) {
		formElem = this._form.elements[i];
		switch (formElem.type) {
			case "submit":
				//alert(formElem.value);
				//strSubmit += (formElem.name + "=").concat(encodeURIComponent(formElem.value)) + "&";
				break;
			case "radio":
			case "checkbox":
				if (formElem.checked) strSubmit += (formElem.name + "=").concat(encodeURIComponent(formElem.value)) + "&";
				break;
			default:
	        /*case 'text':
	        case 'select-one':
	        case 'hidden':
	        case 'password':
	        case 'textarea':
			case 'button':*/
	        	strSubmit += (formElem.name + "=").concat(encodeURIComponent(formElem.value)) + "&";
	            break;
		}
	}
	strSubmit += "domestos=1";
	//alert(strSubmit);
	return strSubmit;
};


Domestos.prototype._addEvent = function(obj, evType, fn) {
	if (obj) {
		if (obj.addEventListener){ 
	   		obj.addEventListener(evType, fn, false); 
	   		return true; 
		} else if (obj.attachEvent){ 
	   		var r = obj.attachEvent("on"+evType, fn); 
	   		return r; 
	 	} else { 
	   		return false; 
	 	} 
	} else {
		return false;
	}
};

Domestos.prototype._stopEvent = function(evt) {
    if ((/msie/).test(navigator.userAgent.toLowerCase())) {
        evt.cancelBubble = true; 
    }
    else {
        evt.preventDefault(); 
    }
    if (evt && evt.stopPropagation) evt.stopPropagation();
    else if (window.event) window.event.cancelBubble = true;
};

Domestos.prototype._updateBubble = function(msg,allowHide) {
	// Trim msg.
	msg = this._trim(msg) ;
	if (msg.length>0) {
		this._$(this._bubbleText).innerHTML = msg;
		this._$(this._bubble).style.display = "";
		//this._fixSize(this._$(this._bubbleText));
		if (allowHide) this._allowBubbleHide = true;
		else this._allowBubbleHide = false;
	} else {
		this._hideBubble();
	}
};

// IE does not fully support max-width style.
// So we have to resize manually, if necessary.
Domestos.prototype._fixSize = function(el) {
	if ((/msie/).test( navigator.userAgent.toLowerCase())) {
		el.style.width="auto";
		el.style.height="auto";
		var rect=el.getBoundingClientRect();
		var allowH = true;
		if(el.style.maxWidth){
			var w = rect.right-rect.left;
			if(parseInt(el.style.maxWidth,10) < parseInt(w,10)) {
				el.style.width = el.style.maxWidth + "px";
				allowH = false;
			}
		}
		if (allowH && el.style.minHeight){
			var h=rect.bottom-rect.top;
			if(parseInt(el.style.minHeight,10) > parseInt(h,10)) {
				el.style.height = el.style.minHeight + "px";
			}
		}
	}
};

Domestos.prototype._posBubble = function(el){
	// Position bubble right next to the calling element.
	if (el) {
		var x = this._absLeft(el)+el.offsetWidth+this.bubbleOffsetLeft;
		this._$(this._bubble).style.left = x+"px";
		var y = this._absTop(el)+el.offsetHeight+this.bubbleOffsetTop;
		this._$(this._bubble).style.top  = y+"px";
		if (this._validationError) el.scrollIntoView();
		//this._$(this._bubble).scrollIntoView();
		
	this._$(this._bubble).style.display = "none";
	this._$(this._bubbleText).innerHTML = "";
	}
};

Domestos.prototype._hideBubble = function() {

	this._$(this._bubble).style.display = "none";
	this._$(this._bubbleText).innerHTML = "";
	this._validationError = false; 
	this._allowBubbleHide = false;
	//this._SearchResultsActive = false;
};

Domestos.prototype._getEventElement	= function(e) {
	// Finds the calling element for an event.
	var el;
    e = e ? e : window.event;
	if (e.target) el = e.target;
	else if (e.srcElement) el = e.srcElement;
	if (el.nodeType == 3) el = el.parentNode; // defeat Safari bug
	e.cancelBubble = true;
    if (!e.preventDefault) {e.preventDefault = function () { return false; };}
    if (!e.stopPropagation) {e.stopPropagation = function () { if (window.event) window.event.cancelBubble = true; };}
	return el;
};


Domestos.prototype._absTop		= function(el) {return (el.offsetParent)?el.offsetTop-el.scrollTop+this._absTop(el.offsetParent):el.offsetTop;};
Domestos.prototype._absLeft	= function(el) {return (el.offsetParent)?el.offsetLeft-el.scrollLeft+this._absLeft(el.offsetParent):el.offsetLeft;};
Domestos.prototype._$			= function(el) {return document.getElementById(el);};
Domestos.prototype._trim		= function(el) {return el.replace( /(^[ \t\n\r]*)|([ \t\n\r]*$)/g, "" );};




