jquery-tablesorter 1.19.1 → 1.19.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,7 +4,7 @@
4
4
  ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██
5
5
  █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀
6
6
  */
7
- /*! tablesorter (FORK) - updated 11-04-2015 (v2.24.3)*/
7
+ /*! tablesorter (FORK) - updated 11-10-2015 (v2.24.4)*/
8
8
  /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */
9
9
  (function(factory) {
10
10
  if (typeof define === 'function' && define.amd) {
@@ -372,13 +372,13 @@
372
372
 
373
373
  })(jQuery);
374
374
 
375
- /*! Widget: filter - updated 11/4/2015 (v2.24.3) *//*
375
+ /*! Widget: filter - updated 11/10/2015 (v2.24.4) *//*
376
376
  * Requires tablesorter v2.8+ and jQuery 1.7+
377
377
  * by Rob Garrison
378
378
  */
379
379
  ;( function ( $ ) {
380
380
  'use strict';
381
- var tsf,
381
+ var tsf, tsfRegex,
382
382
  ts = $.tablesorter || {},
383
383
  tscss = ts.css;
384
384
 
@@ -484,20 +484,21 @@
484
484
  // data.parsed = array ( by column ) of boolean values ( from filter_useParsedData or 'filter-parsed' class )
485
485
  types: {
486
486
  or : function( c, data, vars ) {
487
- if ( tsf.regex.orTest.test( data.iFilter ) || tsf.regex.orSplit.test( data.filter ) ) {
487
+ // look for "|", but not if it is inside of a regular expression
488
+ if ( ( tsfRegex.orTest.test( data.iFilter ) || tsfRegex.orSplit.test( data.filter ) ) &&
489
+ // this test for regex has potential to slow down the overall search
490
+ !tsfRegex.regex.test( data.filter ) ) {
488
491
  var indx, filterMatched, query, regex,
489
492
  // duplicate data but split filter
490
493
  data2 = $.extend( {}, data ),
491
- index = data.index,
492
- parsed = data.parsed[ index ],
493
- filter = data.filter.split( tsf.regex.orSplit ),
494
- iFilter = data.iFilter.split( tsf.regex.orSplit ),
494
+ filter = data.filter.split( tsfRegex.orSplit ),
495
+ iFilter = data.iFilter.split( tsfRegex.orSplit ),
495
496
  len = filter.length;
496
497
  for ( indx = 0; indx < len; indx++ ) {
497
498
  data2.nestedFilters = true;
498
- data2.filter = '' + ( tsf.parseFilter( c, filter[ indx ], index, parsed ) || '' );
499
- data2.iFilter = '' + ( tsf.parseFilter( c, iFilter[ indx ], index, parsed ) || '' );
500
- query = '(' + ( tsf.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')';
499
+ data2.filter = '' + ( tsf.parseFilter( c, filter[ indx ], data ) || '' );
500
+ data2.iFilter = '' + ( tsf.parseFilter( c, iFilter[ indx ], data ) || '' );
501
+ query = '(' + ( tsf.parseFilter( c, data2.filter, data ) || '' ) + ')';
501
502
  try {
502
503
  // use try/catch, because query may not be a valid regex if "|" is contained within a partial regex search,
503
504
  // e.g "/(Alex|Aar" -> Uncaught SyntaxError: Invalid regular expression: /(/(Alex)/: Unterminated group
@@ -519,22 +520,20 @@
519
520
  },
520
521
  // Look for an AND or && operator ( logical and )
521
522
  and : function( c, data, vars ) {
522
- if ( tsf.regex.andTest.test( data.filter ) ) {
523
+ if ( tsfRegex.andTest.test( data.filter ) ) {
523
524
  var indx, filterMatched, result, query, regex,
524
525
  // duplicate data but split filter
525
526
  data2 = $.extend( {}, data ),
526
- index = data.index,
527
- parsed = data.parsed[ index ],
528
- filter = data.filter.split( tsf.regex.andSplit ),
529
- iFilter = data.iFilter.split( tsf.regex.andSplit ),
527
+ filter = data.filter.split( tsfRegex.andSplit ),
528
+ iFilter = data.iFilter.split( tsfRegex.andSplit ),
530
529
  len = filter.length;
531
530
  for ( indx = 0; indx < len; indx++ ) {
532
531
  data2.nestedFilters = true;
533
- data2.filter = '' + ( tsf.parseFilter( c, filter[ indx ], index, parsed ) || '' );
534
- data2.iFilter = '' + ( tsf.parseFilter( c, iFilter[ indx ], index, parsed ) || '' );
535
- query = ( '(' + ( tsf.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')' )
532
+ data2.filter = '' + ( tsf.parseFilter( c, filter[ indx ], data ) || '' );
533
+ data2.iFilter = '' + ( tsf.parseFilter( c, iFilter[ indx ], data ) || '' );
534
+ query = ( '(' + ( tsf.parseFilter( c, data2.filter, data ) || '' ) + ')' )
536
535
  // replace wild cards since /(a*)/i will match anything
537
- .replace( tsf.regex.wild01, '\\S{1}' ).replace( tsf.regex.wild0More, '\\S*' );
536
+ .replace( tsfRegex.wild01, '\\S{1}' ).replace( tsfRegex.wild0More, '\\S*' );
538
537
  try {
539
538
  // use try/catch just in case RegExp is invalid
540
539
  regex = new RegExp( data.isMatch ? query : '^' + query + '$', c.widgetOptions.filter_ignoreCase ? 'i' : '' );
@@ -556,10 +555,10 @@
556
555
  },
557
556
  // Look for regex
558
557
  regex: function( c, data ) {
559
- if ( tsf.regex.regex.test( data.filter ) ) {
558
+ if ( tsfRegex.regex.test( data.filter ) ) {
560
559
  var matches,
561
560
  // cache regex per column for optimal speed
562
- regex = data.filter_regexCache[ data.index ] || tsf.regex.regex.exec( data.filter ),
561
+ regex = data.filter_regexCache[ data.index ] || tsfRegex.regex.exec( data.filter ),
563
562
  isRegex = regex instanceof RegExp;
564
563
  try {
565
564
  if ( !isRegex ) {
@@ -578,18 +577,17 @@
578
577
  // Look for operators >, >=, < or <=
579
578
  operators: function( c, data ) {
580
579
  // ignore empty strings... because '' < 10 is true
581
- if ( tsf.regex.operTest.test( data.iFilter ) && data.iExact !== '' ) {
580
+ if ( tsfRegex.operTest.test( data.iFilter ) && data.iExact !== '' ) {
582
581
  var cachedValue, result, txt,
583
582
  table = c.table,
584
- index = data.index,
585
- parsed = data.parsed[index],
586
- query = ts.formatFloat( data.iFilter.replace( tsf.regex.operators, '' ), table ),
587
- parser = c.parsers[index],
583
+ parsed = data.parsed[ data.index ],
584
+ query = ts.formatFloat( data.iFilter.replace( tsfRegex.operators, '' ), table ),
585
+ parser = c.parsers[ data.index ],
588
586
  savedSearch = query;
589
587
  // parse filter value in case we're comparing numbers ( dates )
590
588
  if ( parsed || parser.type === 'numeric' ) {
591
- txt = $.trim( '' + data.iFilter.replace( tsf.regex.operators, '' ) );
592
- result = tsf.parseFilter( c, txt, index, true );
589
+ txt = $.trim( '' + data.iFilter.replace( tsfRegex.operators, '' ) );
590
+ result = tsf.parseFilter( c, txt, data, true );
593
591
  query = ( typeof result === 'number' && result !== '' && !isNaN( result ) ) ? result : query;
594
592
  }
595
593
  // iExact may be numeric - see issue #149;
@@ -601,10 +599,10 @@
601
599
  txt = isNaN( data.iExact ) ? data.iExact.replace( ts.regex.nondigit, '' ) : data.iExact;
602
600
  cachedValue = ts.formatFloat( txt, table );
603
601
  }
604
- if ( tsf.regex.gtTest.test( data.iFilter ) ) {
605
- result = tsf.regex.gteTest.test( data.iFilter ) ? cachedValue >= query : cachedValue > query;
606
- } else if ( tsf.regex.ltTest.test( data.iFilter ) ) {
607
- result = tsf.regex.lteTest.test( data.iFilter ) ? cachedValue <= query : cachedValue < query;
602
+ if ( tsfRegex.gtTest.test( data.iFilter ) ) {
603
+ result = tsfRegex.gteTest.test( data.iFilter ) ? cachedValue >= query : cachedValue > query;
604
+ } else if ( tsfRegex.ltTest.test( data.iFilter ) ) {
605
+ result = tsfRegex.lteTest.test( data.iFilter ) ? cachedValue <= query : cachedValue < query;
608
606
  }
609
607
  // keep showing all rows if nothing follows the operator
610
608
  if ( !result && savedSearch === '' ) {
@@ -616,13 +614,13 @@
616
614
  },
617
615
  // Look for a not match
618
616
  notMatch: function( c, data ) {
619
- if ( tsf.regex.notTest.test( data.iFilter ) ) {
617
+ if ( tsfRegex.notTest.test( data.iFilter ) ) {
620
618
  var indx,
621
619
  txt = data.iFilter.replace( '!', '' ),
622
- filter = tsf.parseFilter( c, txt, data.index, data.parsed[data.index] ) || '';
623
- if ( tsf.regex.exact.test( filter ) ) {
620
+ filter = tsf.parseFilter( c, txt, data ) || '';
621
+ if ( tsfRegex.exact.test( filter ) ) {
624
622
  // look for exact not matches - see #628
625
- filter = filter.replace( tsf.regex.exact, '' );
623
+ filter = filter.replace( tsfRegex.exact, '' );
626
624
  return filter === '' ? true : $.trim( filter ) !== data.iExact;
627
625
  } else {
628
626
  indx = data.iExact.search( $.trim( filter ) );
@@ -634,29 +632,29 @@
634
632
  // Look for quotes or equals to get an exact match; ignore type since iExact could be numeric
635
633
  exact: function( c, data ) {
636
634
  /*jshint eqeqeq:false */
637
- if ( tsf.regex.exact.test( data.iFilter ) ) {
638
- var txt = data.iFilter.replace( tsf.regex.exact, '' ),
639
- filter = tsf.parseFilter( c, txt, data.index, data.parsed[data.index] ) || '';
635
+ if ( tsfRegex.exact.test( data.iFilter ) ) {
636
+ var txt = data.iFilter.replace( tsfRegex.exact, '' ),
637
+ filter = tsf.parseFilter( c, txt, data ) || '';
640
638
  return data.anyMatch ? $.inArray( filter, data.rowArray ) >= 0 : filter == data.iExact;
641
639
  }
642
640
  return null;
643
641
  },
644
642
  // Look for a range ( using ' to ' or ' - ' ) - see issue #166; thanks matzhu!
645
643
  range : function( c, data ) {
646
- if ( tsf.regex.toTest.test( data.iFilter ) ) {
644
+ if ( tsfRegex.toTest.test( data.iFilter ) ) {
647
645
  var result, tmp, range1, range2,
648
646
  table = c.table,
649
647
  index = data.index,
650
648
  parsed = data.parsed[index],
651
649
  // make sure the dash is for a range and not indicating a negative number
652
- query = data.iFilter.split( tsf.regex.toSplit );
650
+ query = data.iFilter.split( tsfRegex.toSplit );
653
651
 
654
652
  tmp = query[0].replace( ts.regex.nondigit, '' ) || '';
655
- range1 = ts.formatFloat( tsf.parseFilter( c, tmp, index, parsed ), table );
653
+ range1 = ts.formatFloat( tsf.parseFilter( c, tmp, data ), table );
656
654
  tmp = query[1].replace( ts.regex.nondigit, '' ) || '';
657
- range2 = ts.formatFloat( tsf.parseFilter( c, tmp, index, parsed ), table );
655
+ range2 = ts.formatFloat( tsf.parseFilter( c, tmp, data ), table );
658
656
  // parse filter value in case we're comparing numbers ( dates )
659
- if ( parsed || c.parsers[index].type === 'numeric' ) {
657
+ if ( parsed || c.parsers[ index ].type === 'numeric' ) {
660
658
  result = c.parsers[ index ].format( '' + query[0], table, c.$headers.eq( index ), index );
661
659
  range1 = ( result !== '' && !isNaN( result ) ) ? result : range1;
662
660
  result = c.parsers[ index ].format( '' + query[1], table, c.$headers.eq( index ), index );
@@ -677,18 +675,16 @@
677
675
  },
678
676
  // Look for wild card: ? = single, * = multiple, or | = logical OR
679
677
  wild : function( c, data ) {
680
- if ( tsf.regex.wildOrTest.test( data.iFilter ) ) {
681
- var index = data.index,
682
- parsed = data.parsed[ index ],
683
- query = '' + ( tsf.parseFilter( c, data.iFilter, index, parsed ) || '' );
678
+ if ( tsfRegex.wildOrTest.test( data.iFilter ) ) {
679
+ var query = '' + ( tsf.parseFilter( c, data.iFilter, data ) || '' );
684
680
  // look for an exact match with the 'or' unless the 'filter-match' class is found
685
- if ( !tsf.regex.wildTest.test( query ) && data.nestedFilters ) {
681
+ if ( !tsfRegex.wildTest.test( query ) && data.nestedFilters ) {
686
682
  query = data.isMatch ? query : '^(' + query + ')$';
687
683
  }
688
684
  // parsing the filter may not work properly when using wildcards =/
689
685
  try {
690
686
  return new RegExp(
691
- query.replace( tsf.regex.wild01, '\\S{1}' ).replace( tsf.regex.wild0More, '\\S*' ),
687
+ query.replace( tsfRegex.wild01, '\\S{1}' ).replace( tsfRegex.wild0More, '\\S*' ),
692
688
  c.widgetOptions.filter_ignoreCase ? 'i' : ''
693
689
  )
694
690
  .test( data.exact );
@@ -700,21 +696,18 @@
700
696
  },
701
697
  // fuzzy text search; modified from https://github.com/mattyork/fuzzy ( MIT license )
702
698
  fuzzy: function( c, data ) {
703
- if ( tsf.regex.fuzzyTest.test( data.iFilter ) ) {
699
+ if ( tsfRegex.fuzzyTest.test( data.iFilter ) ) {
704
700
  var indx,
705
701
  patternIndx = 0,
706
702
  len = data.iExact.length,
707
703
  txt = data.iFilter.slice( 1 ),
708
- pattern = tsf.parseFilter( c, txt, data.index, data.parsed[data.index] ) || '';
704
+ pattern = tsf.parseFilter( c, txt, data ) || '';
709
705
  for ( indx = 0; indx < len; indx++ ) {
710
706
  if ( data.iExact[ indx ] === pattern[ patternIndx ] ) {
711
707
  patternIndx += 1;
712
708
  }
713
709
  }
714
- if ( patternIndx === pattern.length ) {
715
- return true;
716
- }
717
- return false;
710
+ return patternIndx === pattern.length;
718
711
  }
719
712
  return null;
720
713
  }
@@ -727,8 +720,7 @@
727
720
  and : 'and'
728
721
  }, ts.language );
729
722
 
730
- var options, string, txt, $header, column, filters, val, fxn, noSelect,
731
- regex = tsf.regex;
723
+ var options, string, txt, $header, column, filters, val, fxn, noSelect;
732
724
  c.$table.addClass( 'hasFilters' );
733
725
 
734
726
  // define timers so using clearTimeout won't cause an undefined error
@@ -739,8 +731,8 @@
739
731
  wo.filter_anyColumnSelector = '[data-column="all"],[data-column="any"]';
740
732
  wo.filter_multipleColumnSelector = '[data-column*="-"],[data-column*=","]';
741
733
 
742
- val = '\\{' + tsf.regex.query + '\\}';
743
- $.extend( regex, {
734
+ val = '\\{' + tsfRegex.query + '\\}';
735
+ $.extend( tsfRegex, {
744
736
  child : new RegExp( c.cssChildRow ),
745
737
  filtered : new RegExp( wo.filter_filteredRow ),
746
738
  alreadyFiltered : new RegExp( '(\\s+(' + ts.language.or + '|-|' + ts.language.to + ')\\s+)', 'i' ),
@@ -1018,8 +1010,10 @@
1018
1010
  c.$table.data( 'lastSearch', filters );
1019
1011
  return filters;
1020
1012
  },
1021
- parseFilter: function( c, filter, column, parsed ) {
1022
- return parsed ? c.parsers[column].format( filter, c.table, [], column ) : filter;
1013
+ parseFilter: function( c, filter, data, parsed ) {
1014
+ return parsed || data.parsed[ data.index ] ?
1015
+ c.parsers[ data.index ].format( filter, c.table, [], data.index ) :
1016
+ filter;
1023
1017
  },
1024
1018
  buildRow: function( table, c, wo ) {
1025
1019
  var $filter, col, column, $header, makeSelect, disabled, name, ffxn, tmp,
@@ -1212,8 +1206,12 @@
1212
1206
  c.lastCombinedFilter = null;
1213
1207
  c.lastSearch = [];
1214
1208
  }
1215
- // convert filters to strings (maybe not the best method)- see #1070
1216
- filters = filters.join( '\u0000' ).split( '\u0000' );
1209
+ // convert filters to strings - see #1070
1210
+ filters = Array.prototype.map ?
1211
+ filters.map( String ) :
1212
+ // for IE8 & older browsers - maybe not the best method
1213
+ filters.join( '\u0000' ).split( '\u0000' );
1214
+
1217
1215
  if ( wo.filter_initialized ) {
1218
1216
  c.$table.trigger( 'filterStart', [ filters ] );
1219
1217
  }
@@ -1267,8 +1265,8 @@
1267
1265
  },
1268
1266
  defaultFilter: function( filter, mask ) {
1269
1267
  if ( filter === '' ) { return filter; }
1270
- var regex = tsf.regex.iQuery,
1271
- maskLen = mask.match( tsf.regex.igQuery ).length,
1268
+ var regex = tsfRegex.iQuery,
1269
+ maskLen = mask.match( tsfRegex.igQuery ).length,
1272
1270
  query = maskLen > 1 ? $.trim( filter ).split( /\s/ ) : [ $.trim( filter ) ],
1273
1271
  len = query.length - 1,
1274
1272
  indx = 0,
@@ -1365,9 +1363,8 @@
1365
1363
  return filterMatched;
1366
1364
  },
1367
1365
  processRow: function( c, data, vars ) {
1368
- var hasSelect, result, val, filterMatched,
1366
+ var result, filterMatched,
1369
1367
  fxn, ffxn, txt,
1370
- regex = tsf.regex,
1371
1368
  wo = c.widgetOptions,
1372
1369
  showRow = true,
1373
1370
 
@@ -1446,7 +1443,7 @@
1446
1443
  result = data.rawArray[ columnIndex ] || '';
1447
1444
  data.exact = c.sortLocaleCompare ? ts.replaceAccents( result ) : result; // issue #405
1448
1445
  }
1449
- data.iExact = !regex.type.test( typeof data.exact ) && wo.filter_ignoreCase ?
1446
+ data.iExact = !tsfRegex.type.test( typeof data.exact ) && wo.filter_ignoreCase ?
1450
1447
  data.exact.toLowerCase() : data.exact;
1451
1448
 
1452
1449
  data.isMatch = c.$headerIndexed[ data.index ].hasClass( 'filter-match' );
@@ -1464,21 +1461,13 @@
1464
1461
  data.filter = ts.replaceAccents( data.filter );
1465
1462
  }
1466
1463
 
1467
- val = true;
1468
- if ( wo.filter_defaultFilter && regex.iQuery.test( vars.defaultColFilter[ columnIndex ] ) ) {
1469
- data.filter = tsf.defaultFilter( data.filter, vars.defaultColFilter[ columnIndex ] );
1470
- // val is used to indicate that a filter select is using a default filter;
1471
- // so we override the exact & partial matches
1472
- val = false;
1473
- }
1474
1464
  // data.iFilter = case insensitive ( if wo.filter_ignoreCase is true ),
1475
1465
  // data.filter = case sensitive
1476
1466
  data.iFilter = wo.filter_ignoreCase ? ( data.filter || '' ).toLowerCase() : data.filter;
1477
1467
  fxn = vars.functions[ columnIndex ];
1478
- hasSelect = c.$headerIndexed[ columnIndex ].hasClass( 'filter-select' );
1479
1468
  filterMatched = null;
1480
- if ( fxn || ( hasSelect && val ) ) {
1481
- if ( fxn === true || hasSelect ) {
1469
+ if ( fxn ) {
1470
+ if ( fxn === true ) {
1482
1471
  // default selector uses exact match unless 'filter-match' class is found
1483
1472
  filterMatched = data.isMatch ?
1484
1473
  // data.iExact may be a number
@@ -1504,7 +1493,7 @@
1504
1493
  // Look for match, and add child row data for matching
1505
1494
  } else {
1506
1495
  txt = ( data.iExact + data.childRowText )
1507
- .indexOf( tsf.parseFilter( c, data.iFilter, columnIndex, data.parsed[ columnIndex ] ) );
1496
+ .indexOf( tsf.parseFilter( c, data.iFilter, data ) );
1508
1497
  result = ( ( !wo.filter_startsWith && txt >= 0 ) || ( wo.filter_startsWith && txt === 0 ) );
1509
1498
  }
1510
1499
  } else {
@@ -1524,7 +1513,6 @@
1524
1513
  isChild, childRow, lastSearch, showRow, showParent, time, val, indx,
1525
1514
  notFiltered, searchFiltered, query, injected, res, id, txt,
1526
1515
  storedFilters = $.extend( [], filters ),
1527
- regex = tsf.regex,
1528
1516
  c = table.config,
1529
1517
  wo = c.widgetOptions,
1530
1518
  // data object passed to filters; anyMatch is a flag for the filters
@@ -1606,7 +1594,7 @@
1606
1594
  );
1607
1595
  if ( wo.filter_columnAnyMatch ) {
1608
1596
  // specific columns search
1609
- query = data.anyMatchFilter.split( regex.andSplit );
1597
+ query = data.anyMatchFilter.split( tsfRegex.andSplit );
1610
1598
  injected = false;
1611
1599
  for ( indx = 0; indx < query.length; indx++ ) {
1612
1600
  res = query[ indx ].split( ':' );
@@ -1641,12 +1629,12 @@
1641
1629
  // there are no changes from beginning of filter
1642
1630
  val.indexOf( lastSearch[indx] || '' ) === 0 &&
1643
1631
  // if there is NOT a logical 'or', or range ( 'to' or '-' ) in the string
1644
- !regex.alreadyFiltered.test( val ) &&
1632
+ !tsfRegex.alreadyFiltered.test( val ) &&
1645
1633
  // if we are not doing exact matches, using '|' ( logical or ) or not '!'
1646
- !regex.exactTest.test( val ) &&
1634
+ !tsfRegex.exactTest.test( val ) &&
1647
1635
  // don't search only filtered if the value is negative
1648
1636
  // ( '> -10' => '> -100' will ignore hidden rows )
1649
- !( regex.isNeg1.test( val ) || regex.isNeg2.test( val ) ) &&
1637
+ !( tsfRegex.isNeg1.test( val ) || tsfRegex.isNeg2.test( val ) ) &&
1650
1638
  // if filtering using a select without a 'filter-match' class ( exact match ) - fixes #593
1651
1639
  !( val !== '' && c.$filters && c.$filters.filter( '[data-column="' + indx + '"]' ).find( 'select' ).length &&
1652
1640
  !c.$headerIndexed[indx].hasClass( 'filter-match' ) );
@@ -1664,7 +1652,7 @@
1664
1652
  // replace accents
1665
1653
  data.anyMatchFilter = ts.replaceAccents( data.anyMatchFilter );
1666
1654
  }
1667
- if ( wo.filter_defaultFilter && regex.iQuery.test( vars.defaultAnyFilter ) ) {
1655
+ if ( wo.filter_defaultFilter && tsfRegex.iQuery.test( vars.defaultAnyFilter ) ) {
1668
1656
  data.anyMatchFilter = tsf.defaultFilter( data.anyMatchFilter, vars.defaultAnyFilter );
1669
1657
  // clear search filtered flag because default filters are not saved to the last search
1670
1658
  searchFiltered = false;
@@ -1681,9 +1669,9 @@
1681
1669
 
1682
1670
  txt = $rows[ rowIndex ].className;
1683
1671
  // the first row can never be a child row
1684
- isChild = rowIndex && regex.child.test( txt );
1672
+ isChild = rowIndex && tsfRegex.child.test( txt );
1685
1673
  // skip child rows & already filtered rows
1686
- if ( isChild || ( searchFiltered && regex.filtered.test( txt ) ) ) {
1674
+ if ( isChild || ( searchFiltered && tsfRegex.filtered.test( txt ) ) ) {
1687
1675
  continue;
1688
1676
  }
1689
1677
 
@@ -1793,7 +1781,6 @@
1793
1781
  // custom select source function for a SPECIFIC COLUMN
1794
1782
  arry = fxn( table, column, onlyAvail );
1795
1783
  }
1796
-
1797
1784
  if ( arry === false ) {
1798
1785
  // fall back to original method
1799
1786
  arry = tsf.getOptions( table, column, onlyAvail );
@@ -1807,18 +1794,19 @@
1807
1794
  return false;
1808
1795
  }
1809
1796
  table = $( table )[0];
1810
- var cts, txt, indx, len,
1797
+ var cts, txt, indx, len, parsedTxt, str,
1811
1798
  c = table.config,
1812
1799
  validColumn = typeof column !== 'undefined' && column !== null && column >= 0 && column < c.columns,
1813
1800
  parsed = [];
1814
-
1815
1801
  // get unique elements and sort the list
1816
1802
  // if $.tablesorter.sortText exists ( not in the original tablesorter ),
1817
1803
  // then natural sort the list otherwise use a basic sort
1818
1804
  arry = $.grep( arry, function( value, indx ) {
1805
+ if ( value.text ) {
1806
+ return true;
1807
+ }
1819
1808
  return $.inArray( value, arry ) === indx;
1820
1809
  });
1821
-
1822
1810
  if ( validColumn && c.$headerIndexed[ column ].hasClass( 'filter-select-nosort' ) ) {
1823
1811
  // unsorted select options
1824
1812
  return arry;
@@ -1827,22 +1815,30 @@
1827
1815
  // parse select option values
1828
1816
  for ( indx = 0; indx < len; indx++ ) {
1829
1817
  txt = arry[ indx ];
1818
+ // check for object
1819
+ str = txt.text ? txt.text : txt;
1820
+ // sortNatural breaks if you don't pass it strings
1821
+ parsedTxt = ( validColumn && c.parsers && c.parsers.length &&
1822
+ c.parsers[ column ].format( str, table, [], column ) || str ).toString();
1823
+ parsedTxt = c.widgetOptions.filter_ignoreCase ? parsedTxt.toLowerCase() : parsedTxt;
1830
1824
  // parse array data using set column parser; this DOES NOT pass the original
1831
1825
  // table cell to the parser format function
1832
- parsed.push({
1833
- t : txt,
1834
- // check parser length - fixes #934
1835
- p : validColumn && c.parsers && c.parsers.length &&
1836
- c.parsers[ column ].format( txt, table, [], column ) || txt
1837
- });
1826
+ if ( txt.text ) {
1827
+ txt.parsed = parsedTxt;
1828
+ parsed.push( txt );
1829
+ } else {
1830
+ parsed.push({
1831
+ text : txt,
1832
+ // check parser length - fixes #934
1833
+ parsed : parsedTxt
1834
+ });
1835
+ }
1838
1836
  }
1839
-
1840
1837
  // sort parsed select options
1841
1838
  cts = c.textSorter || '';
1842
1839
  parsed.sort( function( a, b ) {
1843
- // sortNatural breaks if you don't pass it strings
1844
- var x = a.p.toString(),
1845
- y = b.p.toString();
1840
+ var x = a.parsed,
1841
+ y = b.parsed;
1846
1842
  if ( validColumn && typeof cts === 'function' ) {
1847
1843
  // custom OVERALL text sorter
1848
1844
  return cts( x, y, true, column, table );
@@ -1860,7 +1856,7 @@
1860
1856
  arry = [];
1861
1857
  len = parsed.length;
1862
1858
  for ( indx = 0; indx < len; indx++ ) {
1863
- arry.push( parsed[indx].t );
1859
+ arry.push( parsed[indx] );
1864
1860
  }
1865
1861
  return arry;
1866
1862
  }
@@ -1920,7 +1916,7 @@
1920
1916
  return;
1921
1917
  }
1922
1918
 
1923
- var indx, val, txt, t, $filters, $filter,
1919
+ var indx, val, txt, t, $filters, $filter, option,
1924
1920
  c = table.config,
1925
1921
  wo = c.widgetOptions,
1926
1922
  node = c.$headerIndexed[ column ],
@@ -1945,23 +1941,45 @@
1945
1941
  if ( $.isArray( arry ) ) {
1946
1942
  // build option list
1947
1943
  for ( indx = 0; indx < arry.length; indx++ ) {
1948
- txt = arry[indx] = ( '' + arry[indx] ).replace( tsf.regex.quote, '&quot;' );
1949
- val = txt;
1950
- // allow including a symbol in the selectSource array
1951
- // 'a-z|A through Z' so that 'a-z' becomes the option value
1952
- // and 'A through Z' becomes the option text
1953
- if ( txt.indexOf( wo.filter_selectSourceSeparator ) >= 0 ) {
1954
- t = txt.split( wo.filter_selectSourceSeparator );
1955
- val = t[0];
1956
- txt = t[1];
1944
+ option = arry[ indx ];
1945
+ if ( option.text ) {
1946
+ // OBJECT!! add data-function-name in case the value is set in filter_functions
1947
+ option['data-function-name'] = typeof option.value === 'undefined' ? option.text : option.value;
1948
+
1949
+ // support jQuery < v1.8, otherwise the below code could be shortened to
1950
+ // options += $( '<option>', option )[ 0 ].outerHTML;
1951
+ options += '<option';
1952
+ for ( val in option ) {
1953
+ if ( option.hasOwnProperty( val ) && val !== 'text' ) {
1954
+ options += ' ' + val + '="' + option[ val ] + '"';
1955
+ }
1956
+ }
1957
+ if ( !option.value ) {
1958
+ options += ' value="' + option.text + '"';
1959
+ }
1960
+ options += '>' + option.text + '</option>';
1961
+ // above code is needed in jQuery < v1.8
1962
+
1963
+ // make sure we don't turn an object into a string (objects without a "text" property)
1964
+ } else if ( '' + option !== '[object Object]' ) {
1965
+ txt = option = ( '' + option ).replace( tsfRegex.quote, '&quot;' );
1966
+ val = txt;
1967
+ // allow including a symbol in the selectSource array
1968
+ // 'a-z|A through Z' so that 'a-z' becomes the option value
1969
+ // and 'A through Z' becomes the option text
1970
+ if ( txt.indexOf( wo.filter_selectSourceSeparator ) >= 0 ) {
1971
+ t = txt.split( wo.filter_selectSourceSeparator );
1972
+ val = t[0];
1973
+ txt = t[1];
1974
+ }
1975
+ // replace quotes - fixes #242 & ignore empty strings
1976
+ // see http://stackoverflow.com/q/14990971/145346
1977
+ options += option !== '' ?
1978
+ '<option ' +
1979
+ ( val === txt ? '' : 'data-function-name="' + option + '" ' ) +
1980
+ 'value="' + val + '">' + txt +
1981
+ '</option>' : '';
1957
1982
  }
1958
- // replace quotes - fixes #242 & ignore empty strings
1959
- // see http://stackoverflow.com/q/14990971/145346
1960
- options += arry[indx] !== '' ?
1961
- '<option ' +
1962
- ( val === txt ? '' : 'data-function-name="' + arry[indx] + '" ' ) +
1963
- 'value="' + val + '">' + txt +
1964
- '</option>' : '';
1965
1983
  }
1966
1984
  // clear arry so it doesn't get appended twice
1967
1985
  arry = [];
@@ -2007,6 +2025,9 @@
2007
2025
  }
2008
2026
  };
2009
2027
 
2028
+ // filter regex variable
2029
+ tsfRegex = tsf.regex;
2030
+
2010
2031
  ts.getFilters = function( table, getRaw, setFilters, skipFirst ) {
2011
2032
  var i, $filters, $column, cols,
2012
2033
  filters = false,
@@ -1,4 +1,4 @@
1
- /*! Parser: filetype *//*
1
+ /*! Parser: filetype - updated 11/10/2015 (v2.24.4) *//*
2
2
  * When a file type extension is found, the equivalent name is
3
3
  * prefixed into the parsed data, so sorting occurs in groups
4
4
  */
@@ -43,6 +43,7 @@
43
43
  var t,
44
44
  c = table.config,
45
45
  wo = c.widgetOptions,
46
+ groupSeparator = wo.group_separator || '-',
46
47
  i = s.lastIndexOf('.'),
47
48
  sep = $.tablesorter.fileTypes.separator,
48
49
  m = $.tablesorter.fileTypes.matching,
@@ -60,7 +61,8 @@
60
61
  if (m.indexOf(t) >= 0) {
61
62
  for (i in types) {
62
63
  if ((sep + types[i] + sep).indexOf(t) >= 0) {
63
- return i + (wo.group_separator ? wo.group_separator : '-') + s;
64
+ // groupSeparator may use a regular expression!
65
+ return i + ( groupSeparator.toString().charAt(0) !== '/' ? wo.group_separator : '-' ) + s;
64
66
  }
65
67
  }
66
68
  }
@@ -70,4 +72,24 @@
70
72
  type: 'text'
71
73
  });
72
74
 
75
+ // sort by file extension
76
+ // converts "this.is.an.image.jpg" into "jpg.this.is.an.image"
77
+ $.tablesorter.addParser({
78
+ id: 'file-extension',
79
+ is: function() {
80
+ return false;
81
+ },
82
+ format: function( str ) {
83
+ var ext,
84
+ parts = str.split( '.' );
85
+ if ( parts.length ) {
86
+ ext = parts.pop();
87
+ parts.unshift( ext );
88
+ return parts.join( '.' );
89
+ }
90
+ return str;
91
+ },
92
+ type: 'text'
93
+ });
94
+
73
95
  })(jQuery);