/*
   DynAPI Distribution
   Thread Class

   The DynAPI Distribution is distributed under the terms of the GNU LGPL license.

   Requirements:
	dynapi.api [dynlayer, dyndocument, browser, events]
*/
function Thread(dlyr) {
	// Inherit from object. Provides unique ID
	this.DynObject = DynObject
	this.DynObject()
	
	this.setDynLayer(dlyr);
}
Thread.prototype = new DynObject
Thread.prototype.active = false;
Thread.prototype.interval = 50;
Thread.prototype.cancelThread = false;
Thread.prototype.sleep = function (ms) {
	this.interval = Math.abs(parseInt(ms));
	if (this.active) {
		this.stop();
		setTimeout(this+'.start()',this.interval+1);
	}
};
Thread.prototype.setFPS = function (fps) {
	this.sleep(Math.floor(1000/fps));
};
Thread.prototype.cancel = function () {
	this.cancelThread = true;
	this.stop();
};
Thread.prototype.start = function () {
	if (!this.active) {
		this.active = true;
		if (!this.cancelThread) this.timer = setInterval(this+'.run()',this.interval);
	}
};
Thread.prototype.run = function () {}; // overwrite run
Thread.prototype.stop = function () {
	this.active = false;
	if (!this.cancelThread && this.timer) {
		clearInterval(this.timer);
		delete this.timer;
	}
};
Thread.prototype.setDynLayer = function (dlyr) {
	this.dlyr = dlyr;
};
Thread.prototype.getDynLayer = function () {
	return this.dlyr;
};

/*
   DynAPI Distribution
   PathAnimation Class

   The DynAPI Distribution is distributed under the terms of the GNU LGPL license.

   Requirements:
	dynapi.api [dynlayer, dyndocument, browser, events]
	dynapi.util [thread]
*/

function PathAnimation(dlyr) {
	this.Thread = Thread;
	this.Thread(dlyr);
	this.paths = new Array();
	this.pathPlaying = null;
}
PathAnimation.prototype = new Thread;

PathAnimation.prototype.addAnimation = function (path, loops, resets) {
	var n = this.paths.length;
	this.paths[n] = path;
	this.setLoops(n,loops);
	this.setResets(n,resets);
	this.setFrame(n,0);
	return n;
};
PathAnimation.prototype.setLoops = function (n, loops) {
	this.paths[n].loops = (loops);
};
PathAnimation.prototype.setResets = function (n, resets) {
	this.paths[n].resets = (resets);
};
PathAnimation.prototype.setFrame = function (n, frame) {
	this.paths[n].frame = frame;
};
PathAnimation.prototype.playAnimation = function (noevt) {
	if (this.playing) return;
	this.pathPlaying = null;
	if (typeof(arguments[0]) == "number") {
		this.pathPlaying = this.paths[arguments[0]];
	}
	else if (typeof(arguments[0]) == "object") {  
		this.pathPlaying = arguments[0];
		this.pathPlaying.loops = arguments[1]||false;
		this.pathPlaying.resets = arguments[2]||false;
		this.pathPlaying.frame = 0;
	}
	this.playing = true;
	if (this.dlyr!=null && noevt!=false) this.dlyr.invokeEvent("pathstart");
	this.start();
};
PathAnimation.prototype.stopAnimation = function (noevt) {
	if (this.pathPlaying && this.pathPlaying.resets && !this.cancelThread && this.dlyr!=null) this.dlyr.moveTo(this.pathPlaying[0],this.pathPlaying[1]);
	this.stop();
	delete this.pathPlaying; 
	this.playing = false;
	if (this.dlyr!=null && noevt!=false) this.dlyr.invokeEvent("pathstop");
};
PathAnimation.prototype.run = function () {
	if (!this.playing || this.pathPlaying==null) return;
	var anim = this.pathPlaying;
	if (anim.frame==0 && (this.dlyr!=null && this.dlyr.x==anim[0] && this.dlyr.y==anim[1])) {
		anim.frame += 1;
	}
	if (anim.frame>anim.length/2) {
		if (anim.loops) {
			anim.frame = 0;
		}
		else if (anim.resets) {
			anim.frame = 0;
			if (this.dlyr!=null) this.dlyr.moveTo(anim[0],anim[1]);
			this.stopAnimation();
			return;
		}
		else {
			anim.frame = 0;
			this.stopAnimation();
			return;
		}
	}
	this.newX = anim[anim.frame*2];
	this.newY = anim[anim.frame*2+1];
	
	if (this.dlyr!=null) {
		this.dlyr.invokeEvent("pathrun");
		this.dlyr.moveTo(this.newX,this.newY);	
	}
	anim.frame++;
};

