/////////////////////////////////////////////
// Javascript library, for "Adventuring"   //
// Currently Version 2.1                   //
//                                         //
// Gump system, in javascript.             //
//                                         //
// - some files are external               //
//       progressbars.js                   //
//                                         //
/////////////////////////////////////////////

////////////////
// Changelog  //
////////////////

// (most recent)
//	Promoted options panel (opacity sliders) to main library.
//	Speedup code is all in, and working - though it doesn't make things seem any better
//	Made a big chunk of the speedup code (panelevent.js)
//	Remove 'loading' background onLoad and put it back on fail to load
//	Fixed bug 103
//	Fixed bug 108
//	findposx / findposy (from quirksmode.org)
//	Added options panel test
//	Added sliders control
//	Fixed bug 112
// V2.1 checkpointed
//	Opacity can be varied at runtime (see bug 112 though)
//	Fixed bug 105
//	Changed scrolling to use new event model	(see bug 101 though)
//	Event logging (See bugs 108 and 109 though)
//	Fixed bug 102
//	Addition of logging messages, as well as Debug
//	Support for opacity of panels (on mouse over)
//	Support for panels created by javascript as long as it's done before the document.onLoad event (see bug 110)
//	Support for creations of progress bars (same restriction) (same bug)
//	Proper time ticks (unlimited number of timers)
//	Organised document into sections.  Progress bars split out into own file. (ongoing, see bug 106)
// V2 released
//	Panels, all main functionality there
//	Progress bars - don't yet show percentage
// V1 Released
// (started keeping a changelog)	

// Known Outstanding Bugs
//	101	When you resize a scrolling panel, the position moves quite a lot - and unpredictably.
// 	106	Should split out bits of this file for different classes of things, rather than having one monolithic file
// 	109	Event module bug: If an event wraps round time and ends up at T=0, then it will be terminated
//	    ... and due to the round numbers involved, this will happen EVERY time period.
//	110	Really ought to allow dynamic creation, using the DOM or otherwise
//	113	Need a way to notify back to user app when a scroll completes.
//	116	TODO: 'only-one' event (eg. redraw) a second event of the same time doens't add a new slot if one already exists
//	117	Performance is terrible... Improve, urgently.
//	118	TODO: Find a way to constrain things into a smaller window.  (An IFRAME perhaps?)

// Old bugs (in case of regression, smash head into desk)
//	102	FIXED		No need to use innerHTML for img - simply replace with 'loading' image and THEN replace with target image   (Works better that way, anyway)
//	105	FIXED		Minimise then drag out should unhide the content (now does so)
// 	111	WORKAROUND	Make translucency a switchable option rather than always happening (setting CFG_Panel_MinOpacity and CFG_Panel_MaxOpacity to the same value will do this)
//	114	FIXED		IE Bug - the image panel starts up too small (set 'overflow=hidden')
// 	115	WORKAROUND	IE Bug, box model - the outer div needs sizing up a few pixels in each direction (see 114)
// 	108	FIXED		Event module bug: I have seen an erroneous event in the queue - no notify, but a timeslot set. (was a real logic bug.  Fixed it in Event_Tick.)
//	103	FIXED		Add "+substyle+" to button src attributes (for skinning)?  (easily enough acheived)
//	112	FIXED		No way to make global opacity changes take effect straight away (in general - no global list of panels) (added such a list)

// And stuff I'm not going to do
//	104	POSTPONED	Consider using +skin+ as well/instead? (forget about it - substyle is sufficient)
// 	107	POSTPONED	Add different Various debugging levels / debug per module? (Don't think I actually need this, now I have logging as well as alerts)


///////////////////////////
// Functionality control //
///////////////////////////

var DISABLE_PANEL_STRETCHING = false;
var DISABLE_PANEL_MOVING = false;
var DISABLE_PANEL_OPACITY = false;
var DISABLE_PANELEVENT = true;	// PanelEvent collapses redraw type event down.
var DISABLE_PROGRESS_BARS = false;
var DISABLE_CONTROL_SLIDERS = false;

var DEBUGGING_ENABLED = false;
var LOGGING_ENABLED = true;

var CFG_LOGGING_Logfile = 'logfile';
var CFG_LOGGING_EVENTS = false;

var CFG_Panel_MinOpacity = 0.4;		// Gump opacity - probably set via options panel
var CFG_Panel_MaxOpacity = 1;
var CFG_Panel_LoadingImageURL = "Loading.gif";	// The default 'I have no image' image.
var CFG_Panel_MinStretchWidth = 0;		// When resizing a panel, stop it getting too small to be able to see the title.
var CFG_Panel_MinStretchHeight = 0;

var CFG_Event_MaxTime = 2* 24 * 60 * 60 * 1000;		// 2 days
var CFG_Event_Interval = 100;				// TODO: Is 5fps reasonable?

var CFG_Progress_TickSize = 2;		// How far to move each frame


/////////////////
// Real Core   //
/////////////////

// These are used to track WHICH action is in progress - since we now use a single event handler
// and dispatch to the different routines.
var Panel_CurrentAction = "None";
var Panel_CurrentTarget = null;

// This should be attached to document.onMouseMove
function Panel_MouseMove(evt)
{
	// Note that target is not actually used.

	if ( Panel_CurrentAction == "Moving" )
		Panel_ToMoveWhilstMoving(evt,Panel_CurrentTarget);
	else if ( Panel_CurrentAction == "Sizing" )
		Panel_ToMoveWhilstResizing(evt,Panel_CurrentTarget);

// else undefined (maybe no) motion - do NOTHING

}

// This should be attached to document.onClick
function Panel_MouseClick(evt)
{
	Panel_UnlockButtons();
}

// You should probably call this in the onLoad of the document <BODY> tag
function CORE_Init()
{
	Event_Init();
}

