

/*

A windowing system in javascript.
Author: Robert Flack
Alpha release - Mar 28

Include this script and create a new instance of JSwindowGUI. Make sure to call initialize before creation of any windows.
i.e.
wgui = new JSwindowGUI();
wgui.initialize();
wgui.createWindow("This is a test","Test Window");
*/

var wgui;


function JSwindowGUI()
{
	/**
	The window stack is initially empty, and is later filled with all
	created windows. This stack is used to relocate windows if the
	container object is lost, and also to z-order all of the windows
	appropriately when one window receives focus.
	*/
	this.windowstack=null;

	/**
	The window GUI container object is the object to which all created
	window objects will be appended. Typically document.body should work
	unless you have specific needs.
	*/
	this.container = document.body;

	/**
	The window GUI handler object is the object which will receive handler
	methods for mousemove to handle dragging and resizing the windows. As
	document.body only covers the actual used portion of the current page,
	I use document instead which covers all the available space.
	*/
	this.handler = document;

	/** The default amount of padding inside a window's content layer. */
	this.defaultPadding=5;

	/** 
	maxzindex should be higher than the z-indexes of all objects you
	want to see these windows in front of. Also, this must be high enough
	to support the number of windows you expect to have, as each window
	receives a z-index smaller and smaller than this the more windows
	there are.
	*/
	this.maxzindex=500;
	this.minzindex=300;
	
	/* The minimum height and width of a window. Cannot be scaled below
	this size. */
	this.minwidth=120;
	this.minheight=100;

	/** This is the class name that will be applied to all windows */
	this.windowClass='JavascriptWindow';
	
	this.cellsizes=new Array();
	this.getDocumentObject=function()
	{
		if (document.documentElement && document.documentElement.scrollTop)
			return document.documentElement;
		else
			return document.body;
	};
	
	/* Useful for positioning windows in center of screen (works in IE + Firefox and others) */
	this.findScrollTop=function()
	{
		return this.getDocumentObject().scrollTop;
	};

	this.findScrollLeft=function()
	{
		return this.getDocumentObject().scrollLeft;
	};
	
	this.findScrollWidth=function()
	{
		if (document.documentElement && document.documentElement.scrollWidth)
			return document.documentElement.scrollWidth;
		else
			return document.body.scrollWidth;
	};

	this.findScrollHeight=function()
	{
		if (document.documentElement && document.documentElement.scrollHeight)
			return document.documentElement.scrollHeight;
		else
			return document.body.scrollHeight;
	};
this.findWidth=function()
	{
		if (self.innerWidth)
			return self.innerWidth;
		else if (document.documentElement && document.documentElement.clientWidth)
			return document.documentElement.clientWidth;
		else
			return document.body.clientWidth;
	};

	this.findHeight=function()
	{
		if (self.innerHeight)
			return self.innerHeight;
		else if (document.documentElement && document.documentElement.clientHeight)
			return document.documentElement.clientHeight;
		else
			return document.body.clientHeight;
	};

	this.findCenterX=function()
	{
		return this.findScrollLeft()+Math.round(this.findWidth()/2);
	};

	this.findCenterY=function()
	{
		return this.findScrollTop()+Math.round(this.findHeight()/2);
	};

	/** This is a "skin" object for the windows. You can create an
	instance of this and modify the parameters, create your own, or modify
	this one to customize the appearance of the windows.
	
	@param skinpath		The path containing all of the images for this
				skin. Also an easy way to use the same skin
				object for different looks.
	*/

	this.JSwindowGUISkin=function(skinpath)
	{
//		this.modalbg=skinpath+'noisebg.gif';
		this.modalbg=skinpath+'ditherbg.gif';
		this.bgcolor='#666';
		this.bgopacity=0.5;
		this.borderleft=3;
		this.bordertop=24;
		this.borderright=9;
		this.borderbottom=9;
		this.cellbg=new Array();
		for (i=0;i<3;i++)
		{
			this.cellbg[i]=new Array();
			for (j=0;j<3;j++)
				this.cellbg[i][j]=null;
		}
		this.cellbg[1][1]='#fff';
		this.cellimg=new Array();
		this.cellimg[0]=new Array();
		this.cellimg[1]=new Array();
		this.cellimg[2]=new Array();
		this.cellimg[0][0]=skinpath+'tl.gif';
		this.cellimg[0][1]=skinpath+'t.gif';
		this.cellimg[0][2]=skinpath+'tr.gif';
		this.cellimg[1][0]=skinpath+'l.gif';
		this.cellimg[1][1]=null;
		this.cellimg[1][2]=skinpath+'r.gif';
		this.cellimg[2][0]=skinpath+'bl.gif';
		this.cellimg[2][1]=skinpath+'b.gif';
		this.cellimg[2][2]=skinpath+'br.gif';
		this.closeimg=skinpath+'X.gif';
		this.closeimgdepressed=skinpath+'X2.gif';
		this.closeimgover=skinpath+'X3.gif';
		this.closewidth=this.closeheight=15;
		this.closemargintop=5;
		this.closemarginright=8;
		
		this.titlecolour='#fff';
		this.titleweight='bold';
		this.titlemarginleft=10;
		this.titlemargintop=5;
		this.titlealign='left';
		this.titleheight=17;
		
		this.iframeLessX=3;
		this.iframeLessY=3;
	};
	
	this.windowskin=new this.JSwindowGUISkin('/images/windowskin/newskin/');
	
	/** Browser check for certain functionalities */
	this.IE = document.all?true:false;

	/** Sets the z-index of all windows according to their position on
	the stack. */
	this.setZOrder = function()
	{
          var curw=this.windowstack;
          var curz=this.maxzindex;
          while (curw!=null)
          {
                 curw.style.zIndex=curz;
		 if (curz>this.minzindex)
	                 curz--;
                 curw=curw.next;
          }
	}
	
	/**
	If for some reason your container object is removed or the windows
	become detached from it you can call this function to reattach all
	windows to the container object.
	*/
	this.reattachWindows = function()
	{
		var curw;
		curw=this.windowstack;
		while (curw!=null)
		{
			this.container.appendChild(curw);
			curw=curw.next;
		}
	}
	
	/** This function is useful in scripts to obtain the window containing
	any piece of code. I.e. in javascript you may wish to know information
	that is stored relative to the window or modify the window's properties
	*/
	this.findParentWindow = function(elem)
	{
		var e=elem.parentNode;
		while (e!=null && e.className!=this.windowClass)
		{
			e=e.parentNode;
		}
		return e;
	}
	
	/**
	Creates a window and returns a handler to the newly created window. By
	default, the window will appear centered on the screen.

	@param windowcontents	The innerHTML value for the window's contents
	@param wtitle		The title to appear on the window
	@param wname		A textual name for the window that must be
				unique. If a window exists with this name its
				handle will be returned instead.
	@param wwidth		The width for the new window
	@param wheight		The height for the new window
	@param modal		Is this a modal window (background is grayed
				out and only this window can receive events.
	@return			A new window (div element) with the specified
				attributes
	*/
	this.createWindow = function(windowcontents,wtitle,wname,wwidth,wheight,modal)
	{
		/* kindly alert the programmer if they have forgotten to
		initialize this object */
		if (!this.initialized)
		{
			alert ("Please initialize JSwindowGUI before creating windows");
			return false;
		}
		var curw;

		/* Search for an existing window with the specified name. */
		if (wname !=null && wname!='')
		{
			curw=this.windowstack;
			while (curw!=null)
			{
				if (curw.name==wname)
				{
					curw.moveToFront();
					return curw;
				}
				curw=curw.next;
			}
		}
		var curtr;

		/* Create the div element for the window */
		curw=document.createElement('div');
		curw.className=this.windowClass;
		curw.name=wname;
		curw.minwidth=this.minwidth;
		curw.minheight=this.minheight;
		curw.style.position='absolute';
		curw.style.display='block';
		curw.windowgui=this;
		
		if (modal)
		{
			var bkgw,bkgh;
			curw.modal=true;
			curw.background=document.createElement('div');
			curw.background.style.position='absolute';
			if (this.windowskin.bgcolor)
				curw.background.style.background=this.windowskin.bgcolor;
			if (this.windowskin.modalbg)
				curw.background.style.backgroundImage='url("'+this.windowskin.modalbg+'")';
			curw.background.style.top='-50px';
			curw.background.style.left='-50px';
			curw.background.style.zIndex=this.maxzindex-1;
			curw.background.style.opacity=this.windowskin.bgopacity;
			curw.background.style.mozOpacity=this.windowskin.bgopacity;
			curw.background.style.filter='alpha(opacity='+(this.windowskin.bgopacity*100)+')';
			if (this.findScrollWidth()>this.findWidth())
				bkgw=this.findScrollWidth();
			else
				bkgw=this.findWidth();
			if (this.findScrollHeight()>this.findHeight())
				bkgh=this.findScrollHeight();
			else
				bkgh=this.findHeight();
			curw.background.style.width=(bkgw+50)+'px';
			curw.background.style.height=(bkgh+50)+'px';
			document.body.insertBefore(curw.background,document.body.childNodes[0]);
		}
		
		/* Internet Explorer does not properly support the z-index
		property: for some reason select input's appear above
		everything except for iframes. Thus we can use an iframe
		(which does happen to respect z-index) to hide any dropdown
		lists behind the window. */
		if (this.IE)
		{
			curw.iframe=document.createElement('iframe');
			this.container.appendChild(curw.iframe);
			curw.iframe.style.position='absolute';
			curw.iframe.lessX=this.windowskin.iframeLessX;
			curw.iframe.lessY=this.windowskin.iframeLessY;
		}
//		curw.onselectstart=function(){return false;};

		/* The skin of the window is accomplished with a table */
		curw.windowtable=document.createElement('table');
		curw.windowtable.cellSpacing=0;
		curw.windowtable.tbody=document.createElement('tbody');
		curw.windowtable.appendChild(curw.windowtable.tbody);

		curw.windowtable.tcells=new Array();
		for (i=0;i<3;i++)
		{
			curw.windowtable.tcells[i]=new Array();
			curtr=document.createElement('tr');
			for (j=0;j<3;j++)
			{
				curw.windowtable.tcells[i][j]=document.createElement('td');
				curw.style.overflow='hidden';
				if (this.windowskin.cellimg[i][j]!=null)
				{
					curw.windowtable.tcells[i][j].style.backgroundImage="url('"+this.windowskin.cellimg[i][j]+"')";
				}
				if (this.windowskin.cellbg[i][j]!=null)
				{
					curw.windowtable.tcells[i][j].style.background=this.windowskin.cellbg[i][j];
				}
				curw.windowtable.tcells[i][j].guiwindow=curw;
				curtr.appendChild(curw.windowtable.tcells[i][j]);
				if (i!=1 || j!=1)
					curw.windowtable.tcells[i][j].onselectstart=function(){return false;};
				if (j==0)
					curw.windowtable.tcells[i][j].style.width=this.windowskin.borderleft+'px';
				if (j==2)
					curw.windowtable.tcells[i][j].style.width=this.windowskin.borderright+'px';
				if (i==0)
				{
					curw.windowtable.tcells[i][j].style.height=this.windowskin.bordertop+'px';
					curw.windowtable.tcells[i][j].onmousedown=function(e)
					{
						if (this.guiwindow.mouseaction==-1)
							this.guiwindow.mouseaction=0;
						else
							this.guiwindow.mouseaction=1;
						this.guiwindow.onmousedown(e);
						return false;
					}
				}
				if (i==2)
					curw.windowtable.tcells[i][j].style.height=this.windowskin.borderbottom+'px';					
			}
			curw.windowtable.tbody.appendChild(curtr);
		}
		
		// Create the close button
			
		curw.closebutton=document.createElement('div');
		curw.closebutton.style.position='absolute';
		curw.closebutton.style.width=this.windowskin.closewidth+'px';
		curw.closebutton.style.height=this.windowskin.closeheight+'px';
		curw.closebutton.style.backgroundRepeat="no-repeat";
		curw.closebutton.style.backgroundImage="url('"+this.windowskin.closeimg+"')";
		curw.closebutton.img1=this.windowskin.closeimg;
		curw.closebutton.img2=this.windowskin.closeimgdepressed;
		curw.closebutton.img3=this.windowskin.closeimgover;
		curw.closebutton.mright=(this.windowskin.closemarginright+this.windowskin.closewidth);
		curw.closebutton.style.top=this.windowskin.closemargintop+'px';
//		curw.closebutton.style.styleFloat=curw.closebutton.style.cssFloat='right';
		curw.closebutton.guiwindow=curw;
		curw.closebutton.onmousedown=function()
		{
			this.style.backgroundImage="url('"+this.img2+"')";
			this.guiwindow.mouseaction=-1;
			return false;
		}
		curw.closebutton.onmouseover=function()
		{
			this.style.backgroundImage="url('"+this.img3+"')";
		}
		curw.closebutton.onmouseup=curw.closebutton.onmouseout=function()
		{
			this.style.backgroundImage="url('"+this.img1+"')";
		}
		curw.closebutton.onclick=function(){
			this.guiwindow.close();
		}
		curw.windowtable.tcells[0][1].appendChild(curw.closebutton);
		
		// Create the window title
		
		curw.title=wtitle;
		curw.titlelabel=document.createElement('div');
		curw.titlelabel.style.position='absolute';
		curw.titlelabel.style.color=this.windowskin.titlecolour;
		curw.titlelabel.style.fontWeight=this.windowskin.titleweight;
		curw.titlelabel.style.display='block';
		curw.titlelabel.lessX=this.windowskin.borderleft+this.windowskin.borderright+this.windowskin.closewidth+this.windowskin.closemarginright+this.windowskin.titlemarginleft;
		if (this.IE)
			curw.titlelabel.lessX+=this.windowskin.titlemarginleft;
		curw.titlelabel.style.left=this.windowskin.titlemarginleft+'px';
		curw.titlelabel.style.top=this.windowskin.titlemargintop+'px';
/*		if (this.IE)
			curw.titlelabel.style.height=(this.windowskin.titleheight-this.windowskin.titlemargintop)+'px';
		else */
			curw.titlelabel.style.height=this.windowskin.titleheight+'px';
		
		curw.titlelabel.style.textAlign=this.windowskin.titlealign;
		curw.titlelabel.style.cursor='default';
		curw.titlelabel.style.overflow='hidden';
		curw.titlelabel.appendChild(document.createTextNode(wtitle));
		curw.windowtable.tcells[0][1].appendChild(curw.titlelabel);
		
		curw.close=function()
		{
			if (curw.onclose())
			{
				if (this.prev!=null)
					this.prev.next=this.next;
				if (this.next!=null)
					this.next.prev=this.prev;
				if (this==this.windowgui.windowstack)
				{
					this.windowgui.windowstack=this.next;
				}
				if (this.iframe)
					this.windowgui.container.removeChild(this.iframe);
				if (this.modal)
					document.body.removeChild(this.background);
				this.windowgui.container.removeChild(this);
				this.closed='true';
			}
		};

		curw.windowtable.tcells[1][2].style.cursor='e-resize';
		curw.windowtable.tcells[1][2].onmousedown=function(e)
		{
			this.guiwindow.mouseaction=2;
			this.guiwindow.onmousedown(e);
			return false;			
		};
		
		curw.windowtable.tcells[2][0].style.cursor=curw.windowtable.tcells[2][1].style.cursor='s-resize';
		curw.windowtable.tcells[2][0].onmousedown=curw.windowtable.tcells[2][1].onmousedown=function(e)
		{
			this.guiwindow.mouseaction=3;
			this.guiwindow.onmousedown(e);
			return false;			
		}
		
		curw.windowtable.tcells[2][2].style.cursor='se-resize';
		curw.windowtable.tcells[2][2].onmousedown=function(e)
		{
			this.guiwindow.mouseaction=4;
			this.guiwindow.onmousedown(e);
			return false;
		}

		/* Construct the content element */
		curw.content=curw.windowtable.tcells[1][1];
		curw.content.lessX = this.windowskin.borderleft+this.windowskin.borderright;
		curw.content.lessY = this.windowskin.bordertop+this.windowskin.borderbottom;
		curw.content.divlayer = document.createElement('div');
		curw.content.divlayer.style.overflow='auto';
		curw.content.divlayer.style.textAlign='left';
		curw.content.divlayer.guiwindow=curw;
		curw.innerPadding=this.defaultPadding;

		curw.content.divlayer.innerHTML=windowcontents;
		curw.content.appendChild(curw.content.divlayer);

		curw.appendChild(curw.windowtable);

		/* Override this function to provide functionality when an
		ajax request completes and the page has loaded into the window
		*/
		curw.ondisplaypage=function()
		{
			return true;
		}
		
		/* To simplify loading dynamic content into the windows, this
		function can serve as a listener for Ajax requests and will
		display the returned information as HTML content. */
		curw.ajaxlistener=function(ajaxobj,wnd)
		{
			var loadstr;
			var finalpage=0;
			switch (ajaxobj.readyState)
			{
				case 0:
					loadstr="Initialized...";
					break;
				case 1:
					loadstr="Connecting...";
					break;
				case 2:
					loadstr="Connected...";
					break;
				case 3:
					loadstr="Receiving Data...";
					break;
				case 4:
					if (ajaxobj.status==200)
					{
						finalpage=1;
						loadstr=ajaxobj.responseText;
						var bodypos,bodyend,endbodypos;
						var bodypos=loadstr.toLowerCase().indexOf('<body');
						if (bodypos>=0)
						{
							bodyend=loadstr.indexOf('>',bodypos);
							endbodypos=loadstr.toLowerCase().indexOf('</body>',bodyend);
							loadstr=loadstr.substring(bodyend+1,endbodypos);
						}
					}else{
						loadstr=ajaxobj.statusText;
					}
					break;
			}
			wnd.content.divlayer.innerHTML=loadstr;
			if (finalpage==1) wnd.ondisplaypage();
			return true;
		}
		
		/* Everytime properties are changed, this function must be
		called to update the physical div's position with respect
		to the specified values. */
		curw.updateposition=function()
		{
			this.style.top=this.y+'px';
			this.style.left=this.x+'px';			
			if (this.iframe)
			{
				this.iframe.style.top=this.y+'px';
				this.iframe.style.left=this.x+'px';
			}
		}
		
		/* When the properties are changed for the window, call this
		funciton which will update all of the window's elements'
		positions and sizes. */
		curw.update=function()
		{
			this.style.top=this.y+'px';
			this.style.left=this.x+'px';
			this.style.width=this.width+'px';
			this.style.height=this.height+'px';
			this.content.divlayer.style.width=(this.width-this.content.lessX-2*this.innerPadding)+'px';
			this.content.style.width=(this.width-this.content.lessX)+'px';
			this.content.divlayer.style.height=(this.height-this.content.lessY-2*this.innerPadding)+'px';
			this.content.style.height=(this.height-this.content.lessY)+'px';
			this.content.divlayer.style.padding=this.innerPadding+'px';
			this.titlelabel.style.width=(this.width-this.titlelabel.lessX)+'px';
			this.closebutton.style.left=(this.width-this.closebutton.mright)+'px';
			if (this.iframe)
			{
				this.iframe.style.top=this.y+'px';
				this.iframe.style.left=this.x+'px';
				this.iframe.style.width=(this.width-this.iframe.lessX)+'px';
				this.iframe.style.height=(this.height-this.iframe.lessY)+'px';
			}
		}
		
		/* Default width and height for a window */
		if (wwidth==null)
			curw.width=300;
		else
			curw.width=wwidth;
		if (wheight==null)
			curw.height=300;
		else
			curw.height=wheight;
			
		curw.windowgui=this;
		curw.mouseaction=0;

		curw.getcoords=function(e)
		{
			if (this.windowgui.IE) { // grab the x-y pos.s if browser is IE
			this.mouseX = event.clientX + document.body.scrollLeft;
			this.mouseY = event.clientY + document.body.scrollTop;
			}
			else {  // grab the x-y pos.s if browser is NS
			this.mouseX = e.pageX;
			this.mouseY = e.pageY;
			}
		}

		/* Move this window to the front of the stack, and z-order */
		curw.moveToFront=function()
		{
			// Move this window to the front
			if (this!=this.windowgui.windowstack)
			{
				if (this.prev!=null)
					this.prev.next=this.next;
				if (this.next!=null)
					this.next.prev=this.prev;
				
				this.next=this.windowgui.windowstack;
				this.next.prev=this;
				this.prev=null;
				this.windowgui.windowstack=this;
			}
			// Step through and apply z-indexes
			this.windowgui.setZOrder();
		}
		
		/* If the user clicks within the window, it should receive
		focus. Also, remember the cursor's position in case the user
		is dragging the title bar or one of the resizing handles */
		curw.onmousedown=function(e)
		{
			this.getcoords(e);
			this.windowgui.handler.selwindow=this;
			this.diffX=this.mouseX-this.x;
			this.diffY=this.mouseY-this.y;
			this.diffSX=this.mouseX-this.width;
			this.diffSY=this.mouseY-this.height;
			this.moveToFront();
			
			this.windowgui.handler.onselectstart=function(){return false;}
			this.onfocus();
		}
		
		/* Attempt to center the window on the page. Depends heavily
		on the browser supported functions. */
		curw.center=function()
		{	
			this.x=this.windowgui.findCenterX()-Math.round(curw.width/2);
			this.y=this.windowgui.findCenterY()-Math.round(curw.height/2);
			this.update();
		}
		
		curw.center();
		
		this.container.appendChild(curw);
		if (this.windowstack==null)
		{
			curw.next=null;
			curw.prev=null;
			this.windowstack=curw;
		}else{
			curw.next=this.windowstack;
			this.windowstack.prev=curw;
			this.windowstack=curw;
		}
		this.setZOrder();
		curw.onresize=function(){return true;};	// Event handler for window resize
		curw.onclose=function(){return true;};	// Event handler for closing window
		curw.onfocus=function(){return true;};	// Event handler called when window receives focus
		return curw;
	}
	
	/* Initialize the windowGUI object by preparing the handler object
	with the necessary functions to respond to window events */
	this.initialize=function()
	{
		if (!(document && document.createElement && document.createTextNode)) return false;
		this.container.windowgui=this;
		this.handler.selwindow=null;
		this.handler.onmousemove=function(e)
		{
			if (this.selwindow!=null)
			{
				var curw=this.selwindow;
			
				if (curw.mouseaction==1)	// Currently moving window
				{
					curw.getcoords(e);
					curw.x=curw.mouseX-curw.diffX;
					curw.y=curw.mouseY-curw.diffY;
					if (curw.x<0) curw.x=0;
					if (curw.y<0) curw.y=0;
					curw.updateposition();
				}else if (curw.mouseaction>0 && curw.mouseaction<=4)	// Resize window
				{
					curw.getcoords(e);
					if (curw.mouseaction==2 || curw.mouseaction==4)
						curw.width=curw.mouseX-curw.diffSX;
					if (curw.mouseaction>=3)
						curw.height=curw.mouseY-curw.diffSY;
					if (curw.width<curw.minwidth) curw.width=curw.minwidth;
					if (curw.height<curw.minheight) curw.height=curw.minheight;
					curw.update();
					curw.onresize();
				}
			}
		}
		this.handler.onmouseup=function()
		{
			if (this.selwindow!=null)
			{
				var curw=this.selwindow;
				curw.mouseaction=0;
				this.onselectstart=function(){return true;}
			}
		}
		this.initialized=true;
		return this;
	}
};

function SetupWindowGUI()
{
	wgui = new JSwindowGUI();
	wgui.initialize();
}

if (window.attachEvent) {window.attachEvent('onload', SetupWindowGUI);}
else if (window.addEventListener) {window.addEventListener("load", SetupWindowGUI, false);}
else {window.onload = SetupWindowGUI;}

