/*
sliderClass
Purpose: Keep multiple divs in a slideable window

mySlider = new sliderClass(parentNode, isHorizontal, windowHeight, windowWidth, startingPaneCount, urlOfScreenGraphic)
   * parentNode: the DOM parent node that this will live within
   * windowHeight/Width: the outside dimensions of the window area. Also, the size that each pane will be
   * startingPaneCount: the number of panes that it starts with. You can always add later
   * urlOfScreenGraphic: url of a graphic that will be displayed over the area while it is sliding
   * (to keep users from clicking on anything underneath while I'm sliding...)

mySlider.addPane() - adds a new pane to the plate
mySlider.getMainNode() - returns the DOM handle of the main node
mySlider.getPaneCount() - returns the number of panes on the plate
mySlider.getPaneNode(int) - returns the DOM handle to the pane node(integer)
mySlider.setCurrentPane(paneID) - force a "jump" to the paneID
mySlider.setPaneHTML(paneID, someHTMLText) - put <someHTMLText> onto pane(paneID)
mySlider.setPaneNode(paneID, paneNode) - makes pane(paneID) the parent node to DOM node(paneNode)
mySlider.slideLeft() - slide plate to the right which has the effect of panning left. Returns false if at paneCount()
mySlider.slideRight() - slide plate to the left which has the effect of panning right. Returns false if at 0
mySlider.slideUp() - slide plate down which has the effect of panning up. Returns false if at 0
mySlider.slideDown() - slide plate up which has the effect of panning down. Returns false if at paneCount()

Events:
beforeSlide(sender, currentPaneID, 'left|right|up|down')  - fired before anything is done. Return false to halt slide.
afterSlide(sender, newPaneID) - fired after slide is complete
onFirstPane(sender) - fired when panes are first slid into 0. Does not fire at initialization
onLastPane(sender) - fired when panes are slid into current last pane
onMidPane(sender, currentPaneID) - fired after a pane is moved if we are neither at the 0th or last

*/

// This function is required to facilitate the inheritance of methods
// from the sliderClass into the horiz and vertical slider classes...
/*
function scInherit(subClass, superClass)
{
	var c = function() {};
	c.prototype = superClass.prototype;
	subClass.prototype = new c();
}
*/

function log(msg)
{
	bodys = document.getElementsByTagName('body');
	bodys[0].appendChild(document.createTextNode(msg));
	bodys[0].appendChild(document.createElement('br'));
}

function sliderClass(parentNode, horiz, height, width, paneCount, screenGraphic) 
{
	this.isHorizontal = horiz;
	if (this.isHorizontal) { this.startingUp = true; }
	
	this.amSliding = false;
	this.beforeSlide = null;
	this.afterSlide = null;
	this.onFirstPane = null;
	this.onLastPane = null;
	this.onMidPane = null;
	this.paneHandles = new Array();
	this.currentPane = 0;
	this.tObj = new tableManager();
	this.rate = 1.25;
	if (screenGraphic == undefined) { this.screenGraphic = '/graphics/dot_clear.gif'; }
	else { this.screenGraphic = screenGraphic; }
	this.isIE = ((document.all) && (document.getElementById));
	this.wrapZero = false;

	this.parentNode = parentNode; 
	this.height = height;
	this.width = width;
	if ((paneCount == undefined) || (paneCount < 1)) { this.paneCount = 1; }
	else { this.paneCount = paneCount; }
	
	if (this.isHorizontal) { this._calcOffsetArray(this.rate, this.width); }
	else { this._calcOffsetArray(this.rate, this.height); }

	// Create the essential frame here
	this.mainNode = document.createElement('div');
	this.mainNode.style.height = this.height + 'px';
	this.mainNode.style.width = this.width + 'px';
	this.mainNode.style.overflow = 'hidden';
	
	this.plateNode = document.createElement('div');
	this.plateNode.style.position = 'relative';
	this.plateNode.style.top = '0px';
	this.plateNode.style.left = '0px';
	
	var tableAbstract = null;
	if (this.isIE) {
		this.tableNode = document.createElement('TBODY');
		tableAbstract = document.createElement('TABLE');
		this.tObj.attribute(tableAbstract, 'cellpadding', '0', 'cellspacing', '0', 'border', '0');
		tableAbstract.appendChild(this.tableNode);
	} else {
		this.tableNode = document.createElement('TABLE');
		this.tObj.attribute(this.tableNode, 'cellpadding', '0', 'cellspacing', '0', 'border', '0');
		tableAbstract = this.tableNode;
	}
	
	this.plateNode.appendChild(tableAbstract);
	this.mainNode.appendChild(this.plateNode);
	
	// Add the panes that we will be starting with
	for (var i=0; i<paneCount; i++) { this.addPane(); }
	
	// Here we go - add it to the document
	this.parentNode.appendChild(this.mainNode);
	
	// Now: I need <my> top and left so that I can position the 
	// screening graphic...
	this.myTop = getElementTop(this.mainNode);
	this.myLeft = getElementLeft(this.mainNode);
	

	// Now build the screen...
	var meRef = this;
	setTimeout( function() { meRef._installScreen.call(meRef); }, 100);
}

