/***************************************************************************************
 * JQUERY ROUND v1.0
 * @author Keegan Watkins
 * 
 * Rounds the corners of each element: This plugin was designed to simplify the creation
 * of rounded corners, using sprite images and CSS backgrounds. The idea was born from
 * a few observations:
 * 
 * 	- Non-standard CSS properties like -moz-border-radius and -webkit-border-radius don't 
 * 	  yet support anti-aliasing, and don't work in Internet Explorer.
 * 
 * 	- Adding empty elements via JS minimizes the semantic bloat of the original page, and 
 * 	  degrades well in the absence of JS.
 * 
 * 	- Fluid-sizing is much more manageable. Some similar techniques use either fixed-width 
 * 	  or fixed-height containers, limiting reuse of images.
 * 
 * 	- Some JS solutions often create large chunks of DOM to avoid images, but the amount 
 * 	  of manipulation required to add hundreds of 1x1 pixel elements to the page is 
 * 	  expensive, compared to a maximum of four extra elements per container-to-round.

 * 	- Combined with proper expires-headers and cache control for the images, this technique 
 *	  can scale reasonably well.
 * 
 * OPTIONS:
 * 	- url (String)                  - The path to the background image. Required
 * 	- corners (Array)               - The array of corners to round, includes all four by default
 * 	- spriteIndex (Number)          - The position of the corner image (zero-indexed) in the sprite
 * 	- cornerRadius (Number|Object)  - The number value to use for the corner's radius, or a map
 * 	- cornerClass (String)          - The class to apply to each corner, 'ui-corner' by default
 * 
 * TODO:
 * 	- Implement an optional <canvas/> rendering
 * 	- IE6 needs some conditional love
 * 
 **************************************************************************************/

(function($) {
	
	// This is already encapsulated in the core, and should maybe be exposed as $.now() ?
	var now = function() { return +new Date() };
	
	$.fn.round = function(options){
		
		// Don't attempt IE6
		if ($.browser.msie && $.browser.version < 7) {
			return;
		}
		
		var defaults, settings, height, width, bg;
		
		// Store defaults for the corners
		defaults = {
			url: "",
			corners: ["tl", "tr", "bl", "br"],
			spriteIndex: 0,
			cornerRadius: 5,
			cornerClass: "ui-corner"
		};
		
		// Merge options/defaults
		settings = $.extend(defaults, options);
		
		// If a number is passed as the corner radius, use it for the width and height. If an object is passed in, access its properties. 
		height = isFinite( settings.cornerRadius ) ? settings.cornerRadius : settings.cornerRadius.height;
		width = isFinite( settings.cornerRadius ) ? settings.cornerRadius : settings.cornerRadius.width;
		
		// Create a map for the background values
		bg = {
			// The background-positioning from the right edge is the negative value of the corner width
			right: "-" + width + "px",
			// The background-positioning from the left edge is zero
			left: "0px",
			// If the sprite index is zero, the background-positioning from the top is zero. 
			// Otherwise, its the opposite value of twice the height multiplied by the sprite index
			top: (settings.spriteIndex === 0) ? (settings.spriteIndex + "px") : ("-" + ((height * 2) * settings.spriteIndex) + "px"),
			// If the sprite index in zero, the background-positioning from the bottom is the negative height value. 
			// Otherwise, use the position from the top plus the height
			bottom: (settings.spriteIndex === 0) ? ("-" + height + "px") : ("-" + (((height * 2) * settings.spriteIndex) + height) + "px"),
			// Background image from options
			img: settings.url
		};
		
		return this.each(function(){
			
			var target = $(this),
			 
			// Reference to container's ID with a separator: used to create unique IDs for every corner added to the page.
			// NOTE: If the container has no ID, use "jCorner_#{now() value here}_" to ensure unique IDs
			guid = target.attr("id") === "" ? "jCorner_" + now() + "_" : target.attr("id") + "_",
			
			// Create a map to represent each corner's HTML.
			corners = {
				"tl": "<span id='" + guid + "tl' class='" + settings.cornerClass + "'></span>",
				"tr": "<span id='" + guid + "tr' class='" + settings.cornerClass + "'></span>",
				"bl": "<span id='" + guid + "bl' class='" + settings.cornerClass + "'></span>",
				"br": "<span id='" + guid + "br' class='" + settings.cornerClass + "'></span>"
			},  
			
			// Determine offset from container on all edges based on border thickness: EX => border of 1px requires -1px offset from each edge
			offset = (function(){
				// Loop through the properties, normalizing the values
				var borders =  {
					top: target.css("borderTopWidth"),
					left: target.css("borderLeftWidth"),
					right: target.css("borderRightWidth"),
					bottom: target.css("borderBottomWidth")
				}
				for(var edge in borders) {
					// Parse to integer
					var parsed = parseInt(borders[edge]);
					if(parsed) {
						// If parsed to zero, use that. Otherwise, use the negative value
						borders[edge] = ("-" + parsed + "px");
					}
				}
				
				return borders;
			})();
			
			// Set container's positioning if not set: ensures that corners are positioned correctly
			// TODO: Should probably use offset() and/or position() here, at least as an option, to avoid
			// positioning the parent.
			if (target.css("position") === "static") {
				target.css("position", "relative");
			}

			// If IE6, need to force "hasLayout:true" to ensure accuracy of positioning
			// TODO: This check assumes too much: XMLHttpRequest might have been normalized 
			// elsewhere. Need to refactor to use $.support
			if (window.attachEvent && !window.XMLHttpRequest) {
				target.css("zoom", "1.0");
			}

			// Iterate through the corners map, adding each corner to the container and applying the CSS.
			$.each(corners, function(name, html){

				var unique;

				// Make sure the corner is in the settings.corners array
				if ($.inArray(name, settings.corners) === -1) {
					return;
				}

				// Switch to generate specific styles for each corner
				switch (name) {
					case "tl":
						unique = {
							top: offset.top,
							left: offset.left,
							background: "transparent url(" + bg.img + ") no-repeat " + bg.left + " " + bg.top
						};
						break;
					case "tr":
						unique = {
							top: offset.top,
							right: offset.right,
							background: "transparent url(" + bg.img + ") no-repeat " + bg.right + " " + bg.top
						};
						break;
					case "bl":
						unique = {
							bottom: offset.bottom,
							left: offset.left,
							background: "transparent url(" + bg.img + ") no-repeat " + bg.left + " " + bg.bottom
						};
						break;
					case "br":
						unique = {
							bottom: offset.bottom,
							right: offset.right,
							background: "transparent url(" + bg.img + ") no-repeat " + bg.right + " " + bg.bottom
						};
						break;
				}
				
				// Add the markup, and apply the merged styles.
				// TODO: Should consider building the entire HTML string in this loop, reducing to a single call to append().
				$(html).css($.extend({
					display: "block",
					position: "absolute",
					height: height + "px",
					width: width + "px",
					overflow: "hidden",
					padding: "0",
					margin: "0"
				}, unique)).appendTo(target);
			});
		});
	};
})(jQuery);