function findPosX(obj)
  {
    var curleft = 0;
    if(obj.offsetParent)
        while(1)
        {
          curleft += obj.offsetLeft;
          if(!obj.offsetParent)
            break;
          obj = obj.offsetParent;
        }
    else if(obj.x)
        curleft += obj.x;
    return curleft;
}
function findPosY(obj)
  {
    var curtop = 0;
    if(obj.offsetParent)
        while(1)
        {
          curtop += obj.offsetTop;
          if(!obj.offsetParent)
            break;
          obj = obj.offsetParent;
        }
    else if(obj.y)
        curtop += obj.y;
    return curtop;
}


////////////////////
// Event Handling //
////////////////////

var Event_Initialised = false;	// Has the Event module been init'd?
var Event_Interval;			// This is the actual javascript setInterval
var Event_Time = 0;			// The currnet time (in ms)
var Event_Notification	= null;	// The function array
var Event_TimeSlot	= null;		// the time array
var Event_Recur	= null;		// the 'when does this notification happen again' array
var Event_Alive = true;		// Set this false to turn off events but KEEP TICKING.
					// (ie. events are cleared without activating)
var Event_ThisEvent = 0;		// Functions being called on a tick may need to know which event they are (for example, in order to delete themselves)

// These functions are for convenience, to be as much like javascript set functions as possible
function Event_setInterval( notify, delay )
{
	return Event_Add ( notify, delay, delay );
}

// These functions are for convenience, to be as much like javascript set functions as possible
function Event_setTimeout( notify, delay )
{
	return Event_Add ( notify, delay, false );
}

function Event_FindEmpty ()
{
	var i = 0;
	if ( !Event_TimeSlot )
		return 0;

	while ( Event_TimeSlot[i] && Event_TimeSlot[i] > 0 )
		i++;

	return i;
}

function Event_Init()
{
	// Note: Whilst the 'better' event handler is running,
	// setInterval must NOT be used.

	Event_Time = 0;
	Event_Notification = new Array();
	Event_TimeSlot = new Array();
	Event_Recur = new Array();

	Event_Initialised = true;

	Event_Resume();
}

// NOTE: This function can create notifications which examine their arguments immediately upon creation
// or when they are actually run (USE GLOBALS!)
// The formats are:
// Event_Add ( "Function(argument);", ... )		-- this will evaluate its argument only when the tick times out
// Event_Add ( "Function(" + argument + ");", ... )	-- and this will evaluate it immediately
function Event_Add ( call, delay, recurring )
{
	if ( !delay ) delay = 0;
	if ( !recurring ) recrring = 0;
	if ( !Event_Initialised )
		Event_Init();

//	Debug ( "Event_Add ( call=\""+call+"\", delay="+delay+", recurring="+recurring+" )");

	// This uses the more impressive syntax - so you can call it with any chunk of javascript
	// rather than just being able to call functions with no arguments.
	var freeslot = Event_FindEmpty();
	Event_Notification[freeslot] = call;
	if ( recurring )
		Event_Recur[freeslot] = recurring;
	else
		Event_Recur[freeslot] = 0;
	Event_TimeSlot[freeslot] = (delay + Event_Time) % CFG_Event_MaxTime;

	return freeslot;
}

function Event_Quit()
{
	Event_Pause();

	if ( Event_TimeSlot )
		delete Event_TimeSlot;

	if ( Event_Recur )
		delete Event_TimeSlot;

	if ( Event_Notification )
		delete Event_Notification;
}

function Event_DeleteSelf()
{
	Event_Delete( Event_ThisEvent );
}

function Event_Delete( id )
{
	if ( !Event_TimeSlot || !Event_Recur || !Event_Notification )
		return;
	Event_TimeSlot[id] = 0;
	Event_Recur[id] = 0;
	Event_Notification[id] = ";";
}

function Event_Alert(text)
{
	// This function is required since tick events tend to stack up whilst
	// a user notification is acive.
	Event_Pause();
	alert(text);
	Event_Resume();
}

function Event_Tick()
{
	Event_Time = ( parseInt ( Event_Time ) + CFG_Event_Interval) % CFG_Event_MaxTime;

	if ( !Event_Alive || !Event_TimeSlot || !Event_Recur || !Event_Notification )
		return;
	
	for ( i in Event_TimeSlot )
	{
		if ( Event_TimeSlot[i] < parseInt( Event_Time ) && Event_TimeSlot[i] > 0 && Event_Notification[i]!=null )
		{
			if ( Event_Recur[i] > 0 )
			{
				Event_TimeSlot[i] = (Event_Time + Event_Recur[i]) % CFG_Event_MaxTime;
				// Note that bad BAD things happen if the thing being eval'd lasts long enough that another tick goes off...
				Event_ThisEvent = i;
				if ( CFG_LOGGING_EVENTS ) AppendLog ( "Time: "+Event_Time+"    Event: "+Event_Notification[i]+"<br />" );
				eval ( Event_Notification[i] );
			}
			else
			{
				Event_TimeSlot[i] = 0;
				Event_ThisEvent = i;
				var func = Event_Notification[i];		// Fix for bug 108 - you have to clear it here before you execute it.
				Event_Notification[i] = null;
				eval ( func );
			}
		}
	}
}

function Event_Resume()
{
	Event_Interval = setInterval ( "Event_Tick();", CFG_Event_Interval );
}

function Event_Pause()
{
	clearInterval(Event_Interval);	// Not that there's more than one actual interval allowed.
}


// This prints out a debug message (or doesn't if debug is turned off above)
function Debug(text)
{
	//	DEBUGGING_ENABLED = true;

	if ( DEBUGGING_ENABLED == true )
	{
		Event_Alert(text);
	}
}

// This logs debug to the page.
function AppendLog ( text )
{
	if ( LOGGING_ENABLED == true )
	{
		var logfile = FindElementById ( CFG_LOGGING_Logfile );
		if ( logfile )
		{
			logfile.innerHTML = "<code>" + text + "</code><br />" + logfile.innerHTML;
		}
		else
			Debug ( text );
	}
}