// DynLayer Extensions
// These will invoke "pathstart", "pathrun", and "pathstop" events on the dynlayer when used
DynLayer.prototype.slideTo = function(x,y,inc,ms) {
	if (!this.pathanim) this.pathanim = new PathAnimation(this);
	if (!ms) ms = 20;
	if (!inc) inc = 10;
	if (x==null) x = this.x;
	if (y==null) y = this.y;
	this.pathanim.sleep(ms);
	this.pathanim.playAnimation(PathAnimation.line(this.x,this.y, x,y, inc));
};
DynLayer.prototype.stopSlide = function () {
	if (this.pathanim) this.pathanim.stopAnimation();
};

// Path Functions
// Generates a path between 2 points, stepping inc pixels at a time
PathAnimation.line = function(x1,y1,x2,y2,inc) {
	var distx = x2 - x1;
	var disty = y2 - y1;
	var N = Math.floor(Math.sqrt(distx*distx + disty*disty)/inc);
	var a = PathAnimation.getNormalizedAngle(x1,y1,x2,y2);
	var dx = inc * Math.cos(a);
	var dy = -inc * Math.sin(a);
	var path = [];
	for (var i=0;i<=N;i++) {
		path[i*2] = Math.round(x1 +  i*dx);
		path[i*2+1] = Math.round(y1 + i*dy);
	}
	if (path[i*2-2] != x2 || path[i*2-1] != y2) {
		path[i*2] = x2;
		path[i*2+1] = y2;
	}
	return path;
};
// Generates a path between 2 points in N steps
PathAnimation.lineN = function(x1,y1,x2,y2,N) {
	if (N==0) return [];
	var dx = (x2 == x1)? 0 : (x2 - x1)/N;
	var dy = (y2 == y1)? 0 : (y2 - y1)/N;
	var path = new Array();
	for (var i=0;i<=N;i++) {
		path[i*2] = Math.round(x1 + i*dx);
		path[i*2+1] = Math.round(y1 + i*dy);
	}
	return path;
};
// Combines separate [x1,x2],[y1,y2] arrays into a path array [x1,y1,x2,y2]
PathAnimation.interlace = function(x,y) {
	var l = Math.max(x.length,y.length);
	var a = new Array(l*2);
	for (var i=0; i<l; i++) {
		a[i*2] = x[i];
		a[i*2+1] = y[i];
	}
	return a;
};
// Returns correct angle in radians between 2 points
PathAnimation.getNormalizedAngle = function(x1,y1,x2,y2) {
	var distx = Math.abs(x1-x2);
	var disty = Math.abs(y1-y2);
	if (distx==0 && disty==0) angle = 0;
	else if (distx==0) angle = Math.PI/2;
	else angle = Math.atan(disty/distx);
	if (x1<x2) {
		if (y1<y2) angle = Math.PI*2-angle;
	}
	else {
		if (y1<y2) angle = Math.PI+angle;
		else angle = Math.PI-angle;
	}
	return angle;
};
// Radian conversion
PathAnimation.radianToDegree = function(radian) {
	return radian*180/Math.PI
};
PathAnimation.degreeToRadian = function(degree) {
	return degree*Math.PI/180
};
/*
   DynAPI Distribution
   ButtonImage Class

   The DynAPI Distribution is distributed under the terms of the GNU LGPL license.

   Requirements:
	dynapi.api [dynlayer, dyndocument, browser, events]
	dynapi.gui [dynimage]
*/

