
//	the timeout to start scrolling with
var start_scroll_speed = 300;
//	the index to the current scroll object. May be overridden by a client javascript
//	to make another scroll object initially visible
var current_scroller = 0;
//	The global Browser object
if(typeof(app)=='undefined'){
	if(!app){
		var app = new browser();
		if (app.compat_d_layers)	{
			document.getElementById = ns4_getLayerById;
		}
	}
}

// internal use only. Keep away from these!
var scroll_timer;
var curr_scrollspeed = start_scroll_speed;
var scroller;
var constructed = false;

var __version__ = "0.9";

// Degugging utility

function inspect(inObj)	{
	var s = "";
	s += inObj.length + "<br>\n";
	for (var p in inObj)	{
		s = s + p + ": " + inObj[p] + ";<br> ";
	}
	w = window.open("", "_blank");
	w.document.open();
	w.document.write(s);
	w.document.close();
}


//	Browser object which detects and stores commonly
//	used properties for the browser it is run on.
//	The library creates an instance of this object at parse time.
//	This instance has the name "app".
//
//	Interface:
//		properties: All properties are Read-Only (r/o)
//			compat_dom: does this browser support the W3 Document Object Model (r/o)
//			compat_d_all: does this browser support the MSIE Document Object Model (r/o)
//			compat_d_layers: does this browser support the Nestacpe 4 Document Object Model (r/o)
//			version_name: The string returned by navigator.appVersion (r/o)
//			ie5, ie4, ns4:	Shorthand boolean flag for the respective browser (r/o)
//			platform: OS the browser runs on (r/o)
//			scrollfactor: corrector to apply for different OS's interpretations of setTimeout delay value
//			supported_browser: true if the browser is fully supported by this library
//		methods:
//			scrollspeed(inSpeed): returns OS dependant corrected value of inSpeed


function browser()	{
	this.compat_dom = (document.getElementById) ? 1 : 0;
	this.compat_d_all = (document.all) ? 1 : 0;
	this.compat_d_layers = (document.layers) ? 1 : 0;
	this.version_name = navigator.appVersion;
	this.ie6 = ((this.version_name.indexOf('MSIE') != -1) && this.compat_dom);
	this.ie5 = ((this.version_name.indexOf('MSIE') != -1) && this.compat_dom);
	this.ie4 = (this.compat_d_all && !this.compat_dom);
	this.ns4 = (this.compat_d_layers && !this.compat_dom);
	this.ns6 = (this.compat_dom && (parseInt(this.version_name, 10) >= 5) && (navigator.userAgent.indexOf('Gecko') != -1));
	this.platform = (this.version_name.indexOf('Macintosh') != -1) ? 'mac' : ((this.version_name.indexOf('Win') != -1) ? 'win' : 'unix');
	this.scrollfactor = (this.platform == 'win') ? 10 : 1;
	this.scrollspeed = new Function ('inSpeed', 'return this.scrollfactor * inSpeed;');
	this.supported_browser = (this.op6 || this.ie6 || this.ie5 || (this.ie4 && (this.platform == "win")) || this.ns4 || this.ns6);

	return this;
}

// create an instance of the browser object in app


/*
	browser specific stuff
*/

/*
	Nestacpe 4 crap
*/

function ns4_getLayerById(inName)	{
	return ns4_searchTree(document, 'layers', inName);
}

function ns4_searchTree(inStart, inType, inName)	{
	var tree = inStart[inType];
	if (tree)	{
		for (var i = 0; i < tree.length; ++i)	{
			if (tree[i].name == inName)	{
				return tree[i];
			} else {
				var foundlayer = ns4_searchTree(tree[i], inType, inName);
				if (foundlayer) return foundlayer;
			}
		}
	}
	return null;
}

/* 
	IE specific crap
*/

function ie_get_fromAll(inID)	{
	return document.all[inID] || null;
}

