jquery-tablesorter 1.18.1 → 1.18.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.
@@ -1,4 +1,4 @@
1
- /*! TableSorter (FORK) v2.23.1 *//*
1
+ /*! TableSorter (FORK) v2.23.2 *//*
2
2
  * Client-side table sorting with ease!
3
3
  * @requires jQuery v1.2.6+
4
4
  *
@@ -26,7 +26,7 @@
26
26
 
27
27
  var ts = this;
28
28
 
29
- ts.version = '2.23.1';
29
+ ts.version = '2.23.2';
30
30
 
31
31
  ts.parsers = [];
32
32
  ts.widgets = [];
@@ -151,6 +151,15 @@
151
151
  nextNone : 'activate to remove the sort'
152
152
  };
153
153
 
154
+ ts.regex = {
155
+ templateContent : /\{content\}/g,
156
+ templateIcon : /\{icon\}/g,
157
+ templateName : /\{name\}/i,
158
+ spaces : /\s+/g,
159
+ nonWord : /\W/g,
160
+ formElements : /(input|select|button|textarea)/i
161
+ };
162
+
154
163
  // These methods can be applied on table.config instance
155
164
  ts.instanceMethods = {};
156
165
 
@@ -443,7 +452,9 @@
443
452
  // if headerTemplate is empty, don't reformat the header cell
444
453
  if ( c.headerTemplate !== '' && !$t.find('.' + ts.css.headerIn).length ) {
445
454
  // set up header template
446
- t = c.headerTemplate.replace(/\{content\}/g, $t.html()).replace(/\{icon\}/g, $t.find('.' + ts.css.icon).length ? '' : i);
455
+ t = c.headerTemplate
456
+ .replace(ts.regex.templateContent, $t.html())
457
+ .replace(ts.regex.templateIcon, $t.find('.' + ts.css.icon).length ? '' : i);
447
458
  if (c.onRenderTemplate) {
448
459
  h = c.onRenderTemplate.apply( $t, [ index, t ] );
449
460
  if (h && typeof h === 'string') { t = h; } // only change t if something is returned
@@ -856,7 +867,7 @@
856
867
  .join( c.namespace + ' ' );
857
868
  // apply easy methods that trigger bound events
858
869
  $table
859
- .unbind( events.replace( /\s+/g, ' ' ) )
870
+ .unbind( events.replace( ts.regex.spaces, ' ' ) )
860
871
  .bind( 'sortReset' + c.namespace, function( e, callback ) {
861
872
  e.stopPropagation();
862
873
  // using this.config to ensure functions are getting a non-cached version of the config
@@ -1003,7 +1014,7 @@
1003
1014
  c.namespace = '.tablesorter' + Math.random().toString(16).slice(2);
1004
1015
  } else {
1005
1016
  // make sure namespace starts with a period & doesn't have weird characters
1006
- c.namespace = '.' + c.namespace.replace(/\W/g, '');
1017
+ c.namespace = '.' + c.namespace.replace(ts.regex.nonWord, '');
1007
1018
  }
1008
1019
 
1009
1020
  c.$table.children().children('tr').attr('role', 'row');
@@ -1231,7 +1242,7 @@
1231
1242
  }
1232
1243
  }
1233
1244
  t = ( c.pointerDown + ' ' + c.pointerUp + ' ' + c.pointerClick + ' sort keyup ' )
1234
- .replace(/\s+/g, ' ')
1245
+ .replace(ts.regex.spaces, ' ')
1235
1246
  .split(' ')
1236
1247
  .join(c.namespace + ' ');
1237
1248
  // apply event handling to headers and/or additional headers (stickyheaders, scroller, etc)
@@ -1265,7 +1276,7 @@
1265
1276
  }
1266
1277
  downTarget = null;
1267
1278
  // prevent sort being triggered on form elements
1268
- if ( /(input|select|button|textarea)/i.test(e.target.nodeName) ||
1279
+ if ( ts.regex.formElements.test(e.target.nodeName) ||
1269
1280
  // nosort class name, or elements within a nosort container
1270
1281
  $target.hasClass(c.cssNoSort) || $target.parents('.' + c.cssNoSort).length > 0 ||
1271
1282
  // elements within a button
@@ -1551,13 +1562,13 @@
1551
1562
  .join(c.namespace + ' ');
1552
1563
  $t
1553
1564
  .removeData('tablesorter')
1554
- .unbind( events.replace(/\s+/g, ' ') );
1565
+ .unbind( events.replace(ts.regex.spaces, ' ') );
1555
1566
  c.$headers.add($f)
1556
1567
  .removeClass( [ ts.css.header, c.cssHeader, c.cssAsc, c.cssDesc, ts.css.sortAsc, ts.css.sortDesc, ts.css.sortNone ].join(' ') )
1557
1568
  .removeAttr('data-column')
1558
1569
  .removeAttr('aria-label')
1559
1570
  .attr('aria-disabled', 'true');
1560
- $r.find(c.selectorSort).unbind( ('mousedown mouseup keypress '.split(' ').join(c.namespace + ' ')).replace(/\s+/g, ' ') );
1571
+ $r.find(c.selectorSort).unbind( ('mousedown mouseup keypress '.split(' ').join(c.namespace + ' ')).replace(ts.regex.spaces, ' ') );
1561
1572
  ts.restoreHeaders(table);
1562
1573
  $t.toggleClass(ts.css.table + ' ' + c.tableClass + ' tablesorter-' + c.theme, removeClasses === false);
1563
1574
  // clear flag in case the plugin is initialized again
@@ -1573,11 +1584,9 @@
1573
1584
 
1574
1585
  // *** sort functions ***
1575
1586
  // regex used in natural sort
1576
- ts.regex = {
1577
- chunk : /(^([+\-]?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?)?$|^0x[0-9a-f]+$|\d+)/gi, // chunk/tokenize numbers & letters
1578
- chunks: /(^\\0|\\0$)/, // replace chunks @ ends
1579
- hex: /^0x[0-9a-f]+$/i // hex
1580
- };
1587
+ ts.regex.chunk = /(^([+\-]?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?)?$|^0x[0-9a-f]+$|\d+)/gi; // chunk/tokenize numbers & letters
1588
+ ts.regex.chunks = /(^\\0|\\0$)/; // replace chunks @ ends
1589
+ ts.regex.hex = /^0x[0-9a-f]+$/i; // hex
1581
1590
 
1582
1591
  // Natural sort - https://github.com/overset/javascript-natural-sort (date sorting removed)
1583
1592
  // this function will only accept strings, or you'll see 'TypeError: undefined is not a function'
@@ -1804,7 +1813,7 @@
1804
1813
  if (c.debug) { time = new Date(); }
1805
1814
  // look for widgets to apply from in table class
1806
1815
  // stop using \b otherwise this matches 'ui-widget-content' & adds 'content' widget
1807
- wd = new RegExp( '\\s' + c.widgetClass.replace( /\{name\}/i, '([\\w-]+)' ) + '\\s', 'g' );
1816
+ wd = new RegExp( '\\s' + c.widgetClass.replace( ts.regex.templateName, '([\\w-]+)' ) + '\\s', 'g' );
1808
1817
  if ( tableClass.match( wd ) ) {
1809
1818
  // extract out the widget id from the table class (widget id's can include dashes)
1810
1819
  w = tableClass.match( wd );
@@ -2030,6 +2039,10 @@
2030
2039
  return $.trim(val);
2031
2040
  };
2032
2041
 
2042
+ ts.regex.comma = /,/g;
2043
+ ts.regex.digitNonUS = /[\s|\.]/g;
2044
+ ts.regex.digitNegativeTest = /^\s*\([.\d]+\)/;
2045
+ ts.regex.digitNegativeReplace = /^\s*\(([.\d]+)\)/;
2033
2046
  ts.formatFloat = function(s, table) {
2034
2047
  if (typeof s !== 'string' || s === '') { return s; }
2035
2048
  // allow using formatFloat without a table; defaults to US number format
@@ -2038,24 +2051,28 @@
2038
2051
  typeof table !== 'undefined' ? table : true;
2039
2052
  if (t) {
2040
2053
  // US Format - 1,234,567.89 -> 1234567.89
2041
- s = s.replace(/,/g, '');
2054
+ s = s.replace(ts.regex.comma, '');
2042
2055
  } else {
2043
2056
  // German Format = 1.234.567,89 -> 1234567.89
2044
2057
  // French Format = 1 234 567,89 -> 1234567.89
2045
- s = s.replace(/[\s|\.]/g, '').replace(/,/g, '.');
2058
+ s = s.replace(ts.regex.digitNonUS, '').replace(ts.regex.comma, '.');
2046
2059
  }
2047
- if (/^\s*\([.\d]+\)/.test(s)) {
2060
+ if (ts.regex.digitNegativeTest.test(s)) {
2048
2061
  // make (#) into a negative number -> (10) = -10
2049
- s = s.replace(/^\s*\(([.\d]+)\)/, '-$1');
2062
+ s = s.replace(ts.regex.digitNegativeReplace, '-$1');
2050
2063
  }
2051
2064
  i = parseFloat(s);
2052
2065
  // return the text instead of zero
2053
2066
  return isNaN(i) ? $.trim(s) : i;
2054
2067
  };
2055
2068
 
2069
+ ts.regex.digitTest = /^[\-+(]?\d+[)]?$/;
2070
+ ts.regex.digitReplace = /[,.'"\s]/g;
2056
2071
  ts.isDigit = function(s) {
2057
2072
  // replace all unwanted chars and match
2058
- return isNaN(s) ? (/^[\-+(]?\d+[)]?$/).test(s.toString().replace(/[,.'"\s]/g, '')) : s !== '';
2073
+ return isNaN(s) ?
2074
+ ts.regex.digitTest.test( s.toString().replace( ts.regex.digitReplace, '' ) ) :
2075
+ s !== '';
2059
2076
  };
2060
2077
 
2061
2078
  }()
@@ -2114,65 +2131,76 @@
2114
2131
  type: 'text'
2115
2132
  });
2116
2133
 
2134
+ ts.regex.nondigit = /[^\w,. \-()]/g;
2117
2135
  ts.addParser({
2118
2136
  id: 'digit',
2119
2137
  is: function(s) {
2120
2138
  return ts.isDigit(s);
2121
2139
  },
2122
2140
  format: function(s, table) {
2123
- var n = ts.formatFloat((s || '').replace(/[^\w,. \-()]/g, ''), table);
2141
+ var n = ts.formatFloat((s || '').replace(ts.regex.nondigit, ''), table);
2124
2142
  return s && typeof n === 'number' ? n :
2125
2143
  s ? $.trim( s && table.config.ignoreCase ? s.toLocaleLowerCase() : s ) : s;
2126
2144
  },
2127
2145
  type: 'numeric'
2128
2146
  });
2129
2147
 
2148
+ ts.regex.currencyReplace = /[+\-,. ]/g;
2149
+ ts.regex.currencyTest = /^\(?\d+[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]|[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]\d+\)?$/;
2130
2150
  ts.addParser({
2131
2151
  id: 'currency',
2132
2152
  is: function(s) {
2133
- s = (s || '').replace(/[+\-,. ]/g, '');
2153
+ s = (s || '').replace(ts.regex.currencyReplace, '');
2134
2154
  // test for £$€¤¥¢
2135
- return (/^\(?\d+[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]|[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]\d+\)?$/).test(s);
2155
+ return ts.regex.currencyTest.test(s);
2136
2156
  },
2137
2157
  format: function(s, table) {
2138
- var n = ts.formatFloat((s || '').replace(/[^\w,. \-()]/g, ''), table);
2158
+ var n = ts.formatFloat((s || '').replace(ts.regex.nondigit, ''), table);
2139
2159
  return s && typeof n === 'number' ? n :
2140
2160
  s ? $.trim( s && table.config.ignoreCase ? s.toLocaleLowerCase() : s ) : s;
2141
2161
  },
2142
2162
  type: 'numeric'
2143
2163
  });
2144
2164
 
2165
+ // too many protocols to add them all https://en.wikipedia.org/wiki/URI_scheme
2166
+ // now, this regex can be updated before initialization
2167
+ ts.regex.urlProtocolTest = /^(https?|ftp|file):\/\//;
2168
+ ts.regex.urlProtocolReplace = /(https?|ftp|file):\/\//;
2145
2169
  ts.addParser({
2146
2170
  id: 'url',
2147
2171
  is: function(s) {
2148
- return (/^(https?|ftp|file):\/\//).test(s);
2172
+ return ts.regex.urlProtocolTest.test(s);
2149
2173
  },
2150
2174
  format: function(s) {
2151
- return s ? $.trim(s.replace(/(https?|ftp|file):\/\//, '')) : s;
2175
+ return s ? $.trim(s.replace(ts.regex.urlProtocolReplace, '')) : s;
2152
2176
  },
2153
2177
  parsed : true, // filter widget flag
2154
2178
  type: 'text'
2155
2179
  });
2156
2180
 
2181
+ ts.regex.dash = /-/g;
2182
+ ts.regex.isoDate = /^\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}/;
2157
2183
  ts.addParser({
2158
2184
  id: 'isoDate',
2159
2185
  is: function(s) {
2160
- return (/^\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}/).test(s);
2186
+ return ts.regex.isoDate.test(s);
2161
2187
  },
2162
2188
  format: function(s, table) {
2163
- var date = s ? new Date( s.replace(/-/g, '/') ) : s;
2189
+ var date = s ? new Date( s.replace(ts.regex.dash, '/') ) : s;
2164
2190
  return date instanceof Date && isFinite(date) ? date.getTime() : s;
2165
2191
  },
2166
2192
  type: 'numeric'
2167
2193
  });
2168
2194
 
2195
+ ts.regex.percent = /%/g;
2196
+ ts.regex.percentTest = /(\d\s*?%|%\s*?\d)/;
2169
2197
  ts.addParser({
2170
2198
  id: 'percent',
2171
2199
  is: function(s) {
2172
- return (/(\d\s*?%|%\s*?\d)/).test(s) && s.length < 15;
2200
+ return ts.regex.percentTest.test(s) && s.length < 15;
2173
2201
  },
2174
2202
  format: function(s, table) {
2175
- return s ? ts.formatFloat(s.replace(/%/g, ''), table) : s;
2203
+ return s ? ts.formatFloat(s.replace(ts.regex.percent, ''), table) : s;
2176
2204
  },
2177
2205
  type: 'numeric'
2178
2206
  });
@@ -2190,27 +2218,35 @@
2190
2218
  type: 'text'
2191
2219
  });
2192
2220
 
2221
+ ts.regex.dateReplace = /(\S)([AP]M)$/i; // used by usLongDate & time parser
2222
+ ts.regex.usLongDateTest1 = /^[A-Z]{3,10}\.?\s+\d{1,2},?\s+(\d{4})(\s+\d{1,2}:\d{2}(:\d{2})?(\s+[AP]M)?)?$/i;
2223
+ ts.regex.usLongDateTest2 = /^\d{1,2}\s+[A-Z]{3,10}\s+\d{4}/i;
2193
2224
  ts.addParser({
2194
2225
  id: 'usLongDate',
2195
2226
  is: function(s) {
2196
2227
  // two digit years are not allowed cross-browser
2197
2228
  // Jan 01, 2013 12:34:56 PM or 01 Jan 2013
2198
- return (/^[A-Z]{3,10}\.?\s+\d{1,2},?\s+(\d{4})(\s+\d{1,2}:\d{2}(:\d{2})?(\s+[AP]M)?)?$/i).test(s) ||
2199
- (/^\d{1,2}\s+[A-Z]{3,10}\s+\d{4}/i).test(s);
2229
+ return ts.regex.usLongDateTest1.test(s) || ts.regex.usLongDateTest2.test(s);
2200
2230
  },
2201
2231
  format: function(s, table) {
2202
- var date = s ? new Date( s.replace(/(\S)([AP]M)$/i, '$1 $2') ) : s;
2232
+ var date = s ? new Date( s.replace(ts.regex.dateReplace, '$1 $2') ) : s;
2203
2233
  return date instanceof Date && isFinite(date) ? date.getTime() : s;
2204
2234
  },
2205
2235
  type: 'numeric'
2206
2236
  });
2207
2237
 
2238
+ // testing for ##-##-#### or ####-##-##, so it's not perfect; time can be included
2239
+ ts.regex.shortDateTest = /(^\d{1,2}[\/\s]\d{1,2}[\/\s]\d{4})|(^\d{4}[\/\s]\d{1,2}[\/\s]\d{1,2})/;
2240
+ // escaped "-" because JSHint in Firefox was showing it as an error
2241
+ ts.regex.shortDateReplace = /[\-.,]/g;
2242
+ // XXY covers MDY & DMY formats
2243
+ ts.regex.shortDateXXY = /(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{4})/;
2244
+ ts.regex.shortDateYMD = /(\d{4})[\/\s](\d{1,2})[\/\s](\d{1,2})/;
2208
2245
  ts.addParser({
2209
2246
  id: 'shortDate', // 'mmddyyyy', 'ddmmyyyy' or 'yyyymmdd'
2210
2247
  is: function(s) {
2211
- s = (s || '').replace(/\s+/g, ' ').replace(/[\-.,]/g, '/');
2212
- // testing for ##-##-#### or ####-##-##, so it's not perfect; time can be included
2213
- return (/(^\d{1,2}[\/\s]\d{1,2}[\/\s]\d{4})|(^\d{4}[\/\s]\d{1,2}[\/\s]\d{1,2})/).test(s);
2248
+ s = (s || '').replace(ts.regex.spaces, ' ').replace(ts.regex.shortDateReplace, '/');
2249
+ return ts.regex.shortDateTest.test(s);
2214
2250
  },
2215
2251
  format: function(s, table, cell, cellIndex) {
2216
2252
  if (s) {
@@ -2220,14 +2256,13 @@
2220
2256
  format = ci.length && ci[0].dateFormat ||
2221
2257
  ts.getData( ci, ts.getColumnData( table, c.headers, cellIndex ), 'dateFormat') ||
2222
2258
  c.dateFormat;
2223
- // escaped "-" because JSHint in Firefox was showing it as an error
2224
- d = s.replace(/\s+/g, ' ').replace(/[\-.,]/g, '/');
2259
+ d = s.replace(ts.regex.spaces, ' ').replace(ts.regex.shortDateReplace, '/');
2225
2260
  if (format === 'mmddyyyy') {
2226
- d = d.replace(/(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{4})/, '$3/$1/$2');
2261
+ d = d.replace(ts.regex.shortDateXXY, '$3/$1/$2');
2227
2262
  } else if (format === 'ddmmyyyy') {
2228
- d = d.replace(/(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{4})/, '$3/$2/$1');
2263
+ d = d.replace(ts.regex.shortDateXXY, '$3/$2/$1');
2229
2264
  } else if (format === 'yyyymmdd') {
2230
- d = d.replace(/(\d{4})[\/\s](\d{1,2})[\/\s](\d{1,2})/, '$1/$2/$3');
2265
+ d = d.replace(ts.regex.shortDateYMD, '$1/$2/$3');
2231
2266
  }
2232
2267
  date = new Date(d);
2233
2268
  return date instanceof Date && isFinite(date) ? date.getTime() : s;
@@ -2237,13 +2272,14 @@
2237
2272
  type: 'numeric'
2238
2273
  });
2239
2274
 
2275
+ ts.regex.timeTest = /^(([0-2]?\d:[0-5]\d)|([0-1]?\d:[0-5]\d\s?([AP]M)))$/i;
2240
2276
  ts.addParser({
2241
2277
  id: 'time',
2242
2278
  is: function(s) {
2243
- return (/^(([0-2]?\d:[0-5]\d)|([0-1]?\d:[0-5]\d\s?([AP]M)))$/i).test(s);
2279
+ return ts.regex.timeTest.test(s);
2244
2280
  },
2245
2281
  format: function(s, table) {
2246
- var date = s ? new Date( '2000/01/01 ' + s.replace(/(\S)([AP]M)$/i, '$1 $2') ) : s;
2282
+ var date = s ? new Date( '2000/01/01 ' + s.replace(ts.regex.dateReplace, '$1 $2') ) : s;
2247
2283
  return date instanceof Date && isFinite(date) ? date.getTime() : s;
2248
2284
  },
2249
2285
  type: 'numeric'
@@ -4,7 +4,7 @@
4
4
  ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀▀██
5
5
  █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀
6
6
  */
