// ScrollBar.js - JavaScript scroll bar implmentation.

// Define the names of the images used for horizontal and vertical scroll bars.
var imgNamesH = [ "arrowleft.gif", "back.gif", "thumb20h.gif", "arrowright.gif" ];
var imgNamesV = [ "arrowup.gif", "back.gif", "thumb20v.gif", "arrowdown.gif" ];

var scrollBarImageWidth = 20;

// This is the constructor function for our ScrollBar class.
// Calling it creates a ScrollBar object and emits all the
// required HTML tags into the specified document at the 
// current location.
// document	is the Document object in which the scrollbar is to be created.
// orientation	specifies which direction the scrollbar is: "h" or "v".
// imagePath is the URL path containing images used by the scrollbar.
function ScrollBar( document, orientation, imagePath, i_strId) {
	// The first time we are called (and only the first time) we have
	// to do some special stuff. Now that the prototype object is
	// created we can set up our methods.
	if (!ScrollBar.prototype.hide) {
		// Initialize the prototype object to create our methods.
		ScrollBar.prototype.hide = ScrollBarHide;
		ScrollBar.prototype.show = ScrollBarShow;
		ScrollBar.prototype.update = ScrollBarUpdate;
		ScrollBar.prototype.scrollBy = ScrollBarScrollBy;

		// setHeight is not provided. Scroll bars resize to fit in their parent element.
		ScrollBar.prototype.setItems = ScrollBarSetItems;
		ScrollBar.prototype.setVisible = ScrollBarSetVisible;
		ScrollBar.prototype.setThumbSize = ScrollBarSetThumbSize;
		ScrollBar.prototype.setTop = ScrollBarSetTop;

		ScrollBar.prototype.getHeight = ScrollBarGetHeight;
		ScrollBar.prototype.getItems = ScrollBarGetItems;
		ScrollBar.prototype.getVisible = ScrollBarGetVisible;
		ScrollBar.prototype.getThumbSize = ScrollBarGetThumbSize;
		ScrollBar.prototype.getTop = ScrollBarGetTop;
		}

	// Validate the orientation.
	this.orientation = orientation;
	this.horizontal = orientation == "h";
	this.vertical = orientation == "v";
	if (!this.horizontal && !this.vertical) {
		document.write("<div class='B2020H1'>ScrollBar orientation must be 'h' or 'v'.</div>");
		return;
	}
	this.myWidth  = this.vertical ? "width" : "height";
	this.myHeight = this.horizontal ? "width" : "height";

	// Preload the images that we will need.
	if (imagePath.length > 0) {
		if (imagePath.charAt(imagePath.length - 1) != "/") {
			imagePath += "/";
		}
	}
	var imageNames = this.horizontal ? imgNamesH : imgNamesV;
	this.images = new Array(imageNames.length);
	for (var i = 0; i < imageNames.length; i++) {
		this.images[ i ] = new Image();
		this.images[ i ].src = imagePath + imageNames[ i ];
	}
	this.document = document;

	// Initialize the parameters of this scroll bar.
	this.height =  0;		// "Height" in pixels of displayed scrollbar.
	this.items =  1;		// Total number of items in the "list".
	this.visible =  1;		// Number of items currently visible.
	this.top =  0;		// First item that is visible.
	this.thumbSize = 20;		// Size of the "thumb" (needed to drag properly).

	this.heightOld = -1;		// Previous values of these parameters.
	this.itemsOld = 0;
	this.visibleOld = 0;
	this.topOld = 0;

	this.imageWidth = scrollBarImageWidth;	// Width of the scroll bar image when it is displayed.

	// Initialize the call backs.
	this.showToolTip = null;
	this.hideToolTip = null;
	this.performScroll = null;

	// Figure out what entry in the document.images[] array the images
	// for this scrollbar will be stored in.
	var index = document.images.length;
	var br = this.vertical ? "<br>\n" : "";
	var strOutput;
	strOutput = "<img alt='' src='" + imagePath + imageNames[ 0 ] + "' onmousedown='imgClick(-1,this,event);' />" + br;
	strOutput += "<img alt='' src='" + imagePath + imageNames[ 1 ] + "' onmousedown='imgClick(-this.sbar.visible,this,event);' />" + br;
	strOutput += "<img alt='' src='" + imagePath + imageNames[ 2 ] + "' onmousedown='thumbDrag(this,event);' />" + br;
	strOutput += "<img alt='' src='" + imagePath + imageNames[ 1 ] + "' onmousedown='imgClick(this.sbar.visible,this,event);' />" + br;
	strOutput += "<img alt='' src='" + imagePath + imageNames[ 3 ] + "' onmousedown='imgClick(1,this,event);' />";
	if (typeof(i_strId) != "undefined") {
		document.getElementById(i_strId).innerHTML = strOutput;
	}
	else {
		// Now output the HTML code for the scrollbar.
		document.write(strOutput);
	}

	// Now that we have output the <img> tags, save a reference to the Image object
	// that it created in the ScrollBar object. Create links from the images back
	// to the ScrollBar object.
	this.arrowUp = document.images[ index++ ];
	this.arrowUp.sbar = this;
	this.backUp = document.images[ index++ ];
	this.backUp.sbar = this;
	this.thumb = document.images[ index++ ];
	this.thumb.sbar = this;
	this.backDown = document.images[ index++ ];
	this.backDown.sbar = this;
	this.arrowDown = document.images[ index++ ];
	this.arrowDown.sbar = this;

	function ScrollBarHide() {
		// If the scroll bar is not already hidden, hide it by setting the 
		// height of all components to zero.
		if (this.heightOld != 0) {
			this.thumb[ this.myHeight ] = 0;
			this.backUp[ this.myHeight ] = 0;
			this.backDown[  this.myHeight ] = 0;
			this.arrowUp[this.myHeight ] = 0;
			this.arrowDown[ this.myHeight ] = 0;

			this.thumb[ this.myWidth ] = 0;
			this.backUp[ this.myWidth ] = 0;
			this.backDown[  this.myWidth ] = 0;
			this.arrowUp[this.myWidth ] = 0;
			this.arrowDown[ this.myWidth ] = 0;

			this.heightOld = 0;
		}
	}

	function ScrollBarShow() {
		// Determine the height available to the scroll bar.
		// Leave some space or the scrollbars will force the table size.
		if (this.vertical) {
			if (document.all) {
				this.height = this.thumb.parentElement.clientHeight;	// IE
			}
			else {
				this.height = this.thumb.parentNode.scrollHeight;		// NN
			}
		}
		else {
			if (document.all) {
				this.height = this.thumb.parentElement.clientWidth;	// IE
			}
			else {
				this.height = this.thumb.parentNode.scrollWidth;		// NN
			}
		}
		// If the height available is different than the current height
		if (this.height != this.heightOld) {
			// Remember the new height.
			this.heightOld = this.height;

			// If there is any need for a scroll bar
			if (this.visible < this.items) {
				// Calculate the size of the thumb. Don't get too small to be useful.
				this.thumbSize = Math.round( ( this.height - 40 ) * this.visible / this.items );
				var thumbExtra = 0;
				if (this.thumbSize < 10) {
					thumbExtra = 10 - this.thumbSize;
					this.thumbSize = 10;
				}

				// Calculate which thumb image to use from the available set of
				// { { 20, 40, ..., 200 }, { 300, 400, ..., 1000 } } pixels high
				// and set the thumb size and image.
				var thumbImgSize = Math.round( this.thumbSize / 20 ) * 20;
				if (thumbImgSize > 200) {
					thumbImgSize = Math.round( this.thumbSize / 100 ) * 100;
					if (thumbImgSize > 1000 )
						thumbImgSize = 1000;
					}
				this.setThumbSize( thumbImgSize );

				// Calculate the size of the page scroll areas around the thumb.
				var backupSize = Math.round(( this.height - ( 40 + thumbExtra )) * this.top / this.items);
				if (backupSize < 0 )
					backupSize = 0;
				var backdownSize = this.height - ( 40 + this.thumbSize + backupSize );
				if (backdownSize < 0) {
					if (backupSize >= -backdownSize ) {
						backupSize += backdownSize;
					}
					else {
						backupSize = 0;
					}
					backdownSize = 0;
				}

				// Update the size of the areas (other than the thumb).
				this.thumb[ this.myHeight ] = this.thumbSize;
				this.thumb[ this.myWidth  ] = 20;
				this.backUp[this.myHeight ] = backupSize;
				this.backUp[this.myWidth  ] = 20;
				this.backDown[  this.myHeight ] = backdownSize;
				this.backDown[  this.myWidth  ] = 20;
				this.arrowUp[this.myHeight ] = 20;
				this.arrowUp[this.myWidth ] = 20;
				this.arrowDown[ this.myHeight ] = 20;
				this.arrowDown[ this.myWidth ] = 20;
			}
			else {
				// Otherwise all items are visible so hide the scroll bar.
				this.hide();
			}
		}
	}

	function ScrollBarSetThumbSize(size) {
		// Get the old file name and strip it into components. Discard any old size infromation.
		var fileName = this.thumb.src;
		var ext = fileName.substring( fileName.lastIndexOf( "." ), fileName.length );
		var path = fileName.substring( 0, fileName.lastIndexOf( "/" ) + 1 );
		var name = fileName.substring( fileName.lastIndexOf( "/" ) + 1, fileName.lastIndexOf( "." ) )
		ext = this.orientation + ext;
		name = name.substring( 0, name.length - 2 );
		while (( "0" <= name.charAt(name.length - 1)) && (name.charAt(name.length - 1) <= "9" )) {
			name = name.substring(0, name.length - 1);
		}

		// Create the new file name and if a change is needed update the image.
		var newFileName = path + name + size + ext;
		if (this.thumb[ this.verticalHeight ] != size) {
			this.thumb.src = newFileName;
		}
	}

	function ScrollBarUpdate() {
		var changed = false;

		// Number of items must be greater than or equal to zero.
		if (this.items < 0) {
			this.items = 0;
		}

		// Number of visible items must also be greater than or equal to zero,
		// less than or equal to the number of items, and can not be zero if there are any items.
		if (this.visible < 0) {
			this.visible = 0;
		}
		if (this.visible > this.items) {
			this.visible = this.items;
		}
		if (this.visible == 0 && this.items != 0) {
			this.visible = 1;
		}

		// The top position must be less than or equal to the number of invisible items and
		// must be greater than or eqaul to zero.
		if (this.top > (this.items - this.visible )) {
			this.top = ( this.items - this.visible );
		}
		if (this.top < 0) {
			this.top = 0;
		}

		// If any of the values have changed, then the scroll bar has changed.
		changed = this.items != this.itemsOld || this.visible != this.visibleOld || this.top != this.topOld;

		// If so, update the old values.
		if (changed) {
			this.itemsOld = this.items;
			this.visibleOld = this.visible;
			this.topOld = this.top;
		}

		return changed;
	}

	function ScrollBarScrollBy(delta) {
		// Update the top position by the specified ammount.
		this.top += delta;

		// If the scroll occurs (not at a limit)
		if (this.update()) {
			// Scroll the data.
			if (this.performScroll != null )
				this.performScroll();
			}
		}

	function ScrollBarSetItems(items) {
		this.items = items;
		// Since we have changed the number of items, go back to the top.
		this.top = 0;
		}

	function ScrollBarSetVisible(visible) {
		this.visible = visible;
	}

	function ScrollBarSetTop(top) {
		this.top = top;
	}

	// Get various attributes of the scroll bar.
	function ScrollBarGetHeight() { return this.height;}
	function ScrollBarGetItems() { return this.items; }
	function ScrollBarGetVisible() { return this.visible;}
	function ScrollBarGetThumbSize() { return this.thumbSize; }
	function ScrollBarGetTop() { return this.top;}
}