function ButtonImage() {
	this.DynImage = DynImage;
	this.DynImage();
	if (arguments.length>0) this.setImages(arguments);
	this.addEventListener(ButtonImage.events);
};

ButtonImage.events = new EventListener();
ButtonImage.events.onmousedown = function (e) {
	var o = e.getSource();
	if (o.checkbox) o.setSelected(!o.selected);
	else o.setImage(o.selectedImage);
	e.setBubble(false);
};
ButtonImage.events.onmouseup = function (e) {
	var o = e.getSource();
	if (!o.checkbox) o.setImage(o.defaultImage);
	e.setBubble(false);
};
ButtonImage.events.onmouseover = function (e) {
	var o = e.getSource();
	if (o.selected) o.setImage(o.selectedRoll);
	else o.setImage(o.defaultRoll);
	e.setBubble(false);
};
ButtonImage.events.onmouseout = function (e) {
	var o = e.getSource();
	if (o.selected) o.setImage(o.selectedImage);
	else o.setImage(o.defaultImage);
	e.setBubble(false);
}

ButtonImage.prototype = new DynImage;
ButtonImage.prototype.checkbox = false;
ButtonImage.prototype.setImages = function(defaultImage,defaultRoll,selectedImage,selectedRoll) {
	if (arguments.length==4) this.checkbox = true;
	this.defaultImage = defaultImage;
	this.defaultRoll = defaultRoll;
	this.selectedImage = selectedImage;
	this.selectedRoll = selectedRoll;
	this.setImage(this.defaultImage);
	//Hack so NS6/Mozilla show button on first load
	//setTimeout(this+".setSize("+this+".defaultImage.width,"+this+".defaultImage.height);",0);
};
ButtonImage.prototype.setSelected = function(b) {
	this.selected=b
	if (b) {
		this.setImage(this.selectedImage);
		this.invokeEvent("select");
	}
	else {
		this.setImage(this.defaultImage);
		this.invokeEvent("deselect");
	}
};

/*
   DynAPI Distribution
   Button Class

   The DynAPI Distribution is distributed under the terms of the GNU LGPL license.

   Requirements:
	dynapi.api [dynlayer, dyndocument, browser, events]
	dynapi.gui [label, dynimage]
*/

// to do:
// do support for new Button(new DynImage()) and new Button(new Label())