// And this creates a log panel
function DEBUG_CreateLogFile(x,y)
{
	if ( LOGGING_ENABLED == true )
	{
		Panel_StaticCreate ( "holds_the_logfile", x, y, 300, 150, true, true, true, true, true, "", "LOG", "<div class=logfile id=logfile><code>Log begins here.</code></div><a href=\"#\" onClick=\"TEST_DumpEventState();\">Dump Events</a> | <a href=\"#\" onClick=\"Event_Alive = false;\">KILL EVENTS</a>", false );
		// Let the log start minimised
		Div_Resize("holds_the_logfile_content",null,'1px'); 
		Div_SetVisibility("holds_the_logfile_content", 'hidden');
	}
}


////////////////////////
// Browser Detection  //
////////////////////////

// TODO: Expand this to a more complete version
var BROWSER_DETECTION__IS_IE = 0;
if ( navigator.appName.indexOf('Microsoft Internet Explorer') != -1)
	BROWSER_DETECTION__IS_IE = 1;

/////////////////////////
// Panel Functionality //
/////////////////////////

var Panel_FrontIndex = 100;		// When bringing to front, how FAR do you need to bring it?
// This is used to prevent more than one layer responding to a button click
// any click handler should set this, and the body should clear it
var Panel_ButtonInFrontClicked = false;

// This marks that a window drag/resize is in action, and holds offsets used to continue it correctly
var Panel_OffsetX = 0;
var Panel_OffsetY = 0;
// This is the object (NOT THE ID) that is being affectd (as above)
// Panel_ObjMoveOrStretchY!=null is used to test whether an action is in progress or not.
var Panel_ObjMoveOrStretchY = null;
var Panel_ObjStretchX = null;	// Drag is used, ,whilst stretching, for height
				// but because of panel layout, we need a second object for width
				// and stretch is used for that.
// And that's STILL not enough - if a click handler sets the document.onclick then that MAY
// immediately fire (putting the thing you picked up, back down) - to cope, set this Panel_Debounce
// and check it in any document onclick handler.
var Panel_Debounce = false;

var Panel_List = new Array();

function Panel_Add(id)
{
	AppendLog ( "Panel_Add("+id+");" );
	Panel_List[Panel_List.length] = id;
}

function Panel_Remove(id)
{
	for ( i in Panel_List )
	{
		if ( Panel_List[i] == id )
		{
			Panel_List.splice(i, 1 );
		}
	}
}

function Panel_SetAllOpacity ( level )
{
	for ( i in Panel_List )
	{
		var target = Panel_List[i];
		Div_SetOpacity ( target, level );
	}
}

function Panel_Display( target, hide )
{
	var text = "inherit";
	if ( hide )
	{
		text = "hidden";
	}
	Div_SetVisibility( target, text );
}

function Panel_Resize ( target, w, h )
{
	Div_Resize ( target, w, null );
	Div_Resize ( target+"_content", null, h );
}

function Panel_Move ( target, x, y )
{
	Div_Move ( target, x, y );
}

function Panel_StartStopDrag(evt,idToMove)
{
	if ( DISABLE_PANEL_MOVING != false )
		return;

	Debug("Panel_StartStopDrag(evt=" + evt + "idToMove=" + idToMove + ") : BIFC=" + Panel_ButtonInFrontClicked );

	if (Panel_ButtonInFrontClicked)
	{
		// Handled by the button already - next press might go anywhere
	}
	else
	{
		Div_MoveToFront(idToMove);

		Panel_ButtonInFrontClicked = true;

		// Ok - we've been picked up, or put down
		if ( Panel_ObjMoveOrStretchY != null )
		{
			Panel_StopDraggingPanel();
		}
		else
		{
			Debug("Grabbing");
			// Not up, so pick it up.
			Panel_ObjMoveOrStretchY = FindElementById(idToMove);
			if ( Panel_ObjMoveOrStretchY != null )
			{
				Panel_CurrentAction = "Moving";
				Panel_CurrentTarget = idToMove;
				if ( !DISABLE_PANELEVENT )
				{
					PanelEvent_Attach();
				}
				// TODO: Might it be worth catching the escape kay, as well?
				else
					document.onclick = Panel_StopDraggingPanel;

				// AppendLog ( "Mouse at " + evt.clientX + "," + evt.clientY );
				// AppendLog ( "Bar at " + Panel_ObjMoveOrStretchY.style.left + "," + Panel_ObjMoveOrStretchY.style.top );
	// Ok - a problem remains.  We're setting positions as 160px
	// and then we want to treat it as a number.  parseInt fixes this.
				Panel_OffsetX = evt.clientX - parseInt(Panel_ObjMoveOrStretchY.style.left);
				Panel_OffsetY = evt.clientY - parseInt(Panel_ObjMoveOrStretchY.style.top);
				Panel_ObjMoveOrStretchY.style.position = 'absolute';	// Should already be set
				Panel_Debounce = true;
				// AppendLog("Should now be moving, relative " + Panel_OffsetX + "," + Panel_OffsetY);
			}
		}
	}
}

// Put that panel back down
function Panel_StopDraggingPanel()
{
	if ( DISABLE_PANEL_MOVING != false )
		return;

	if ( Panel_Debounce )
	{

		Panel_Debounce = false;
		return;
	}
	Debug("Panel_StopDraggingPanel()");
	// Were up, put us down - nothing to do, except to stop tracking the mouse.
	if ( Panel_ObjMoveOrStretchY != null )
	{
		Panel_CurrentAction = "None";
		Panel_CurrentTarget = null;

		if ( !DISABLE_PANELEVENT )
		{
			PanelEvent_Detach();
		}
		else
			document.onlick = null;

		Panel_ObjMoveOrStretchY = null;		// Panel_ObjMoveOrStretchY null is used to mark 'no action'
	}
}

