/**
 * Common UI related objects with small footprints.
 * 
 * @module ui
 * @version   1.02.101229
 * @requires  jQuery, LBi
 * @author    LBi Lost Boys
 */
(function($) {

	var LBi = window.LBi;
	var document = window.document;

	var Class = LBi.Class;
	var Event = LBi.Event;
	var Dispatcher = LBi.Dispatcher;
	

	/**
	 * The UIComponent serves as an extendable base for interface components that manage page-wide 
	 * functionality, are aware of the DOMNodeInserted event, and rely on a settings object for 
	 * customizable options.
	 *
	 * @class LBi.UIComponent
	 * @extends LBi.DOMListener
	 * @constructor
	 * @param {Object} settings Customizable settings that overrule the defaults.
	 * @return {UIComponent} instance
	 */
	var UIComponent = Class.extend(
		LBi.DOMListener,

		function(settings) {
			this.settings = $.extend({}, this.constructor.Defaults, settings);
			this.parseNode(document);
		},{
		
		nodeInserted: function(e) { 
			this.parseNode(e.target); 
		},
		
		nodeRemoved: function(e) {
			this.releaseNode(e.target);
		},

		/**
		 * Handler for the inserted node. MUST be implemented by subclasses, and is automatically 
		 * called for document when an instance is created.
		 * 
		 * @param {Node} node The inserted node
		 */
		parseNode: LBi.AbstractMethod,
		
		/**
		 * Handler for nodes about to be removed. May be implemented to unbind events etc.
		 * 
		 * @param {Node} node The removed node
		 */
		releaseNode: function(node) {
		}
	});

	LBi.namespace('UIComponent', UIComponent);

	/**
	 * Suggested default settings for UIComponent classes.
	 *
	 * @static
	 * @class LBi.UIComponent.Defaults
	 */
	UIComponent.Defaults = {
		/**
		 * Suggested property for the component's outer element, for instance ".tabs" or ".equalized".
		 * @property selector
		 * @type String
		 * @default 'undefined'
		 */
		selector: 'undefined',

		/**
		 * Suggested property for the component's primary child nodes, for instance "li", ".tab" or ".equal".
		 * @property children
		 * @type String
		 * @default 'undefined'
		 */
		children: 'undefined'
	};


	/**
	 * The Forms class augments forms with additional behavior. The settings object
	 * may be used to specify preferences. By default listens to the DOMNodeInserted and -removed 
	 * events as fired by the DOM mediator, to automatically augment new forms inserted via ajax.
	 * Use the selector setting to assign forms to specific instances when more than one is used.
	 * 
	 * @class LBi.Forms
	 * @extends LBi.UIComponent
	 * @constructor
	 * @param {Object} settings Settings object, see LBi.Forms.Defaults.
	 * @return {Forms} forms instance
	 */
	var Forms = Class.extend(
		UIComponent, null, {

		/**
		 * Parses the given node for forms and inputs, and augments them based on settings.
		 * This method is automatically called by LBi.Form instances.
		 * 
		 * @param {Node} node 
		 */
		parseNode:function(node){
			var set = this.settings;
			var forms = $(set.selector, node);
			var inputs = $('input,select,textarea', node);
			
			if(forms.length > 0){				
				Dispatcher.capture(Event.SUBMIT, forms);
			}

			if(inputs.length > 0) {
				Dispatcher.capture(Event.CHANGE, inputs);
				
				if(set.replaceButtons){
					this.replaceButtons(inputs);
				}
				
				if(set.placeholders){
					this.applyPlaceholders(inputs);
				}
			}
		},

		/**
		 * Automatically called via the DOMNodeRemoved event. Cleans up form and iput events.
		 *
		 * @param {Event} e The event object
		 */
		releaseNode:function(node){
			var forms = $(this.settings.selector, node);
			forms.unbind();
			
			var inputs = $('input,select,textarea', node);
			inputs.unbind();
		},

		/**
		 * Binds focus and blur events to inputs encountered in the given form(s). 
		 *
		 * @param {Node|NodeList|jQuery} forms The node(s) to search for inputs. 
		 */
		applyPlaceholders:function(inputs){
			var REG_TEXT = /text|pass/i;
			var focusInput = this.focusInput.bind(this);
			var blurInput = this.blurInput.bind(this);
			inputs.each(function(){
				if(!REG_TEXT.test(this.type)){
					return;
				}

				var $input = $(this);
				if($input.attr('placeholder')) {
					$input.bind(Event.FOCUS, focusInput);
					$input.bind(Event.BLUR, blurInput);
				}
			});

			inputs.trigger(Event.BLUR);
		},

		/**
		 * Focus handler for patched placeholder inputs. Conditionally clears the input's value and removes 
		 * the placeholderClass from the input.
		 * 
		 * @param {Event} e The event object
		 */
		focusInput:function(e){
			var settings = this.settings;
			var input = e.target;
			var $input = $(input);
			var placeholder = $input.attr(settings.placeholder);

			if(input.value === placeholder){
				input.value = '';
				$input.removeClass(settings.placeholderClass);
			}
		},

		/**
		 * Blur handler for patched placeholder inputs. Conditionally sets the input's value to the placeholder
		 * and adds the placeholderClass to the input.
		 * 
		 * @param {Event} e The event object
		 */
		blurInput:function(e){
			var settings = this.settings;
			var input = e.target;
			var $input = $(input);
			var placeholder = $input.attr(settings.placeholder);

			if(input.value === placeholder || input.value === ''){
				input.value = placeholder;
				$input.addClass(settings.placeholderClass);
			}
		},

		/**
		 * Replaces submit and reset buttons by stylable links based on the template provided
		 * to the settings object. Using the default settings, a click event is fired for the 
		 * replaced input when the related link is clicked.
		 * 
		 * @param {Node|NodeList|jQuery} forms The node(s) to search for inputs.
		 */
		replaceButtons:function(inputs){
			var REG_BUTTON = /submit|reset/i;
			var REG_SUBMIT = /submit/i;
			var set = this.settings;
			var template = set.buttonTemplate;
			var replaced = set.replacedClass;
			var simulate = set.simulateClick;

			inputs.each(function(){
				if(!REG_BUTTON.test(this.type)){
					return;
				}

				var input = this;
				var form = input.form;
				var $input = $(input);
				var $button = $(
					template.replace(
						/\$([a-z]+)/mig, 
						function(match, attr){
							return input[attr] || '';
						}
					)
				);
				
				$input.addClass(replaced);
				$input.after($button);
				
				if(simulate){
					var eventType = REG_SUBMIT.test(input.type)? Event.SUBMIT : Event.RESET;
					$button.bind(Event.CLICK, function(e){
						e.preventDefault();
						if($(form).triggerHandler(eventType, {explicitTarget: input}) !== false){
							$input.trigger(Event.CLICK);
						}
					});
				}
			});
		}
	});

	LBi.namespace('Forms', Forms);	
	

	/**
	 * Default settings for the LBi.Forms class.
	 *
	 * @static
	 * @class LBi.Forms.Defaults
	 */
	Forms.Defaults = {
		/**
		 * Defines the nodes interpreted as "form". Typically this is an actual form, but in specific
		 * cases you might want to target other nodes, or use multiple LBi.Forms instances that target 
		 * differently classed forms, each with individual settings.
		 * @property selector
		 * @type String
		 * @default "form"
		 */
		selector: 'form',
		
		/**
		 * Defines whether the HTML 5 placeholder attribute is patched. Off by default, detect support with Modernizr.
		 * @property placeholders
		 * @type boolean
		 * @default false
		 */
		placeholders: false,

		/**
		 * The attribute from which the placeholder text is taken
		 * @property placeholder
		 * @type String
		 * @default "placeholder"
		 */
		placeholder: "placeholder",
		
		/**
		 * The class that is set on patched placeholder inputs, present when the placeholder value is active.
		 * @property placeholderClass
		 * @type String
		 * @default "placeholder"
		 */
		placeholderClass: 'placeholder',
		
		/**
		 * Defines whether input buttons are replaced by styled links.
		 * @property replaceButtons
		 * @type boolean
		 * @default true
		 */
		replaceButtons: true,

		/**
		 * The selector for replaced buttons. Regardless of its value will only target submit and reset inputs.
		 * @property buttonSelector
		 * @type String
		 * @default "input"
		 */
		buttonSelector: 'input',

		/**
		 * The button template. Copies the classname and value of the input. Additional 
		 * attributes may be copied by marking them with a $.
		 * @property buttonTemplate
		 * @type String
		 * @default "&lt;a href="#" class="$className"&gt;&lt;span&gt;$value&lt;/span&gt;&lt;/a&gt;"
		 */
		buttonTemplate: '<a href="#" class="$className"><span>$value</span></a>',
		
		/**
		 * Class for replaced inputs, may be used to hide them, or move them out of view.
		 * @property replacedClass
		 * @type String
		 * @default "replaced"
		 */
		replacedClass: 'replaced',
		
		/**
		 * Defines whether a click is simulated on the original input to submit or reset a form. 
		 * Ensures that a possible name/value pair is added to the post, similar to a native click.
		 * @property simulateClick
		 * @type boolean
		 * @default true
		 */
		simulateClick: true
	};

	

	/**
	 * Divides the childnodes of selected nodes over 2 or more separate clones, which can more
	 * easily be positioned next to each other. The effect is pretty similar to css' column-count.
	 * See the SplittedNodes.Defaults for the available options. 
	 * 
	 * @class LBi.SplittedNodes
	 * @extends LBi.UIComponent
	 * @constructor
	 * @param {Object} settings Settings object, see LBi.SplittedNodes.Defaults.
	 * @return {SplittedNodes} instance
	 */
	var SplittedNodes = Class.extend(
		UIComponent, null, {

		/**
		 * Checks the given node for childs to split. This method is called automatically for
		 * the document on DOMContentLoaded, and also for nodes inserted via the LBi.DOM mediator.
		 * In any other case, the method must be called manually on dynamically inserted nodes.
		 * 
		 * @param {Node} node The node to check for splittable childs.
		 */
		parseNode: function(node) {
			var nodes = $(node).find(this.settings.selector);
			for(var i=0,l=nodes.length; i<l; i++) {
				this.split(nodes[i]);
			}
		},

		/**
		 * Parses a node's classname, and returns a data object for the wrapper template.
		 * 
		 * @param {String} className
		 * @return {Object} Data object with pattern and cols properties.
		 */
		getData: function(className) {
			var reg = this.settings.pattern;
			if(reg.test(className)) {
				var match = reg.exec(className);
				return {
					pattern: match[0],
					cols: parseInt(match[1], 10)
				};
			}

			return false;
		},

		/**
		 * Creates the wrapper for splitted nodes based on the template and data object.
		 * 
		 * @param {Object} data Data object with pattern and cols properties.
		 * @return {jQuery} jQuery result containing the transformed template.
		 */
		getWrapper: function(data) {
			var template = new LBi.Template(this.settings.template);
			return $(template.parse(data));
		},

		/**
		 * Splits the given node based on the current properties.
		 * 
		 * @param {Node} node The node to split
		 */
		split: function(node) {
			var data = this.getData(node.className);
			if(data) {
				var $origin = $(node);
				var items = $origin.find(this.settings.children);
				var $node = this.getWrapper(data);
				
				var split;
				var l = items.length;
				var rows = Math.ceil(l / data.cols);
				for(var i=0; i<l; i++) {
					if(i % rows === 0) {
						split = $(node.cloneNode(false));
						split.removeClass(data.pattern);
						$node.append(split);
					}

					split.append(items[i]);
				}

				$origin.replaceWith($node);
			}
		}
	});

	LBi.namespace('SplittedNodes', SplittedNodes);
	
	/**
	 * Default settings for the LBi.SplittedNodes class.
	 *
	 * @static
	 * @class LBi.SplittedNodes.Defaults
	 */
	SplittedNodes.Defaults = {
		/**
		 * Indicates which nodes are selected for splitting. Note that an additional pattern class is also required.
		 * @property selector
		 * @type String
		 * @default '.splitted'
		 */
		selector: '.splitted',

		/**
		 * Indicates which childnodes are split
		 * @property children
		 * @type String
		 * @default 'li'
		 */
		children: 'li',

		/**
		 * The template for the wrapper that surrounds the splitted nodes, $pattern equals the pattern setting's classname match, $cols the optional amount.
		 * @property template
		 * @type String
		 * @default '<div class="$pattern"></div>'
		 */
		template: '<div class="$pattern"></div>',

		/**
		 * Pattern check on the classname of selected nodes. Used by getData to retreive the split count, and copied to the wrapper as classname for styling.
		 * @property pattern
		 * @type RegExp
		 * @default /split-([0-9]+)/i
		 */
		pattern: /split-([0-9]+)/i
	};


	/**
	 * EqualizedNodes sets the height of all childnodes of the target node to the highest value found.
	 * This is sometimes required for specific styling purposes where a min-height is not enough.
	 * 
	 * @class LBi.EqualizedNodes
	 * @extends LBi.UIComponent
	 * @constructor
	 * @param {Object} settings Settings object, see LBi.EqualizedNodes.Defaults.
	 * @return {EqualizedNodes} instance
	 */
	var EqualizedNodes = Class.extend(
		UIComponent, null, {
		
		/**
		 * Checks the given node for nodes to process. This method is called automatically for
		 * the document on DOMContentLoaded, and also for nodes inserted via the LBi.DOM mediator.
		 * In any other case, the method must be called manually on dynamically inserted nodes.
		 * 
		 * @param {Node} node The node to check for equalizable targets.
		 */
		parseNode: function(node) {
			var nodes = $(node).find(this.settings.selector);
			for(var i=0,l=nodes.length; i<l; i++) {
				this.equalize(nodes[i]);
			}
		},
	
		/**
		 * Checks the childnodes of a given node as defined by the children setting, and sets the
		 * height to the largest value found.
		 * 
		 * @param {Node} node The node to check for equalizable childnodes.
		 */
		equalize: function(node) {
			var settings = this.settings;
			var nodes = $(node).find(settings.children);
			var minHeight = settings.useMinHeight;
			var property = minHeight? 'minHeight' : 'height';
			var reset = minHeight? '0' : 'auto';
			var height = 0;

			for(var node,i=0, l=nodes.length; i<l; i++) {
				node = nodes[i];
				node.style[property] = reset;
				height = Math.max(height, node.offsetHeight);
			}

			if(height > 0) {
				nodes.css(property, height);
			}
		}
	});

	LBi.namespace('EqualizedNodes', EqualizedNodes);

	/**
	 * Default settings for the LBi.EqualizedNodes class.
	 *
	 * @static
	 * @class LBi.EqualizedNodes.Defaults
	 */
	EqualizedNodes.Defaults = {
		/**
		 * Indicates which containing nodes are targeted for equalizing.
		 * @property selector
		 * @type String
		 * @default '.equalized'
		 */
		selector: '.equalized',

		/**
		 * Indicates which childnodes are split
		 * @property children
		 * @type String
		 * @default 'equal'
		 */
		children: '.equal',

		/**
		 * Defines whether minheight is used instead of height
		 * @property useMinHeight
		 * @type boolean
		 * @default false
		 */
		useMinHeight: false
	};


	/**
	 * The Tabs class manages one or more tab menus on a given page. A tab menu typically consists
	 * of a unordered list of links, which can be linked to individual tabs using an anchor; the 
	 * #hash value must correspond to the ID of the related tab. A rel attribute with (default) 
	 * value "tab" is required on links for the Tabs class to identify them. 
	 *
	 * @class LBi.Tabs
	 * @constructor
	 * @extends LBi.UIComponent
	 * @param {Object} settings Settings, see LBi.Tabs.Defaults
	 * @return {Tabs} Tabs instance
	 */
	var Tabs = Class.extend(
		UIComponent,
		
		function() {
			var settings = this.settings;
			var relations = new LBi.LinkRelations();
			var regex = new RegExp('(^|\\s)' + settings.relation + '(\\s|$)');
			
			relations.subscribe(regex, this.handleClick.bind(this));
			
			if(settings.hashEnabled) {
				this.pollHash();
			}
		},{

		/**
		 * Applies the active state of tabs within a given node. Called automatically
		 *
		 * @param {Node} node
		 */
		parseNode:function(root) {
			var settings = this.settings;
			var activated = $(
				settings.children + '.' + 
				settings.activeClass + ' a[rel=' + 
				settings.relation + ']', root
			);

			for(var i=0; i<activated.length; i++) {
				this.activate(activated[i], true);
			}
		},

		/**
		 * Click handler, called via the relation attribute
		 * @private
		 */
		handleClick:function(e) {
			var link = $(e.target).closest('a')[0];
			this.activate(link);
			e.preventDefault();
		},

		/**
		 * Activates or toggles a tab based on the given link. The related tab (ID referenced via its hash) 
		 * is made visible. All other tabs of links in the same tabmenu are hidden. This method
		 * is called automatically, but may also be called manually when needed. A "layoutchanged"
		 * event is fired for each tab.
		 * 
		 * @param {Node} link The link to activate. MUST be contained in a tab menu.
		 */
		activate:function(link, overruled) {
			var settings = this.settings;
			var toggle = (!overruled && settings.toggleEnabled)? null : true;

			// activate the new one first
			this.activateLink(link, toggle);
			
			// then deactivate others
			var tabs = $(link).closest(settings.selector);
			var links = tabs.find('a[rel=' + settings.relation + ']');
			for(var i=0; i<links.length; i++) {
				var tab = links[i];
				if(tab !== link) {
					this.activateLink(tab, false);
				}
			}
		},

		/**
		 * Toggles a tab
		 * @private
		 */
		activateLink: function(link, toggle) {
			var settings = this.settings;
			var active = settings.activeClass;
			var $item = $(link).closest(settings.children);

			$item.toggleClass(active, toggle);

			if(settings.hashEnabled) {
				var isActive = $item.hasClass(active);
				var target = $(link.hash)[0];
				if(target) {
					settings.animation.run(target, isActive, {
						duration: settings.duration,
						complete: function() {
							Dispatcher.fire(Event.LAYOUT_CHANGED, target);
						}
					});
				}
			}
		},

		/**
		 * Polls the window location hash for a reference to a tab, and activates it when encountered.
		 * This method is called once automatically for every instance of Tabs.
		 * @method pollHash
		 */
		pollHash:function() {
			var hash = window.location.hash;
			if(hash) {
				this.activateHash(hash);
			}
		},

		/**
		 * Polls the document for a tab link with the given hash, and activates it when encountered.
		 * 
		 * @param {String} hash
		 */
		activateHash: function(hash) {
			var link = $('a[rel=' + this.settings.relation + ']').filter('[href$='+hash+']');
			if(link.length) {
				this.activate(link[0]);
			}
		}
	});

	LBi.namespace('Tabs', Tabs);

	/**
	 * Default settings for tabs instances. 
	 * 
	 * @static
	 * @class LBi.Tabs.Defaults
	 */
	Tabs.Defaults = {
		/**
		 * Tab menu selector, used while traversing upward from a clicked tab link. 
		 * @property selector
		 * @type String
		 * @default "ul"
		 */
		selector: 'ul',
		
		/**
		 * Tab item selector, used while traversing upward from a clicked tab link. 
		 * @property children
		 * @type String
		 * @default "li"
		 */
		children: 'li',
		
		/**
		 * Class for an active tab, for styling purposes. Applied on the node selected by the children property.
		 * @property activeClass
		 * @type String
		 * @default "active"
		 */
		activeClass: 'active',
		
		/**
		 * Rel value for tab links.
		 * @property relation
		 * @type String
		 * @default "tab"
		 */
		relation: 'tab',
		
		/**
		 * Defines whether hash values are used at all. If not, clicking a tab only toggles the active class, and visibility must be toggled using css.
		 * @property hashEnabled
		 * @type boolean
		 * @default true
		 */
		hashEnabled: true,

		/**
		 * Defines whether a second click closes an active item.
		 * @property toggleEnabled
		 * @type boolean
		 * @default false
		 * 
		 */
		toggleEnabled: false,

		/**
		 * Animation for toggling tabs. See the the LBi.Animation class.
		 * @property animation
		 * @type LBi.Animation
		 * @default LBi.Animation.TOGGLE
		 */
		animation: LBi.Animation.TOGGLE,

		/**
		 * The duration for the animation (if any)
		 * @property duration
		 * @type number
		 * @default 0
		 */
		duration: 0
	};


	/**
	 * ClickableAreas assigns a larger area to be clickable than just a link, like table rows or
	 * banner like divs. A click on the first link in such an area is then triggered. Clicks on
	 * links or inputs are ignored by default. See the Defaults for more specific settings.
	 * 
	 * @class LBi.ClickableAreas
	 * @constructor
	 * @param {Object} settings Settings, see LBi.ClickableAreas.Defaults
	 * @return {ClickableAreas} instance
	 */
	var ClickableAreas = function(settings) {
		this.settings = $.extend({}, ClickableAreas.Defaults, settings);
		LBi.subscribe(Event.CLICK, this.handleClick.bind(this));
	};

	ClickableAreas.prototype = {
		constructor: ClickableAreas,
		
		/**
		 * click handler
		 * @private
		 */
		handleClick: function(e) {
			var node = $(e.target);
			var settings = this.settings;
			var ignored = node.closest(settings.ignored);
			if(ignored.length === 0) {
				var area = node.closest(settings.clickable);
				if(area.length > 0) {
					var link = area.find('a[href]').eq(0);
					if(link.length > 0) {
						link.trigger(Event.CLICK);
					}
				}
			}
		}
	};

	LBi.namespace('ClickableAreas', ClickableAreas);

	/**
	 * Default settings for ClickableArea instances. 
	 * 
	 * @static
	 * @class LBi.ClickableAreas.Defaults
	 */
	ClickableAreas.Defaults = {
		/**
		 * Selector indicating which areas are clickable. Multiple area selectors may be specified comma separated.
		 * @property clickable
		 * @type String
		 * @default '.clickable'
		 */
		clickable: '.clickable',

		/**
		 * Elements for which clicks are ignored. Kept simple for speed, though a more complex selector may be required for larger areas with more versatile content.
		 * @property ignored
		 * @type String
		 * @default 'a,input'
		 */
		ignored: 'a,input'
	};


	/**
	 * Uses the canvas 2D context to apply rounded edges, shadow and border to selected images. See 
	 * RoundedImages.Defaults for the available settings.
	 * 
	 * @class LBi.RoundedImages
	 * @constructor
	 * @extends LBi.UIComponent
	 * @param {Object} settings Settings, see LBi.RoundedImages.Defaults
	 * @return {RoundedImages} instance
	 */
	var RoundedImages = Class.extend(
		UIComponent, null, {
		
		/**
		 * Checks the node for images to transform.
		 * 
		 * @param {Node} node
		 */
		parseNode: function(node) {
			this.border = this.getBorder();
			this.shadow = this.getShadow();

			var images = $(node).find(this.settings.selector);
			var l = images.length;
			for(var i=0; i<l; i++) {
				this.queue(images[i]);
			}
		},
		
		/**
		 * Queues an image for transforming. If the image is not yet loaded, transform is bound to
		 * its onload event. This method is called automatically.
		 * 
		 * @param {Node} img To be transformed image.
		 */
		queue: function(img) {
			if(img.complete) {
				this.transform(img);
			} else {
				var self = this;
				img.onload = function() {
					self.transform(img);
				};
			}
		},
		
		/**
		 * Transforms the given image to a styled one in canvas, and replaces the image for it.
		 * 
		 * @param {Node} img 
		 */
		transform: function(img) {
			var settings = this.settings;
			var w = img.width;
			var h = img.height;
			
			var shadow = this.shadow;
			if(shadow) {
				 w += shadow.x + 2*shadow.blur;
				 h += shadow.y + 2*shadow.blur;
			}

			var canvas = document.createElement('canvas');
			canvas.className = img.className;
			canvas.width = w;
			canvas.height = h;
			var ctx = canvas.getContext('2d');

			if(shadow) {
				ctx.shadowOffsetX = shadow.x;
				ctx.shadowOffsetY = shadow.y;
				ctx.shadowBlur    = shadow.blur;
				ctx.shadowColor   = shadow.color;
				ctx.translate(shadow.blur, shadow.blur);
			}

			this.roundRect(ctx, img.width, img.height, settings.radius);
			
			if(shadow) {
				ctx.fillStyle = '#888';
				ctx.fill();
			}

			ctx.clip();
			ctx.drawImage(img, 0, 0);
			
			var border = this.border;
			if(border) {
				ctx.lineWidth = border.width;
				ctx.strokeStyle = border.color;
				ctx.shadowColor = 'transparent';
				ctx.stroke();
			}

			$(img).replaceWith(canvas);
		},

		/**
		 * Draws a rounded rectangle
		 * @private
		 */
		roundRect: function(ctx, w, h, r) {
			ctx.beginPath();
			ctx.moveTo(0, r);
			ctx.quadraticCurveTo(0, 0, r, 0);
			ctx.lineTo(w - r, 0);
			ctx.quadraticCurveTo(w, 0, w, r);
			ctx.lineTo(w, h - r);
			ctx.quadraticCurveTo(w, h, w - r, h);
			ctx.lineTo(r, h);
			ctx.quadraticCurveTo(0, h, 0, h - r);
			ctx.lineTo(0, r);
			ctx.closePath();
		},
		
		/**
		 * Parses the border style for usable values
		 * @private
		 */
		getBorder: function() {
			var border = this.settings.border;
			if(border) {
				border = border.split(' ');
				return {
					width: parseInt(border[0], 10),
					style: border[1],
					color: border[2]
				};
			}
			return null;
		},
		
		/**
		 * Parses the shadow style for usable values
		 * @private
		 */
		getShadow: function() {
			var shadow = this.settings.shadow;
			if(shadow) {
				shadow = shadow.split(' ');
				return {
					x: parseInt(shadow[0], 10),
					y: parseInt(shadow[1], 10),
					blur: parseInt(shadow[2], 10),
					color: shadow[3]
				};
			}
			return null;
		}
	});

	LBi.namespace('RoundedImages', RoundedImages);
	
	/**
	 * Default settings for RoundedImages instances. 
	 * 
	 * @static
	 * @class LBi.RoundedImages.Defaults
	 */
	RoundedImages.Defaults = {
		/**
		 * Selector indicating which images are replaced.
		 * @property selector
		 * @type String
		 * @default 'img.rounded'
		 */
		selector: 'img.rounded',
		
		/**
		 * The border radius
		 * @property radius
		 * @type Number
		 * @default 7
		 */
		radius: 7,
		
		/**
		 * The border style, as a css border. Rgba color values are allowed, but don't use spaces within.
		 * @property border
		 * @type String
		 * @default '1px solid white'
		 */
		border: '1px solid white',

		/**
		 * The shadow style, as a css shadow. Rgba color values are allowed, but don't use spaces within.
		 * @property shadow
		 * @type String
		 * @default '1px 1px 3px gray'
		 */
		shadow: '1px 1px 3px gray'
	};

})(jQuery);