function ie_getClipheight(inObj)	{
	if ( (app.ie5) || (app.ie6) )	{
		var t = inObj.content.ref.parentElement.currentStyle.clip;
		if (t && (t.length > 0))	{
			t = t.substring(t.indexOf("(")+1, t.indexOf(")"));
			var vals = t.split(" ");
			for (var i = 0; i < vals.length; ++i)	{
				vals[i] = parseInt(vals[i], 10);			
			}
			return  vals[2] - vals[0];
		} else {
			var vtop = inObj.content.ref.parentElement.currentStyle.clipTop;
			var vbot = inObj.content.ref.parentElement.currentStyle.clipBottom;
			if (vtop && vbot)	{
				vbot = parseInt(vbot, 10);
				vtop = parseInt(vtop, 10);
				return vbot - vtop;
			}
		}
	} else {	// this is ie4
		var parentID = inObj.content.ref.parentElement.id;
		for (i = 0; i < document.styleSheets.length; ++i)	{
			var css = document.styleSheets[i].cssText;
			if (css)	{
				var o;
				o = css.indexOf("#"+parentID);
				if (o == -1)	continue;
				o = css.indexOf("{", o);
				p = css.indexOf("}", o);
				var s = css.substring(o+1,p);
				if (s.length == 0) break;

				var beg = s.indexOf("clipBottom:");
				var end = s.indexOf(";", beg);
				var vbot = s.substring(beg + 1+ "clipBottom:".length, end);
				vbot = parseInt(vbot, 10);
				beg = s.indexOf("clipTop:");
				end = s.indexOf(";", beg);
				var vtop = s.substring(beg + 1+ "clipTop:".length, end);
				vtop = parseInt(vtop, 10);
				return vbot - vtop;
			}
		}
	}
//	if everything before failed return at least something
//	assuming 75% percent of divider's height is visible
//	this is main intended for braindead MSIE 4 on Mac
	return Math.floor(inObj.content.ref.parentElement.clientHeight * 0.75);
}

/* end of browser specific stuff */


//	An abstract DIVIDER object
//	purpose is to supply acces and manipulation of commonly used 
//	Divider properties independent of browser-implementation
//
//	Clients should not write to the properties of the div_obj object
//	but rather use the accessor methods provided. All prpoerties should be
//	treated Read-Only
//	Interface:
//		Properties:
//			ref:	a reference to the underlying browser specific DIVIDER or LAYER object
//			style:	a reference to the associated STYLE object (also pretty browser dependant)
//
//			get_top(), get_left(): return the current values of the top and left coordinates of the divider/layer
//			move_top_by(inVal), move_left_by(inVal): Move the divider by the amount of pixels in inVal
//											return the previous value of the effected coordinate
//			move_top_to(inVal), move_left_to(inVal): Move the divider to the specified value in inVal
//											return the previous value of the effected coordinate
//			show(), hide():	make the divider visible or invisible


function divider_obj(inID)	{

// properties
	this.ref = (app.compat_dom) ? document.getElementById(inID) : ((app.compat_d_all) ? document.all[inID] : ns4_getLayerById(inID));
	this.style = (app.ns4) ? this.ref : this.ref.style;

// methods
//	for performance reasons, we disparch to different function
//	depending on the browser, thus avoiding any if/else branches during runtime
	this.get_top = (app.compat_d_all) ? __divider_obj_get_top_all : ((app.compat_dom) ? __divider_obj_get_top_dom : __divider_obj_get_top_ns);
	this.get_left = (app.compat_d_all) ? __divider_obj_get_left_all : ((app.compat_dom) ? __divider_obj_get_left_dom : __divider_obj_get_left_ns);
	this.move_top_by = (app.compat_d_all) ? __divider_obj_move_top_by_all : __divider_obj_move_top_by;
	this.move_left_by = (app.compat_d_all) ? __divider_obj_move_left_by_all : __divider_obj_move_left_by;
	this.move_top_to = (app.compat_d_all) ? __divider_obj_move_top_to_all : __divider_obj_move_top_to;
	this.move_left_to = (app.compat_d_all) ? __divider_obj_move_left_to_all : __divider_obj_move_left_to;

	this.show = __divider_obj_show;
	this.hide = __divider_obj_hide;
	return this;
}


