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.
@@ -1,10 +1,10 @@
1
- /*! Widget: filter - updated 11/4/2015 (v2.24.3) *//*
1
+ /*! Widget: filter - updated 11/10/2015 (v2.24.4) *//*
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 tsf,
7
+ var tsf, tsfRegex,
8
8
  ts = $.tablesorter || {},
9
9
  tscss = ts.css;
10
10
 
@@ -110,20 +110,21 @@
110
110
  // data.parsed = array ( by column ) of boolean values ( from filter_useParsedData or 'filter-parsed' class )
111
111
  types: {
112
112
  or : function( c, data, vars ) {
113
- if ( tsf.regex.orTest.test( data.iFilter ) || tsf.regex.orSplit.test( data.filter ) ) {
113
+ // look for "|", but not if it is inside of a regular expression
114
+ if ( ( tsfRegex.orTest.test( data.iFilter ) || tsfRegex.orSplit.test( data.filter ) ) &&
115
+ // this test for regex has potential to slow down the overall search
116
+ !tsfRegex.regex.test( data.filter ) ) {
114
117
  var indx, filterMatched, query, regex,
115
118
  // duplicate data but split filter
116
119
  data2 = $.extend( {}, data ),
117
- index = data.index,
118
- parsed = data.parsed[ index ],
119
- filter = data.filter.split( tsf.regex.orSplit ),
120
- iFilter = data.iFilter.split( tsf.regex.orSplit ),
120
+ filter = data.filter.split( tsfRegex.orSplit ),
121
+ iFilter = data.iFilter.split( tsfRegex.orSplit ),
121
122
  len = filter.length;
122
123
  for ( indx = 0; indx < len; indx++ ) {
123
124
  data2.nestedFilters = true;
124
- data2.filter = '' + ( tsf.parseFilter( c, filter[ indx ], index, parsed ) || '' );
125
- data2.iFilter = '' + ( tsf.parseFilter( c, iFilter[ indx ], index, parsed ) || '' );
126
- query = '(' + ( tsf.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')';
125
+ data2.filter = '' + ( tsf.parseFilter( c, filter[ indx ], data ) || '' );
126
+ data2.iFilter = '' + ( tsf.parseFilter( c, iFilter[ indx ], data ) || '' );
127
+ query = '(' + ( tsf.parseFilter( c, data2.filter, data ) || '' ) + ')';
127
128
  try {
128
129
  // use try/catch, because query may not be a valid regex if "|" is contained within a partial regex search,
129
130
  // e.g "/(Alex|Aar" -> Uncaught SyntaxError: Invalid regular expression: /(/(Alex)/: Unterminated group
@@ -145,22 +146,20 @@
145
146
  },
146
147
  // Look for an AND or && operator ( logical and )
147
148
  and : function( c, data, vars ) {
148
- if ( tsf.regex.andTest.test( data.filter ) ) {
149
+ if ( tsfRegex.andTest.test( data.filter ) ) {
149
150
  var indx, filterMatched, result, query, regex,
150
151
  // duplicate data but split filter
151
152
  data2 = $.extend( {}, data ),
152
- index = data.index,
153
- parsed = data.parsed[ index ],
154
- filter = data.filter.split( tsf.regex.andSplit ),
155
- iFilter = data.iFilter.split( tsf.regex.andSplit ),
153
+ filter = data.filter.split( tsfRegex.andSplit ),
154
+ iFilter = data.iFilter.split( tsfRegex.andSplit ),
156
155
  len = filter.length;
157
156
  for ( indx = 0; indx < len; indx++ ) {
158
157
  data2.nestedFilters = true;
159
- data2.filter = '' + ( tsf.parseFilter( c, filter[ indx ], index, parsed ) || '' );
160
- data2.iFilter = '' + ( tsf.parseFilter( c, iFilter[ indx ], index, parsed ) || '' );
161
- query = ( '(' + ( tsf.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')' )
158
+ data2.filter = '' + ( tsf.parseFilter( c, filter[ indx ], data ) || '' );
159
+ data2.iFilter = '' + ( tsf.parseFilter( c, iFilter[ indx ], data ) || '' );
160
+ query = ( '(' + ( tsf.parseFilter( c, data2.filter, data ) || '' ) + ')' )
162
161
  // replace wild cards since /(a*)/i will match anything
163
- .replace( tsf.regex.wild01, '\\S{1}' ).replace( tsf.regex.wild0More, '\\S*' );
162
+ .replace( tsfRegex.wild01, '\\S{1}' ).replace( tsfRegex.wild0More, '\\S*' );
164
163
  try {
165
164
  // use try/catch just in case RegExp is invalid
166
165
  regex = new RegExp( data.isMatch ? query : '^' + query + '$', c.widgetOptions.filter_ignoreCase ? 'i' : '' );
@@ -182,10 +181,10 @@
182
181
  },
183
182
  // Look for regex
184
183
  regex: function( c, data ) {
185
- if ( tsf.regex.regex.test( data.filter ) ) {
184
+ if ( tsfRegex.regex.test( data.filter ) ) {
186
185
  var matches,
187
186
  // cache regex per column for optimal speed
188
- regex = data.filter_regexCache[ data.index ] || tsf.regex.regex.exec( data.filter ),
187
+ regex = data.filter_regexCache[ data.index ] || tsfRegex.regex.exec( data.filter ),
189
188
  isRegex = regex instanceof RegExp;
190
189
  try {
191
190
  if ( !isRegex ) {
@@ -204,18 +203,17 @@
204
203
  // Look for operators >, >=, < or <=
205
204
  operators: function( c, data ) {
206
205
  // ignore empty strings... because '' < 10 is true
207
- if ( tsf.regex.operTest.test( data.iFilter ) && data.iExact !== '' ) {
206
+ if ( tsfRegex.operTest.test( data.iFilter ) && data.iExact !== '' ) {
208
207
  var cachedValue, result, txt,
209
208
  table = c.table,
210
- index = data.index,
211
- parsed = data.parsed[index],
212
- query = ts.formatFloat( data.iFilter.replace( tsf.regex.operators, '' ), table ),
213
- parser = c.parsers[index],
209
+ parsed = data.parsed[ data.index ],
210
+ query = ts.formatFloat( data.iFilter.replace( tsfRegex.operators, '' ), table ),
211
+ parser = c.parsers[ data.index ],
214
212
  savedSearch = query;
215
213
  // parse filter value in case we're comparing numbers ( dates )
216
214
  if ( parsed || parser.type === 'numeric' ) {
217
- txt = $.trim( '' + data.iFilter.replace( tsf.regex.operators, '' ) );
218
- result = tsf.parseFilter( c, txt, index, true );
215
+ txt = $.trim( '' + data.iFilter.replace( tsfRegex.operators, '' ) );
216
+ result = tsf.parseFilter( c, txt, data, true );
219
217
  query = ( typeof result === 'number' && result !== '' && !isNaN( result ) ) ? result : query;
220
218
  }
221
219
  // iExact may be numeric - see issue #149;
@@ -227,10 +225,10 @@
227
225
  txt = isNaN( data.iExact ) ? data.iExact.replace( ts.regex.nondigit, '' ) : data.iExact;
228
226
  cachedValue = ts.formatFloat( txt, table );
229
227
  }
230
- if ( tsf.regex.gtTest.test( data.iFilter ) ) {
231
- result = tsf.regex.gteTest.test( data.iFilter ) ? cachedValue >= query : cachedValue > query;
232
- } else if ( tsf.regex.ltTest.test( data.iFilter ) ) {
233
- result = tsf.regex.lteTest.test( data.iFilter ) ? cachedValue <= query : cachedValue < query;
228
+ if ( tsfRegex.gtTest.test( data.iFilter ) ) {
229
+ result = tsfRegex.gteTest.test( data.iFilter ) ? cachedValue >= query : cachedValue > query;
230
+ } else if ( tsfRegex.ltTest.test( data.iFilter ) ) {
231
+ result = tsfRegex.lteTest.test( data.iFilter ) ? cachedValue <= query : cachedValue < query;
234
232
  }
235
233
  // keep showing all rows if nothing follows the operator
236
234
  if ( !result && savedSearch === '' ) {
@@ -242,13 +240,13 @@
242
240
  },
243
241
  // Look for a not match
244
242
  notMatch: function( c, data ) {
245
- if ( tsf.regex.notTest.test( data.iFilter ) ) {
243
+ if ( tsfRegex.notTest.test( data.iFilter ) ) {
246
244
  var indx,
247
245
  txt = data.iFilter.replace( '!', '' ),
248
- filter = tsf.parseFilter( c, txt, data.index, data.parsed[data.index] ) || '';
249
- if ( tsf.regex.exact.test( filter ) ) {
246
+ filter = tsf.parseFilter( c, txt, data ) || '';
247
+ if ( tsfRegex.exact.test( filter ) ) {
250
248
  // look for exact not matches - see #628
251
- filter = filter.replace( tsf.regex.exact, '' );
249
+ filter = filter.replace( tsfRegex.exact, '' );
252
250
  return filter === '' ? true : $.trim( filter ) !== data.iExact;
253
251
  } else {
254
252
  indx = data.iExact.search( $.trim( filter ) );
@@ -260,29 +258,29 @@
260
258
  // Look for quotes or equals to get an exact match; ignore type since iExact could be numeric
261
259
  exact: function( c, data ) {
262
260
  /*jshint eqeqeq:false */
