var aj_mainDelimeter = "~!";//"___";
var aj_attributeDelimeter = "~|";//"|||";
var aj_rowDelimeter = "~:";//"_|_";
var aj_columnDelimeter = "~^";//"|_|";

var wm = wm || {};
wm.Utils = wm.Utils || {};
if (!wm.Utils.addEvent){
	wm.Utils.addEvent = function(pTarget, pEventType, pEventHandler){
		if (pTarget.addEventListener){
				pTarget.addEventListener(pEventType, pEventHandler, false);
		}
		else if (pTarget.attachEvent){
			pTarget.attachEvent("on" + pEventType, pEventHandler);
		}
	}
}
if (!Array.prototype.wmRemove){
	Array.prototype.wmRemove = function(p){
		for (var i = 0; i < this.length; i++){
			if (p == this[i]){
				this.splice(i, 1);
				break;
			}
		}
	}
}

function KMDialog(pSender, pContainer){
	var divDialog = document.createElement("div");
	divDialog.className = "kmDialog";
	divDialog.style.position = "absolute";
	divDialog.style.zIndex = 100000;

	var coordinates = wm_coordinates(pSender, pContainer).document;
	divDialog.X = coordinates.X;
	divDialog.Y = coordinates.Y + pSender.offsetHeight;
	divDialog.style.left = divDialog.X + "px";
	divDialog.style.top = divDialog.Y + "px";

	divDialog.divHeader = document.createElement("div");
	divDialog.appendChild(divDialog.divHeader);
	divDialog.divHeader.className = "kmDialog_header";
	divDialog.divHeader.style.position = "relative";
	divDialog.divHeader.onmousedown = function(pEvt){KMDialog_beginDrag(pEvt, divDialog);}
	pContainer.appendChild(divDialog);

	divDialog.tblLayout = document.createElement("table");divDialog.appendChild(divDialog.tblLayout);
	divDialog.tbodyLayout = document.createElement("tbody");divDialog.tblLayout.appendChild(divDialog.tbodyLayout);

	divDialog.getLayoutRow = function(){
		var tr = document.createElement("tr");divDialog.tbodyLayout.appendChild(tr);
		var td1 = document.createElement("td");tr.appendChild(td1);
		var td2 = document.createElement("td");tr.appendChild(td2);
		return tr;
	}

	return divDialog;
}

function KMDialog_beginDrag(pEvt, pDivDialog){
	var evt = pEvt || window.event;
	pDivDialog.mouseX = evt.pageX || evt.clientX + document.documentElement.scrollLeft;
	pDivDialog.mouseY = evt.pageY || evt.clientY + document.documentElement.scrollTop;
	pDivDialog.lastMouseX = pDivDialog.mouseX;
	pDivDialog.lastMouseY = pDivDialog.mouseY;

	document.onmousemove = function(pEvt2){KMDialog_doDrag(pEvt2, pDivDialog);}
	document.onmouseup = function(){KMDialog_endDrag();}
}

function KMDialog_doDrag(pEvt, pDivDialog){
	var evt = pEvt || window.event;
	pDivDialog.mouseX = evt.pageX || evt.clientX + document.documentElement.scrollLeft;
	pDivDialog.mouseY = evt.pageY || evt.clientY + document.documentElement.scrollTop;
	
	var diffX = pDivDialog.mouseX - pDivDialog.lastMouseX;
	var diffY = pDivDialog.mouseY - pDivDialog.lastMouseY;
	
	pDivDialog.lastMouseX = pDivDialog.mouseX;
	pDivDialog.lastMouseY = pDivDialog.mouseY;
	
	pDivDialog.X += diffX;
	pDivDialog.Y += diffY;
	
	pDivDialog.style.left = pDivDialog.X + "px";
	pDivDialog.style.top = pDivDialog.Y + "px";
}

KMDialog_endDrag = function(){
	document.onmousemove = null;
	document.onmouseup = null;
}

function km_createULHandle(){
	var ul = null;
	
	try{
		ul = document.createElement("<ul class='kmDashboard_handles'>");//FF3 raises an 'string contains an invalid character code "5"' on this - it is probably the < or > that result in the error, however IE cannot will often have problems setting attributes especially setting any type attribute eg. <input type="radio">
	}
	catch(exc){
		ul = document.createElement("ul");//FF3 lands here
	}
	ul.setAttribute("class", "kmDashboard_handles");//both IE & FF3 lands here, however some attributes will not be set this way in IE - especially the type attribute (eg. <input type="radio">)
	
	return ul;
}


var _kmDashboard = {
	index			:	0,
	type			:	{Static:0, User:1},
	loaded			:	false,
	newDashboard_dialog	: null,
	urlImages		:	function(pImageName){return "_images/Windows/" + pImageName;},
	urlHandleImages	:	function(pImageName){return "_images/DashboardHandles/" + pImageName;},
	ulHandles		:	km_createULHandle(), //this object can be attached anywhere on the website (eg. set by a different usercontrol), just by xx.appendChild(_kmDashboard.ulHandles);
	kmDashboards	:	[],
	deselectWindows :	function(){for (var d = 0; d < this.kmDashboards.length; d++){this.kmDashboards[d].deselectWindows();}},
	addDashboard	:	function(pkmDashboard){
		if (this.ulHandles.childNodes.length == 0){
			var liAddNew = document.createElement("li");
			liAddNew.className = "kmDashboard_newDashboard_reg";
			liAddNew.onmouseover = function(){this.className = "kmDashboard_newDashboard_hot";}
			liAddNew.onmouseout = function(){this.className = "kmDashboard_newDashboard_reg";}
			liAddNew.onclick = function(){_kmDashboard.newDashboard_open(this);}
			this.ulHandles.appendChild(liAddNew);
		}
		var theLiAddNew = this.ulHandles.removeChild(this.ulHandles.childNodes[this.ulHandles.childNodes.length-1]); //liAddNew is always last
		this.kmDashboards.push(pkmDashboard);
		pkmDashboard.sortOrder = this.kmDashboards.length - 1; //sortOrder starts with 1 not 0, however need to subtract 1 because the default dashboard is also in the kmDashboards array
		this.ulHandles.appendChild(pkmDashboard.liHandle);
		this.ulHandles.appendChild(theLiAddNew); //then add the liAddNew again to set it last
		this.index++;
	},
	openDashboard	:	function(pkmDashboard){
		for (var d = 0; d < this.kmDashboards.length; d++){
			if (this.kmDashboards[d].id == pkmDashboard.id){
				this.kmDashboards[d].divDashboard.style.display = "block";
				this.kmDashboards[d].liHandle.className = "kmDashboard_handle_sel";
			}
			else{
				this.kmDashboards[d].divDashboard.style.display = "none";
				this.kmDashboards[d].liHandle.className = "kmDashboard_handle_reg";
			}
		}
		//set page to top
		window.scrollTo(0, 0);
	},
	newDashboard_open	: function(pLiAddNew){ //then clicking on addNewDashboard LI
		if (this.newDashboard_dialog){return;}
		var divNewDashboard = KMDialog(pLiAddNew, document.body); //-divNewDashboard is actually the dialog to create the new dashboard
		this.newDashboard_dialog = divNewDashboard

		divNewDashboard.style.zIndex = 2000;
		var coordinates = wm_coordinates(pLiAddNew).document;
		divNewDashboard.Y = coordinates.Y + (pLiAddNew.offsetHeight / 2);
		divNewDashboard.X = coordinates.X - divNewDashboard.offsetWidth - 5;
		divNewDashboard.style.top = divNewDashboard.Y + "px";
		divNewDashboard.style.left = divNewDashboard.X + "px";

		var btnCancel = null;
		try{
			btnCancel = document.createElement("<input type=\"button\" value=\"Cancel\" />");
		}
		catch(ex){
			btnCancel = document.createElement("input");
			btnCancel.setAttribute("type", "button");
			btnCancel.setAttribute("value", "Cancel");
		}
		btnCancel.onclick = function(){_kmDashboard.newDashboard_close();}
		
		var btnApply = null;
		try{
			btnApply = document.createElement("<input type=\"button\" value=\"Apply\" />");
		}
		catch(ex){
			btnApply = document.createElement("input");
			btnApply.setAttribute("type", "button")
			btnApply.setAttribute("value", "Apply");
		}
		btnApply.onclick = function(){_kmDashboard.newDashboard_save(false);}
		
		var btnOk = null;
		try{
			btnOk = document.createElement("<input type=\"button\" value=\" Ok \" />");
		}
		catch(ex){
			btnOk = document.createElement("input");
			btnOk.setAttribute("type", "button");
			btnOk.setAttribute("value", " OK ");
		}
		btnOk.onclick = function(){_kmDashboard.newDashboard_save(true);} //ok call newDashboardSave

		var trFinish = divNewDashboard.getLayoutRow();
		trFinish.childNodes[0].appendChild(btnCancel);
		trFinish.childNodes[1].align = "right";
		trFinish.childNodes[1].appendChild(btnApply);
		trFinish.childNodes[1].appendChild(document.createTextNode("\u00a0\u00a0\u00a0"));
		trFinish.childNodes[1].appendChild(btnOk);
	},
	newDashboard_save	:	function(pClose){
		var kmDashboard = new KMDashboard();
		if (pClose){this.newDashboard_close();} //newDashboard_close is just closing the dialog not the new dashboard
		
		_kmDashboard.openDashboard(kmDashboard); //ok, open the dashboard
		
		kmDashboard.save(); //and then persist it to db
	},
	newDashboard_close	:	function(){
		document.body.removeChild(this.newDashboard_dialog);
		this.newDashboard_dialog = null;
	},
	onLoad				:	function(){
		//this is NOT _kmDashboard, it must be the window object, since _kmDashboard.onLoad is attached to window.onload
		_kmDashboard.loaded = true;
	}
}