function Button() {   // Image() or "string"
	
	this.DynLayer = DynLayer;
	this.DynLayer();
	
	this.setBgColor("#CECECE");
	
	if (typeof(arguments[0])=="string") {
		this.setText(arguments[0]);
	}
	else if (typeof(arguments[0])=="object") {
		this.setImage(arguments[0]);
	}
	
	this.t = this.addChild(new DynLayer(null,0,0,0,1,"#636363"));
	this.r = this.addChild(new DynLayer(null,0,0,1,0,"#636363"));
	this.b = this.addChild(new DynLayer(null,0,0,0,1,"#636363"));
	this.l = this.addChild(new DynLayer(null,0,0,1,0,"#636363"));
	
	this.bvt = this.addChild(new DynLayer(null,1,1,0,1,"#FFFFFF"));
	this.bvl = this.addChild(new DynLayer(null,1,1,1,0,"#FFFFFF"));

	var listener = new EventListener(this);
	listener.oncreate = function(e) {
		var o = e.getTarget();
		if (o.label) o.setText(o.text)
		if (o.w==null && o.h==null) {
			if (o.label) {
				o.setSize(o.label.w+4,o.label.h+4);
			}
			else if (o.imglyr) o.setSize(o.imglyr.w+4,o.imglyr.h+4);
		}
		else o.recenter();
		if (o.label) o.label.setVisible(true);
		else if (o.imglyr) o.imglyr.setVisible(true);
	};
	listener.onresize = function(e) {
		var o = e.getTarget();
		
		o.t.setWidth(o.w);
		o.b.setWidth(o.w);
		o.r.setHeight(o.h);
		o.l.setHeight(o.h);
		
		o.r.setX(o.w-1);
		o.b.setY(o.h-1);
		
		o.bvt.setWidth(o.w-2);
		o.bvl.setHeight(o.h-2);
		
		o.recenter();
	};
	listener.onmousedown = function(e) {
		var o = e.getTarget();
		if (o.enabled!=false) o.setDown();
	};
	listener.onmouseup = function(e) {
		var o = e.getTarget();
		if (o.enabled!=false) o.setUp();
	};
	this.addEventListener(listener);
};
Button.prototype = new DynLayer;
Button.prototype.setImage = function(imgObj) {
	this.img = imgObj;
	if (!this.imglyr) {
		this.imglyr = this.addChild(new DynImage(imgObj));
		this.imglyr.setVisible(false);
	}
	else this.imglyr.setImage(imgObj);
  	this.imglyr.addEventListener(Button.imglyrlistener); //added
};
Button.prototype.setText = function(text) {
	this.text = text
	if (!this.label) {
		this.label = this.addChild(new Label(arguments[0]));
		this.label.setVisible(false);
		this.label.setSelectable(false);
		if (this.padding) this.label.setPadding(this.padding);
		this.label.pack();
	}
	else {
		this.label.setText(text);
		this.label.pack();
	}
	this.recenter();
};
Button.prototype.setPadding = function(p) { // only for label for right now
	this.padding = p;
	if (this.label) this.label.setPadding(p);
};
Button.prototype.setEnabled = function(b) { // only for label for right now
	this.enabled = b;
};
Button.prototype.recenter = function() {
	if (!this.created) return;
	if (this.label) {
		var x = Math.round((this.w-this.label.w)/2);
		var y = Math.round((this.h-this.label.h)/2);
		this.label.moveTo(x,y);
	}
	else if (this.imglyr) {
		var x = Math.round((this.w-this.imglyr.w)/2);
		var y = Math.round((this.h-this.imglyr.h)/2);
		this.imglyr.moveTo(x,y);
	}
};
Button.prototype.setDown = function() {
	this.bvt.setVisible(false);
	this.bvl.setVisible(false);
	this.setBgColor("#9C9C9C");
};
Button.prototype.setUp = function() {
	this.bvt.setVisible(true);
	this.bvl.setVisible(true);
	this.setBgColor("#CECECE");
};
Button.imglyrlistener=new EventListener();
Button.imglyrlistener.onresize=function(e){
	var o = e.getSource();
	o.w=o.img.width; o.h=o.img.height; o.parent.recenter();
}
/*
   DynAPI Distribution
   ViewPort Class

   The DynAPI Distribution is distributed under the terms of the GNU LGPL license.

   Requirements:
	dynapi.api.*
	dynapi.util [thread, pathanim]
	dynapi.gui [label]
*/

// note: we need an onremove event

function ViewPort(content) {
	this.DynLayer = DynLayer;
	this.DynLayer();

	this.contentPane = new DynLayer();
	this.addChild(this.contentPane);
	
	this.bufferW = 0;
	this.bufferH = 0;

	var scrollEvent = new EventListener(this);
	scrollEvent.onpathrun = function(e) {
		e.getTarget().invokeEvent('scroll');
	};
	this.contentPane.addEventListener(scrollEvent);

	var viewportListener = new EventListener(this);
	viewportListener.onresize = function(e) {
		var o = e.getTarget();
		if (!o.created || !o.content) return;

		o.findDimensions();
		
		if (!o.enableHScroll) o.contentPane.setX(0);
		else if (o.contentPane.x<-o.availableScrollX) {o.contentPane.setX(-o.availableScrollX);}
		
		if (!o.enableVScroll) o.contentPane.setY(0);
		else if (o.contentPane.y<-o.availableScrollY) {o.contentPane.setY(-o.availableScrollY);}
		
		o.invokeEvent("scroll");
	};
	viewportListener.oncreate = function(e) {
		var o = e.getTarget();
		if(is.def && o.css) o.css.overflow='hidden'
		o.reset(false);
	};
	this.addEventListener(viewportListener);

	this.contentResizeListener = new EventListener(this);
	this.contentResizeListener.onresize = function(e) {
		var o = e.getTarget();
		o.findDimensions();
		o.invokeEvent("contentchange");
	};
	this.contentResizeListener.onload = function(e) {  // for loadpanel
		var o = e.getTarget();
		if (o.created && o.content) {
			o.reset();
		}
	};
	
	this.setContent(content);
}

