/**
 * pane.js - a collection of methods for asynchronous markup loading and presentation management
 * author: Alex Vigdor
 **/
if(typeof allPanesOnPage=='undefined'){
var allPanesOnPage = new Object();
var urlLoaderBase = '/javascript/loadmarkup?include=';
// a place to store Markup objects that cache content on or off screen
var urlMarkupFragments = new Object();
// urlMarkupListeners[url] = Array of callback functions with signature function(Markup)
var urlMarkupListeners = new Object();
//a place to store temporary dynamic script tags
var scriptElements = new Object();

//remove event handlers on unload
addOnunload(function(){
	var url, markup,pane,id;
	for(url in urlMarkupFragments){
		markup = urlMarkupFragments[url];
		markup.destroy();
		urlMarkupFragments[url]=null;
	}
	for(id in allPanesOnPage){
		pane=allPanesOnPage[id];
		pane.destroy();
		allPanesOnPage[id]=null;
	}
});

// this is the callback function for dynamic markup loaders
function setMarkup(url, markup){
	//alert('setting markup '+markup+' for '+url);
	var mo = urlMarkupFragments[url];
	if(mo == null){
		mo = new Markup();
		urlMarkupFragments[url] = mo;
	}
	mo.setMarkup(markup);
	notifyListeners(url,mo);
	removeScriptTag(url);
	return mo;
}

function notifyListeners(url, markup){
	var listeners = urlMarkupListeners[url];
	if(listeners != null){
		while(listeners.length > 0){
			listeners.shift()(markup);
		}
	}
}

function removeScriptTag(url){
	var script = scriptElements[url];
	if(script != null){
		script.parentNode.removeChild(script);
		scriptElements[url]=null;
	}
}

/**
 * Kick off an asynchronous markup loader request
 * maxage = maximum age in milliseconds of cached content (-1 for no expiration)
 * callback = function that takes a single Markup object as an argument
 **/
function getMarkup(url, maxage, callback){
	if(callback!=null){
		var listeners = urlMarkupListeners[url];
		if(listeners == null){
			listeners = new Array();
			urlMarkupListeners[url] = listeners;
		}
		listeners.push(callback);
	}
	var mo = urlMarkupFragments[url];
	if((mo!= null) && (mo.isLoaded()) && (maxage >= 0) && (mo.getAge() > maxage)){
		mo = null;
	}
	if(mo==null){
		mo = new Markup();
		urlMarkupFragments[url] = mo;
		var head = document.getElementsByTagName('head')[0];
		if(head==null){
			head = document.getElementsByTagName('body')[0];
		}
		var script = document.createElement('script');
		script.setAttribute("type", "text/javascript"); 
		script.src = urlLoaderBase+encodeURIComponent(url);
		head.appendChild(script); 
		scriptElements[url] = script;
	}
	else if(mo.isLoaded()){
		notifyListeners(url,mo);
	}
}


/**
 * Represents a markup fragment, with a notion of age and a DOM element cache
 * Arguments: 
 **/
function Markup(){
	this.markup = null;
	this.contentElement = null;
	this.loaded = null;
	this.scripts = new Array();
}

Markup.prototype.getContentElement = function(){
	if(this.contentElement == null){
		this.parse();
	}
	return this.contentElement;
}

Markup.prototype.getAge = function(){
	return new Date().getTime() - this.loaded.getTime();	
}

Markup.prototype.parse = function(){
	if(this.contentElement == null){
		var el = document.createElement('div');
		el.innerHTML = this.markup;
		this.contentElement = el;
	}
}

Markup.prototype.isLoaded = function(){
	return this.markup !=null;
}

Markup.prototype.setMarkup = function(markup){
	this.destroy();
	this.markup = this.extractJS(markup);
	this.loaded = new Date();
}

Markup.prototype.destroy = function(){
	if(this.contentElement!=null){
		removeEventHandlers(this.contentElement,true);
		this.contentElement=null;
	}
	for(var i=0;i<this.scripts.length;i++){
		this.scripts[i].destroyScript();
	}
	this.scripts = new Array();
	this.markup=null;
	this.loaded=null;
}


/**
 *	This function uses regular expressions to find script tags in the string of markup;
 *  it will load each script tag into a scriptHolder, and remove the tag from the
 *  markup string.  The marjkup string with all scripts removed is returned.
 *  This should be executed before the markup string is set.
 **/
Markup.prototype.extractJS = function(markup){
	var scriptMatcher = /<script [^>]*?(?:(type|src)\s*=\s*(["'])(.*?)\2)\s*(?:(type|src)\s*=\s*(["'])(.*?)\5)?[^>]*?>([\s\S]*?)<\/script>/mi;
	while((match = scriptMatcher.exec(markup)) != null){
		var sh = new ScriptHolder();
		if(match[1]){
			sh[match[1]] = match[3];
		}
		if(match[4]){
			sh[match[4]] = match[6];
		}
		if(match[7]){
			sh.text = match[7].replace(/\s*[\n]\s*/g,'\n');
			sh.text = sh.text.replace(/[ \t]+/g,' ');
		}
		this.scripts.push(sh);
		markup = markup.replace(match[0],'\n');
	}
	return markup;
}

function ScriptHolder(){
	this.type = null;
	this.src = null;
	this.text = null;
	this.el = null;
}

ScriptHolder.prototype.deployScript = function(){
  if(this.el==null){
	this.el = document.createElement("script");
    this.el.type = "text/javascript";
    if(this.src == null){
    	if(this.text != null){	
    		try{
    			this.el.appendChild(document.createTextNode(this.text));
    		}
    		catch(e){
    			//IE throws an exception here, but supports the text property as an alternative
    			this.el.text = this.text;
    		}
	    }
	}
	else{
		this.el.src = this.src;
	}
	getHeadElement().appendChild(this.el);
  }
}

ScriptHolder.prototype.destroyScript = function(){
	if(this.el != null){
		getHeadElement().removeChild(this.el);
		this.el = null;
	}
}

//given a DOM object, cycle through its ancestors until you find one 
//whose ID corresponds to a known pane.
function findPane(obj){
	var par = obj;
	while(par!=null){
		var id = par.id;
		if(id!=null){
			var pane = getPane(id);
			if(pane!=null){
				return pane;
			}
		}
		par = par.parentNode;
	}
}

function getPane(id){
	return allPanesOnPage[id];
}

function getContainedPanes(obj){
	var list = new Array(0);
	getContainedPanesRecursive(obj,list);
	return list;
}

function getContainedPanesRecursive(obj,arr){
	if(obj.firstChild!=null){
		var len = obj.childNodes.length;
		for(var i=0;i<len;i++){
			var node = obj.childNodes[i];
			if(node.nodeType == 1){
				if(node.id && allPanesOnPage[node.id]){
					arr.push(allPanesOnPage[node.id]);
				}
				getContainedPanes(node,arr);
			}
		}
	}
}

function Pane(id){
	this.id = id;
	this.host = document.getElementById(id);
	allPanesOnPage[id]=this;
	this.markup;
	//what is the maximum age allowed for content in this pane in milliseconds
	this.maxage=600000;
	this.currentURL = null;
	//Should this pane automatically reload its contents when the maxage is exceeded?
	this.autoReload = false;
	this.timer=null;
	this.transition = null;
	
	this.shell = document.createElement('div');
	this.shell.style.position="absolute";
	this.shell.style.overflow="hidden";
	this.host.appendChild(this.shell);
	
	this.viewport = document.createElement('div');
	this.viewport.style.position="absolute";
	this.viewport.style.overflow = "hidden";
	
	this.shell.appendChild(this.viewport);
	this.staging = document.createElement('div');
	this.staging.style.position="absolute";
	this.staging.style.visibility="hidden";
	this.shell.appendChild(this.staging);
	this.content= null;
	this.contentHeight=0;
	this.contentWidth = 0;
	this.oldContent = null;
	//var resize = makePaneResize(this);
	//addOnload(resize);
	//addResize(resize);
	
	this.minScroll=10;
	this.scrollbarWidth = 17;
	this.scrollbarBorderWidth = 0;
	this.scrollFirstInterval = 300;
	this.scrollInterval=20;
	this.scrollFactorX = 1;
	this.scrollFactorY = 1;
	this.engaged = false;
	this.disengageTimer = null;
	this.scrollTimer = null;
	this.currentAnimation = null;
	
	this.ismouseover=false;
	this.statechanges = new Array();
	this.mouseovers = new Array();
	this.mouseouts = new Array();
	this.mousedowns = new Array();
	this.mousewheels = new Array();
	this.engages = new Array();
	this.disengages =  new Array();
	this.onloads = new Array();
	this.onloader = makePaneOnloader(this);
	
	this.shell.onmouseover = makePaneMouseOver(this);
	this.shell.onmouseout = makePaneMouseOut(this,this.host);
	this.shell.onmousedown = makePaneMouseDown(this);
	this.shell.onmousewheel = makeScrollWheel(this);
	if (this.shell.addEventListener){
		this.shell.addEventListener('DOMMouseScroll', this.shell.onmousewheel, false);
	}
	
}

Pane.prototype.destroy = function(){
	this.shell.onmouseover=null;
	this.shell.onmousoeut=null;
	this.shell.onmousedown=null;
	if (this.shell.addEventListener){
		this.shell.removeEventListener('DOMMouseScroll', this.shell.onmousewheel,false);
	}
	this.shell.onmousewheel=null;
	this.shell=null;
	this.content=null;
	this.oldContent = null;
	this.staging=null;
	this.viewport=null;
	this.host=null;
	if(this.yscrollbar != null){
		this.ytrough.onmousedown=null;
		this.upscroller.onmousedown=null;
		this.downscroller.onmousedown=null;
		this.yscrollbar=null;
		this.yscroller=null;
		this.yscrollertop=null;
		this.yscrollerbottom=null;
		this.ytrough=null;
		this.upscroller=null;
		this.downscroller=null;
	}
	if(this.xscrollbar != null){
		this.xtrough.onmousedown=null;
		this.leftscroller.onmousedown=null;
		this.rightscroller.onmousedown=null;
		this.xscrollbar=null;
		this.xscroller=null;
		this.xscrollerleft=null;
		this.xscrollerright=null;
		this.xtrough=null;
		this.leftscroller=null;
		this.rightscroller=null;
	}
}

function makePaneResize(pane){
	var pane = pane;
	return function(){
		try{
			pane.resize();
		}
		catch(e){alert(e.message)}
	}
}
//indicate whether user is currently interacting with this pane
Pane.prototype.engage = function(){
	if(!this.engaged){
		this.engaged = true;
		//run this panes engage handlers, and disengage other panes
		var len = this.engages.length;
    	for(var i=0;i<len;i++){
    		this.engages[i]();
    	}
    	for(id in allPanesOnPage){
    		if(id != this.id){
    			allPanesOnPage[id].disengage();
    		}
    	}
	}
}

Pane.prototype.disengage = function(){
	if(this.engaged){
		this.engaged=false;
		var len = this.disengages.length;
    	for(var i=0;i<len;i++){
    		this.disengages[i]();
    	}
	}
}

Pane.prototype.resize = function(){
	this.getHostDimensions();
	this.refreshScrollbars();
	this.display();
}


//the purpose of this method is to resize the pane according to the size of the host element
Pane.prototype.getHostDimensions = function(){
	this.width=this.host.clientWidth;
	this.height=this.host.clientHeight;
	this.shell.style.width = this.width+"px";
	this.shell.style.height = this.height+"px";
	this.viewport.style.width = this.width+"px";
	this.viewport.style.height = this.height+"px";
	this.viewportWidth=this.width;
	this.viewportHeight=this.height;
}

Pane.prototype.refreshScrollbars = function(){
	if(this.yscrollbar != null){
		this.yscrollbar.style.width = (this.scrollbarWidth - 2*this.scrollbarBorderWidth) + "px";
		this.yscrollbar.style.left = (this.width - this.scrollbarWidth )+"px";
	}
	if(this.xscrollbar != null){
		this.xscrollbar.style.height = (this.scrollbarWidth - 2*this.scrollbarBorderWidth) + "px";
		this.xscrollbar.style.top = (this.height - this.scrollbarWidth)+"px";
	}
}

Pane.prototype.setViewportWidth = function(width){
	this.viewportWidth = width;
	this.viewport.style.width = width+"px";	
}

Pane.prototype.setViewportHeight = function(height){
	this.viewportHeight = height;
	this.viewport.style.height = height+"px";
}

Pane.prototype.stageContent = function(){
	if(!this.width){
		this.getHostDimensions();
	}
	if(this.content != null){
		this.oldContent = this.content;
	}
	this.content = this.markup.getContentElement();
	this.content.style.position="absolute";
	this.content.style.top="0px";
	this.content.style.left="0px";
	this.content.style.overflow="visible";
	this.content.style.width = "";
	this.content.style.height = "";
	while(this.staging.firstChild != null){
		this.staging.removeChild(this.staging.firstChild);
	}
	if(this.yscrollbar!=null){
		this.staging.style.width = this.viewportWidth+"px";
	}
	else if(this.xscrollbar!=null){
		this.staging.style.height = this.viewportHeight+"px";
	}
	this.staging.appendChild(this.content);
	this.contentWidth = this.content.clientWidth;
	this.contentHeight = this.content.clientHeight;
	this.content.style.width = (this.contentWidth)+"px";
	this.content.style.height = (this.contentHeight)+"px";
	this.staging.removeChild(this.content);
	this.scrollFactorY = this.contentHeight/this.viewportHeight;
	this.scrollFactorX = this.contentWidth/this.viewportWidth;
}

Pane.prototype.redrawScrollbars = function(){
	if(this.yscroller!=null){
		var yh = this.yscroller.style.top;
	}
	this.content.style.width = "";
	this.content.style.height = "";
	this.contentWidth = this.content.clientWidth;
	this.contentHeight = this.content.clientHeight;
	this.content.style.width = (this.contentWidth)+"px";
	this.content.style.height = (this.contentHeight)+"px";
	this.scrollFactorY = this.contentHeight/this.viewportHeight;
	this.scrollFactorX = this.contentWidth/this.viewportWidth;
	this.renderScrollbars();
	if(this.yscroller!=null){
		this.yscroller.style.top = yh;
	}
}

Pane.prototype.addEngage = function(handler){
	this.engages.push(handler);
}

Pane.prototype.removeEngage = function(handler){
	this.engages.remove(handler);
}

Pane.prototype.addDisengage = function(handler){
	this.disengages.push(handler);
}

Pane.prototype.removeDisengage = function(handler){
	this.disengages.remove(handler);
}

Pane.prototype.addOnload = function(handler){
	this.onloads.push(handler);
}

Pane.prototype.removeOnload = function(handler){
	this.onloads.remove(handler);
}

Pane.prototype.addMouseOver = function(handler){
	this.mouseovers.push(handler);
}

Pane.prototype.addMouseOut = function(handler){
	this.mouseouts.push(handler);
}

Pane.prototype.addMouseDown = function(handler){
	this.mousedowns.push(handler);
}

Pane.prototype.addStateChange = function(handler){
	this.statechanges.push(handler);
}

Pane.prototype.removeStateChange = function(handler){
	this.statechanges.remove(handler);
}

Pane.prototype.removeMouseDown = function(handler){
	this.mousedowns.remove(handler);
}

Pane.prototype.removeMouseOver = function(handler){
	this.mouseovers.remove(handler);
}

Pane.prototype.removeMouseOut = function(handler){
	this.mouseouts.remove(handler);
}

Pane.prototype.addMouseWheel = function(handler){
	this.mousewheels.push(handler);
}

Pane.prototype.removeMouseWheel = function(handler){
	this.mousewheels.remove(handler);
}

function makeScrollWheel(pane){
	var pane = pane;
	return function(e){
		if(!e){
    		var e = event;
    	}
    	var returnval = true;
    	var len = pane.mousewheels.length;
    	for(var i=0;i<len;i++){
    		if(!pane.mousewheels[i](e)){
    			returnval = false;
    		}
    	}
    	if(!returnval){
    		stopEvent(e);
    	}
    	return returnval;
	}
}

function makePaneMouseDown(pane){
	var pane = pane;
	return function(e){
		if(!e){
    		var e = event;
    	}
    	var returnval = true;
   		var len = pane.mousedowns.length;
    	for(var i=0;i<len;i++){
    		if(!pane.mousedowns[i](e)){
    			returnval = false;
    		}
    	}
    	if(!returnval){
    		stopEvent(e);
    	}
    	return returnval;
	}
}


function makePaneMouseOver(pane){
	var pane = pane;
	return function(e){
		if(!e){
    		var e = event;
    	}
    	var returnval = true;
    	if(!pane.ismouseover){
    		pane.ismouseover = true;
    		var len = pane.mouseovers.length;
	    	for(var i=0;i<len;i++){
	    		if(!pane.mouseovers[i](e)){
	    			returnval = false;
	    		}
	    	}
    	}
    	if(!returnval){
    		stopEvent(e);
    	}
    	return returnval;
	}
}

function makePaneMouseOut(pane,ancestor){
	var pane = pane;
	return function(e){
		if(!e){
    		var e = event;
    	}
    	var returnval = true;
    	if(pane.ismouseover){
    		var landing = e.relatedTarget || e.toElement;
    		if(landing==null || !descendsFrom(landing,ancestor)){
    			pane.ismouseover = false;
			var len = pane.mouseouts.length;
			for(var i=0;i<len;i++){
				if(!pane.mouseouts[i](e)){
					returnval = false;
				}
			}
    		}
    	}
    	if(!returnval){
    		stopEvent(e);
    	}
    	return returnval;
	}
}

Pane.prototype.reload = function(){
	if(this.currentURL != null){
		this.loadURL(this.currentURL);
	}
}

Pane.prototype.setTransition = function(trans){
	this.transition = trans;
}

Pane.prototype.loadURL = function(url,callback,transition){
	if(this.timer != null){
		window.clearTimeout(this.timer);
		this.timer = null;
	}
	this.currentURL = url;
	this.processStateChange('loading');
	getMarkup(url,this.maxage,makePaneCallback(this,callback,transition));
}

Pane.prototype.setAutoReload = function(bool){
	this.autoReload = bool;
}

Pane.prototype.setMarkup = function(markup){
	this.cancelAnimation();
	if(this.markup!=null){
		this.unloadScripts();
	}
	this.markup = markup;
	this.display();
}

Pane.prototype.display = function(){
	if(this.markup){
		this.processStateChange('displaying');
		this.stageContent();
		this.renderScrollbars();
		if(this.transition){
			this.transition.addCallback(this.onloader);
			this.animate(this.transition);
		}
		else{
			this.doOnload();
		}
	}
}

function makePaneOnloader(pane){
	var pane = pane;
	return function(){
		pane.doOnload();
	}
}

Pane.prototype.doOnload = function(){
	var cn = this.viewport.childNodes;
	var cnl = cn.length;
	var node, i;
	for(i=cnl-1;i>=0;i--){
		node = cn[i];
		if(node!=this.content){
			this.viewport.removeChild(node);
		}
	}
	if(this.content.parentNode != this.viewport){
		this.viewport.appendChild(this.content);
	}
	this.loadScripts();
	var len = this.onloads.length;
	for(i=0;i<len;i++){
		this.onloads[i]();
	}
	this.processStateChange('done');
}

Pane.prototype.cancelAnimation = function(){
	if(this.currentAnimation !=null && this.currentAnimation.running){
		if(masterAnimationController){
			masterAnimationController.cancel(this.currentAnimation);
		}
		else{
			this.currentAnimation.cancel(true);
		}
	}
}

Pane.prototype.animate = function(animation){
	this.currentAnimation = animation;
	this.currentAnimation.reset();
	if(masterAnimationController){
		masterAnimationController.animate(this.currentAnimation);
	}
	else{
		this.currentAnimation.run();
	}
}

Pane.prototype.processStateChange = function(state){
	var len = this.statechanges.length;
   	for(var i=0;i<len;i++){
   		this.statechanges[i](state);
   	}
}

Pane.prototype.loadScripts = function(){
	var len = this.markup.scripts.length;
	for(var i=0;i<len;i++){
		this.markup.scripts[i].deployScript();
	}
}

Pane.prototype.unloadScripts = function(){
	var len = this.markup.scripts.length;
	for(var i=0;i<len;i++){
		this.markup.scripts[i].destroyScript();
	}
}

Pane.prototype.setMaxAge = function(millis){
	this.maxage = millis;
}

function makePaneCallback(pane,callback,transition){
	var pane = pane;
	var callback = callback;
	var transition = transition;
	return function(markup){
		var docallback = (typeof callback == 'function');
		if(!transition && pane.transition){
			transition = pane.transition;
		}	
		if(!isEmpty(transition)){
			transition.reset();
		}
		if(pane.markup==null || markup.markup != pane.markup.markup){
			if(transition){
				if(docallback){
					transition.addCallback(callback);
					docallback=false;
				} 
			}
			pane.setTransition(transition);
			pane.setMarkup(markup);
		}
		else{
			pane.processStateChange('displaying');
			pane.processStateChange('done');
		}
		if(docallback){
			callback();
		}
		if(pane.autoReload){
			var waitTime = pane.maxage - markup.getAge();
			pane.timer = window.setTimeout(makePaneReload(pane),waitTime);
		}
		pane=null;
		callback=null;
		transition=null;
	}
}

function makePaneReload(pane){
	var pane = pane;
	return function(){
		pane.reload();
	}
}

//create a sequence to cross fade from oldContent to newContent
Pane.prototype.makeCrossFadeSequence = function(){
	var seq = new Sequence();
	var pane = this;
	seq.handler = function(val){
		setOpacity(pane.content,val);
	};
	seq.init = function(){
		if(pane.content && pane.content.parentNode != pane.viewport){
			setOpacity(pane.content,seq.start);
			pane.viewport.appendChild(pane.content);
			pane.content.style.zIndex=3;
		};
		if(!isEmpty(pane.oldContent)){
			pane.oldContent.style.zIndex=2;
		}
	}
	seq.callback = function(){
		if(pane.content!=pane.oldContent && pane.oldContent && pane.oldContent.parentNode==pane.viewport){
			setOpacity(pane.oldContent,1-seq.end);
			pane.oldContent.style.zIndex="";
			pane.viewport.removeChild(pane.oldContent);
		}
		if(!isEmpty(pane.content)){
			pane.content.style.zIndex="";
		}
	}
	return seq;
}
//create a sequence with the appropriate init function to set start and end values to animate a scroll
Pane.prototype.makeYScrollSequence = function(elementProducer){
	var seq = new Sequence();
	seq.init = makeYScrollSequenceInit(this,seq,elementProducer);
	return seq;
}

Pane.prototype.makeXScrollSequence = function(elementProducer){
	var seq = new Sequence();
	seq.init = makeXScrollSequenceInit(this,seq,elementProducer);
	return seq;
}

function makeYScrollSequenceInit(pane,seq,elementProducer){
	var scrollpane = pane;
	var seq = seq;
	var elementProducer = elementProducer;
	return function(){
		var el = elementProducer();
		if(el && scrollpane.content){
			var offset = calcPosition(el,"Top",scrollpane.content);
			var min = -scrollpane.content.offsetTop;
			var max = min+scrollpane.viewportHeight;
			if((offset + el.offsetHeight ) > max){
				offset+=el.offsetHeight;
			}
			if(offset >= min && offset <= max){
				seq.handler= null;
				return;
			}
			
			seq.start = scrollpane.content.offsetTop;
			seq.end = (-offset > seq.start) ? -offset : -offset+scrollpane.viewportHeight;
			var pane = scrollpane;
			var direction = seq.end-seq.start;
			seq.handler = function(val){
				var amount = Math.round((pane.content.offsetTop-val)/pane.scrollFactorY);
				if((amount < 0 && (direction>0)) || (amount > 0 && (direction<0))){
					pane.scrolly(amount);
				}
			}
		}
		else{
			seq.handler=null;
		}
	}
}

function makeXScrollSequenceInit(pane,seq,elementProducer){
	var scrollpane = pane;
	var seq = seq;
	var elementProducer = elementProducer;
	return function(){
		var el = elementProducer();
		if(el && scrollpane.content){
			var offset = calcPosition(el,"Left",scrollpane.content);
			var min = -scrollpane.content.offsetLeft;
			var max = min+scrollpane.viewportWidth;
			if((offset + el.offsetWidth ) > max){
				offset+=el.offsetWidth;
			}
			if(offset >= min && offset <= max){
				seq.handler= null;
				return;
			}
			
			seq.start = scrollpane.content.offsetLeft;
			seq.end = (-offset > seq.start) ? -offset : -offset+scrollpane.viewportWidth;
			var pane = scrollpane;
			var direction = seq.end-seq.start;
			seq.handler = function(val){
				var amount = Math.round((pane.content.offsetLeft-val)/pane.scrollFactorX);
				if((amount < 0 && (direction>0)) || (amount > 0 && (direction<0))){
					pane.scrollx(amount);
				}
			}
		}
		else{
			seq.handler=null;
		}
	}
}

Pane.prototype.renderScrollbars = function(){
	var x = false;
	var y = false;
	var xw = this.width-(2*this.scrollbarBorderWidth);
	var yh = this.height-(2*this.scrollbarBorderWidth);
	
	var ovw = this.viewportWidth;
	var ovh = this.viewportHeight;
	
	if(this.contentHeight > this.height && this.yscrollbar){
		y = true;
		this.setViewportWidth(this.width - this.scrollbarWidth);
	}
	else{
		this.setViewportWidth(this.width);
	}
	if(ovw != this.viewportWidth){
		this.stageContent();
	}
	if(this.contentWidth > this.width && this.xscrollbar){
		x=true;
		this.setViewportHeight(this.height - this.scrollbarWidth);
	}
	else{
		this.setViewportHeight(this.height);
	}
	if(ovh != this.viewportHeight){
		this.stageContent();
	}
	if(y && x){
		xw -= this.scrollbarWidth;
		yh -= this.scrollbarWidth;
	}
	if(y){
		this.yscrollbar.style.height = yh+"px";
		var ytroughheight = yh-(2*(this.scrollbarWidth-(2*this.scrollbarBorderWidth)));
		this.ytrough.style.height = ytroughheight+"px";
		this.ytrough.style.top = (this.scrollbarWidth-(2*this.scrollbarBorderWidth))+"px";
		var nyh = (ytroughheight*(yh/this.contentHeight));
		this.yscroller.style.height = nyh+'px';
		this.yscroller.style.top="0px";
		this.downscroller.style.top = (yh-this.scrollbarWidth+(2*this.scrollbarBorderWidth))+"px";
		this.yscrollbar.style.display = "block";
		this.yscrollerbottom.style.top  = nyh - this.yscrollerbottom.offsetHeight+"px";
	}
	else if(this.yscrollbar){
		this.yscrollbar.style.display = "none";
	}
	if(x){
		this.xscrollbar.style.width = xw+"px";
		var xtroughwidth = xw-(2*(this.scrollbarWidth-(2*this.scrollbarBorderWidth)));
		this.xtrough.style.width = xtroughwidth+"px";
		this.xtrough.style.left = this.scrollbarWidth-(2*this.scrollbarBorderWidth)+"px";
		this.xscroller.style.width = (xtroughwidth*(xw/this.contentWidth))+'px';
		this.xscroller.style.left="0px";
		this.rightscroller.style.left = (xw-this.scrollbarWidth+(2*this.scrollbarBorderWidth))+"px";
		this.xscrollbar.style.display = "block";
	}
	else if(this.xscrollbar){
		this.xscrollbar.style.display = "none";
	}
	
}


Pane.prototype.addXScrollbar = function(){
	this.getHostDimensions();
	
	this.xscrollbar = document.createElement("div");
	this.xscrollbar.className = "xScrollbar";
	this.xscrollbar.style.position="absolute";
	this.xscrollbar.style.height = (this.scrollbarWidth - 2*this.scrollbarBorderWidth) +"px";
	this.xscrollbar.style.margin="0px";
	this.xscrollbar.style.padding="0px";
	this.xscrollbar.style.display = "none";
	
	this.xscroller = document.createElement("div");
	this.xscroller.className = "xScroller";
	this.xscroller.style.position="relative";
	this.xscroller.style.fontSize = "1px"
	this.xscroller.style.height = "17px";
	this.xscroller.onmousedown = makeScrollOnMouseDownX(this);
	
	this.xscrollerleft = document.createElement("div");
	this.xscrollerleft.className = "xScrollerCapLeft";
	this.xscrollerleft.style.cssFloat="left";
	this.xscrollerleft.style.styleFloat="left";
	this.xscrollerleft.style.top = "0px";
	this.xscrollerleft.style.fontSize = "1px";
	this.xscroller.appendChild(this.xscrollerleft);
	
	this.xscrollerright = document.createElement("div");
	this.xscrollerright.className = "xScrollerCapRight";
	this.xscrollerright.style.cssFloat="right";
	this.xscrollerright.style.styleFloat="right";
	this.xscrollerright.style.fontSize = "1px";
	this.xscroller.appendChild(this.xscrollerright);
	
	this.xtrough = document.createElement("div");
	this.xtrough.className = "xTrough";
	this.xtrough.style.position="absolute";
	this.xtrough.style.height = (this.scrollbarWidth - 2*this.scrollbarBorderWidth) +"px";
	this.xtrough.appendChild(this.xscroller);
	this.xtrough.onmousedown = makeScrollPagerX(this);
	this.xscrollbar.appendChild(this.xtrough);
	
	var pane = this;
	var scrollstopper = makeScrollStopper(pane);
	
	this.leftscroller = document.createElement("div");
	this.leftscroller.className = "leftScroller";
	this.leftscroller.style.height = (this.scrollbarWidth - 2*this.scrollbarBorderWidth)+"px";
	this.leftscroller.style.width = (this.scrollbarWidth - 2*this.scrollbarBorderWidth)+"px";
	this.leftscroller.style.position="absolute";
	this.leftscroller.style.fontSize = "1px";
	this.leftscroller.style.left = "0px";
	this.leftscroller.onmousedown = function(){
		var func = makeLeftScroll(pane,pane.scrollFirstInterval);
		func();
		document.onmouseup = scrollstopper;
	};
	this.xscrollbar.appendChild(this.leftscroller);
	
	this.rightscroller = document.createElement("div");
	this.rightscroller.className = "rightScroller";
	this.rightscroller.style.height = (this.scrollbarWidth - 2*this.scrollbarBorderWidth)+"px";
	this.rightscroller.style.width = (this.scrollbarWidth - 2*this.scrollbarBorderWidth)+"px";
	this.rightscroller.style.position="absolute";
	this.rightscroller.style.fontSize = "1px";
	this.rightscroller.onmousedown = function(){
		var func = makeRightScroll(pane,pane.scrollFirstInterval);
		func();
		document.onmouseup = scrollstopper;
	};
	this.xscrollbar.appendChild(this.rightscroller);
	
	this.shell.appendChild(this.xscrollbar);
	this.refreshScrollbars();
}

function makeScrollStopper(pane){
	var pane = pane;
	return function(){
		window.clearTimeout(pane.scrollTimer);
		document.onmouseup=null;
	}
}

function makeLeftScroll(pane,time){
	this.pane=pane;
	this.time = time;
	return function(){
		pane.scrollLeft();
		var func = makeLeftScroll(pane,pane.scrollInterval);
		pane.scrollTimer =window.setTimeout(func,time);
	}
}

function makeRightScroll(pane,time){
	this.pane=pane;
	this.time = time;
	return function(){
		pane.scrollRight();
		var func = makeRightScroll(pane,pane.scrollInterval);
		pane.scrollTimer =window.setTimeout(func,time);
	}
}

function makeUpScroll(pane,time){
	this.pane=pane;
	this.time = time;
	return function(){
		pane.scrollUp();
		var func = makeUpScroll(pane,pane.scrollInterval);
		pane.scrollTimer =window.setTimeout(func,time);
	}
}

function makeDownScroll(pane,time){
	this.pane=pane;
	this.time = time;
	return function(){
		pane.scrollDown();
		var func = makeDownScroll(pane,pane.scrollInterval);
		pane.scrollTimer =window.setTimeout(func,time);
	}
}

Pane.prototype.addYScrollbar = function(){
	this.getHostDimensions();
	this.yscrollbar = document.createElement("div");
	this.yscrollbar.className = "yScrollbar";
	this.yscrollbar.style.position="absolute";
	this.yscrollbar.style.top="0px";
	this.yscrollbar.style.display = "none";
	
	this.yscroller = document.createElement("div");
	this.yscroller.className = "yScroller";
	this.yscroller.style.position="absolute";
	this.yscroller.style.width = "17px";
	this.yscroller.onmousedown = makeScrollOnMouseDownY(this);
	this.yscrollbar.appendChild(this.yscroller);
	
	this.yscrollertop = document.createElement("div");
	this.yscrollertop.className = "yScrollerCapTop";
	this.yscrollertop.style.position="absolute";
	this.yscrollertop.style.top = "0px";
	this.yscrollertop.style.fontSize = "1px";
	this.yscroller.appendChild(this.yscrollertop);
	
	this.yscrollerbottom = document.createElement("div");
	this.yscrollerbottom.className = "yScrollerCapBottom";
	this.yscrollerbottom.style.position="absolute";
	this.yscrollerbottom.style.fontSize = "1px";
	this.yscroller.appendChild(this.yscrollerbottom);
	
	this.ytrough = document.createElement("div");
	this.ytrough.className = "yTrough";
	this.ytrough.style.position="absolute";
	this.ytrough.style.width = (this.scrollbarWidth - 2*this.scrollbarBorderWidth) +"px";
	this.ytrough.appendChild(this.yscroller);
	this.ytrough.onmousedown = makeScrollPagerY(this);
	this.yscrollbar.appendChild(this.ytrough);
	
	var pane = this;
	var scrollstopper = makeScrollStopper(pane);
	
	this.upscroller = document.createElement("div");
	this.upscroller.className = "upScroller";
	this.upscroller.style.height = (this.scrollbarWidth - 2*this.scrollbarBorderWidth)+"px";
	this.upscroller.style.width = (this.scrollbarWidth - 2*this.scrollbarBorderWidth)+"px";
	this.upscroller.style.position="absolute";
	this.upscroller.style.fontSize = "1px";
	this.upscroller.style.top = "0px";
	this.upscroller.onmousedown = function(){
		var func = makeUpScroll(pane,pane.scrollFirstInterval);
		func();
		document.onmouseup = scrollstopper;
	};
	this.yscrollbar.appendChild(this.upscroller);
	
	this.downscroller = document.createElement("div");
	this.downscroller.className = "downScroller";
	this.downscroller.style.height = (this.scrollbarWidth - 2*this.scrollbarBorderWidth)+"px";
	this.downscroller.style.width = (this.scrollbarWidth - 2*this.scrollbarBorderWidth)+"px";
	this.downscroller.style.position="absolute";
	this.downscroller.style.fontSize = "1px";
	this.downscroller.onmousedown = function(){
		var func = makeDownScroll(pane,pane.scrollFirstInterval);
		func();
		document.onmouseup = scrollstopper;
	};
	this.yscrollbar.appendChild(this.downscroller);
	
	this.shell.appendChild(this.yscrollbar);
	this.addMouseWheel(makeScrollOnMouseWheel(this));
	this.refreshScrollbars();
}

Pane.prototype.scrollUp = function(){
	this.scrolly(-Math.ceil(this.minScroll/this.scrollFactorY));
}

Pane.prototype.scrollDown = function(){
	this.scrolly(Math.ceil(this.minScroll/this.scrollFactorY));
}

Pane.prototype.scrollLeft = function(){
	this.scrollx(-Math.ceil(this.minScroll/this.scrollFactorX));
}

Pane.prototype.scrollRight = function(){
	this.scrollx(Math.ceil(this.minScroll/this.scrollFactorX));
}

Pane.prototype.scrolly = function(delta){
	if(this.yscrollbar && this.yscrollbar.style.display=='block'){
		var originy = this.yscroller.offsetTop;
		var top = originy+delta;
		if(top < 0){
			top = 0;
		}
		var max = this.ytrough.clientHeight - this.yscroller.offsetHeight;
		if(top > max){
			top = max;
		}
		if(top!=originy){
			this.yscroller.style.top = top+"px";
			
			this.content.style.top = (-(this.contentHeight-this.viewportHeight)*(top/max))+"px";
			return false;
		}
		return false;
	}
	return true;
}

Pane.prototype.scrollx = function(delta){
	if(this.xscrollbar && this.xscrollbar.style.display=='block'){
		var originx = this.xscroller.offsetLeft;
		var left = originx+delta;
		if(left < 0){
			left = 0;
		}
		var max = this.xtrough.clientWidth - this.xscroller.offsetWidth;
		if(left > max){
			left = max;
		}
		if(left!=originx){
			this.xscroller.style.left = left+"px";
			
			this.content.style.left = (-(this.contentWidth-this.viewportWidth)*(left/max))+"px";
			return false;
		}
		return false;
	}
	return true;
}

Pane.prototype.pageUp = function(){
	return this.scrolly(-((this.yscroller.offsetHeight) - 2*(this.minScroll/this.scrollFactorY)));
}

Pane.prototype.pageDown = function(){
	return this.scrolly(((this.yscroller.offsetHeight) - 2*(this.minScroll/this.scrollFactorY)));
}

Pane.prototype.pageLeft = function(){
	return this.scrollx(-((this.xscroller.offsetWidth) - 2*(this.minScroll/this.scrollFactorX)));
}

Pane.prototype.pageRight = function(){
	return this.scrollx(((this.xscroller.offsetWidth) - 2*(this.minScroll/this.scrollFactorX)));
}


function makeScrollPagerY(scrollpane){
	var scrollpane = scrollpane;
	
	return function(e){
		if (!e) var e = window.event;
		if(absTop(e) < calcPosition(scrollpane.yscroller,"Top")){
			scrollpane.pageUp();
		}
		else if(absTop(e) > (calcPosition(scrollpane.yscroller,"Top") + scrollpane.yscroller.offsetHeight)){
			scrollpane.pageDown();
		}
	};
}

function makeScrollPagerX(scrollpane){
	var scrollpane = scrollpane;
	
	return function(e){
		if (!e) var e = window.event;
		if(absLeft(e) < calcPosition(scrollpane.xscroller,"Left")){
			scrollpane.pageLeft();
		}
		else if (absLeft(e) > (calcPosition(scrollpane.xscroller,"Left") + scrollpane.xscroller.offsetWidth)){
			scrollpane.pageRight();
		}
	};
}

function makeScrollOnMouseWheel(scrollpane){
	var scrollpane = scrollpane;
	
	return function(e){
		var wheelDelta;
		if (e.wheelDelta) wheelDelta = e.wheelDelta / 120 * ((window.opera) ? 1 : -1);
		if (e.detail) wheelDelta = e.detail;
		if(wheelDelta < 0){
			wheelDelta = -2;
		}
		else{
			wheelDelta = 2;
		}	
		var rval = scrollpane.scrolly((wheelDelta*scrollpane.minScroll)/scrollpane.scrollFactorY);
		if(!rval){
			stopEvent(e);
		}
		return rval;
	}
}	

xScrollMouseDownTimer = null;
var clearx = function(){xScrollMouseDownTimer = null;};

function makeScrollOnMouseDownX(scrollpane){
	var scrollpane = scrollpane;
	
	return function(e){
		//prevent text selection on ie
		document.body.ondrag = function () { return false; };
    	document.body.onselectstart = function () { return false; };
		var originx = scrollpane.xscroller.offsetLeft;
	
		document.onmouseup = makeScrollOnMouseUp(scrollpane);
		var startx = absLeft(e);
		var minx = 1/scrollpane.minScroll;
		document.onmousemove = function(e){
			if(xScrollMouseDownTimer == null){
				var nx = absLeft(e);
				var delta = (nx-startx);
				if(Math.abs(delta) >= minx){
					xScrollMouseDownTimer = window.setTimeout(clearx,8);
					scrollpane.scrollx(delta);
					startx = nx;
				}
				
			}
			stopEvent(e);
		}
		return false;
	}
}

var yScrollMouseDownTimer = null;

function makeScrollOnMouseDownY(scrollpane){
	var scrollpane = scrollpane;
	
	return function(e){
		//prevent text selection on ie
		document.body.ondrag = function () { return false; };
    	document.body.onselectstart = function () { return false; };
		var originy = scrollpane.yscroller.offsetTop;
	
		document.onmouseup = makeScrollOnMouseUp(scrollpane);
		var starty = absTop(e);
		var cleary = function(){yScrollMouseDownTimer = null;};
		var miny = 1/scrollpane.minScroll;
		document.onmousemove = function(e){
			if(yScrollMouseDownTimer == null){
				var ny = absTop(e);
				var delta = (ny-starty);
				if(Math.abs(delta) >= miny){
					yScrollMouseDownTimer = window.setTimeout(cleary,8);
					scrollpane.scrolly(delta);
					starty = ny;			
				}
			}
			stopEvent(e);
		}
		return false;
	}
}

function makeScrollOnMouseUp(scrollpane){
	var scrollpane=scrollpane;
	return function(e){
		var starty = absTop(e);
		document.onmousemove = null;
		document.body.ondrag = null;
    	document.body.onselectstart = null;
		stopEvent(e);
	}
}
}

