//MooCanvas, My Object Oriented Canvas Element. Copyright (c) 2008 Olmo Maldonado, <http://ibolmo.com/>, MIT Style License.
if (Browser.Engine.trident){	document.createStyleSheet().cssText = 		'canvas {text-align:left;display:inline-block;}' +		'canvas div, canvas div * {position:absolute;overflow:hidden}' +		'canvas div * {width:10px;height:10px;}' +		'v\\:group,v\\:image,v\\:fill,v\\:stroke,v\\:shape{behavior:url(#default#VML)}';}Element.Constructors.canvas = function(props){	return new Canvas(props);};
$.element = function(el, nocash){	$uid(el);	if (!nocash && !el.$family && !(/^object|embed$/i).test(el.tagName)){		var proto = Element.Prototype;		for (var p in proto) el[p] = proto[p];	};	return el;};$.Element = $.element;$.element = function(el, nocash){    if ((/^canvas$/i).test(el.tagName) && !el.getContext) {    	var clone = new Canvas({ id: el.id, width: el.width, height: el.height });    	if (el.parentNode) el.parentNode.replaceChild(clone, el);    	el = clone;    } else {    	el = $.Element(el, nocash);    }    return el;};var Canvas = new Native({        name: 'Canvas',	initialize: function(){		var params = Array.link(arguments, {properties: Object.type, element: Element.type });		var props = $extend({width: 300, height: 150}, params.properties);		var el = (params.element || $.Element(document.createElement('canvas'))).set(props);		if (el.getContext) return el;		el.attachEvent('onpropertychange', Canvas.changeproperty);		el.attachEvent('onresize', Canvas.resize);		el.getContext = function(){			return this.context = this.context || new CanvasRenderingContext2D(el);		};		return el.setStyles({			width: props.width,			height: props.height		});	}});Canvas.changeproperty = function(e){	var property = e.propertyName;	if (property == 'width' || property == 'height'){		e = e.srcElement;		e.style[property] = e[property];		e.getContext().clearRect();	}};Canvas.resize = function(e){	e = e.srcElement;	var efC = e.firstChild;	if (efC){		efC.style.width = e.width;		efC.style.height = e.height;	}};var CanvasRenderingContext2D = new Class({	initialize: function(el){		this.element = new Element('div').setStyles({			width: el.clientWidth,			height: el.clientHeight		}).inject(el);		this.m = [			[1, 0, 0],			[0, 1, 0],			[0, 0, 1]		];		this.l = 0;		this.rot = 0;		this.state = [];		this.path = [];		this.Z = 10;		this.Z2 = this.Z / 2;		this.miterLimit = this.Z * 1;	},    	arcScaleX: 1,	arcScaleY: 1,	currentX: 0,	currentY: 0,	lineWidth: 1,	strokeStyle: '#000',	fillStyle: '#fff',	globalAlpha: 1,	globalCompositeOperation: 'source-over',	lineCap: 'butt',	lineJoin: 'miter',	shadowBlur: 0,	shadowColor: '#000',	shadowOffsetX: 0,	shadowOffsetY: 0,	getCoords: function(x,y){		var m = this.m, Z = this.Z, Z2 = this.Z2;		return {			x: Z * (x * m[0][0] + y * m[1][0] + m[2][0]) - Z2,			y: Z * (x * m[0][1] + y * m[1][1] + m[2][1]) - Z2,			toString: function(){ return this.x.round() + ',' + this.y.round() }		};	}});