// imgClick() is designed to be called from an onmousedown event handler.
// amount	specifies the number of items to scroll (typically 1, -1, or +/- visible page size).
// obj		must specify the image that was clicked.
// e		must be the Event object for the mousedown event.
// This implementation works with both the DOM Level 2 event model and the IE Event model.

var timeToWait = 500;		// Initially delay for 500 mS before autorepeating, then repeat at 100 mS intervals.
var timeOut = 0;			// Timeout object for autorepeat

var doRepeat = null;		// This is a kludge to allow us to callback a local function.

function imgClick(amount, obj, e) {
	if (!e )
		e = window.event; // IE event model

	// Update the image to have the pressed bitmap.
	if (obj )
		setButtonState( "press" );

	// Allow the repeat local function to be called back and then do the first operation.
	doRepeat = repeat;
	doRepeat();

	// Register the event handlers that will respond to the mouseout events
	// and the mouseup event that follow this mousedown event.
	if (document.addEventListener) {
		// DOM Level 2 Event Model
		// Register capturing event handlers
		document.addEventListener( "mouseout", outHandler, true );
		document.addEventListener( "mouseup",  upHandler,  true );
	}
	else if (document.attachEvent) {
		// In the IE 5+ Event model, we can't capture events, so these handlers
		// are triggered when only if the event bubbles up to them.
		// This assumes that there aren't any intervening elements that
		// handle the events and stop them from bubbling.
		document.attachEvent( "onmouseout", outHandler );
		document.attachEvent( "onmouseup",  upHandler  );
	}
	else {
		// In IE 4 we can't use attachEvent(), so assign the event handlers
		// directly after storing any previously assigned handlers so they
		// can be restored. Note that this also relies on event bubbling.
		var oldouthandler = document.onmouseout;
		var olduphandler = document.onmouseup;
		document.onmouseout = outHandler;
		document.onmouseup  = upHandler;
		}

	// We've handled this event. Don't let anybody else see it.
	if (e.stopPropagation )
		e.stopPropagation();		// DOM Level 2
	else
		e.cancelBubble = true;		// IE

	// Now prevent any default action.
	if (e.preventDefault )
		e.preventDefault();			// DOM Level 2
	else
		e.returnValue = false;		// IE

	// Function to be executed to do autorepeat.
	function repeat() {
		// Ensure the timeout is complete.
		timeOut = 0;

		// Update the top position by the specified ammount.
		obj.sbar.setTop( obj.sbar.getTop() + amount );

		// If the scroll occurs (not at a limit)
		if (obj.sbar.update()) {
			// Set up another event to do the autorepeat
			// (500 mS delay first time, 100 mS repeat rate).
			timeOut = setTimeout( "doRepeat();", timeToWait );
			timeToWait = 100;

			// Scroll the data.
			if (obj.sbar.performScroll != null )
				obj.sbar.performScroll();
			}

		// Otherwise we are at a limit, so don't autorepeat any more and reset the timeout.
		else
			timeToWait = 500;
	}

	// This is the handler that captures the final mouseup event that
	// occurs at the end of a click (up or out). Both up and out are handled
	// the same way.
	function upHandler(e) { outHandler( e ) }
	
	function outHandler(e) {
		if (!e )
			e = window.event; // IE event model

		// Clear the autorepeat.
		clearTimeout( timeOut );
		timeOut = 0;
		timeToWait = 500;

		// Restore the image to have the unpressed bitmap.
		setButtonState( "" );

		// Unregister the capturing event handlers.
		if (document.removeEventListener) {
			// DOM Event Model
			document.removeEventListener("mouseup",  upHandler,  true );
			document.removeEventListener("mouseout", outHandler, true );
			}
		else if (document.detachEvent) {
			// IE 5+ Event Model
			document.detachEvent( "onmouseup",  upHandler  );
			document.detachEvent( "onmouseout", outHandler );
		}
		else {
			// IE 4 Event Model
			document.onmouseup  = olduphandler;
			document.onmouseout = oldouthandler;
		}

		// And don't let the event propagate any further.
		if (e.stopPropagation )
			// DOM Level 2
			e.stopPropagation();
		else
			// IE
			e.cancelBubble = true; 
	}

	// Sets the button state by updating the image to be displayed. State should either be "",
	// or should be the tail end of the file names, added with a leading underscore.
	function setButtonState(state) {
		// Get the old file name and strip it into components. Discard any suffix.
		var fileName = obj.src;
		var ext = fileName.substring( fileName.lastIndexOf( "." ), fileName.length );
		var path = fileName.substring( 0, fileName.lastIndexOf( "/" ) + 1 );
		var name = fileName.substring( fileName.lastIndexOf( "/" ) + 1, fileName.lastIndexOf( "." ) )
		if (name.indexOf( "_" ) > 0 )
			name = name.substring( 0, name.indexOf( "_" ) );

		// If there is a new suffix, add it.
		if (state != "" )
			state = "_" + state;

		// Create the new file name and if a change is needed update the image.
		var newFileName = path + name + state + ext;
		if (newFileName != fileName)
			obj.src = newFileName;

	}
}