//	divider_obj implementation part
//	get_top
function __divider_obj_get_top_dom()	{
	return this.ref.offsetTop;
}
function __divider_obj_get_top_all()	{
	return  this.ref.style.posTop;
}
function __divider_obj_get_top_ns()	{
	return  this.ref.top;
}
//	get_left
function __divider_obj_get_left_dom()	{
	return this.ref.offsetLeft;
}
function __divider_obj_get_left_all()	{
	return this.ref.style.posLeft;
}
function __divider_obj_get_left_ns()	{
	return this.ref.left;
}
//	move_top_by
function __divider_obj_move_top_by_all(inVal)	{

	var t = this.get_top(); 
	this.style.posTop -= inVal;
	return t;
}
function __divider_obj_move_top_by(inVal)	{
	var t = this.get_top(); 
	this.style.top = (t -= inVal);
	return t;
}
//	move_left_by
function __divider_obj_move_left_by_all(inVal)	{
	var t = this.get_left(); 
	this.style.posLeft -= inVal; 
	return t;
}
function __divider_obj_move_left_by(inVal)	{
	var t = this.get_left(); 
	this.style.left = ( t -= inVal); 
	return t;
}
//	move_top_to
function __divider_obj_move_top_to_all(inVal)	{
	var t = this.get_top(); 
	this.style.posTop = -inVal;
	return t;
}
function __divider_obj_move_top_to(inVal)	{
	var t = this.get_top(); 
	this.style.top = -inVal;
	return t;
}
//	move_left_to
function __divider_obj_move_left_to_all(inVal)	{
	var t = this.get_left(); 
	this.style.posLeft = -inVal; 
	return t;
}
function __divider_obj_move_left_to(inVal)	{
	var t = this.get_left(); 
	this.style.left = -inVal; 
	return t;
}

function __divider_obj_show()	{
	this.style.visibility = 'visible';
}

function __divider_obj_hide()	{
	this.style.visibility = 'hidden';
}


// end divider_obj implementation part


// Scroll object
// It assumes to be placed inside a container divider to supply a clipping mask
// It groups together the scrollable object and its User-Interface element to control scrolling
// Configures itself to the actual the dimensions of the embedded dividers
// Automatically hides the User-interface controller if all content
// fits into the mask (thus no scrolling necessary).
// Supports pagewise scrolling on mouse-click as well as continuos scrolling
// Dynamically adjusts the scrolling speed
// 


function scroll_obj(inContentID, inController, inDoHide)	{

	this.content = new divider_obj(inContentID);
	this.controller = new divider_obj(inController);
		
	this.mask = new divider_obj((app.compat_dom) ? this.content.ref.parentNode.getAttribute("id") : ((app.compat_d_all) ? this.content.ref.parentElement.id : this.content.ref.parentLayer.name));
	this.content_len = (app.compat_d_all) ? this.content.ref.scrollHeight : (app.compat_d_layers ? this.content.ref.clip.height : (this.content.ref.offsetHeight || this.content.style.pixelHeight));//)
	
	this.clip_height = this.content_len;	// use this if we cannot find a better value
	if (app.compat_d_layers)	{

		var par = this.content.ref.parentLayer;
		if (par)
			this.clip_height = par.clip.height;
	}	else if (app.compat_d_all)	{
		this.clip_height = ie_getClipheight(this);
	}	else	{
		this.clip_height = this.content.ref.parentNode.offsetHeight  || this.content.ref.parentNode.style.pixelHeight;
	}
	if ((this.content_len <= this.clip_height)	&& inDoHide) {
		this.controller.hide();
	}
	this.still_in_bounds = __scroll_obj_still_in_bounds;
	this.adjust_offset = __scroll_adjust_offset;
	this.hide = __scroll_obj_hide;
	this.show = __scroll_obj_show;
	return this;
}

//	scroll_obj implementation

