/*StrikeDisplay 1.0
&copy; 2011, Josh Strike. 
If you like this code, please donate $1 per domain you use it on! Paypal to josh@joshstrike.com. And send me a note! 
I'd also like to see any mods you come up with for the source. Enjoy =)
*This software is provided as-is with no warranty express or implied. This software may be freely copied so long as this header is included. 
Copying of the software implies no transfer of ownership. 
This software may not be re-sold commercially, in whole or in part, without the express written permission from the author.*/

//useful for testing... create a div with the id 'tracer' to use it.

var Strike = function(){
    
    var _mainContext;
    var _lineHeightMultiplier = 1.3;
    
	function trace(str) {
		document.getElementById('tracer').innerHTML+=str+"<br />";
	}
	function traceOnce(str) {
		document.getElementById('tracer').innerHTML=str+"<br />";
	}
    
    function canvasOffset(canvas) {
        var offset = [0,0];
        while (canvas = canvas.offsetParent) {
            offset[0]+=canvas.offsetLeft;
            offset[1]+=canvas.offsetTop;
        }
        return (offset);
    }
	
	Function.prototype.bind = function(obj) {
		var method = this, temp = function() {
			return method.apply(obj, arguments);
		};
		temp._method = method; //used to remove event listeners on bound events. 
		return temp;
	}
    
    Function.prototype.inherits = function(obj) {
		this.prototype = new obj();
		this.prototype.constructor = this.bind(this);
        this._inherits = obj;
		return (this);
	}
    Function.prototype.supercall = function(instance) {
        this._inherits.prototype.constructor.bind(instance)();
    }
    
    function _arraySum(a) {
        var k=0;
        for (var c in a) { if (a[c]) k+=a[c]; }
        return (k);
    }
	
	var _MainEventList = [];
	
	function Mouse(display) {
		this.x = -1000;
		this.y = -1000;
		this.display = display;
		this.clicked = false;
		this.down = false;
		this.downDispatched = false;
		this.up = false;
		this.moved = false;
	}
	Mouse.prototype.registerPosition = function(e) { 
        var offset = canvasOffset(this.display.canvas);
		var toX = e.clientX-offset[0];//this.display.canvas.offsetLeft;
		var toY = e.clientY-offset[1];//this.display.canvas.offsetLeft;
        //var toX = e.clientX-this.display.canvas.offsetLeft;
		//var toY = e.clientY-this.display.canvas.offsetLeft;
		this.moved = (toX != this.x || toY != this.y);
        this.x = toX;
		this.y = toY;
	}
	Mouse.prototype.registerDown = function(e) {
		this.down = true;
	}
	Mouse.prototype.registerUp = function(e) {
		this.up = true;
	}	
	Mouse.prototype.registerClick = function(e) {
		this.clicked = true;
	}
	
	function StrikeDisplay(canvasID,backgroundColor,framerate,debug) {
		this.canvas = document.getElementById(canvasID);
		this.context = this.canvas.getContext('2d');
        _mainContext = this.context;
		
		this.hiddenCanvas = document.createElement('CANVAS');
		this.hiddenCanvas.setAttribute('width',this.canvas.width);
		this.hiddenCanvas.setAttribute('height',this.canvas.height);
		this.hiddenContext = this.hiddenCanvas.getContext('2d');
		
		this._mouse = new Mouse(this);
		this.canvas.onmousemove = this._mouse.registerPosition.bind(this._mouse);
		this.canvas.onclick = this._mouse.registerClick.bind(this._mouse);
		this.canvas.onmousedown = this._mouse.registerDown.bind(this._mouse);
		this.canvas.onmouseup = this._mouse.registerUp.bind(this._mouse);
		
		this.stage = new Sprite(this);
		this.stage.name = "stage";
		this.stage.display = this;
		this.framerate = framerate;
		this.backgroundColor = backgroundColor;
		this.showRedrawBounds = debug;
		this._uniqueChildBoundaries = true;
		this._redrawWithTraverser = false;
		this._boundFlicker = false;
		this.context.fillStyle = this.backgroundColor;
		this.context.fillRect(0,0,this.canvas.width,this.canvas.height);
		this._lastwr = new Array();
		setInterval("root.render()",1000/this.framerate);
	}
	StrikeDisplay.prototype.translateToSprite = function(s,cont) {

		if (!cont) {
			cont = this.context;
		} else if (cont=="hidden") {
			cont = this.hiddenContext;
		} else {
            //don't translate the local graphics cache context to the sprite's position when drawing. 
            return (false);
        }

		var q = s;	
		var c = s.concatMatrix();
		try {
			cont.setTransform(c.a,c.b,c.c,c.d,c.tx,c.ty);
		} catch (err) {
			//alert (c.a+','+c.b+','+c.c+','+c.d+','+c.tx+',',c.ty);
		}
	}
	StrikeDisplay.prototype.translateWithinSprite = function(x,y,r,sx,sy,fullContext) {
		var cont = fullContext;
		cont.translate(Number(x),Number(y));
		cont.rotate(r*(Math.PI/180));
		cont.scale(sx,sy);
	}
	StrikeDisplay.prototype.unTranslate = function(cont) {
		if (!cont) {
			cont = this.context;
		} else if (cont=="hidden") {
			cont = this.hiddenContext;
		} else {
            return (false);
        }
		cont.setTransform(1,0,0,1,0,0);
	}
	StrikeDisplay.prototype.render = function(s,wr,redraw,childrenBounded) {
		//proceed through the entire display chain and create a _bb based on the invalid sprites.
		if (s==null) {
			var origin = true;
			var s = this.stage;
			var redraw = new Array();
			var wr = new Array();
			var childrenBounded = false;
		}
		s.dispatchEvent(new Event("enterFrame",cr,cr));
		var holdpush = new Array();
		for (var i=0;i<s.children.length;i++) {
			var cr = s.children[i];
			if (cr.blockRedraw) {
				childrenBounded = true;
				continue;
			}
			if (cr.invalid) {
				if (!childrenBounded) {
					//only drill down once for any invalid sprite... 
					wr.push(deriveBounds(cr,false,0,0,!this._uniqueChildBoundaries));
					childrenBounded = !this._uniqueChildBoundaries;
				}
				
				redraw.push(cr);
				
				//By default, this code block (and the associated _redrawWithTraverser flag) is off. There are problems with it. 
				//In essence, it tries to merge all overlapping bounding boxes so that only moving sprites or those they overlap need to be redrawn. 
				//Traversing the display chain for bounding box collisions, not surprisingly, turns out to be slower than just redrawing each sprite on 
				//each frame; this was basically abandoned early-on and there are still probably issues with miscalculation in some circumstances that could 
				//lead to graphics errors. Not recommended for use. 
				if (this._redrawWithTraverser && this.stage && this._lastwr && this._lastwr.length>0) {
					cr.invalid = false;
					var addl = this.BoundCollisionTraverse(cr);
					for (var j=0;j<addl.length;j++) {
						var lastwrbox = this._lastwr[this._lastwr.length-1];
						lastwrbox = deriveBounds(addl[j],lastwrbox,0,0);
						addl[j].invalid = false;
						if (redraw.indexOf(addl[j])==-1 && holdpush.indexOf(addl[j])==-1) {
							this.zCompare(cr,addl[j])>0?redraw.unshift(addl[j]):holdpush.push(addl[j]);
						}
					}
				}
				
			}		
		
			this.render(cr,wr,redraw,childrenBounded);	
			if (holdpush.length>0) {
				redraw.push(holdpush[0]);
			}
			childrenBounded = false;
		}
		if (origin) {
			this.context.fillStyle = this.backgroundColor;
			
			var usewr = this._lastwr;
			for (var ik = 0;ik<usewr.length;ik++) {
				this.context.fillRect(usewr[ik].left,usewr[ik].top,usewr[ik].width(),usewr[ik].height());
				if (this.showRedrawBounds) {
					this.context.strokeStyle = !this._boundFlicker?"#FF0000":Math.random()>.5?("#FF0000"):"#FFFFFF";
					this.context.lineWidth = 1;
					this.context.strokeRect(usewr[ik].left,usewr[ik].top,usewr[ik].width(),usewr[ik].height());
				}
			}
			
			//render draw each item on the list. 
			for (var k=0;k<redraw.length;k++) {
				redraw[k].graphics.draw(this.canvas,this.context);
			}
			
			this._lastwr = wr.slice();
			wr = [];
			
			this.checkMouse();
		}
		
		return (wr);
	}
	
	StrikeDisplay.prototype.checkMouse = function() {
		var mpoint = new Point(this._mouse.x,this._mouse.y);
		/*if (mpoint.x>this.stage.stageWidth || mpoint.y>this.stage.stageHeight || 
				mpoint.x<0 || mpoint.y<0) {
				//To do: mouse off-stage behavior... presently 
		}*/
		var viable = new Array();
		var b = this.SpritesUnderPoint(mpoint);
		for (var k in b) {
			if (b[k].hasParentMouseListeners) {
				if ((b[k] == this.stage || b[k]._pointInGraphics(mpoint)) && b[k].mouseEnabled) {
					//we don't re-render the whole stage on the hidden canvas each time, to save speed. 
					//so here we take a shortcut and always push the stage into the viable objects under the mouse. 
					viable.push(b[k]);
				}
			}
		}
        var vlen = viable.length; //now always includes stage listeners, if any. 
		if (vlen>0) {
			viable.sort(this.zCompare);
			if (this._mouse.clicked) {
				this.chainDispatchAsNeeded(viable[0],"click");
			}
			if (this._mouse.down && !this._mouse.downDispatched) {
				this.chainDispatchAsNeeded(viable[0],"mouseDown");
				this._mouse.downDispatched = true;
			}
			if (this._mouse.up) {
				this.chainDispatchAsNeeded(viable[0],"mouseUp");
				this._mouse.downDispatched = false;
			}
            if (this._mouse.moved) this.chainDispatchAsNeeded(viable[0],"mouseMoved");
            
            //note here that stage dispatches mouseOvers itself, whether hovering a child or not. 
            //this behavior differs from AS3. 
			this.chainDispatchAsNeeded(viable[0],"mouseOver");
            
			this.canvas.style.cursor = 'pointer';
            viable[0].mouseWithin=true; //necessary to handle mouseOut actions when no other is handled. 
		} else {
			this.canvas.style.cursor = '';
		}
        
		//if (_MainEventList["mouseOut"]) { 
        //now runs every time, to also set mouseWithin false on mouseOver listeners, 
        //even if there are no mouseOuts. 
			var viableChain = [];
			if (viable.length>0) {
				viableChain.push(viable[0]);
				var v = viable[0];
				while (v = v.parent) {
					viableChain.push(v);
				}
			}
			var mo = _MainEventList["mouseOut"]
			for (var m in mo) {
				if (mo[m].sprite.mouseWithin && viableChain.indexOf(mo[m].sprite)==-1) {
					mo[m].sprite.dispatchEvent(new Event("mouseOut",mo[m].sprite,mo[m].sprite));
					mo[m].sprite.mouseWithin = false;
				}
			}
            mo = _MainEventList["mouseOver"]
			for (var m in mo) {
				if (mo[m].sprite.mouseWithin && viableChain.indexOf(mo[m].sprite)==-1) {
					mo[m].sprite.mouseWithin = false;
				}
			}
            /*if (this._mouse.moved) {
                mo = _MainEventList["mouseMove"]
                for (var m in mo) {
                    mo[m].sprite.dispatchEvent(new Event("mouseMove",mo[m].sprite,mo[m].sprite));
                }
            }*/
		//}
	
		this._mouse.clicked = false;
		this._mouse.down = false;
		this._mouse.up = false;
	}
	StrikeDisplay.prototype.chainDispatchAsNeeded = function(a,type,orig) {
		if (orig == null) {
			orig = a;
		}
		if (a._listeners[type]) {
			for (var k in a._listeners[type]) {
				var evt = new Event(type,a,orig);
				if (!a.mouseWithin || type!="mouseOver") //POST //bad, we want to keep dispatching mouseOvers. 
                a.dispatchEvent(evt);
				if (type == "mouseOver" || type == "click" || type == "mouseDown" || type == "mouseUp") {
					a.mouseWithin = true;
				}
			}
		}
		if (a.parent != null) {
			this.chainDispatchAsNeeded(a.parent,type,orig);
		}
	}
	
	StrikeDisplay.prototype.SpritesUnderPoint = function(point,s,out) {
		if (s == null) {
			var out = [];
			var s = this.stage;
		}
		if (s==this.stage || s._pointInBounds(point)) { 
            //can include stage by default; this is ignored for MouseOver/Out. 
			out.push(s);
		}
		for (var k in s.children) {	
			this.SpritesUnderPoint(point,s.children[k],out);
		}
		return (out);
	}
	
	StrikeDisplay.prototype.zCompare = function(a,b) {
		var ar = [];
		var j = a;
		var k = b;
		ar.push(j);
		while (j = j.parent) {
			ar.push(j);
		}
		var lastk = k;
		while (k = k.parent) {
			var ark = ar.indexOf(k);
			if (ark>-1) {
				return (k.children.indexOf(ar[ark-1])>k.children.indexOf(lastk)?-1:1);
			}
			lastk = k;
		}
		return (-1);
	}
	
	StrikeDisplay.prototype.BoundCollisionTraverse = function(child,s,out) {
		if (s == null) {
			var out = [];
			var s = this.stage;
		}
		if (s != child && !s.invalid) {
			if (child._intersectsBounds(s)) {
				out.push(s);
			}
		}
		for (var k in s.children) {
			this.BoundCollisionTraverse(child,s.children[k],out);
		}
		return (out);
	}
    
    function deriveBounds(s,wr,xhold,yhold,drill) {
		//Traverses display nodes downward from an arbitrary Sprite, 
		//Returns a the smalles possible bounding box holding the sprite and all its children, in the global coordinate space.
		//This function assumes the _bb's for every object are already set by their various drawing commands. 
		if (wr===false) {
			wr = new BoundingBox();
			xhold = s.x;
			yhold = s.y;
		} else {
			xhold = s.x;
			yhold = s.y;
		}
		var mat = s.concatMatrix();
		wr.set(s._bb.left+xhold,s._bb.right+xhold,s._bb.top+yhold,s._bb.bottom+yhold);
		
		if (drill) {
			for (var i=0;i<s.children.length;i++) {
				var cr = s.children[i];
				deriveBounds(cr,wr,xhold,yhold,drill);
			}
		}
		return (wr);
	}
	
	function Point(x,y) {
		this.x = x;
		this.y = y;
	}
	
	function Matrix(_a,_b,_c,_d,_tx,_ty,_u,_v,_w) {
		this.a = _a || 1;
		this.b = _b || 0;
		this.c = _c || 0;
		this.d = _d || 1;
		this.tx = _tx || 0;
		this.ty = _ty || 0;
		this.u = _u || 0;
		this.v = _v || 0;
		this.w = _w || 1;
	}
	Matrix.prototype.translate = function(x,y) {
		this.tx+=x;
		this.ty+=y;
	}
	Matrix.prototype.scale = function(sx,sy) {
		this.a *= sx;
		this.d *= sy;
		this.tx *= sx;
		this.ty *= sy;
	}
	Matrix.prototype.innerScale = function(sx,sy) {
		var m = new Matrix();
		m.scale(sx,sy);
		m = m.multiply(this);
		this.a = m.a;
		this.b = m.b;
		this.c = m.c;
		this.d = m.d;
		this.tx = m.tx;
		this.ty = m.ty;
	}
	Matrix.prototype.rotate = function(deg) {
		var sine = Math.sin(deg*(Math.PI/180));
		var cosine = Math.cos(deg*(Math.PI/180));
		var k = [this.a,this.b,this.c,this.d,this.tx,this.ty];
		this.a = k[0]*cosine - k[1]*sine;
		this.b = k[0]*sine + k[1]*cosine;
		this.c = k[2]*cosine - k[3]*sine;
		this.d = k[2]*sine + k[3]*cosine;
	}
	Matrix.prototype.transformPoint = function(point) {
		var prime = new Point(point.x*this.a+point.y*this.c+this.tx,point.x*this.b+point.y*this.d+this.ty);
		return (prime);
	}
	Matrix.prototype.deltaTransformPoint = function(point) {
		var prime = new Point(point.x*this.a+point.y*this.c,point.x*this.b+point.y*this.d);
		return (prime);
	}
	Matrix.prototype.multiply = function(m) {
		var mult = new Matrix(this.a*m.a+this.b*m.c+this.u*m.tx,
							  this.a*m.b+this.b*m.d+this.u*m.ty,
							  this.c*m.a+this.d*m.c+this.v*m.tx,
							  this.c*m.b+this.d*m.d+this.v*m.ty,
							  this.tx*m.a+this.ty*m.c+this.w*m.tx,
							  this.tx*m.b+this.ty*m.d+this.w*m.ty,
							  this.a*m.u+this.b*m.v+this.u*m.w,
							  this.c*m.u+this.d*m.v+this.v*m.w,
							  this.tx*m.u+this.ty*m.u+this.w*m.w);
		return (mult);
	}
	Matrix.prototype.inverse = function() {
		var n = this.a*this.d-this.b*this.c;
		var inv = new Matrix(this.d/n,
							 -this.b/n,
							 -this.c/n,
							 this.a/n,
							 (this.c*this.ty-this.d*this.tx)/n,
							 -(this.a*this.ty-this.b*this.tx)/n,
							 0,0,1);
		return (inv);
	}
	Matrix.prototype.getScaleX = function() {
		return (Math.sqrt(this.a * this.a + this.b * this.b));
	}
	Matrix.prototype.getScaleY = function() {
		return (Math.sqrt(this.c * this.c + this.d * this.d));
	}
	Matrix.prototype.getRotation = function() {
		var px = this.deltaTransformPoint(new Point(0,1));
		return ((180/Math.PI) * Math.atan2(px.y, px.x) - 90);	
	}
	
	function BoundingBox() {
		this.left = null;
		this.right = null;
		this.top = null;
		this.bottom = null;
	}
	BoundingBox.prototype.set = function(l,r,t,b,strokeOn,mult,xtra) {
		//additive set function
		if (!xtra) {
			xtra = 0;
		}
		if (!strokeOn) {
			sw = 1+xtra;
		} else {
			sw = (strokeOn.lineWidth+xtra)*mult;
		}
		if (this.left == null || l-sw<this.left) {
			this.left = l-sw;
		}
		if (this.right == null || r+sw>this.right) {
			this.right = r+sw;
		}
		if (this.top == null || t-sw<this.top) {
			this.top = t-sw;
		}
		if (this.bottom == null || b+sw>this.bottom) {
			this.bottom = b+sw;
		}
	}
	BoundingBox.prototype.unset = function() {
		this.left = null;
		this.right = null;
		this.top = null;
		this.bottom = null;
	}
	BoundingBox.prototype.width = function() {
		return (this.right-this.left);
	}
	BoundingBox.prototype.height = function() {
		return (this.bottom-this.top);
	}
	BoundingBox.prototype.debug = function() {
		return (this.left+","+this.right+","+this.top+","+this.bottom);
	}
	
	function Graphics(s) {
		this.sprite = s;
		this.list = new Array();
		this.corners = [];
		this.images = {};
		this.divs = [];
		this.fillOn = false;
		this.strokeOn = null;
		this.pathOn = false;
        this._cacheOnStage = false;
        this._imagesNotLoaded = 0;
	}
    Graphics.prototype._findContext = function(cont) {
        var context;
        if (!cont) {
            context = this.sprite.stage.display.context;
        } else if (cont=='hidden') {
            context = this.sprite.stage.display.hiddenContext;
        } else if (cont=='cache') {
            context = this.cacheContext;
        }
        return (context);
    }
	Graphics.prototype.clear = function() {
		this._tempuncache();
		this.list = new Array();
		this.corners = [];
		this.images = {};
		this.divs = [];
		this.sprite._bb.unset();
		this.sprite.invalid = true;
	}
	Graphics.prototype._setContextFill = function(context) {
		var fo = this.fillOn;
		if (fo.type == "basic") {
			context.fillStyle = fo.color;
		} else if (fo.type == "linearGradient") {
			var g = context.createLinearGradient(fo.xa,fo.ya,fo.xb,fo.yb);
			g.addColorStop(0,fo.c1);
			g.addColorStop(1,fo.c2);
			context.fillStyle = g;
		}
	}
	Graphics.prototype._beginFill = function(c,a,context) {
		if (!context || context=='cache') {
			if (c!="none") {
				this.fillOn = {type:"basic",color:_colorToStyle(c,a)};
			} else {
				this.fillOn = false;
			}
		} else {
            //hidden context
			this.fillOn = {type:"basic",color:"#000000"};
		}
	}
	Graphics.prototype.beginFill = function(color,alpha) {
		this._tempuncache();
		if (!alpha) {
			alpha = 1;
		}
		this.list.push({f:'_beginFill',v:[color,alpha]});
		this.sprite.invalid = true;
		this._beginFill(color,alpha);
	}
	Graphics.prototype._beginSimpleGradient = function(ca,aa,cb,ab,xa,ya,xb,yb,cont) {
		if (!cont || cont=='cache') {
			var c1 = _colorToStyle(ca,aa);
			var c2 = _colorToStyle(cb,ab);
			this.fillOn = {type:"linearGradient",c1:c1,c2:c2,xa:xa,ya:ya,xb:xb,yb:yb};
		} else {
			this.fillOn = {type:"basic",color:"#000000"};
		}
	}
	Graphics.prototype.beginSimpleGradient = function(colorA,alphaA,colorB,alphaB,xa,ya,xb,yb) {
		this._tempuncache();
		this.list.push({f:'_beginSimpleGradient',v:[colorA,alphaA,colorB,alphaB,xa,ya,xb,yb]});
		this.sprite.invalid = true;
		this._beginSimpleGradient(colorA,alphaA,colorB,alphaB,xa,ya,xb,yb);
	}
	Graphics.prototype._endFill = function(context) {
		this.fillOn = false;
	}
	Graphics.prototype.endFill = function() {
		this._tempuncache();
		this.list.push({f:'_endFill',v:[]});
		this.sprite.invalid = true;
		this._beginFill("none",1);
	}
	Graphics.prototype._lineStyle = function(w,c,a,context) {
		if (w>0) {
			this.strokeOn = {lineWidth:w,strokeStyle:_colorToStyle(c,a)};
		} else {
			this.strokeOn = null;
		}
	}
	Graphics.prototype.lineStyle = function(width,color,alpha) {
		this._tempuncache();
		if (!alpha) {
			alpha = 1;
		}
		this.list.push({f:'_lineStyle',v:[width,color,alpha]});
		this.sprite.invalid = true;
		this._lineStyle(width,color,alpha);
	}
	Graphics.prototype._moveTo = function(x,y,cont) {
		if (this.sprite.stage) {
			var context = this._findContext(cont);
			if (!this.pathOn) {
				this.pathOn = true;
				if (!this.sprite._isClip) {
					context.beginPath();
				}
			}
			var display = this.sprite.stage.display;
			display.translateToSprite(this.sprite,cont);
	
			context.moveTo(x,y);
			display.unTranslate(cont);
	
		}
	}
	Graphics.prototype.moveTo = function(x,y) {
		this._tempuncache();
		x = Number(x);
		y = Number(y);
		this.list.push({f:'_moveTo',v:[x,y]});
		this.corners.push(new Point(x,y));
		this.sprite.invalid = true;
		this.sprite.setBoundingBox();
	}
	Graphics.prototype._lineTo = function(x,y,cont) {
		if (this.sprite.stage) {
			var context = this._findContext(cont);
			if (!cont) {
				context = this.sprite.stage.display.context;
			} else {
				context = this.sprite.stage.display.hiddenContext;
			}
			if (!this.pathOn) {
				this.pathOn = true;
				if (!this.sprite._isClip) {
					context.beginPath();
				}
			}
			var display = this.sprite.stage.display;
			display.translateToSprite(this.sprite,cont);
			context.lineTo(x,y);
			if (this.strokeOn != null) {
				context.lineWidth = this.strokeOn.lineWidth;
				context.strokeStyle = this.strokeOn.strokeStyle;
				context.stroke();
			}
			display.unTranslate(cont);
		}
	}
	Graphics.prototype.lineTo = function(x,y) {
		this._tempuncache();
		x = Number(x);
		y = Number(y);
		this.list.push({f:'_lineTo',v:[x,y]});
		this.corners.push(new Point(x,y));
		this.sprite.invalid = true;
		this.sprite.setBoundingBox();
	}
	Graphics.prototype._curveTo = function(xa,ya,xb,yb,cont) {
		if (this.sprite.stage) {
			var context = this._findContext(cont);
			if (!this.pathOn) {
				this.pathOn = true;
				if (!this.sprite._isClip) {
					//only one path can be made per clip in the entire graphics object. 
					//so we only beginPath once from the parent when drawing the clippingSprite prior to the parent's draw. 
					context.beginPath();
				}
			}
			var display = this.sprite.stage.display;
			display.translateToSprite(this.sprite,cont);			
			context.quadraticCurveTo(xa,ya,xb,yb);
			if (this.strokeOn != null) {
				context.lineWidth = this.strokeOn.lineWidth;
				context.strokeStyle = this.strokeOn.strokeStyle;
				context.stroke();
			}
			display.unTranslate(cont);
		}
	}
	Graphics.prototype.curveTo = function(xa,ya,xb,yb) {
		this._tempuncache();
		xa = Number(xa);
		ya = Number(ya);
		xb = Number(xb);
		yb = Number(yb);
		this.list.push({f:'_curveTo',v:[xa,ya,xb,yb]});
		this.corners.push(new Point(xa,ya),new Point(xb,yb));
		this.sprite.invalid = true;
		this.sprite.setBoundingBox();
	}
	Graphics.prototype._endPath = function(cont) {
		if (!this.pathOn) return (false);
		var context = this._findContext(cont);
		this.pathOn = false;
		if (this.fillOn && !this.sprite._isClip) {
			this._setContextFill(context);
			context.fill();
			context.closePath();
		}
	}
	Graphics.prototype.endPath = function() {
		this._tempuncache();
		this.list.push({f:'_endPath',v:[]});
		this.sprite.invalid = true;
		this._endPath();
	}
		
	Graphics.prototype._drawRect = function(x,y,w,h,cont) {
		if (this.sprite.stage) {
			var context = this._findContext(cont);
			var display = this.sprite.stage.display; 
			display.translateToSprite(this.sprite,cont); //transform the context to the sprite's collective transformation. 
			
			if (!this.sprite._isClip) {
				context.beginPath();
			}
            context.rect(x,y,w,h);
			if (this.fillOn && !this.sprite._isClip) {
				this._setContextFill(context);
                context.fill();
			}
			if (this.strokeOn != null) {
				context.lineWidth = this.strokeOn.lineWidth;
				context.strokeStyle = this.strokeOn.strokeStyle;
				context.strokeRect(x,y,w,h);
			}
			context.closePath();
			display.unTranslate(cont);
		}
	}
	Graphics.prototype.drawRect = function(x,y,w,h) {
		this._tempuncache();
		x = Number(x);
		y = Number(y);
		w = Number(w);
		h = Number(h);
		this.list.push({f:'_drawRect',v:[x,y,w,h]});
		this.corners.push(new Point(x,y),new Point(x+w,y),new Point(x,y+h),new Point(x+w,y+h));
		this.sprite.invalid = true;
		this.sprite.setBoundingBox();
	}
	Graphics.prototype._drawRoundRect = function(x,y,w,h,p,cont) {
		if (this.sprite.stage) {
			var context = this._findContext(cont);
			var display = this.sprite.stage.display; 
			display.translateToSprite(this.sprite,cont); //transform the context to the sprite's collective transformation. 
			display.translateWithinSprite(x,y,0,1,1,context); //important for round rects. 
			x = 0;
			y = 0;
			if (!this.sprite._isClip) {
				context.beginPath();
			}
			context.moveTo(x+p,y);
			context.lineTo((x+w)-p,y);
			context.quadraticCurveTo(x+w,y,x+w,y+p);
			context.lineTo(x+w,(y+h)-p);
			context.quadraticCurveTo(x+w,y+h,(x+w)-p,y+h);
			context.lineTo(x+p,y+h);
			context.quadraticCurveTo(x,y+h,x,(y+h)-p);
			context.lineTo(x,y+p);
			context.quadraticCurveTo(x,y,x+p,y);
			
			if (this.fillOn && !this.sprite._isClip) {
				this._setContextFill(context);
				context.fill();
			}
			if (this.strokeOn != null) {
				context.lineWidth = this.strokeOn.lineWidth;
				context.strokeStyle = this.strokeOn.strokeStyle;
				context.stroke();
			}
			context.closePath();
			display.unTranslate(cont);
		}
	}
	Graphics.prototype.drawRoundRect = function(x,y,w,h,p) {
		this._tempuncache();
		x = Number(x);
		y = Number(y);
		w = Number(w);
		h = Number(h);
		this.list.push({f:'_drawRoundRect',v:[x,y,w,h,p]});
		this.corners.push(new Point(x,y),new Point(x+w,y),new Point(x,y+h),new Point(x+w,y+h));
		this.sprite.invalid = true;
		this.sprite.setBoundingBox();
	}
	Graphics.prototype._drawCircle = function(x,y,r,cont) {
		if (this.sprite.stage) {
			var context = this._findContext(cont);
			var display = this.sprite.stage.display; 
			display.translateToSprite(this.sprite,cont); //transform the context to the sprite's collective transformation. 
			
			if (!this.sprite._isClip) {
				context.beginPath();
			}
			context.arc(x,y,r,0,Math.PI*2,false);
			if (this.fillOn && !this.sprite._isClip) {
				this._setContextFill(context);
				context.fill();
			}
			if (this.strokeOn != null) {
				context.lineWidth = this.strokeOn.lineWidth;
				context.strokeStyle = this.strokeOn.strokeStyle;
				context.stroke();
			}
			
			context.closePath();

			display.unTranslate(cont);
		}
	}
	Graphics.prototype.drawCircle = function(x,y,radius) {
		this._tempuncache();
		x = Number(x);
		y = Number(y);
		radius = Number(radius);
		this.list.push({f:'_drawCircle',v:[x,y,radius]});
		this.corners.push(new Point(x-radius,y-radius),new Point(x-radius,y+radius),new Point(x+radius,y-radius),new Point(x+radius,y+radius));
		this.sprite.invalid = true;
		this.sprite.setBoundingBox();
	}
	Graphics.prototype._drawImage = function(name,x,y,dw,dh,sx,sy,sw,sh,cont) {
		if (this.sprite.stage && this.images[name].img) {
			var context = this._findContext(cont);
			var display = this.sprite.stage.display;
			display.translateToSprite(this.sprite,cont);
			if (cont!='hidden') {
				if (sx == "none") {
					context.drawImage(this.images[name].img,x,y,dw,dh);
				} else {
					context.drawImage(this.images[name].img,x,y,dw,dh,sx,sy,sw,sh);
				}
			} else {
				context.fillStyle = '#000000';
				context.fillRect(x,y,dw,dh);
			}
			display.unTranslate(cont);
		}
	}
	Graphics.prototype.drawImage = function(name,src,x,y,dw,dh,sx,sy,sw,sh) {
		this._tempuncache();
		x = x ? Number(x) : 0;
		y = y ? Number(y) : 0;
		dw = dw ? Number(dw) : 0;
		dh = dh ? Number(dh) : 0;
		if (!sx && !sy && !sw && !sh) {
			sx = sx ? Number(sx) : "none";
		} else {
			sx = sx ? Number(sx) : 0;
			sy = sy ? Number(sy) : 0;
			sw = sw ? Number(sw) : 0;
			sh = sh ? Number(sh) : 0;
		}
		if (dw || dh) {
			this.list.push({f:'_drawImage',v:[name,x,y,dw,dh,sx,sy,sw,sh]});
			this.corners.push(new Point(x,y),new Point(x+dw,y),new Point(x,y+dh),new Point(x+dw,y+dh));
		}
		var img = new Image();
		img.name = name;
		img.onload = this._imageLoaded.bind(this);
		this.images[name] = {img:null,x:x,y:y,dw:dw,dh:dh};
		img.src = src;
		this.sprite.invalid = true;
		this.sprite.setBoundingBox();
        
        this._imagesNotLoaded++;
	}
	Graphics.prototype._imageLoaded = function(e) {
		this.images[e.target.name].img = e.target;
		var imgObj = this.images[e.target.name];
		if (imgObj.dw==0) {
			var dw = this.images[e.target.name].img.width;
			var dh = this.images[e.target.name].img.height;
            this.list.push({f:'_drawImage',v:[e.target.name,imgObj.x,imgObj.y,dw,dh,"none",0,0,0]});             
			this.corners.push(new Point(imgObj.x,imgObj.y),new Point(imgObj.x+dw,imgObj.y),new Point(imgObj.x,imgObj.y+dh),new Point(imgObj.x+dw,imgObj.y+dh));
		}
		this.sprite.invalid = true;
		this.sprite.setBoundingBox();
		this.sprite.dispatchEvent(new Event("loaded"));
        this._imagesNotLoaded--;
        
        if (this._cacheOnStage && this._imagesNotLoaded==0) {
            this._cacheAsBitmap();
        }
	}
    Graphics.prototype._drawTextLine = function(x,y,text,font,size,color,cont) {
        if (this.sprite.stage) {
			var context = this._findContext(cont);
			var display = this.sprite.stage.display; 
			display.translateToSprite(this.sprite,cont); //transform the context to the sprite's collective transformation. 
			this.fillOn = {type:"basic",color:_colorToStyle(color,1)};
            context.lineWidth = 0;
            this._setContextFill(context);
            context.font = size+"pt "+font;
            context.textAlign = 'left';
            context.textBaseline = 'top';
            context.fillText(text,x,y);
            this.fillOn = false;
			display.unTranslate(cont);
		}
    }
    Graphics.prototype.drawTextLine = function(x,y,text,font,size,color,checkOnly,justified,widthOnly) {
		this._tempuncache();
		x = Number(x);
		y = Number(y);
        _mainContext.font = size+"pt "+font; //this may be a different context than where we ultimately draw. 
		var k = 0;
		if (!justified && text.substring(text.length-1)==" ") {
			text = text.substring(0,text.length-1); //drop endline spaces
			k++;
		}
        var w = _mainContext.measureText(text).width;
        while (this.sprite.width>0 && w > this.sprite.width && text.length>0) {
            text = text.substring(0,text.length-1);
            k++;
			if (!justified && text.substring(text.length-1)==" ") {
				text = text.substring(0,text.length-1);
				k++;
			}
            w = _mainContext.measureText(text).width;
        }
        if (checkOnly) return (k); //for checks return the number of leftover characters. 
		if (widthOnly) return (w);
		this.list.push({f:'_drawTextLine',v:[x,y,text,font,size,color]});
		this.corners.push(new Point(x,y),new Point(x+w,y),new Point(x,y+size*_lineHeightMultiplier*1.3),new Point(x+w,y+size*_lineHeightMultiplier*1.3));
		this.sprite.invalid = true;
		this.sprite.setBoundingBox();
        return (w); //if we're not checking for characters, return the pixel width instead. 
	}
	Graphics.prototype.attachDiv = function(id,x,y) {
		for (var i in this.divs) {
			if (this.divs[i].id == id) return (false);
		}
		if (!x) x = 0;
		if (!y) y = 0;
		this.divs.push({id:id,x:x,y:y});
	}
	Graphics.prototype.removeDiv = function(id) {
		for (var i in this.divs) {
			if (this.divs[i].id == id) {
				this.divs.splice(i,1);
				return (true);
			}
		}
		return (false);
	}
	Graphics.prototype.setDivProperties = function(id,props) {
		for (var i in this.divs) {
			if (this.divs[i].id == id) {
				for (var k in props) {
					this.divs[i][k] = props[k];
				}
				return (true);
			}
		}
		return (false);
	}
	Graphics.prototype.deriveAlpha = function() {
		var s = this.sprite;
		var a = this._isCached?1:s.alpha;
		while (s = s.parent) {
			a *= s.alpha;
		}
		return (a)
	}
    Graphics.prototype._cacheAsBitmap = function () {
		if (this._isCached || this._cacheOnRedraw) return (false); //is UNSET before this is called from the draw. 
		if (!this.sprite.cachedByUser && this.sprite.filters.length==0) return (false);
        if (!this.sprite.stage || this._imagesNotLoaded>0) {
            this._cacheOnStage = true;
            return (false);
        }
		if (!this.sprite._cachedWidth) {
			this.sprite._cachedWidth = this.sprite.width;
			this.sprite._cachedHeight = this.sprite.height;
		}
		
        this.cacheCanvas = document.createElement('canvas');
        //pre-rotate; only true drawn offsets left and top of the centerpoint should be considered here, 
        //NOT the offsets to the bounding box caused by rotation.
        //this was simply the -bb.left and -bb.top of the sprite's real bounding box before. 
        var bb = this.sprite.getNativeSize().nativebb;
        var offset = new Point(this.sprite.x-bb.left,this.sprite.y-bb.top);
				
        var maxFilter = this.sprite.maxFilter;
		var spacing = maxFilter.spacing; //added all around to the size of the bounding box
		var padding = maxFilter.padding; //shifts the cache canvas left and up within the bb.
        var tw;
        if (!this.sprite.textWidth) { 
            tw = [this.sprite.filterWidth+spacing,this.sprite.filterHeight+spacing];
        } else {
            tw = [this.sprite.textWidth+spacing,this.sprite.textHeight+spacing];
			offset.x = offset.y = 0;
        }
        this.cacheCanvas.setAttribute('width',tw[0]);
        this.cacheCanvas.setAttribute('height',tw[1]);
        this.cacheContext = this.cacheCanvas.getContext('2d');
		
		/*test to show the cached canvas*/
		/*this.cacheContext.fillStyle = "#333333";
		this.cacheContext.rect(0,0,tw[0],tw[1]);
		this.cacheContext.fill();*/
		
        this.cacheContext.setTransform(1,0,0,1,offset.x,offset.y);
        this.draw(this.cacheCanvas,this.cacheContext,true);
        this.cacheContext.setTransform(1,0,0,1,0,0);
		
        this._cacheList = this.list.slice();
		if (!this._cacheCorners) this._cacheCorners = this.corners.slice();
        this.images['cacheImage'] = {img:this.cacheCanvas};
		var x = -offset.x;
		var y = -offset.y;
		var dw = tw[0];
		var dh = tw[1];
        this.list = [{f:'_drawImage',v:['cacheImage',-offset.x-padding,-offset.y-padding,tw[0],tw[1],"none",0,0,0]}];
		this.corners = [new Point(x,y),new Point(x+dw,y),new Point(x,y+dh),new Point(x+dw,y+dh)]
		this.sprite.invalid = true;
		this.sprite.setBoundingBox();
        this._isCached = true;
    }
	Graphics.prototype._tempuncache = function () {
		//called when drawing to a cached sprite. Restores the former cached state on the next redraw.
		//filtered sprites and textfields are automatically cached. 
		if (this._uncache()) 
			this._cacheOnRedraw = true;
    }
    Graphics.prototype._uncache = function () {
		//_isCached is a flag set on each draw.
		//_cacheList and _cacheCorners hold the values for the list and corners of the vector draw.
		//sprite.cachedByUser is flagged when they _want_ it to cache or not. Only set by the public method. TextField flags true automatically.
		//_cacheOnStage triggers when sprites cached earlier are added to the stage. Trying to do it before leads to an empty draw.
		//_cacheOnRedraw is set by various methods that want to cache on the next redraw, and skip the generic drawing method. 
		if (this._cacheOnStage) this._cacheOnStage = false;
        if (!this._isCached) return (false);
		this.sprite._cachedWidth = this.sprite._cachedHeight = 0;
        this.list = this._cacheList.slice();
		this.corners = this._cacheCorners.slice();
		this._cacheList = null;
		this._cacheCorners = null;
		this.sprite.invalid = true;
		this.sprite.setBoundingBox();
        this._isCached = false;
        delete(this.images['cacheImage']);
		return (true);
    }
	Graphics.prototype.draw = function(rcan,rc,caching) {
		if (this._cacheOnRedraw) {
			this._cacheOnRedraw = false;
			this._cacheAsBitmap();
		}
		//go through the list and draw. 
        //set caching true 
        rc.save();
		
        var cs = this.sprite;
        var bp = false;
        
        while (this.sprite.stage && cs.name != "stage" && !cs._isClip && !caching) {
            if (cs.clippingSprite != null) {
                if (!bp) { 
                    rc.beginPath();
                    bp = true;
                }
                cs.clippingSprite.graphics.draw(rcan,rc);
            }
            cs = cs.parent;
        }
        if (bp && !caching) rc.clip();
        
        rc.globalAlpha = this.deriveAlpha();
        
        //prefilters//
        var filters = this.sprite._filters;
        var flen = filters.length;
		if (!this._isCached) {
			for (var f=0;f<flen;f++) {
				if (filters[f].predraw) {
					filters[f]._act(rcan,null);
				}
			}
		}
        
        for (var i=0;i<this.list.length;i++) {
            var com = this.list[i];
            var iter = "";
            for (var k=0;k<com.v.length;k++) {
                
                if (k>0) {
                    iter += ",";
                }
                iter += '"'+com.v[k]+'"';
            }
            
            if (caching) iter += ',"cache"';
            //alert('this.'+com.f+'('+iter+')');
            try {eval('this.'+com.f+'('+iter+');')} catch (err) {
                //alert('this.'+com.f+'('+iter+')');
            }
        }
        
		var m = this.sprite.concatMatrix();
		for (var k in this.divs) {
			var div = document.getElementById(this.divs[k].id);
			div.style.position = 'absolute';
			div.style.left = (m.tx+canvasOffset(rcan)[0]+this.divs[k].x)+'px';
			div.style.top = (m.ty+canvasOffset(rcan)[1]+this.divs[k].y)+'px';
		}
        
        /*postfilters only operate on the caching canvases and are reapplied as images*/
        if (caching) {
			var spacing = this.sprite.maxFilter.spacing;
            var filteredPix = rc.getImageData(-spacing,-spacing,this.sprite.filterWidth+spacing*2,this.sprite.filterHeight+spacing*2);
            for (var f=0;f<flen;f++) {
                if (!filters[f].predraw) {
                    filteredPix = filters[f]._act(rcan,filteredPix);
                }
            }
            if (filteredPix) {
                rc.putImageData(filteredPix,-spacing/2,-spacing/2);
            }
        }
        
		rc.restore();
	}
	Graphics.prototype._drawToHidden = function() {
		this.sprite.stage.display.hiddenContext.save();
		var list = this.sprite._cacheList ? this._cacheList : this.list;
		var len = list.length;
		for (var i=0;i<len;i++) {
			var com = list[i];
			var iter = "";
            for (var k=0;k<com.v.length;k++) {
                if (k>0) {
                    iter += ",";
                }
                iter += '"'+com.v[k]+'"';
            }
			try {
				var cstr = '';
				if (iter != "") {
					cstr = 'this.'+com.f+'('+iter+',"hidden");';
					eval('this.'+com.f+'('+iter+',"hidden");');
				} else {
					cstr = 'this.'+com.f+'('+iter+',"hidden");';
					eval('this.'+com.f+'("hidden");');
				}
			} catch (err) {
				//alert(cstr);
			}
		}
		this.sprite.stage.display.hiddenContext.save();
	}
	
	
    function Sprite(display) {
		this._listeners = {};
		this.graphics = new Graphics(this);
        this._cacheAsBitmap = false;
		this.children = new Array();
		this._bb = new BoundingBox();
		this.blockRedraw = false;
		this._clippingSprite = null;
		this._isClip = false;
        this.mouseEnabled = true;
		if (display) {
			this.stage = this;
			this.stageWidth = display.canvas.width;
			this.stageHeight = display.canvas.height;
			this._bb.set(0,this.stageWidth,0,this.stageHeight);
		}
		this.transform = new Matrix();
		this.alpha = 1;
		this.shadow = null;
		this.mouseWithin = false;
	}
    
	Sprite.prototype.name = "";
	Sprite.prototype.parent = null;
	Sprite.prototype.invalid = true;
	Sprite.prototype._rotation = 0; //arbitrary usage...
    Sprite.prototype._filters = []; 
    Sprite.prototype._parentFilters = [];
	Sprite.prototype.assignStage = function(s,st) {
		s.stage = st;
		for (var k=0;k<s.children.length;k++) {
			s.assignStage(s.children[k],st);
		}
        if (st && s.graphics._cacheOnStage && !s.graphics._cacheOnRedraw) {
            s.graphics._cacheAsBitmap();
        }
	}
	
	///EVENTS///
	function Event(type,currentTarget,target) {
		this.type = type;
		this.currentTarget = currentTarget;
		this.target = target;
	}
	
	Sprite.prototype.dispatchEvent = function(evt) {
		for (var i in _MainEventList) {
			for (var k in _MainEventList[i]) {
				if (i == evt.type && _MainEventList[i][k].sprite == this) {
					evt.currentTarget = this;
					if (!evt.target) evt.target = this;
					_MainEventList[i][k].fun(evt);
					return (true);
				}
			}
		}
	}
	Sprite.prototype._addMainListener = function(type,fun) {
		if (_MainEventList[type]) {
			_MainEventList[type].push({sprite:this,fun:fun,type:type});
		} else {
			_MainEventList[type] = [{sprite:this,fun:fun,type:type}];
		}
	}
	Sprite.prototype._removeMainListener = function(type,fun) {
		for (var k in _MainEventList[type]) {
			if (_MainEventList[type][k].sprite == this && (_MainEventList[type][k].fun == fun || _MainEventList[type][k].fun._method==fun)) {
				delete (_MainEventList[type][k].sprite);
				delete (_MainEventList[type][k].fun);
				_MainEventList[type].splice(k,1);
				if (_MainEventList[type].length<1) {
					delete _MainEventList[type];
				}
			}
		}
	}
	Sprite.prototype._detachFromMain = function() {
		//remove all listeners associated with this sprite from the main event list... 
		for (var j in _MainEventList) {
			for (var k in _MainEventList[j]) {
				if (_MainEventList[j][k].sprite == this) {
					var mlist = _MainEventList[type].splice(k,1);
					mlist = null;
					delete mlist;
				}
			}
		}
		for (var c=0;c<this.children.length;c++) {
			this.children[c]._detachFromMain();
		}
	}
	Sprite.prototype.addEventListener = function(type,fun) {
		if (this._listeners[type]) {
			if (this.hasEventListener(type,fun)) {
				return (false);
			}
			this._listeners[type].push(fun);
		} else {
			this._listeners[type] = [fun];
		}
		this._addMainListener(type,fun);
	}
	Sprite.prototype.hasEventListener = function(type,fun,pos) {
		//pos flag returns the index of the listener on success (used for removal). 
		if (!this._listeners[type]) return (false);
		var len = this._listeners[type].length;
		for (var i=0;i<len;i++) {
			if (this._listeners[type][i] == fun || this._listeners[type][i]._method==fun) return (pos?i:true);
		}
		return (false);
	}
	Sprite.prototype.removeEventListener = function(type,fun) {
		var i = this.hasEventListener(type,fun,true);
		if (i !== false) {
			this._listeners[type].splice(i,1);
			this._removeMainListener(type,fun);
			if (this._listeners[type].length<1) {
				this._listeners[type] = null;
				delete this._listeners[type];
			}
		}
	}
	Sprite.prototype.destroyEventListeners = function() {
		//remove your listeners before calling this. Helps to free up memory. 
		this._listeners = {};
		this._detachFromMain();
	}
	
	///DRAWING///
	Sprite.prototype.setBBChildren = function() {
		for (var i=0;i<this.children.length;i++) {
			this.children[i].setBoundingBox();
			this.children[i].setBBChildren();
		}
	}
	Sprite.prototype.setBoundingBox = function() {
		var minmax=[null,null,null,null];
		var matrix = this.concatMatrix();
		matrix.translate(-this.x,-this.y);
		for (var i=0;i<this.graphics.corners.length;i++) {
			//0 0 is not necessary here... 
			var g = matrix.transformPoint(i==-1?new Point(0,0):this.graphics.corners[i]);
			
			if (minmax[0]==null || g.x<minmax[0]) {
				minmax[0] = g.x;
			}
			if (minmax[1]==null || g.x>minmax[1]) {
				minmax[1] = g.x;
			}
			if (minmax[2]==null || g.y<minmax[2]) {
				minmax[2] = g.y;
			}
			if (minmax[3]==null || g.y>minmax[3]) {
				minmax[3] = g.y;
			}
		}
		this._bb.unset();
		var sx = this.scaleX;
		var sy = this.scaleY;
		var larger = sx>sy?sx:sy;
		var xtra = this.maxFilter.spacing*2;
		this._bb.set(minmax[0],minmax[1],minmax[2],minmax[3],this.graphics.strokeOn,larger,xtra);
		this.setBBChildren();
	}
	Sprite.prototype.getNativeSize = function(wr,matrix,skipXtra) {
		if (wr == null) {
			wr = new BoundingBox();
			var first = this.transform.getRotation();
			this.transform.rotate(-first);
			var matrix = this.transform;
		} else {
			var matrix = this.transform.multiply(matrix);
		}
		
		var minmax=[null,null,null,null];
		for (var i=-1;i<this.graphics.corners.length;i++) {
			var g = matrix.transformPoint(i==-1?new Point(0,0):this.graphics.corners[i]);
			if (minmax[0]==null || g.x<minmax[0]) {
				minmax[0] = g.x;
			}
			if (minmax[1]==null || g.x>minmax[1]) {
				minmax[1] = g.x;
			}
			if (minmax[2]==null || g.y<minmax[2]) {
				minmax[2] = g.y;
			}
			if (minmax[3]==null || g.y>minmax[3]) {
				minmax[3] = g.y;
			}
		}
		wr.set(minmax[0],minmax[1],minmax[2],minmax[3]);
		for (var k=0;k<this.children.length;k++) {
			this.children[k].getNativeSize(wr,matrix,true);
		}
		if (first) {
			this.transform.rotate(first);
		}
		
		//adjust the nativebb for blur filters, so they don't get truncated by the cache canvas.
		if (!skipXtra) {
			var spacing = this.maxFilter.spacing;
			wr.left-=spacing*this.scaleX;
			wr.right+=spacing*this.scaleX;
			wr.top-=spacing*this.scaleY;
			wr.bottom+=spacing*this.scaleY;
		}
        return ({width:wr.width(),height:wr.height(),nativebb:wr});
	}
	Sprite.prototype.addChild = function(s) {
		if (s.parent) {
			s.parent.removeChild(s,false);
		}
		s.parent = this;
		//recursively assign the stage to all children. 
		this.assignStage(s,this.stage);
		this.children.push(s);
        this.setBoundingBox();
	}
	Sprite.prototype.addChildAt = function(s,k) {
		s.parent = this;
		this.assignStage(s,this.stage);
		this.children.splice(k,0,s);
        this.setBoundingBox();
	}
	Sprite.prototype.getChildAt = function(k) {
		return (this.children[k]);
	}
	Sprite.prototype.removeChild = function(s) {
		var idx = this.children.indexOf(s);
		if (idx==-1) {
			return (false);
		}
        s.cacheAsBitmap = false;
		s.parent = null;
		this.assignStage(s,null);
		this.children.splice(this.children.indexOf(s),1);
		s._detachFromMain();
		delete (s);
        this.setBoundingBox();
	}
	Sprite.prototype.removeChildAt = function(k) {
		this.removeChild(this.children[k]);
	}
	Sprite.prototype.setChildIndex = function(c,i) {
		var ix = this.children.indexOf(c);
		if (ix>-1) {
			this.children.splice(ix,1);
			this.children.splice(i,0,c);
		}
	}
	
	var spritePrototype = Sprite.prototype;
	spritePrototype.__defineGetter__("numChildren", function() {
		return (this.children.length);
	});
	
	spritePrototype.__defineGetter__("x", function() {
		return (this.transform.tx);
	});
	spritePrototype.__defineSetter__("x", function(k) {
		if (isNaN(k)) {
			return (false);
		}
		this.transform.tx = Number(k);
		this.setBoundingBox();
		this.invalid = true;
	});
	
	spritePrototype.__defineGetter__("y", function() {
		return (this.transform.ty);
	});
	spritePrototype.__defineSetter__("y", function(k) {
		if (isNaN(k)) {
			return (false);
		}
		this.transform.ty = Number(k);
		this.setBoundingBox();
		this.invalid = true;
	});
	
	spritePrototype.__defineGetter__("rotation", function() {
		return (this._rotation);
	});
	spritePrototype.__defineSetter__("rotation", function(k) {
		this.transform.rotate(k-this.transform.getRotation());
		this._rotation = k;
		this.setBoundingBox();
		this.invalid = true;
	});
	
	spritePrototype.__defineGetter__("scaleX", function() {
		return (this.transform.getScaleX());
	});
	spritePrototype.__defineSetter__("scaleX", function(sx) {
		this.transform.innerScale(sx,1);
		this.setBoundingBox();
		this.invalid = true;
	});
	
	spritePrototype.__defineGetter__("scaleY", function() {
		return (this.transform.getScaleY());
	});
	spritePrototype.__defineSetter__("scaleY", function(sy) {
		this.transform.innerScale(1,sy);
		this.setBoundingBox();
		this.invalid = true;
	});
	
	spritePrototype.__defineGetter__("width", function() {
		return (this._cachedWidth?this._cachedWidth:this.getNativeSize(null,null,true).width);
	});
	spritePrototype.__defineSetter__("width", function(w) {
		if (this.width>1) {
			this.scaleX = w/this.width;
		}
	});
	spritePrototype.__defineGetter__("filterWidth", function() {
		return (this.getNativeSize().width);
	});
	
	spritePrototype.__defineGetter__("height", function() {
		return (this._cachedHeight?this._cachedHeight:this.getNativeSize(null,null,true).height);
	});
	spritePrototype.__defineSetter__("height", function(h) {
		if (this.height>1) {
			this.scaleY = h/this.height;
		}
	});
	spritePrototype.__defineGetter__("filterHeight", function() {
		return (this.getNativeSize().height);
	});
	
	spritePrototype.__defineGetter__("clippingSprite", function () {
		return (this._clippingSprite);
	});
	spritePrototype.__defineSetter__("clippingSprite", function (sprite) {
		if (sprite != null) {
			this._clippingSprite = sprite;
			this._clippingSprite._isClip = true;
			this.addChild(sprite);
		} else if (this._clippingSprite) {
			this._clippingSprite._isClip = false;
			this.removeChild(this._clippingSprite);
			this._clippingSprite = null;
		}
	});
	spritePrototype.__defineGetter__("_clipsParentUp", function () {
		var n = this;
		while (n.parent) {
			if (n.parent && n.parent.clippingSprite == n) {
				return (true);
			}
			n = n.parent;
		}
		return (false);
	});
    spritePrototype.__defineGetter__("cacheAsBitmap", function () {
        return (this._cacheAsBitmap);
    });
    spritePrototype.__defineSetter__("cacheAsBitmap", function (c) {
        if (c) {
            this.cachedByUser = true;
            this.graphics._cacheAsBitmap();
        } else {
            this.cachedByUser = false;
            this.graphics._uncache();
			this.filters = this.filters;
        }
    });
    spritePrototype.__defineGetter__("filters", function () {
        return (this._filters);
    });
    spritePrototype.__defineSetter__("filters", function (f) {
		if (f.length==1 && f[0].meaningless) f=[];
        if (f.length>0) {
            this._filters = f;
			var postdraw = false;
			for (var k=0;k<f.length;k++) {
				if (!f[k].predraw) {
					postdraw = true;
					break;
				}
			}
			this.invalid = true;
			this.setBoundingBox();
			this.graphics._uncache();
			if (this.cachedByUser) {
				this.graphics._cacheAsBitmap();
			} else if (postdraw) {
				this.graphics._cacheOnRedraw = true;
			}
        } else {
            this._filters = [];
			this.graphics._uncache();
            if (this.cachedByUser) {
				this.graphics._cacheAsBitmap();
			}
        }
    });
	spritePrototype.__defineGetter__("maxFilter", function () {
		var maxFilter = {spacing:0,padding:0};
		for (var f=0;f<this._filters.length;f++) {
			if (this._filters[f].spacing>maxFilter.spacing) maxFilter.spacing += this._filters[f].spacing;
			if (this._filters[f].padding>maxFilter.padding) maxFilter.padding = this._filters[f].padding;
		}
		return (maxFilter);
	});
	spritePrototype.__defineGetter__("mouseX", function () {
		if (!this.stage) return (null);
        return (this.globalToLocal(new Point(this.stage.display._mouse.x,0)).x);
    });
	spritePrototype.__defineGetter__("mouseY", function () {
		if (!this.stage) return (null);
        return (this.globalToLocal(new Point(0,this.stage.display._mouse.y)).y);
    });
	
	Sprite.prototype.localToGlobal = function(point) {
		return (this.concatMatrix().transformPoint(point));
	}
	Sprite.prototype.globalToLocal = function(point) {
		return (this.concatMatrix().inverse().transformPoint(point));
	}
	
	Sprite.prototype.concatMatrix = function(s,m) {
		if (s == null) {
			s = this;
			var m = new Matrix(this.transform.a,this.transform.b,this.transform.c,this.transform.d,this.transform.tx,this.transform.ty);
		}
		while (s = s.parent) {
			m = m.multiply(s.transform);
			this.concatMatrix(s,m);
		}	
		return (m);
	}
	Sprite.prototype._intersectsBounds = function(s) {
		var ab = deriveBounds(this,false,0,0,false);
		var sb = deriveBounds(s,false,0,0,false);
		if (ab.bottom<sb.top) return (false);
		if (ab.top>sb.bottom) return (false);
		if (ab.right<sb.left) return (false);
		if (ab.left>sb.right) return (false);
		return (true);
	}
	Sprite.prototype._pointInBounds = function(point) {
		//takes a GLOBAL point and checks it against the bounds here. 
        //if (this == this.stage) return (point.x<this.stage.stageWidth&&point.y<this.stage.stageHeight);
		var ab = deriveBounds(this,false,0,0,false);
		if (ab.bottom<point.y) return (false);
		if (ab.top>point.y) return (false);
		if (ab.right<point.x) return (false);
		if (ab.left>point.x) return (false);
		return (true);
	}
	Sprite.prototype._pointInGraphics = function(point) {
		//check the hidden canvas against a global point to see whether the sprite has drawn into that area.
		if (!this.stage) return (false);
		var hc = this.stage.display.hiddenContext;
		hc.fillStyle = "#FFFFFF";
		hc.fillRect(0,0,this.stage.stageWidth,this.stage.stageHeight);
		this.graphics._drawToHidden();
		var hdat = hc.getImageData(point.x,point.y,1,1);
		var red = hdat.data[0];
		return (red != 255);
	}
	spritePrototype.__defineGetter__("hasParentMouseListeners", function() {
		if (this._listeners["mouseUp"] || this._listeners["mouseDown"] || this._listeners["click"] || this._listeners["mouseOver"] || this._listeners["mouseOut"] || this._listeners["mouseMoved"]) return (true);
		var e = this;
		while (e = e.parent) {
			if (e._listeners["mouseUp"] || e._listeners["mouseDown"] || e._listeners["click"] || e._listeners["mouseOver"] || this._listeners["mouseOut"] || e._listeners["mouseMoved"]) return (true);
		}
	});
	
	function _colorToStyle(c,a) {
		if (a==1) {
			return (c);
		} else {
			var r = parseInt(c.substring(1,3),16);
			var g = parseInt(c.substring(3,5),16);
			var b = parseInt(c.substring(5,7),16);
	
			return ('rgba('+r+','+g+','+b+','+a+')');
		}
	}
    
    TextField.inherits(Sprite);
    function TextField(display) {
        this._width = 0;
        this._height = 0;
        this._text = "";
        this._textFormat;
        this._multiline = false;
        TextField.supercall(this);
		this.cacheAsBitmap = true;
    }
    TextField.prototype._redraw = function () {
        if (!this._textFormat) this._textFormat = new TextFormat("Arial",12,"#333333","left");
        this.graphics.clear();	
        if (this._text.indexOf("\n")>-1) this._multiline=true; //canvas chokes on line breaks anyway. 
        if (!this._multiline) {
			var lw = 0;
			var mvrt = 0;
			if (this.width>0) {
				lw = this.graphics.drawTextLine(0,0,this._text,this._textFormat.font,this._textFormat.size,this._textFormat.color,false,false,true);
				if (this._textFormat.align=="center") {
					mvrt = (this.width/2)-(lw/2);
				} else if (this._textFormat.align=="right") {
					mvrt = this.width-lw-this._textFormat.size/2;
				}
			}
            this.graphics.drawTextLine(mvrt,0,this._text,this._textFormat.font,this._textFormat.size,this._textFormat.color);
        } else {
            var ta = this._text.split('\n');
            for (var ll=0;ll<ta.length;ll++) {
                var linetex = ta[ll];
                var leftover = this.graphics.drawTextLine(0,ll*this._textFormat.size*_lineHeightMultiplier,linetex,this._textFormat.font,this._textFormat.size,this._textFormat.color,true); //returns a number of characters. 
                if (leftover>0) {
                    var breaker = linetex.substring(0,linetex.length-leftover).lastIndexOf(" ")+1;
                    if (breaker<=3) {
                        breaker = linetex.length-leftover;
                        if (linetex.length-leftover<=2) break;
                    } 
                    if (ta[ll+1]==null) {
                        ta[ll+1] = "";
                    } else {
                        ta.splice(ll+1,0,"");
                    }
                    ta[ll+1] = linetex.substring(breaker,linetex.length)+ta[ll+1];
                    linetex = ta[ll].substring(0,breaker);
                    if (this._textFormat.align=="justify") {
                        var spacecount = linetex.split(" ").length;
                        var lastspace = linetex.lastIndexOf(" ");
                        lastspace = linetex.substring(0,lastspace).lastIndexOf(" ");
                        var pref = [0];
                        while (lastspace>0) {
                            lastspace = linetex.lastIndexOf(" ");
                            lastspace = linetex.substring(0,lastspace).lastIndexOf(" ");
                            var spaceplace = Math.floor(Math.random()*lastspace-1);
                            if (pref[spaceplace]>_arraySum(pref)/(spacecount-1)) continue;
                            pref[spaceplace]?pref[spaceplace]++:pref[spaceplace]=1;
                            var ins = linetex.indexOf(" ",spaceplace+1);
                            var hl = linetex;
                            linetex = linetex.substring(0,ins)+" "+linetex.substring(ins,linetex.length);
                            if (this.graphics.drawTextLine(0,ll*this._textFormat.size*_lineHeightMultiplier,linetex,this._textFormat.font,this._textFormat.size,this._textFormat.color,true,true)>0)  break; //characters over the line
                        }
                    }
                }
var lw = this.graphics.drawTextLine(0,ll*this._textFormat.size*_lineHeightMultiplier,linetex,this._textFormat.font,this._textFormat.size,this._textFormat.color,false); //final line width returned.
                if (this.width>0) {
					var mvrt = 0;
                   	if (this._textFormat.align=="center") {
						mvrt = (this.width/2)-(lw/2);
					} else if (this._textFormat.align=="right") {
						mvrt = this.width-lw-this._textFormat.size/2;
					}
					this.graphics.list[this.graphics.list.length-1].v[0]+=mvrt;
					for (var c=0;c<4;c++) {
						//center by moving the right corners of the bbox over. 
						if (c%2!=0) this.graphics.corners[c].x+=this._textFormat.size/2;
	                }
				}
            }
        }
		
		//Don't re-cache this if there are filters on it. Conversely, don't cache from assigning filters in Sprite 
		//if _cacheOnRedraw is already set. You can only cache once per frame redraw. 
		if ((this.filters.length>0 && this.graphics._cacheOnRedraw) || this.cachedByUser) {
			this.graphics._cacheOnRedraw = true;
		}
		
    }
    TextField.prototype.setTextFormat = function (tf) {
        this._textFormat = tf;
        this._redraw();
    }
    var textFieldPrototype = TextField.prototype;
    textFieldPrototype.__defineGetter__("width", function () {
        return (this._width?this._width:this.textWidth);
    });
    textFieldPrototype.__defineSetter__("width", function (w) {
        this._width = w;
        this._redraw();
    });
    textFieldPrototype.__defineGetter__("height", function () {
        return (this._height?this._height:this.textHeight);
    });
    textFieldPrototype.__defineSetter__("height", function (h) {
        this._height = h;
        this._redraw();
    });
    textFieldPrototype.__defineGetter__("text", function () {
        return (this._text);
    });
    textFieldPrototype.__defineSetter__("text", function (t) {
        this._text = t;
        this._redraw();
    });
    textFieldPrototype.__defineSetter__("multiline", function (m) {
        this._multiline = m;
        this._redraw();
    });
    textFieldPrototype.__defineGetter__("textWidth", function () {
		//filterWidth returns a new native w/h, but the blank bounding box it creates comes with some padding.
		//ignore it if there is no real width yet. 
		var norm = this._bb.right-this._bb.left;
		if (!norm) return (0);
        return (this.filterWidth>norm?this.filterWidth:norm);
    });
    textFieldPrototype.__defineGetter__("textHeight", function () {
		var norm = this._bb.bottom-this._bb.top;
		if (!norm) return (0);
        return (this.filterHeight<norm?this.filterHeight:norm);
    });
    
    function TextFormat(font,size,color,align) {
        this.font = font;
        this.size = size;
        this.color = color?_colorToStyle(color,1):_colorToStyle("#000000",1);
        this.align = align?align:"left";
    }
	
    var Filter = function() {
    }
    Filter.prototype.predraw = true;
    Filter.prototype.spacing = 0;
	Filter.prototype.padding = 0;
	Filter.prototype.meaningless = false; //flag to ignore the filter based on inputs that do nothing. 
    Filter.prototype.convolute = function(ctx, pixels, weights, opaque) {
		var side = Math.round(Math.sqrt(weights.length));
		var halfSide = Math.floor(side/2);
		var src = pixels.data;
		var sw = pixels.width;
		var sh = pixels.height;
		var w = sw;
		var h = sh;
		var output = ctx.createImageData(w, h);
		var dst = output.data;
		var alphaFac = opaque ? 1 : 0;
		for (var y=0; y<h; y++) {
			for (var x=0; x<w; x++) {
				var sy = y;
				var sx = x;
				var dstOff = (y*w+x)*4;
				var r=0, g=0, b=0, a=0;
				for (var cy=0; cy<side; cy++) {
					for (var cx=0; cx<side; cx++) {
						var scy = sy + cy - halfSide;
						var scx = sx + cx - halfSide;
						if (scy >= 0 && scy < sh && scx >= 0 && scx < sw) {
							var srcOff = (scy*sw+scx)*4;
							var wt = weights[cy*side+cx];
							r += src[srcOff] * wt;
							g += src[srcOff+1] * wt;
							b += src[srcOff+2] * wt;
							a += src[srcOff+3] * wt;
						}
					}
				}
				dst[dstOff] = r;
				dst[dstOff+1] = g;
				dst[dstOff+2] = b;
				dst[dstOff+3] = a + alphaFac*(255-a);
			}
		}
        return output;
    }
    
    DropShadowFilter.inherits(Filter);
	function DropShadowFilter(ox,oy,bluramt,color,alpha) {
        DropShadowFilter.supercall(this);
		this.ox = ox;
		this.oy = oy;
		this.bluramt = bluramt;
		alpha = alpha?alpha:1;
		this.color = color?_colorToStyle(color,alpha):_colorToStyle("#000000",1);
		this.spacing = (ox>oy?ox:oy)+bluramt;
		this.padding = this.spacing/2; //(ox>oy?ox:oy)/2;
	}
    DropShadowFilter.prototype._act = function(can,pixels) {
        var ctx = can.getContext("2d");
        ctx.shadowOffsetX = this.ox;
        ctx.shadowOffsetY = this.oy;
        ctx.shadowBlur = this.bluramt;
        ctx.shadowColor = this.color;
    }
    
	
	BrightnessFilter.inherits(Filter);
    function BrightnessFilter(v) {
        BrightnessFilter.supercall(this);
        this.predraw = false; //do this filter before drawing the graphics.
        this.v = v;
	}
    BrightnessFilter.prototype._act = function(can,pixels) {
		var cheapAntiAliasing = 0;
        if (!pixels) {
            var width = parseInt(can.getAttribute("width"));
            var height = parseInt(can.getAttribute("height"));
        } else {
           	var width = pixels.width;
           	var height = pixels.height;
        }
		var llen = width*height*4;
		for (var c=0;c<llen;c++) {
			if (!((c+1)%4)) continue
			pixels.data[c]+=(pixels.data[(Math.ceil(c/4)*4)-1]>cheapAntiAliasing)?this.v:0;
		}
		return (pixels);
	}
	
	ColorFilter.inherits(Filter);
	function ColorFilter(r,g,b,a) {
		ColorFilter.supercall(this);
		this.predraw = false;
		this.a = [r,g,b,a];
	}
	ColorFilter.prototype._act = function(can,pixels) {
		if (!pixels) {
            var width = parseInt(can.getAttribute("width"));
            var height = parseInt(can.getAttribute("height"));
        } else {
           	var width = pixels.width;
           	var height = pixels.height;
        }
		var llen = width*height*4;
		for (var c=0;c<llen;c++) {
			pixels.data[c]+=this.a[c%4];
		}
		return (pixels);
	}
	
	ConvolutionFilter.inherits(Filter);
	function ConvolutionFilter(c) {
		ConvolutionFilter.supercall(this);
		this.predraw = false;
		this.c = c;
	}
	ConvolutionFilter.prototype._act = function(can,pixels) {
		if (!pixels) {
            var width = parseInt(can.getAttribute("width"));
            var height = parseInt(can.getAttribute("height"));
        } else {
           	var width = pixels.width;
           	var height = pixels.height;
        }
		return (this.convolute(can.getContext('2d'),pixels,this.c,true));
	}
    
	BlurFilter.inherits(Filter);
    function BlurFilter(radius,iterations) {
        BlurFilter.supercall(this);
		if (radius <= 0) {
			radius = 0;
			this.meaningless = true;
		}
        this.predraw = false; //do this filter before drawing the graphics.
        this.radius = Math.round(radius);
		this.iterations = iterations?iterations:1;
        this.spacing = this.padding = 2+radius/2; //padded within the bounding box / bitmap cache canvas. 
	}
    BlurFilter.prototype._act = function(can,pixels) {
		//Thanks to Mario Klingemann for his open source StackBlur algorithm -- http://www.quasimondo.com/
        if (!pixels) {
            var width = parseInt(can.getAttribute("width"));
            var height = parseInt(can.getAttribute("height"));
        } else {
           	var width = pixels.width;
           	var height = pixels.height;
        }
		
		var mul_table = [ 1,171,205,293,57,373,79,137,241,27,391,357,41,19,283,265,497,469,443,421,25,191,365,349,335,161,155,149,9,278,269,261,505,245,475,231,449,437,213,415,405,395,193,377,369,361,353,345,169,331,325,319,313,307,301,37,145,285,281,69,271,267,263,259,509,501,493,243,479,118,465,459,113,446,55,435,429,423,209,413,51,403,199,393,97,3,379,375,371,367,363,359,355,351,347,43,85,337,333,165,327,323,5,317,157,311,77,305,303,75,297,294,73,289,287,71,141,279,277,275,68,135,67,133,33,262,260,129,511,507,503,499,495,491,61,121,481,477,237,235,467,232,115,457,227,451,7,445,221,439,218,433,215,427,425,211,419,417,207,411,409,203,202,401,399,396,197,49,389,387,385,383,95,189,47,187,93,185,23,183,91,181,45,179,89,177,11,175,87,173,345,343,341,339,337,21,167,83,331,329,327,163,81,323,321,319,159,79,315,313,39,155,309,307,153,305,303,151,75,299,149,37,295,147,73,291,145,289,287,143,285,71,141,281,35,279,139,69,275,137,273,17,271,135,269,267,133,265,33,263,131,261,130,259,129,257,1];
        
   
		var shg_table = [0,9,10,11,9,12,10,11,12,9,13,13,10,9,13,13,14,14,14,14,10,13,14,14,14,13,13,13,9,14,14,14,15,14,15,14,15,15,14,15,15,15,14,15,15,15,15,15,14,15,15,15,15,15,15,12,14,15,15,13,15,15,15,15,16,16,16,15,16,14,16,16,14,16,13,16,16,16,15,16,13,16,15,16,14,9,16,16,16,16,16,16,16,16,16,13,14,16,16,15,16,16,10,16,15,16,14,16,16,14,16,16,14,16,16,14,15,16,16,16,14,15,14,15,13,16,16,15,17,17,17,17,17,17,14,15,17,17,16,16,17,16,15,17,16,17,11,17,16,17,16,17,16,17,17,16,17,17,16,17,17,16,16,17,17,17,16,14,17,17,17,17,15,16,14,16,15,16,13,16,15,16,14,16,15,16,12,16,15,16,17,17,17,17,17,13,16,15,17,17,17,16,15,17,17,17,16,15,17,17,14,16,17,17,16,17,17,16,15,17,16,14,17,16,15,17,16,17,17,16,17,15,16,17,14,17,16,15,17,16,17,13,17,16,17,17,16,17,14,17,16,17,16,17,16,17,9];
		
		var dat = pixels.data;
		var radius = this.radius;
		var iterations = this.iterations;
			
		var x, y, i, p, yp, yi, yw, r_sum, g_sum, b_sum, a_sum, 
		r_out_sum, g_out_sum, b_out_sum, a_out_sum,
		r_in_sum, g_in_sum, b_in_sum, a_in_sum, 
		pr, pg, pb, pa, rbs;
				
		var div = radius + radius + 1;
		var w4 = width << 2;
		var widthMinus1  = width - 1;
		var heightMinus1 = height - 1;
		var radiusPlus1  = radius + 1;
		
		var stackStart = new BlurStack();
		
		var stack = stackStart;
		for ( i = 1; i < div; i++ )
		{
			stack = stack.next = new BlurStack();
			if ( i == radiusPlus1 ) var stackEnd = stack;
		}
		stack.next = stackStart;
		var stackIn = null;
		
		
		
		var mul_sum = mul_table[radius];
		var shg_sum = shg_table[radius];
		while ( iterations-- > 0 ) {
			yw = yi = 0;
			for ( y = height; --y > -1; )
			{
				r_sum = radiusPlus1 * ( pr = dat[yi] );
				g_sum = radiusPlus1 * ( pg = dat[yi+1] );
				b_sum = radiusPlus1 * ( pb = dat[yi+2] );
				a_sum = radiusPlus1 * ( pa = dat[yi+3] );
				
				stack = stackStart;
				
				for( i = radiusPlus1; --i > -1; )
				{
					stack.r = pr;
					stack.g = pg;
					stack.b = pb;
					stack.a = pa;
					stack = stack.next;
				}
				
				for( i = 1; i < radiusPlus1; i++ )
				{
					p = yi + (( widthMinus1 < i ? widthMinus1 : i ) << 2 );
					r_sum += ( stack.r = dat[p]);
					g_sum += ( stack.g = dat[p+1]);
					b_sum += ( stack.b = dat[p+2]);
					a_sum += ( stack.a = dat[p+3]);
					
					stack = stack.next;
				}
				
				stackIn = stackStart;
				for ( x = 0; x < width; x++ )
				{
					dat[yi++] = (r_sum * mul_sum) >>> shg_sum;
					dat[yi++] = (g_sum * mul_sum) >>> shg_sum;
					dat[yi++] = (b_sum * mul_sum) >>> shg_sum;
					dat[yi++] = (a_sum * mul_sum) >>> shg_sum;
					
					p =  ( yw + ( ( p = x + radius + 1 ) < widthMinus1 ? p : widthMinus1 ) ) << 2;
					
					r_sum -= stackIn.r - ( stackIn.r = dat[p]);
					g_sum -= stackIn.g - ( stackIn.g = dat[p+1]);
					b_sum -= stackIn.b - ( stackIn.b = dat[p+2]);
					a_sum -= stackIn.a - ( stackIn.a = dat[p+3]);
					
					stackIn = stackIn.next;
					
				}
				yw += width;
			}
	
			
			for ( x = 0; x < width; x++ )
			{
				yi = x << 2;
				
				r_sum = radiusPlus1 * ( pr = dat[yi]);
				g_sum = radiusPlus1 * ( pg = dat[yi+1]);
				b_sum = radiusPlus1 * ( pb = dat[yi+2]);
				a_sum = radiusPlus1 * ( pa = dat[yi+3]);
				
				stack = stackStart;
				
				for( i = 0; i < radiusPlus1; i++ )
				{
					stack.r = pr;
					stack.g = pg;
					stack.b = pb;
					stack.a = pa;
					stack = stack.next;
				}
				
				yp = width;
				
				for( i = 1; i <= radius; i++ )
				{
					yi = ( yp + x ) << 2;
					
					r_sum += ( stack.r = dat[yi]);
					g_sum += ( stack.g = dat[yi+1]);
					b_sum += ( stack.b = dat[yi+2]);
					a_sum += ( stack.a = dat[yi+3]);
				   
					stack = stack.next;
				
					if( i < heightMinus1 )
					{
						yp += width;
					}
				}
				
				yi = x;
				stackIn = stackStart;
				for ( y = 0; y < height; y++ )
				{
					p = yi << 2;
					dat[p+3] = pa =(a_sum * mul_sum) >>> shg_sum;
					if ( pa > 0 )
					{
						pa = 255 / pa;
						dat[p]   = ((r_sum * mul_sum) >>> shg_sum ) * pa; 
						dat[p+1] = ((g_sum * mul_sum) >>> shg_sum ) * pa;
						dat[p+2] = ((b_sum * mul_sum) >>> shg_sum ) * pa;
					} else {
						dat[p] = dat[p+1] = dat[p+2] = 0
					}
					
					p = ( x + (( ( p = y + radiusPlus1) < heightMinus1 ? p : heightMinus1 ) * width )) << 2;
					
					r_sum -= stackIn.r - ( stackIn.r = dat[p]);
					g_sum -= stackIn.g - ( stackIn.g = dat[p+1]);
					b_sum -= stackIn.b - ( stackIn.b = dat[p+2]);
					a_sum -= stackIn.a - ( stackIn.a = dat[p+3]);
				   
					stackIn = stackIn.next;
					
					yi += width;
				}
			}
		}
		
		pixels.data = dat;
        return (pixels);
    }
	function BlurStack() {
		this.r = 0;
		this.g = 0;
		this.b = 0;
		this.a = 0;
		this.next = null;
	}
    
	function Tween(sprite,prop,easing,from,to,seconds,display) {
		if (!sprite.stage && !display) {
			//you must pass a display parameter unless it's a visual sprite on the stage. 
			return (false);
		} else if (sprite.stage) {
			this.framerate = sprite.stage.display.framerate;
		} else {
			this.framerate = display.framerate;
		}
		this.sprite = sprite;
		this._listeners = {};
		this.prop = prop;
		this.easing = easing;
		this.from = from?from:0;
		this.to = to;
		this.seconds = seconds;
		this.ticks = (1000/this.framerate)*seconds;
		this.iterator = 0;
		this.running = true;
		this.pos = from;
		this.doFunction = this.doTween.bind(this);
		this.interval = setInterval(this.doFunction,1000/this.framerate);
	}
	Tween.prototype.doTween = function() {
		this.iterator++;
		if (this.iterator >= this.ticks) {
			this.dispatchEvent(new Event("motionFinished",this,this));
			this.stop();
		}
		this.pos = this.easing(this.from,this.to,this.iterator,this.ticks,this.pos);
		this.sprite[this.prop] = this.pos;
	}
	Tween.prototype.stop = function() {
		if (this.running) {
			clearInterval(this.interval);
		}
		this.running = false;
		delete (this.doFunction);
	}
	function EaseNone(f,t,i,tot,p) {
		return (f-((f-t)*(i/tot)));
	}
	function ei(r,p) {
		return Math.pow(r,p);
	}
	function eo(r,p) {
		return 1-ei(1-r,p);
	}
	function eio(r,p) {
		if (r<=.5) return (ei(2*r,p)/2);
		return (eo(2*(r-.5),p)/2+.5);
	}
	function EaseIn(f,t,i,tot,p) {
		return (f-ei(i/tot,2)*(f-t));
	}
	function EaseOut(f,t,i,tot,p) {
		return (f-eo(i/tot,2)*(f-t));
	}
	function EaseInOut(f,t,i,tot,p) {
		return (f-eio(i/tot,2)*(f-t));
	}
	Tween.prototype.dispatchEvent = function(evt) {
		for (var i in _MainEventList) {
			for (var k in _MainEventList[i]) {
				if (i == evt.type && _MainEventList[i][k].sprite == this) {
					evt.currentTarget = this;
					_MainEventList[i][k].fun(evt);
					return (true);
				}
			}
		}
	}
	Tween.prototype._addMainListener = function(type,fun) {
		if (_MainEventList[type]) {
			_MainEventList[type].push({sprite:this,fun:fun});
		} else {
			_MainEventList[type] = [{sprite:this,fun:fun}];
		}
	}
	Tween.prototype._removeMainListener = function(type,fun) {
		for (var k in _MainEventList[type]) {
			if (_MainEventList[type][k].sprite == this && (_MainEventList[type][k].fun == fun || _MainEventList[type][k].fun._method==fun)) {
				var mlist = _MainEventList[type].splice(k,1);
				mlist = null;
				delete (mlist);
				if (_MainEventList[type].length<1) {
					delete _MainEventList[type];
				}
			}
		}
	}
	Tween.prototype._detachFromMain = function() {
		//remove all listeners associated with this sprite from the main event list... 
		for (var j in _MainEventList) {
			for (var k in _MainEventList[j]) {
				if (_MainEventList[j][k].sprite == this) {
					var mlist = _MainEventList[j].splice(k,1);
					mlist = null;
					delete mlist;
				}
			}
		}
	}
	Tween.prototype.addEventListener = function(type,fun) {
		if (this._listeners[type]) {
			if (this.hasEventListener(type,fun)) return (false);
			this._listeners[type].push(fun);
		} else {
			this._listeners[type] = [fun];
		}
		this._addMainListener(type,fun);
	}
	Tween.prototype.hasEventListener = function(type,fun,pos) {
		//pos flag returns the index of the listener on success (used for removal). 
		if (!this._listeners[type]) return (false);
		var len = this._listeners[type].length;
		for (var i=0;i<len;i++) {
			if (this._listeners[type][i] == fun || this._listeners[type][i]._method==fun) return (pos?i:true);
		}
		return (false);
	}
	Tween.prototype.removeEventListener = function(type,fun) {
		var i = this.hasEventListener(type,fun,true);
		if (i !== false) {
			var list = this._listeners[type].splice(i,1);
			list = null;
			delete list;
			this._removeMainListener(type,fun);
			if (this._listeners[type].length<1) {
				this._listeners[type] = null;
				delete this._listeners[type];
			}
		}
	}
	Tween.prototype.destroyEventListeners = function() {
		this._listeners = {};
		this._detachFromMain();
	}
	
	

	return {
		//public methods//
		trace: trace, 
		traceOnce: traceOnce, 
		StrikeDisplay: StrikeDisplay, 
		Point: Point, 
		Matrix: Matrix, 
		Sprite: Sprite, 
        TextField: TextField, 
        TextFormat: TextFormat, 
		Graphics: Graphics, 
		Tween: Tween, 
		EaseIn: EaseIn, 
		EaseOut: EaseOut, 
		EaseInOut: EaseInOut, 
		EaseNone: EaseNone, 
		DropShadowFilter: DropShadowFilter, 
        BrightnessFilter: BrightnessFilter, 
		ColorFilter: ColorFilter, 
		ConvolutionFilter: ConvolutionFilter, 
		BlurFilter: BlurFilter, 
		Event: Event
	} //end wrap Strike object
}() //end and invoke anon function

//top-level commands; you can disable these and preface everything with Strike. if you prefer.
var trace = Strike.trace;
var traceOnce = Strike.traceOnce;
var StrikeDisplay = Strike.StrikeDisplay;
var Point = Strike.Point;
var Matrix = Strike.Matrix;
var Sprite = Strike.Sprite;
var Graphics = Strike.Graphics;
var Tween = Strike.Tween;
var EaseIn = Strike.EaseIn;
var EaseOut = Strike.EaseOut;
var EaseInOut = Strike.EaseInOut;
var EaseNone = Strike.EaseNone;
var DropShadowFilter = Strike.DropShadowFilter;
var BrightnessFilter = Strike.BrightnessFilter;
var ColorFilter = Strike.ColorFilter;
var ConvolutionFilter = Strike.ConvolutionFilter;
var BlurFilter = Strike.BlurFilter; 
var Event = Strike.Event;
var TextField = Strike.TextField;
var TextFormat = Strike.TextFormat;