wm.Utils.addEvent(window, "load", _kmDashboard.onLoad);

function KMDashboard(pConfig){
	var kmDashboard = this;
	
	var defaults = {
		dashboardID					:	null, //MUST have
		sortOrder					:	0,
		liHandle					:	null, //
		handle_imageName			:	"", //the tab selecting the dashboard
		divDashboard				:	null,
		type						:	(pConfig == null || typeof(pConfig["divDashboard".toLowerCase()]) == "undefined") ? _kmDashboard.type.User : _kmDashboard.type.Static,
		divCanvas_backgroundColor	:	"#fff",
		title						:	"dashboard"
	}
	
	for (var p in defaults){
		this[p] = (pConfig == null || typeof(pConfig[p.toLowerCase()]) == "undefined") ? defaults[p] : pConfig[p.toLowerCase()]; //the DashboardsPersonal.ascx.cs is sending the parameters from the db in lowercase (this makes it possible to have different casing in the db column names and the above defaults object)
	}
	
	if (this.liHandle == null){
		this.liHandle = document.createElement("li");
		this.liHandle.dashboard = this;
		this.liHandle.className = "kmDashboard_handle_reg";
		this.liHandle.onmouseover = function(){if (this.className != "kmDashboard_handle_sel"){this.className = "kmDashboard_handle_hot";}}
		this.liHandle.onmouseout = function(){if (this.className != "kmDashboard_handle_sel"){this.className = "kmDashboard_handle_reg";}}
		this.liHandle.onclick = function(){_kmDashboard.openDashboard(this.dashboard);}
		this.liHandle.title = this.title;

		if (this.handle_imageName != ""){
			var imgHandle = document.createElement("img");this.liHandle.appendChild(imgHandle);
			imgHandle.src = _kmDashboard.urlHandleImages(this.handle_imageName);
			imgHandle.style.margin = "7px 0 5px 10px"; //TODO: centering the image should be better handled
		}
	}
	
	this.id = (this.type == _kmDashboard.type.User) ? "kmDashboard_" + _kmDashboard.index : "kmDashboard" + this.divDashboard.id.substring(this.divDashboard.id.indexOf('_'));
	
	//handle windows collection
	this.kmWindows = [];
	this.deselectWindows = function(){for (var w = 0; w < this.kmWindows.length; w++){this.kmWindows[w].deselect();}}, //this function is normally invoked by _kmDashboard one dashboard at a time looping _kmDashboard.kmDashboards
	this.addWindow = function(pkmWindow){this.kmWindows.push(pkmWindow);_kmWindow.index++;},
	this.removeWindow = function(pkmWindow){
		this.kmWindows.wmRemove(pkmWindow);
		pkmWindow.container.removeChild(pkmWindow.divWindow); //pkmWindow.container is dashboard.divCanvas
		pkmWindow = null;
	}
	
	if (this.type == _kmDashboard.type.User){
		createDashboard();
		createSidePanel();
		createCanvas();
	}
	
	_kmDashboard.addDashboard(this); // Add me to the dashboard collection
	
	function createDashboard(){
		kmDashboard.divDashboard = document.createElement("div");
		kmDashboard.divDashboard.id = "divDashboard_" + _kmDashboard.index;
		if (kmDashboard.dashboardID == null){
			var newDashboardID = -_kmDashboard.index;
			kmDashboard.dashboardID = newDashboardID;
		}
		kmDashboard.divDashboard.className = "kmDashboard";
		(document.getElementById("divDashboards")).appendChild(kmDashboard.divDashboard);
		var table = document.createElement("table"); table.cellPadding = 0; table.cellSpacing = 0; table.border = 0; kmDashboard.divDashboard.appendChild(table);
		var tbody = document.createElement("tbody"); table.appendChild(tbody);
		var tr = document.createElement("tr");tbody.appendChild(tr);
		kmDashboard.tdSidePanel = document.createElement("td");tr.appendChild(kmDashboard.tdSidePanel);kmDashboard.tdSidePanel.vAlign = "top";kmDashboard.tdSidePanel.style.width = "25px";
		kmDashboard.tdCanvas = document.createElement("td");tr.appendChild(kmDashboard.tdCanvas);kmDashboard.tdCanvas.vAlign = "top";kmDashboard.tdCanvas.style.width = "900px";
	}
	
	function createSidePanel(){
		kmDashboard.divSidePanel = document.createElement("div");
		kmDashboard.divSidePanel.id = "divDashboard_sidePanel_" + _kmDashboard.index;
		kmDashboard.divSidePanel.className = "kmDashboard_sidePanel";
//		kmDashboard.divDashboard.appendChild(kmDashboard.divSidePanel);
		kmDashboard.tdSidePanel.appendChild(kmDashboard.divSidePanel);
		
		kmDashboard.ulSidePanel = document.createElement("ul");
		kmDashboard.divSidePanel.appendChild(kmDashboard.ulSidePanel);
//		kmDashboard.divSidePanel.className = "kmDashboard_sidePanel";
		
		var liConfigureDashboard = document.createElement("li");kmDashboard.ulSidePanel.appendChild(liConfigureDashboard);
		var imgConfigureDashboard = document.createElement("img");liConfigureDashboard.appendChild(imgConfigureDashboard);
		imgConfigureDashboard.src = _kmDashboard.urlImages("configureDashboard_reg.gif");
		imgConfigureDashboard.onmouseover = function(){this.src = _kmDashboard.urlImages("configureDashboard_hot.gif");}
		imgConfigureDashboard.onmouseout = function(){this.src = _kmDashboard.urlImages("configureDashboard_reg.gif");}
		imgConfigureDashboard.onclick = function(){kmDashboard.configureDashboard(this);}
		imgConfigureDashboard.title = "Configure Dashboard";
		
		var liNewKMWindow = document.createElement("li");kmDashboard.ulSidePanel.appendChild(liNewKMWindow);
		var imgNewKMWindow = document.createElement("img");liNewKMWindow.appendChild(imgNewKMWindow);
		imgNewKMWindow.dashboard = kmDashboard;
		imgNewKMWindow.src = _kmDashboard.urlImages("newWindow_reg.jpg");
		imgNewKMWindow.onmouseover = function(){this.src = _kmDashboard.urlImages("newWindow_hot.jpg");}
		imgNewKMWindow.onmouseout = function(){this.src = _kmDashboard.urlImages("newWindow_reg.jpg");}
		imgNewKMWindow.onclick = function(){kmDashboard.createKMWindow(null, true);}//null for pConfig, true for pPersist
		imgNewKMWindow.title = "New Window";
		
		var liOpenAllWindows = document.createElement("li");kmDashboard.ulSidePanel.appendChild(liOpenAllWindows);
		var imgOpenAllWindows = document.createElement("img");liOpenAllWindows.appendChild(imgOpenAllWindows);
		imgOpenAllWindows.dashboard = kmDashboard;
		imgOpenAllWindows.src = _kmDashboard.urlImages("openAllWindows_reg.gif");
		imgOpenAllWindows.onmouseover = function(){this.src = _kmDashboard.urlImages("openAllWindows_hot.gif");}
		imgOpenAllWindows.onmouseout = function(){this.src = _kmDashboard.urlImages("openAllWindows_reg.gif");}
		imgOpenAllWindows.onclick = function(){kmDashboard.openAllWindows();}
		imgOpenAllWindows.title = "Open All Windows";
		
		var liMinimizeAllWindows = document.createElement("li");kmDashboard.ulSidePanel.appendChild(liMinimizeAllWindows);
		var imgMinimizeAllWindows = document.createElement("img");liMinimizeAllWindows.appendChild(imgMinimizeAllWindows);
		imgMinimizeAllWindows.dashboard = kmDashboard;
		imgMinimizeAllWindows.src = _kmDashboard.urlImages("minimizeAllWindows_reg.gif");
		imgMinimizeAllWindows.onmouseover = function(){this.src = _kmDashboard.urlImages("minimizeAllWindows_hot.gif");}
		imgMinimizeAllWindows.onmouseout = function(){this.src = _kmDashboard.urlImages("minimizeAllWindows_reg.gif");}
		imgMinimizeAllWindows.onclick = function(){kmDashboard.minimizeAllWindows();}
		imgMinimizeAllWindows.title = "Minimize All Windows";
		
		var liCloseAllWindows = document.createElement("li");kmDashboard.ulSidePanel.appendChild(liCloseAllWindows);
		var imgCloseAllWindows = document.createElement("img");liCloseAllWindows.appendChild(imgCloseAllWindows);
		imgCloseAllWindows.dashboard = kmDashboard;
		imgCloseAllWindows.src = _kmDashboard.urlImages("closeAllWindows_reg.gif");
		imgCloseAllWindows.onmouseover = function(){this.src = _kmDashboard.urlImages("closeAllWindows_hot.gif");}
		imgCloseAllWindows.onmouseout = function(){this.src = _kmDashboard.urlImages("closeAllWindows_reg.gif");}
		imgCloseAllWindows.onclick = function(){kmDashboard.closeAllWindows();}
		imgCloseAllWindows.title = "Close All Windows";
	}
	
	function createCanvas(){
		kmDashboard.divCanvas = document.createElement("div");
		kmDashboard.divCanvas.id = "divDashboard_canvas_" + _kmDashboard.index;
		kmDashboard.divCanvas.className = "kmDashboard_canvas";
		kmDashboard.divCanvas.style.backgroundColor = kmDashboard.divCanvas_backgroundColor;
//		kmDashboard.divDashboard.appendChild(kmDashboard.divCanvas);
		kmDashboard.tdCanvas.appendChild(kmDashboard.divCanvas);
	}
	
	this.save = function(){
		if (!_kmDashboard.loaded){return;} //KMDashboard.save is not invoked then initializing, so it is not necessary to test whether page have loaded, however it looks nice

		/*
		A dashboard is saving then: 1) it is created (from _kmDashboard.newDashboard_save), 2) then the configure dialog is saved (kmDashboard.configureSave)
		*/

		var url = dashboard_getSaveDashboardUrl(kmDashboard.dashboardID);
		var strDashboard	 = kmDashboard.dashboardID + aj_mainDelimeter;
		strDashboard		+= kmDashboard.title + aj_mainDelimeter;
		strDashboard		+= kmDashboard.sortOrder + aj_mainDelimeter;
		strDashboard		+= kmDashboard.handle_imageName + aj_mainDelimeter;
		strDashboard		+= kmDashboard.divCanvas_backgroundColor + aj_mainDelimeter;
		//not necessary to add UserID since UserID will be available code-behind as KMUser.GetInstance.UserID

		var dashboardID = kmDashboard.dashboardID;
		var http = wm_getHttp_new();
		wm_Http_nvc.add("dashboard_save_" + dashboardID, http);
		http.open("POST", url, true);
		http.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
		http.onreadystatechange = function(){kmDashboard.save_handler(dashboardID);};
		http.send("dashboard_sd=" + strDashboard);//since open method is POST, I need to put the namevalue parameters in the send() method in this format: n1=v1&n2=v2
	}
	this.save_handler = function(pDashboardID){ //in case of new dashboard, the dashboardID will be negative (first updated in save_update)
		var http = wm_Http_nvc.getHttp("dashboard_save_" + pDashboardID);
		if (http == null){return;} //If I move the mouse very fast over many links generating multiple XmlHttpRequests very fast, eventually the http object will end up null
		if (http.readyState == 4){
			if (http.status == 200){
				var responseText = http.responseText;
				if (responseText.indexOf("ERROR") == 0){
					alert(responseText);
				}
				else{
					var newDashboardID = parseInt(responseText);
					if (newDashboardID == 0){alert("You are not logged in, your new Dashboard cannot be persisted");return;}
					this.save_update(pDashboardID, newDashboardID);
				}
			}
			else{
				alert("Problem: status code: " + http.status + " " + http.statusText);
			}
			wm_Http_nvc.remove("dashboard_save_" + pDashboardID);
		}
	}
	this.save_update = function(pOldDashboardID, pNewDashboardID){
		if (pOldDashboardID == pNewDashboardID){return;} //just an existing dasbhoard updated eg. if background color changed
		
		for (var d = 0; d < _kmDashboard.kmDashboards.length; d++){
			var dashboard = _kmDashboard.kmDashboards[d];
			if (dashboard.dashboardID == pOldDashboardID){
				dashboard.dashboardID = pNewDashboardID;
			}
		}
	}
	
	this.deleteDashboard = function(){
		if (confirm("Permanently delete this dashboard including all it's windows ?")){
			var dashboardID = this.dashboardID
			if (dashboardID > 0){ //window is in database
				var url = dashboard_getDeleteDashboardUrl(dashboardID);
				var http = wm_getHttp_new();
				wm_Http_nvc.add("dashboard_delete_" + dashboardID, http);
				http.open("GET", url, true);
				http.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
	//			http.onreadystatechange = function(){there is no handler};
				http.send(null);
				wm_Http_nvc.remove("dashboard_delete_" + dashboardID);
			}
			
			//ok, now remove the dashboard from the client DOM
			_kmDashboard.ulHandles.removeChild(this.liHandle);
			var divDashboards = document.getElementById("divDashboards");
			divDashboards.removeChild(this.divDashboard);
			_kmDashboard.kmDashboards.wmRemove(this);
			_kmDashboard.openDashboard(_kmDashboard.kmDashboards[0]);//open the default dashboard
			
			//recalculate sortOrder on all dashboards (since dashboards with a higher sortOrder than the one deleted should now get their sortOrder adjusted
			for (var d = 1; d < _kmDashboard.kmDashboards.length; d++){ //start with d=1 to avoid setting anything on the default dashboard
				_kmDashboard.kmDashboards[d].sortOrder = d;
				_kmDashboard.kmDashboards[d].save();
			}
		}
	}
}

