/**
 * jQuery Fi Checkbox Component
 * 
 * Replaces checkboxes and radio buttons with a styled span tag. The change 
 * event on the checkbox is monitored and the span style will be toggled 
 * whenever a change is detected. This has been tested using both implicit 
 * and explicit label elements in Firefox, Webkit, and IE.
 * 
 * @author David Lindkvist (Fi)
 * @author Joseph Schmitt (Fi)
 * @author Kevin Sweeney (Fi)
 * @version 1.0
 * 
 * -----------------------------------------------------------------------------
 * OPTIONS
 * -----------------------------------------------------------------------------
 * 
 * 1. {String}		styleClass			CSS class name of default state
 * 2. {String}		checkedStyleClass	CSS class name of selected state
 * 
 * -----------------------------------------------------------------------------
 * EXAMPLES
 * -----------------------------------------------------------------------------
 * 
 * HTML:
 * <!-- Implicit Labels -->
 * <label>
 * 		<input name="implicitRadioGroup" type="radio" />
 * 		Option A
 * </label>
 * <label>
 * 		<input name="implicitRadioGroup" type="radio" />
 * 		Option B
 * </label>
 * 		
 * <!-- Explicit Labels -->
 * <input type="checkbox" name="explicitCheckA" id="explicitCheckA" />
 * <label for="explicitCheckA">Checkbox A (unchecked)</label>
 * 
 * 
 * Javascript:
 * $('input[type="checkbox"]').checkbox();
 * $('input[type="radio"]').checkbox();
 * 
 * -----------------------------------------------------------------------------
 * TODO LIST
 * -----------------------------------------------------------------------------
 * 
 * 1. Test "disabled" attribute logic in all browsers.
 * 
 */

(function ($) {
	
	$.fn.checkbox = function (options) {
	  
		// Build main options before element iteration
		var opts = $.extend({}, $.fn.checkbox.defaults, options);
		var $this = $(this);
		
		/**
		 * Replace a checkbox
		 * @param {Object} checkbox
		 */
		function replace(checkbox) {
			
			// Add fake styled checkbox
			var replacement = $('<span/>');
			replacement.addClass(opts.styleClass);
			replacement.attr('for', checkbox.attr('id'));
			checkbox.after(replacement);
			
			// Hide original checkbox
			checkbox.hide();
			
			// Init replacement with current value 
			if (checkbox.attr('checked')) {
				replacement.addClass(opts.checkedStyleClass);
			}
			
			// Set click targets for labls dependeding on whether they are 
			// implicit (the label element wraps the input element) or 
			// explicit (the label has a "for" attribute bound to the "id" 
			// attribute of the input)
			var clickTarget;
			if (checkbox.parent('label').length > 0) {
				clickTarget = checkbox.parent('label');
			}
			else {
				clickTarget = $(checkbox).parents('form').find('*[for="' + checkbox.attr('id') + '"]');
			}
			
			clickTarget.click(function (e) {
				e.stopImmediatePropagation();
				e.preventDefault();
				clickListener(replacement, checkbox);
				return false;
			});
			
			/**
			 * Add change listener to original checkbox - this triggers the change of style
			 * @param {Event} e - Change event object
			 */
			checkbox.change(function (e) {
				
				var selector = $(e.target).parents('form').find('input[name="' + checkbox.attr('name') + '"]' + ' + span.' + opts.checkedStyleClass);
				
				$(selector).removeClass(opts.checkedStyleClass);
				
				// Toggle checked styleClass of replacement				
				if (checkbox.attr('checked')) {
					if (replacement.hasClass(opts.checkedStyleClass) === false) {
						replacement.addClass(opts.checkedStyleClass);
					}
				}
				else {
					replacement.removeClass(opts.checkedStyleClass);
				}
				
			});
			
		}	
		
		/**
		 * Updates the actual HTML element and gives focus to the replacement
		 * @param {HTMLElement} replacement - The skinned span element
		 * @param {HTMLElement} checkbox - The checkbox or radio element to trigger
		 */
		function clickListener(replacement, checkbox) {
			
			var notDisabled			= checkbox.attr('disabled') === false;
			var notSelectedRadio	= true;
			
			if ((checkbox.attr('type') === 'radio') && (checkbox.attr('checked') === true)) {
				notSelectedRadio = false;
			}
			
			// Ignore clicks if checkbox is disabled or is a radio button that 
			// is already selected
			if (notDisabled && notSelectedRadio) {
				toggleCheckbox(checkbox);
				checkbox.change();
				replacement.focus();
			}
			
		}
		
		/**
		 * Updates the checkbox's value
		 * @param {HTMLElement} checkbox - The checkbox or radio element to trigger
		 */
		function toggleCheckbox(checkbox) {
			if (checkbox.attr('checked')) {
				checkbox.attr('checked', false);
			} 
			else {
				checkbox.attr('checked', true);
			}
		}
		
		// Iterate and reformat each matched element
		return this.each(function () {
			var checkbox = $(this);	
			replace(checkbox, opts);
		});
		
	};
	
	// Expose plugin defaults
	$.fn.checkbox.defaults = {
		styleClass: 'styledCheckbox',
		checkedStyleClass: 'styledCheckboxChecked'
	};
	
})(jQuery);