sliderClass.prototype._calcOffsetArray = function(theRate, theDistance)
{
	this.offsetArr = new Array();
	var slopes = new Array();
	
	var oneThird = theDistance / 3;
	var twoThird = oneThird * 2;
	oneThird = Math.round(oneThird);
	twoThird = Math.round(twoThird);
	
	// Setup basic vars...
	var stepPtr = 0;
	this.offsetArr[stepPtr] = 1;
	slopes[stepPtr] = 1;
	var lastStep = 1;
	
	// Do the left side accelleration...
	while (true)
	{
		stepPtr++;
		lastStep *= theRate;
		slopes[stepPtr] = lastStep;
		this.offsetArr[stepPtr] = this.offsetArr[stepPtr - 1] + Math.round(lastStep);
		if ((this.offsetArr[stepPtr] > oneThird) || (stepPtr > 50)) { break; }
	}
	largestStep = Math.round(lastStep);
	
	// do the midband...
	while (true)
	{
		stepPtr++;
		this.offsetArr[stepPtr] = this.offsetArr[stepPtr - 1] + largestStep;
		if ((this.offsetArr[stepPtr] > twoThird) || (stepPtr > 100)) { break; }
	}

	// right side decelleration...
	var lastPos = 0;
	var sMax = (slopes.length - 1);
	for (var i=0; i<=sMax; i++)
	{
		var ptr = stepPtr + i;
		this.offsetArr[ptr] = this.offsetArr[ptr - 1] + Math.round(slopes[sMax - i]);
		lastPos = this.offsetArr[ptr];
	}
	
	// Now: whatever the delta is, I'm going to add/subtract a pixel from each
	// entry so that I wind up just right - but I am going to skip 5 steps because
	// those first 5 are really important to the look and a single pixel from
	// later in the decelleration will be less detectable...
	var shim = 4;
	var stepsAvail = this.offsetArr.length - (shim * 2); // for both sides
	var delta = theDistance - lastPos;
	var pixOff = Math.abs(delta/stepsAvail);
	if (delta > 0)
	{
		while (delta > 0)
		{ 
			this.offsetArr[ptr--] += Math.round(delta);
			if (shim-- < 0) { delta -= pixOff; }
		}
	} else if (delta < 0) {
		while (delta < 0)
		{ 
			this.offsetArr[ptr--] += Math.round(delta);
			if (shim-- < 0) { delta += pixOff; }
		}
	}
}