KMDashboard.prototype.configureClose = function(){
	this.divDashboard.removeChild(this.divConfigure);
	this.divConfigure = null;
}

KMDashboard.prototype.configureSave = function(pClose){
	this.divCanvas_backgroundColor = this.divConfigure.tbBackgroundColor.value;//set the value as a property on kmDashboard.divCanvas
	this.divCanvas.style.backgroundColor = this.divCanvas_backgroundColor;//update the divCanvas to reflect the new property value
	
	var selectedImages = wm.ImageComboBox.getSelectedImages("icbTabIcon");
	if (selectedImages.length == 0){
		this.handle_imageName = "";
		if (this.liHandle.firstChild){this.liHandle.removeChild(this.liHandle.firstChild);}
	}
	else{
		var lastIndexOfSlash = selectedImages[0].src.lastIndexOf('/');
		var selectedImageName = (lastIndexOfSlash == -1) ? selectedImages[0].src : selectedImages[0].src.substring(lastIndexOfSlash + 1);
		this.handle_imageName = selectedImageName;
		var imgHandle = this.liHandle.firstChild;
		if (imgHandle == null){imgHandle = document.createElement("img");this.liHandle.appendChild(imgHandle);}
		imgHandle.src = "_images/DashboardHandles/" + this.handle_imageName;
		imgHandle.style.margin = "7px 0 5px 10px"; //TODO: centering the image should be better handled
	}
	
	if (pClose){this.configureClose();}
	
	this.save(); //persist the values
}

