jquery-tablesorter 1.19.1 → 1.19.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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);