ViewPort.prototype = new DynLayer;

ViewPort.prototype.reset = function(b) {
	this.contentPane.moveTo(0,0);
	this.findDimensions();
	if (b!=false) this.invokeEvent("contentchange");
};
ViewPort.prototype.setContent = function(content) {
	if (this.content && this.contentPane.children.length>0) {
		if (this.content==content) return;
		this.content.removeFromParent();
		this.content.removeEventListener(this.contentResizeListener);
	}
	if (!content) this.content = new DynLayer();
	else this.content = content;
	
	this.content.moveTo(0,0);
	this.contentPane.moveTo(0,0);

	this.contentPane.addChild(this.content);
	this.content.addEventListener(this.contentResizeListener);
	
	this.findDimensions();

	this.invokeEvent("contentchange");
};
ViewPort.prototype.findDimensions = function() {
	if (!this.content) return;
	this.contentPane.setSize(this.content.getWidth(),this.content.getHeight());
	this.availableScrollX = this.content.getWidth()-this.getWidth()+this.bufferW;
	this.availableScrollY = this.content.getHeight()-this.getHeight()+this.bufferH;
	this.enableHScroll = this.availableScrollX>0;
	this.enableVScroll = this.availableScrollY>0;
//alert('vHRZ: ' + this.enableHScroll+'\n'+this.availableScrollX+'\n'+this.content.getWidth()+'\n'+this.getWidth()+'\n'+this.bufferW)	
//alert('vVRT: ' + this.enableVScroll+'\n'+this.availableScrollY+'\n'+this.content.getHeight()+'\n'+this.getHeight()+'\n'+this.bufferH)	
};
ViewPort.prototype.jumpTo = function(x,y) {
	this.content.moveTo(x,y);
	this.invokeEvent("scroll");
};
ViewPort.prototype.setRatio = function(rx,ry) {
	this.setRatioX(rx);
	this.setRatioY(ry);
};
ViewPort.prototype.setRatioX = function(rx) {
	if (rx!=null) this.contentPane.setX(-this.availableScrollX*rx);
};
ViewPort.prototype.setRatioY = function(ry) {
	if (ry!=null) this.contentPane.setY(-this.availableScrollY*ry);
};
ViewPort.prototype.getRatioX = function() {
	if (!this.content || !this.enableHScroll) return 0;
	else if (this.contentPane.x==0) return 0;
	else if (this.contentPane.x==-this.availableScrollX) return 1;
	else return 1-(this.availableScrollX+this.contentPane.x)/this.availableScrollX;
};
ViewPort.prototype.getRatioY = function() {
	if (!this.content || !this.enableVScroll) return 0;
	else if (this.contentPane.y==0) return 0;
	else if (this.contentPane.y==-this.availableScrollY) return 1;
	else return 1-(this.availableScrollY+this.contentPane.y)/this.availableScrollY;
};
	
ViewPort.prototype.scrollUp = function() {this.scrollSlide(null,0);};
ViewPort.prototype.scrollDown = function() {this.scrollSlide(null,-this.availableScrollY);};
ViewPort.prototype.scrollLeft = function() {this.scrollSlide(0,null);};
ViewPort.prototype.scrollRight = function() {this.scrollSlide(-this.availableScrollX,null);};
ViewPort.prototype.scrollSlide = function(x,y) {
	if (x!=null && this.enableHScroll) {
		this.invokeEvent("scrollstart");
		this.contentPane.slideTo(x,this.contentPane.y);
	}
	else if (y!=null && this.enableVScroll) {
		this.invokeEvent("scrollstart");
		this.contentPane.slideTo(this.contentPane.x,y);
	}
};
ViewPort.prototype.cancelScroll = function() {
	this.contentPane.stopSlide();
	this.invokeEvent("scrollend");
};