KMDashboard.prototype.configureDashboard = function(pSender){
	var kmDashboard = this;
	if (this.divConfigure){
		alert("Configure is already open");
		return;
	}
	
	this.divConfigure = KMDialog(pSender, this.divDashboard);

	var tblLayout = document.createElement("table");this.divConfigure.appendChild(tblLayout);
	var tbodyLayout = document.createElement("tbody");tblLayout.appendChild(tbodyLayout);
	
//			var trTitle = getLayoutRow(tbodyLayout);
//			trTitle.childNodes[0].appendChild(document.createTextNode("Title"));
//			trTitle.childNodes[1].appendChild(this.divConfigure.tbTitle = document.createElement("<input type=\"text\" value=\"" + this.title + "\" />"));

	var trBackgroundColor = getLayoutRow(tbodyLayout);
	trBackgroundColor.childNodes[0].appendChild(document.createTextNode("Background Color"));
	var tbBackgroundColor = null;
	try{
		tbBackgroundColor = document.createElement("<input type\"text\" value=\"" + this.divCanvas_backgroundColor + "\" />");
	}
	catch(ex){
		tbBackgroundColor = document.createElement("input");
		tbBackgroundColor.setAttribute("type", "text");
		tbBackgroundColor.setAttribute("value", this.divCanvas_backgroundColor);
	}
	trBackgroundColor.childNodes[1].appendChild(tbBackgroundColor);
	this.divConfigure.tbBackgroundColor = tbBackgroundColor;
	
	//create an ImageComboBox for the TabIcon
	//1) create a style object (wm.ImageComboBox.CssObject)
	var icbCssObject_tabIcon = new wm.ImageComboBox.CssObject();
	icbCssObject_tabIcon.table_list = "";
	icbCssObject_tabIcon.div_listContainer = "";
	icbCssObject_tabIcon.td_header_selectedImage_reg = "background-color:yellow;";
	icbCssObject_tabIcon.td_header_selectedImage_hot = "background-color:red;";
	icbCssObject_tabIcon.td_header_arrow_reg = "background-color:#ccc;width:22px;height:22px;";
	icbCssObject_tabIcon.td_header_arrow_hot = "background-color:yellow;width:22px;height:22px;";
	icbCssObject_tabIcon.img_header_selectedImage_reg_ = "width:24px;height:48px;";
	icbCssObject_tabIcon.img_header_selectedImage_hot = "width:24px;height:24px;";
	icbCssObject_tabIcon.img_header_arrow_reg = "width:12px;height:12px;";
	icbCssObject_tabIcon.img_header_arrow_hot = "width:14px;height:14px;";
	icbCssObject_tabIcon.td_item_reg = "background-color:yellow;";
	icbCssObject_tabIcon.td_item_hot = "background-color:blue;border-color:blue;";
	icbCssObject_tabIcon.td_item_sel = "background-color:red;";
	icbCssObject_tabIcon.img_item_reg = "";
	icbCssObject_tabIcon.img_item_hot = "";
	icbCssObject_tabIcon.img_item_sel = "";
	
	//2) pass the style object to the ImageComboBox create function (that will write out the necessary styles)
	var icbTabIcon_ID = "icbTabIcon";
	var icbTabIcon_sourceFolder = "_images/DashboardHandles/";
	var icbTabIcon_selectedImages = [kmDashboard.handle_imageName];
	var icbTabIcon_urlComboBoxArrowDown = "_images/Template/imageComboBox_arrowDown.png";//arrowUp needs to be named xxx_arrowUp.png
	var icbTabIcon_repeatColumns = 3;
	var icbTabIcon_selectionMode = "single";
	var icbTabIcon_minimumSelectCount = 0;
	var icbTabIcon_maximumSelectCount = 1;
	var icbTabIcon_callbackFunction = null; //it does not give meaning with a callback function in this case because the callback function does not belong to a specific dashboard because the this keyboard in the callback function will not refer to the object on which the callback function is defined but instead refer to the object which invoked the callback function (which seems to be the window)
	var icbTabIcon = wm.ImageComboBox.create(icbTabIcon_ID, icbTabIcon_sourceFolder, icbTabIcon_urlComboBoxArrowDown, icbTabIcon_selectedImages, icbTabIcon_repeatColumns, icbTabIcon_selectionMode, icbTabIcon_minimumSelectCount, icbTabIcon_maximumSelectCount, icbTabIcon_callbackFunction, icbCssObject_tabIcon);
	
	var trTabIcon = getLayoutRow(tbodyLayout);
	trTabIcon.childNodes[0].appendChild(document.createTextNode("Tab Icon"));
	trTabIcon.childNodes[1].appendChild(icbTabIcon);
	
	var imgDelete = document.createElement("img");
	imgDelete.src = "_images/Dashboard/dashboard_delete.png";
	imgDelete.onclick = function(){kmDashboard.deleteDashboard();}
	
	var btnCancel = null;
	try{
		btnCancel = document.createElement("<input type=\"button\" value=\"Cancel\" />");
	}
	catch(ex){
		btnCancel = document.createElement("input");
		btnCancel.setAttribute("type", "button");
		btnCancel.setAttribute("value", "Cancel");
	}
	btnCancel.onclick = function(){kmDashboard.configureClose();}
	
	var btnApply = null;
	try{
		btnApply = document.createElement("<input type=\"button\" value=\"Apply\" />");
	}
	catch(ex){
		btnApply = document.createElement("input");
		btnApply.setAttribute("type", "button");
		btnApply.setAttribute("value", "Apply");
	}
	btnApply.onclick = function(){kmDashboard.configureSave(false);}
	
	var btnOk = null;
	try{
		btnOk = document.createElement("<input type=\"button\" value=\" Ok \" />");
	}
	catch(ex){
		btnOk = document.createElement("input");
		btnOk.setAttribute("type", "button");
		btnOk.setAttribute("value", " OK ");
	}
	btnOk.onclick = function(){kmDashboard.configureSave(true);}

	var trFinish = getLayoutRow(tbodyLayout);
	trFinish.childNodes[0].appendChild(btnCancel);
	trFinish.childNodes[1].align = "right";
	trFinish.childNodes[1].appendChild(btnApply);
	trFinish.childNodes[1].appendChild(document.createTextNode("\u00a0\u00a0\u00a0"));
	trFinish.childNodes[1].appendChild(btnOk);
	
	var trDelete = getLayoutRow(tbodyLayout);
	trDelete.childNodes[0].appendChild(imgDelete);
	trDelete.childNodes[1].appendChild(document.createTextNode("\u00a0"));
	
	function getLayoutRow(pTbody, pName){
		var tr = document.createElement("tr");pTbody.appendChild(tr);
		var td1 = document.createElement("td");tr.appendChild(td1);
		var td2 = document.createElement("td");tr.appendChild(td2);
		return tr;
	}
}

KMDashboard.prototype.createKMWindow = function(pConfig, pPersist){
	var kmDashboard = this;
	var newWindowID = -_kmWindow.index;
	var config = (pConfig) ? pConfig : {windowid:newWindowID, title:"someTitle", divWindowH:400, divWindowW:300, minHeight:50, minWidth:50, dashboard:this};//'this' is kmDashboard
	var kmWindow = new KMWindow(config);
	
	if (pPersist){
		kmWindow.save();
	}
}

KMDashboard.prototype.openAllWindows = function (){
	for (var w = 0; w < this.kmWindows.length; w++){
		if (this.kmWindows[w].state == _kmWindow.state.closed){//only call open on closed windows
			this.kmWindows[w].open();
		}
	}
}

KMDashboard.prototype.closeAllWindows = function (){
	for (var w = 0; w < this.kmWindows.length; w++){
		if (this.kmWindows[w].state != _kmWindow.state.closed){
			this.kmWindows[w].close();
		}
	}
}

KMDashboard.prototype.minimizeAllWindows = function (){
	for (var w = 0; w < this.kmWindows.length; w++){
		if (this.kmWindows[w].state != _kmWindow.state.minimized && this.kmWindows[w].state != _kmWindow.state.closed){
			this.kmWindows[w].minimize();
		}
	}
}



var _kmWindow = {
	index		:	0,
	zIndex		:	0,
	state		:	{closed:0, minimized:1, restored:2, maximized:3},
	lastMouseX	:	0,
	lastMouseY	:	0,
	mouseX		:	0,
	mouseY		:	0
}