sliderClass.prototype._handleTimer = function()
{
	var newPos = this.movePoints[this.currentPtr++];
	
	if (this.isHorizontal) { this.plateNode.style.left = newPos; }
	else { this.plateNode.style.top = newPos; }
	
	if (this.currentPtr < this.maxCount) 
	{ 
		var meRef = this;
		setTimeout( function() { meRef._handleTimer.call(meRef); }, 10);
	} else {
		if (this.afterSlide) { this.afterSlide(this, this.currentPane); }
		this.screenNode.style.display = 'none';
		
		if ((this.currentPane == 0) && (this.onFirstPane != null)) { this.onFirstPane(this); }
		else if ((this.currentPane == this.paneHandles.length - 1) && (this.onLastPane != null)) { this.onLastPane(this); }
		else if (this.onMidPane != null) { this.onMidPane(this, this.currentPane); }
		
		this.amSliding = false;
		
		// OK: If this.inWrap is true, then it means that I have just slid
		// onto a TEMPORARY cell that is at the end of the list and where I
		// really want to be is at pane 0. Set the pane to 0 then kill the temp cell.
		if (this.inWrap)
		{
			this.setCurrentPane(0);
			var target = this.lastPaneAdded.parentNode;
			target.removeNode(this.lastPaneAdded);
			this.paneHandles.length = this.paneHandles.length - 1;
			this.inWrap = false;
			this.lastPaneAdded = null;
		}
	}	
}

sliderClass.prototype._installScreen = function()
{
	this.screenNode = document.createElement('div');
	this.screenNode.style.display = 'none';
	this.screenNode.style.position = 'absolute';
	this.screenNode.style.top = this.myTop + 'px';
	this.screenNode.style.left = this.myLeft + 'px';
	this.screenNode.style.height = this.height + 'px';
	this.screenNode.style.width = this.width + 'px';
	this.screenNode.style.backgroundImage = "url('" + this.screenGraphic + "')";
	this.screenNode.style.backgroundRepeat = 'repeat';
	this.screenNode.style.zIndex = '100';
	var bodys = document.getElementsByTagName('body');
	bodys[0].appendChild(this.screenNode);

}

sliderClass.prototype._slideTowards0 = function(currentPos, msg)
{
	if (this.currentPane <= 0) { return false; }
	if (this.amSliding) { return false; }
	
	if (this.beforeSlide != null) 
	{ 
		if (!this.beforeSlide(this, this.currentPane, msg)) { return false; }
	}

	this.amSliding = true;
	this.screenNode.style.display = 'block';
	this.movePoints = new Array();
	this.maxCount = this.offsetArr.length;
	for (var i=0; i<this.maxCount; i++) { this.movePoints[i] = (currentPos + this.offsetArr[i]) + 'px'; }
	this.currentPtr = 0;
	this.currentPane--;
	
	var meRef = this;
	setTimeout( function() { meRef._handleTimer.call(meRef); }, 10);
	
	return true;
}

sliderClass.prototype._slideAwayFrom0 = function(currentPos, msg)
{
	if ((this.currentPane == (this.paneHandles.length - 1)) && (!this.wrapZero)) { return false; }
	if (this.amSliding) { return false; }
	
	if (this.beforeSlide != null) 
	{ 
		if (!this.beforeSlide(this, this.currentPane, msg)) { return false; }
	}

	// If we are at the max then it means the caller wants me to wrap to zero.
	// Make a temporary cell at (max + 1) and put cell zero's contents there...
	// then I'll wrap to it, toggling a flag so that I know when the slide is done
	// to "jump" to zero and kill the temporary cell...
	
	if (this.currentPane == (this.paneHandles.length - 1))
	{
		this.inWrap = true;
		this.addPane();
		this.setPaneHTML(this.paneHandles.length - 1, buff);
	}

	this.amSliding = true;
	this.screenNode.style.display = 'block';
	this.movePoints = new Array();
	this.maxCount = this.offsetArr.length;
	for (var i=0; i<this.maxCount; i++) { this.movePoints[i] = (currentPos - this.offsetArr[i]) + 'px'; }
	this.currentPtr = 0;
	this.currentPane++;
	
	var meRef = this;
	setTimeout( function() { meRef._handleTimer.call(meRef); }, 10);
	
	return true;
}

