jquery-tablesorter 1.18.1 → 1.18.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,4 @@
1
- /* Widget: columnSelector (responsive table widget) - updated 8/17/2015 (v2.23.0) *//*
1
+ /* Widget: columnSelector (responsive table widget) - updated 8/23/2015 (v2.23.2) *//*
2
2
  * Requires tablesorter v2.8+ and jQuery 1.7+
3
3
  * by Justin Hallett & Rob Garrison
4
4
  */
@@ -224,7 +224,7 @@
224
224
  }
225
225
  // trigger columnUpdate if auto is true (it gets skipped in updateCols()
226
226
  if (colSel.auto) {
227
- c.$table.trigger('columnUpdate');
227
+ c.$table.trigger(wo.columnSelector_updated);
228
228
  }
229
229
  },
230
230
 
@@ -299,7 +299,7 @@
299
299
  if (wo.columnSelector_saveColumns && ts.storage) {
300
300
  ts.storage( c.$table[0], 'tablesorter-columnSelector', colSel.states );
301
301
  }
302
- c.$table.trigger('columnUpdate');
302
+ c.$table.trigger(wo.columnSelector_updated);
303
303
  },
304
304
 
305
305
  attachTo : function(table, elm) {
@@ -370,7 +370,9 @@
370
370
  columnSelector_priority : 'data-priority',
371
371
  // class name added to checked checkboxes - this fixes an issue with Chrome not updating FontAwesome
372
372
  // applied icons; use this class name (input.checked) instead of input:checked
373
- columnSelector_cssChecked : 'checked'
373
+ columnSelector_cssChecked : 'checked',
374
+ // event triggered when columnSelector completes
375
+ columnSelector_updated : 'columnUpdate'
374
376
 
375
377
  },
