/*!
 * SVG_roundies dynamically creates SVG graphics to make boxes look like as if they have rounded corners.
 * Adapted from DD_roundies: http://dillerdesign.com/experiment/DD_roundies/
 *
 * Author: Boris Schaeling
 * Email: boris@highscore.de
 * URL: http://www.highscore.de/SVG_roundies/
 * Version: 0.0.2a
 *
 * Licensed under the MIT License: http://www.highscore.de/SVG_roundies/#license
 * Copyright (c) 2008-2009 Drew Diller, Boris Schaeling
 *
 * Usage:
 * SVG_roundies.addRule('p', '10px 5px'); // tag name and multiple radii
 */

var SVG_roundies = {

	ns: 'SVG_roundies',
	imgSize: {},

	/* reset querySelectorAll to document.querySelectorAll if you know your browsers support it */
	querySelectorAll: function(selector) {
		return document.getElementsByTagName(selector);
	},

	/*
	 * Method to use from afar - refer to it whenever.
	 * Example: SVG_roundies.addRule('p', '10px 5px');
	 * @param {String} tag - REQUIRED - a tag name, such as 'p'
	 * @param {String} radii - REQUIRED - the desired radii for the box corners
	 */
	addRule: function(selector, rad) {
		if (typeof rad == 'undefined' || rad === null) {
			rad = 0;
		}
		if (rad.constructor.toString().search('Array') == -1) {
			rad = rad.toString().replace(/[^0-9 ]/g, '').split(' ');
		}
		for (var i=0; i<4; i++) {
			rad[i] = (!rad[i] && rad[i] !== 0) ? rad[Math.max((i-2), 0)] : rad[i];
		}
		var selectors = selector.split(',');
		for (var i=0; i<selectors.length; i++) {
			var els = this.querySelectorAll(selector);
			for (var j=0; j<els.length; j++) {
				this.roundify(els[j], rad);
			}
		}
	},

	/* equivalent to IE's runtimeStyle */
	runtimeStyle: function(el) {
		var compStyle = document.defaultView.getComputedStyle(el, null);
		if (el.SVG_roundies.v === undefined) {
			el.SVG_roundies.v = {};
		}
		if (el.SVG_roundies.v.bgColor === undefined || el.SVG_roundies.v.bgColor != compStyle.getPropertyValue('background-color')) {
			el.SVG_roundies.v.bgColor = compStyle.getPropertyValue('background-color');
		}
		if (el.SVG_roundies.v.bgImage === undefined || el.SVG_roundies.v.bgImage != compStyle.getPropertyValue('background-image')) {
			el.SVG_roundies.v.bgImage = compStyle.getPropertyValue('background-image');
		}
		/* Firefox doesn't like border-color */
		if (el.SVG_roundies.v.borderColor === undefined || el.SVG_roundies.v.borderColor != compStyle.getPropertyValue('border-left-color')) {
			el.SVG_roundies.v.borderColor = compStyle.getPropertyValue('border-left-color');
		}
	},

	readPropertyChanges: function(evt) {
		/* as events bubble make sure that only events at target phase are processed */
		if (evt.currentTarget == evt.target && evt.attrName == 'style') {
			/*
			 * As in Firefox prevValue and newValue are the same (see https://bugzilla.mozilla.org/show_bug.cgi?id=338679)
			 * and as it's very difficult to find the difference between the previous and the new style in Opera, too,
			 * this is a brute force approach.
			 */
			var el = evt.target;
			this.runtimeStyle(el);
			el.SVG_roundies.svg.style.setProperty('z-index', el.style.getPropertyValue('z-index'), null);
			this.applySVG(el);
		}
	},

	applySVG: function(el) {
		this.removeEventListeners(el);
		this.svgFill(el);
		this.svgStrokeColor(el);
		this.svgStrokeWeight(el);
		this.svgOffsets(el);
		this.svgPath(el);
		this.svgOpacity(el);
		this.addEventListeners(el);
	},

	/* <p style="opacity: 0.5"> */
	svgOpacity: function(el) {
		var compStyle = document.defaultView.getComputedStyle(el, null);
		el.SVG_roundies.svg.setAttribute('opacity', compStyle.getPropertyValue('opacity'));
	},

	/* <p style="background-color: #FF0000"> or <p style="background-image: url(foo.png)> or <img src="foo.png"> */
	svgFill: function(el) {
		el.style.setProperty('background-color', 'transparent', null);
		if (el.SVG_roundies.v.bgImage != 'none' || el.SVG_roundies.isImg) {
			var initImage = function(lib, el, img) {
				var defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs');
				var pattern = document.createElementNS('http://www.w3.org/2000/svg', 'pattern');
				pattern.setAttribute('patternUnits', 'userSpaceOnUse');
				pattern.setAttribute('width', img.offsetWidth + 'px');
				pattern.setAttribute('height', img.offsetHeight + 'px');
				var id = lib.ns + '_' + img.getAttribute('src').replace(/\W/g, '');
				pattern.setAttribute('id', id);
				el.SVG_roundies.paths.color.setAttribute('fill', 'url(#' + id +')');
				var image = document.createElementNS('http://www.w3.org/2000/svg', 'image');
				image.setAttributeNS('http://www.w3.org/1999/xlink', 'href', img.getAttribute('src'));
				image.setAttribute('width', img.offsetWidth + 'px');
				image.setAttribute('height', img.offsetHeight + 'px');
				pattern.appendChild(image);
				defs.appendChild(pattern);
				el.SVG_roundies.svg.appendChild(defs);
			}
			var lib = this;
			if (el.SVG_roundies.isImg) {
				initImage(lib, el, el);
			} else {
				var bg;
				el.style.setProperty('background-image', 'none', null);
				var url = el.SVG_roundies.v.bgImage;
				bg = url.substring(4, url.length-1);
				if (bg[0] == '"' || bg[0] == '\'') {
					bg = bg.substring(1, bg.length-1);
				}
				if (this.imgSize[bg] === undefined) {
					var img = document.createElement('img');
					img.addEventListener('load', function() {
						initImage(lib, el, img);
					}, false);
					img.style.setProperty('position', 'absolute', null);
					img.style.setProperty('top', '-10000px', null);
					img.style.setProperty('left', '-10000px', null);
					img.setAttribute('src', bg);
					document.body.insertBefore(img, document.body.firstChild);
					this.imgSize[bg] = img;
				} else {
					initImage(lib, el, this.imgSize[bg]);
				}
			}
		}
		else {
			el.SVG_roundies.paths.color.setAttribute('fill', (el.SVG_roundies.v.bgColor == 'transparent') ? 'none' : el.SVG_roundies.v.bgColor);
		}
	},

	/* <p style="border-color: 0000FF"> */
	svgStrokeColor: function(el) {
		el.style.setProperty('border-color', 'transparent', null);
		el.SVG_roundies.paths.stroke.setAttribute('fill', el.SVG_roundies.v.borderColor);
	},

	/* <p style="border-width: 5px"> */
	svgStrokeWeight: function(el) {
		var compStyle = document.defaultView.getComputedStyle(el, null);
		var borders = ['top', 'right', 'bottom', 'left'];
		el.SVG_roundies.bW = [];
		for (var b=0; b<4; b++) {
			el.SVG_roundies.bW[borders[b]] = parseInt(compStyle.getPropertyValue('border-' + borders[b] + '-width'), 10) || 0;
		}
	},

	svgOffsets: function(el) {
		var dims = ['Left', 'Top', 'Width', 'Height'];
		el.SVG_roundies.dim = [];
		for (var d=0; d<4; d++) {
			el.SVG_roundies.dim[dims[d]] = el['offset' + dims[d]];
		}
		var assign = function(obj) {
			obj.style.setProperty('left', el.SVG_roundies.dim.Left + 'px', null);
			obj.style.setProperty('top', el.SVG_roundies.dim.Top + 'px', null);
			obj.style.setProperty('width', el.SVG_roundies.dim.Width + 'px', null);
			obj.style.setProperty('height', el.SVG_roundies.dim.Height + 'px', null);
		};
		assign(el.SVG_roundies.svg);
	},

	svgPath: function(el) {
		var coords = function(direction, w, h, r, aL, aT) {
			var R = r.slice(); /* do not affect original array */
			for (var i=0; i<4; i++) {
				R[i] = Math.min(w/2, h/2, R[i]); /* make sure you do not get funky shapes - pick the smallest: half of the width, half of the height, or current value */
			}
			if (direction) {
				var cmd = ['M', 'A', 'L', 'A', 'L', 'A', 'L', 'A', 'L'];
				var coords = [
					cmd[0] + Math.floor(0+aL) +','+ Math.floor(R[0]+aT),
					cmd[1] + R[0] + ',' + R[0] + ' 0 0 1 ' + Math.floor(R[0]+aL) +','+ Math.floor(0+aT),
					cmd[2] + Math.ceil(w-R[1]+aL) +','+ Math.floor(0+aT),
					cmd[3] + R[1] + ',' + R[1] + ' 0 0 1 ' + Math.ceil(w+aL) +','+ Math.floor(R[1]+aT),
					cmd[4] + Math.ceil(w+aL) +','+ Math.ceil(h-R[2]+aT),
					cmd[5] + R[2] + ',' + R[2] + ' 0 0 1 ' + Math.ceil(w-R[2]+aL) +','+ Math.ceil(h+aT),
					cmd[6] + Math.floor(R[3]+aL) +','+ Math.ceil(h+aT),
					cmd[7] + R[3] + ',' + R[3] + ' 0 0 1 ' + Math.floor(0+aL) +','+ Math.ceil(h-R[3]+aT),
					cmd[8] + Math.floor(0+aL) +','+ Math.floor(R[0]+aT)
				];
			} else {
				var cmd = ['A', 'L', 'A', 'L', 'A', 'L', 'A', 'L', 'M'];
				var coords = [
					cmd[0] + R[0] + ',' + R[0] + ' 0 0 0 ' + Math.floor(0+aL) +','+ Math.floor(R[0]+aT),
					cmd[1] + Math.floor(R[0]+aL) +','+ Math.floor(0+aT),
					cmd[2] + R[1] + ',' + R[1] + ' 0 0 0 ' + Math.ceil(w-R[1]+aL) +','+ Math.floor(0+aT),
					cmd[3] + Math.ceil(w+aL) +','+ Math.floor(R[1]+aT),
					cmd[4] + R[2] + ',' + R[2] + ' 0 0 0 ' + Math.ceil(w+aL) +','+ Math.ceil(h-R[2]+aT),
					cmd[5] + Math.ceil(w-R[2]+aL) +','+ Math.ceil(h+aT),
					cmd[6] + R[3] + ',' + R[3] + ' 0 0 0 ' + Math.floor(R[3]+aL) +','+ Math.ceil(h+aT),
					cmd[7] + Math.floor(0+aL) +','+ Math.ceil(h-R[3]+aT),
					cmd[8] + Math.floor(0+aL) +','+ Math.floor(R[0]+aT)
				];
				coords.reverse();
			}
			var path = coords.join('');
			return path;
		};

		var bW = el.SVG_roundies.bW;
		var rad = el.SVG_roundies.radii.slice();

		/* determine outer curves */
		var outer = coords(true, el.SVG_roundies.dim.Width, el.SVG_roundies.dim.Height, rad, 0, 0);

		/* determine inner curves */
		rad[0] -= Math.max(bW.left, bW.top);
		rad[1] -= Math.max(bW.top, bW.right);
		rad[2] -= Math.max(bW.right, bW.bottom);
		rad[3] -= Math.max(bW.bottom, bW.left);
		for (var i=0; i<4; i++) {
			rad[i] = Math.max(rad[i], 0);
		}
		var inner = coords(false, el.SVG_roundies.dim.Width-bW.left-bW.right, el.SVG_roundies.dim.Height-bW.top-bW.bottom, rad, bW.left, bW.top);

		/* apply huge path string */
		el.SVG_roundies.paths.color.setAttribute('d', inner);
		el.SVG_roundies.paths.stroke.setAttribute('d', outer + inner);
	},

	reposition: function(el) {
		this.svgOffsets(el);
		this.svgPath(el);
	},

	addEventListeners: function(el) {
		if (el.handlers === undefined) {
			el.handlers = [];
			var lib = SVG_roundies;
			var handlers = {resize: 'reposition'};
			for (var h in handlers) {
				var handler = function() {
					lib[handlers[h]](el);
				};
				if (h == 'resize') {
					window.addEventListener(h, handler, false); /* resize is only fired for the entire window */
				} else {
					el.addEventListener(h, handler, false);
				}
				el.handlers.push({type: h, listener: handler});
			}
			var handler = function(evt) {
				lib.readPropertyChanges(evt);
			};
			el.addEventListener('DOMAttrModified', handler, false); /* doesn't fire in Safari 4 Public Beta if an existing attribute is changed */
			el.handlers.push({type: 'DOMAttrModified', listener: handler});
		}
	},

	removeEventListeners: function(el) {
		if (el.handlers !== undefined) {
			for (var i=0; i<el.handlers.length; i++) {
				var handler = el.handlers[i];
				if (handler.type == 'resize') {
					window.removeEventListener(handler.type, handler.listener, false);
				} else {
					el.removeEventListener(handler.type, handler.listener, false);
				}
			}
			delete el.handlers;
		}
	},

	roundify: function(el, rad) {
		var lib = SVG_roundies;
		el.SVG_roundies = {};
		el.SVG_roundies.radii = rad;

		var allowed = {TABLE: false, TR: false, TD: false, OPTION: false};
		if (allowed[el.nodeName] === false) { /* elements not supported */
			return;
		}

		/* save style properties which will be overriden */
		this.runtimeStyle(el);

		/* create svg elements */
		var compStyle = document.defaultView.getComputedStyle(el, null);
		el.SVG_roundies.svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
		el.SVG_roundies.svg.style.setProperty('position', 'absolute', null);
		el.SVG_roundies.svg.style.setProperty('z-index', compStyle.getPropertyValue('z-index'), null);

		el.SVG_roundies.paths = {};

		el.SVG_roundies.paths.color = document.createElementNS('http://www.w3.org/2000/svg', 'path');
		el.SVG_roundies.svg.appendChild(el.SVG_roundies.paths.color);

		el.SVG_roundies.paths.stroke = document.createElementNS('http://www.w3.org/2000/svg', 'path');
		el.SVG_roundies.svg.appendChild(el.SVG_roundies.paths.stroke);

		el.parentNode.insertBefore(el.SVG_roundies.svg, el);

		/* position element to move it to front (otherwise it's hidden by the SVG graphic) */
		if (compStyle.getPropertyValue('position') == 'static') {
			el.style.setProperty('position', 'relative', null);
		}

		el.SVG_roundies.isImg = false;
		if (el.nodeName == 'IMG') {
			el.SVG_roundies.isImg = true;
			el.style.setProperty('visibility', 'hidden', null);
		}

		setTimeout(function() {
			lib.applySVG(el);
		}, 1);
	}

};