sliderClass.prototype.addPane = function()
{
	if (this.isHorizontal)
	{
		if (this.startingUp)
		{
			this.rowNode = document.createElement('tr');
			this.tObj.attribute(this.rowNode, 'valign', 'top');
			this.tableNode.appendChild(this.rowNode);
			this.startingUp = false;
		}
		
		var newPtr = this.paneHandles.length;
		var newDiv = document.createElement('div');
		newDiv.style.overflow = 'hidden';
		newDiv.style.height = this.height + 'px';
		newDiv.style.width = this.width + 'px';
		this.paneHandles[newPtr] = newDiv;
		var newCell = document.createElement('td');
		newCell.appendChild(newDiv);
		this.rowNode.appendChild(newCell);
		this.lastPaneAdded = newCell;
	} else {
		this.rowNode = document.createElement('tr');
		this.tObj.attribute(this.rowNode, 'valign', 'top');
		this.tableNode.appendChild(this.rowNode);
		
		var newPtr = this.paneHandles.length;
		var newDiv = document.createElement('div');
		newDiv.style.overflow = 'hidden';
		newDiv.style.height = this.height + 'px';
		newDiv.style.width = this.width + 'px';
		this.paneHandles[newPtr] = newDiv;
		var newCell = document.createElement('td');
		newCell.appendChild(newDiv);
		this.rowNode.appendChild(newCell);
		this.tableNode.appendChild(this.rowNode);
		this.lastPaneAdded = this.rowNode;
	}
	return newDiv;
}

sliderClass.prototype.debugOffsetArray = function()
{
	var outStr = '';
	var theMax = this.offsetArr.length;
	for (var i=0; i<theMax; i++)
	{
		outStr += 'offsetArray[' + i + '] = ' + this.offsetArr[i] + String.fromCharCode(10);
	}
	return outStr;
}

sliderClass.prototype.getCurrentPane = function() { return this.currentPane; }
sliderClass.prototype.getMainNode = function() { return this.mainNode; }
sliderClass.prototype.getPaneCount = function() { return this.paneHandles.length; }
sliderClass.prototype.getPaneNode = function(which) { return this.paneHandles[which]; }
sliderClass.prototype.setCurrentPane = function(thePaneID) 
{
	if ((thePaneID < 0) || (thePaneID >= this.paneHandles.length)) { return false; }
	
	this.currentPane = thePaneID;
	if (this.isHorizontal) { this.plateNode.style.left = (-(thePaneID * this.width)) + 'px'; }
	else {	this.plateNode.style.top = (-(thePaneID * this.height)) + 'px'; }

//	this._handleJump(thePaneID);
	
	if ((this.currentPane == 0) && (this.onFirstPane != null)) { this.onFirstPane(this); }
	else if ((this.currentPane == this.paneHandles.length - 1) && (this.onLastPane != null)) { this.onLastPane(this); }
	else if (this.onMidPane != null) { this.onMidPane(this, this.currentPane); }
		
}
sliderClass.prototype.setRate = function(newRate) { this._calcOffsetArray(newRate, (this.isHorizontal) ? this.width : this.height); }

sliderClass.prototype.setPaneHTML = function(paneID, theHTML)
{	
	var temp = this.paneHandles[paneID];
	if (!temp) { return false; }
	var ht = temp.style.height;
	var wd = temp. style.width;
	temp.innerHTML = theHTML;
	temp.style.height = ht;
	temp.style.width = wd;
}

sliderClass.prototype.setPaneNode = function(paneID, theNode)
{
	var temp = this.paneHandles[paneID];
	if (!temp) { return false; }
	var ht = temp.style.height;
	var wd = temp. style.width;
	temp.appendChild(theNode);
	temp.style.height = ht;
	temp.style.width = wd;
}

sliderClass.prototype.slideDown = function()
{
	if (this.isHorizontal) { return false; }
	var currentPos = parseInt(this.plateNode.style.top);
	return this._slideAwayFrom0(currentPos, 'down');
}

sliderClass.prototype.slideLeft = function()
{
	if (!this.isHorizontal) { return false; }
	var currentPos = parseInt(this.plateNode.style.left);
	return this._slideTowards0(currentPos, 'left');
}

sliderClass.prototype.slideRight = function()
{
	if (!this.isHorizontal) { return false; }
	var currentPos = parseInt(this.plateNode.style.left);
	return this._slideAwayFrom0(currentPos, 'right');
}

sliderClass.prototype.slideUp = function()
{
	if (this.isHorizontal) { return false; }
	var currentPos = parseInt(this.plateNode.style.top);
	return this._slideTowards0(currentPos, 'up');
}