function KMWindow(pConfig){
	var kmWindow = this;//for internal reference to 'this' then 'this' references to something else eg. in a nested function, a nested object or an eventhandler
	this.id = "kmWindow_" + _kmWindow.index;

	var defaults = {
		windowID	: null, //MUST have
		dashboard : new Object(), //dashboard MUST be part of pConfig
		title : "",
		divWindowCssClass : "kmWindow",
		divWindowY : 100,
		divWindowX : 100,
		divWindowW : 200,
		divWindowH : 200,
		divHeaderCssClass : "kmWindow_header",
		state : _kmWindow.state.restored,
		resizeHandles : ["topLeft", "topCenter", "topRight", "middleLeft", "middleRight", "bottomLeft", "bottomCenter", "bottomRight"],
		minWidth: 50, minHeight: 50, //minimum size of window in pixels
		minLeft: 0, maxLeft: 9999, minTop: 0, maxTop: 9999, // bounding box area in pixels
		isActive : false //then kmWindow.deselect() is invoked it should not deselect a kmWindow that have isActive=true
	}

	for (var p in defaults){
		this[p] = (typeof(pConfig[p.toLowerCase()]) == "undefined") ? defaults[p] : pConfig[p.toLowerCase()];
		if (p == "windowID"){
			
		}
	}

	//properties not suitable for user configuration
	this.container = this.dashboard.divCanvas;
	this.mOffX = 0; //Offset between window & mouse.
	this.mOffY = 0;
	this.savedPosition = {
		top		:	kmWindow.divWindowY,
		left	:	kmWindow.divWindowX,
		height	:	kmWindow.divWindowH,
		width	:	kmWindow.divWindowW,
		state	:	kmWindow.state
	}
	this.savePosition = function(){
		kmWindow.savedPosition.top = kmWindow.divWindowY;
		kmWindow.savedPosition.left = kmWindow.divWindowX;
		if (kmWindow.state != _kmWindow.state.minimized){
			kmWindow.savedPosition.height = kmWindow.divWindowH;
			kmWindow.savedPosition.width = kmWindow.divWindowW;
		}
		
		//here comes ajax to persist position to db (also state should be persisted to db)
		//alternatively I could make a new function called persistWindow, persistAll, persistDashboard
	}
	this.save = function(){
		if (!_kmDashboard.loaded){return;} //otherwise lots of initializing window manipulation may result in unnecessary ajax calls, (eg. if a window is initial closed, the KMWindow.close() will be called upon initialization of that window and that will result in an unnecessary (though harmless) ajax call to update the database with the parameters for that window, however the parameters have not changed)

		/*
		A window is saving then: 1) endDrag, 2) endResize, 3) close, 4)open, 5) minimize, 6) maximize, 7) restore, 8) configureSave
		*/
		
		var url = dashboard_getSaveWindowUrl(kmWindow.windowID);
		var strWindow	 = kmWindow.windowID + aj_mainDelimeter;
		strWindow		+= kmWindow.dashboard.dashboardID + aj_mainDelimeter;
		strWindow		+= kmWindow.divWindowY + aj_mainDelimeter;
		strWindow		+= kmWindow.divWindowX + aj_mainDelimeter;
		strWindow		+= kmWindow.divWindowH + aj_mainDelimeter;
		strWindow		+= kmWindow.divWindowW + aj_mainDelimeter;
		strWindow		+= kmWindow.state + aj_mainDelimeter;
		strWindow		+= kmWindow.title;

		var windowID = kmWindow.windowID;
		var http = wm_getHttp_new();
		wm_Http_nvc.add("window_save_" + windowID, http);
		http.open("POST", url, true);
		http.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
		http.onreadystatechange = function(){kmWindow.save_handler(windowID);};
		http.send("dashboard_sw=" + strWindow);//since open method is POST, I need to put the namevalue parameters in the send() method in this format: n1=v1&n2=v2
	}
	this.save_handler = function(pWindowID){ //in case of new window, the windowID will be negative (first updated in save_update)
		var http = wm_Http_nvc.getHttp("window_save_" + pWindowID);
		if (http == null){return;} //If I move the mouse very fast over many links generating multiple XmlHttpRequests very fast, eventually the http object will end up null
		if (http.readyState == 4){
			if (http.status == 200){
				var responseText = http.responseText;
				if (responseText.indexOf("ERROR") == 0){
					alert(responseText);
				}
				else{
					var newWindowID = parseInt(responseText);
					this.save_update(pWindowID, newWindowID);
				}
			}
			else{
				alert("Problem: status code: " + http.status + " " + http.statusText);
			}
			wm_Http_nvc.remove("window_save_" + pWindowID);
		}
	}
	this.save_update = function(pOldWindowID, pNewWindowID){
		if (pOldWindowID == pNewWindowID){return;} //just an existing window updated eg. if moved
		
		for (var d = 0; d < _kmDashboard.kmDashboards.length; d++){
			var dashboard = _kmDashboard.kmDashboards[d];
			for (var w = 0; w < dashboard.kmWindows.length; w++){
				var window = dashboard.kmWindows[w];
				if (window.windowID == pOldWindowID){
					window.windowID = pNewWindowID;
				}
			}
		}
	}
	
	this.liSidePanel = null; //liSidePanel will be added first time .close() is invoked
	
	if (this.container != document.body && this.maxTop == defaults.maxTop){ //windows live inside a custom box and boundaries are not set, so set boundaries to fit the custom box
		this.minLeft = 1;
		this.minTop = 1;
		this.maxLeft = this.container.offsetWidth - 5;
		this.maxTop = this.container.offsetHeight - 5;
	}

	this.divWindow = document.createElement("div");
	this.divWindow.kmWindow = this;
	this.divWindow.style.zIndex = ++_kmWindow.zIndex;
	this.divWindow.className = "kmWindow";
	createHeader(this);
	createResizeHandles(this);

	//initialize
	this.divWindow.style.position = "absolute";
	this.divWindow.className = this.divWindowCssClass;
	this.divWindow.style.top = this.divWindowY + "px";
	this.divWindow.style.left = this.divWindowX + "px";
	this.divWindow.style.height = this.divWindowH + "px";
	this.divWindow.style.width = this.divWindowW + "px";
	this.divHeader.className = this.divHeaderCssClass;
	this.divWindow.onmousedown = function(){this.style.zIndex = ++_kmWindow.zIndex;}

	this.container.appendChild(this.divWindow);
	
	//respond to initial state
	if (this.state == _kmWindow.state.closed){
		this.close();
	}
	else if (this.state == _kmWindow.state.maximized){
		this.maximize();
	}
	else if (this.state == _kmWindow.state.minimized){
		this.minimize();
	}

	function createHeader(pkmWindow){
		pkmWindow.divHeader = document.createElement("div"); pkmWindow.divWindow.appendChild(pkmWindow.divHeader);
		pkmWindow.divHeader.divWindow = pkmWindow.divWindow;
		pkmWindow.divHeader.kmWindow = pkmWindow;

		pkmWindow.divHeader.imgConfigure = document.createElement("img");
		pkmWindow.divHeader.imgConfigure.id = pkmWindow.id + "_btnConfigure";
		pkmWindow.divHeader.imgConfigure.kmWindow = pkmWindow;
		pkmWindow.divHeader.imgConfigure.src = _kmDashboard.urlImages("configure_reg.png");
		pkmWindow.divHeader.imgConfigure.style.styleFloat = "right";
		pkmWindow.divHeader.imgConfigure.style.cssFloat = "right";
		pkmWindow.divHeader.imgConfigure.onmouseover = function(){this.src = _kmDashboard.urlImages("configure_hot.png");}
		pkmWindow.divHeader.imgConfigure.onmouseout = function(){this.src = _kmDashboard.urlImages("configure_reg.png");}
		pkmWindow.divHeader.imgConfigure.onclick = function(){this.kmWindow.configureOpen(this);}
		pkmWindow.divHeader.imgConfigure.style.cursor = "default";
		pkmWindow.divHeader.imgConfigure.alt = "Configure";
		pkmWindow.divHeader.imgConfigure.title = "Configure";
		pkmWindow.divHeader.appendChild(pkmWindow.divHeader.imgConfigure);

		pkmWindow.divHeader.imgClose = document.createElement("img");
		pkmWindow.divHeader.imgClose.id = pkmWindow.id + "_btnClose";
		pkmWindow.divHeader.imgClose.kmWindow = pkmWindow;
		pkmWindow.divHeader.imgClose.src = _kmDashboard.urlImages("close_reg.png");
		pkmWindow.divHeader.imgClose.style.styleFloat = "right";
		pkmWindow.divHeader.imgClose.style.cssFloat = "right";
		pkmWindow.divHeader.imgClose.onmouseover = function(){this.src = _kmDashboard.urlImages("close_hot.png");}
		pkmWindow.divHeader.imgClose.onmouseout = function(){this.src = _kmDashboard.urlImages("close_reg.png");}
		pkmWindow.divHeader.imgClose.onclick = function(){this.kmWindow.close();}
		pkmWindow.divHeader.imgClose.style.cursor = "default";
		pkmWindow.divHeader.imgClose.alt = "Close";
		pkmWindow.divHeader.imgClose.title = "Close";
		pkmWindow.divHeader.appendChild(pkmWindow.divHeader.imgClose);

		pkmWindow.divHeader.imgMaximize = document.createElement("img");
		pkmWindow.divHeader.imgMaximize.id = pkmWindow.id + "_btnMaximize";
		pkmWindow.divHeader.imgMaximize.kmWindow = pkmWindow;
		pkmWindow.divHeader.imgMaximize.src = _kmDashboard.urlImages("maximize_reg.png");
		pkmWindow.divHeader.imgMaximize.style.styleFloat = "right";
		pkmWindow.divHeader.imgMaximize.style.cssFloat = "right";
		pkmWindow.divHeader.imgMaximize.onmouseover = function(){this.src = _kmDashboard.urlImages("maximize_hot.png");}
		pkmWindow.divHeader.imgMaximize.onmouseout = function(){this.src = _kmDashboard.urlImages("maximize_reg.png");}
		pkmWindow.divHeader.imgMaximize.onclick = function(){this.kmWindow.maximize();}
//				pkmWindow.divHeader.imgMaximize.onmousedown = function(pEvt){fd_cancelEvent(pEvt, true);}
		pkmWindow.divHeader.imgMaximize.style.cursor = "default";
		pkmWindow.divHeader.imgMaximize.alt = "Maximize";
		pkmWindow.divHeader.imgMaximize.title = "Maximize";
		pkmWindow.divHeader.appendChild(pkmWindow.divHeader.imgMaximize);

		pkmWindow.divHeader.imgMinimize = document.createElement("img");
		pkmWindow.divHeader.imgMinimize.id = pkmWindow.id + "_btnMinimize";
		pkmWindow.divHeader.imgMinimize.kmWindow = pkmWindow;
		pkmWindow.divHeader.imgMinimize.src = _kmDashboard.urlImages("minimize_reg.png");
		pkmWindow.divHeader.imgMinimize.style.styleFloat = "right";
		pkmWindow.divHeader.imgMinimize.style.cssFloat = "right";
		pkmWindow.divHeader.imgMinimize.onmouseover = function(){this.src = _kmDashboard.urlImages("minimize_hot.png");}
		pkmWindow.divHeader.imgMinimize.onmouseout = function(){this.src = _kmDashboard.urlImages("minimize_reg.png");}
		pkmWindow.divHeader.imgMinimize.onclick = function(){this.kmWindow.minimize();}
//				pkmWindow.divHeader.imgMinimize.onmousedown = function(pEvt){fd_cancelEvent(pEvt, true);}
		pkmWindow.divHeader.imgMinimize.style.cursor = "default";
		pkmWindow.divHeader.imgMinimize.alt = "Minimize";
		pkmWindow.divHeader.imgMinimize.title = "Minimize";
		pkmWindow.divHeader.appendChild(pkmWindow.divHeader.imgMinimize);

		pkmWindow.divHeader.spanTitle = document.createElement("span"); pkmWindow.divHeader.appendChild(pkmWindow.divHeader.spanTitle);
		pkmWindow.divHeader.spanTitle.appendChild(document.createTextNode(pkmWindow.title));
	}

	function createResizeHandles(pkmWindow){
		pkmWindow.resizeHandleDivs = [];
		for (var h = 0; h < pkmWindow.resizeHandles.length; h++){
			var divHandle = document.createElement("div");
			divHandle.className = "kmWindow_resizeHandle kmWindow_resizeHandle_" + pkmWindow.resizeHandles[h];
			divHandle.style.visibility = "hidden";
			pkmWindow.divWindow.appendChild(divHandle);
			pkmWindow.resizeHandleDivs.push(divHandle);
			divHandle.onmousedown = function(pEvt){kmWindow.beginResize(pEvt, this);}//'this' is a reference to divHandle
		}
	}

	this.enableDragging();
	this.dashboard.addWindow(this);
}