// thumbDrag() is designed to be called from an onmousedown event handler.
// obj	must specify the image that was clicked.
// e	must be the Event object for the mousedown event.
// This implementation works with both the DOM Level 2 event model and the IE Event model.
function thumbDrag(obj, e) {
	if (!e) 
		e = window.event; // IE event model

	// Record the original pixel position of the thumb and the first visible item.
	var posOrig = obj.sbar.horizontal ? e.clientX : e.clientY;
	var topOrig = obj.sbar.getTop();
	
	// Register the event handlers that will respond to the mousemove events
	// and the mouseup event that follow this mousedown event.
	if (document.addEventListener) {
		// DOM Level 2 Event Model
		// Register capturing event handlers
		document.addEventListener( "mousemove", moveHandler, true );
		document.addEventListener( "mouseup", upHandler, true );
	}
	else if (document.attachEvent) {
		// In the IE 5+ Event model, we can't capture events, so these handlers
		// are triggered when only if the event bubbles up to them.
		// This assumes that there aren't any intervening elements that
		// handle the events and stop them from bubbling.
		document.attachEvent( "onmousemove", moveHandler );
		document.attachEvent( "onmouseup", upHandler);
	}
	else {
		// In the IE 4 event model, we can't use attachEvent(), so assign the
		// event handlers directly after storing any previously assigned handlers 
		// so they can be restored. Note that this also relies on event bubbling.
		var oldmovehandler = document.onmousemove;
		var olduphandler = document.onmouseup;
		document.onmousemove = moveHandler;
		document.onmouseup = upHandler;
	}

	// We've handled this event. Don't let anybody else see it.
	if (e.stopPropagation) {
		// DOM Level 2
		e.stopPropagation();
	}
	else {
		// IE
		e.cancelBubble = true;
	}

	// Now prevent any default action.
	if (e.preventDefault )
		e.preventDefault();			// DOM Level 2
	else
		e.returnValue = false;		// IE

	// This is the handler that captures mousemove events when the thumb
	// is being dragged. It is responsible for scrolling the data.
	function moveHandler(e) {
		if (!e )
			e = window.event; // IE event model

		// Calculate the new top position and refresh.
		// If the scroll bar is collapsed to just the thumb and two buttons,
		// scroll by the number of pixels the thumb is "moved". Otherwise,
		// use the proportion of the visible items to total items and
		// the movement to the total size.
		var h = obj.sbar.getHeight() - ( 40 + obj.sbar.getThumbSize() );
		var posDiff = ( obj.sbar.horizontal ? e.clientX : e.clientY ) - posOrig;
		if (h > 0 )
			posDiff *= ( obj.sbar.getItems() - obj.sbar.getVisible() ) / h;
		obj.sbar.setTop( Math.round( topOrig + posDiff ) );

		// Perform the actual scrolling, but allow it to proceeed asynchronously
		// so that this event completes first. If a timeout is already in progress
		// then show the tooltip since we are scrolling faster than the table is
		// being updated.
		if (obj.sbar.update()) {
			// Force the scrollbar to redraw to show the new thumb position.
			obj.sbar.hide();
			obj.sbar.show();
			
			if (timeOut != 0) {
				clearTimeout( timeOut );
				if (obj.sbar.showToolTip != null) {
					obj.sbar.showToolTip(e.clientX, e.clientY);
				}
			}
			if (obj.sbar.performScroll != null) {
				// We need to do this to provide static binding to the parent object
				// in a consistent fashion otherwise dragging the thumb will not
				// work properly.
				timeOut = setTimeout( function() { obj.sbar.performScroll(); }, 200 );
			}
		}

		// And don't let anyone else see this event.
		if (e.stopPropagation )
			e.stopPropagation();		// DOM Level 2
		else
			e.cancelBubble = true;		// IE

		// Now prevent any default action.
		if (e.preventDefault )
			e.preventDefault();			// DOM Level 2
		else
			e.returnValue = false;		// IE

	}

	// This is the handler that captures the final mouseup event that
	// occurs at the end of a drag.
	function upHandler(e) {
		if (!e )
			e = window.event; // IE event model

		// Hide the tooltip.
		if (obj.sbar.hideToolTip != null )
			obj.sbar.hideToolTip();

		// Unregister the capturing event handlers.
		if (document.removeEventListener) {
			// DOM Event Model
			document.removeEventListener( "mouseup", upHandler, true );
			document.removeEventListener( "mousemove", moveHandler, true );
		}
		else if (document.detachEvent) {
			// IE 5+ Event Model
			document.detachEvent( "onmouseup", upHandler);
			document.detachEvent( "onmousemove", moveHandler );
		}
		else {
			// IE 4 Event Model
			document.onmouseup = olduphandler;
			document.onmousemove = oldmovehandler;
		}

		// And don't let the event propagate any further.
		if (e.stopPropagation )
			// DOM Level 2
			e.stopPropagation();
		else
			// IE
			e.cancelBubble = true; 
	}
}