/*
   DynAPI Distribution
   PushPanel Class

   The DynAPI Distribution is distributed under the terms of the GNU LGPL license.

   Requirements:
	dynapi.api.*
	dynapi.util [thread, pathanim]
	dynapi.gui [button, label, viewport, dynimage]
*/

function PushPanel(content) {
	this.ViewPort = ViewPort;
	this.ViewPort(content);
	
	this.inc = 7;
	
	this.button0 = this.addChild(new Button());
	this.button1 = this.addChild(new Button());
	this.button0.setVisible(false);
	this.button1.setVisible(false);

	var button0Listener = new EventListener(this);
	button0Listener.onmousedown = function(e) {
		var o = e.getTarget();
		if (o.vertical) o.scrollUp();
		else o.scrollLeft();
	};
	var button1Listener = new EventListener(this);
	button1Listener.onmousedown = function(e) {
		var o = e.getTarget();
		if (o.vertical) o.scrollDown();
		else o.scrollRight();
	};
	var cancelListener = new EventListener(this);
	cancelListener.onmouseup = function(e) {
		var o = e.getTarget();
		o.cancelScroll();
		o.checkButtons();
	};
	
	this.button0.addEventListener(button0Listener);
	this.button0.addEventListener(cancelListener);
	this.button1.addEventListener(button1Listener);
	this.button1.addEventListener(cancelListener);
	
	this.setVertical();

	this.addEventListener(PushPanel.viewportListener);
	
	this.setTheme(MetalPushPanel());
}
PushPanel.prototype = new ViewPort;

PushPanel.viewportListener = new EventListener();
PushPanel.viewportListener.onprecreate = function(e) {
	var o = e.getSource();
	
	if (o.vertical) {
		o.button0.setImage(o.theme.up);
		o.button1.setImage(o.theme.dn);
	}
	else if (o.horizontal) {
		o.button0.setImage(o.theme.lt);
		o.button1.setImage(o.theme.rt);
	}
	o.resizeButtons();
}
PushPanel.viewportListener.oncreate = function(e) {
	var o = e.getSource();
	o.checkButtons();
};
PushPanel.viewportListener.onresize = function(e) {
	var o = e.getSource();

	if (o.created) o.resizeButtons();
	o.checkButtons();
};
PushPanel.viewportListener.oncontentchange = function(e) {
	var o = e.getSource();
	if (!o.created) return;

	o.resizeButtons();
	o.checkButtons();
};
PushPanel.viewportListener.onscroll = function(e) {
	var o = e.getSource();
	if (!o.created) return;
	
	o.checkButtons();
};

PushPanel.prototype.resizeButtons = function() {
	if (this.vertical) {
		this.button0.setSize(this.w,16);
		this.button1.setY(this.h-16);
		this.button1.setSize(this.w,16);
	}
	else {
		this.button0.setSize(16,this.h);
		this.button1.setX(this.w-16);
		this.button1.setSize(16,this.h);
	}
};

PushPanel.prototype.checkButtons = function() {
	if (!this.created) return;
	
	var b0vis = false;
	var b1vis = false;
	if (this.vertical) {
		if (this.availableScrollY>0) {
			if (this.contentPane.y==0) b1vis = true;
			else if (this.contentPane.y==-this.availableScrollY) b0vis = true;
			else b0vis = b1vis = true;
		}
	}
	else if (this.horizontal) {
		if (this.availableScrollX>0) {
			if (this.contentPane.x==0) b1vis = true;
			else if (this.contentPane.x==-this.availableScrollX) b0vis = true;
			else b0vis = b1vis = true;
		}
	}
	if (this.button0.visible!=b0vis) this.button0.setVisible(b0vis);
	if (this.button1.visible!=b1vis) this.button1.setVisible(b1vis);
	
	if (!b0vis) this.button0.setUp();
	if (!b1vis) this.button1.setUp();
	//alert('check '+b0vis+' '+b1vis+' '+this.horizontal+' '+this.button1.visible+' '+this.button1.created);
};