KMWindow.prototype.select = function(pEvt, pDivHeader){
	var evt = pEvt || window.event;
	//'this' turns out to be the header (as it is the header that the event is fired on), but only if using the complicated event handler, otherwise this will be the window object
	//ok, now 'this' is kmWindow

	var reciever = evt.target || evt.srcElement;
	if (reciever && reciever.id.indexOf(this.id + "_btn") == 0){return;}
	
	_kmWindow.mouseX = evt.pageX || evt.clientX + document.documentElement.scrollLeft;
	_kmWindow.mouseY = evt.pageY || evt.clientY + document.documentElement.scrollTop;
	_kmWindow.lastMouseX = _kmWindow.mouseX;
	_kmWindow.lastMouseY = _kmWindow.mouseY;
	this.mOffX = 0; //necessary to reset mOff in case the window was tried to drag out of its containing border
	this.mOffY = 0; //(remember: 'this' is kmWindow and different from _kmWindow, mOff is set on the instance kmWindow not on singleton _kmWindow)
	
	var kmWindow = this;
	this.isActive = true;
	if (this.state != _kmWindow.state.minimized && this.state != _kmWindow.state.maximized){
		this.resizeHandlesSet(true);
	}
	this.beginDrag();
}

KMWindow.prototype.disableDragging = function(){
	this.divHeader.onmousedown = null;
	this.divHeader.style.cursor = "default";
}

KMWindow.prototype.enableDragging = function(){
	this.divHeader.onmousedown = function(pEvt){this.kmWindow.select(pEvt, this);}
	this.divHeader.style.cursor = "move";
}

KMWindow.prototype.deselect = function(){
	if (!this.isActive){this.resizeHandlesSet(false);}
}

KMWindow.prototype.resizeHandlesSet = function(pShow){
	for (var h = 0; h < this.resizeHandles.length; h++){
		this.resizeHandleDivs[h].style.visibility = (pShow) ? "inherit" : "hidden";
	}
}

KMWindow.prototype.beginDrag = function(){
	var kmWindow = this;
	document.onmousemove = function(pEvt){kmWindow.doDrag(pEvt);}
	document.onmouseup = function(){kmWindow.endDrag();}
}

KMWindow.prototype.doDrag = function(pEvt){
	var evt = pEvt || window.event;
	_kmWindow.mouseX = evt.pageX || evt.clientX + document.documentElement.scrollLeft;
	_kmWindow.mouseY = evt.pageY || evt.clientY + document.documentElement.scrollTop;
	
	var diffX = _kmWindow.mouseX - _kmWindow.lastMouseX + this.mOffX;
	var diffY = _kmWindow.mouseY - _kmWindow.lastMouseY + this.mOffY;
	_kmWindow.lastMouseX = _kmWindow.mouseX;
	_kmWindow.lastMouseY = _kmWindow.mouseY;

	this.mOffX = this.mOffY = 0;
	
	with (this){
		var dX = diffX;
		var dY = diffY;
		if (divWindowX + dX < minLeft) {mOffX = (dX - (diffX = minLeft - divWindowX));}
		else if (divWindowX + divWindowW + dX > maxLeft) {mOffX = (dX - (diffX = maxLeft - divWindowX - divWindowW));}
		if (divWindowY + dY < minTop) {mOffY = (dY - (diffY = minTop - divWindowY));}
		else if (divWindowY + divWindowH + dY > maxTop) {mOffY = (dY - (diffY = maxTop - divWindowY - divWindowH));}
		
		divWindowX += diffX;
		divWindowY += diffY;
		
		divWindow.style.left = divWindowX + "px";
		divWindow.style.top = divWindowY + "px";
	}
}

KMWindow.prototype.endDrag = function(){
	this.savePosition();
	document.onmousemove = null;
	document.onmouseup = null;
	this.isActive = false;
	this.save();
}

