/*! Widget: filter, html5 formatter functions - updated 7/17/2014 (v2.17.5) *//*
 * requires: tableSorter (FORK) 2.15+ and jQuery 1.4.3+
 *
 * html5Number (spinner)
 * html5Range (slider)
 * html5Color (color)
 */
/*jshint browser:true, jquery:true, unused:false */
/*global jQuery: false */
;(function($){
	'use strict';

	var ts = $.tablesorter || {},

	// compare option selector class name (jQuery selector)
	compareSelect = '.compare-select',

	tsff = ts.filterFormatter = $.extend( {}, ts.filterFormatter, {

		addCompare: function($cell, indx, options){
			if (options.compare && $.isArray(options.compare) && options.compare.length > 1) {
				var opt = '',
					compareSelectClass = [ compareSelect.slice(1), ' ' + compareSelect.slice(1), '' ],
					txt = options.cellText ? '<label class="' + compareSelectClass.join('-label') + indx + '">' + options.cellText + '</label>' : '';
				$.each(options.compare, function(i, c){
					opt += '<option ' + (options.selected === i ? 'selected' : '') + '>' + c + '</option>';
				});
				$cell
					.wrapInner('<div class="' + compareSelectClass.join('-wrapper') + indx + '" />')
					.prepend( txt + '<select class="' + compareSelectClass.join('') + indx + '" />' )
					.find('select')
					.append(opt);
			}
		},

		updateCompare : function($cell, $input, o) {
			var val = $input.val() || '',
				num = val.replace(/\s*?[><=]\s*?/g, ''),
				compare = val.match(/[><=]/g) || '';
			if (o.compare) {
				if ($.isArray(o.compare)){
					compare = (compare || []).join('') || o.compare[o.selected || 0];
				}
				$cell.find(compareSelect).val( compare );
			}
			return [ val, num ];
		},

		/**********************\
		HTML5 Number (spinner)
		\**********************/
		html5Number : function($cell, indx, def5Num) {
			var t, o = $.extend({
				value : 0,
				min : 0,
				max : 100,
				step : 1,
				delayed : true,
				disabled : false,
				addToggle : false,
				exactMatch : false,
				cellText : '',
				compare : '',
				skipTest: false
			}, def5Num),

			$input,
			// test browser for HTML5 range support
			$number = $('<input type="number" style="visibility:hidden;" value="test">').appendTo($cell),
			// test if HTML5 number is supported - from Modernizr
			numberSupported = o.skipTest || $number.attr('type') === 'number' && $number.val() !== 'test',
			$shcell = [],
			c = $cell.closest('table')[0].config,

			updateNumber = function(delayed, notrigger){
				var chkd = o.addToggle ? $cell.find('.toggle').is(':checked') : true,
					v = $cell.find('.number').val(),
					compare = ($.isArray(o.compare) ? $cell.find(compareSelect).val() || o.compare[ o.selected || 0] : o.compare) || '',
					searchType = c.$table[0].hasInitialized ? (delayed ? delayed : o.delayed) || '' : true;
				$input
					// add equal to the beginning, so we filter exact numbers
					.val( !o.addToggle || chkd ? (compare ? compare : o.exactMatch ? '=' : '') + v : '' )
					.trigger( notrigger ? '' : 'search', searchType ).end()
					.find('.number').val(v);
				if ($cell.find('.number').length) {
					$cell.find('.number')[0].disabled = (o.disabled || !chkd);
				}
				// update sticky header cell
				if ($shcell.length) {
					$shcell.find('.number').val(v)[0].disabled = (o.disabled || !chkd);
					$shcell.find(compareSelect).val(compare);
					if (o.addToggle) {
						$shcell.find('.toggle')[0].checked = chkd;
					}
				}
			};
			$number.remove();

			if (numberSupported) {
				t = o.addToggle ? '<div class="button"><input id="html5button' + indx + '" type="checkbox" class="toggle" />' +
					'<label for="html5button' + indx + '"></label></div>' : '';
				t += '<input class="number" type="number" min="' + o.min + '" max="' + o.max + '" value="' +
					o.value + '" step="' + o.step + '" />';
				// add HTML5 number (spinner)
				$cell
					.append(t + '<input type="hidden" />')
					.find('.toggle, .number').bind('change', function(){
						updateNumber();
					})
					.closest('thead').find('th[data-column=' + indx + ']')
					.addClass('filter-parsed') // get exact numbers from column
					// on reset
					.closest('table').bind('filterReset', function(){
						if ($.isArray(o.compare)) {
							$cell.add($shcell).find(compareSelect).val( o.compare[ o.selected || 0 ] );
						}
						// turn off the toggle checkbox
						if (o.addToggle) {
							$cell.find('.toggle')[0].checked = false;
							if ($shcell.length) {
								$shcell.find('.toggle')[0].checked = false;
							}
						}
						$cell.find('.number').val( o.value );
						setTimeout(function(){
							updateNumber();
						}, 0);
					});
				$input = $cell.find('input[type=hidden]').bind('change', function(){
					$cell.find('.number').val( this.value );
					updateNumber();
				});

				// update slider from hidden input, in case of saved filters
				c.$table.bind('filterFomatterUpdate', function(){
					var val = tsff.updateCompare($cell, $input, o)[0] || o.value;
					$cell.find('.number').val( ((val || '') + '').replace(/[><=]/g, '') );
					updateNumber(false, true);
					ts.filter.formatterUpdated($cell, indx);
				});

				if (o.compare) {
					// add compare select
					tsff.addCompare($cell, indx, o);
					$cell.find(compareSelect).bind('change', function(){
						updateNumber();
					});
				}

				// has sticky headers?
				c.$table.bind('stickyHeadersInit', function(){
					$shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty();
					$shcell
						.append(t)
						.find('.toggle, .number').bind('change', function(){
							$cell.find('.number').val( $(this).val() );
							updateNumber();
						});

					if (o.compare) {
						// add compare select
						tsff.addCompare($shcell, indx, o);
						$shcell.find(compareSelect).bind('change', function(){
							$cell.find(compareSelect).val( $(this).val() );
							updateNumber();
						});
					}

					updateNumber();
				});

				updateNumber();

			}

			return numberSupported ? $cell.find('input[type="hidden"]') : $('<input type="search">');
		},

		/**********************\
		HTML5 range slider
		\**********************/
		html5Range : function($cell, indx, def5Range) {
			var o = $.extend({
				value : 0,
				min : 0,
				max : 100,
				step : 1,
				delayed : true,
				valueToHeader : true,
				exactMatch : true,
				cellText : '',
				compare : '',
				allText : 'all',
				skipTest : false
			}, def5Range),

			$input,
			// test browser for HTML5 range support
			$range = $('<input type="range" style="visibility:hidden;" value="test">').appendTo($cell),
			// test if HTML5 range is supported - from Modernizr (but I left out the method to detect in Safari 2-4)
			// see https://github.com/Modernizr/Modernizr/blob/master/feature-detects/inputtypes.js
			rangeSupported = o.skipTest || $range.attr('type') === 'range' && $range.val() !== 'test',
			$shcell = [],
			c = $cell.closest('table')[0].config,

			updateRange = function(v, delayed, notrigger){
				/*jshint eqeqeq:false */
				// hidden input changes may include compare symbols
				v = ( typeof v === 'undefined' ? $input.val() : v ).toString().replace(/[<>=]/g, '') || o.value;
				var compare = ($.isArray(o.compare) ? $cell.find(compareSelect).val() || o.compare[ o.selected || 0] : o.compare) || '',
					t = ' (' + (compare ? compare + v : v == o.min ? o.allText : v) + ')',
					searchType =  c.$table[0].hasInitialized ? (delayed ? delayed : o.delayed) || '' : true;
				$cell.find('input[type=hidden]')
					// add equal to the beginning, so we filter exact numbers
					.val( ( compare ? compare + v : ( v == o.min ? '' : ( o.exactMatch ? '=' : '' ) + v ) ) )
					// ( val == o.min ? '' : val + (o.exactMatch ? '=' : ''))
					.trigger( notrigger ? '' : 'search', searchType ).end()
					.find('.range').val(v);
				// or add current value to the header cell, if desired
				$cell.closest('thead').find('th[data-column=' + indx + ']').find('.curvalue').html(t);
				// update sticky header cell
				if ($shcell.length) {
					$shcell
						.find('.range').val(v).end()
						.find(compareSelect).val( compare );
					$shcell.closest('thead').find('th[data-column=' + indx + ']').find('.curvalue').html(t);
				}
			};
			$range.remove();

			if (rangeSupported) {
				// add HTML5 range
				$cell
					.html('<input type="hidden"><input class="range" type="range" min="' + o.min + '" max="' + o.max + '" value="' + o.value + '" />')
					.closest('thead').find('th[data-column=' + indx + ']')
					.addClass('filter-parsed') // get exact numbers from column
					// add span to header for the current slider value
					.find('.tablesorter-header-inner').append('<span class="curvalue" />');
				// hidden filter update namespace trigger by filter widget
				$input = $cell.find('input[type=hidden]').bind('change' + c.namespace + 'filter', function(){
					/*jshint eqeqeq:false */
					var v = this.value,
						compare = ($.isArray(o.compare) ? $cell.find(compareSelect).val() || o.compare[ o.selected || 0] : o.compare) || '';
					if (v !== this.lastValue) {
						this.lastValue = ( compare ? compare + v : ( v == o.min ? '' : ( o.exactMatch ? '=' : '' ) + v ) );
						this.value = this.lastValue;
						updateRange( v );
					}
				});

				$cell.find('.range').bind('change', function(){
					updateRange( this.value );
				});

				// update spinner from hidden input, in case of saved filters
				c.$table.bind('filterFomatterUpdate', function(){
					var val = tsff.updateCompare($cell, $input, o)[0];
					$cell.find('.range').val( val );
					updateRange(val, false, true);
					ts.filter.formatterUpdated($cell, indx);
				});

				if (o.compare) {
					// add compare select
					tsff.addCompare($cell, indx, o);
					$cell.find(compareSelect).bind('change', function(){
						updateRange();
					});
				}

				// has sticky headers?
				c.$table.bind('stickyHeadersInit', function(){
					$shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty();
					$shcell
						.html('<input class="range" type="range" min="' + o.min + '" max="' + o.max + '" value="' + o.value + '" />')
						.find('.range').bind('change', function(){
							updateRange( $shcell.find('.range').val() );
						});
					updateRange();

					if (o.compare) {
						// add compare select
						tsff.addCompare($shcell, indx, o);
						$shcell.find(compareSelect).bind('change', function(){
							$cell.find(compareSelect).val( $(this).val() );
							updateRange();
						});
					}

				});

				// on reset
				$cell.closest('table').bind('filterReset', function(){
					if ($.isArray(o.compare)) {
						$cell.add($shcell).find(compareSelect).val( o.compare[ o.selected || 0 ] );
					}
					setTimeout(function(){
						updateRange(o.value, false, true);
					}, 0);
				});
				updateRange();

			}

			return rangeSupported ? $cell.find('input[type="hidden"]') : $('<input type="search">');
		},

		/**********************\
		HTML5 Color picker
		\**********************/
		html5Color: function($cell, indx, defColor) {
			var t, o = $.extend({
				value : '#000000',
				disabled : false,
				addToggle : true,
				exactMatch : true,
				valueToHeader : false,
				skipTest : false
			}, defColor),
			$input,
			// Add a hidden input to hold the range values
			$color = $('<input type="color" style="visibility:hidden;" value="test">').appendTo($cell),
			// test if HTML5 color is supported - from Modernizr
			colorSupported = o.skipTest || $color.attr('type') === 'color' && $color.val() !== 'test',
			$shcell = [],
			c = $cell.closest('table')[0].config,

			updateColor = function(v, notrigger) {
				v = ( typeof v === 'undefined' ? $input.val() : v ).toString().replace('=', '') || o.value;
				var chkd = true,
					t = ' (' + v + ')';
				if (o.addToggle) {
					chkd = $cell.find('.toggle').is(':checked');
				}
				if ($cell.find('.colorpicker').length) {
					$cell.find('.colorpicker').val(v)[0].disabled = (o.disabled || !chkd);
				}

				$input
					.val( chkd ? v + (o.exactMatch ? '=' : '') : '' )
					.trigger( !c.$table[0].hasInitialized || notrigger ? '' : 'search' );
				if (o.valueToHeader) {
					// add current color to the header cell
					$cell.closest('thead').find('th[data-column=' + indx + ']').find('.curcolor').html(t);
				} else {
					// current color to span in cell
					$cell.find('.currentColor').html(t);
				}

				// update sticky header cell
				if ($shcell.length) {
					$shcell.find('.colorpicker').val(v)[0].disabled = (o.disabled || !chkd);
					if (o.addToggle) {
						$shcell.find('.toggle')[0].checked = chkd;
					}
					if (o.valueToHeader) {
						// add current color to the header cell
						$shcell.closest('thead').find('th[data-column=' + indx + ']').find('.curcolor').html(t);
					} else {
						// current color to span in cell
						$shcell.find('.currentColor').html(t);
					}
				}
			};
			$color.remove();

			if (colorSupported) {
				t = '' + indx + Math.round(Math.random() * 100);
				// add HTML5 color picker
				t = '<div class="color-controls-wrapper">' +
					(o.addToggle ? '<div class="button"><input id="colorbutton' + t + '" type="checkbox" class="toggle" /><label for="colorbutton' +
					t + '"></label></div>' : '') +
					'<input type="hidden"><input class="colorpicker" type="color" />' +
					(o.valueToHeader ? '' : '<span class="currentColor">(#000000)</span>') + '</div>';
				$cell.html(t);
				// add span to header for the current color value - only works if the line in the updateColor() function is also un-commented out
				if (o.valueToHeader) {
					$cell.closest('thead').find('th[data-column=' + indx + ']').find('.tablesorter-header-inner').append('<span class="curcolor" />');
				}

				$cell.find('.toggle, .colorpicker').bind('change', function(){
					updateColor( $cell.find('.colorpicker').val() );
				});

				// hidden filter update namespace trigger by filter widget
				$input = $cell.find('input[type=hidden]').bind('change' + c.namespace + 'filter', function(){
					updateColor( this.value );
				});

				// update slider from hidden input, in case of saved filters
				c.$table.bind('filterFomatterUpdate', function(){
					updateColor( $input.val(), true );
					ts.filter.formatterUpdated($cell, indx);
				});

				// on reset
				$cell.closest('table').bind('filterReset', function(){
					// just turn off the colorpicker
					if (o.addToggle) {
						$cell.find('.toggle')[0].checked = false;
					}
					// delay needed because default color needs to be set in the filter
					// there is no compare option here, so if addToggle = false,
					// default color is #000000 (even with no value set)
					setTimeout(function(){
						updateColor();
					}, 0);
				});

				// has sticky headers?
				c.$table.bind('stickyHeadersInit', function(){
					$shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx);
					$shcell
						.html(t)
						.find('.toggle, .colorpicker').bind('change', function(){
							updateColor( $shcell.find('.colorpicker').val() );
						});
					updateColor( $shcell.find('.colorpicker').val() );
				});

				updateColor( o.value );
			}
			return colorSupported ? $cell.find('input[type="hidden"]') : $('<input type="search">');
		}

	});

})(jQuery);
