/**
 * Commonly used helpers.
 *
 * Needs jquery 1.4
 *
 * All plugins have dfault options, which may be set global and / or overwritten at each call.
 *
 */
(function($) {


	// >>> formInputFilter plugin >>>
	/**
	 * formInputFilter restricts input in a form field to a defined set of characters.
	 *
	 * Mostly you want to use the specialized versions which are preconfigured for the most common use cases.
	 */
	$.fn.formInputFilter = function(options)	{
		var opts	= $.extend({
			allowedChars: ''
		}, options);
		var allowed = opts.allowedChars.split('');

		return this.each(function(){
			$(this).keypress(function(e){
				var key = String.fromCharCode(e.charCode ? e.charCode : e.which);
				if (allowed.indexOf(key) === -1) {
					e.preventDefault();
				}
			});
		});
	};

	// >>> formInputFilterAlpha >>>
	/**
	 * Restricts input to alphabetic characters.
	 */
	$.fn.formInputFilterAlpha = function(options)	{
		var opts	= $.extend({}, $.fn.formInputFilterAlpha.defaults, options);		
		return this.each(function(){
			$(this).formInputFilter(opts);
		});
	};

	// formInputFilterAlpha default options
	$.fn.formInputFilterAlpha.defaults = {
		allowedChars:	'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
	};
	// <<< formInputFilterAlpha <<<

	
	// >>> formInputFilterNumeric >>>
	/**
	 * Restricts input to numeric characters.
	 */
	$.fn.formInputFilterNumeric = function(options)	{
		var opts	= $.extend({}, $.fn.formInputFilterNumeric.defaults, options);
		return this.each(function(){
			$(this).formInputFilter(opts);
		});
	};

	// formInputFilterAlpha default options
	$.fn.formInputFilterNumeric.defaults = {
		allowedChars:	'0123456789'
	};
	// <<< formInputFilterNumeric <<<
	
	// >>> formInputFilterDecimal >>>
	/**
	 * Restricts input to numeric characters including separators.
	 */
	$.fn.formInputFilterDecimal = function(options)	{
		var opts	= $.extend({}, $.fn.formInputFilterDecimal.defaults, options);
		return this.each(function(){
			$(this).formInputFilter(opts);
		});
	};

	// formInputFilterAlpha default options
	$.fn.formInputFilterDecimal.defaults = {
		allowedChars:	'0123456789.,'
	};
	// <<< formInputFilterNumeric <<<
	

	// >>> formInputFilterAlphaNumeric >>>
	/**
	 * Restricts input to alphanumeric characters.
	 */
	$.fn.formInputFilterAlphaNumeric = function(options)	{
		var opts	= $.extend({}, $.fn.formInputFilterAlphaNumeric.defaults, options);
		return this.each(function(){
			$(this).formInputFilter(opts);
		});
	};

	// formInputFilterAlpha default options
	$.fn.formInputFilterAlphaNumeric.defaults = {
		allowedChars:	'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
	};
	
	// <<< formInputFilterNumeric <<<


	// <<< formInputFilter plugin <<<



	// >>> formInputBlur plugin >>>
	/**
	 * formInputBlur switches a text an a class depending on an input elements focus/blur state.
	 */
	$.fn.formInputBlur = function(options)	{
		var opts	= $.extend({}, $.fn.formInputBlur.defaults, options);

		return this.each(function(){
			if ($(this).val().length !== 0) {
				opts.blurText = $(this).val();
			}
			$(this).val(opts.blurText).removeClass(opts.focusClass).addClass(opts.blurClass);
			$(this).focus(function(){
				if ($(this).val() === opts.blurText) {
					$(this).val(opts.focusText);
				}
				$(this).removeClass(opts.blurClass).addClass(opts.focusClass);
			});
			$(this).blur(function(){
				if ($(this).val() === opts.focusText) {
					$(this).val(opts.blurText);
				}				
				$(this).removeClass(opts.focusClass).addClass(opts.blurClass);
			});
		});
	};

	// formInputBlur default options
	$.fn.formInputBlur.defaults = {
		blurText:	'',
		blurClass:	'',
		focusText:	'',
		focusClass: ''
	};
	// <<< formInputBlur plugin <<<

	// >>> tableHighlight plugin >>>

	/**
	 * Highlights table rows on hover.
	 */
	$.fn.tableHighlight = function(options)	{
		var opts	= $.extend({}, $.fn.tableHighlight.defaults, options);

		return this.each(function(){
			$(this).find('tr').hover(
				function(){
					$(this).addClass(opts.hoverClass);
				},
				function(){
					$(this).removeClass(opts.hoverClass);
				}
				).click(function(){
				if ($(this).hasClass(opts.clickClass)) {
					$(this).removeClass(opts.clickClass)
				} else {
					$(this).addClass(opts.clickClass)
				}
			});
		});
	};

	// tableHighlight default options
	$.fn.tableHighlight.defaults = {
		hoverClass: 'rowHover',
		clickClass: 'rowClick'
	};
	
// <<< tableHighlight plugin <<<



	jQuery.fn.selectToUISlider = function(settings){
		var selects = jQuery(this);

		//accessible slider options
		var options = jQuery.extend({
			labels: 3, //number of visible labels
			tooltip: true, //show tooltips, boolean
			tooltipSrc: 'text',//accepts 'value' as well
			labelSrc: 'value',//accepts 'value' as well	,
			sliderOptions: null
		}, settings);


		//handle ID attrs - selects each need IDs for handles to find them
		var handleIds = (function(){
			var tempArr = [];
			selects.each(function(){
				tempArr.push('handle_'+jQuery(this).attr('id'));
			});
			return tempArr;
		})();

		//array of all option elements in select element (ignores optgroups)
		var selectOptions = (function(){
			var opts = [];
			selects.eq(0).find('option').each(function(){
				opts.push({
					value: jQuery(this).attr('value'),
					text: jQuery(this).text()
				});
			});
			return opts;
		})();

		//array of opt groups if present
		var groups = (function(){
			if(selects.eq(0).find('optgroup').size()>0){
				var groupedData = [];
				selects.eq(0).find('optgroup').each(function(i){
					groupedData[i] = {};
					groupedData[i].label = jQuery(this).attr('label');
					groupedData[i].options = [];
					jQuery(this).find('option').each(function(){
						groupedData[i].options.push({text: jQuery(this).text(), value: jQuery(this).attr('value')});
					});
				});
				return groupedData;
			}
			else return null;
		})();

		//check if obj is array
		function isArray(obj) {
			return obj.constructor == Array;
		}
		//return tooltip text from option index
		function ttText(optIndex){
			return (options.tooltipSrc == 'text') ? selectOptions[optIndex].text : selectOptions[optIndex].value;
		}

		//plugin-generated slider options (can be overridden)
		var sliderOptions = {
			step: 1,
			min: 0,
			orientation: 'horizontal',
			max: selectOptions.length-1,
			range: selects.length > 1,//multiple select elements = true
			slide: function(e, ui) {//slide function
					var thisHandle = jQuery(ui.handle);
					//handle feedback
					var textval = ttText(ui.value);
					thisHandle
						.attr('aria-valuetext', textval)
						.attr('aria-valuenow', ui.value)
						.find('.ui-slider-tooltip .ttContent')
							.text( textval );

					//control original select menu
					var currSelect = jQuery('#' + thisHandle.attr('id').split('handle_')[1]);
					currSelect.find('option').eq(ui.value).attr('selected', 'selected');
			},
			values: (function(){
				var values = [];
				selects.each(function(){
					values.push( jQuery(this).get(0).selectedIndex );
				});
				return values;
			})()
		};

		//slider options from settings
		options.sliderOptions = (settings) ? jQuery.extend(sliderOptions, settings.sliderOptions) : sliderOptions;

		//select element change event
		selects.bind('change keyup click', function(){
			var thisIndex = jQuery(this).get(0).selectedIndex;
			var thisHandle = jQuery('#handle_'+ jQuery(this).attr('id'));
			var handleIndex = thisHandle.data('handleNum');
			thisHandle.parents('.ui-slider:eq(0)').slider("values", handleIndex, thisIndex);
		});


		//create slider component div
		var sliderComponent = jQuery('<div></div>');

		//CREATE HANDLES
		selects.each(function(i){
			var hidett = '';

			//associate label for ARIA
			var thisLabel = jQuery('label[for=' + jQuery(this).attr('id') +']');
			//labelled by aria doesn't seem to work on slider handle. Using title attr as backup
			var labelText = (thisLabel.size()>0) ? 'Slider control for '+ thisLabel.text()+'' : '';
			var thisLabelId = thisLabel.attr('id') || thisLabel.attr('id', 'label_'+handleIds[i]).attr('id');


			if( options.tooltip == false ){hidett = ' style="display: none;"';}
			jQuery('<a '+
					'href="#" tabindex="0" '+
					'id="'+handleIds[i]+'" '+
					'class="ui-slider-handle" '+
					'role="slider" '+
					'aria-labelledby="'+thisLabelId+'" '+
					'aria-valuemin="'+options.sliderOptions.min+'" '+
					'aria-valuemax="'+options.sliderOptions.max+'" '+
					'aria-valuenow="'+options.sliderOptions.values[i]+'" '+
					'aria-valuetext="'+ttText(options.sliderOptions.values[i])+'" '+
				'><span class="screenReaderContext">'+labelText+'</span>'+
				'<span class="ui-slider-tooltip ui-widget-content ui-corner-all"'+ hidett +'><span class="ttContent"></span>'+
					'<span class="ui-tooltip-pointer-down ui-widget-content"><span class="ui-tooltip-pointer-down-inner"></span></span>'+
				'</span></a>')
				.data('handleNum',i)
				.appendTo(sliderComponent);
		});

		//CREATE SCALE AND TICS

		//write dl if there are optgroups
		if(groups) {
			var inc = 0;
			var scale = sliderComponent.append('<dl class="ui-slider-scale ui-helper-reset" role="presentation"></dl>').find('.ui-slider-scale:eq(0)');
			jQuery(groups).each(function(h){
				scale.append('<dt style="width: '+ (100/groups.length).toFixed(2) +'%' +'; left:'+ (h/(groups.length-1) * 100).toFixed(2)  +'%' +'"><span>'+this.label+'</span></dt>');//class name becomes camelCased label
				var groupOpts = this.options;
				jQuery(this.options).each(function(i){
					var style = (inc == selectOptions.length-1 || inc == 0) ? 'style="display: none;"' : '' ;
					var labelText = (options.labelSrc == 'text') ? groupOpts[i].text : groupOpts[i].value;
					scale.append('<dd style="left:'+ leftVal(inc) +'"><span class="ui-slider-label">'+ labelText +'</span><span class="ui-slider-tic ui-widget-content"'+ style +'></span></dd>');
					inc++;
				});
			});
		}
		//write ol
		else {
			var scale = sliderComponent.append('<ol class="ui-slider-scale ui-helper-reset" role="presentation"></ol>').find('.ui-slider-scale:eq(0)');
			jQuery(selectOptions).each(function(i){
				var style = (i == selectOptions.length-1 || i == 0) ? 'style="display: none;"' : '' ;
				var labelText = (options.labelSrc == 'text') ? this.text : this.value;
				scale.append('<li style="left:'+ leftVal(i) +'"><span class="ui-slider-label">'+ labelText +'</span><span class="ui-slider-tic ui-widget-content"'+ style +'></span></li>');
			});
		}

		function leftVal(i){
			return (i/(selectOptions.length-1) * 100).toFixed(2)  +'%';

		}




		//show and hide labels depending on labels pref
		//show the last one if there are more than 1 specified
		if(options.labels > 1) sliderComponent.find('.ui-slider-scale li:last span.ui-slider-label, .ui-slider-scale dd:last span.ui-slider-label').addClass('ui-slider-label-show');

		//set increment
		var increm = Math.max(1, Math.round(selectOptions.length / options.labels));
		//show em based on inc
		for(var j=0; j<selectOptions.length; j+=increm){
			if((selectOptions.length - j) > increm){//don't show if it's too close to the end label
				sliderComponent.find('.ui-slider-scale li:eq('+ j +') span.ui-slider-label, .ui-slider-scale dd:eq('+ j +') span.ui-slider-label').addClass('ui-slider-label-show');
			}
		}

		//style the dt's
		sliderComponent.find('.ui-slider-scale dt').each(function(i){
			jQuery(this).css({
				'left': ((100 /( groups.length))*i).toFixed(2) + '%'
			});
		});


		//inject and return
		sliderComponent
		.insertAfter(jQuery(this).eq(this.length-1))
		.slider(options.sliderOptions)
		.attr('role','application')
		.find('.ui-slider-label')
		.each(function(){
			jQuery(this).css('marginLeft', -jQuery(this).width()/2);
		});

		//update tooltip arrow inner color
		sliderComponent.find('.ui-tooltip-pointer-down-inner').each(function(){
					var bWidth = jQuery('.ui-tooltip-pointer-down-inner').css('borderTopWidth');
					var bColor = jQuery(this).parents('.ui-slider-tooltip').css('backgroundColor')
					jQuery(this).css('border-top', bWidth+' solid '+bColor);
		});

		var values = sliderComponent.slider('values');

		if(isArray(values)){
			jQuery(values).each(function(i){
				sliderComponent.find('.ui-slider-tooltip .ttContent').eq(i).text( ttText(this) );
			});
		}
		else {
			sliderComponent.find('.ui-slider-tooltip .ttContent').eq(0).text( ttText(values) );
		}

		return this;
	}



})(jQuery);