KMWindow.prototype.beginResize = function(pEvt, pDivHandle){
	//'this' turns out to be the kmWindow instance (eg. this.id = kmWindow_0)
	var evt = pEvt || window.event;
	_kmWindow.mouseX = evt.pageX || evt.clientX + document.documentElement.scrollLeft;
	_kmWindow.mouseY = evt.pageY || evt.clientY + document.documentElement.scrollTop;
	_kmWindow.lastMouseX = _kmWindow.mouseX;
	_kmWindow.lastMouseY = _kmWindow.mouseY;
	this.mOffY = 0;
	this.mOffX = 0;

	var kmWindow = this;
	this.isActive = true;
	
	document.onmousemove = function(pEvt2){kmWindow.doResize(pEvt2, pDivHandle);}
	document.onmouseup = function(){kmWindow.endResize();}
}

KMWindow.prototype.doResize = function(pEvt, pDivHandle){
	var evt = pEvt || window.event;
	_kmWindow.mouseX = evt.pageX || evt.clientX + document.documentElement.scrollLeft;
	_kmWindow.mouseY = evt.pageY || evt.clientY + document.documentElement.scrollTop;

	var diffX = _kmWindow.mouseX - _kmWindow.lastMouseX + this.mOffX;
	var diffY = _kmWindow.mouseY - _kmWindow.lastMouseY + this.mOffY;
	_kmWindow.lastMouseX = _kmWindow.mouseX;
	_kmWindow.lastMouseY = _kmWindow.mouseY;
	var dX = diffX;
	var dY = diffY;

	this.mOffX = this.mOffY = 0;

	var handleName = pDivHandle.className.substring(pDivHandle.className.lastIndexOf('_') + 1);

	with (this){
		if (handleName == "topLeft"){
			if (divWindowH - dY < minHeight) {mOffY = (dY - (diffY = divWindowH - minHeight));}
			else if (divWindowY + dY < minTop) {mOffY = (dY - (diffY = minTop - divWindowY));}
			divWindowY += diffY;
			divWindowH -= diffY;
			
			if (divWindowW - dX < minWidth) {mOffX = (dX - (diffX = divWindowW - minWidth));}
			else if (divWindowX + dX < minLeft) {mOffX = (dX - (diffX = minLeft - divWindowX));}
			divWindowX += diffX;
			divWindowW -= diffX;
		}
		else if (handleName == "topCenter"){
			if (divWindowH - dY < minHeight) {mOffY = (dY - (diffY = divWindowH - minHeight));}
			else if (divWindowY + dY < minTop) {mOffY = (dY - (diffY = minTop - divWindowY));}
			divWindowY += diffY;
			divWindowH -= diffY;
		}
		else if (handleName == "topRight"){
			if (divWindowH - dY < minHeight) {mOffY = (dY - (diffY = divWindowH - minHeight));}
			else if (divWindowY + dY < minTop) {mOffY = (dY - (diffY = minTop - divWindowY));}
			divWindowY += diffY;
			divWindowH -= diffY;
			
			if (divWindowW + dX < minWidth) {mOffX = (dX - (diffX = minWidth - divWindowW));}
			else if (divWindowX + divWindowW + dX > maxLeft) {mOffX = (dX - (diffX = maxLeft - divWindowX - divWindowW));}
			divWindowW += diffX;
		}
		else if (handleName == "middleLeft"){
			if (divWindowW - dX < minWidth) {mOffX = (dX - (diffX = divWindowW - minWidth));}
			else if (divWindowX + dX < minLeft) {mOffX = (dX - (diffX = minLeft - divWindowX));}
			divWindowX += diffX;
			divWindowW -= diffX;
		}
		else if (handleName == "middleRight"){
			if (divWindowW + dX < minWidth) {mOffX = (dX - (diffX = minWidth - divWindowW));}
			else if (divWindowX + divWindowW + dX > maxLeft) {mOffX = (dX - (diffX = maxLeft - divWindowX - divWindowW));}
			divWindowW += diffX;
		}
		else if (handleName == "bottomLeft"){
			if (divWindowH + dY < minHeight) {mOffY = (dY - (diffY = minHeight - divWindowH));}
			else if (divWindowY + divWindowH + dY > maxTop) {mOffY = (dY - (diffY = maxTop - divWindowY - divWindowH));}
			divWindowH += diffY;
			
			if (divWindowW - dX < minWidth) {mOffX = (dX - (diffX = divWindowW - minWidth));}
			else if (divWindowX + dX < minLeft) {mOffX = (dX - (diffX = minLeft - divWindowX));}
			divWindowX += diffX;
			divWindowW -= diffX;
		}
		else if (handleName == "bottomCenter"){
			if (divWindowH + dY < minHeight) {mOffY = (dY - (diffY = minHeight - divWindowH));}
			else if (divWindowY + divWindowH + dY > maxTop) {mOffY = (dY - (diffY = maxTop - divWindowY - divWindowH));}
			divWindowH += diffY;
		}
		else if (handleName == "bottomRight"){
			if (divWindowH + dY < minHeight) {mOffY = (dY - (diffY = minHeight - divWindowH));}
			else if (divWindowY + divWindowH + dY > maxTop) {mOffY = (dY - (diffY = maxTop - divWindowY - divWindowH));}
			divWindowH += diffY;
			
			if (divWindowW + dX < minWidth) {mOffX = (dX - (diffX = minWidth - divWindowW));}
			else if (divWindowX + divWindowW + dX > maxLeft) {mOffX = (dX - (diffX = maxLeft - divWindowX - divWindowW));}
			divWindowW += diffX;
		}

		divWindow.style.top = divWindowY + "px";
		divWindow.style.height = divWindowH + "px";
		divWindow.style.left = divWindowX + "px";
		divWindow.style.width = divWindowW + "px";
	}
}

KMWindow.prototype.endResize = function(){
	//'this' is still kmWindow
	this.savePosition();
	document.onmousemove = null;
	document.onmouseup = null;
	this.isActive = false;
	this.save();
}

KMWindow.prototype.configureOpen = function(pImgConfigure){
	var kmWindow = this;
	if (this.divConfigure == null){
		this.divConfigure = KMDialog(pImgConfigure, this.divWindow);
		
		var tblLayout = document.createElement("table");this.divConfigure.appendChild(tblLayout);
		var tbodyLayout = document.createElement("tbody");tblLayout.appendChild(tbodyLayout);
		
		var trTitle = getLayoutRow(tbodyLayout);
		trTitle.childNodes[0].appendChild(document.createTextNode("Title"));
		trTitle.childNodes[1].appendChild(this.divConfigure.tbTitle = document.createElement("<input type=\"text\" value=\"" + this.title + "\" />"));
		
		var imgDelete = document.createElement("img");
		imgDelete.src = "_images/Dashboard/window_delete.png";
		imgDelete.onclick = function(){kmWindow.deleteWindow();}
		
		var btnCancel = document.createElement("<input type=\"button\" value=\"Cancel\" />");
		btnCancel.onclick = function(){kmWindow.configureClose();}
		
		var btnApply = document.createElement("<input type=\"button\" value=\"Apply\" />");
		btnApply.onclick = function(){kmWindow.configureSave(kmWindow.divConfigure, false);}
		
		var btnOk = document.createElement("<input type=\"button\" value=\" Ok \" />");
		btnOk.onclick = function(){kmWindow.configureSave(kmWindow.divConfigure, true);}
	
		var trFinish = getLayoutRow(tbodyLayout);
		trFinish.childNodes[0].appendChild(btnCancel);
		trFinish.childNodes[1].align = "right";
		trFinish.childNodes[1].appendChild(btnApply);
		trFinish.childNodes[1].appendChild(document.createTextNode("\u00a0\u00a0\u00a0"));
		trFinish.childNodes[1].appendChild(btnOk);
		
		var trDelete = getLayoutRow(tbodyLayout);
		trDelete.childNodes[0].appendChild(imgDelete);
		trDelete.childNodes[1].appendChild(document.createTextNode("\u00a0"));
	}
	else if (!this.divWindow.contains(this.divConfigure)){ //configureClose works by removing divConfigure from the divWindow collection, so if divConfigure have been closed it will exist as an object but not appended divWindow
		this.divWindow.appendChild(this.divConfigure);
	}
	
	this.divConfigure.style.zIndex = this.divWindow.style.zIndex + 1000;
	
	function getLayoutRow(pTbody, pName){
		var tr = document.createElement("tr");pTbody.appendChild(tr);
		var td1 = document.createElement("td");tr.appendChild(td1);
		var td2 = document.createElement("td");tr.appendChild(td2);
		return tr;
	}
}