// Start stretching the panel

// Something a bit funny here - IE will let you downsize the width of the titlebar
//       too far - though it caps the whole size.  Not sure WHAT is going on.
//       But it does keep everything that needs to be within the bar, and caps it to that size
//       - so we're probably ok.
//       Leaving some leeway (200px instead of 150) seems to make it all ok.
function Panel_StartStretchingPanel(evt, idToSizeHorizontal, idToSizeVertical, minWidth, minHeight )
{
	if ( DISABLE_PANEL_STRETCHING != false )
		return;

	if (Panel_ButtonInFrontClicked)
	{
		// Handled by the button already - next press might go anywhere
	}
	else
	{
            // This fixes a silly: When you hit minimise then stretch it visible again we DO want to be able to see it.
            Div_SetVisibility(idToSizeVertical, 'inherit'); 

            Debug ( "Panel_StartStretchingPanel(evt=" + evt + ", idToSizeVertical=" + idToSizeVertical + ", idToSizeHorizontal=" + idToSizeHorizontal + " ) : BIFC=" + Panel_ButtonInFrontClicked );

		Div_MoveToFront(idToSizeHorizontal);

		Panel_ButtonInFrontClicked = true;
		// Ok - we've been picked up, or put down
		if ( Panel_ObjMoveOrStretchY != null )
		{
			Panel_StopStretching();
		}
		else
		{
			// Default minimum sizes for panels
			if ( minWidth < 1 )  minWidth  = 200;
			if ( minHeight < 1 ) minHeight = 1;
			CFG_Panel_MinStretchWidth  = minWidth;
			CFG_Panel_MinStretchHeight = minHeight;

			Panel_ObjStretchX = FindElementById(idToSizeHorizontal);
			if ( Panel_ObjStretchX != null )
			{
				Panel_CurrentAction = "Sizing";
				Panel_CurrentTarget = idToSizeHorizontal;

				if ( !DISABLE_PANELEVENT )
				{
					PanelEvent_Attach();
				}
				else
				{
					AppendLog("Doc onclick= stopstretch");
					document.onclick = Panel_StopStretching;
				}

				Debug ( "Horizontal: Mouse at " + evt.clientX + "," + evt.clientY );
				Debug ( "Sized " + Panel_ObjStretchX.style.width );

				Panel_OffsetX = parseInt(Panel_ObjStretchX.style.width)  - evt.clientX;
				Panel_Debounce = true;

				Debug("Should now be moving, relative " + Panel_OffsetX + "," + Panel_OffsetY);
			}
			Panel_ObjMoveOrStretchY = FindElementById(idToSizeVertical);
			if ( Panel_ObjMoveOrStretchY != null )
			{
				Panel_CurrentAction = "Sizing";
				Panel_CurrentTarget = idToSizeVertical;

				Debug ( "Vertical: Mouse at " + evt.clientX + "," + evt.clientY );
				Debug ( "Sized " + Panel_ObjMoveOrStretchY.style.height );

				Panel_OffsetY = parseInt(Panel_ObjMoveOrStretchY.style.height) - evt.clientY;
				Panel_Debounce = true;
// document.onmousemove doesn't seem to WORK for firefox.  Argh.
// .. .is it possible that FireFox doesn't allow you to dynamically assign handler?  Argh.


				if ( !DISABLE_PANELEVENT )
				{
					PanelEvent_Attach();
				}
				else
				{
					AppendLog("Doc onclick= stopstretch");
					document.onclick = Panel_StopStretching;
				}

	// TODO: It might be worth having 'escape' put down the bar too?
				Debug("Should now be moving, relative " + Panel_OffsetX + "," + Panel_OffsetY);
			}
		}
	}
}

// Stop the resize
function Panel_StopStretching()
{
	//AppendLog("Stop stretch");

	if ( DISABLE_PANEL_STRETCHING != false )
		return;

	if ( Panel_Debounce )
	{
		Panel_Debounce = false;
		return;
	}
	Debug ( "Panel_StopStretching()" );
	// Were up, put us down - nothing to do, except to stop trakcing the mouse.
	if ( Panel_ObjMoveOrStretchY != null )
	{
		Panel_CurrentAction = "None";
		Panel_CurrentTarget = null;

		if ( !DISABLE_PANELEVENT )
		{
			PanelEvent_Detach();
		}
		document.onclick = null;
		Panel_ObjMoveOrStretchY = null;		// This marks 'no activity'
		Panel_ObjStretchX = null;
	}
}

// Actually do the resize (called on document.onmousemove)
function Panel_ToMoveWhilstResizing(evt,target,x,y)
{
	if ( !x && !y )
	{
		x = evt.clientX;
		y = evt.clientY;
	}
	// NOTE THAT TARGET IS NOT USED
	// This is kinda important, since (up to) two targets exist for a resize.

	if ( DISABLE_PANEL_STRETCHING != false )
		return;

	// Be VERY sure you want to turn on full debug, and print out on every tick
	// Debug ( "Resize to " + x + ", " + y + " from " + Panel_OffsetX + ", " + Panel_OffsetY );

	if ( evt != null && Panel_ObjMoveOrStretchY != null )
	{
		var size = y + Panel_OffsetY;
		// Check we're not trying to go negative
		// (Throws an exception, and in IE causes us to go 100%)
		if ( size < CFG_Panel_MinStretchHeight )	size = CFG_Panel_MinStretchHeight;
		Panel_ObjMoveOrStretchY.style.height = size;

	}
	if ( evt != null && Panel_ObjStretchX != null )
	{
		var size = x  + Panel_OffsetX;
		if ( size < CFG_Panel_MinStretchWidth )	size = CFG_Panel_MinStretchWidth;
		Panel_ObjStretchX.style.width  = size;
	}
}