PushPanel.prototype.setVertical = function() {
	this.vertical = true;
	this.horizontal = false;
}
PushPanel.prototype.setHorizontal = function() {
	this.vertical = false;
	this.horizontal = true;
}

PushPanel.prototype.setTheme = function(theme) {
	this.theme = theme;
}

function MetalPushPanel(url) {		
	if (!DynAPI.librarypath) return null;
	return {
		up : DynImage.getImage(DynAPI.librarypath+'dynapi/images/common/arrowup.gif',9,5),
		dn : DynImage.getImage(DynAPI.librarypath+'dynapi/images/common/arrowdown.gif',9,5),
		lt : DynImage.getImage(DynAPI.librarypath+'dynapi/images/common/arrowleft.gif',5,9),
		rt : DynImage.getImage(DynAPI.librarypath+'dynapi/images/common/arrowright.gif',5,9)
	};
};

/*
   DynAPI Distribution
   ScrollBar Class

   The DynAPI Distribution is distributed under the terms of the GNU LGPL license.

   Requirements:
	dynapi.api.*
	dynapi.util [thread, pathanim]
	dynapi.gui [dynimage]
*/

// to do: add support for no images

function ScrollBar(orientation,bUseImages) {
	this.DynLayer = DynLayer;
	this.DynLayer();
	
	this.knob = this.addChild(new DynLayer());

	this.dragEvents = new EventListener(this);
	this.dragEvents.ondragmove = function (e) {
		var o = e.getTarget();
		o.findRatio();
		o.invokeEvent("scroll");
		e.setBubble(false);			
	};
	this.dragEvents.ondragstart = function (e) {
		var o = e.getTarget();
		e.setBubble(false);
	};
	this.dragEvents.ondragend = function (e) {
		var o = e.getTarget();
		e.setBubble(false);
	};
		
	DragEvent.setDragBoundary(this.knob);
	DragEvent.enableDragEvents(this.knob);
	
	this.knob.addEventListener(this.dragEvents);
		
	this.mouseEvents = new EventListener(this);
	this.mouseEvents.onmousemove = function(e) {
		var knob = e.getTarget().knob;
		if (knob.pathanim && knob.pathanim.playing) e.setBubble(false);
	};
	this.mouseEvents.onmousedown = function(e) {
		var lyr = e.getSource()
		var o = e.getTarget()
		if (!lyr.pathanim || !lyr.pathanim.playing) {
			var newx = e.getX()-Math.ceil(o.knob.w/2);
			var newy = e.getY()-Math.ceil(o.knob.h/2);
			var offW = o.getWidth()-o.knob.w;
			var offH = o.getHeight()-o.knob.h;
			if (newx<0) newx=0;
			if (newx>=offW) newx=offW;
			if (newy<0) newy=0;
			if (newy>=offH) newy=offH;
						
			o.knob.slideTo(newx,newy);
		}
		e.setBubble(false);
	};
	this.mouseEvents.onmouseup = function(e) {
		e.getTarget().knob.stopSlide();
	};
	this.addEventListener(this.mouseEvents);
	
	var slideEvents = new EventListener(this);
	slideEvents.onpathrun = function(e) {
		var o = e.getTarget();
		o.findRatio();
		o.invokeEvent("scroll");
	};
	slideEvents.onpathstop = function(e) {
		var lyr = e.getSource();
		var o = e.getTarget();
		var evt = new DynMouseEvent();
		evt.bubble = false;
		evt.type = "mousedown";
		evt.src = lyr;
		evt.x = o.knob.getWidth()/2;
		evt.y = o.knob.getHeight()/2;
		evt.pageX = o.knob.getPageX()+evt.x;
		evt.pageY = o.knob.getPageY()+evt.y;
		lyr.invokeEvent("mousedown",evt);
	};
	this.knob.addEventListener(slideEvents);
	
	var resizeEvents = new EventListener(this);
	resizeEvents.onresize = function(e) {
		var o = e.getTarget();
		if (o.getWidth()>0 && o.knob.x+o.knob.w>o.getWidth()) o.knob.setX(o.getWidth()-o.knob.w);
		if (o.getHeight()>0 && o.knob.y+o.knob.h>o.getHeight()) o.knob.setY(o.getHeight()-o.knob.h);
		o.findRatio();
	};
	this.addEventListener(resizeEvents);
	
	this.setOrientation(orientation);
	if (bUseImages!=false) this.setTheme(MetalScrollBar());
};