376
378
  init: function(table, thisWidget, c, wo) {
@@ -1,11 +1,12 @@
1
- /*! Widget: filter - updated 8/17/2015 (v2.23.0) *//*
1
+ /*! Widget: filter - updated 8/23/2015 (v2.23.2) *//*
2
2
  * Requires tablesorter v2.8+ and jQuery 1.7+
3
3
  * by Rob Garrison
4
4
  */
5
5
  ;( function ( $ ) {
6
6
  'use strict';
7
- var ts = $.tablesorter || {},
8
- tscss = ts.css;
7
+ var tsf,
8
+ ts = $.tablesorter || {},
9
+ tscss = ts.css;
9
10
 
10
11
  $.extend( tscss, {
11
12
  filterRow : 'tablesorter-filter-row',
@@ -49,7 +50,7 @@
49
50
  },
50
51
  format: function( table, c, wo ) {
51
52
  if ( !c.$table.hasClass( 'hasFilters' ) ) {
52
- ts.filter.init( table, c, wo );
53
+ tsf.init( table, c, wo );
53
54
  }
54
55
  },
55
56
  remove: function( table, c, wo, refreshing ) {
@@ -61,7 +62,7 @@
61
62
  $table
62
63
  .removeClass( 'hasFilters' )
63
64
  // add .tsfilter namespace to all BUT search
64
- .unbind( events.replace( /\s+/g, ' ' ) )
65
+ .unbind( events.replace( ts.regex.spaces, ' ' ) )
65
66
  // remove the filter row even if refreshing, because the column might have been moved
66
67
  .find( '.' + tscss.filterRow ).remove();
67
68
  if ( refreshing ) { return; }
@@ -76,7 +77,7 @@
76
77
  }
77
78
  });
78
79
 
79
- ts.filter = {
80
+ tsf = ts.filter = {
80
81
 
81
82
  // regex used in filter 'check' functions - not for general use and not documented
82
83
  regex: {
@@ -85,9 +86,13 @@
85
86
  filtered : /filtered/, // filtered (hidden) row class name; updated in the script
86
87
  type : /undefined|number/, // check type
87
88
  exact : /(^[\"\'=]+)|([\"\'=]+$)/g, // exact match (allow '==')
88
- nondigit : /[^\w,. \-()]/g, // replace non-digits (from digit & currency parser)
89
89
  operators : /[<>=]/g, // replace operators
90
- query : '(q|query)' // replace filter queries
90
+ query : '(q|query)', // replace filter queries
91
+ wild01 : /\?/g, // wild card match 0 or 1
92
+ wild0More : /\*/g, // wild care match 0 or more
93
+ quote : /\"/g,
94
+ isNeg1 : /(>=?\s*-\d)/,
95
+ isNeg2 : /(<=?\s*\d)/
91
96
  },
92
97
  // function( c, data ) { }
93
98
  // c = table.config
@@ -104,27 +109,27 @@
104
109
  // data.parsed = array ( by column ) of boolean values ( from filter_useParsedData or 'filter-parsed' class )
105
110
  types: {
106
111
  or : function( c, data, vars ) {
107
- if ( /\|/.test( data.iFilter ) || ts.filter.regex.orSplit.test( data.filter ) ) {
112
+ if ( tsf.regex.orTest.test( data.iFilter ) || tsf.regex.orSplit.test( data.filter ) ) {
108
113
  var indx, filterMatched, query, regex,
109
114
  // duplicate data but split filter
110
115
  data2 = $.extend( {}, data ),
111
116
  index = data.index,
112
117
  parsed = data.parsed[ index ],
113
- filter = data.filter.split( ts.filter.regex.orSplit ),
114
- iFilter = data.iFilter.split( ts.filter.regex.orSplit ),
118
+ filter = data.filter.split( tsf.regex.orSplit ),
119
+ iFilter = data.iFilter.split( tsf.regex.orSplit ),
115
120
  len = filter.length;
116
121
  for ( indx = 0; indx < len; indx++ ) {
117
122
  data2.nestedFilters = true;
118
- data2.filter = '' + ( ts.filter.parseFilter( c, filter[ indx ], index, parsed ) || '' );
119
- data2.iFilter = '' + ( ts.filter.parseFilter( c, iFilter[ indx ], index, parsed ) || '' );
120
- query = '(' + ( ts.filter.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')';
123
+ data2.filter = '' + ( tsf.parseFilter( c, filter[ indx ], index, parsed ) || '' );
124
+ data2.iFilter = '' + ( tsf.parseFilter( c, iFilter[ indx ], index, parsed ) || '' );
125
+ query = '(' + ( tsf.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')';
121
126
  try {
122
127
  // use try/catch, because query may not be a valid regex if "|" is contained within a partial regex search,
123
128
  // e.g "/(Alex|Aar" -> Uncaught SyntaxError: Invalid regular expression: /(/(Alex)/: Unterminated group
124
129
  regex = new RegExp( data.isMatch ? query : '^' + query + '$', c.widgetOptions.filter_ignoreCase ? 'i' : '' );
125
130
  // filterMatched = data2.filter === '' && indx > 0 ? true
126
131
  // look for an exact match with the 'or' unless the 'filter-match' class is found
127
- filterMatched = regex.test( data2.exact ) || ts.filter.processTypes( c, data2, vars );
132
+ filterMatched = regex.test( data2.exact ) || tsf.processTypes( c, data2, vars );
128
133
  if ( filterMatched ) {
129
134
  return filterMatched;
130
135
  }
@@ -139,27 +144,27 @@
139
144
  },
140
145
  // Look for an AND or && operator ( logical and )
141
146
  and : function( c, data, vars ) {
142
- if ( ts.filter.regex.andTest.test( data.filter ) ) {
147
+ if ( tsf.regex.andTest.test( data.filter ) ) {
143
148
  var indx, filterMatched, result, query, regex,
144
149
  // duplicate data but split filter
145
150
  data2 = $.extend( {}, data ),
146
151
  index = data.index,
147
152
  parsed = data.parsed[ index ],
148
- filter = data.filter.split( ts.filter.regex.andSplit ),
149
- iFilter = data.iFilter.split( ts.filter.regex.andSplit ),
153
+ filter = data.filter.split( tsf.regex.andSplit ),
154
+ iFilter = data.iFilter.split( tsf.regex.andSplit ),
150
155
  len = filter.length;
151
156
  for ( indx = 0; indx < len; indx++ ) {
152
157
  data2.nestedFilters = true;
153
- data2.filter = '' + ( ts.filter.parseFilter( c, filter[ indx ], index, parsed ) || '' );
154
- data2.iFilter = '' + ( ts.filter.parseFilter( c, iFilter[ indx ], index, parsed ) || '' );
155
- query = ( '(' + ( ts.filter.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')' )
158
+ data2.filter = '' + ( tsf.parseFilter( c, filter[ indx ], index, parsed ) || '' );
159
+ data2.iFilter = '' + ( tsf.parseFilter( c, iFilter[ indx ], index, parsed ) || '' );
160
+ query = ( '(' + ( tsf.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')' )
156
161
  // replace wild cards since /(a*)/i will match anything
157
- .replace( /\?/g, '\\S{1}' ).replace( /\*/g, '\\S*' );
162
+ .replace( tsf.regex.wild01, '\\S{1}' ).replace( tsf.regex.wild0More, '\\S*' );
158
163
  try {
159
164
  // use try/catch just in case RegExp is invalid
160
165
  regex = new RegExp( data.isMatch ? query : '^' + query + '$', c.widgetOptions.filter_ignoreCase ? 'i' : '' );
161
166
  // look for an exact match with the 'and' unless the 'filter-match' class is found
162
- result = ( regex.test( data2.exact ) || ts.filter.processTypes( c, data2, vars ) );
167
+ result = ( regex.test( data2.exact ) || tsf.processTypes( c, data2, vars ) );
163
168
  if ( indx === 0 ) {
164
169
  filterMatched = result;
165
170
  } else {
@@ -176,10 +181,10 @@
176
181
  },
177
182
  // Look for regex
178
183
  regex: function( c, data ) {
179
- if ( ts.filter.regex.regex.test( data.filter ) ) {
184
+ if ( tsf.regex.regex.test( data.filter ) ) {
180
185
  var matches,
181
186
  // cache regex per column for optimal speed
182
- regex = data.filter_regexCache[ data.index ] || ts.filter.regex.regex.exec( data.filter ),
187
+ regex = data.filter_regexCache[ data.index ] || tsf.regex.regex.exec( data.filter ),
183
188
  isRegex = regex instanceof RegExp;
184
189
  try {
185
190
  if ( !isRegex ) {
@@ -198,18 +203,18 @@
198
203
  // Look for operators >, >=, < or <=
199
204
  operators: function( c, data ) {
200
205
  // ignore empty strings... because '' < 10 is true
201
- if ( /^[<>]=?/.test( data.iFilter ) && data.iExact !== '' ) {
206
+ if ( tsf.regex.operTest.test( data.iFilter ) && data.iExact !== '' ) {
202
207
  var cachedValue, result, txt,
203
208
  table = c.table,
204
209
  index = data.index,
205
210
  parsed = data.parsed[index],
206
- query = ts.formatFloat( data.iFilter.replace( ts.filter.regex.operators, '' ), table ),
211
+ query = ts.formatFloat( data.iFilter.replace( tsf.regex.operators, '' ), table ),
207
212
  parser = c.parsers[index],
208
213
  savedSearch = query;
209
214
  // parse filter value in case we're comparing numbers ( dates )
210
215
  if ( parsed || parser.type === 'numeric' ) {
211
- txt = $.trim( '' + data.iFilter.replace( ts.filter.regex.operators, '' ) );
212
- result = ts.filter.parseFilter( c, txt, index, true );
216
+ txt = $.trim( '' + data.iFilter.replace( tsf.regex.operators, '' ) );
217
+ result = tsf.parseFilter( c, txt, index, true );
213
218
  query = ( typeof result === 'number' && result !== '' && !isNaN( result ) ) ? result : query;
214
219
  }
215
220
  // iExact may be numeric - see issue #149;
@@ -218,13 +223,13 @@
218
223
  typeof data.cache !== 'undefined' ) {
219
224
  cachedValue = data.cache;
220
225
  } else {
221
- txt = isNaN( data.iExact ) ? data.iExact.replace( ts.filter.regex.nondigit, '' ) : data.iExact;
226
+ txt = isNaN( data.iExact ) ? data.iExact.replace( ts.regex.nondigit, '' ) : data.iExact;
222
227
  cachedValue = ts.formatFloat( txt, table );
223
228
  }
224
- if ( />/.test( data.iFilter ) ) {
225
- result = />=/.test( data.iFilter ) ? cachedValue >= query : cachedValue > query;
226
- } else if ( /</.test( data.iFilter ) ) {
227
- result = /<=/.test( data.iFilter ) ? cachedValue <= query : cachedValue < query;
229
+ if ( tsf.regex.gtTest.test( data.iFilter ) ) {
230
+ result = tsf.regex.gteTest.test( data.iFilter ) ? cachedValue >= query : cachedValue > query;
231
+ } else if ( tsf.regex.ltTest.test( data.iFilter ) ) {
232
+ result = tsf.regex.lteTest.test( data.iFilter ) ? cachedValue <= query : cachedValue < query;
228
233
  }
229
234
  // keep showing all rows if nothing follows the operator
230
235
  if ( !result && savedSearch === '' ) {
@@ -236,13 +241,13 @@
236
241
  },
237
242
  // Look for a not match
238
243
  notMatch: function( c, data ) {
239
- if ( /^\!/.test( data.iFilter ) ) {
244
+ if ( tsf.regex.notTest.test( data.iFilter ) ) {
240
245
  var indx,
241
246
  txt = data.iFilter.replace( '!', '' ),
242
- filter = ts.filter.parseFilter( c, txt, data.index, data.parsed[data.index] ) || '';
243
- if ( ts.filter.regex.exact.test( filter ) ) {
247
+ filter = tsf.parseFilter( c, txt, data.index, data.parsed[data.index] ) || '';
248
+ if ( tsf.regex.exact.test( filter ) ) {
244
249
  // look for exact not matches - see #628
245
- filter = filter.replace( ts.filter.regex.exact, '' );
250
+ filter = filter.replace( tsf.regex.exact, '' );
246
251
  return filter === '' ? true : $.trim( filter ) !== data.iExact;
247
252
  } else {
248
253
  indx = data.iExact.search( $.trim( filter ) );
@@ -254,27 +259,27 @@
254
259
  // Look for quotes or equals to get an exact match; ignore type since iExact could be numeric
255
260
  exact: function( c, data ) {
256
261
  /*jshint eqeqeq:false */
257
- if ( ts.filter.regex.exact.test( data.iFilter ) ) {
258
- var txt = data.iFilter.replace( ts.filter.regex.exact, '' ),
259
- filter = ts.filter.parseFilter( c, txt, data.index, data.parsed[data.index] ) || '';
262
+ if ( tsf.regex.exact.test( data.iFilter ) ) {
263
+ var txt = data.iFilter.replace( tsf.regex.exact, '' ),
264
+ filter = tsf.parseFilter( c, txt, data.index, data.parsed[data.index] ) || '';
260
265
  return data.anyMatch ? $.inArray( filter, data.rowArray ) >= 0 : filter == data.iExact;
261
266
  }
262
267
  return null;
263
268
  },
264
269
  // Look for a range ( using ' to ' or ' - ' ) - see issue #166; thanks matzhu!
265
270
  range : function( c, data ) {
266
- if ( ts.filter.regex.toTest.test( data.iFilter ) ) {
271
+ if ( tsf.regex.toTest.test( data.iFilter ) ) {
267
272
  var result, tmp, range1, range2,
268
273
  table = c.table,
269
274
  index = data.index,
270
275
  parsed = data.parsed[index],
271
276
  // make sure the dash is for a range and not indicating a negative number
272
- query = data.iFilter.split( ts.filter.regex.toSplit );
277
+ query = data.iFilter.split( tsf.regex.toSplit );
273
278
 
274
- tmp = query[0].replace( ts.filter.regex.nondigit, '' ) || '';
275
- range1 = ts.formatFloat( ts.filter.parseFilter( c, tmp, index, parsed ), table );
276
- tmp = query[1].replace( ts.filter.regex.nondigit, '' ) || '';
277
- range2 = ts.formatFloat( ts.filter.parseFilter( c, tmp, index, parsed ), table );
279
+ tmp = query[0].replace( ts.regex.nondigit, '' ) || '';
280
+ range1 = ts.formatFloat( tsf.parseFilter( c, tmp, index, parsed ), table );
281
+ tmp = query[1].replace( ts.regex.nondigit, '' ) || '';
282
+ range2 = ts.formatFloat( tsf.parseFilter( c, tmp, index, parsed ), table );
278
283
  // parse filter value in case we're comparing numbers ( dates )
279
284
  if ( parsed || c.parsers[index].type === 'numeric' ) {
280
285
  result = c.parsers[ index ].format( '' + query[0], table, c.$headers.eq( index ), index );
@@ -285,7 +290,7 @@
285
290
  if ( ( parsed || c.parsers[ index ].type === 'numeric' ) && !isNaN( range1 ) && !isNaN( range2 ) ) {
286
291
  result = data.cache;
287
292
  } else {
288
- tmp = isNaN( data.iExact ) ? data.iExact.replace( ts.filter.regex.nondigit, '' ) : data.iExact;
293
+ tmp = isNaN( data.iExact ) ? data.iExact.replace( ts.regex.nondigit, '' ) : data.iExact;
289
294
  result = ts.formatFloat( tmp, table );
290
295
  }
291
296
  if ( range1 > range2 ) {
@@ -297,18 +302,18 @@
297
302
  },
298
303
  // Look for wild card: ? = single, * = multiple, or | = logical OR
299
304
  wild : function( c, data ) {
300
- if ( /[\?\*\|]/.test( data.iFilter ) ) {
305
+ if ( tsf.regex.wildOrTest.test( data.iFilter ) ) {
301
306
  var index = data.index,
302
307
  parsed = data.parsed[ index ],
303
- query = '' + ( ts.filter.parseFilter( c, data.iFilter, index, parsed ) || '' );
308
+ query = '' + ( tsf.parseFilter( c, data.iFilter, index, parsed ) || '' );
304
309
  // look for an exact match with the 'or' unless the 'filter-match' class is found
305
- if ( !/\?\*/.test( query ) && data.nestedFilters ) {
310
+ if ( !tsf.regex.wildTest.test( query ) && data.nestedFilters ) {
306
311
  query = data.isMatch ? query : '^(' + query + ')$';
307
312
  }
308
313
  // parsing the filter may not work properly when using wildcards =/
309
314
  try {
310
315
  return new RegExp(
311
- query.replace( /\?/g, '\\S{1}' ).replace( /\*/g, '\\S*' ),
316
+ query.replace( tsf.regex.wild01, '\\S{1}' ).replace( tsf.regex.wild0More, '\\S*' ),
312
317
  c.widgetOptions.filter_ignoreCase ? 'i' : ''
313
318
  )
314
319
  .test( data.exact );
@@ -320,12 +325,12 @@
320
325
  },
321
326
  // fuzzy text search; modified from https://github.com/mattyork/fuzzy ( MIT license )
322
327
  fuzzy: function( c, data ) {
323
- if ( /^~/.test( data.iFilter ) ) {
328
+ if ( tsf.regex.fuzzyTest.test( data.iFilter ) ) {
324
329
  var indx,
325
330
  patternIndx = 0,
326
331
  len = data.iExact.length,
327
332
  txt = data.iFilter.slice( 1 ),
328
- pattern = ts.filter.parseFilter( c, txt, data.index, data.parsed[data.index] ) || '';
333
+ pattern = tsf.parseFilter( c, txt, data.index, data.parsed[data.index] ) || '';
329
334
  for ( indx = 0; indx < len; indx++ ) {
330
335
  if ( data.iExact[ indx ] === pattern[ patternIndx ] ) {
331
336
  patternIndx += 1;
@@ -348,7 +353,7 @@
348
353
  }, ts.language );
349
354
 
350
355
  var options, string, txt, $header, column, filters, val, fxn, noSelect,
351
- regex = ts.filter.regex;
356
+ regex = tsf.regex;
352
357
  c.$table.addClass( 'hasFilters' );
353
358
 
354
359
  // define timers so using clearTimeout won't cause an undefined error
@@ -359,7 +364,7 @@
359
364
  wo.filter_anyColumnSelector = '[data-column="all"],[data-column="any"]';
360
365
  wo.filter_multipleColumnSelector = '[data-column*="-"],[data-column*=","]';
361
366
 
362
- val = '\\{' + ts.filter.regex.query + '\\}';
367
+ val = '\\{' + tsf.regex.query + '\\}';
363
368
  $.extend( regex, {
364
369
  child : new RegExp( c.cssChildRow ),
365
370
  filtered : new RegExp( wo.filter_filteredRow ),
@@ -368,9 +373,20 @@
368
373
  toSplit : new RegExp( '(?:\\s+(?:-|' + ts.language.to + ')\\s+)', 'gi' ),
369
374
  andTest : new RegExp( '\\s+(' + ts.language.and + '|&&)\\s+', 'i' ),
370
375
  andSplit : new RegExp( '(?:\\s+(?:' + ts.language.and + '|&&)\\s+)', 'gi' ),
376
+ orTest : /\|/,
371
377
  orSplit : new RegExp( '(?:\\s+(?:' + ts.language.or + ')\\s+|\\|)', 'gi' ),
372
378
  iQuery : new RegExp( val, 'i' ),
373
- igQuery : new RegExp( val, 'ig' )
379
+ igQuery : new RegExp( val, 'ig' ),
380
+ operTest : /^[<>]=?/,
381
+ gtTest : />/,
382
+ gteTest : />=/,
383
+ ltTest : /</,
384
+ lteTest : /<=/,
385
+ notTest : /^\!/,
386
+ wildOrTest : /[\?\*\|]/,
387
+ wildTest : /\?\*/,
388
+ fuzzyTest : /^~/,
389
+ exactTest : /[=\"\|!]/
374
390
  });
375
391
 
376
392
  // don't build filter row if columnFilters is false or all columns are set to 'filter-false'
@@ -378,7 +394,7 @@
378
394
  val = c.$headers.filter( '.filter-false, .parser-false' ).length;
379
395
  if ( wo.filter_columnFilters !== false && val !== c.$headers.length ) {
380
396
  // build filter row
381
- ts.filter.buildRow( table, c, wo );
397
+ tsf.buildRow( table, c, wo );
382
398
  }
383
399
 
384
400
  txt = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search '
@@ -391,13 +407,13 @@
391
407
  c.$table.find( '.' + tscss.filterRow ).toggleClass( wo.filter_filteredRow, val ); // fixes #450
392
408
  if ( !/(search|filter)/.test( event.type ) ) {
393
409
  event.stopPropagation();
394
- ts.filter.buildDefault( table, true );
410
+ tsf.buildDefault( table, true );
395
411
  }
396
412
  if ( event.type === 'filterReset' ) {
397
413
  c.$table.find( '.' + tscss.filter ).add( wo.filter_$externalFilters ).val( '' );
398
- ts.filter.searching( table, [] );
414
+ tsf.searching( table, [] );
399
415
  } else if ( event.type === 'filterEnd' ) {
400
- ts.filter.buildDefault( table, true );
416
+ tsf.buildDefault( table, true );
401
417
  } else {
402
418
  // send false argument to force a new search; otherwise if the filter hasn't changed,
403
419
  // it will return
@@ -411,7 +427,7 @@
411
427
  // pass true ( skipFirst ) to prevent the tablesorter.setFilters function from skipping the first
412
428
  // input ensures all inputs are updated when a search is triggered on the table
413
429
  // $( 'table' ).trigger( 'search', [...] );
414
- ts.filter.searching( table, filter, true );
430
+ tsf.searching( table, filter, true );
415
431
  }
416
432
  return false;
417
433
  });
@@ -444,7 +460,7 @@
444
460
  noSelect = !( $header.hasClass( 'filter-false' ) || $header.hasClass( 'parser-false' ) );
445
461
  options = '';
446
462
  if ( fxn === true && noSelect ) {
447
- ts.filter.buildSelect( table, column );
463
+ tsf.buildSelect( table, column );
448
464
  } else if ( typeof fxn === 'object' && noSelect ) {
449
465
  // add custom drop down list
450
466
  for ( string in fxn ) {
@@ -477,7 +493,7 @@
477
493
  fxn = $.isFunction( txt ) ? true : ts.getColumnData( table, txt, column );
478
494
  if ( fxn ) {
479
495
  // updating so the extra options are appended
480
- ts.filter.buildSelect( c.table, column, '', true, $header.hasClass( wo.filter_onlyAvail ) );
496
+ tsf.buildSelect( c.table, column, '', true, $header.hasClass( wo.filter_onlyAvail ) );
481
497
  }
482
498
  }
483
499
  }
@@ -485,22 +501,22 @@
485
501
  }
486
502
  // not really updating, but if the column has both the 'filter-select' class &
487
503
  // filter_functions set to true, it would append the same options twice.
488
- ts.filter.buildDefault( table, true );
504
+ tsf.buildDefault( table, true );
489
505
 
490
- ts.filter.bindSearch( table, c.$table.find( '.' + tscss.filter ), true );
506
+ tsf.bindSearch( table, c.$table.find( '.' + tscss.filter ), true );
491
507
  if ( wo.filter_external ) {
492
- ts.filter.bindSearch( table, wo.filter_external );
508
+ tsf.bindSearch( table, wo.filter_external );
493
509
  }
494
510
 
495
511
  if ( wo.filter_hideFilters ) {
496
- ts.filter.hideFilters( table, c );
512
+ tsf.hideFilters( table, c );
497
513
  }
498
514
 
499
515
  // show processing icon
500
516
  if ( c.showProcessing ) {
501
517
  txt = 'filterStart filterEnd '.split( ' ' ).join( c.namespace + 'filter ' );
502
518
  c.$table
503
- .unbind( txt.replace( /\s+/g, ' ' ) )
519
+ .unbind( txt.replace( ts.regex.spaces, ' ' ) )
504
520
  .bind( txt, function( event, columns ) {
505
521
  // only add processing to certain columns to all columns
506
522
  $header = ( columns ) ?
@@ -520,11 +536,11 @@
520
536
  // add default values
521
537
  txt = 'tablesorter-initialized pagerBeforeInitialized '.split( ' ' ).join( c.namespace + 'filter ' );
522
538
  c.$table
523
- .unbind( txt.replace( /\s+/g, ' ' ) )
539
+ .unbind( txt.replace( ts.regex.spaces, ' ' ) )
524
540
  .bind( txt, function() {
525
541
  // redefine 'wo' as it does not update properly inside this callback
526
542
  var wo = this.config.widgetOptions;
527
- filters = ts.filter.setDefaults( table, c, wo ) || [];
543
+ filters = tsf.setDefaults( table, c, wo ) || [];
528
544
  if ( filters.length ) {
529
545
  // prevent delayInit from triggering a cache build if filters are empty
530
546
  if ( !( c.delayInit && filters.join( '' ) === '' ) ) {
@@ -535,7 +551,7 @@
535
551
  // trigger init after setTimeout to prevent multiple filterStart/End/Init triggers
536
552
  setTimeout( function() {
537
553
  if ( !wo.filter_initialized ) {
538
- ts.filter.filterInitComplete( c );
554
+ tsf.filterInitComplete( c );
539
555
  }
540
556
  }, 100 );
541
557
  });
@@ -543,7 +559,7 @@
543
559
  if ( c.pager && c.pager.initialized && !wo.filter_initialized ) {
544
560
  c.$table.trigger( 'filterFomatterUpdate' );
545
561
  setTimeout( function() {
546
- ts.filter.filterInitComplete( c );
562
+ tsf.filterInitComplete( c );
547
563
  }, 100 );
548
564
  }
549
565
  },
@@ -564,7 +580,7 @@
564
580
  completed = function() {
565
581
  wo.filter_initialized = true;
566
582
  c.$table.trigger( 'filterInit', c );
567
- ts.filter.findRows( c.table, c.$table.data( 'lastSearch' ) || [] );
583
+ tsf.findRows( c.table, c.$table.data( 'lastSearch' ) || [] );
568
584
  };
569
585
  if ( $.isEmptyObject( wo.filter_formatter ) ) {
570
586
  completed();
@@ -716,7 +732,7 @@
716
732
  // use data attribute instead of jQuery data since the head is cloned without including
717
733
  // the data/binding
718
734
  .attr( 'data-lastSearchTime', new Date().getTime() )
719
- .unbind( tmp.replace( /\s+/g, ' ' ) )
735
+ .unbind( tmp.replace( ts.regex.spaces, ' ' ) )
720
736
  // include change for select - fixes #473
721
737
  .bind( 'keyup' + namespace, function( event ) {
722
738
  $( this ).attr( 'data-lastSearchTime', new Date().getTime() );
@@ -736,17 +752,18 @@
736
752
  return;
737
753
  }
738
754
  // change event = no delay; last true flag tells getFilters to skip newest timed input
739
- ts.filter.searching( table, true, true );
755
+ tsf.searching( table, true, true );
740
756
  })
741
757
  .bind( 'search change keypress '.split( ' ' ).join( namespace + ' ' ), function( event ) {
742
- var column = $( this ).data( 'column' );
758
+ // don't get cached data, in case data-column changes dynamically
759
+ var column = parseInt( $( this ).attr( 'data-column' ), 10 );
743
760
  // don't allow 'change' event to process if the input value is the same - fixes #685
744
761
  if ( event.which === 13 || event.type === 'search' ||
745
762
  event.type === 'change' && this.value !== c.lastSearch[column] ) {
746
763
  event.preventDefault();
747
764
  // init search with no delay
748
765
  $( this ).attr( 'data-lastSearchTime', new Date().getTime() );
749
- ts.filter.searching( table, false, true );
766
+ tsf.searching( table, false, true );
750
767
  }
751
768
  });
752
769
  },
@@ -756,11 +773,11 @@
756
773
  if ( typeof filter === 'undefined' || filter === true ) {
757
774
  // delay filtering
758
775
  wo.searchTimer = setTimeout( function() {
759
- ts.filter.checkFilters( table, filter, skipFirst );
776
+ tsf.checkFilters( table, filter, skipFirst );
760
777
  }, wo.filter_liveSearch ? wo.filter_searchDelay : 10 );
761
778
  } else {
762
779
  // skip delay
763
- ts.filter.checkFilters( table, filter, skipFirst );
780
+ tsf.checkFilters( table, filter, skipFirst );
764
781
  }
765
782
  },
766
783
  checkFilters: function( table, filter, skipFirst ) {
@@ -774,7 +791,7 @@
774
791
  // update cache if delayInit set & pager has initialized ( after user initiates a search )
775
792
  if ( c.delayInit && c.pager && c.pager.initialized ) {
776
793
  c.$table.trigger( 'updateCache', [ function() {
777
- ts.filter.checkFilters( table, false, skipFirst );
794
+ tsf.checkFilters( table, false, skipFirst );
778
795
  } ] );
779
796
  }
780
797
  return;
@@ -805,11 +822,11 @@
805
822
  if ( c.showProcessing ) {
806
823
  // give it time for the processing icon to kick in
807
824
  setTimeout( function() {
808
- ts.filter.findRows( table, filters, combinedFilters );
825
+ tsf.findRows( table, filters, combinedFilters );
809
826
  return false;
810
827
  }, 30 );
811
828
  } else {
812
- ts.filter.findRows( table, filters, combinedFilters );
829
+ tsf.findRows( table, filters, combinedFilters );
813
830
  return false;
814
831
  }
815
832
  },
@@ -852,8 +869,8 @@
852
869
  },
853
870
  defaultFilter: function( filter, mask ) {
854
871
  if ( filter === '' ) { return filter; }
855
- var regex = ts.filter.regex.iQuery,
856
- maskLen = mask.match( ts.filter.regex.igQuery ).length,
872
+ var regex = tsf.regex.iQuery,
873
+ maskLen = mask.match( tsf.regex.igQuery ).length,
857
874
  query = maskLen > 1 ? $.trim( filter ).split( /\s/ ) : [ $.trim( filter ) ],
858
875
  len = query.length - 1,
859
876
  indx = 0,
@@ -889,7 +906,10 @@
889
906
  // & don't target 'all' column inputs if they don't exist
890
907
  targets = wo.filter_initialized || !$input.filter( wo.filter_anyColumnSelector ).length,
891
908
  columns = [],
892
- val = $.trim( ts.filter.getLatestSearch( $input ).attr( 'data-column' ) || '' );
909
+ val = $.trim( tsf.getLatestSearch( $input ).attr( 'data-column' ) || '' );
910
+ if ( !/[,-]/.test(val) && val.length === 1 ) {
911
+ return parseInt( val, 10 );
912
+ }
893
913
  // process column range
894
914
  if ( targets && /-/.test( val ) ) {
895
915
  ranges = val.match( /(\d+)\s*-\s*(\d+)/g );
@@ -936,9 +956,9 @@
936
956
  var ffxn,
937
957
  filterMatched = null,
938
958
  matches = null;
939
- for ( ffxn in ts.filter.types ) {
959
+ for ( ffxn in tsf.types ) {
940
960
  if ( $.inArray( ffxn, vars.excludeMatch ) < 0 && matches === null ) {
941
- matches = ts.filter.types[ffxn]( c, data, vars );
961
+ matches = tsf.types[ffxn]( c, data, vars );
942
962
  if ( matches !== null ) {
943
963
  filterMatched = matches;
944
964
  }
@@ -947,16 +967,23 @@
947
967
  return filterMatched;
948
968
  },
949
969
  processRow: function( c, data, vars ) {
950
- var columnIndex, hasSelect, result, val, filterMatched,
970
+ var hasSelect, result, val, filterMatched,
951
971
  fxn, ffxn, txt,
952
- regex = ts.filter.regex,
972
+ regex = tsf.regex,
953
973
  wo = c.widgetOptions,
954
- showRow = true;
974
+ showRow = true,
975
+
976
+ // if wo.filter_$anyMatch data-column attribute is changed dynamically
977
+ // we don't want to do an "anyMatch" search on one column using data
978
+ // for the entire row - see #998
979
+ columnIndex = wo.filter_$anyMatch && wo.filter_$anyMatch.length ?
980
+ // look for multiple columns '1-3,4-6,8'
981
+ tsf.multipleColumns( c, wo.filter_$anyMatch ) :
982
+ [];
983
+
955
984
  data.$cells = data.$row.children();
956
985
 
957
- if ( data.anyMatchFlag ) {
958
- // look for multiple columns '1-3,4-6,8'
959
- columnIndex = ts.filter.multipleColumns( c, wo.filter_$anyMatch );
986
+ if ( data.anyMatchFlag && columnIndex.length > 1 ) {
960
987
  data.anyMatch = true;
961
988
  data.isMatch = true;
962
989
  data.rowArray = data.$cells.map( function( i ) {
@@ -980,7 +1007,7 @@
980
1007
  data.cache = data.cacheArray.slice( 0, -1 ).join( ' ' );
981
1008
 
982
1009
  vars.excludeMatch = vars.noAnyMatch;
983
- filterMatched = ts.filter.processTypes( c, data, vars );
1010
+ filterMatched = tsf.processTypes( c, data, vars );
984
1011
 
985
1012
  if ( filterMatched !== null ) {
986
1013
  showRow = filterMatched;
@@ -1041,7 +1068,7 @@
1041
1068
 
1042
1069
  val = true;
1043
1070
  if ( wo.filter_defaultFilter && regex.iQuery.test( vars.defaultColFilter[ columnIndex ] ) ) {
1044
- data.filter = ts.filter.defaultFilter( data.filter, vars.defaultColFilter[ columnIndex ] );
1071
+ data.filter = tsf.defaultFilter( data.filter, vars.defaultColFilter[ columnIndex ] );
1045
1072
  // val is used to indicate that a filter select is using a default filter;
1046
1073
  // so we override the exact & partial matches
1047
1074
  val = false;
@@ -1072,13 +1099,13 @@
1072
1099
  if ( filterMatched === null ) {
1073
1100
  // cycle through the different filters
1074
1101
  // filters return a boolean or null if nothing matches
1075
- filterMatched = ts.filter.processTypes( c, data, vars );
1102
+ filterMatched = tsf.processTypes( c, data, vars );
1076
1103
  if ( filterMatched !== null ) {
1077
1104
  result = filterMatched;
1078
1105
  // Look for match, and add child row data for matching
1079
1106
  } else {
1080
1107
  txt = ( data.iExact + data.childRowText )
1081
- .indexOf( ts.filter.parseFilter( c, data.iFilter, columnIndex, data.parsed[ columnIndex ] ) );
1108
+ .indexOf( tsf.parseFilter( c, data.iFilter, columnIndex, data.parsed[ columnIndex ] ) );
1082
1109
  result = ( ( !wo.filter_startsWith && txt >= 0 ) || ( wo.filter_startsWith && txt === 0 ) );
1083
1110
  }
1084
1111
  } else {
@@ -1098,7 +1125,7 @@
1098
1125
  isChild, childRow, lastSearch, showRow, time, val, indx,
1099
1126
  notFiltered, searchFiltered, query, injected, res, id, txt,
1100
1127
  storedFilters = $.extend( [], filters ),
1101
- regex = ts.filter.regex,
1128
+ regex = tsf.regex,
1102
1129
  c = table.config,
1103
1130
  wo = c.widgetOptions,
1104
1131
  // data object passed to filters; anyMatch is a flag for the filters
@@ -1175,7 +1202,7 @@
1175
1202
  data.anyMatchFlag = true;
1176
1203
  data.anyMatchFilter = '' + (
1177
1204
  filters[ c.columns ] ||
1178
- wo.filter_$anyMatch && ts.filter.getLatestSearch( wo.filter_$anyMatch ).val() ||
1205
+ wo.filter_$anyMatch && tsf.getLatestSearch( wo.filter_$anyMatch ).val() ||
1179
1206
  ''
1180
1207
  );
1181
1208
  if ( wo.filter_columnAnyMatch ) {
@@ -1217,10 +1244,10 @@
1217
1244
  // if there is NOT a logical 'or', or range ( 'to' or '-' ) in the string
1218
1245
  !regex.alreadyFiltered.test( val ) &&
1219
1246
  // if we are not doing exact matches, using '|' ( logical or ) or not '!'
1220
- !/[=\"\|!]/.test( val ) &&
1247
+ !regex.exactTest.test( val ) &&
1221
1248
  // don't search only filtered if the value is negative
1222
1249
  // ( '> -10' => '> -100' will ignore hidden rows )
1223
- !( /(>=?\s*-\d)/.test( val ) || /(<=?\s*\d)/.test( val ) ) &&
1250
+ !( regex.isNeg1.test( val ) || regex.isNeg2.test( val ) ) &&
1224
1251
  // if filtering using a select without a 'filter-match' class ( exact match ) - fixes #593
1225
1252
  !( val !== '' && c.$filters && c.$filters.eq( indx ).find( 'select' ).length &&
1226
1253
  !c.$headerIndexed[indx].hasClass( 'filter-match' ) );
@@ -1239,7 +1266,7 @@
1239
1266
  data.anyMatchFilter = ts.replaceAccents( data.anyMatchFilter );
1240
1267
  }
1241
1268
  if ( wo.filter_defaultFilter && regex.iQuery.test( vars.defaultAnyFilter ) ) {
1242
- data.anyMatchFilter = ts.filter.defaultFilter( data.anyMatchFilter, vars.defaultAnyFilter );
1269
+ data.anyMatchFilter = tsf.defaultFilter( data.anyMatchFilter, vars.defaultAnyFilter );
1243
1270
  // clear search filtered flag because default filters are not saved to the last search
1244
1271
  searchFiltered = false;
1245
1272
  }
@@ -1282,7 +1309,7 @@
1282
1309
  '';
1283
1310
  }
1284
1311
 
1285
- showRow = ts.filter.processRow( c, data, vars );
1312
+ showRow = tsf.processRow( c, data, vars );
1286
1313
  childRow = rowData.$row.filter( ':gt( 0 )' );
1287
1314
 
1288
1315
  if ( wo.filter_childRows && childRow.length ) {
@@ -1293,7 +1320,7 @@
1293
1320
  data.cacheArray = rowData.child[ indx ];
1294
1321
  data.rawArray = data.cacheArray;
1295
1322
  // use OR comparison on child rows
1296
- showRow = showRow || ts.filter.processRow( c, data, vars );
1323
+ showRow = showRow || tsf.processRow( c, data, vars );
1297
1324
  }
1298
1325
  }
1299
1326
  childRow.toggleClass( wo.filter_filteredRow, !showRow );
@@ -1355,7 +1382,7 @@
1355
1382
  }
1356
1383
  if ( arry === false ) {
1357
1384
  // fall back to original method
1358
- arry = ts.filter.getOptions( table, column, onlyAvail );
1385
+ arry = tsf.getOptions( table, column, onlyAvail );
1359
1386
  }
1360
1387
 
1361
1388
  // get unique elements and sort the list
@@ -1467,13 +1494,13 @@
1467
1494
  // nothing included in arry ( external source ), so get the options from
1468
1495
  // filter_selectSource or column data
1469
1496
  if ( typeof arry === 'undefined' || arry === '' ) {
1470
- arry = ts.filter.getOptionSource( table, column, onlyAvail );
1497
+ arry = tsf.getOptionSource( table, column, onlyAvail );
1471
1498
  }
1472
1499
 
1473
1500
  if ( $.isArray( arry ) ) {
1474
1501
  // build option list
1475
1502
  for ( indx = 0; indx < arry.length; indx++ ) {
1476
- txt = arry[indx] = ( '' + arry[indx] ).replace( /\"/g, '&quot;' );
1503
+ txt = arry[indx] = ( '' + arry[indx] ).replace( tsf.regex.quote, '&quot;' );
1477
1504
  val = txt;
1478
1505
  // allow including a symbol in the selectSource array
1479
1506
  // 'a-z|A through Z' so that 'a-z' becomes the option value
@@ -1529,7 +1556,7 @@
1529
1556
  // look for the filter-select class; build/update it if found
1530
1557
  if ( ( $header.hasClass( 'filter-select' ) ||
1531
1558
  ts.getColumnData( table, wo.filter_functions, columnIndex ) === true ) && noSelect ) {
1532
- ts.filter.buildSelect( table, columnIndex, '', updating, $header.hasClass( wo.filter_onlyAvail ) );
1559
+ tsf.buildSelect( table, columnIndex, '', updating, $header.hasClass( wo.filter_onlyAvail ) );
1533
1560
  }
1534
1561
  }
1535
1562
  }
@@ -1565,7 +1592,7 @@
1565
1592
  $column = $filters.filter( cols );
1566
1593
  if ( $column.length ) {
1567
1594
  // move the latest search to the first slot in the array
1568
- $column = ts.filter.getLatestSearch( $column );
1595
+ $column = tsf.getLatestSearch( $column );
1569
1596
  if ( $.isArray( setFilters ) ) {
1570
1597
  // skip first ( latest input ) to maintain cursor position while typing
1571
1598
  if ( skipFirst && $column.length > 1 ) {
@@ -1615,7 +1642,7 @@
1615
1642
  // ensure new set filters are applied, even if the search is the same
1616
1643
  c.lastCombinedFilter = null;
1617
1644
  c.lastSearch = [];
1618
- ts.filter.searching( c.table, filter, skipFirst );
1645
+ tsf.searching( c.table, filter, skipFirst );
1619
1646
  c.$table.trigger( 'filterFomatterUpdate' );
1620
1647
  }
1621
1648
  return !!valid;