function Panel_ToMoveWhilstMoving(evt, target,x,y)
{
	if ( !x && !y )
	{
		x = evt.clientX;
		y = evt.clientY;
	}
	// Note that target is not actually used.

	if ( DISABLE_PANEL_MOVING != false )
		return;

//	alert ( "Panel_ToMoveWhilstMoving(evt="+evt+")" );

	// Firefox problem - IE uses 'event' to represent the thing that fires the event
	// (i.e. the mouse) - but this doesn't WORK in firefox.

	if ( evt != null && Panel_ObjMoveOrStretchY != null)
	{
// Be REALLY sure that you want to print out on EVERY tick!
//		Debug( x + "," + y + " from " + Panel_OffsetX + "," + Panel_OffsetY );
		Panel_ObjMoveOrStretchY.style.left = x - Panel_OffsetX;
		Panel_ObjMoveOrStretchY.style.top  = y - Panel_OffsetY;
	}
}


// If you don't want any layers behind you to handle a click (ie. you've handled it, or you're
	// something like a text pane that wants to stop everything) then call this
function Panel_BlockButtons()
{
	Panel_ButtonInFrontClicked = true;
}

function Panel_UnlockButtons()
{
	Debug ( "UnlockButtons()" );
	Panel_ButtonInFrontClicked = false;
}

function Panel_BringUp ( target )
{
	Div_MoveToFront ( target );
}

function Panel_Maximise ( panel_name )
{
	Debug ( "Panel_Maximise( panel_name="+panel_name+" )" );
	Div_Resize( panel_name+"_content" ,null, "100%");
	Div_SetVisibility(panel_name+"_content", "inherit");
}

function Panel_SetTitle ( panel_name, title )
{
	var target = panel_name + "_title";
	var obj = FindElementById ( target );
	if ( obj )
	{
		obj.innerHTML = title;
	}	
}

function Panel_StaticCreate( panel_name, x, y, w, h, allow_close, allow_max, allow_min, allow_move, allow_resize, substyle, title, content, suppress_output, stay_small )
{
	var output = "";
	// NOTE: This function is almost certain not to work if invoked after the documents onLoad method
		// In fact, in both firefox and IE it takes you to a page consisting SOLELY of that panel (with no css or anything)
	// TODO: Really ought to create using the DOM.  Maybe try that next

	Debug( "Panel_StaticCreate( panel_name="+panel_name+", x="+x+", y="+y+", w="+w+", h="+h+", a_c="+allow_close+", a_m="+allow_max+", a_i="+allow_min+", a_v="+allow_move+", a_r="+allow_resize+", substyle="+substyle+", title="+title+", content )" );

	// Outermost level of panel - and resize
	output += ( "<div class=PanelOuter"+substyle+" id="+panel_name+" onClick=\"Div_MoveToFront(\'"+panel_name+"\'); " );

	if ( allow_resize )
		output += ( "Panel_StartStretchingPanel(event, \'"+panel_name+"\', \'"+panel_name+"_content\', 0, 0); " );
	output += ( "Panel_BlockButtons();\" " );

// AHA!  These two events exist for IE, but not firefox.  That could actually be useful, given the IE-specific method of setting opacity
// (At least, in quirksmode)
//	output += ( "onMouseEnter=\"Div_SetOpacityIE(\'"+panel_name+"\',"+CFG_Panel_MaxOpacity+");\" " );
//	output += ( "onMouseExit=\"Div_SetOpacityIE(\'"+panel_name+"\',"+CFG_Panel_MinOpacity+");\" " );
// And exit never seems to fire anyway.

//	output += ( "onMouseEnter=\"AppendLog(\'enter\');\" ");
//	output += (  "onMouseExit=\"AppendLog(\'exit\');\"  ");
//	output += (  "onMouseOver=\"AppendLog(\'over\');\"  ");
//	output += (   "onMouseOut=\"AppendLog(\'out\');\"   ");

	if ( !DISABLE_PANELEVENT )
	{
		output += PanelEvent_StaticCreate_InOut(panel_name);
	}
	else
	{
		// These two should work cross-browser
		output += ( "onMouseOver=\"Div_SetOpacity(\'"+panel_name+"\',CFG_Panel_MaxOpacity);\" " );
		output += ( "onMouseOut=\"Div_SetOpacity(\'"+panel_name+"\',CFG_Panel_MinOpacity);\" " );
	}
	output+= ( "style=\"filter: alpha(opacity="+(100*CFG_Panel_MinOpacity)+"); opacity:"+CFG_Panel_MinOpacity+";\" " );

	output += ( ">\n" );

	// Title bar - and movement
	output += ( "<div class=PanelBar"+substyle+" onClick=" );
	if ( allow_move )
		output += ( "\"Panel_StartStopDrag(event, \'"+panel_name+"\'); " );
	output += ( "Panel_BlockButtons();\"> \n");

	// Actual title
	output += ( "<div class=PanelTitle"+substyle+" id="+panel_name+"_title>" );
	output += ( title );
	output += ( "</div>\n" );

	// Button 1 - close
	if ( allow_close )
	{
		output += ( "<div class=PanelButton"+substyle+" onClick=\"Div_SetVisibility(\'"+panel_name+"\', \'hidden\'); Panel_BlockButtons();\">" );
		output += ( "<img src=\"close"+substyle+".gif\" ALT=\"[close]\" WIDTH=100% HEIGHT=100%>");
		output += ( "</div>\n" );
	}

	if ( allow_max )
	{
		output += ( "<div class=PanelButton"+substyle+" onClick=\"Panel_Maximise(\'"+panel_name+"\'); Panel_BlockButtons();\">" );
		output += ( "<img src=\"maximise"+substyle+".gif\" ALT=\"[up]\" WIDTH=100% HEIGHT=100%>" );
		output += ( "</div>\n" );
	}

	if ( allow_min )
	{
		output += ( "<div class=PanelButton"+substyle+" onClick=\"Div_Resize(\'"+panel_name+"_content\',null,\'1px\'); Div_SetVisibility(\'"+panel_name+"_content\', \'hidden\'); Panel_BlockButtons();\">" );
		output += ( "<img src=\"minimise"+substyle+".gif\" ALT=\"[down]\" HEIGHT=100% WIDTH=100%>" );
		output += ( "</div>\n" );
	}

	// Close the title bar
	output += ( "</div>" );

	// Body
	output += ( "<div class=PanelInner"+substyle+" id="+panel_name+"_content>" );

	// Content
	output += ( "<div class=PanelContent"+substyle+" onClick=\"Panel_BlockButtons(); Div_MoveToFront(\'"+panel_name+"\');\" align=left id="+panel_name+"_actual>\n" );
	output +=( content );
	output += ( "\n</div>\n");

	// And close everything - content, then body then panel
	output += ( "</div>\n");
	output += ( "</div>\n");

//	alert ( output );

	if ( !suppress_output )
	{
		Debug ( "Actually printing" );
		document.writeln ( output );
		Panel_Resize ( panel_name, w, h );
		Panel_Move ( panel_name, x, y );
		if ( !stay_small )
			Panel_Maximise ( panel_name );
	}

	// Record the panels creation (for later targetting)
	Panel_Add ( panel_name );

	return output;

}