function __scroll_obj_still_in_bounds(inOffset)	{
	var newPos = this.content.get_top() - inOffset;  
	return ((newPos <= 0) && (newPos > - (this.content_len - this.clip_height)));
}

function __scroll_adjust_offset(inOffset)	{
	var retval = 0;

	if (this.still_in_bounds(inOffset))	{
		retval = inOffset;
	} else 	if (inOffset > 0)	{

		retval = ((this.content_len - this.clip_height) + this.content.get_top());
	} else if (inOffset < 0)	{
		retval =  (this.content.get_top());
	}

	return retval; 
}

function __scroll_obj_hide()	{
	this.mask.hide();
	this.content.hide(); 
	this.controller.hide();
}

function __scroll_obj_show()	{
	this.mask.show();
	this.content.show(); 
	if (this.content_len > this.clip_height) 
		this.controller.show();
}

// A mutually exclusive group of scroll objects.
// This is used to implement switching between different scrollers
// only one to visible at any given time
//
// Several scoll_mutex objects can be contained in a html-page

function scroll_mutex(inMemberlist)	{

	this.current_scroller = 0;
	this.curr_scrollspeed = start_scroll_speed;
	this.scroll_timer = 0;
	this.scrollers = new Array (inMemberlist.length / 2);
	for (var i = 0; i < this.scrollers.length; ++i)	{
		var j = i * 2;
		var obj = new scroll_obj(inMemberlist[j], inMemberlist[j + 1], i == this.current_scroller);
		this.scrollers[i] = obj;
	}
	this.do_scroll = __scroll_mutex_do_scroll;
	this.stop_scroll = __scroll_mutex_stop_scroll;
	this.still_in_bounds = __scroll_mutex_still_in_bounds;
	this.adjust_offset = __scroll_mutex_adjust_offset;
	this.swap_scroller = __scroll_mutex_swap_scroller;
	this.move_top_to = __scroll_mutex_move_top_to;
	this.move_left_to = __scroll_mutex_move_left_to;
	this.move_top_by = __scroll_mutex_move_top_by;
	this.move_left_by = __scroll_mutex_move_left_by;
	this.get_clip_height = __scroll_mutex_get_clip_height;
	this.get_content_len = __scroll_mutex_get_content_len;
	this.do_page_scroll = __scroll_mutex_do_page_scroll;
	this.show = __scroll_mutex_show;
	this.hide = __scroll_mutex_hide;

	this.get_top = __get_top;
	
	return this;
}


// scroll_mutex implementation part

/***************** ermittelt den aktuellen oberen scrollabstand, fs *******************/ 

function __get_top(){

	return this.scrollers[this.current_scroller].content.get_top();
}

/***************** einfuegung ende *******************/ 

function __scroll_mutex_do_scroll(inOffset, inMutex)	{
	var newOffset = this.adjust_offset(inOffset);
	if (newOffset)	{
		if (this.curr_scrollspeed != start_scroll_speed)			
			this.scrollers[this.current_scroller].content.move_top_by(newOffset);		
		this.scroll_timer = setTimeout('do_scroll(' + newOffset + ', '+ inMutex + ')', this.curr_scrollspeed);

		if (this.curr_scrollspeed > 40)
			this.curr_scrollspeed -= 15;
	}
}

function __scroll_mutex_stop_scroll()	{
	if (this.scroll_timer)	
		clearTimeout(this.scroll_timer);
	this.scroll_timer = null;
	this.curr_scrollspeed = start_scroll_speed;
}

function __scroll_mutex_still_in_bounds(inOffset)	{
	return this.scrollers[this.current_scroller].still_in_bounds(inOffset);
}

function __scroll_mutex_adjust_offset(inOffset)	{
	return this.scrollers[this.current_scroller].adjust_offset(inOffset);
}

function __scroll_mutex_swap_scroller(inNew)	{
	if ((inNew >= 0) && (inNew < this.scrollers.length))	{
		this.scrollers[this.current_scroller].hide();
		this.current_scroller = inNew;
		this.scrollers[this.current_scroller].show();
	}
}