CanvasRenderingContext2D.implement({

	beginPath: function(){
		this.l = 0;
		this.path.length = 0;
	},

	moveTo: function(x, y){
		this.path[this.l++] = 'm';
		this.path[this.l++] = this.getCoords(x, y);
		this.currentX = x;
		this.currentY = y;
	},

	closePath: function(){
		this.path[this.l++] = 'x';
	},

	lineTo: function(x, y){
		this.path[this.l++] = 'l';
		this.path[this.l++] = this.getCoords(x,y);
		this.currentX = x;
		this.currentY = y;
	},

	quadraticCurveTo: function(cpx, cpy, x, y){
		var cx = 2 * cpx,
			cy = 2 * cpy;

		this.bezierCurveTo(
			(cx + this.currentX) / 3,
			(cy + this.currentY) / 3,
			(cx + x) / 3,
			(cy + y) / 3,
			x,
			y
		);
	},

	bezierCurveTo: function(cp0x, cp0y, cp1x, cp1y, x, y){
		this.path[this.l++] = ' c ' + [
			this.getCoords(cp0x, cp0y),
			this.getCoords(cp1x, cp1y),
			this.getCoords(x,y)
		].join(',');

		this.currentX = x;
		this.currentY = y;
	},

	arcTo: Function.empty,

	arc: function(x, y, rad, a0, a1, cw){
		rad *= this.Z;

		var x0 = a0.cos() * rad, y0 = a0.sin() * rad,
			x1 = a1.cos() * rad, y1 = a1.sin() * rad;

		if (x0 == x1 && !cw) x0 += 0.125;
		
        var Z2 = this.Z2,
            c = this.getCoords(x, y),
			aSXr = this.arcScaleX * rad,
			aSYr = this.arcScaleY * rad;
			
		x -= Z2;
		y -= Z2;

		this.path[this.l++] = [
			cw ? 'at ' : 'wa ',
			(c.x - aSXr).round() + ',' + (c.y - aSYr).round(), ' ',
			(c.x + aSXr).round() + ',' + (c.y + aSYr).round(), ' ',
			this.getCoords(x0 + x, y0 + y), ' ',
			this.getCoords(x1 + x, y1 + y),
		].join('');
	},

	rect: function(x, y, w, h){
		this.moveTo(x, y);
		this.lineTo(x + w, y);
		this.lineTo(x + w, y + h);
		this.lineTo(x, y + h);
		this.closePath();
	},

	fill: function(){
		this.stroke(true);
	},

	stroke: function(fill){
		if(!this.path.length) return;

		var size = this.Z * 10,
			fS = this.fillStyle,
			rgb = String.type(fS),
			color = this.processColor(fill && rgb ? fS : this.strokeStyle),
			a = (fill) ?
				['filled="true" stroked="',
				['<v:fill', !rgb ? this.processColorObject(fS) : 'color="' + color.color + '" opacity="' + color.opacity, '"></v:fill>']]
			:
				['strokeweight=' + 0.8 * this.lineWidth * this.m[0][0] + ' filled="',
				['<v:stroke',
					'endcap=', (this.lineCap == 'butt') ? 'flat' : this.lineCap,
					'joinstyle=', this.lineJoin,
					'color=', color.color,
					'opacity="', color.opacity, '" />']];

		this.element.insertAdjacentHTML('beforeEnd', [
			'<v:shape path="', this.path.join(''), '" coordorigin="0 0" coordsize="' + size + ' ' + size + '" ', a[0], 'false">',
				a[1].join(' '),
			'</v:shape>'
		].join(''));

		if(fill && fS.img) this.element.getLast().fill.alignshape = false; // not sure why this has to be called explicitly

		this.beginPath();
	},

	clip: Function.empty,

	isPointInPath: Function.empty,

	processColor: function(col){
		var a = this.globalAlpha;
		if (col.substr(0, 3) == 'rgb'){
			if (col.charAt(3) == "a") a *= col.match(/([\d.]*)\)$/)[1];
			col = col.rgbToHex();
		}
		return {
			color: col,
			opacity: a
		};
	},

	processColorObject: function(obj){
		var ret = '';
		if(obj.addColorStop){
			var oc0 = obj.col0, oc1 = obj.col1, stops = '';
			if(obj.stops) for (var i = 0, j = obj.stops.length; i < j; i++) stops += (100 * obj.stops[i][0]).round() + '% ' + obj.stops[i][1];
			ret += ((obj.r0) ?
				'type=gradientradial focusposition="0.2,0.2" focussize="0.2,0.2"'
			:
				'type=gradient method=linear focus=0 angle=' + 180 * (1 + obj.angle / Math.PI) + ' '
			) + [
				'color="' + oc0.color,
				'opacity="' + oc0.opacity * 100 + '%',
				'color2="' + oc1.color,
				'o:opacity2="' + oc1.opacity * 100 + '%',
				'colors="' + stops
			].join('" ');
		}

		return (obj.img) ?  'type="tile" src="' + obj.img.src : ret;
	}
	
});

CanvasRenderingContext2D.implement({

	clearRect: function(x, y, w, h){
	    this.element.innerHTML = '';
		this.m = [
			[1, 0 ,0],
			[0, 1, 0],
			[0, 0, 1]
		];
	},

	fillRect: function(x, y, w, h){
		this.rect(x, y, w, h);
		this.fill();
	},

	strokeRect: function(x, y, w, h){
		this.rect(x, y, w, h);
		this.stroke();
	}

});