function Panel_CreateImagePanel( id, title, substyle, x, y, w, h, url )
{
	var content = "<div class=ConstrainingImage>\n";
	content += "<div class=LoadingImage id=\""+id+"_loading\" style=\"background: url("+CFG_Panel_LoadingImageURL+"); width:"+w+"; height:"+h+";\"> </div>\n";
	content += "<div class=LoadedImage id="+id+"_inner>\n";
 	content += "<img id=\""+id+"_img\" src=\""+url+"\" alt=\""+title+"\" width="+w+" height="+h+" onLoad=\"Panel_SetDivBackground(\'"+id+"_loading\',null);\" />\n";
	content += "</div>\n";
	content += "</div>\n";

	Panel_StaticCreate( id, x, y, w, h, true, false, false, true, true, "_img"+substyle, title, content, false, false );
	
	// Remove the scrollbars (Bug 114, 115)
	var obj = FindElementById(	id+"_content");
	if ( obj )
	{
		obj.style.overflow = "hidden";
		obj.style.height = h;
	}
}

function Panel_ClearBackground()
{
	// COULD get it via DOM - it's a sibling of the parent.
	var target = this.id.replace(/_img/ ,"_loading");

	Panel_SetDivBackground ( target, null );
	this.onload  = null;
}

function Panel_SetDivBackground( target, value )
{
	Debug ( "Panel_SetDivBackground( target="+target+", value="+value+" )" );
	var obj = FindElementById(target);
	if ( obj != null )
	{
		obj.style.background = value;
	}
}

function Panel_KeepBackground()
{
	var target = this.id.replace(/_img/ ,"_loading");
		// COULD get it via DOM - it's a sibling of the parent.
	var val = "url(" + CFG_Panel_LoadingImageURL+")";

	Panel_SetDivBackground ( target, val );
	this.onload  = null;
	this.onerror = null;
}

function Panel_SetImagePanel ( id, title, url, w, h, resize, stretch )
{
	var obj   = FindElementById ( id + "_img" );
	var bgobj = FindElementById ( id + "_loading" );

	if ( obj )
	{
		// Load in the 'no image' image - so that if loading in the real image fails, at least we see something change
		// (And NOT The old image just staying there)
		obj.src = CFG_Panel_LoadingImageURL;
		if ( bgobj != null ) bgobj.style.background = "url("+CFG_Panel_LoadingImageURL+")";
		if ( !w )
			obj.width = "100%";
		else
			obj.width = w;
		if ( !h )
			obj.height = "100%";
		else
			obj.height = h;

		// Prepare to remove the 'loading' background
		obj.onerror = Panel_KeepBackground;
		obj.onload  = Panel_ClearBackground;

		// And load in the real image
		obj.src = url;
	}


	if ( resize )
	{
		Panel_Resize ( id, w, h );
		obj = FindElementById ( id + "_loading" );
		if ( obj )
		{
			obj.style.width  = w;
			obj.style.height = h;
		}
	}

	if ( title )
	{
		Panel_SetTitle ( id, title );
	}
}

////////////////////////////////////////////////////////////
// DIV functions
// These should work on any object in the DOM with an id  //
// But they're mainly intended for panels.                //
////////////////////////////////////////////////////////////

// Quicky function to check whether an id has a corresponding object, and that object is visible
// (due to being explicitly visible, or inheriting visibility from a container)
// NOTE: IE (perhaps wrongly?) says that a visible object inside a hidden container is visible!
function Div_IsVisible( idToCheck )
{
	var objToCheck = FindElementById(idToCheck);
	var rval = false;
	// Should, ideally, check parents if inherit...
	while ( objToCheck != null )
	{
		if ( objToCheck.style.visibility == 'hidden' || objToCheck.style.visibility == 'hide' )
		{
			rval = false;
			objToCheck = null;
		}
		else if ( objToCheck.style.visibility == 'visible' || objToCheck.style.visibility == 'show' )
		{
			rval = true;
			objToCheck = null;
		}
		else
		{
			objToCheck = objToCheck.parentNode;
		}
	}
	return rval;
}

// This shows, or hides, an object
function Div_SetVisibility(idToHide, show)
{
	Debug( "Div_SetVisibility(idToHide=" + idToHide + ", show=" + show + ")");
	var objToHide = FindElementById(idToHide);
	if ( objToHide != null )
	{
		var parent = objToHide.parentNode;
		if ( parent != null && parent.style.visibility == 'hidden' )
		{
			Debug ( "Parent hidden, not altering child (breaks relations)");
		}
		else
		{
			objToHide.style.visibility = show;
		}
	}
	else
	{
		Debug ( "No such object " + idToHide + "?" )
	}
}

