/**
 *	RoundedImages - V1.13.090623
 *	---------------------------------------------
 *	(c) 2009 - Lost Boys - http://www.lostboys.nl
 *
 *	Applies rounded edges to <img /> elements.
 *	Supports edges, strokes, shadows and rotation.
 *	Uses canvas for supported browsers, vml for IE.
 */

var RoundedImages = (function(){

	/**
	 *	Rounder 
	 *	--------------------------
	 */

	function Rounder() {
		var isIE = /msie/i.test(navigator.userAgent);
		this.roundImage = isIE? this.roundImageByVML : this.roundImageByCanvas;

		this.regSoft = /soft/i;
		this.regAuto = /auto/i;
		
		if(isIE) {
			this.importNS = '';
			if(!document.documentMode || document.documentMode < 8) {
				// IE6, 7 and 8 in compat mode
				document.namespaces.add("vml", "urn:schemas-microsoft-com:vml"); 
				document.createStyleSheet().addRule('vml\\:*', "behavior: url(#default#VML);");
			} else if(document.documentMode && document.documentMode >= 8) {
				// IE8 in strict mode
				document.namespaces.add("vml","urn:schemas-microsoft-com:vml","#default#VML");
				this.importNS = '<?import namespace="vml" implementation="#default#VML" ?>'; 
			}
		}
	}

	Rounder.prototype = {
		
		// processes images using canvas, if supported
		roundImageByCanvas: function(image, style){
			var canvas = document.createElement('canvas'),
				ctx = canvas.getContext('2d'),
				s = style.shadowOffset, 
				w = image.width, h = image.height;

			var rot = this.getRotation(style.rotation),
				bounds = this.getRotatedBounds(w, h, s, rot),
				borders = this.parseShorthand(style.borderRadius),
				widths = this.parseShorthand(style.borderWidth),
				r = Math.max.apply(Math, borders),
				k = Math.max.apply(Math, widths);
				
			canvas.width = bounds.offsetW;
			canvas.height = bounds.offsetH;
			ctx.strokeStyle = 'rgba(0,0,0,0)';
			ctx.translate(bounds.offsetX, bounds.offsetY);
			ctx.rotate(bounds.angle);

			if(s != 0) {
				ctx.save();
				if(this.regSoft.test(style.shadowStyle)) {
					r = this.regAuto.test(style.shadowRadius)? r : style.shadowRadius,
					this.gradientRect(ctx, s,s, w, h, r, style.shadowColor);
				} else {
					ctx.globalAlpha = style.shadowOpacity; 
					ctx.fillStyle = style.shadowColor;
					this.roundedRect(ctx, s,s, w, h, borders, false);
					ctx.clip(); 
					ctx.fillRect(s,s, w+s, h+s);
				}
				ctx.restore();
			}

			this.roundedRect(ctx, 0, 0, w, h, borders, false);
			ctx.clip();

			try {
				ctx.drawImage(image, 0, 0, w, h);
			} catch (e) {}

			if(k != 0) {
				var closed = Math.min.apply(Math, widths)? true : false;
				ctx.globalAlpha = style.borderOpacity;
				ctx.strokeStyle = style.borderColor;
				ctx.lineWidth = k * 2;
				this.roundedRect(ctx, 0, 0, w, h, borders, widths, closed);
			}

			try {
				var src = canvas.toDataURL();
				var img = new Image();
				img.src = src;
				this.replace(image, img, style.replaced);
			} catch (e) {
				this.replace(image, canvas, style.replaced);
			}
		},

		// processes images using VML for IE, if canvas is not supported
		roundImageByVML: function(image, style){
			var w = image.width, h = image.height, 
				shadow = style.shadowOffset, 
				shape = [this.importNS],
				rot = this.getRotation(style.rotation),
				bounds = this.getRotatedBounds(w, h, shadow, rot),
				borders = this.parseShorthand(style.borderRadius),
				widths = this.parseShorthand(style.borderWidth),
				tw = Math.round(bounds.offsetW),
				th = Math.round(bounds.offsetH);

			var span = document.createElement('span'), css = span.style;

			css.display = 'inline-block';
			css.position = 'relative';
			css.width = tw + 'px';
			css.height = th + 'px';

			var path = this.roundedRect(new Path(), 0, 0, w, h, borders, false);
			shape.push('<vml:group coordsize="',tw,',',th,'" coordorigin="0,0" style="width:',tw,'px;height:',th,'px;position:absolute;left:',bounds.offsetX, ';top:', bounds.offsetY, ';rotation:', rot, '" >');

			if(shadow != 0) {
				var opacity = style.shadowOpacity;
				if(this.regSoft.test(style.shadowStyle)) {
					var r = this.regAuto.test(style.shadowRadius)? Math.max.apply(Math, borders) : style.shadowRadius,
						fx = r/w, fy = r/h, fw = 1-(2*fx), fh = 1-(2*fy), arc = r/(Math.min(w, h));
					shape.push('<vml:roundrect stroked="false" arcsize="', arc, '" style="left:',shadow,'px; top:',shadow,'px; width:',w,'px; height:',h,'px;"><vml:fill type="GradientRadial" opacity="0" opacity2="',0,'" color="black" color2="', style.shadowColor, '" focusposition="',fx,',',fy,'" focussize="',fw,',',fh,'" /></vml:roundrect>');
				} else {
					shape.push('<vml:shape coordsize="',w,',',h,'" filled="true" stroked="true" path="', path,'" coordorigin="0,0" style="width:',w,'px;height:',h,'px;position:absolute;left:', shadow, 'px;top:', shadow, 'px;"><vml:fill color="', style.shadowColor, '" opacity="', opacity, '" /></vml:shape>');
				}
			}

			shape.push('<vml:shape coordsize="',w,',',h,'" filled="true" stroked="false" path="', path,'" coordorigin="0,0" style="width:',w,'px;height:',h,'px;position:absolute;left:0;top:0;"><vml:fill src="', image.src, '" type="frame" rotate="true" alignshape="true" /></vml:shape>');

			if(style.borderWidth != 0) {
				var closed = Math.min.apply(Math, widths)? true : false,
					strokePath = this.roundedRect(new Path(), 0, 0, w, h, borders, widths, closed),
					k = Math.max.apply(Math, widths), offset = k - k%2, half = k/2;
				shape.push('<vml:shape coordsize="',w,',',h,'" filled="false" stroked="true" path="', strokePath,'" coordorigin="0,0" style="width:',(w-offset),'px;height:',(h-offset),'px;position:absolute;left:',half,'px;top:',half,'px;"><vml:stroke weight="', k, '" joinstyle="miter" color="', style.borderColor, '" opacity="', style.borderOpacity, '" /></vml:shape>');
			}

			shape.push('</vml:group>');
			span.insertAdjacentHTML('BeforeEnd', shape.join(''));
			this.replace(image, span, style.replaced);
		},

		// creates a rectangle with rounded borders
		roundedRect: function(c, x, y, w, h, borders, widths, close){
			var r, rad = function(i){ return borders[i]; },
				s, wdt = function(i){ return widths? widths[i] : 1; };
			c.beginPath();
			r = rad(0); c.moveTo(x, y+r); 
			s = wdt(0); r && c.quadraticCurveTo(x, y, x+r, y);
			r = rad(1); c[s? 'lineTo':'moveTo'](x+w-r, y);
			s = wdt(1); r && c.quadraticCurveTo(x+w, y, x+w, y+r);
			r = rad(2); c[s? 'lineTo':'moveTo'](x+w, y+h-r);
			s = wdt(2); r && c.quadraticCurveTo(x+w, y+h, x+w-r, y+h);
			r = rad(3); c[s? 'lineTo':'moveTo'](x+r, y+h);
			s = wdt(3); r && c.quadraticCurveTo(x, y+h, x, y+h-r);
			c[s? 'lineTo':'moveTo'](x, y + rad(0));
			close && c.closePath(); // shouldn't close non-continuous paths
			return c.stroke();
		},

		// canvas only, creates a gradient rectangle shadow with rounded borders
		gradientRect: function(c, x, y, w, h, r, color){
			var linear, radial, r2 = r*2;
			
			function getAlpha(c, alpha) {
				if(c.length <= 4) { c = c.replace(/([0-9a-f])/ig, '$10'); } // #rgb to #rrggbb
				var reg = new RegExp('[0-9a-f]{2}','ig'), trans = [];
				c.replace(reg, function(s){
					trans.push(parseInt(s, 16));
				});	trans.push(alpha);
				return  'rgba(' + trans.join(',') + ')';
			}
			
			function createStops(gradient) {
				gradient.addColorStop( 0, color);
				gradient.addColorStop(.8, getAlpha(color, 0)); // .8 is closer to IE's gradient fill than 1
			}

			createStops(radial = c.createRadialGradient(r,r,0, r,r,r));
			createStops(linear = c.createLinearGradient(0,r, 0,0));
			
			function createEdge(rad, x, y, w, h) {
				c.save(); 
				c.translate(x, y); c.rotate(rad/180 * Math.PI);
				c.fillStyle = radial; c.fillRect(0, 0, h, h);
				c.fillStyle = linear; c.fillRect(h, 0, w-(2*h), h);
				c.restore();
			}
			createEdge(0, x, y, w, r);
			createEdge(90, w+x, y, h, r);
			createEdge(180, w+x, h+y, w, r);
			createEdge(270, x, h+y, h, r);
			c.fillStyle = color; 
			c.fillRect(r+x, r+y, w-r2, h-r2);
		},

		// returns new bounding box for rotated images
		getRotatedBounds: function(w, h, s, rot){
			var ws = w + s, hs = h + s;
			var rad = rot/180 * Math.PI, sin = Math.sin(rad), cos = Math.cos(rad);
			var ry = Math.abs(sin * ws), rx = Math.abs(sin * hs);		
			var rw = rx + Math.abs(cos * ws), rh = ry + Math.abs(cos * hs);
			var dx = rot > 0? rx : 0, dy = dx? 0 : ry;
			return {
				offsetW:rw, offsetH:rh, offsetX:dx, offsetY:dy, angle:rad
			}
		},

		// transforms border radius/width shorthands to arrays.
		parseShorthand:function(r) {
			var nr = function(i){ return parseInt(i, 10); };
			if(typeof r == 'string') {
				var ra = r.split(' '), l = ra.length, a = nr(ra[0]), b = nr(ra[1]);
				switch(l) {
					case 2: var c = a, d = b; break;
					case 3: var c = nr(ra[2]), d = b; break;
					case 4: var c = nr(ra[2]), d = nr(ra[3]); break;
				}
				return [a,b,c,d];
			} else {
				return [r,r,r,r];
			}
		},

		// rotation may be specified as a string with 2 values for a random range
		getRotation:function(r) {
			if(typeof r == 'string') {
				var ra = r.split(' '),
					min = parseFloat(ra[0], 10),
					max = parseFloat(ra[1], 10);
				return min + (Math.random() * (max - min));
			} else {
				return r;
			}
		},

		// replaces original image with canvas or VML element
		replace: function(target, fragment, replaced){
			var parent = target.parentNode;
			parent.replaceChild(fragment, target);
			fragment.className = target.className + ' ' + replaced;
			if(/^a$/i.test(parent.nodeName)) {
				fragment.style.cursor = 'hand';
			}
		}
	};
	
	/**
	 *	Canvas to VML interface
	 *	--------------------------
	 */

	function Path() {}
	
	Path.prototype = {
		beginPath:function(){ this.path = ''; },
		closePath:function(){ this.path += ' x'; },
		moveTo:function(x, y){ this.set(x, y); this.path += ' m '+x+', '+y },
		lineTo:function(x, y){ this.set(x, y); this.path += ' l '+x+', '+y },
		set:function(x, y) { this.x = x; this.y = y; },
		stroke:function(){ return this.path; },
		quadraticCurveTo:function(cx, cy, x, y){ 
			this.path += ' c '+this.x+','+this.y+','+cx+', '+cy+', '+x+', '+y;
			this.set(x, y);
		}
	};

	/**
	 *	Style object
	 *	--------------------------
	 */

	function Style(properties) {
		this.set(properties);
	}

	Style.prototype = {
		clone:function() { return new Style(this); },

		set:function(properties) {
			for(var i in properties) {
				if(typeof this[i] != 'function') {
					this[i] = properties[i];
				}
			}
			return this;
		},
		
		using:function(properties) {
			var clone = this.clone();
			clone.set(properties);
			return clone;
		}
	};

	/**
	 *	Local instances
	 *	--------------------------
	 */
	
	var roundedImages = new Rounder();

	var baseStyle = new Style({
		borderRadius:  4,
		borderWidth:   0,
		borderColor:   '#000',
		borderOpacity: 1,
		shadowOffset:  0,
		shadowColor:   '#000',
		shadowOpacity: 1,
		shadowStyle:   'soft',
		shadowRadius:  'auto',
		rotation:      0,
		replaced:      'replacedImage'
	});

	/**
	 *	jQuery check
	 *	--------------------------
	 */
	
	if(window.jQuery) {
		jQuery.extend(jQuery.fn, {
			roundImages:function(style){
				var settings = style || baseStyle;
				for(var i=0; i<this.length; i++) {
					RoundedImages.parseImage(this[i], settings);
				}
			}
		});
	}

	/**
	 *	Public interface
	 *	--------------------------
	 */
	
	return {
		parse:function(images, style) {
			for(var i=0; i<images.length; i++) {
				this.parseImage(images[i], style);
			}
		},

		parseImage:function(image, style) {
			var self = this, settings = style || baseStyle;
			if(image.complete) {
				roundedImages.roundImage(image, settings);
			} else {
				image.onload = function() {
					roundedImages.roundImage(image, settings);
				}
			}
		},

		createStyle:function(properties) {
			var style = baseStyle.clone();
			style.set(properties);
			return style;
		}
	}

})();