var _winCollection = new Array();   //collection of windows
var __currentTop = 2;             //max z-index
var currentWheelObject = null;      //current object that is handling the onWheel event
var ltScrollObj = false;            //lastScrollObject -- used in mechanism for fixing the Opera bug
var cancelThisClick = false;        //if function attachBodyCloseMenus should not do anything on it's next run


var threads = [];

function SetEnd (TB)
{
	if (TB.createTextRange) {
		var FieldRange = TB.createTextRange();
		FieldRange.moveStart('character', TB.value.length);
		FieldRange.collapse();
		FieldRange.select();
	}
}

function addThread(func) {
	threads.push(func);
}

function stopThread(func) {
	for (var i=0; i<threads.length; i++) {
		if (threads[i] == func) {
			threads.splice(i, 1);
			return;
		}
	}
}

function thread30Ms() {
	for (var i=0; i<threads.length; i++) {
		if (threads[i]()) {threads[i];};
	};
}

var threadsHandle = setInterval('thread30Ms();', 100);


/* onMouseScroll event handler */
function _wheel (event) {
	if (currentWheelObject === null) { return; }
	if (!currentWheelObject.onMouseWheel) { return; }
	var delta = 0;
	if (!event) { event = window.event; }
	if (event.wheelDelta) {
		delta = event.wheelDelta/120;
/*		if (window.opera) { delta = -delta; }*/
	} else if (event.detail) {
			delta = -event.detail/3;
	}
	if (delta) { currentWheelObject.onMouseWheel(delta); }
	if (event.preventDefault) {
		event.preventDefault();
	}
	event.returnValue = false;
};

/* ... and register the event */
if (window.addEventListener) window.addEventListener('DOMMouseScroll', _wheel, false);
window.onmousewheel = document.onmousewheel = _wheel;

/* Returns the desktop width */
function getMaxX() {
	return document.body.offsetWidth;
}

/* Returns the desktop height */
function getMaxY() {
	return document.body.offsetHeight;
}

/* Returns the desktop X Center */
function centerX() {
	return Math.floor(getMaxX() / 2);
}

/* Returns the desktop Y Center */
function centerY() {
	return Math.floor(getMaxY() / 2);
}

/* Returns the coordinates of an object */
function getXY(obj) {
	var x = y = 0;
	if (obj.offsetParent) {
		do {
			x += obj.offsetLeft;
			y += obj.offsetTop;
		} while (obj = obj.offsetParent);
	}
	return [x, y];
}

/* Cancels a browser event */
function cancelEvent(e) {
	e = e ? e : window.event;
	if(e.stopPropagation) e.stopPropagation();
	if(e.preventDefault) e.preventDefault();
	e.cancelBubble = true;
	e.cancel = true;
	e.returnValue = false;
	return false;
}

/* Returns document.getElementById */
function ID(str) {
	return document.getElementById(str);
}

/* Creates a new element */
function $(tag) {
	return document.createElement(tag);
}

/* Disables text selection */
function disableSelection(target) {
	if (typeof target.onselectstart!="undefined") { target.onselectstart=function(){return false;}; }
	else if (typeof target.style.MozUserSelect!="undefined") { target.style.MozUserSelect="none"; } 
	else { target.onmousedown=function(){return false;}; target.style.cursor = "default"; }
}

/* Adds a style to className of an object */
function addStyle(obj, styleName) {
	var arr = (obj.className && (obj.className.length > 0)) ? obj.className.split(' ') : [];
	for (var i=0; i<arr.length; i++) { if (arr[i] == styleName) { return; } }
	arr.push(styleName);
	obj.className = arr.join(' ');
}

/* Removes a style from className of an object */
function removeStyle(obj, styleName) {
	var arr = (obj.className && (obj.className.length > 0)) ? obj.className.split(' ') : [];
	var found = false;
	for (var i=0; i<arr.length; i++) { if (arr[i] == styleName) { found = i; } }
	if (found === false) { return; }
	arr.splice(found, 1);
	obj.className = (arr.length > 0) ? arr.join(' ') : '';
}

/* Add functions tomorrow and yesterday to the Date object */
Date.prototype.tomorrow = function() { this.setMonth(this.getMonth(), this.getDate()+1); return this; }
Date.prototype.yesterday= function() { this.setMonth(this.getMonth(), this.getDate()-1); return this; }


/* Returns true if the browser is Opera */
function isOpera() {
	return window.opera ? true : false;
}

/* Fix for Opera Scrollable div Bug */
if (isOpera()) {
	window.restoreScroll = function () {
		ltScrollObj.scrollTop = ltScrollObj.scrollSaveY;
		ltScrollObj.scrollLeft = ltScrollObj.scrollSaveX;
	};
}
function preventScrollBug(obj) {
	if (isOpera()) {
		obj.onscroll = function() {
			this.scrollSaveY = this.scrollTop;
			this.scrollSaveX = this.scrollLeft;
		}
		obj.onmouseup = function(ev) {
			ltScrollObj = this;
			setTimeout('restoreScroll();', 1);
		}
	}
}
function attachBodyCloseMenus() {
	if (!cancelThisClick) {
		var divs = document.body.getElementsByTagName('div');
		for (var i=divs.length-1; i>=0; i--) {
			if (divs[i].className == 'DOMSuggestBox') {
				if (!divs[i].preventClose) { divs[i].style.display = 'none'; }
			}
		}
	}
	cancelThisClick = false;
}