263
- if ( tsf.regex.exact.test( data.iFilter ) ) {
264
- var txt = data.iFilter.replace( tsf.regex.exact, '' ),
265
- filter = tsf.parseFilter( c, txt, data.index, data.parsed[data.index] ) || '';
261
+ if ( tsfRegex.exact.test( data.iFilter ) ) {
262
+ var txt = data.iFilter.replace( tsfRegex.exact, '' ),
263
+ filter = tsf.parseFilter( c, txt, data ) || '';
266
264
  return data.anyMatch ? $.inArray( filter, data.rowArray ) >= 0 : filter == data.iExact;
267
265
  }
268
266
  return null;
269
267
  },
270
268
  // Look for a range ( using ' to ' or ' - ' ) - see issue #166; thanks matzhu!
271
269
  range : function( c, data ) {
272
- if ( tsf.regex.toTest.test( data.iFilter ) ) {
270
+ if ( tsfRegex.toTest.test( data.iFilter ) ) {
273
271
  var result, tmp, range1, range2,
274
272
  table = c.table,
275
273
  index = data.index,
276
274
  parsed = data.parsed[index],
277
275
  // make sure the dash is for a range and not indicating a negative number
278
- query = data.iFilter.split( tsf.regex.toSplit );
276
+ query = data.iFilter.split( tsfRegex.toSplit );
279
277
 
280
278
  tmp = query[0].replace( ts.regex.nondigit, '' ) || '';
281
- range1 = ts.formatFloat( tsf.parseFilter( c, tmp, index, parsed ), table );
279
+ range1 = ts.formatFloat( tsf.parseFilter( c, tmp, data ), table );
282
280
  tmp = query[1].replace( ts.regex.nondigit, '' ) || '';
283
- range2 = ts.formatFloat( tsf.parseFilter( c, tmp, index, parsed ), table );
281
+ range2 = ts.formatFloat( tsf.parseFilter( c, tmp, data ), table );
284
282
  // parse filter value in case we're comparing numbers ( dates )
285
- if ( parsed || c.parsers[index].type === 'numeric' ) {
283
+ if ( parsed || c.parsers[ index ].type === 'numeric' ) {
286
284
  result = c.parsers[ index ].format( '' + query[0], table, c.$headers.eq( index ), index );
287
285
  range1 = ( result !== '' && !isNaN( result ) ) ? result : range1;
288
286
  result = c.parsers[ index ].format( '' + query[1], table, c.$headers.eq( index ), index );
@@ -303,18 +301,16 @@
303
301
  },
304
302
  // Look for wild card: ? = single, * = multiple, or | = logical OR
305
303
  wild : function( c, data ) {
306
- if ( tsf.regex.wildOrTest.test( data.iFilter ) ) {
307
- var index = data.index,
308
- parsed = data.parsed[ index ],
309
- query = '' + ( tsf.parseFilter( c, data.iFilter, index, parsed ) || '' );
304
+ if ( tsfRegex.wildOrTest.test( data.iFilter ) ) {
305
+ var query = '' + ( tsf.parseFilter( c, data.iFilter, data ) || '' );
310
306
  // look for an exact match with the 'or' unless the 'filter-match' class is found
311
- if ( !tsf.regex.wildTest.test( query ) && data.nestedFilters ) {
307
+ if ( !tsfRegex.wildTest.test( query ) && data.nestedFilters ) {
312
308
  query = data.isMatch ? query : '^(' + query + ')$';
313
309
  }
314
310
  // parsing the filter may not work properly when using wildcards =/
315
311
  try {
316
312
  return new RegExp(
317
- query.replace( tsf.regex.wild01, '\\S{1}' ).replace( tsf.regex.wild0More, '\\S*' ),
313
+ query.replace( tsfRegex.wild01, '\\S{1}' ).replace( tsfRegex.wild0More, '\\S*' ),
318
314
  c.widgetOptions.filter_ignoreCase ? 'i' : ''
319
315
  )
320
316
  .test( data.exact );
@@ -326,21 +322,18 @@
326
322
  },
327
323
  // fuzzy text search; modified from https://github.com/mattyork/fuzzy ( MIT license )
328
324
  fuzzy: function( c, data ) {
329
- if ( tsf.regex.fuzzyTest.test( data.iFilter ) ) {
325
+ if ( tsfRegex.fuzzyTest.test( data.iFilter ) ) {
330
326
  var indx,
331
327
  patternIndx = 0,
332
328
  len = data.iExact.length,
333
329
  txt = data.iFilter.slice( 1 ),
334
- pattern = tsf.parseFilter( c, txt, data.index, data.parsed[data.index] ) || '';
330
+ pattern = tsf.parseFilter( c, txt, data ) || '';
335
331
  for ( indx = 0; indx < len; indx++ ) {
336
332
  if ( data.iExact[ indx ] === pattern[ patternIndx ] ) {
337
333
  patternIndx += 1;
338
334
  }
339
335
  }
340
- if ( patternIndx === pattern.length ) {
341
- return true;
342
- }
343
- return false;
336
+ return patternIndx === pattern.length;
344
337
  }
345
338
  return null;
346
339
  }
@@ -353,8 +346,7 @@
353
346
  and : 'and'
354
347
  }, ts.language );
355
348
 
356
- var options, string, txt, $header, column, filters, val, fxn, noSelect,
357
- regex = tsf.regex;
349
+ var options, string, txt, $header, column, filters, val, fxn, noSelect;
358
350
  c.$table.addClass( 'hasFilters' );
359
351
 
360
352
  // define timers so using clearTimeout won't cause an undefined error
@@ -365,8 +357,8 @@
365
357
  wo.filter_anyColumnSelector = '[data-column="all"],[data-column="any"]';
366
358
  wo.filter_multipleColumnSelector = '[data-column*="-"],[data-column*=","]';
367
359
 
368
- val = '\\{' + tsf.regex.query + '\\}';
369
- $.extend( regex, {
360
+ val = '\\{' + tsfRegex.query + '\\}';
361
+ $.extend( tsfRegex, {
370
362
  child : new RegExp( c.cssChildRow ),
371
363
  filtered : new RegExp( wo.filter_filteredRow ),
372
364
  alreadyFiltered : new RegExp( '(\\s+(' + ts.language.or + '|-|' + ts.language.to + ')\\s+)', 'i' ),
@@ -644,8 +636,10 @@
644
636
  c.$table.data( 'lastSearch', filters );
645
637
  return filters;
646
638
  },
647
- parseFilter: function( c, filter, column, parsed ) {
648
- return parsed ? c.parsers[column].format( filter, c.table, [], column ) : filter;
639
+ parseFilter: function( c, filter, data, parsed ) {
640
+ return parsed || data.parsed[ data.index ] ?
641
+ c.parsers[ data.index ].format( filter, c.table, [], data.index ) :
642
+ filter;
649
643
  },
650
644
  buildRow: function( table, c, wo ) {
651
645
  var $filter, col, column, $header, makeSelect, disabled, name, ffxn, tmp,
@@ -838,8 +832,12 @@
838
832
  c.lastCombinedFilter = null;
839
833
  c.lastSearch = [];
840
834
  }
841
- // convert filters to strings (maybe not the best method)- see #1070
842
- filters = filters.join( '\u0000' ).split( '\u0000' );
835
+ // convert filters to strings - see #1070
836
+ filters = Array.prototype.map ?
837
+ filters.map( String ) :
838
+ // for IE8 & older browsers - maybe not the best method
839
+ filters.join( '\u0000' ).split( '\u0000' );
840
+
843
841
  if ( wo.filter_initialized ) {
844
842
  c.$table.trigger( 'filterStart', [ filters ] );
845
843
  }
@@ -893,8 +891,8 @@
893
891
  },
894
892
  defaultFilter: function( filter, mask ) {
895
893
  if ( filter === '' ) { return filter; }
896
- var regex = tsf.regex.iQuery,
897
- maskLen = mask.match( tsf.regex.igQuery ).length,
894
+ var regex = tsfRegex.iQuery,
895
+ maskLen = mask.match( tsfRegex.igQuery ).length,
898
896
  query = maskLen > 1 ? $.trim( filter ).split( /\s/ ) : [ $.trim( filter ) ],
899
897
  len = query.length - 1,
900
898
  indx = 0,
@@ -991,9 +989,8 @@
991
989
  return filterMatched;
992
990
  },
993
991
  processRow: function( c, data, vars ) {
994
- var hasSelect, result, val, filterMatched,
992
+ var result, filterMatched,
995
993
  fxn, ffxn, txt,
996
- regex = tsf.regex,
997
994
  wo = c.widgetOptions,
998
995
  showRow = true,
999
996
 
@@ -1072,7 +1069,7 @@
1072
1069
  result = data.rawArray[ columnIndex ] || '';
1073
1070
  data.exact = c.sortLocaleCompare ? ts.replaceAccents( result ) : result; // issue #405
1074
1071
  }
1075
- data.iExact = !regex.type.test( typeof data.exact ) && wo.filter_ignoreCase ?
1072
+ data.iExact = !tsfRegex.type.test( typeof data.exact ) && wo.filter_ignoreCase ?
1076
1073
  data.exact.toLowerCase() : data.exact;
1077
1074
 
1078
1075
  data.isMatch = c.$headerIndexed[ data.index ].hasClass( 'filter-match' );
@@ -1090,21 +1087,13 @@
1090
1087
  data.filter = ts.replaceAccents( data.filter );
1091
1088
  }
1092
1089
 
1093
- val = true;
1094
- if ( wo.filter_defaultFilter && regex.iQuery.test( vars.defaultColFilter[ columnIndex ] ) ) {
1095
- data.filter = tsf.defaultFilter( data.filter, vars.defaultColFilter[ columnIndex ] );
1096
- // val is used to indicate that a filter select is using a default filter;
1097
- // so we override the exact & partial matches
1098
- val = false;
1099
- }
1100
1090
  // data.iFilter = case insensitive ( if wo.filter_ignoreCase is true ),
1101
1091
  // data.filter = case sensitive
1102
1092
  data.iFilter = wo.filter_ignoreCase ? ( data.filter || '' ).toLowerCase() : data.filter;
1103
1093
  fxn = vars.functions[ columnIndex ];
1104
- hasSelect = c.$headerIndexed[ columnIndex ].hasClass( 'filter-select' );
1105
1094
  filterMatched = null;
1106
- if ( fxn || ( hasSelect && val ) ) {
1107
- if ( fxn === true || hasSelect ) {
1095
+ if ( fxn ) {
1096
+ if ( fxn === true ) {
1108
1097
  // default selector uses exact match unless 'filter-match' class is found
1109
1098
  filterMatched = data.isMatch ?
1110
1099
  // data.iExact may be a number
@@ -1130,7 +1119,7 @@
1130
1119
  // Look for match, and add child row data for matching
1131
1120
  } else {
1132
1121
  txt = ( data.iExact + data.childRowText )
1133
- .indexOf( tsf.parseFilter( c, data.iFilter, columnIndex, data.parsed[ columnIndex ] ) );
1122
+ .indexOf( tsf.parseFilter( c, data.iFilter, data ) );
1134
1123
  result = ( ( !wo.filter_startsWith && txt >= 0 ) || ( wo.filter_startsWith && txt === 0 ) );
1135
1124
  }
1136
1125
  } else {
@@ -1150,7 +1139,6 @@
1150
1139
  isChild, childRow, lastSearch, showRow, showParent, time, val, indx,
1151
1140
  notFiltered, searchFiltered, query, injected, res, id, txt,
1152
1141
  storedFilters = $.extend( [], filters ),
1153
- regex = tsf.regex,
1154
1142
  c = table.config,
1155
1143
  wo = c.widgetOptions,
1156
1144
  // data object passed to filters; anyMatch is a flag for the filters
@@ -1232,7 +1220,7 @@
1232
1220
  );
1233
1221
  if ( wo.filter_columnAnyMatch ) {
1234
1222
  // specific columns search
1235
- query = data.anyMatchFilter.split( regex.andSplit );
1223
+ query = data.anyMatchFilter.split( tsfRegex.andSplit );
1236
1224
  injected = false;
1237
1225
  for ( indx = 0; indx < query.length; indx++ ) {
1238
1226
  res = query[ indx ].split( ':' );
@@ -1267,12 +1255,12 @@
1267
1255
  // there are no changes from beginning of filter
1268
1256
  val.indexOf( lastSearch[indx] || '' ) === 0 &&
1269
1257
  // if there is NOT a logical 'or', or range ( 'to' or '-' ) in the string
1270
- !regex.alreadyFiltered.test( val ) &&
1258
+ !tsfRegex.alreadyFiltered.test( val ) &&
1271
1259
  // if we are not doing exact matches, using '|' ( logical or ) or not '!'
1272
- !regex.exactTest.test( val ) &&
1260
+ !tsfRegex.exactTest.test( val ) &&
1273
1261
  // don't search only filtered if the value is negative
1274
1262
  // ( '> -10' => '> -100' will ignore hidden rows )
1275
- !( regex.isNeg1.test( val ) || regex.isNeg2.test( val ) ) &&
1263
+ !( tsfRegex.isNeg1.test( val ) || tsfRegex.isNeg2.test( val ) ) &&
1276
1264
  // if filtering using a select without a 'filter-match' class ( exact match ) - fixes #593
1277
1265
  !( val !== '' && c.$filters && c.$filters.filter( '[data-column="' + indx + '"]' ).find( 'select' ).length &&
1278
1266
  !c.$headerIndexed[indx].hasClass( 'filter-match' ) );
@@ -1290,7 +1278,7 @@
1290
1278
  // replace accents
1291
1279
  data.anyMatchFilter = ts.replaceAccents( data.anyMatchFilter );
1292
1280
  }
1293
- if ( wo.filter_defaultFilter && regex.iQuery.test( vars.defaultAnyFilter ) ) {
1281
+ if ( wo.filter_defaultFilter && tsfRegex.iQuery.test( vars.defaultAnyFilter ) ) {
1294
1282
  data.anyMatchFilter = tsf.defaultFilter( data.anyMatchFilter, vars.defaultAnyFilter );
1295
1283
  // clear search filtered flag because default filters are not saved to the last search
1296
1284
  searchFiltered = false;
@@ -1307,9 +1295,9 @@
1307
1295
 
1308
1296
  txt = $rows[ rowIndex ].className;
1309
1297
  // the first row can never be a child row
1310
- isChild = rowIndex && regex.child.test( txt );
1298
+ isChild = rowIndex && tsfRegex.child.test( txt );
1311
1299
  // skip child rows & already filtered rows
1312
- if ( isChild || ( searchFiltered && regex.filtered.test( txt ) ) ) {
1300
+ if ( isChild || ( searchFiltered && tsfRegex.filtered.test( txt ) ) ) {
1313
1301
  continue;
1314
1302
  }
1315
1303
 
@@ -1419,7 +1407,6 @@
1419
1407
  // custom select source function for a SPECIFIC COLUMN
1420
1408
  arry = fxn( table, column, onlyAvail );
1421
1409
  }
1422
-
1423
1410
  if ( arry === false ) {
1424
1411
  // fall back to original method
1425
1412
  arry = tsf.getOptions( table, column, onlyAvail );
@@ -1433,18 +1420,19 @@
1433
1420
  return false;
1434
1421
  }
1435
1422
  table = $( table )[0];
1436
- var cts, txt, indx, len,
1423
+ var cts, txt, indx, len, parsedTxt, str,
1437
1424
  c = table.config,
1438
1425
  validColumn = typeof column !== 'undefined' && column !== null && column >= 0 && column < c.columns,
1439
1426
  parsed = [];
1440
-
1441
1427
  // get unique elements and sort the list
1442
1428
  // if $.tablesorter.sortText exists ( not in the original tablesorter ),
1443
1429
  // then natural sort the list otherwise use a basic sort
1444
1430
  arry = $.grep( arry, function( value, indx ) {
1431
+ if ( value.text ) {
1432
+ return true;
1433
+ }
1445
1434
  return $.inArray( value, arry ) === indx;
1446
1435
  });
1447
-
1448
1436
  if ( validColumn && c.$headerIndexed[ column ].hasClass( 'filter-select-nosort' ) ) {
1449
1437
  // unsorted select options
1450
1438
  return arry;
@@ -1453,22 +1441,30 @@
1453
1441
  // parse select option values
1454
1442
  for ( indx = 0; indx < len; indx++ ) {
1455
1443
  txt = arry[ indx ];
1444
+ // check for object
1445
+ str = txt.text ? txt.text : txt;
1446
+ // sortNatural breaks if you don't pass it strings
1447
+ parsedTxt = ( validColumn && c.parsers && c.parsers.length &&
1448
+ c.parsers[ column ].format( str, table, [], column ) || str ).toString();
1449
+ parsedTxt = c.widgetOptions.filter_ignoreCase ? parsedTxt.toLowerCase() : parsedTxt;
1456
1450
  // parse array data using set column parser; this DOES NOT pass the original
1457
1451
  // table cell to the parser format function
1458
- parsed.push({
1459
- t : txt,
1460
- // check parser length - fixes #934
1461
- p : validColumn && c.parsers && c.parsers.length &&
1462
- c.parsers[ column ].format( txt, table, [], column ) || txt
1463
- });
1452
+ if ( txt.text ) {
1453
+ txt.parsed = parsedTxt;
1454
+ parsed.push( txt );
1455
+ } else {
1456
+ parsed.push({
1457
+ text : txt,
1458
+ // check parser length - fixes #934
1459
+ parsed : parsedTxt
1460
+ });
1461
+ }
1464
1462
  }
1465
-
1466
1463
  // sort parsed select options
1467
1464
  cts = c.textSorter || '';
1468
1465
  parsed.sort( function( a, b ) {
1469
- // sortNatural breaks if you don't pass it strings
1470
- var x = a.p.toString(),
1471
- y = b.p.toString();
1466
+ var x = a.parsed,
1467
+ y = b.parsed;
1472
1468
  if ( validColumn && typeof cts === 'function' ) {
1473
1469
  // custom OVERALL text sorter
1474
1470
  return cts( x, y, true, column, table );
@@ -1486,7 +1482,7 @@
1486
1482
  arry = [];
1487
1483
  len = parsed.length;
1488
1484
  for ( indx = 0; indx < len; indx++ ) {
1489
- arry.push( parsed[indx].t );
1485
+ arry.push( parsed[indx] );
1490
1486
  }
1491
1487
  return arry;
1492
1488
  }
@@ -1546,7 +1542,7 @@
1546
1542
  return;
1547
1543
  }
1548
1544
 
1549
- var indx, val, txt, t, $filters, $filter,
1545
+ var indx, val, txt, t, $filters, $filter, option,
1550
1546
  c = table.config,
1551
1547
  wo = c.widgetOptions,
1552
1548
  node = c.$headerIndexed[ column ],
@@ -1571,23 +1567,45 @@
1571
1567
  if ( $.isArray( arry ) ) {
1572
1568
  // build option list
1573
1569
  for ( indx = 0; indx < arry.length; indx++ ) {
1574
- txt = arry[indx] = ( '' + arry[indx] ).replace( tsf.regex.quote, '&quot;' );
1575
- val = txt;
1576
- // allow including a symbol in the selectSource array
1577
- // 'a-z|A through Z' so that 'a-z' becomes the option value
1578
- // and 'A through Z' becomes the option text
1579
- if ( txt.indexOf( wo.filter_selectSourceSeparator ) >= 0 ) {
1580
- t = txt.split( wo.filter_selectSourceSeparator );
1581
- val = t[0];
1582
- txt = t[1];
1570
+ option = arry[ indx ];
1571
+ if ( option.text ) {
1572
+ // OBJECT!! add data-function-name in case the value is set in filter_functions
1573
+ option['data-function-name'] = typeof option.value === 'undefined' ? option.text : option.value;
1574
+
1575
+ // support jQuery < v1.8, otherwise the below code could be shortened to
1576
+ // options += $( '<option>', option )[ 0 ].outerHTML;
1577
+ options += '<option';
1578
+ for ( val in option ) {
1579
+ if ( option.hasOwnProperty( val ) && val !== 'text' ) {
1580
+ options += ' ' + val + '="' + option[ val ] + '"';
1581
+ }
1582
+ }
1583
+ if ( !option.value ) {
1584
+ options += ' value="' + option.text + '"';
1585
+ }
1586
+ options += '>' + option.text + '</option>';
1587
+ // above code is needed in jQuery < v1.8
1588
+
1589
+ // make sure we don't turn an object into a string (objects without a "text" property)
1590
+ } else if ( '' + option !== '[object Object]' ) {
1591
+ txt = option = ( '' + option ).replace( tsfRegex.quote, '&quot;' );
1592
+ val = txt;
1593
+ // allow including a symbol in the selectSource array
1594
+ // 'a-z|A through Z' so that 'a-z' becomes the option value
1595
+ // and 'A through Z' becomes the option text
1596
+ if ( txt.indexOf( wo.filter_selectSourceSeparator ) >= 0 ) {
1597
+ t = txt.split( wo.filter_selectSourceSeparator );
1598
+ val = t[0];
1599
+ txt = t[1];
1600
+ }
1601
+ // replace quotes - fixes #242 & ignore empty strings
1602
+ // see http://stackoverflow.com/q/14990971/145346
1603
+ options += option !== '' ?
1604
+ '<option ' +
1605
+ ( val === txt ? '' : 'data-function-name="' + option + '" ' ) +
1606
+ 'value="' + val + '">' + txt +
1607
+ '</option>' : '';
1583
1608
  }
1584
- // replace quotes - fixes #242 & ignore empty strings
1585
- // see http://stackoverflow.com/q/14990971/145346
1586
- options += arry[indx] !== '' ?
1587
- '<option ' +
1588
- ( val === txt ? '' : 'data-function-name="' + arry[indx] + '" ' ) +
1589
- 'value="' + val + '">' + txt +
1590
- '</option>' : '';
1591
1609
  }
1592
1610
  // clear arry so it doesn't get appended twice
1593
1611
  arry = [];
@@ -1633,6 +1651,9 @@
1633
1651
  }
1634
1652
  };
1635
1653
 
1654
+ // filter regex variable
1655
+ tsfRegex = tsf.regex;
1656
+
1636
1657
  ts.getFilters = function( table, getRaw, setFilters, skipFirst ) {
1637
1658
  var i, $filters, $column, cols,
1638
1659
  filters = false,
@@ -1,4 +1,4 @@
1
- /*! Widget: grouping - updated 11/2/2015 (v2.24.1) *//*
1
+ /*! Widget: grouping - updated 11/10/2015 (v2.24.4) *//*
2
2
  * Requires tablesorter v2.8+ and jQuery 1.7+
3
3
  * by Rob Garrison
4
4
  */
@@ -223,7 +223,7 @@
223
223
  if ( data.group !== data.currentGroup ) {
224
224
  data.group = data.currentGroup;
225
225
  if ( $.isFunction( wo.group_formatter ) ) {
226
- data.currentGroup = wo.group_formatter( ( data.group || '' ).toString(), data.column, c.table, c, wo ) || data.group;
226
+ data.currentGroup = wo.group_formatter( ( data.group || '' ).toString(), data.column, c.table, c, wo, data ) || data.group;
227
227
  }
228
228
  data.$row.before( tsg.groupHeaderHTML( c, wo, data ) );
229
229
  if ( wo.group_saveGroups && !data.savedGroup && wo.group_collapsed && wo.group_collapsible ) {
@@ -1,4 +1,4 @@
1
- /*! Widget: headerTitles - updated 10/31/2015 (v2.24.0) *//*
1
+ /*! Widget: headerTitles - updated 11/10/2015 (v2.24.4) *//*
2
2
  * Requires tablesorter v2.8+ and jQuery 1.7+
3
3
  * by Rob Garrison
4
4
  */
@@ -59,7 +59,7 @@
59
59
  sortDirection = $this.hasClass(ts.css.sortAsc) ? 0 : $this.hasClass(ts.css.sortDesc) ? 1 : 2,
60
60
  sortNext = c.sortVars[ col ].order[ ( c.sortVars[ col ].count + 1 ) % ( c.sortReset ? 3 : 2 ) ];
61
61
  if (wo.headerTitle_useAria) {
62
- txt = $this.hasClass('sorter-false') ? wo.headerTitle_output_nosort : $this.attr('aria-label') || '';
62
+ txt = $this.attr('aria-label') || wo.headerTitle_output_nosort || '';
63
63
  } else {
64
64
  txt = (wo.headerTitle_prefix || '') + // now deprecated
65
65
  ($this.hasClass('sorter-false') ? wo.headerTitle_output_nosort :
@@ -1,4 +1,4 @@
1
- /*! Widget: math - updated 10/31/2015 (v2.24.0) *//*
1
+ /*! Widget: math - updated 11/10/2015 (v2.24.4) *//*
2
2
  * Requires tablesorter v2.16+ and jQuery 1.7+
3
3
  * by Rob Garrison
4
4
  */
@@ -172,17 +172,18 @@
172
172
 
173
173
  // all non-info tbody cells
174
174
  mathAttr = wo.math_dataAttrib;
175
- $mathCells = c.$tbodies.find( '[' + mathAttr + ']' );
175
+ $mathCells = c.$tbodies.children( 'tr' ).children( '[' + mathAttr + ']' );
176
176
  math.mathType( c, $mathCells, wo.math_priority );
177
177
 
178
178
  // only info tbody cells
179
179
  $mathCells = c.$table
180
180
  .children( '.' + c.cssInfoBlock + ', tfoot' )
181
- .find( '[' + mathAttr + ']' );
181
+ .children( 'tr' )
182
+ .children( '[' + mathAttr + ']' );
182
183
  math.mathType( c, $mathCells, wo.math_priority );
183
184
 
184
185
  // find the 'all' total
185
- $mathCells = c.$table.find( '[' + mathAttr + '^=all]' );
186
+ $mathCells = c.$table.children().children( 'tr' ).children( '[' + mathAttr + '^=all]' );
186
187
  math.mathType( c, $mathCells, [ 'all' ] );
187
188
 
188
189
  wo.math_isUpdating = true;
@@ -522,7 +523,7 @@
522
523
  if ( refreshing ) { return; }
523
524
  c.$table
524
525
  .off( ( math.events + ' updateComplete.tsmath ' + wo.math_event ).replace( /\s+/g, ' ' ) )
525
- .find( '[data-' + wo.math_data + ']' ).empty();
526
+ .children().children( 'tr' ).children( '[data-' + wo.math_data + ']' ).empty();
526
527
  }
527
528
  });
528
529
 
@@ -1,4 +1,4 @@
1
- /*! Widget: Pager - updated 10/31/2015 (v2.24.0) */
1
+ /*! Widget: Pager - updated 11/10/2015 (v2.24.4) */
2
2
  /* Requires tablesorter v2.8+ and jQuery 1.7+
3
3
  * by Rob Garrison
4
4
  */
@@ -13,7 +13,7 @@
13
13
  priority: 55, // load pager after filter widget
14
14
  options : {
15
15
  // output default: '{page}/{totalPages}'
16
- // possible variables: {page}, {totalPages}, {filteredPages}, {startRow}, {endRow}, {filteredRows} and {totalRows}
16
+ // possible variables: {size}, {page}, {totalPages}, {filteredPages}, {startRow}, {endRow}, {filteredRows} and {totalRows}
17
17
  pager_output: '{startRow} to {endRow} of {totalRows} rows', // '{page}/{totalPages}'
18
18
 
19
19
  // apply disabled classname to the pager arrows when the rows at either extreme is visible
@@ -654,6 +654,12 @@
654
654
  wo = c.widgetOptions;
655
655
  // process data
656
656
  if ( $.isFunction(wo.pager_ajaxProcessing) ) {
657
+
658
+ // in case nothing is returned by ajax, empty out the table; see #1032
659
+ // but do it before calling pager_ajaxProcessing because that function may add content
660
+ // directly to the table
661
+ c.$tbodies.eq(0).empty();
662
+
657
663
  // ajaxProcessing result: [ total, rows, headers ]
658
664
  var i, j, t, hsh, $f, $sh, $headers, $h, icon, th, d, l, rr_count, len,
659
665
  $table = c.$table,
@@ -712,9 +718,6 @@
712
718
  if (wo.pager_processAjaxOnInit) {
713
719
  c.$tbodies.eq(0).html( tds );
714
720
  }
715
- } else {
716
- // nothing returned by ajax, empty out the table; see #1032
717
- c.$tbodies.eq(0).empty();
718
721
  }
719
722
  wo.pager_processAjaxOnInit = true;
720
723
  // only add new header text if the length matches