/ajax/libs/jquery.tablesorter/2.15.7/js/jquery.tablesorter.widgets-filter-formatter.js

https://gitlab.com/Mirros/cdnjs · JavaScript · 1130 lines · 836 code · 113 blank · 181 comment · 170 complexity · 7062e9133eec626c5722c4602bfadcaa MD5 · raw file

  1. /*! Filter widget formatter functions - updated 3/9/2014 (v2.15.7)
  2. * requires: tableSorter 2.15+ and jQuery 1.4.3+
  3. *
  4. * uiSpinner (jQuery UI spinner)
  5. * uiSlider (jQuery UI slider)
  6. * uiRange (jQuery UI range slider)
  7. * uiDateCompare (jQuery UI datepicker; 1 input)
  8. * uiDatepicker (jQuery UI datepicker; 2 inputs, filter range)
  9. * html5Number (spinner)
  10. * html5Range (slider)
  11. * html5Color (color)
  12. */
  13. /*jshint browser:true, jquery:true, unused:false */
  14. /*global jQuery: false */
  15. ;(function($){
  16. "use strict";
  17. var ts = $.tablesorter || {},
  18. // compare option selector class name (jQuery selector)
  19. compareSelect = '.compare-select',
  20. tsff = ts.filterFormatter = {
  21. addCompare: function($cell, indx, options){
  22. if (options.compare && $.isArray(options.compare) && options.compare.length > 1) {
  23. var opt = '',
  24. compareSelectClass = [ compareSelect.slice(1), ' ' + compareSelect.slice(1), '' ],
  25. txt = options.cellText ? '<label class="' + compareSelectClass.join('-label') + indx + '">' + options.cellText + '</label>' : '';
  26. $.each(options.compare, function(i, c){
  27. opt += '<option ' + (options.selected === i ? 'selected' : '') + '>' + c + '</option>';
  28. });
  29. $cell
  30. .wrapInner('<div class="' + compareSelectClass.join('-wrapper') + indx + '" />')
  31. .prepend( txt + '<select class="' + compareSelectClass.join('') + indx + '" />' )
  32. .find('select')
  33. .append(opt);
  34. }
  35. },
  36. updateCompare : function($cell, $input, o) {
  37. var val = $input.val() || '',
  38. num = val.replace(/\s*?[><=]\s*?/g, ''),
  39. compare = val.match(/[><=]/g) || '';
  40. if (o.compare) {
  41. if ($.isArray(o.compare)){
  42. compare = (compare || []).join('') || o.compare[o.selected || 0];
  43. }
  44. $cell.find(compareSelect).val( compare );
  45. }
  46. return [ val, num ];
  47. },
  48. /**********************\
  49. jQuery UI Spinner
  50. \**********************/
  51. uiSpinner: function($cell, indx, spinnerDef) {
  52. var o = $.extend({
  53. // filter formatter options
  54. delayed : true,
  55. addToggle : true,
  56. exactMatch : true,
  57. value : 1,
  58. cellText : '',
  59. compare : '',
  60. // include ANY jQuery UI spinner options below
  61. min : 0,
  62. max : 100,
  63. step : 1,
  64. disabled : false
  65. }, spinnerDef ),
  66. // Add a hidden input to hold the range values
  67. $input = $('<input class="filter" type="hidden">')
  68. .appendTo($cell)
  69. // hidden filter update namespace trigger by filter widget
  70. .bind('change' + c.namespace + 'filter', function(){
  71. updateSpinner({ value: this.value, delayed: false });
  72. }),
  73. $shcell = [],
  74. c = $cell.closest('table')[0].config,
  75. // this function updates the hidden input and adds the current values to the header cell text
  76. updateSpinner = function(ui, notrigger) {
  77. var chkd = true, state,
  78. // ui is not undefined on create
  79. v = ui && ui.value && ts.formatFloat((ui.value + '').replace(/[><=]/g,'')) ||
  80. $cell.find('.spinner').val() || o.value,
  81. compare = ($.isArray(o.compare) ? $cell.find(compareSelect).val() || o.compare[ o.selected || 0] : o.compare) || '',
  82. searchType = ui && typeof ui.delayed === 'boolean' ? ui.delayed : c.$table[0].hasInitialized ? o.delayed : true;
  83. if (o.addToggle) {
  84. chkd = $cell.find('.toggle').is(':checked');
  85. }
  86. state = o.disabled || !chkd ? 'disable' : 'enable';
  87. $cell.find('.filter')
  88. // add equal to the beginning, so we filter exact numbers
  89. .val( chkd ? (compare ? compare : o.exactMatch ? '=' : '') + v : '' )
  90. .trigger( notrigger ? '' : 'search', searchType ).end()
  91. .find('.spinner').spinner(state).val(v);
  92. // update sticky header cell
  93. if ($shcell.length) {
  94. $shcell
  95. .find('.spinner').spinner(state).val(v).end()
  96. .find(compareSelect).val( compare );
  97. if (o.addToggle) {
  98. $shcell.find('.toggle')[0].checked = chkd;
  99. }
  100. }
  101. };
  102. // add callbacks; preserve added callbacks
  103. o.oldcreate = o.create;
  104. o.oldspin = o.spin;
  105. o.create = function(event, ui) {
  106. updateSpinner(); // ui is an empty object on create
  107. if (typeof o.oldcreate === 'function') { o.oldcreate(event, ui); }
  108. };
  109. o.spin = function(event, ui) {
  110. updateSpinner(ui);
  111. if (typeof o.oldspin === 'function') { o.oldspin(event, ui); }
  112. };
  113. if (o.addToggle) {
  114. $('<div class="button"><input id="uispinnerbutton' + indx + '" type="checkbox" class="toggle" />' +
  115. '<label for="uispinnerbutton' + indx + '"></label></div>')
  116. .appendTo($cell)
  117. .find('.toggle')
  118. .bind('change', function(){
  119. updateSpinner();
  120. });
  121. }
  122. // make sure we use parsed data
  123. $cell.closest('thead').find('th[data-column=' + indx + ']').addClass('filter-parsed');
  124. // add a jQuery UI spinner!
  125. $('<input class="spinner spinner' + indx + '" />')
  126. .val(o.value)
  127. .appendTo($cell)
  128. .spinner(o)
  129. .bind('change keyup', function(){
  130. updateSpinner();
  131. });
  132. // update spinner from hidden input, in case of saved filters
  133. c.$table.bind('filterFomatterUpdate', function(){
  134. var val = tsff.updateCompare($cell, $input, o)[0];
  135. $cell.find('.spinner').val( val );
  136. updateSpinner({ value: val }, true);
  137. });
  138. if (o.compare) {
  139. // add compare select
  140. tsff.addCompare($cell, indx, o);
  141. $cell.find(compareSelect).bind('change', function(){
  142. updateSpinner();
  143. });
  144. }
  145. // has sticky headers?
  146. c.$table.bind('stickyHeadersInit', function(){
  147. $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty();
  148. if (o.addToggle) {
  149. $('<div class="button"><input id="stickyuispinnerbutton' + indx + '" type="checkbox" class="toggle" />' +
  150. '<label for="stickyuispinnerbutton' + indx + '"></label></div>')
  151. .appendTo($shcell)
  152. .find('.toggle')
  153. .bind('change', function(){
  154. $cell.find('.toggle')[0].checked = this.checked;
  155. updateSpinner();
  156. });
  157. }
  158. // add a jQuery UI spinner!
  159. $('<input class="spinner spinner' + indx + '" />')
  160. .val(o.value)
  161. .appendTo($shcell)
  162. .spinner(o)
  163. .bind('change keyup', function(){
  164. $cell.find('.spinner').val( this.value );
  165. updateSpinner();
  166. });
  167. if (o.compare) {
  168. // add compare select
  169. tsff.addCompare($shcell, indx, o);
  170. $shcell.find(compareSelect).bind('change', function(){
  171. $cell.find(compareSelect).val( $(this).val() );
  172. updateSpinner();
  173. });
  174. }
  175. });
  176. // on reset
  177. c.$table.bind('filterReset', function(){
  178. if ($.isArray(o.compare)) {
  179. $cell.add($shcell).find(compareSelect).val( o.compare[ o.selected || 0 ] );
  180. }
  181. // turn off the toggle checkbox
  182. if (o.addToggle) {
  183. $cell.find('.toggle')[0].checked = false;
  184. }
  185. $cell.find('.spinner').spinner('value', o.value);
  186. setTimeout(function(){
  187. updateSpinner();
  188. }, 0);
  189. });
  190. updateSpinner();
  191. return $input;
  192. },
  193. /**********************\
  194. jQuery UI Slider
  195. \**********************/
  196. uiSlider: function($cell, indx, sliderDef) {
  197. var o = $.extend({
  198. // filter formatter options
  199. delayed : true,
  200. valueToHeader : false,
  201. exactMatch : true,
  202. cellText : '',
  203. compare : '',
  204. allText : 'all',
  205. // include ANY jQuery UI spinner options below
  206. // except values, since this is a non-range setup
  207. value : 0,
  208. min : 0,
  209. max : 100,
  210. step : 1,
  211. range : "min"
  212. }, sliderDef ),
  213. // Add a hidden input to hold the range values
  214. $input = $('<input class="filter" type="hidden">')
  215. .appendTo($cell)
  216. // hidden filter update namespace trigger by filter widget
  217. .bind('change' + c.namespace + 'filter', function(){
  218. updateSlider({ value: this.value });
  219. }),
  220. $shcell = [],
  221. c = $cell.closest('table')[0].config,
  222. // this function updates the hidden input and adds the current values to the header cell text
  223. updateSlider = function(ui, notrigger) {
  224. // ui is not undefined on create
  225. var v = typeof ui !== "undefined" ? ts.formatFloat((ui.value + '').replace(/[><=]/g,'')) || o.value : o.value,
  226. val = o.compare ? v : v === o.min ? o.allText : v,
  227. compare = ($.isArray(o.compare) ? $cell.find(compareSelect).val() || o.compare[ o.selected || 0] : o.compare) || '',
  228. result = compare + val,
  229. searchType = ui && typeof ui.delayed === 'boolean' ? ui.delayed : c.$table[0].hasInitialized ? o.delayed : true;
  230. if (o.valueToHeader) {
  231. // add range indication to the header cell above!
  232. $cell.closest('thead').find('th[data-column=' + indx + ']').find('.curvalue').html(' (' + result + ')');
  233. } else {
  234. // add values to the handle data-value attribute so the css tooltip will work properly
  235. $cell.find('.ui-slider-handle').addClass('value-popup').attr('data-value', result);
  236. }
  237. // update the hidden input;
  238. // ****** ADD AN EQUAL SIGN TO THE BEGINNING! <- this makes the slide exactly match the number ******
  239. // when the value is at the minimum, clear the hidden input so all rows will be seen
  240. $cell.find('.filter')
  241. .val( ( compare ? compare + v : v === o.min ? '' : (o.exactMatch ? '=' : '') + v ) )
  242. .trigger( notrigger ? '' : 'search', searchType ).end()
  243. .find('.slider').slider('value', v);
  244. // update sticky header cell
  245. if ($shcell.length) {
  246. $shcell
  247. .find(compareSelect).val( compare ).end()
  248. .find('.slider').slider('value', v);
  249. if (o.valueToHeader) {
  250. $shcell.closest('thead').find('th[data-column=' + indx + ']').find('.curvalue').html(' (' + result + ')');
  251. } else {
  252. $shcell.find('.ui-slider-handle').addClass('value-popup').attr('data-value', result);
  253. }
  254. }
  255. };
  256. $cell.closest('thead').find('th[data-column=' + indx + ']').addClass('filter-parsed');
  257. // add span to header for value - only works if the line in the updateSlider() function is also un-commented out
  258. if (o.valueToHeader) {
  259. $cell.closest('thead').find('th[data-column=' + indx + ']').find('.tablesorter-header-inner').append('<span class="curvalue" />');
  260. }
  261. // add callbacks; preserve added callbacks
  262. o.oldcreate = o.create;
  263. o.oldslide = o.slide;
  264. o.create = function(event, ui) {
  265. updateSlider(); // ui is an empty object on create
  266. if (typeof o.oldcreate === 'function') { o.oldcreate(event, ui); }
  267. };
  268. o.slide = function(event, ui) {
  269. updateSlider(ui);
  270. if (typeof o.oldslide === 'function') { o.oldslide(event, ui); }
  271. };
  272. // add a jQuery UI slider!
  273. $('<div class="slider slider' + indx + '"/>')
  274. .appendTo($cell)
  275. .slider(o);
  276. // update slider from hidden input, in case of saved filters
  277. c.$table.bind('filterFomatterUpdate', function(){
  278. var val = tsff.updateCompare($cell, $input, o)[0];
  279. $cell.find('.slider').slider('value', val );
  280. updateSlider({ value: val }, false);
  281. });
  282. if (o.compare) {
  283. // add compare select
  284. tsff.addCompare($cell, indx, o);
  285. $cell.find(compareSelect).bind('change', function(){
  286. updateSlider({ value: $cell.find('.slider').slider('value') });
  287. });
  288. }
  289. // on reset
  290. c.$table.bind('filterReset', function(){
  291. if ($.isArray(o.compare)) {
  292. $cell.add($shcell).find(compareSelect).val( o.compare[ o.selected || 0 ] );
  293. }
  294. setTimeout(function(){
  295. updateSlider({ value: o.value });
  296. }, 0);
  297. });
  298. // has sticky headers?
  299. c.$table.bind('stickyHeadersInit', function(){
  300. $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty();
  301. // add a jQuery UI slider!
  302. $('<div class="slider slider' + indx + '"/>')
  303. .val(o.value)
  304. .appendTo($shcell)
  305. .slider(o)
  306. .bind('change keyup', function(){
  307. $cell.find('.slider').slider('value', this.value );
  308. updateSlider();
  309. });
  310. if (o.compare) {
  311. // add compare select
  312. tsff.addCompare($shcell, indx, o);
  313. $shcell.find(compareSelect).bind('change', function(){
  314. $cell.find(compareSelect).val( $(this).val() );
  315. updateSlider();
  316. });
  317. }
  318. });
  319. return $input;
  320. },
  321. /*************************\
  322. jQuery UI Range Slider (2 handles)
  323. \*************************/
  324. uiRange: function($cell, indx, rangeDef) {
  325. var o = $.extend({
  326. // filter formatter options
  327. delayed : true,
  328. valueToHeader : false,
  329. // include ANY jQuery UI spinner options below
  330. // except value, since this one is range specific)
  331. values : [0, 100],
  332. min : 0,
  333. max : 100,
  334. range : true
  335. }, rangeDef ),
  336. // Add a hidden input to hold the range values
  337. $input = $('<input class="filter" type="hidden">')
  338. .appendTo($cell)
  339. // hidden filter update namespace trigger by filter widget
  340. .bind('change' + c.namespace + 'filter', function(){
  341. getRange();
  342. }),
  343. $shcell = [],
  344. c = $cell.closest('table')[0].config,
  345. getRange = function(){
  346. var val = $input.val(),
  347. v = val.split(' - ');
  348. if (val === '') { v = [ o.min, o.max ]; }
  349. if (v && v[1]) {
  350. updateUiRange({ values: v, delay: false }, true);
  351. }
  352. },
  353. // this function updates the hidden input and adds the current values to the header cell text
  354. updateUiRange = function(ui, notrigger) {
  355. // ui.values are undefined for some reason on create
  356. var val = ui && ui.values || o.values,
  357. result = val[0] + ' - ' + val[1],
  358. // make range an empty string if entire range is covered so the filter row will hide (if set)
  359. range = val[0] === o.min && val[1] === o.max ? '' : result,
  360. searchType = ui && typeof ui.delayed === 'boolean' ? ui.delayed : c.$table[0].hasInitialized ? o.delayed : true;
  361. if (o.valueToHeader) {
  362. // add range indication to the header cell above (if not using the css method)!
  363. $cell.closest('thead').find('th[data-column=' + indx + ']').find('.currange').html(' (' + result + ')');
  364. } else {
  365. // add values to the handle data-value attribute so the css tooltip will work properly
  366. $cell.find('.ui-slider-handle')
  367. .addClass('value-popup')
  368. .eq(0).attr('data-value', val[0]).end() // adding value to data attribute
  369. .eq(1).attr('data-value', val[1]); // value popup shown via css
  370. }
  371. // update the hidden input
  372. $cell.find('.filter').val(range)
  373. .trigger(notrigger ? '' : 'search', searchType).end()
  374. .find('.range').slider('values', val);
  375. // update sticky header cell
  376. if ($shcell.length) {
  377. $shcell.find('.range').slider('values', val);
  378. if (o.valueToHeader) {
  379. $shcell.closest('thead').find('th[data-column=' + indx + ']').find('.currange').html(' (' + result + ')');
  380. } else {
  381. $shcell.find('.ui-slider-handle')
  382. .addClass('value-popup')
  383. .eq(0).attr('data-value', val[0]).end() // adding value to data attribute
  384. .eq(1).attr('data-value', val[1]); // value popup shown via css
  385. }
  386. }
  387. };
  388. $cell.closest('thead').find('th[data-column=' + indx + ']').addClass('filter-parsed');
  389. // add span to header for value - only works if the line in the updateUiRange() function is also un-commented out
  390. if (o.valueToHeader) {
  391. $cell.closest('thead').find('th[data-column=' + indx + ']').find('.tablesorter-header-inner').append('<span class="currange"/>');
  392. }
  393. // add callbacks; preserve added callbacks
  394. o.oldcreate = o.create;
  395. o.oldslide = o.slide;
  396. // add a jQuery UI range slider!
  397. o.create = function(event, ui) {
  398. updateUiRange(); // ui is an empty object on create
  399. if (typeof o.oldcreate === 'function') { o.oldcreate(event, ui); }
  400. };
  401. o.slide = function(event, ui) {
  402. updateUiRange(ui);
  403. if (typeof o.oldslide === 'function') { o.oldslide(event, ui); }
  404. };
  405. $('<div class="range range' + indx +'"/>')
  406. .appendTo($cell)
  407. .slider(o);
  408. // update slider from hidden input, in case of saved filters
  409. c.$table.bind('filterFomatterUpdate', function(){
  410. getRange();
  411. });
  412. // on reset
  413. c.$table.bind('filterReset', function(){
  414. $cell.find('.range').slider('values', o.values);
  415. setTimeout(function(){
  416. updateUiRange();
  417. }, 0);
  418. });
  419. // has sticky headers?
  420. c.$table.bind('stickyHeadersInit', function(){
  421. $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty();
  422. // add a jQuery UI slider!
  423. $('<div class="range range' + indx + '"/>')
  424. .val(o.value)
  425. .appendTo($shcell)
  426. .slider(o)
  427. .bind('change keyup', function(){
  428. $cell.find('.range').val( this.value );
  429. updateUiRange();
  430. });
  431. });
  432. // return the hidden input so the filter widget has a reference to it
  433. return $input;
  434. },
  435. /*************************\
  436. jQuery UI Datepicker compare (1 input)
  437. \*************************/
  438. uiDateCompare: function($cell, indx, defDate) {
  439. var o = $.extend({
  440. // filter formatter options
  441. cellText : '',
  442. compare : '',
  443. endOfDay : true,
  444. // include ANY jQuery UI spinner options below
  445. defaultDate : '',
  446. changeMonth : true,
  447. changeYear : true,
  448. numberOfMonths : 1
  449. }, defDate),
  450. $date,
  451. // make sure we're using parsed dates in the search
  452. $hdr = $cell.closest('thead').find('th[data-column=' + indx + ']').addClass('filter-parsed'),
  453. // Add a hidden input to hold the range values
  454. $input = $('<input class="dateCompare" type="hidden">')
  455. .appendTo($cell)
  456. // hidden filter update namespace trigger by filter widget
  457. .bind('change' + c.namespace + 'filter', function(){
  458. var v = this.value;
  459. if (v) {
  460. o.onClose(v);
  461. }
  462. }),
  463. t, $shcell = [],
  464. c = $cell.closest('table')[0].config,
  465. // this function updates the hidden input
  466. date1Compare = function(v, notrigger) {
  467. var date, query,
  468. getdate = v || $date.datepicker('getDate') || '',
  469. compare = ($.isArray(o.compare) ? $cell.find(compareSelect).val() || o.compare[ o.selected || 0] : o.compare) || '',
  470. searchType = c.$table[0].hasInitialized ? o.delayed : true;
  471. $date.datepicker('setDate', getdate === '' ? o.defaultDate || '' : getdate);
  472. if (getdate === '') { notrigger = false; }
  473. date = $date.datepicker('getDate');
  474. query = date ? ( o.endOfDay && /<=/.test(compare) ? date.setHours(23, 59, 59) : date.getTime() ) || '' : '';
  475. if (date && o.endOfDay && compare === '=') {
  476. compare = '';
  477. query += ' - ' + date.setHours(23, 59, 59);
  478. notrigger = false;
  479. }
  480. $cell.find('.dateCompare')
  481. // add equal to the beginning, so we filter exact numbers
  482. .val(compare + query)
  483. .trigger( notrigger ? '' : 'search', searchType ).end();
  484. // update sticky header cell
  485. if ($shcell.length) {
  486. $shcell
  487. .find('.dateCompare').val(compare + query).end()
  488. .find(compareSelect).val(compare);
  489. }
  490. };
  491. // Add date range picker
  492. t = '<input type="text" class="date date' + indx + '" placeholder="' +
  493. ($hdr.data('placeholder') || $hdr.attr('data-placeholder') || '') + '" />';
  494. $date = $(t).appendTo($cell);
  495. // add callbacks; preserve added callbacks
  496. o.oldonClose = o.onClose;
  497. o.onClose = function( selectedDate, ui ) {
  498. date1Compare();
  499. if (typeof o.oldonClose === 'function') { o.oldonClose(selectedDate, ui); }
  500. };
  501. $date.datepicker(o);
  502. // on reset
  503. c.$table.bind('filterReset', function(){
  504. if ($.isArray(o.compare)) {
  505. $cell.add($shcell).find(compareSelect).val( o.compare[ o.selected || 0 ] );
  506. }
  507. $cell.add($shcell).find('.date').val(o.defaultDate).datepicker('setDate', '');
  508. setTimeout(function(){
  509. date1Compare();
  510. }, 0);
  511. });
  512. // update date compare from hidden input, in case of saved filters
  513. c.$table.bind('filterFomatterUpdate', function(){
  514. var num, v = $input.val();
  515. if (/\s+-\s+/.test(v)) {
  516. // date range found; assume an exact match on one day
  517. $cell.find(compareSelect).val('=');
  518. num = new Date ( Number( v.split(/\s+-\s+/)[0] ) );
  519. $date.datepicker( 'setDate', num );
  520. } else {
  521. num = (tsff.updateCompare($cell, $input, o)[1]).toString() || '';
  522. // differeniate 1388556000000 from 1/1/2014 using \d{5} regex
  523. num = num !== '' ? new Date( /\d{5}/g.test(num) ? Number(num) : num ) || '' : '';
  524. }
  525. $cell.add($shcell).find('.date').datepicker( 'setDate', num );
  526. date1Compare(num, true);
  527. });
  528. if (o.compare) {
  529. // add compare select
  530. tsff.addCompare($cell, indx, o);
  531. $cell.find(compareSelect).bind('change', function(){
  532. date1Compare();
  533. });
  534. }
  535. // has sticky headers?
  536. c.$table.bind('stickyHeadersInit', function(){
  537. $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty();
  538. // add a jQuery datepicker!
  539. $shcell
  540. .append(t)
  541. .find('.date')
  542. .datepicker(o);
  543. if (o.compare) {
  544. // add compare select
  545. tsff.addCompare($shcell, indx, o);
  546. $shcell.find(compareSelect).bind('change', function(){
  547. $cell.find(compareSelect).val( $(this).val() );
  548. date1Compare();
  549. });
  550. }
  551. });
  552. // return the hidden input so the filter widget has a reference to it
  553. return $input.val( o.defaultDate ? o.defaultDate : '' );
  554. },
  555. /*************************\
  556. jQuery UI Datepicker (2 inputs)
  557. \*************************/
  558. uiDatepicker: function($cell, indx, defDate) {
  559. var o = $.extend({
  560. // filter formatter options
  561. endOfDay : true,
  562. textFrom : 'from',
  563. textTo : 'to',
  564. from : '', // defaultDate "from" input
  565. to : '', // defaultDate "to" input
  566. // include ANY jQuery UI spinner options below
  567. changeMonth : true,
  568. changeYear : true,
  569. numberOfMonths : 1
  570. }, defDate),
  571. t, closeTo, closeFrom, $shcell = [],
  572. // Add a hidden input to hold the range values
  573. $input = $('<input class="dateRange" type="hidden">')
  574. .appendTo($cell)
  575. // hidden filter update namespace trigger by filter widget
  576. .bind('change' + c.namespace + 'filter', function(){
  577. var v = this.value;
  578. if (v.match(' - ')) {
  579. v = v.split(' - ');
  580. $cell.find('.dateTo').val(v[1]);
  581. closeFrom(v[0]);
  582. } else if (v.match('>=')) {
  583. closeFrom( v.replace('>=', '') );
  584. } else if (v.match('<=')) {
  585. closeTo( v.replace('<=', '') );
  586. }
  587. }),
  588. c = $cell.closest('table')[0].config;
  589. // make sure we're using parsed dates in the search
  590. $cell.closest('thead').find('th[data-column=' + indx + ']').addClass('filter-parsed');
  591. // Add date range picker
  592. t = '<label>' + o.textFrom + '</label><input type="text" class="dateFrom" /><label>' + o.textTo + '</label><input type="text" class="dateTo" />';
  593. $(t).appendTo($cell);
  594. // add callbacks; preserve added callbacks
  595. o.oldonClose = o.onClose;
  596. var localfrom = o.defaultDate = o.from || o.defaultDate;
  597. closeFrom = o.onClose = function( selectedDate, ui ) {
  598. var range,
  599. from = new Date( $cell.find('.dateFrom').datepicker('getDate') ).getTime() || '',
  600. to = $cell.find('.dateTo').datepicker('getDate') || '';
  601. to = to ? ( o.endOfDay ? to.setHours(23, 59, 59) : to.getTime() ) || '' : '';
  602. range = from ? ( to ? from + ' - ' + to : '>=' + from ) : (to ? '<=' + to : '');
  603. $cell
  604. .find('.dateRange').val(range)
  605. .trigger('search').end()
  606. .find('.dateTo').datepicker('option', 'minDate', selectedDate ).end()
  607. .find('.dateFrom').val(selectedDate);
  608. // update sticky header cell
  609. if ($shcell.length) {
  610. $shcell
  611. .find('.dateTo').datepicker('option', 'minDate', selectedDate ).end()
  612. .find('.dateFrom').val(selectedDate);
  613. }
  614. if (typeof o.oldonClose === 'function') { o.oldonClose(selectedDate, ui); }
  615. };
  616. $cell.find('.dateFrom').datepicker(o);
  617. o.defaultDate = o.to || '+7d'; // set to date +7 days from today (if not defined)
  618. closeTo = o.onClose = function( selectedDate, ui ) {
  619. var range,
  620. from = new Date( $cell.find('.dateFrom').datepicker('getDate') ).getTime() || '',
  621. to = $cell.find('.dateTo').datepicker('getDate') || '';
  622. to = to ? ( o.endOfDay ? to.setHours(23, 59, 59) : to.getTime() ) || '' : '';
  623. range = from ? ( to ? from + ' - ' + to : '>=' + from ) : (to ? '<=' + to : '');
  624. $cell
  625. .find('.dateRange').val(range)
  626. .trigger('search').end()
  627. .find('.dateFrom').datepicker('option', 'maxDate', selectedDate ).end()
  628. .find('.dateTo').val(selectedDate);
  629. // update sticky header cell
  630. if ($shcell.length) {
  631. $shcell
  632. .find('.dateFrom').datepicker('option', 'maxDate', selectedDate ).end()
  633. .find('.dateTo').val(selectedDate);
  634. }
  635. if (typeof o.oldonClose === 'function') { o.oldonClose(selectedDate, ui); }
  636. };
  637. $cell.find('.dateTo').datepicker(o);
  638. // update date compare from hidden input, in case of saved filters
  639. c.$table.bind('filterFomatterUpdate', function(){
  640. var val = $input.val() || '',
  641. from = '',
  642. to = '';
  643. // date range
  644. if (/\s+-\s+/.test(val)){
  645. val = val.split(/\s+-\s+/) || [];
  646. from = val[0] || '';
  647. to = val[1] || '';
  648. } else if (/>=/.test(val)) {
  649. // greater than date (to date empty)
  650. from = new Date(Number( val.replace(/>=/, '') )) || '';
  651. } else if (/<=/.test(val)) {
  652. // less than date (from date empty)
  653. to = new Date(Number( val.replace(/<=/, '') )) || '';
  654. }
  655. $cell.add($shcell).find('.dateFrom').datepicker('setDate', from);
  656. $cell.add($shcell).find('.dateTo').datepicker('setDate', to);
  657. closeTo(to);
  658. });
  659. // has sticky headers?
  660. c.$table.bind('stickyHeadersInit', function(){
  661. $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty();
  662. $shcell.append(t);
  663. // add a jQuery datepicker!
  664. o.onClose = closeTo;
  665. $shcell.find('.dateTo').datepicker(o);
  666. o.defaultDate = localfrom;
  667. o.onClose = closeFrom;
  668. $shcell.find('.dateFrom').datepicker(o);
  669. });
  670. // on reset
  671. $cell.closest('table').bind('filterReset', function(){
  672. $cell.add($shcell).find('.dateFrom').val('').datepicker('setDate', o.from );
  673. $cell.add($shcell).find('.dateTo').val('').datepicker('setDate', o.to );
  674. });
  675. // return the hidden input so the filter widget has a reference to it
  676. return $input.val( o.from ? ( o.to ? o.from + ' - ' + o.to : '>=' + o.from ) : (o.to ? '<=' + o.to : '') );
  677. },
  678. /**********************\
  679. HTML5 Number (spinner)
  680. \**********************/
  681. html5Number : function($cell, indx, def5Num) {
  682. var t, o = $.extend({
  683. value : 0,
  684. min : 0,
  685. max : 100,
  686. step : 1,
  687. delayed : true,
  688. disabled : false,
  689. addToggle : false,
  690. exactMatch : false,
  691. cellText : '',
  692. compare : '',
  693. skipTest: false
  694. }, def5Num),
  695. $input,
  696. // test browser for HTML5 range support
  697. $number = $('<input type="number" style="visibility:hidden;" value="test">').appendTo($cell),
  698. // test if HTML5 number is supported - from Modernizr
  699. numberSupported = o.skipTest || $number.attr('type') === 'number' && $number.val() !== 'test',
  700. $shcell = [],
  701. c = $cell.closest('table')[0].config,
  702. updateNumber = function(delayed, notrigger){
  703. var chkd = o.addToggle ? $cell.find('.toggle').is(':checked') : true,
  704. v = $cell.find('.number').val(),
  705. compare = ($.isArray(o.compare) ? $cell.find(compareSelect).val() || o.compare[ o.selected || 0] : o.compare) || '',
  706. searchType = c.$table[0].hasInitialized ? (delayed ? delayed : o.delayed) : true;
  707. $input
  708. // add equal to the beginning, so we filter exact numbers
  709. .val( !o.addToggle || chkd ? (compare ? compare : o.exactMatch ? '=' : '') + v : '' )
  710. .trigger( notrigger ? '' : 'search', searchType ).end()
  711. .find('.number').val(v);
  712. if ($cell.find('.number').length) {
  713. $cell.find('.number')[0].disabled = (o.disabled || !chkd);
  714. }
  715. // update sticky header cell
  716. if ($shcell.length) {
  717. $shcell.find('.number').val(v)[0].disabled = (o.disabled || !chkd);
  718. $shcell.find(compareSelect).val(compare);
  719. if (o.addToggle) {
  720. $shcell.find('.toggle')[0].checked = chkd;
  721. }
  722. }
  723. };
  724. $number.remove();
  725. if (numberSupported) {
  726. t = o.addToggle ? '<div class="button"><input id="html5button' + indx + '" type="checkbox" class="toggle" />' +
  727. '<label for="html5button' + indx + '"></label></div>' : '';
  728. t += '<input class="number" type="number" min="' + o.min + '" max="' + o.max + '" value="' +
  729. o.value + '" step="' + o.step + '" />';
  730. // add HTML5 number (spinner)
  731. $cell
  732. .append(t + '<input type="hidden" />')
  733. .find('.toggle, .number').bind('change', function(){
  734. updateNumber();
  735. })
  736. .closest('thead').find('th[data-column=' + indx + ']')
  737. .addClass('filter-parsed') // get exact numbers from column
  738. // on reset
  739. .closest('table').bind('filterReset', function(){
  740. if ($.isArray(o.compare)) {
  741. $cell.add($shcell).find(compareSelect).val( o.compare[ o.selected || 0 ] );
  742. }
  743. // turn off the toggle checkbox
  744. if (o.addToggle) {
  745. $cell.find('.toggle')[0].checked = false;
  746. if ($shcell.length) {
  747. $shcell.find('.toggle')[0].checked = false;
  748. }
  749. }
  750. $cell.find('.number').val( o.value );
  751. setTimeout(function(){
  752. updateNumber();
  753. }, 0);
  754. });
  755. $input = $cell.find('input[type=hidden]').bind('change', function(){
  756. $cell.find('.number').val( this.value );
  757. updateNumber();
  758. });
  759. // update slider from hidden input, in case of saved filters
  760. c.$table.bind('filterFomatterUpdate', function(){
  761. var val = tsff.updateCompare($cell, $input, o)[0] || o.value;
  762. $cell.find('.number').val( ((val || '') + '').replace(/[><=]/g,'') );
  763. updateNumber(false, true);
  764. });
  765. if (o.compare) {
  766. // add compare select
  767. tsff.addCompare($cell, indx, o);
  768. $cell.find(compareSelect).bind('change', function(){
  769. updateNumber();
  770. });
  771. }
  772. // has sticky headers?
  773. c.$table.bind('stickyHeadersInit', function(){
  774. $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty();
  775. $shcell
  776. .append(t)
  777. .find('.toggle, .number').bind('change', function(){
  778. $cell.find('.number').val( $(this).val() );
  779. updateNumber();
  780. });
  781. if (o.compare) {
  782. // add compare select
  783. tsff.addCompare($shcell, indx, o);
  784. $shcell.find(compareSelect).bind('change', function(){
  785. $cell.find(compareSelect).val( $(this).val() );
  786. updateNumber();
  787. });
  788. }
  789. updateNumber();
  790. });
  791. updateNumber();
  792. }
  793. return numberSupported ? $cell.find('input[type="hidden"]') : $('<input type="search">');
  794. },
  795. /**********************\
  796. HTML5 range slider
  797. \**********************/
  798. html5Range : function($cell, indx, def5Range) {
  799. var o = $.extend({
  800. value : 0,
  801. min : 0,
  802. max : 100,
  803. step : 1,
  804. delayed : true,
  805. valueToHeader : true,
  806. exactMatch : true,
  807. cellText : '',
  808. compare : '',
  809. allText : 'all',
  810. skipTest : false
  811. }, def5Range),
  812. $input,
  813. // test browser for HTML5 range support
  814. $range = $('<input type="range" style="visibility:hidden;" value="test">').appendTo($cell),
  815. // test if HTML5 range is supported - from Modernizr (but I left out the method to detect in Safari 2-4)
  816. // see https://github.com/Modernizr/Modernizr/blob/master/feature-detects/inputtypes.js
  817. rangeSupported = o.skipTest || $range.attr('type') === 'range' && $range.val() !== 'test',
  818. $shcell = [],
  819. c = $cell.closest('table')[0].config,
  820. updateRange = function(v, delayed, notrigger){
  821. /*jshint eqeqeq:false */
  822. // hidden input changes may include compare symbols
  823. v = ( typeof v === "undefined" ? $input.val() : v ).toString().replace(/[<>=]/g,'') || o.value;
  824. var compare = ($.isArray(o.compare) ? $cell.find(compareSelect).val() || o.compare[ o.selected || 0] : o.compare) || '',
  825. t = ' (' + (compare ? compare + v : v == o.min ? o.allText : v) + ')',
  826. searchType = c.$table[0].hasInitialized ? (delayed ? delayed : o.delayed) : true;
  827. $cell.find('input[type=hidden]')
  828. // add equal to the beginning, so we filter exact numbers
  829. .val( ( compare ? compare + v : ( v == o.min ? '' : ( o.exactMatch ? '=' : '' ) + v ) ) )
  830. //( val == o.min ? '' : val + (o.exactMatch ? '=' : ''))
  831. .trigger( notrigger ? '' : 'search', searchType ).end()
  832. .find('.range').val(v);
  833. // or add current value to the header cell, if desired
  834. $cell.closest('thead').find('th[data-column=' + indx + ']').find('.curvalue').html(t);
  835. // update sticky header cell
  836. if ($shcell.length) {
  837. $shcell
  838. .find('.range').val(v).end()
  839. .find(compareSelect).val( compare );
  840. $shcell.closest('thead').find('th[data-column=' + indx + ']').find('.curvalue').html(t);
  841. }
  842. };
  843. $range.remove();
  844. if (rangeSupported) {
  845. // add HTML5 range
  846. $cell
  847. .html('<input type="hidden"><input class="range" type="range" min="' + o.min + '" max="' + o.max + '" value="' + o.value + '" />')
  848. .closest('thead').find('th[data-column=' + indx + ']')
  849. .addClass('filter-parsed') // get exact numbers from column
  850. // add span to header for the current slider value
  851. .find('.tablesorter-header-inner').append('<span class="curvalue" />');
  852. // hidden filter update namespace trigger by filter widget
  853. $input = $cell.find('input[type=hidden]').bind('change' + c.namespace + 'filter', function(){
  854. /*jshint eqeqeq:false */
  855. var v = this.value,
  856. compare = ($.isArray(o.compare) ? $cell.find(compareSelect).val() || o.compare[ o.selected || 0] : o.compare) || '';
  857. if (v !== this.lastValue) {
  858. this.lastValue = ( compare ? compare + v : ( v == o.min ? '' : ( o.exactMatch ? '=' : '' ) + v ) );
  859. this.value = this.lastValue;
  860. updateRange( v );
  861. }
  862. });
  863. $cell.find('.range').bind('change', function(){
  864. updateRange( this.value );
  865. });
  866. // update spinner from hidden input, in case of saved filters
  867. c.$table.bind('filterFomatterUpdate', function(){
  868. var val = tsff.updateCompare($cell, $input, o)[0];
  869. $cell.find('.range').val( val );
  870. updateRange(val, false, true);
  871. });
  872. if (o.compare) {
  873. // add compare select
  874. tsff.addCompare($cell, indx, o);
  875. $cell.find(compareSelect).bind('change', function(){
  876. updateRange();
  877. });
  878. }
  879. // has sticky headers?
  880. c.$table.bind('stickyHeadersInit', function(){
  881. $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty();
  882. $shcell
  883. .html('<input class="range" type="range" min="' + o.min + '" max="' + o.max + '" value="' + o.value + '" />')
  884. .find('.range').bind('change', function(){
  885. updateRange( $shcell.find('.range').val() );
  886. });
  887. updateRange();
  888. if (o.compare) {
  889. // add compare select
  890. tsff.addCompare($shcell, indx, o);
  891. $shcell.find(compareSelect).bind('change', function(){
  892. $cell.find(compareSelect).val( $(this).val() );
  893. updateRange();
  894. });
  895. }
  896. });
  897. // on reset
  898. $cell.closest('table').bind('filterReset', function(){
  899. if ($.isArray(o.compare)) {
  900. $cell.add($shcell).find(compareSelect).val( o.compare[ o.selected || 0 ] );
  901. }
  902. setTimeout(function(){
  903. updateRange(o.value, false, true);
  904. }, 0);
  905. });
  906. updateRange();
  907. }
  908. return rangeSupported ? $cell.find('input[type="hidden"]') : $('<input type="search">');
  909. },
  910. /**********************\
  911. HTML5 Color picker
  912. \**********************/
  913. html5Color: function($cell, indx, defColor) {
  914. var t, o = $.extend({
  915. value : '#000000',
  916. disabled : false,
  917. addToggle : true,
  918. exactMatch : true,
  919. valueToHeader : false,
  920. skipTest : false
  921. }, defColor),
  922. $input,
  923. // Add a hidden input to hold the range values
  924. $color = $('<input type="color" style="visibility:hidden;" value="test">').appendTo($cell),
  925. // test if HTML5 color is supported - from Modernizr
  926. colorSupported = o.skipTest || $color.attr('type') === 'color' && $color.val() !== 'test',
  927. $shcell = [],
  928. c = $cell.closest('table')[0].config,
  929. updateColor = function(v, notrigger){
  930. v = ( typeof v === "undefined" ? $input.val() : v ).toString().replace('=','') || o.value;
  931. var chkd = true,
  932. t = ' (' + v + ')';
  933. if (o.addToggle) {
  934. chkd = $cell.find('.toggle').is(':checked');
  935. }
  936. if ($cell.find('.colorpicker').length) {
  937. $cell.find('.colorpicker').val(v)[0].disabled = (o.disabled || !chkd);
  938. }
  939. $input
  940. .val( chkd ? v + (o.exactMatch ? '=' : '') : '' )
  941. .trigger( !c.$table[0].hasInitialized || notrigger ? '' : 'search' );
  942. if (o.valueToHeader) {
  943. // add current color to the header cell
  944. $cell.closest('thead').find('th[data-column=' + indx + ']').find('.curcolor').html(t);
  945. } else {
  946. // current color to span in cell
  947. $cell.find('.currentColor').html(t);
  948. }
  949. // update sticky header cell
  950. if ($shcell.length) {
  951. $shcell.find('.colorpicker').val(v)[0].disabled = (o.disabled || !chkd);
  952. if (o.addToggle) {
  953. $shcell.find('.toggle')[0].checked = chkd;
  954. }
  955. if (o.valueToHeader) {
  956. // add current color to the header cell
  957. $shcell.closest('thead').find('th[data-column=' + indx + ']').find('.curcolor').html(t);
  958. } else {
  959. // current color to span in cell
  960. $shcell.find('.currentColor').html(t);
  961. }
  962. }
  963. };
  964. $color.remove();
  965. if (colorSupported) {
  966. t = '' + indx + Math.round(Math.random() * 100);
  967. // add HTML5 color picker
  968. t = '<div class="color-controls-wrapper">' +
  969. (o.addToggle ? '<div class="button"><input id="colorbutton' + t + '" type="checkbox" class="toggle" /><label for="colorbutton' +
  970. t + '"></label></div>' : '') +
  971. '<input type="hidden"><input class="colorpicker" type="color" />' +
  972. (o.valueToHeader ? '' : '<span class="currentColor">(#000000)</span>') + '</div>';
  973. $cell.html(t);
  974. // add span to header for the current color value - only works if the line in the updateColor() function is also un-commented out
  975. if (o.valueToHeader) {
  976. $cell.closest('thead').find('th[data-column=' + indx + ']').find('.tablesorter-header-inner').append('<span class="curcolor" />');
  977. }
  978. $cell.find('.toggle, .colorpicker').bind('change', function(){
  979. updateColor( $cell.find('.colorpicker').val() );
  980. });
  981. // hidden filter update namespace trigger by filter widget
  982. $input = $cell.find('input[type=hidden]').bind('change' + c.namespace + 'filter', function(){
  983. updateColor( this.value );
  984. });
  985. // update slider from hidden input, in case of saved filters
  986. c.$table.bind('filterFomatterUpdate', function(){
  987. updateColor( $input.val(), true );
  988. });
  989. // on reset
  990. $cell.closest('table').bind('filterReset', function(){
  991. // just turn off the colorpicker
  992. if (o.addToggle) {
  993. $cell.find('.toggle')[0].checked = false;
  994. }
  995. // delay needed because default color needs to be set in the filter
  996. // there is no compare option here, so if addToggle = false,
  997. // default color is #000000 (even with no value set)
  998. setTimeout(function(){
  999. updateColor();
  1000. }, 0);
  1001. });
  1002. // has sticky headers?
  1003. c.$table.bind('stickyHeadersInit', function(){
  1004. $shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx);
  1005. $shcell
  1006. .html(t)
  1007. .find('.toggle, .colorpicker').bind('change', function(){
  1008. updateColor( $shcell.find('.colorpicker').val() );
  1009. });
  1010. updateColor( $shcell.find('.colorpicker').val() );
  1011. });
  1012. updateColor( o.value );
  1013. }
  1014. return colorSupported ? $cell.find('input[type="hidden"]') : $('<input type="search">');
  1015. }
  1016. };
  1017. })(jQuery);