KMWindow.prototype.configureSave = function(pDivConfigure, pClose){
	var title = pDivConfigure.tbTitle.value;
	this.title = title;
	this.divHeader.spanTitle.innerText = title;
	
	if (pClose){this.configureClose();}
	this.save();
}

KMWindow.prototype.configureClose = function(){
	this.divWindow.removeChild(this.divConfigure);
}

KMWindow.prototype.deleteWindow = function(){
	if (confirm("Permanently delete this window ?")){
		var windowID = this.windowID
		if (windowID > 0){ //window is in database
			var url = dashboard_getDeleteWindowUrl(windowID);
			var http = wm_getHttp_new();
			wm_Http_nvc.add("window_delete_" + windowID, http);
			http.open("GET", url, true);
			http.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
//			http.onreadystatechange = function(){there is no handler};
			http.send(null);
			wm_Http_nvc.remove("window_delete_" + windowID);
		}
		
		//ok, now remove the window from the client DOM
		this.dashboard.removeWindow(this);
	}
}

KMWindow.prototype.minimize = function(){
	this.divWindow.style.height = this.divHeader.offsetHeight + "px";
	
	if (this.state == _kmWindow.state.maximized){
		this.divWindow.style.top = this.savedPosition.top + "px";
		this.divWindow.style.left = this.savedPosition.left + "px";
		this.divWindow.style.width = this.savedPosition.width + "px";
		
		this.divHeader.imgMaximize.src = _kmDashboard.urlImages("maximize_reg.png");
		this.divHeader.imgMaximize.onmouseover = function(){this.src = _kmDashboard.urlImages("maximize_hot.png");}
		this.divHeader.imgMaximize.onmouseout = function(){this.src = _kmDashboard.urlImages("maximize_reg.png");}
		this.divHeader.imgMaximize.onclick = function(){this.kmWindow.maximize();}
		
		this.enableDragging();
	}
	
	this.divHeader.imgMinimize.src = _kmDashboard.urlImages("restoreUp_reg.png");
	this.divHeader.imgMinimize.onmouseover = function(){this.src = _kmDashboard.urlImages("restoreUp_hot.png");}
	this.divHeader.imgMinimize.onmouseout = function(){this.src = _kmDashboard.urlImages("restoreUp_reg.png");}
	this.divHeader.imgMinimize.onclick = function(){this.kmWindow.restore();}
	this.divHeader.imgMinimize.alt = "Restore Up";
	this.divHeader.imgMinimize.title = "Restore Up";
	
	var lowZIndex = (_kmWindow.zIndex - 10 < 0) ? 0 : _kmWindow.zIndex - 10;
	this.divWindow.style.zIndex = lowZIndex;
	
	this.state = _kmWindow.state.minimized;
	this.savedPosition.state = this.state;
	this.save();
}

KMWindow.prototype.maximize = function(pEvt){
	this.divWindow.style.width = "100%";
	this.divWindow.style.height = "100%";
	this.divWindow.style.left = "0px";
	this.divWindow.style.top = "0px";
	
	if (this.state == _kmWindow.state.minimized){
		this.divHeader.imgMinimize.src = _kmDashboard.urlImages("minimize_reg.png");
		this.divHeader.imgMinimize.onmouseover = function(){this.src = _kmDashboard.urlImages("minimize_hot.png");}
		this.divHeader.imgMinimize.onmouseout = function(){this.src = _kmDashboard.urlImages("minimize_reg.png");}
		this.divHeader.imgMinimize.onclick = function(){this.kmWindow.minimize();}
		this.divHeader.imgMinimize.alt = "Minimize";
		this.divHeader.imgMinimize.title = "Minimize";
	}

	this.divHeader.imgMaximize.src = _kmDashboard.urlImages("restoreDown_reg.png");
	this.divHeader.imgMaximize.onmouseover = function(){this.src = _kmDashboard.urlImages("restoreDown_hot.png");}
	this.divHeader.imgMaximize.onmouseout = function(){this.src = _kmDashboard.urlImages("restoreDown_reg.png");}
	this.divHeader.imgMaximize.onclick = function(){this.kmWindow.restore();}
	this.divHeader.imgMaximize.alt = "Restore Down"; //only set 'alt' to make it validate, 'alt' does not serve any purpose
	this.divHeader.imgMaximize.title = "Restore Down"; //title works also in FF (and have priority in IE)
	
	this.divWindow.style.zIndex = ++_kmWindow.zIndex;
	this.disableDragging();

	this.state = _kmWindow.state.maximized;
	this.savedPosition.state = this.state;
	this.save();
}

KMWindow.prototype.restore = function(){
	this.divWindow.style.top = this.savedPosition.top + "px";
	this.divWindow.style.left = this.savedPosition.left + "px";
	this.divWindow.style.height = this.savedPosition.height + "px";
	this.divWindow.style.width = this.savedPosition.width + "px";
	
	if (this.state == _kmWindow.state.minimized){
		this.divHeader.imgMinimize.src = _kmDashboard.urlImages("minimize_reg.png");
		this.divHeader.imgMinimize.onmouseover = function(){this.src = _kmDashboard.urlImages("minimize_hot.png");}
		this.divHeader.imgMinimize.onmouseout = function(){this.src = _kmDashboard.urlImages("minimize_reg.png");}
		this.divHeader.imgMinimize.onclick = function(){this.kmWindow.minimize();}
		this.divHeader.imgMinimize.alt = "Minimize";
		this.divHeader.imgMinimize.title = "Minimize";
	}
	else if (this.state == _kmWindow.state.maximized){
		this.enableDragging();
	}

	this.divHeader.imgMaximize.src = _kmDashboard.urlImages("maximize_reg.png");
	this.divHeader.imgMaximize.onmouseover = function(){this.src = _kmDashboard.urlImages("maximize_hot.png");}
	this.divHeader.imgMaximize.onmouseout = function(){this.src = _kmDashboard.urlImages("maximize_reg.png");}
	this.divHeader.imgMaximize.onclick = function(){this.kmWindow.maximize();}
	this.divHeader.imgMaximize.alt = "Maximize";
	this.divHeader.imgMaximize.title = "Maximize";
	
	this.divWindow.style.zIndex = ++_kmWindow.zIndex;

	this.state = _kmWindow.state.restored;
	this.savedPosition.state = this.state;
	this.save();
}

KMWindow.prototype.close = function(){
	var kmWindow = this;
	var ulSidePanel = this.dashboard.ulSidePanel;
	if (!this.ulSidePanel){
		this.liSidePanel = document.createElement("li");ulSidePanel.appendChild(this.liSidePanel);
		this.liSidePanel.className = "reg";
		this.liSidePanel.title = this.title;
		this.liSidePanel.onmouseover = function(){this.className = "hot";}
		this.liSidePanel.onmouseout = function(){this.className = "reg";}
		this.liSidePanel.onclick = function(){kmWindow.open();}
	}
	setTitleVertical();
	
	this.divWindow.style.display = "none";
	this.state = _kmWindow.state.closed; //then setting state to closed it should not be persisted to savedPosition, since savedPosition holds the state to revert to then recover from closed
	this.save();
	
	function setTitleVertical(){
		for (var c = 0; c < kmWindow.liSidePanel.childNodes.length; c++){kmWindow.liSidePanel.childNodes[c].removeChild(kmWindow.liSidePanel.childNodes[c]);}
		var button_title = kmWindow.title.length > 8 ? kmWindow.title.substring(0, 6) : kmWindow.title;
		for (var c = 0; c < button_title.length; c++){
			var chr = button_title.substring(c, c+1);
			var span = document.createElement("span");kmWindow.liSidePanel.appendChild(span);
			span.appendChild(document.createTextNode(chr));
		}
	}
}

KMWindow.prototype.open = function(){
	var ulSidePanel = this.dashboard.ulSidePanel;
	if (this.liSidePanel){ //don't try to open a window that have not been closed
		ulSidePanel.removeChild(this.liSidePanel);
		this.divWindow.style.display = "block";
		this.state = (this.savedPosition.state == _kmWindow.state.closed) ? _kmWindow.state.restored : this.savedPosition.state; //in case the window was closed from the database, the window state should change to restored
		this.divWindow.style.zIndex = ++_kmWindow.zIndex;
	}
	this.save();
}