function Div_SetOpacity( target, level )
{
	if ( DISABLE_PANEL_OPACITY != false )
		return;

	Debug ( "Div_SetOpacity( target="+target+", level="+level+" )" );
	var obj = FindElementById ( target );
	if ( obj && obj.style )
	{
		obj.style.opacity = level;
		// And for IE...
		obj.style.filter = 'alpha(opacity=' + (level *100)+ ')';

		Debug ( ""+target+".style.opacity:" + obj.style.opacity + "&nbsp;&nbsp;&nbsp;IE gets "+target+".style.filter:" + obj.style.filter );
	}
	else
		Debug ( "No such object" );
}

function Div_SetBackground(target,color)
{
	Debug( "Div_SetBackground(target="+target+",color="+color+")" );
	var obj = FindElementById(target);
	if ( obj && obj.style )
		obj.style.background=color;
}


function Div_Move ( target, x, y )
{
	var obj = FindElementById ( target );
	if ( obj )
	{
		obj.style.left = x;
		obj.style.top = y;
	}
}

// Resize an object
function Div_Resize(idToSize, x, y )
{
	Debug("Div_Resize( id=" + idToSize + ", x=" + x + ", y=" + y + " );" );
	objToSize = FindElementById(idToSize);
	if ( objToSize != null )
	{
		if ( x != null )
		{
			Debug ( "setting " + idToSize + ".width = " + x );
			objToSize.style.width = x;
		}
		if ( y != null )
		{
			Debug ( "setting " + idToSize + ".height = " + y );
			objToSize.style.height = y;
		}
	}
	else 
	{
		Debug ( "No such object" + idToSize + "?" );
	}
}

function Div_MoveToFront( target )
{
	obj = FindElementById(target)
	if ( obj != null )
	{
		Panel_FrontIndex = Panel_FrontIndex + 1;
		obj.style.zIndex = Panel_FrontIndex;
	}
}

function Div_Scroll (target, dx, x_units, dy, y_units, loop, reset)
{
	var notify = null;

	var obj = FindElementById(target);
	if ( obj )
	{
		if ( reset )
		{
			obj.style.top = 0;
			obj.style.left = 0;
		}

		var arguments = "\'"+target+"\', "+dx+", \'"+x_units+"\', "+dy+", \'"+y_units+"\'";

		if ( loop )
		{
			 notify = "\"Div_Scroll("+arguments+",true,true);\"";
		}


		Event_Add ( "Div_ScrollTick ( "+arguments+","+notify+" );", CFG_Event_Interval, CFG_Event_Interval );
	}
}

function Div_ScrollTick ( target, dx, x_units, dy, y_units, localNotifyScrollingComplete )
{
	Debug ( "MoveDown( target=" + target + ", dx="+dx+", x_units="+x_units+", dy="+dy+", y_units="+y_units+" )" );

	var obj = FindElementById(target);
	if ( obj != null )
	{
		if ( dx )
		{
			var pos = 0;
			if ( obj.style && obj.style.left )
			{
				pos = parseInt ( obj.style.left );
			}
			pos = pos + dx;
			pos = pos + x_units;

			if ( pos < - obj.offsetWidth )
			{
				Event_Delete(this);
				if ( localNotifyScrollingComplete )
				{
					AppendLog ( "Ending scroll of "+target+" due to x-pos");
					Event_Add( localNotifyScrollingComplete, 1 ); 
				}
			}

			obj.style.left = pos;
		}
		if ( dy )
		{
			var pos = 0;
			if ( obj.style && obj.style.top )
			{
				pos = parseInt ( obj.style.top );
			}
			pos = pos + dy;
			pos = pos + y_units;


			if ( (0-parseInt(pos)) > parseInt(obj.offsetHeight)  )
			{
				Event_Delete(this);
				if ( localNotifyScrollingComplete )
				{
					AppendLog ( "Ending scroll of "+target+" due to y-pos");
					Event_Add( localNotifyScrollingComplete, 1 ); 
				}
			}

			obj.style.top = pos;
		}
	}
}

/////////////////////////
// Progress Bars       //
/////////////////////////
// These are now included in "progressbar.js" since few files need them.

/////////////////////
// Alerts          //
/////////////////////

function BlinkOn(target, style, offset)
{
	if ( style == null )
		style = "3px double #ff9933";

	var obj = FindElementById(target);
	if ( obj != null )
	{
		obj.style.border = style;
// Flashing the opacity - I'm not sure whether or not I like this effect
//		obj.style.opacity = .3;

		// Offset is used because the box model is STUPID and targetsthe corner outside the border as the top/left
		if ( offset && obj.style && obj.style.top && obj.style.left )
		{
			obj.style.top = parseInt(obj.style.top) - offset;
			obj.style.left = parseInt(obj.style.left) - offset;
			// TODO: IE needs you to increment the width as well, but that's a box-model bug - so I'm not fixing it.
		}

	}
}

function BlinkOff(target,offset)
{
//	Debug("BlinkOff(target="+target+")" );
	var obj = FindElementById(target);
	if ( obj != null )
	{
		obj.style.border = "0px none #000000";
//		obj.style.opacity = 1;
		if ( obj.style && obj.style.top && obj.style.left )
		{
			obj.style.top  = parseInt(obj.style.top ) + offset;
			obj.style.left = parseInt(obj.style.left) + offset;
		}
	}
}

// NOTE: There is nothing (at this level) stopping you from setting a panel
// from flshing multiple times, possibly with different styles or offsets.
// This will look very very strange - but should settle itself out in the end.
// NOTE: Offsets will end up culmulative!
function StartBlinking(target,style,offset,onTime,offTime,numBlinks)
{
	BlinkOn ( target, style, offset);
	Event_Add ( "BlinkOff( \""+target+"\","+offset+" );", offTime, false );
	if ( numBlinks > 1 )
	{
		numBlinks = parseInt(numBlinks) - 1;
		if ( style != null )
			style = "\""+style+"\"";
		var n = "StartBlinking(\""+target+"\","+style+","+offset+","+onTime+","+offTime+","+numBlinks+");";
//		alert ( n );
		Event_Add( n, onTime + offTime, false);
	}
}