function SuggestBox(text_ctl, rpcUrl) {
	document.body.onclick = function() { attachBodyCloseMenus(); }
	var isOpen    = false;
	var xmlhttp   = null;
	var enabled   = true;
	var control   = text_ctl;
	var lastValue = text_ctl.value;
	var container = $('div');
	container.className = 'DOMSuggestBox';
	var isActive  = false;
	var rpc       = rpcUrl;
	
	text_ctl.suggestObj = this;
	text_ctl.onkeyup    = function() { this.suggestObj.run(); };
	text_ctl.onkeypress = function() { this.suggestObj.run(); };
	text_ctl.onkeypress  = function(event) { this.suggestObj.kbHandler(event); };
	text_ctl.setAttribute("autocomplete","off");
	
	var that = this;
	var snapshotValue = '';
	var items     = [];
	var ctlIndex  = 0;
	
	
	document.body.appendChild(container);
	
	this.isVisible      = function()     { return isOpen; };
	this.setEnabled     = function(bool) { enabled = bool; };
	
	this.hide = function() {
		isOpen = false;
		container.style.display = 'none';
	};
	
	this.show = function() {
		var cwidth    = parseInt(text_ctl.offsetWidth);
		if (cwidth < 100) { cwidth = 100; }
		container.style.width = cwidth+'px';
		
		var arr = getXY(control);
		var xP  = arr[0];
		var yP  = arr[1]+parseInt(control.offsetHeight);
		container.style.left = xP+2+'px';
		container.style.top  = yP+'px';
		__currentTop++;
		container.style.zIndex = __currentTop;
	
		isOpen = true;
		container.style.display = 'block';
	};
	
	this.run = function() {
		if (enabled === false) { return; }
		if (lastValue == text_ctl.value) { return;  }
		if (xmlhttp !== null) { return; }
		lastValue = text_ctl.value;
		if (!lastValue) { if (isOpen) this.hide(); return; }
		this.exec(lastValue);
	};
	
	this.createAJAX = function() {
		try { xmlhttp = new ActiveXObject("Msxml2.XMLHTTP"); } 
		catch (e) {
			try { xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); } 
			catch (err) { xmlhttp = null; } 
		}
		if (!xmlhttp && typeof XMLHttpRequest != "undefined") xmlhttp = new XMLHttpRequest();
	};
	
	this.exec       = function(value) {
		if (xmlhttp) { return false; } //allready running
		this.createAJAX();
		if (!xmlhttp) { return false; }//cannot create ajax
		var usedURL = rpc.replace('%q%', escape(value));
/*		xmlhttp.onreadystatechange = function() {
			try {
			if ((this.readyState == 4)) || (this.status == 200)) {
				that.onRpcResponse(this.responseText ? this.responseText : '');
			}
			} catch (ex) {
				alert(ex);
			}
		}*/
		xmlhttp.open('GET', usedURL, false);
		xmlhttp.send(usedURL);
		snapshotValue = value;
		this.onRpcResponse(xmlhttp.responseText ? xmlhttp.responseText : '');
	};
	
	this.hlObj = function (div) {
		for (var i=0; i<items.length; i++) {
			if (items[i] != div) { items[i].className = 'DOMSuggestBoxRow'; } else
			{   items[i].className = 'DOMSuggestBoxRow_hover'; 
				ctlIndex = i;
			}
		}
	};
	
	this.getCtl = function() {
		return text_ctl;
	};
	
	this.update = function(arr) {
		ctlIndex = 0;
		while (container.firstChild) { container.removeChild(container.firstChild); }
		items = [];
		var div; var span;
		for (var i=0; i<arr.length; i++) { 
			div = $('div');
			div.className = (i != 0 ? 'DOMSuggestBoxRow' : 'DOMSuggestBoxRow_hover');
			div.onclick = function() {
				that.hide();
				that.setValue(that.getDivValue(this));
			};
			span = $('span');
			span.innerHTML = arr[i];
			div.appendChild(span);
			div.onmouseover = function() { that.hlObj(this); };
			container.appendChild(div);
			items.push(div);
		}
		if (items.length > 5) { container.style.height = '100px'; } else { container.style.height = null; }
	}
	
	this.onRpcResponse = function (txt) {
		if (txt.length > 0) { 
			this.update(txt.split('\n'));
			this.show(); 
		} else {
			this.hide();
		}
		xmlhttp = null;
	};
	
	this.getDivValue = function(div) {
		var val = div.firstChild.innerHTML; //get what's inside the span
		return val;
	};
	
	this.setValue = function(whatValue) {
		var nowValue = text_ctl.value;
		if (nowValue.indexOf(',') == -1) {
			nowValue = [];
		} else {
			nowValue = nowValue.split(',');
			nowValue.splice(nowValue.length-1, 1);
		}
		nowValue.push(whatValue);
		text_ctl.value = nowValue.join(', ')+', ';
		text_ctl.focus();
		SetEnd(text_ctl);
	}
	
	this.kbHandler = function(e) {
		if (!isOpen) { return; }
		e = e ? e : window.event;
		charcode = e.keyCode? e.keyCode : e.charCode;
		switch (charcode) {
			case 40: { //down
				if (ctlIndex < items.length-1) { ctlIndex++; } else { ctlIndex = 0; }
				this.hlObj(items[ctlIndex]);
				try { container.scrollTop = items[ctlIndex].offsetTop; } catch (ex) {}
				break;
			}
			case 38: { //up
				if (ctlIndex > 0) { ctlIndex--; } else { ctlIndex = items.length - 1; }
				this.hlObj(items[ctlIndex]);
				try { container.scrollTop = items[ctlIndex].offsetTop; } catch (ex) {}
				break;
			}
			case 13: { //enter
				this.hide();
				try {
					this.setValue(this.getDivValue(items[ctlIndex]));
					lastValue = text_ctl.value;
				} catch(ex) { }
			}
			case 27: { //esc
				this.hide();
				lastValue = text_ctl.value;
			}
		}
	};
}