function __scroll_mutex_move_top_to(inNewPos)	{
	this.scrollers[this.current_scroller].content.move_top_to(inNewPos);
}

function __scroll_mutex_move_left_to(inNewPos)	{
	this.scrollers[this.current_scroller].content.move_left_to(inNewPos);
}

function __scroll_mutex_move_top_by(inOffset)	{
	this.scrollers[this.current_scroller].content.move_top_by(inOffset);
}

function __scroll_mutex_move_left_by(inOffset)	{
	this.scrollers[this.current_scroller].content.move_left_by(inOffset);
}

function __scroll_mutex_get_clip_height()	{
	return this.scrollers[this.current_scroller].clip_height;
}

function __scroll_mutex_get_content_len()	{
	return this.scrollers[this.current_scroller].content_len;
}

function __scroll_mutex_do_page_scroll(inDirection)	{
	this.stop_scroll();
	var realoffset = this.get_clip_height();
	realoffset = realoffset * ((inDirection < 0) ? -1 : 1) - 16;

	if (!this.still_in_bounds(realoffset))	{
		realoffset = this.scrollers[this.current_scroller].content.get_top();
		if  (inDirection > 0)	{
			realoffset +=  (this.get_content_len() - this.get_clip_height());
		}
	}
	this.move_top_by(realoffset);
}

function __scroll_mutex_show()	{
	this.scrollers[this.current_scroller].show();
}

function __scroll_mutex_hide()	{
	this.scrollers[this.current_scroller].hide();
}

// end scroll_mutex implementation

function __ckec_mutex(inMutex)	{
	return (inMutex && (inMutex >=0) && (inMutex < scroller.length)) ? inMutex : 0;
}


// The functions should be used within an HTML page to control the scrolling dividers
// 
//	Avoid peeking directly into any of the objects defined above
//	or you'll break the browser independant abstraction this library provides

/***************** ermittelt und setzt den oberen scrollabstand, fs *******************/ 

function get_scroll_top(inMutex)	{
	if (constructed && scroller)	{
		return scroller[__ckec_mutex(inMutex)].get_top();
	}
}

function set_top(top_value,inMutex)	{
	if (constructed && scroller)	{
		scroller[__ckec_mutex(inMutex)].move_top_to(top_value);
	}
}

/***************** einfuegung ende *******************/

function do_pagescroll(inDirection, inMutex)	{
	if (constructed && scroller)	{
		scroller[__ckec_mutex(inMutex)].do_page_scroll(inDirection);
	}
}

function swap_scroller(inNew, inMutex)	{
	if (constructed && scroller)	{
		scroller[__ckec_mutex(inMutex)].swap_scroller(inNew);
	}
}

function backToTop(inMutex)	{
	if (constructed && scroller)	{
		var m = __ckec_mutex(inMutex);
		scroller[m].stop_scroll();
		scroller[m].move_top_to(0);
	}
}

function __scroll_mutex_show()	{
		this.scrollers[this.current_scroller].show();
}

function __scroll_mutex_hide()	{
		this.scrollers[this.current_scroller].hide();
}


function do_scroll(inOffset, inMutex)	{
	if (constructed && scroller)	{
		inMutex = __ckec_mutex(inMutex);

		scroller[inMutex].do_scroll(inOffset, inMutex);
	}
}

function stop_scroll(inMutex)	{
	if (constructed && scroller)	{
		inMutex = __ckec_mutex(inMutex);
		scroller[inMutex].stop_scroll();
	}
}



//	scrollinit()
//	Must be called by onLoad handler

function scrollinit(inArray)	{
	if 	((typeof(inArray) == "object") && (inArray.length > 0))	{
		if (typeof(inArray[0]) == "string")	{
			scroller = new Array (1);
			var obj = new scroll_mutex(inArray);
			scroller[0] = obj;
		} else	{
			scroller = new Array (inArray.length);
			for (var i = 0; i < inArray.length; ++i)	{
				var obj = new scroll_mutex(inArray[i]);
				scroller[i] = obj;
			}
		}
	}
	constructed = true;
}