/////////////////////
// Controls        //
/////////////////////

/////////////////////
// Slider          //
/////////////////////

function Control_Slider_StaticCreate( SliderName, title, tleft, tright, substyle, w, value, notify, suppress )
{
	if ( DISABLE_CONTROL_SLIDERS != false )
		return null;

	var output = "";
	output += "<div id="+SliderName+" class=slider_outer"+substyle+" style=\"width:"+w+";\">\n";
	output += "<div id="+SliderName+"_SliderTitle class=slider_title"+substyle+">\n";
	output += title;
	output += "\n</div>\n";
	output += "<div class=slider_wrap"+substyle+">\n";
	output += "<div id="+SliderName+"_SliderBackground class=slider_background"+substyle+" onMouseUp=\"Control_Slider_DoClick(event, \'"+SliderName+"\', \'"+notify+"\');\">\n";
	output += "<div id="+SliderName+"_SliderTick class=slider_tick"+substyle+" style=\"left:"+value+"%;\" >\n";
	output += "</div>\n";
	output += "</div>\n";
	output += "<div class=slider_value"+substyle+">\n";
	output += "<form><input id="+SliderName+"_Value maxlength=3 type=text onChange=\"Control_Slider_ChangeValue(event, \'"+SliderName+"\', \'"+notify+"\');\" value="+value+" size=3 /></form></div>\n";
	output += "</div>\n";
	output += "<div id="+SliderName+"_SliderMin class=slider_subscript_min"+substyle+">\n";
	output += tleft;
	output += "\n</div>\n";
	output += "<div id="+SliderName+"_SliderMax class=slider_subscript_max"+substyle+">\n";
	output += tright;
	output += "\n</div>\n</div>\n";

	//alert ( output );

	if ( !suppress )
		document.write ( output );

	return output;
}

function Control_Slider_DoClick(evt, slider, notify)
{
	if ( DISABLE_CONTROL_SLIDERS != false )
		return;

	var slide = slider + "_SliderTick";
	var sobj = FindElementById(slide);
	var back = slider + "_SliderBackground";
	var bobj = FindElementById(back);
	var text = slider + "_Value";
	var tobj = FindElementById( text );

	if ( !tobj || !sobj || !bobj )
		return;

	var left = evt.clientX - findPosX(bobj);
	sobj.style.left = left;

	var pos = ( left * 100 ) / bobj.clientWidth

	tobj.value = parseInt(pos);

	Event_Add ( notify, 1, false );
}

function Control_Slider_ChangeValue(event, slider, notify)
{
	if ( DISABLE_CONTROL_SLIDERS != false )
		return;

	var slide = slider + "_SliderTick";
	var sobj = FindElementById(slide);
	var back = slider + "_SliderBackground";
	var bobj = FindElementById(back);
	var text = slider + "_Value";
	var tobj = FindElementById( text );

	if ( !tobj || !sobj || !bobj )
		return;

	var pos = parseInt(tobj.value);
	tobj.value = pos;

	var left = ( bobj.clientWidth * pos ) / 100;
	sobj.style.left = left;

	Event_Add ( notify, 1, false );
}


////////////////////////////////
// Options panel
////////////////////////////////


function OPTION_SetPassiveTrans()
{
	var text = "slider_passivetrans_Value";
	var tobj = FindElementById( text );
	if ( tobj )
	{
		var pos = parseInt(tobj.value);
		Debug( "slider_passive set to " + pos );
		CFG_Panel_MinOpacity = pos / 100;
		Panel_SetAllOpacity ( CFG_Panel_MinOpacity );
		Div_SetOpacity ( PanelEvent_ActivePanel, CFG_Panel_MaxOpacity );
	}
}

function OPTION_SetActiveTrans()
{
	var text = "slider_activetrans_Value";
	var tobj = FindElementById( text );
	if ( tobj )
	{
		var pos = parseInt(tobj.value);
		if ( pos < 20 )
		{
			pos = 20;
			tobj.value = 20;
		}
		Debug ( "slider_activetrans set to " + pos );
		CFG_Panel_MaxOpacity = pos / 100;
		Div_SetOpacity ( PanelEvent_ActivePanel, CFG_Panel_MaxOpacity );
	}
}

function OPTION_CreateStaticOptionsPanel( id, title, substyle, x, y, w, h, suppress_creation )
{
	var slider_min = ""; var slider_max="";
	if ( DISABLE_CONTROL_SLIDERS == false )
		slider_min = Control_Slider_StaticCreate( "slider_passivetrans", "Inactive Panel Transparency", "", "", "", "95%", 40, "OPTION_SetPassiveTrans();", true );
	if ( DISABLE_CONTROL_SLIDERS == false )
		slider_max = Control_Slider_StaticCreate( "slider_activetrans", "Active Panel Transparency", "Fully Transparent (0)", "Fully Opaque (100)", "", "95%", 100, "OPTION_SetActiveTrans();", true );
	var output = Panel_StaticCreate( id, x, y, w, h, true, true, true, true, true, substyle, title, slider_min+slider_max, suppress_creation, false );
	OPTION_SetActiveTrans();
	OPTION_SetPassiveTrans();
	return output;
}


// This will get an outer element, if possible
function GetOuterElementById(target)
{
	var outer;
	if ( parent ) outer = parent.document;
	if ( !outer ) outer = document;
	return outer.getElementById ( target );
}

// This will get an inner element - or look for it in the outer, if none exists
// (You can use this globally - though it doubles the time taken finding objects)
function FindElementById(target)
{
	var obj = document.getElementById ( target );
	if ( !obj && parent && parent.document ) obj = parent.document.getElementById ( target );
	return obj;
}