ScrollBar.prototype = new DynLayer;

ScrollBar.prototype.ratiox = 0;
ScrollBar.prototype.ratioy = 0;

ScrollBar.prototype.setOrientation = function(type) { // 0=horz,1=vert
	this.horizontal = (type==ScrollBar.HORIZONTAL);
	this.vertical = (type==ScrollBar.VERTICAL);
};
ScrollBar.prototype.setTheme = function(theme) {
	if (!theme) return;
	if (this.horizontal) this.setImages(theme.htrough, theme.hknob);
	else if (this.vertical) this.setImages(theme.vtrough, theme.vknob);
}
ScrollBar.prototype.setImages = function(troughImage,knobImage) {
	if (troughImage) {
		this.troughImage = troughImage;
		this.setBgImage(this.troughImage.src);
		if (this.vertical) this.setWidth(this.troughImage.width,false);
		if (this.horizontal) this.setHeight(this.troughImage.height,false);
	}
	if (knobImage) {
		this.knobImage = knobImage;
		this.knob.setBgImage(this.knobImage.src);
		this.knob.setSize(this.knobImage.width,this.knobImage.height);
	}
	this.hasImages = true;
};
ScrollBar.prototype.setRatio = function(rx,ry) {
	this.setRatioX(rx);
	this.setRatioY(ry);
};
ScrollBar.prototype.setRatioX = function(rx) {
	this.knob.setX(Math.floor(rx*(this.getWidth()-this.knob.getWidth())));
};
ScrollBar.prototype.setRatioY = function(ry) {
	this.knob.setY(Math.floor(ry*(this.getHeight()-this.knob.getHeight())));
};
ScrollBar.prototype.getRatioX = function() {
	return this.ratiox;
};
ScrollBar.prototype.getRatioY = function() {
	return this.ratioy;
};

ScrollBar.prototype.findRatio = function() {
	var tx = (this.getWidth()-this.knob.w);
	var ty = (this.getHeight()-this.knob.h);
	this.ratiox = tx==0 ? 0 : (this.knob.x)/tx;
	this.ratioy = ty==0 ? 0 : (this.knob.y)/ty;
};
ScrollBar.prototype.reset = function() { 
	this.knob.moveTo(0,0); 
	this.ratiox=this.ratioy=0;
};
ScrollBar.VERTICAL = 1;
ScrollBar.HORIZONTAL = 2;

function MetalScrollBar () {
	if (!DynAPI.librarypath) return null;
	return {
		vtrough : DynImage.getImage(DynAPI.librarypath+'dynapi/images/scrollpane/scrollbar-vbg.gif',16,16),
		vknob : DynImage.getImage(DynAPI.librarypath+'dynapi/images/scrollpane/scrollbar-vknob.gif',16,37),
		htrough : DynImage.getImage(DynAPI.librarypath+'dynapi/images/scrollpane/scrollbar-hbg.gif',16,16),
		hknob : DynImage.getImage(DynAPI.librarypath+'dynapi/images/scrollpane/scrollbar-hknob.gif',37,16)
	}
}