7
- /*! tablesorter (FORK) - updated 08-19-2015 (v2.23.1)*/
7
+ /*! tablesorter (FORK) - updated 08-23-2015 (v2.23.2)*/
8
8
  /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */
9
9
  (function(factory) {
10
10
  if (typeof define === 'function' && define.amd) {
@@ -372,14 +372,15 @@
372
372
 
373
373
  })(jQuery);
374
374
 
375
- /*! Widget: filter - updated 8/17/2015 (v2.23.0) *//*
375
+ /*! Widget: filter - updated 8/23/2015 (v2.23.2) *//*
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 ts = $.tablesorter || {},
382
- tscss = ts.css;
381
+ var tsf,
382
+ ts = $.tablesorter || {},
383
+ tscss = ts.css;
383
384
 
384
385
  $.extend( tscss, {
385
386
  filterRow : 'tablesorter-filter-row',
@@ -423,7 +424,7 @@
423
424
  },
424
425
  format: function( table, c, wo ) {
425
426
  if ( !c.$table.hasClass( 'hasFilters' ) ) {
426
- ts.filter.init( table, c, wo );
427
+ tsf.init( table, c, wo );
427
428
  }
428
429
  },
429
430
  remove: function( table, c, wo, refreshing ) {
@@ -435,7 +436,7 @@
435
436
  $table
436
437
  .removeClass( 'hasFilters' )
437
438
  // add .tsfilter namespace to all BUT search
438
- .unbind( events.replace( /\s+/g, ' ' ) )
439
+ .unbind( events.replace( ts.regex.spaces, ' ' ) )
439
440
  // remove the filter row even if refreshing, because the column might have been moved
440
441
  .find( '.' + tscss.filterRow ).remove();
441
442
  if ( refreshing ) { return; }
@@ -450,7 +451,7 @@
450
451
  }
451
452
  });
452
453
 
453
- ts.filter = {
454
+ tsf = ts.filter = {
454
455
 
455
456
  // regex used in filter 'check' functions - not for general use and not documented
456
457
  regex: {
@@ -459,9 +460,13 @@
459
460
  filtered : /filtered/, // filtered (hidden) row class name; updated in the script
460
461
  type : /undefined|number/, // check type
461
462
  exact : /(^[\"\'=]+)|([\"\'=]+$)/g, // exact match (allow '==')
462
- nondigit : /[^\w,. \-()]/g, // replace non-digits (from digit & currency parser)
463
463
  operators : /[<>=]/g, // replace operators
464
- query : '(q|query)' // replace filter queries
464
+ query : '(q|query)', // replace filter queries
465
+ wild01 : /\?/g, // wild card match 0 or 1
466
+ wild0More : /\*/g, // wild care match 0 or more
467
+ quote : /\"/g,
468
+ isNeg1 : /(>=?\s*-\d)/,
469
+ isNeg2 : /(<=?\s*\d)/
465
470
  },
466
471
  // function( c, data ) { }
467
472
  // c = table.config
@@ -478,27 +483,27 @@
478
483
  // data.parsed = array ( by column ) of boolean values ( from filter_useParsedData or 'filter-parsed' class )
479
484
  types: {
480
485
  or : function( c, data, vars ) {
481
- if ( /\|/.test( data.iFilter ) || ts.filter.regex.orSplit.test( data.filter ) ) {
486
+ if ( tsf.regex.orTest.test( data.iFilter ) || tsf.regex.orSplit.test( data.filter ) ) {
482
487
  var indx, filterMatched, query, regex,
483
488
  // duplicate data but split filter
484
489
  data2 = $.extend( {}, data ),
485
490
  index = data.index,
486
491
  parsed = data.parsed[ index ],
487
- filter = data.filter.split( ts.filter.regex.orSplit ),
488
- iFilter = data.iFilter.split( ts.filter.regex.orSplit ),
492
+ filter = data.filter.split( tsf.regex.orSplit ),
493
+ iFilter = data.iFilter.split( tsf.regex.orSplit ),
489
494
  len = filter.length;
490
495
  for ( indx = 0; indx < len; indx++ ) {
491
496
  data2.nestedFilters = true;
492
- data2.filter = '' + ( ts.filter.parseFilter( c, filter[ indx ], index, parsed ) || '' );
493
- data2.iFilter = '' + ( ts.filter.parseFilter( c, iFilter[ indx ], index, parsed ) || '' );
494
- query = '(' + ( ts.filter.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')';
497
+ data2.filter = '' + ( tsf.parseFilter( c, filter[ indx ], index, parsed ) || '' );
498
+ data2.iFilter = '' + ( tsf.parseFilter( c, iFilter[ indx ], index, parsed ) || '' );
499
+ query = '(' + ( tsf.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')';
495
500
  try {
496
501
  // use try/catch, because query may not be a valid regex if "|" is contained within a partial regex search,
497
502
  // e.g "/(Alex|Aar" -> Uncaught SyntaxError: Invalid regular expression: /(/(Alex)/: Unterminated group
498
503
  regex = new RegExp( data.isMatch ? query : '^' + query + '$', c.widgetOptions.filter_ignoreCase ? 'i' : '' );
499
504
  // filterMatched = data2.filter === '' && indx > 0 ? true
500
505
  // look for an exact match with the 'or' unless the 'filter-match' class is found
501
- filterMatched = regex.test( data2.exact ) || ts.filter.processTypes( c, data2, vars );
506
+ filterMatched = regex.test( data2.exact ) || tsf.processTypes( c, data2, vars );
502
507
  if ( filterMatched ) {
503
508
  return filterMatched;
504
509
  }
@@ -513,27 +518,27 @@
513
518
  },
514
519
  // Look for an AND or && operator ( logical and )
515
520
  and : function( c, data, vars ) {
516
- if ( ts.filter.regex.andTest.test( data.filter ) ) {
521
+ if ( tsf.regex.andTest.test( data.filter ) ) {
517
522
  var indx, filterMatched, result, query, regex,
518
523
  // duplicate data but split filter
519
524
  data2 = $.extend( {}, data ),
520
525
  index = data.index,
521
526
  parsed = data.parsed[ index ],
522
- filter = data.filter.split( ts.filter.regex.andSplit ),
523
- iFilter = data.iFilter.split( ts.filter.regex.andSplit ),
527
+ filter = data.filter.split( tsf.regex.andSplit ),
528
+ iFilter = data.iFilter.split( tsf.regex.andSplit ),
524
529
  len = filter.length;
525
530
  for ( indx = 0; indx < len; indx++ ) {
526
531
  data2.nestedFilters = true;
527
- data2.filter = '' + ( ts.filter.parseFilter( c, filter[ indx ], index, parsed ) || '' );
528
- data2.iFilter = '' + ( ts.filter.parseFilter( c, iFilter[ indx ], index, parsed ) || '' );
529
- query = ( '(' + ( ts.filter.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')' )
532
+ data2.filter = '' + ( tsf.parseFilter( c, filter[ indx ], index, parsed ) || '' );
533
+ data2.iFilter = '' + ( tsf.parseFilter( c, iFilter[ indx ], index, parsed ) || '' );
534
+ query = ( '(' + ( tsf.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')' )
530
535
  // replace wild cards since /(a*)/i will match anything
531
- .replace( /\?/g, '\\S{1}' ).replace( /\*/g, '\\S*' );
536
+ .replace( tsf.regex.wild01, '\\S{1}' ).replace( tsf.regex.wild0More, '\\S*' );
532
537
  try {
533
538
  // use try/catch just in case RegExp is invalid
534
539
  regex = new RegExp( data.isMatch ? query : '^' + query + '$', c.widgetOptions.filter_ignoreCase ? 'i' : '' );
535
540
  // look for an exact match with the 'and' unless the 'filter-match' class is found
536
- result = ( regex.test( data2.exact ) || ts.filter.processTypes( c, data2, vars ) );
541
+ result = ( regex.test( data2.exact ) || tsf.processTypes( c, data2, vars ) );
537
542
  if ( indx === 0 ) {
538
543
  filterMatched = result;
539
544
  } else {
@@ -550,10 +555,10 @@
550
555
  },
551
556
  // Look for regex
552
557
  regex: function( c, data ) {
553
- if ( ts.filter.regex.regex.test( data.filter ) ) {
558
+ if ( tsf.regex.regex.test( data.filter ) ) {
554
559
  var matches,
555
560
  // cache regex per column for optimal speed
556
- regex = data.filter_regexCache[ data.index ] || ts.filter.regex.regex.exec( data.filter ),
561
+ regex = data.filter_regexCache[ data.index ] || tsf.regex.regex.exec( data.filter ),
557
562
  isRegex = regex instanceof RegExp;
558
563
  try {
559
564
  if ( !isRegex ) {
@@ -572,18 +577,18 @@
572
577
  // Look for operators >, >=, < or <=
573
578
  operators: function( c, data ) {
574
579
  // ignore empty strings... because '' < 10 is true
575
- if ( /^[<>]=?/.test( data.iFilter ) && data.iExact !== '' ) {
580
+ if ( tsf.regex.operTest.test( data.iFilter ) && data.iExact !== '' ) {
576
581
  var cachedValue, result, txt,
577
582
  table = c.table,
578
583
  index = data.index,
579
584
  parsed = data.parsed[index],
580
- query = ts.formatFloat( data.iFilter.replace( ts.filter.regex.operators, '' ), table ),
585
+ query = ts.formatFloat( data.iFilter.replace( tsf.regex.operators, '' ), table ),
581
586
  parser = c.parsers[index],
582
587
  savedSearch = query;
583
588
  // parse filter value in case we're comparing numbers ( dates )
584
589
  if ( parsed || parser.type === 'numeric' ) {
585
- txt = $.trim( '' + data.iFilter.replace( ts.filter.regex.operators, '' ) );
586
- result = ts.filter.parseFilter( c, txt, index, true );
590
+ txt = $.trim( '' + data.iFilter.replace( tsf.regex.operators, '' ) );
591
+ result = tsf.parseFilter( c, txt, index, true );
587
592
  query = ( typeof result === 'number' && result !== '' && !isNaN( result ) ) ? result : query;
588
593
  }
589
594
  // iExact may be numeric - see issue #149;
@@ -592,13 +597,13 @@
592
597
  typeof data.cache !== 'undefined' ) {
593
598
  cachedValue = data.cache;
594
599
  } else {
595
- txt = isNaN( data.iExact ) ? data.iExact.replace( ts.filter.regex.nondigit, '' ) : data.iExact;
600
+ txt = isNaN( data.iExact ) ? data.iExact.replace( ts.regex.nondigit, '' ) : data.iExact;
596
601
  cachedValue = ts.formatFloat( txt, table );
597
602
  }
598
- if ( />/.test( data.iFilter ) ) {
599
- result = />=/.test( data.iFilter ) ? cachedValue >= query : cachedValue > query;
600
- } else if ( /</.test( data.iFilter ) ) {
601
- result = /<=/.test( data.iFilter ) ? cachedValue <= query : cachedValue < query;
603
+ if ( tsf.regex.gtTest.test( data.iFilter ) ) {
604
+ result = tsf.regex.gteTest.test( data.iFilter ) ? cachedValue >= query : cachedValue > query;
605
+ } else if ( tsf.regex.ltTest.test( data.iFilter ) ) {
606
+ result = tsf.regex.lteTest.test( data.iFilter ) ? cachedValue <= query : cachedValue < query;
602
607
  }
603
608
  // keep showing all rows if nothing follows the operator
604
609
  if ( !result && savedSearch === '' ) {
@@ -610,13 +615,13 @@
610
615
  },
611
616
  // Look for a not match
612
617
  notMatch: function( c, data ) {
613
- if ( /^\!/.test( data.iFilter ) ) {
618
+ if ( tsf.regex.notTest.test( data.iFilter ) ) {
614
619
  var indx,
615
620
  txt = data.iFilter.replace( '!', '' ),
616
- filter = ts.filter.parseFilter( c, txt, data.index, data.parsed[data.index] ) || '';
617
- if ( ts.filter.regex.exact.test( filter ) ) {
621
+ filter = tsf.parseFilter( c, txt, data.index, data.parsed[data.index] ) || '';
622
+ if ( tsf.regex.exact.test( filter ) ) {
618
623
  // look for exact not matches - see #628
619
- filter = filter.replace( ts.filter.regex.exact, '' );
624
+ filter = filter.replace( tsf.regex.exact, '' );
620
625
  return filter === '' ? true : $.trim( filter ) !== data.iExact;
621
626
  } else {
622
627
  indx = data.iExact.search( $.trim( filter ) );
@@ -628,27 +633,27 @@
628
633
  // Look for quotes or equals to get an exact match; ignore type since iExact could be numeric
629
634
  exact: function( c, data ) {
630
635
  /*jshint eqeqeq:false */
631
- if ( ts.filter.regex.exact.test( data.iFilter ) ) {
632
- var txt = data.iFilter.replace( ts.filter.regex.exact, '' ),
633
- filter = ts.filter.parseFilter( c, txt, data.index, data.parsed[data.index] ) || '';
636
+ if ( tsf.regex.exact.test( data.iFilter ) ) {
637
+ var txt = data.iFilter.replace( tsf.regex.exact, '' ),
638
+ filter = tsf.parseFilter( c, txt, data.index, data.parsed[data.index] ) || '';
634
639
  return data.anyMatch ? $.inArray( filter, data.rowArray ) >= 0 : filter == data.iExact;
635
640
  }
636
641
  return null;
637
642
  },
638
643
  // Look for a range ( using ' to ' or ' - ' ) - see issue #166; thanks matzhu!
639
644
  range : function( c, data ) {
640
- if ( ts.filter.regex.toTest.test( data.iFilter ) ) {
645
+ if ( tsf.regex.toTest.test( data.iFilter ) ) {
641
646
  var result, tmp, range1, range2,
642
647
  table = c.table,
643
648
  index = data.index,
644
649
  parsed = data.parsed[index],
645
650
  // make sure the dash is for a range and not indicating a negative number
646
- query = data.iFilter.split( ts.filter.regex.toSplit );
651
+ query = data.iFilter.split( tsf.regex.toSplit );
647
652
 
648
- tmp = query[0].replace( ts.filter.regex.nondigit, '' ) || '';
649
- range1 = ts.formatFloat( ts.filter.parseFilter( c, tmp, index, parsed ), table );
650
- tmp = query[1].replace( ts.filter.regex.nondigit, '' ) || '';
651
- range2 = ts.formatFloat( ts.filter.parseFilter( c, tmp, index, parsed ), table );
653
+ tmp = query[0].replace( ts.regex.nondigit, '' ) || '';
654
+ range1 = ts.formatFloat( tsf.parseFilter( c, tmp, index, parsed ), table );
655
+ tmp = query[1].replace( ts.regex.nondigit, '' ) || '';
656
+ range2 = ts.formatFloat( tsf.parseFilter( c, tmp, index, parsed ), table );
652
657
  // parse filter value in case we're comparing numbers ( dates )
653
658
  if ( parsed || c.parsers[index].type === 'numeric' ) {
654
659
  result = c.parsers[ index ].format( '' + query[0], table, c.$headers.eq( index ), index );
@@ -659,7 +664,7 @@
659
664
  if ( ( parsed || c.parsers[ index ].type === 'numeric' ) && !isNaN( range1 ) && !isNaN( range2 ) ) {
660
665
  result = data.cache;
661
666
  } else {
662
- tmp = isNaN( data.iExact ) ? data.iExact.replace( ts.filter.regex.nondigit, '' ) : data.iExact;
667
+ tmp = isNaN( data.iExact ) ? data.iExact.replace( ts.regex.nondigit, '' ) : data.iExact;
663
668
  result = ts.formatFloat( tmp, table );
664
669
  }
665
670
  if ( range1 > range2 ) {
@@ -671,18 +676,18 @@
671
676
  },
672
677
  // Look for wild card: ? = single, * = multiple, or | = logical OR
673
678
  wild : function( c, data ) {
674
- if ( /[\?\*\|]/.test( data.iFilter ) ) {
679
+ if ( tsf.regex.wildOrTest.test( data.iFilter ) ) {
675
680
  var index = data.index,
676
681
  parsed = data.parsed[ index ],
677
- query = '' + ( ts.filter.parseFilter( c, data.iFilter, index, parsed ) || '' );
682
+ query = '' + ( tsf.parseFilter( c, data.iFilter, index, parsed ) || '' );
678
683
  // look for an exact match with the 'or' unless the 'filter-match' class is found
679
- if ( !/\?\*/.test( query ) && data.nestedFilters ) {
684
+ if ( !tsf.regex.wildTest.test( query ) && data.nestedFilters ) {
680
685
  query = data.isMatch ? query : '^(' + query + ')$';
681
686
  }
682
687
  // parsing the filter may not work properly when using wildcards =/
683
688
  try {
684
689
  return new RegExp(
685
- query.replace( /\?/g, '\\S{1}' ).replace( /\*/g, '\\S*' ),
690
+ query.replace( tsf.regex.wild01, '\\S{1}' ).replace( tsf.regex.wild0More, '\\S*' ),
686
691
  c.widgetOptions.filter_ignoreCase ? 'i' : ''
687
692
  )
688
693
  .test( data.exact );
@@ -694,12 +699,12 @@
694
699
  },
695
700
  // fuzzy text search; modified from https://github.com/mattyork/fuzzy ( MIT license )
696
701
  fuzzy: function( c, data ) {
697
- if ( /^~/.test( data.iFilter ) ) {
702
+ if ( tsf.regex.fuzzyTest.test( data.iFilter ) ) {
698
703
  var indx,
699
704
  patternIndx = 0,
700
705
  len = data.iExact.length,
701
706
  txt = data.iFilter.slice( 1 ),
702
- pattern = ts.filter.parseFilter( c, txt, data.index, data.parsed[data.index] ) || '';
707
+ pattern = tsf.parseFilter( c, txt, data.index, data.parsed[data.index] ) || '';
703
708
  for ( indx = 0; indx < len; indx++ ) {
704
709
  if ( data.iExact[ indx ] === pattern[ patternIndx ] ) {
705
710
  patternIndx += 1;
@@ -722,7 +727,7 @@
722
727
  }, ts.language );
723
728
 
724
729
  var options, string, txt, $header, column, filters, val, fxn, noSelect,
725
- regex = ts.filter.regex;
730
+ regex = tsf.regex;
726
731
  c.$table.addClass( 'hasFilters' );
727
732
 
728
733
  // define timers so using clearTimeout won't cause an undefined error
@@ -733,7 +738,7 @@
733
738
  wo.filter_anyColumnSelector = '[data-column="all"],[data-column="any"]';
734
739
  wo.filter_multipleColumnSelector = '[data-column*="-"],[data-column*=","]';
735
740
 
736
- val = '\\{' + ts.filter.regex.query + '\\}';
741
+ val = '\\{' + tsf.regex.query + '\\}';
737
742
  $.extend( regex, {
738
743
  child : new RegExp( c.cssChildRow ),
739
744
  filtered : new RegExp( wo.filter_filteredRow ),
@@ -742,9 +747,20 @@
742
747
  toSplit : new RegExp( '(?:\\s+(?:-|' + ts.language.to + ')\\s+)', 'gi' ),
743
748
  andTest : new RegExp( '\\s+(' + ts.language.and + '|&&)\\s+', 'i' ),
744
749
  andSplit : new RegExp( '(?:\\s+(?:' + ts.language.and + '|&&)\\s+)', 'gi' ),
750
+ orTest : /\|/,
745
751
  orSplit : new RegExp( '(?:\\s+(?:' + ts.language.or + ')\\s+|\\|)', 'gi' ),
746
752
  iQuery : new RegExp( val, 'i' ),
747
- igQuery : new RegExp( val, 'ig' )
753
+ igQuery : new RegExp( val, 'ig' ),
754
+ operTest : /^[<>]=?/,
755
+ gtTest : />/,
756
+ gteTest : />=/,
757
+ ltTest : /</,
758
+ lteTest : /<=/,
759
+ notTest : /^\!/,
760
+ wildOrTest : /[\?\*\|]/,
761
+ wildTest : /\?\*/,
762
+ fuzzyTest : /^~/,
763
+ exactTest : /[=\"\|!]/
748
764
  });
749
765
 
750
766
  // don't build filter row if columnFilters is false or all columns are set to 'filter-false'
@@ -752,7 +768,7 @@
752
768
  val = c.$headers.filter( '.filter-false, .parser-false' ).length;
753
769
  if ( wo.filter_columnFilters !== false && val !== c.$headers.length ) {
754
770
  // build filter row
755
- ts.filter.buildRow( table, c, wo );
771
+ tsf.buildRow( table, c, wo );
756
772
  }
757
773
 
758
774
  txt = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search '
@@ -765,13 +781,13 @@
765
781
  c.$table.find( '.' + tscss.filterRow ).toggleClass( wo.filter_filteredRow, val ); // fixes #450
766
782
  if ( !/(search|filter)/.test( event.type ) ) {
767
783
  event.stopPropagation();
768
- ts.filter.buildDefault( table, true );
784
+ tsf.buildDefault( table, true );
769
785
  }
770
786
  if ( event.type === 'filterReset' ) {
771
787
  c.$table.find( '.' + tscss.filter ).add( wo.filter_$externalFilters ).val( '' );
772
- ts.filter.searching( table, [] );
788
+ tsf.searching( table, [] );
773
789
  } else if ( event.type === 'filterEnd' ) {
774
- ts.filter.buildDefault( table, true );
790
+ tsf.buildDefault( table, true );
775
791
  } else {
776
792
  // send false argument to force a new search; otherwise if the filter hasn't changed,
777
793
  // it will return
@@ -785,7 +801,7 @@
785
801
  // pass true ( skipFirst ) to prevent the tablesorter.setFilters function from skipping the first
786
802
  // input ensures all inputs are updated when a search is triggered on the table
787
803
  // $( 'table' ).trigger( 'search', [...] );
788
- ts.filter.searching( table, filter, true );
804
+ tsf.searching( table, filter, true );
789
805
  }
790
806
  return false;
791
807
  });
@@ -818,7 +834,7 @@
818
834
  noSelect = !( $header.hasClass( 'filter-false' ) || $header.hasClass( 'parser-false' ) );
819
835
  options = '';
820
836
  if ( fxn === true && noSelect ) {
821
- ts.filter.buildSelect( table, column );
837
+ tsf.buildSelect( table, column );
822
838
  } else if ( typeof fxn === 'object' && noSelect ) {
823
839
  // add custom drop down list
824
840
  for ( string in fxn ) {
@@ -851,7 +867,7 @@
851
867
  fxn = $.isFunction( txt ) ? true : ts.getColumnData( table, txt, column );
852
868
  if ( fxn ) {
853
869
  // updating so the extra options are appended
854
- ts.filter.buildSelect( c.table, column, '', true, $header.hasClass( wo.filter_onlyAvail ) );
870
+ tsf.buildSelect( c.table, column, '', true, $header.hasClass( wo.filter_onlyAvail ) );
855
871
  }
856
872
  }
857
873
  }
@@ -859,22 +875,22 @@
859
875
  }
860
876
  // not really updating, but if the column has both the 'filter-select' class &
861
877
  // filter_functions set to true, it would append the same options twice.
862
- ts.filter.buildDefault( table, true );
878
+ tsf.buildDefault( table, true );
863
879
 
864
- ts.filter.bindSearch( table, c.$table.find( '.' + tscss.filter ), true );
880
+ tsf.bindSearch( table, c.$table.find( '.' + tscss.filter ), true );
865
881
  if ( wo.filter_external ) {
866
- ts.filter.bindSearch( table, wo.filter_external );
882
+ tsf.bindSearch( table, wo.filter_external );
867
883
  }
868
884
 
869
885
  if ( wo.filter_hideFilters ) {
870
- ts.filter.hideFilters( table, c );
886
+ tsf.hideFilters( table, c );
871
887
  }
872
888
 
873
889
  // show processing icon
874
890
  if ( c.showProcessing ) {
875
891
  txt = 'filterStart filterEnd '.split( ' ' ).join( c.namespace + 'filter ' );
876
892
  c.$table
877
- .unbind( txt.replace( /\s+/g, ' ' ) )
893
+ .unbind( txt.replace( ts.regex.spaces, ' ' ) )
878
894
  .bind( txt, function( event, columns ) {
879
895
  // only add processing to certain columns to all columns
880
896
  $header = ( columns ) ?
@@ -894,11 +910,11 @@
894
910
  // add default values
895
911
  txt = 'tablesorter-initialized pagerBeforeInitialized '.split( ' ' ).join( c.namespace + 'filter ' );
896
912
  c.$table
897
- .unbind( txt.replace( /\s+/g, ' ' ) )
913
+ .unbind( txt.replace( ts.regex.spaces, ' ' ) )
898
914
  .bind( txt, function() {
899
915
  // redefine 'wo' as it does not update properly inside this callback
900
916
  var wo = this.config.widgetOptions;
901
- filters = ts.filter.setDefaults( table, c, wo ) || [];
917
+ filters = tsf.setDefaults( table, c, wo ) || [];
902
918
  if ( filters.length ) {
903
919
  // prevent delayInit from triggering a cache build if filters are empty
904
920
  if ( !( c.delayInit && filters.join( '' ) === '' ) ) {
@@ -909,7 +925,7 @@
909
925
  // trigger init after setTimeout to prevent multiple filterStart/End/Init triggers
910
926
  setTimeout( function() {
911
927
  if ( !wo.filter_initialized ) {
912
- ts.filter.filterInitComplete( c );
928
+ tsf.filterInitComplete( c );
913
929
  }
914
930
  }, 100 );
915
931
  });
@@ -917,7 +933,7 @@
917
933
  if ( c.pager && c.pager.initialized && !wo.filter_initialized ) {
918
934
  c.$table.trigger( 'filterFomatterUpdate' );
919
935
  setTimeout( function() {
920
- ts.filter.filterInitComplete( c );
936
+ tsf.filterInitComplete( c );
921
937
  }, 100 );
922
938
  }
923
939
  },
@@ -938,7 +954,7 @@
938
954
  completed = function() {
939
955
  wo.filter_initialized = true;
940
956
  c.$table.trigger( 'filterInit', c );
941
- ts.filter.findRows( c.table, c.$table.data( 'lastSearch' ) || [] );
957
+ tsf.findRows( c.table, c.$table.data( 'lastSearch' ) || [] );
942
958
  };
943
959
  if ( $.isEmptyObject( wo.filter_formatter ) ) {
944
960
  completed();
@@ -1090,7 +1106,7 @@
1090
1106
  // use data attribute instead of jQuery data since the head is cloned without including
1091
1107
  // the data/binding
1092
1108
  .attr( 'data-lastSearchTime', new Date().getTime() )
1093
- .unbind( tmp.replace( /\s+/g, ' ' ) )
1109
+ .unbind( tmp.replace( ts.regex.spaces, ' ' ) )
1094
1110
  // include change for select - fixes #473
1095
1111
  .bind( 'keyup' + namespace, function( event ) {
1096
1112
  $( this ).attr( 'data-lastSearchTime', new Date().getTime() );
@@ -1110,17 +1126,18 @@
1110
1126
  return;
1111
1127
  }
1112
1128
  // change event = no delay; last true flag tells getFilters to skip newest timed input
1113
- ts.filter.searching( table, true, true );
1129
+ tsf.searching( table, true, true );
1114
1130
  })
1115
1131
  .bind( 'search change keypress '.split( ' ' ).join( namespace + ' ' ), function( event ) {
1116
- var column = $( this ).data( 'column' );
1132
+ // don't get cached data, in case data-column changes dynamically
1133
+ var column = parseInt( $( this ).attr( 'data-column' ), 10 );
1117
1134
  // don't allow 'change' event to process if the input value is the same - fixes #685
1118
1135
  if ( event.which === 13 || event.type === 'search' ||
1119
1136
  event.type === 'change' && this.value !== c.lastSearch[column] ) {
1120
1137
  event.preventDefault();
1121
1138
  // init search with no delay
1122
1139
  $( this ).attr( 'data-lastSearchTime', new Date().getTime() );
1123
- ts.filter.searching( table, false, true );
1140
+ tsf.searching( table, false, true );
1124
1141
  }
1125
1142
  });
1126
1143
  },
@@ -1130,11 +1147,11 @@
1130
1147
  if ( typeof filter === 'undefined' || filter === true ) {
1131
1148
  // delay filtering
1132
1149
  wo.searchTimer = setTimeout( function() {
1133
- ts.filter.checkFilters( table, filter, skipFirst );
1150
+ tsf.checkFilters( table, filter, skipFirst );
1134
1151
  }, wo.filter_liveSearch ? wo.filter_searchDelay : 10 );
1135
1152
  } else {
1136
1153
  // skip delay
1137
- ts.filter.checkFilters( table, filter, skipFirst );
1154
+ tsf.checkFilters( table, filter, skipFirst );
1138
1155
  }
1139
1156
  },
1140
1157
  checkFilters: function( table, filter, skipFirst ) {
@@ -1148,7 +1165,7 @@
1148
1165
  // update cache if delayInit set & pager has initialized ( after user initiates a search )
1149
1166
  if ( c.delayInit && c.pager && c.pager.initialized ) {
1150
1167
  c.$table.trigger( 'updateCache', [ function() {
1151
- ts.filter.checkFilters( table, false, skipFirst );
1168
+ tsf.checkFilters( table, false, skipFirst );
1152
1169
  } ] );
1153
1170
  }
1154
1171
  return;
@@ -1179,11 +1196,11 @@
1179
1196
  if ( c.showProcessing ) {
1180
1197
  // give it time for the processing icon to kick in
1181
1198
  setTimeout( function() {
1182
- ts.filter.findRows( table, filters, combinedFilters );
1199
+ tsf.findRows( table, filters, combinedFilters );
1183
1200
  return false;
1184
1201
  }, 30 );
1185
1202
  } else {
1186
- ts.filter.findRows( table, filters, combinedFilters );
1203
+ tsf.findRows( table, filters, combinedFilters );
1187
1204
  return false;
1188
1205
  }
1189
1206
  },
@@ -1226,8 +1243,8 @@
1226
1243
  },
1227
1244
  defaultFilter: function( filter, mask ) {
1228
1245
  if ( filter === '' ) { return filter; }
1229
- var regex = ts.filter.regex.iQuery,
1230
- maskLen = mask.match( ts.filter.regex.igQuery ).length,
1246
+ var regex = tsf.regex.iQuery,
1247
+ maskLen = mask.match( tsf.regex.igQuery ).length,
1231
1248
  query = maskLen > 1 ? $.trim( filter ).split( /\s/ ) : [ $.trim( filter ) ],
1232
1249
  len = query.length - 1,
1233
1250
  indx = 0,
@@ -1263,7 +1280,10 @@
1263
1280
  // & don't target 'all' column inputs if they don't exist
1264
1281
  targets = wo.filter_initialized || !$input.filter( wo.filter_anyColumnSelector ).length,
1265
1282
  columns = [],
1266
- val = $.trim( ts.filter.getLatestSearch( $input ).attr( 'data-column' ) || '' );
1283
+ val = $.trim( tsf.getLatestSearch( $input ).attr( 'data-column' ) || '' );
1284
+ if ( !/[,-]/.test(val) && val.length === 1 ) {
1285
+ return parseInt( val, 10 );
1286
+ }
1267
1287
  // process column range
1268
1288
  if ( targets && /-/.test( val ) ) {
1269
1289
  ranges = val.match( /(\d+)\s*-\s*(\d+)/g );
@@ -1310,9 +1330,9 @@
1310
1330
  var ffxn,
1311
1331
  filterMatched = null,
1312
1332
  matches = null;
1313
- for ( ffxn in ts.filter.types ) {
1333
+ for ( ffxn in tsf.types ) {
1314
1334
  if ( $.inArray( ffxn, vars.excludeMatch ) < 0 && matches === null ) {
1315
- matches = ts.filter.types[ffxn]( c, data, vars );
1335
+ matches = tsf.types[ffxn]( c, data, vars );
1316
1336
  if ( matches !== null ) {
1317
1337
  filterMatched = matches;
1318
1338
  }
@@ -1321,16 +1341,23 @@
1321
1341
  return filterMatched;
1322
1342
  },
1323
1343
  processRow: function( c, data, vars ) {
1324
- var columnIndex, hasSelect, result, val, filterMatched,
1344
+ var hasSelect, result, val, filterMatched,
1325
1345
  fxn, ffxn, txt,
1326
- regex = ts.filter.regex,
1346
+ regex = tsf.regex,
1327
1347
  wo = c.widgetOptions,
1328
- showRow = true;
1348
+ showRow = true,
1349
+
1350
+ // if wo.filter_$anyMatch data-column attribute is changed dynamically
1351
+ // we don't want to do an "anyMatch" search on one column using data
1352
+ // for the entire row - see #998
1353
+ columnIndex = wo.filter_$anyMatch && wo.filter_$anyMatch.length ?
1354
+ // look for multiple columns '1-3,4-6,8'
1355
+ tsf.multipleColumns( c, wo.filter_$anyMatch ) :
1356
+ [];
1357
+
1329
1358
  data.$cells = data.$row.children();
1330
1359
 
1331
- if ( data.anyMatchFlag ) {
1332
- // look for multiple columns '1-3,4-6,8'
1333
- columnIndex = ts.filter.multipleColumns( c, wo.filter_$anyMatch );
1360
+ if ( data.anyMatchFlag && columnIndex.length > 1 ) {
1334
1361
  data.anyMatch = true;
1335
1362
  data.isMatch = true;
1336
1363
  data.rowArray = data.$cells.map( function( i ) {
@@ -1354,7 +1381,7 @@
1354
1381
  data.cache = data.cacheArray.slice( 0, -1 ).join( ' ' );
1355
1382
 
1356
1383
  vars.excludeMatch = vars.noAnyMatch;
1357
- filterMatched = ts.filter.processTypes( c, data, vars );
1384
+ filterMatched = tsf.processTypes( c, data, vars );
1358
1385
 
1359
1386
  if ( filterMatched !== null ) {
1360
1387
  showRow = filterMatched;
@@ -1415,7 +1442,7 @@
1415
1442
 
1416
1443
  val = true;
1417
1444
  if ( wo.filter_defaultFilter && regex.iQuery.test( vars.defaultColFilter[ columnIndex ] ) ) {
1418
- data.filter = ts.filter.defaultFilter( data.filter, vars.defaultColFilter[ columnIndex ] );
1445
+ data.filter = tsf.defaultFilter( data.filter, vars.defaultColFilter[ columnIndex ] );
1419
1446
  // val is used to indicate that a filter select is using a default filter;
1420
1447
  // so we override the exact & partial matches
1421
1448
  val = false;
@@ -1446,13 +1473,13 @@
1446
1473
  if ( filterMatched === null ) {
1447
1474
  // cycle through the different filters
1448
1475
  // filters return a boolean or null if nothing matches
1449
- filterMatched = ts.filter.processTypes( c, data, vars );
1476
+ filterMatched = tsf.processTypes( c, data, vars );
1450
1477
  if ( filterMatched !== null ) {
1451
1478
  result = filterMatched;
1452
1479
  // Look for match, and add child row data for matching
1453
1480
  } else {
1454
1481
  txt = ( data.iExact + data.childRowText )
1455
- .indexOf( ts.filter.parseFilter( c, data.iFilter, columnIndex, data.parsed[ columnIndex ] ) );
1482
+ .indexOf( tsf.parseFilter( c, data.iFilter, columnIndex, data.parsed[ columnIndex ] ) );
1456
1483
  result = ( ( !wo.filter_startsWith && txt >= 0 ) || ( wo.filter_startsWith && txt === 0 ) );
1457
1484
  }
1458
1485
  } else {
@@ -1472,7 +1499,7 @@
1472
1499
  isChild, childRow, lastSearch, showRow, time, val, indx,
1473
1500
  notFiltered, searchFiltered, query, injected, res, id, txt,
1474
1501
  storedFilters = $.extend( [], filters ),
1475
- regex = ts.filter.regex,
1502
+ regex = tsf.regex,
1476
1503
  c = table.config,
1477
1504
  wo = c.widgetOptions,
1478
1505
  // data object passed to filters; anyMatch is a flag for the filters
@@ -1549,7 +1576,7 @@
1549
1576
  data.anyMatchFlag = true;
1550
1577
  data.anyMatchFilter = '' + (
1551
1578
  filters[ c.columns ] ||
1552
- wo.filter_$anyMatch && ts.filter.getLatestSearch( wo.filter_$anyMatch ).val() ||
1579
+ wo.filter_$anyMatch && tsf.getLatestSearch( wo.filter_$anyMatch ).val() ||
1553
1580
  ''
1554
1581
  );
1555
1582
  if ( wo.filter_columnAnyMatch ) {
@@ -1591,10 +1618,10 @@
1591
1618
  // if there is NOT a logical 'or', or range ( 'to' or '-' ) in the string
1592
1619
  !regex.alreadyFiltered.test( val ) &&
1593
1620
  // if we are not doing exact matches, using '|' ( logical or ) or not '!'
1594
- !/[=\"\|!]/.test( val ) &&
1621
+ !regex.exactTest.test( val ) &&
1595
1622
  // don't search only filtered if the value is negative
1596
1623
  // ( '> -10' => '> -100' will ignore hidden rows )
1597
- !( /(>=?\s*-\d)/.test( val ) || /(<=?\s*\d)/.test( val ) ) &&
1624
+ !( regex.isNeg1.test( val ) || regex.isNeg2.test( val ) ) &&
1598
1625
  // if filtering using a select without a 'filter-match' class ( exact match ) - fixes #593
1599
1626
  !( val !== '' && c.$filters && c.$filters.eq( indx ).find( 'select' ).length &&
1600
1627
  !c.$headerIndexed[indx].hasClass( 'filter-match' ) );
@@ -1613,7 +1640,7 @@
1613
1640
  data.anyMatchFilter = ts.replaceAccents( data.anyMatchFilter );
1614
1641
  }
1615
1642
  if ( wo.filter_defaultFilter && regex.iQuery.test( vars.defaultAnyFilter ) ) {
1616
- data.anyMatchFilter = ts.filter.defaultFilter( data.anyMatchFilter, vars.defaultAnyFilter );
1643
+ data.anyMatchFilter = tsf.defaultFilter( data.anyMatchFilter, vars.defaultAnyFilter );
1617
1644
  // clear search filtered flag because default filters are not saved to the last search
1618
1645
  searchFiltered = false;
1619
1646
  }
@@ -1656,7 +1683,7 @@
1656
1683
  '';
1657
1684
  }
1658
1685
 
1659
- showRow = ts.filter.processRow( c, data, vars );
1686
+ showRow = tsf.processRow( c, data, vars );
1660
1687
  childRow = rowData.$row.filter( ':gt( 0 )' );
1661
1688
 
1662
1689
  if ( wo.filter_childRows && childRow.length ) {
@@ -1667,7 +1694,7 @@
1667
1694
  data.cacheArray = rowData.child[ indx ];
1668
1695
  data.rawArray = data.cacheArray;
1669
1696
  // use OR comparison on child rows
1670
- showRow = showRow || ts.filter.processRow( c, data, vars );
1697
+ showRow = showRow || tsf.processRow( c, data, vars );
1671
1698
  }
1672
1699
  }
1673
1700
  childRow.toggleClass( wo.filter_filteredRow, !showRow );
@@ -1729,7 +1756,7 @@
1729
1756
  }
1730
1757
  if ( arry === false ) {
1731
1758
  // fall back to original method
1732
- arry = ts.filter.getOptions( table, column, onlyAvail );
1759
+ arry = tsf.getOptions( table, column, onlyAvail );
1733
1760
  }
1734
1761
 
1735
1762
  // get unique elements and sort the list
@@ -1841,13 +1868,13 @@
1841
1868
  // nothing included in arry ( external source ), so get the options from
1842
1869
  // filter_selectSource or column data
1843
1870
  if ( typeof arry === 'undefined' || arry === '' ) {
1844
- arry = ts.filter.getOptionSource( table, column, onlyAvail );
1871
+ arry = tsf.getOptionSource( table, column, onlyAvail );
1845
1872
  }
1846
1873
 
1847
1874
  if ( $.isArray( arry ) ) {
1848
1875
  // build option list
1849
1876
  for ( indx = 0; indx < arry.length; indx++ ) {
1850
- txt = arry[indx] = ( '' + arry[indx] ).replace( /\"/g, '&quot;' );
1877
+ txt = arry[indx] = ( '' + arry[indx] ).replace( tsf.regex.quote, '&quot;' );
1851
1878
  val = txt;
1852
1879
  // allow including a symbol in the selectSource array
1853
1880
  // 'a-z|A through Z' so that 'a-z' becomes the option value
@@ -1903,7 +1930,7 @@
1903
1930
  // look for the filter-select class; build/update it if found
1904
1931
  if ( ( $header.hasClass( 'filter-select' ) ||
1905
1932
  ts.getColumnData( table, wo.filter_functions, columnIndex ) === true ) && noSelect ) {
1906
- ts.filter.buildSelect( table, columnIndex, '', updating, $header.hasClass( wo.filter_onlyAvail ) );
1933
+ tsf.buildSelect( table, columnIndex, '', updating, $header.hasClass( wo.filter_onlyAvail ) );
1907
1934
  }
1908
1935
  }
1909
1936
  }
@@ -1939,7 +1966,7 @@
1939
1966
  $column = $filters.filter( cols );
1940
1967
  if ( $column.length ) {
1941
1968
  // move the latest search to the first slot in the array
1942
- $column = ts.filter.getLatestSearch( $column );
1969
+ $column = tsf.getLatestSearch( $column );
1943
1970
  if ( $.isArray( setFilters ) ) {
1944
1971
  // skip first ( latest input ) to maintain cursor position while typing
1945
1972
  if ( skipFirst && $column.length > 1 ) {
@@ -1989,7 +2016,7 @@
1989
2016
  // ensure new set filters are applied, even if the search is the same
1990
2017
  c.lastCombinedFilter = null;
1991
2018
  c.lastSearch = [];
1992
- ts.filter.searching( c.table, filter, skipFirst );
2019
+ tsf.searching( c.table, filter, skipFirst );
1993
2020
  c.$table.trigger( 'filterFomatterUpdate' );
1994
2021
  }
1995
2022
  return !!valid;