jquery-tablesorter 1.19.1 → 1.19.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,7 +4,7 @@
4
4
  ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██
5
5
  █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀
6
6
  */
7
- /*! tablesorter (FORK) - updated 11-04-2015 (v2.24.3)*/
7
+ /*! tablesorter (FORK) - updated 11-10-2015 (v2.24.4)*/
8
8
  /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */
9
9
  (function(factory) {
10
10
  if (typeof define === 'function' && define.amd) {
@@ -16,7 +16,7 @@
16
16
  }
17
17
  }(function($) {
18
18
 
19
- /*! TableSorter (FORK) v2.24.3 *//*
19
+ /*! TableSorter (FORK) v2.24.4 *//*
20
20
  * Client-side table sorting with ease!
21
21
  * @requires jQuery v1.2.6+
22
22
  *
@@ -39,7 +39,7 @@
39
39
  'use strict';
40
40
  var ts = $.tablesorter = {
41
41
 
42
- version : '2.24.3',
42
+ version : '2.24.4',
43
43
 
44
44
  parsers : [],
45
45
  widgets : [],
@@ -157,12 +157,13 @@
157
157
 
158
158
  // labels applied to sortable headers for accessibility (aria) support
159
159
  language : {
160
- sortAsc : 'Ascending sort applied, ',
161
- sortDesc : 'Descending sort applied, ',
162
- sortNone : 'No sort applied, ',
163
- nextAsc : 'activate to apply an ascending sort',
164
- nextDesc : 'activate to apply a descending sort',
165
- nextNone : 'activate to remove the sort'
160
+ sortAsc : 'Ascending sort applied, ',
161
+ sortDesc : 'Descending sort applied, ',
162
+ sortNone : 'No sort applied, ',
163
+ sortDisabled : 'sorting is disabled',
164
+ nextAsc : 'activate to apply an ascending sort',
165
+ nextDesc : 'activate to apply a descending sort',
166
+ nextNone : 'activate to remove the sort'
166
167
  },
167
168
 
168
169
  regex : {
@@ -1011,7 +1012,7 @@
1011
1012
  ▀████▀ ██ █████▀ ██ ██ ██ ██████
1012
1013
  */
1013
1014
  setHeadersCss : function( c ) {
1014
- var $sorted, header, indx, column, $header, nextSort, txt, tmp,
1015
+ var $sorted, indx, column,
1015
1016
  list = c.sortList,
1016
1017
  len = list.length,
1017
1018
  none = ts.css.sortNone + ' ' + c.cssNone,
@@ -1079,50 +1080,62 @@
1079
1080
  }
1080
1081
  // add verbose aria labels
1081
1082
  len = c.$headers.length;
1082
- $headers = c.$headers.not( '.sorter-false' );
1083
1083
  for ( indx = 0; indx < len; indx++ ) {
1084
- $header = $headers.eq( indx );
1085
- if ( $header.length ) {
1086
- header = $headers[ indx ];
1087
- column = parseInt( $header.attr( 'data-column' ), 10 );
1088
- nextSort = c.sortVars[ column ].order[ ( c.sortVars[ column ].count + 1 ) % ( c.sortReset ? 3 : 2 ) ];
1084
+ ts.setColumnAriaLabel( c, c.$headers.eq( indx ) );
1085
+ }
1086
+ },
1087
+
1088
+ // nextSort (optional), lets you disable next sort text
1089
+ setColumnAriaLabel : function( c, $header, nextSort ) {
1090
+ if ( $header.length ) {
1091
+ var column = parseInt( $header.attr( 'data-column' ), 10 ),
1089
1092
  tmp = $header.hasClass( ts.css.sortAsc ) ?
1090
1093
  'sortAsc' :
1091
- $header.hasClass( ts.css.sortDesc ) ? 'sortDesc' : 'sortNone';
1092
- txt = $.trim( $header.text() ) + ': ' +
1093
- ts.language[ tmp ] +
1094
- ts.language[ nextSort === 0 ? 'nextAsc' : nextSort === 1 ? 'nextDesc' : 'nextNone' ];
1095
- $header.attr( 'aria-label', txt );
1094
+ $header.hasClass( ts.css.sortDesc ) ? 'sortDesc' : 'sortNone',
1095
+ txt = $.trim( $header.text() ) + ': ' + ts.language[ tmp ];
1096
+ if ( $header.hasClass( 'sorter-false' ) || nextSort === false ) {
1097
+ txt += ts.language.sortDisabled;
1098
+ } else {
1099
+ nextSort = c.sortVars[ column ].order[ ( c.sortVars[ column ].count + 1 ) % ( c.sortReset ? 3 : 2 ) ];
1100
+ // if nextSort
1101
+ txt += ts.language[ nextSort === 0 ? 'nextAsc' : nextSort === 1 ? 'nextDesc' : 'nextNone' ];
1096
1102
  }
1103
+ $header.attr( 'aria-label', txt );
1097
1104
  }
1098
1105
  },
1099
1106
 
1100
1107
  updateHeader : function( c ) {
1101
- var index, isDisabled, $th, col,
1108
+ var index, isDisabled, $header, col,
1102
1109
  table = c.table,
1103
1110
  len = c.$headers.length;
1104
1111
  for ( index = 0; index < len; index++ ) {
1105
- $th = c.$headers.eq( index );
1112
+ $header = c.$headers.eq( index );
1106
1113
  col = ts.getColumnData( table, c.headers, index, true );
1107
1114
  // add 'sorter-false' class if 'parser-false' is set
1108
- isDisabled = ts.getData( $th, col, 'sorter' ) === 'false' || ts.getData( $th, col, 'parser' ) === 'false';
1109
- $th[ 0 ].sortDisabled = isDisabled;
1110
- $th[ isDisabled ? 'addClass' : 'removeClass' ]( 'sorter-false' ).attr( 'aria-disabled', '' + isDisabled );
1111
- // disable tab index on disabled cells
1112
- if ( c.tabIndex ) {
1113
- if ( isDisabled ) {
1114
- $th.removeAttr( 'tabindex' );
1115
- } else {
1116
- $th.attr( 'tabindex', '0' );
1117
- }
1115
+ isDisabled = ts.getData( $header, col, 'sorter' ) === 'false' || ts.getData( $header, col, 'parser' ) === 'false';
1116
+ ts.setColumnSort( c, $header, isDisabled );
1117
+ }
1118
+ },
1119
+
1120
+ setColumnSort : function( c, $header, isDisabled ) {
1121
+ var id = c.table.id;
1122
+ $header[ 0 ].sortDisabled = isDisabled;
1123
+ $header[ isDisabled ? 'addClass' : 'removeClass' ]( 'sorter-false' )
1124
+ .attr( 'aria-disabled', '' + isDisabled );
1125
+ // disable tab index on disabled cells
1126
+ if ( c.tabIndex ) {
1127
+ if ( isDisabled ) {
1128
+ $header.removeAttr( 'tabindex' );
1129
+ } else {
1130
+ $header.attr( 'tabindex', '0' );
1118
1131
  }
1119
- // aria-controls - requires table ID
1120
- if ( table.id ) {
1121
- if ( isDisabled ) {
1122
- $th.removeAttr( 'aria-controls' );
1123
- } else {
1124
- $th.attr( 'aria-controls', table.id );
1125
- }
1132
+ }
1133
+ // aria-controls - requires table ID
1134
+ if ( id ) {
1135
+ if ( isDisabled ) {
1136
+ $header.removeAttr( 'aria-controls' );
1137
+ } else {
1138
+ $header.attr( 'aria-controls', id );
1126
1139
  }
1127
1140
  }
1128
1141
  },
@@ -1420,13 +1433,12 @@
1420
1433
  event[ c.sortResetKey ] ? 2 : ( c.sortVars[ col ].count + 1 ) % ( c.sortReset ? 3 : 2 );
1421
1434
  // reset all sorts on non-current column - issue #30
1422
1435
  if ( c.sortRestart ) {
1423
- tmp = cell;
1424
1436
  for ( headerIndx = 0; headerIndx < len; headerIndx++ ) {
1425
1437
  $header = c.$headers.eq( headerIndx );
1438
+ tmp = parseInt( $header.attr( 'data-column' ), 10 );
1426
1439
  // only reset counts on columns that weren't just clicked on and if not included in a multisort
1427
- if ( $header[ 0 ] !== tmp &&
1428
- ( notMultiSort || $header.hasClass( ts.css.sortNone ) ) ) {
1429
- c.sortVars[ $header.attr( 'data-column' ) ].count = -1;
1440
+ if ( col !== tmp && ( notMultiSort || $header.hasClass( ts.css.sortNone ) ) ) {
1441
+ c.sortVars[ tmp ].count = -1;
1430
1442
  }
1431
1443
  }
1432
1444
  }
@@ -2185,14 +2197,14 @@
2185
2197
 
2186
2198
  // *** Process table ***
2187
2199
  // add processing indicator
2188
- isProcessing : function( $table, toggle, $ths ) {
2200
+ isProcessing : function( $table, toggle, $headers ) {
2189
2201
  $table = $( $table );
2190
2202
  var c = $table[ 0 ].config,
2191
2203
  // default to all headers
2192
- $header = $ths || $table.find( '.' + ts.css.header );
2204
+ $header = $headers || $table.find( '.' + ts.css.header );
2193
2205
  if ( toggle ) {
2194
- // don't use sortList if custom $ths used
2195
- if ( typeof $ths !== 'undefined' && c.sortList.length > 0 ) {
2206
+ // don't use sortList if custom $headers used
2207
+ if ( typeof $headers !== 'undefined' && c.sortList.length > 0 ) {
2196
2208
  // get headers from the sortList
2197
2209
  $header = $header.filter( function() {
2198
2210
  // get data-column from attr to keep compatibility with jQuery 1.2.6
@@ -2984,13 +2996,13 @@
2984
2996
 
2985
2997
  })(jQuery);
2986
2998
 
2987
- /*! Widget: filter - updated 11/4/2015 (v2.24.3) *//*
2999
+ /*! Widget: filter - updated 11/10/2015 (v2.24.4) *//*
2988
3000
  * Requires tablesorter v2.8+ and jQuery 1.7+
2989
3001
  * by Rob Garrison
2990
3002
  */
2991
3003
  ;( function ( $ ) {
2992
3004
  'use strict';
2993
- var tsf,
3005
+ var tsf, tsfRegex,
2994
3006
  ts = $.tablesorter || {},
2995
3007
  tscss = ts.css;
2996
3008
 
@@ -3096,20 +3108,21 @@
3096
3108
  // data.parsed = array ( by column ) of boolean values ( from filter_useParsedData or 'filter-parsed' class )
3097
3109
  types: {
3098
3110
  or : function( c, data, vars ) {
3099
- if ( tsf.regex.orTest.test( data.iFilter ) || tsf.regex.orSplit.test( data.filter ) ) {
3111
+ // look for "|", but not if it is inside of a regular expression
3112
+ if ( ( tsfRegex.orTest.test( data.iFilter ) || tsfRegex.orSplit.test( data.filter ) ) &&
3113
+ // this test for regex has potential to slow down the overall search
3114
+ !tsfRegex.regex.test( data.filter ) ) {
3100
3115
  var indx, filterMatched, query, regex,
3101
3116
  // duplicate data but split filter
3102
3117
  data2 = $.extend( {}, data ),
3103
- index = data.index,
3104
- parsed = data.parsed[ index ],
3105
- filter = data.filter.split( tsf.regex.orSplit ),
3106
- iFilter = data.iFilter.split( tsf.regex.orSplit ),
3118
+ filter = data.filter.split( tsfRegex.orSplit ),
3119
+ iFilter = data.iFilter.split( tsfRegex.orSplit ),
3107
3120
  len = filter.length;
3108
3121
  for ( indx = 0; indx < len; indx++ ) {
3109
3122
  data2.nestedFilters = true;
3110
- data2.filter = '' + ( tsf.parseFilter( c, filter[ indx ], index, parsed ) || '' );
3111
- data2.iFilter = '' + ( tsf.parseFilter( c, iFilter[ indx ], index, parsed ) || '' );
3112
- query = '(' + ( tsf.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')';
3123
+ data2.filter = '' + ( tsf.parseFilter( c, filter[ indx ], data ) || '' );
3124
+ data2.iFilter = '' + ( tsf.parseFilter( c, iFilter[ indx ], data ) || '' );
3125
+ query = '(' + ( tsf.parseFilter( c, data2.filter, data ) || '' ) + ')';
3113
3126
  try {
3114
3127
  // use try/catch, because query may not be a valid regex if "|" is contained within a partial regex search,
3115
3128
  // e.g "/(Alex|Aar" -> Uncaught SyntaxError: Invalid regular expression: /(/(Alex)/: Unterminated group
@@ -3131,22 +3144,20 @@
3131
3144
  },
3132
3145
  // Look for an AND or && operator ( logical and )
3133
3146
  and : function( c, data, vars ) {
3134
- if ( tsf.regex.andTest.test( data.filter ) ) {
3147
+ if ( tsfRegex.andTest.test( data.filter ) ) {
3135
3148
  var indx, filterMatched, result, query, regex,
3136
3149
  // duplicate data but split filter
3137
3150
  data2 = $.extend( {}, data ),
3138
- index = data.index,
3139
- parsed = data.parsed[ index ],
3140
- filter = data.filter.split( tsf.regex.andSplit ),
3141
- iFilter = data.iFilter.split( tsf.regex.andSplit ),
3151
+ filter = data.filter.split( tsfRegex.andSplit ),
3152
+ iFilter = data.iFilter.split( tsfRegex.andSplit ),
3142
3153
  len = filter.length;
3143
3154
  for ( indx = 0; indx < len; indx++ ) {
3144
3155
  data2.nestedFilters = true;
3145
- data2.filter = '' + ( tsf.parseFilter( c, filter[ indx ], index, parsed ) || '' );
3146
- data2.iFilter = '' + ( tsf.parseFilter( c, iFilter[ indx ], index, parsed ) || '' );
3147
- query = ( '(' + ( tsf.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')' )
3156
+ data2.filter = '' + ( tsf.parseFilter( c, filter[ indx ], data ) || '' );
3157
+ data2.iFilter = '' + ( tsf.parseFilter( c, iFilter[ indx ], data ) || '' );
3158
+ query = ( '(' + ( tsf.parseFilter( c, data2.filter, data ) || '' ) + ')' )
3148
3159
  // replace wild cards since /(a*)/i will match anything
3149
- .replace( tsf.regex.wild01, '\\S{1}' ).replace( tsf.regex.wild0More, '\\S*' );
3160
+ .replace( tsfRegex.wild01, '\\S{1}' ).replace( tsfRegex.wild0More, '\\S*' );
3150
3161
  try {
3151
3162
  // use try/catch just in case RegExp is invalid
3152
3163
  regex = new RegExp( data.isMatch ? query : '^' + query + '$', c.widgetOptions.filter_ignoreCase ? 'i' : '' );
@@ -3168,10 +3179,10 @@
3168
3179
  },
3169
3180
  // Look for regex
3170
3181
  regex: function( c, data ) {
3171
- if ( tsf.regex.regex.test( data.filter ) ) {
3182
+ if ( tsfRegex.regex.test( data.filter ) ) {
3172
3183
  var matches,
3173
3184
  // cache regex per column for optimal speed
3174
- regex = data.filter_regexCache[ data.index ] || tsf.regex.regex.exec( data.filter ),
3185
+ regex = data.filter_regexCache[ data.index ] || tsfRegex.regex.exec( data.filter ),
3175
3186
  isRegex = regex instanceof RegExp;
3176
3187
  try {
3177
3188
  if ( !isRegex ) {
@@ -3190,18 +3201,17 @@
3190
3201
  // Look for operators >, >=, < or <=
3191
3202
  operators: function( c, data ) {
3192
3203
  // ignore empty strings... because '' < 10 is true
3193
- if ( tsf.regex.operTest.test( data.iFilter ) && data.iExact !== '' ) {
3204
+ if ( tsfRegex.operTest.test( data.iFilter ) && data.iExact !== '' ) {
3194
3205
  var cachedValue, result, txt,
3195
3206
  table = c.table,
3196
- index = data.index,
3197
- parsed = data.parsed[index],
3198
- query = ts.formatFloat( data.iFilter.replace( tsf.regex.operators, '' ), table ),
3199
- parser = c.parsers[index],
3207
+ parsed = data.parsed[ data.index ],
3208
+ query = ts.formatFloat( data.iFilter.replace( tsfRegex.operators, '' ), table ),
3209
+ parser = c.parsers[ data.index ],
3200
3210
  savedSearch = query;
3201
3211
  // parse filter value in case we're comparing numbers ( dates )
3202
3212
  if ( parsed || parser.type === 'numeric' ) {
3203
- txt = $.trim( '' + data.iFilter.replace( tsf.regex.operators, '' ) );
3204
- result = tsf.parseFilter( c, txt, index, true );
3213
+ txt = $.trim( '' + data.iFilter.replace( tsfRegex.operators, '' ) );
3214
+ result = tsf.parseFilter( c, txt, data, true );
3205
3215
  query = ( typeof result === 'number' && result !== '' && !isNaN( result ) ) ? result : query;
3206
3216
  }
3207
3217
  // iExact may be numeric - see issue #149;
@@ -3213,10 +3223,10 @@
3213
3223
  txt = isNaN( data.iExact ) ? data.iExact.replace( ts.regex.nondigit, '' ) : data.iExact;
3214
3224
  cachedValue = ts.formatFloat( txt, table );
3215
3225
  }
3216
- if ( tsf.regex.gtTest.test( data.iFilter ) ) {
3217
- result = tsf.regex.gteTest.test( data.iFilter ) ? cachedValue >= query : cachedValue > query;
3218
- } else if ( tsf.regex.ltTest.test( data.iFilter ) ) {
3219
- result = tsf.regex.lteTest.test( data.iFilter ) ? cachedValue <= query : cachedValue < query;
3226
+ if ( tsfRegex.gtTest.test( data.iFilter ) ) {
3227
+ result = tsfRegex.gteTest.test( data.iFilter ) ? cachedValue >= query : cachedValue > query;
3228
+ } else if ( tsfRegex.ltTest.test( data.iFilter ) ) {
3229
+ result = tsfRegex.lteTest.test( data.iFilter ) ? cachedValue <= query : cachedValue < query;
3220
3230
  }
3221
3231
  // keep showing all rows if nothing follows the operator
3222
3232
  if ( !result && savedSearch === '' ) {
@@ -3228,13 +3238,13 @@
3228
3238
  },
3229
3239
  // Look for a not match
3230
3240
  notMatch: function( c, data ) {
3231
- if ( tsf.regex.notTest.test( data.iFilter ) ) {
3241
+ if ( tsfRegex.notTest.test( data.iFilter ) ) {
3232
3242
  var indx,
3233
3243
  txt = data.iFilter.replace( '!', '' ),
3234
- filter = tsf.parseFilter( c, txt, data.index, data.parsed[data.index] ) || '';
3235
- if ( tsf.regex.exact.test( filter ) ) {
3244
+ filter = tsf.parseFilter( c, txt, data ) || '';
3245
+ if ( tsfRegex.exact.test( filter ) ) {
3236
3246
  // look for exact not matches - see #628
3237
- filter = filter.replace( tsf.regex.exact, '' );
3247
+ filter = filter.replace( tsfRegex.exact, '' );
3238
3248
  return filter === '' ? true : $.trim( filter ) !== data.iExact;
3239
3249
  } else {
3240
3250
  indx = data.iExact.search( $.trim( filter ) );
@@ -3246,29 +3256,29 @@
3246
3256
  // Look for quotes or equals to get an exact match; ignore type since iExact could be numeric
3247
3257
  exact: function( c, data ) {
3248
3258
  /*jshint eqeqeq:false */
3249
- if ( tsf.regex.exact.test( data.iFilter ) ) {
3250
- var txt = data.iFilter.replace( tsf.regex.exact, '' ),
3251
- filter = tsf.parseFilter( c, txt, data.index, data.parsed[data.index] ) || '';
3259
+ if ( tsfRegex.exact.test( data.iFilter ) ) {
3260
+ var txt = data.iFilter.replace( tsfRegex.exact, '' ),
3261
+ filter = tsf.parseFilter( c, txt, data ) || '';
3252
3262
  return data.anyMatch ? $.inArray( filter, data.rowArray ) >= 0 : filter == data.iExact;
3253
3263
  }
3254
3264
  return null;
3255
3265
  },
3256
3266
  // Look for a range ( using ' to ' or ' - ' ) - see issue #166; thanks matzhu!
3257
3267
  range : function( c, data ) {
3258
- if ( tsf.regex.toTest.test( data.iFilter ) ) {
3268
+ if ( tsfRegex.toTest.test( data.iFilter ) ) {
3259
3269
  var result, tmp, range1, range2,
3260
3270
  table = c.table,
3261
3271
  index = data.index,
3262
3272
  parsed = data.parsed[index],
3263
3273
  // make sure the dash is for a range and not indicating a negative number
3264
- query = data.iFilter.split( tsf.regex.toSplit );
3274
+ query = data.iFilter.split( tsfRegex.toSplit );
3265
3275
 
3266
3276
  tmp = query[0].replace( ts.regex.nondigit, '' ) || '';
3267
- range1 = ts.formatFloat( tsf.parseFilter( c, tmp, index, parsed ), table );
3277
+ range1 = ts.formatFloat( tsf.parseFilter( c, tmp, data ), table );
3268
3278
  tmp = query[1].replace( ts.regex.nondigit, '' ) || '';
3269
- range2 = ts.formatFloat( tsf.parseFilter( c, tmp, index, parsed ), table );
3279
+ range2 = ts.formatFloat( tsf.parseFilter( c, tmp, data ), table );
3270
3280
  // parse filter value in case we're comparing numbers ( dates )
3271
- if ( parsed || c.parsers[index].type === 'numeric' ) {
3281
+ if ( parsed || c.parsers[ index ].type === 'numeric' ) {
3272
3282
  result = c.parsers[ index ].format( '' + query[0], table, c.$headers.eq( index ), index );
3273
3283
  range1 = ( result !== '' && !isNaN( result ) ) ? result : range1;
3274
3284
  result = c.parsers[ index ].format( '' + query[1], table, c.$headers.eq( index ), index );
@@ -3289,18 +3299,16 @@
3289
3299
  },
3290
3300
  // Look for wild card: ? = single, * = multiple, or | = logical OR
3291
3301
  wild : function( c, data ) {
3292
- if ( tsf.regex.wildOrTest.test( data.iFilter ) ) {
3293
- var index = data.index,
3294
- parsed = data.parsed[ index ],
3295
- query = '' + ( tsf.parseFilter( c, data.iFilter, index, parsed ) || '' );
3302
+ if ( tsfRegex.wildOrTest.test( data.iFilter ) ) {
3303
+ var query = '' + ( tsf.parseFilter( c, data.iFilter, data ) || '' );
3296
3304
  // look for an exact match with the 'or' unless the 'filter-match' class is found
3297
- if ( !tsf.regex.wildTest.test( query ) && data.nestedFilters ) {
3305
+ if ( !tsfRegex.wildTest.test( query ) && data.nestedFilters ) {
3298
3306
  query = data.isMatch ? query : '^(' + query + ')$';
3299
3307
  }
3300
3308
  // parsing the filter may not work properly when using wildcards =/
3301
3309
  try {
3302
3310
  return new RegExp(
3303
- query.replace( tsf.regex.wild01, '\\S{1}' ).replace( tsf.regex.wild0More, '\\S*' ),
3311
+ query.replace( tsfRegex.wild01, '\\S{1}' ).replace( tsfRegex.wild0More, '\\S*' ),
3304
3312
  c.widgetOptions.filter_ignoreCase ? 'i' : ''
3305
3313
  )
3306
3314
  .test( data.exact );
@@ -3312,21 +3320,18 @@
3312
3320
  },
3313
3321
  // fuzzy text search; modified from https://github.com/mattyork/fuzzy ( MIT license )
3314
3322
  fuzzy: function( c, data ) {
3315
- if ( tsf.regex.fuzzyTest.test( data.iFilter ) ) {
3323
+ if ( tsfRegex.fuzzyTest.test( data.iFilter ) ) {
3316
3324
  var indx,
3317
3325
  patternIndx = 0,
3318
3326
  len = data.iExact.length,
3319
3327
  txt = data.iFilter.slice( 1 ),
3320
- pattern = tsf.parseFilter( c, txt, data.index, data.parsed[data.index] ) || '';
3328
+ pattern = tsf.parseFilter( c, txt, data ) || '';
3321
3329
  for ( indx = 0; indx < len; indx++ ) {
3322
3330
  if ( data.iExact[ indx ] === pattern[ patternIndx ] ) {
3323
3331
  patternIndx += 1;
3324
3332
  }
3325
3333
  }
3326
- if ( patternIndx === pattern.length ) {
3327
- return true;
3328
- }
3329
- return false;
3334
+ return patternIndx === pattern.length;
3330
3335
  }
3331
3336
  return null;
3332
3337
  }
@@ -3339,8 +3344,7 @@
3339
3344
  and : 'and'
3340
3345
  }, ts.language );
3341
3346
 
3342
- var options, string, txt, $header, column, filters, val, fxn, noSelect,
3343
- regex = tsf.regex;
3347
+ var options, string, txt, $header, column, filters, val, fxn, noSelect;
3344
3348
  c.$table.addClass( 'hasFilters' );
3345
3349
 
3346
3350
  // define timers so using clearTimeout won't cause an undefined error
@@ -3351,8 +3355,8 @@
3351
3355
  wo.filter_anyColumnSelector = '[data-column="all"],[data-column="any"]';
3352
3356
  wo.filter_multipleColumnSelector = '[data-column*="-"],[data-column*=","]';
3353
3357
 
3354
- val = '\\{' + tsf.regex.query + '\\}';
3355
- $.extend( regex, {
3358
+ val = '\\{' + tsfRegex.query + '\\}';
3359
+ $.extend( tsfRegex, {
3356
3360
  child : new RegExp( c.cssChildRow ),
3357
3361
  filtered : new RegExp( wo.filter_filteredRow ),
3358
3362
  alreadyFiltered : new RegExp( '(\\s+(' + ts.language.or + '|-|' + ts.language.to + ')\\s+)', 'i' ),
@@ -3630,8 +3634,10 @@
3630
3634
  c.$table.data( 'lastSearch', filters );
3631
3635
  return filters;
3632
3636
  },
3633
- parseFilter: function( c, filter, column, parsed ) {
3634
- return parsed ? c.parsers[column].format( filter, c.table, [], column ) : filter;
3637
+ parseFilter: function( c, filter, data, parsed ) {
3638
+ return parsed || data.parsed[ data.index ] ?
3639
+ c.parsers[ data.index ].format( filter, c.table, [], data.index ) :
3640
+ filter;
3635
3641
  },
3636
3642
  buildRow: function( table, c, wo ) {
3637
3643
  var $filter, col, column, $header, makeSelect, disabled, name, ffxn, tmp,
@@ -3824,8 +3830,12 @@
3824
3830
  c.lastCombinedFilter = null;
3825
3831
  c.lastSearch = [];
3826
3832
  }
3827
- // convert filters to strings (maybe not the best method)- see #1070
3828
- filters = filters.join( '\u0000' ).split( '\u0000' );
3833
+ // convert filters to strings - see #1070
3834
+ filters = Array.prototype.map ?
3835
+ filters.map( String ) :
3836
+ // for IE8 & older browsers - maybe not the best method
3837
+ filters.join( '\u0000' ).split( '\u0000' );
3838
+
3829
3839
  if ( wo.filter_initialized ) {
3830
3840
  c.$table.trigger( 'filterStart', [ filters ] );
3831
3841
  }
@@ -3879,8 +3889,8 @@
3879
3889
  },
3880
3890
  defaultFilter: function( filter, mask ) {
3881
3891
  if ( filter === '' ) { return filter; }
3882
- var regex = tsf.regex.iQuery,
3883
- maskLen = mask.match( tsf.regex.igQuery ).length,
3892
+ var regex = tsfRegex.iQuery,
3893
+ maskLen = mask.match( tsfRegex.igQuery ).length,
3884
3894
  query = maskLen > 1 ? $.trim( filter ).split( /\s/ ) : [ $.trim( filter ) ],
3885
3895
  len = query.length - 1,
3886
3896
  indx = 0,
@@ -3977,9 +3987,8 @@
3977
3987
  return filterMatched;
3978
3988
  },
3979
3989
  processRow: function( c, data, vars ) {
3980
- var hasSelect, result, val, filterMatched,
3990
+ var result, filterMatched,
3981
3991
  fxn, ffxn, txt,
3982
- regex = tsf.regex,
3983
3992
  wo = c.widgetOptions,
3984
3993
  showRow = true,
3985
3994
 
@@ -4058,7 +4067,7 @@
4058
4067
  result = data.rawArray[ columnIndex ] || '';
4059
4068
  data.exact = c.sortLocaleCompare ? ts.replaceAccents( result ) : result; // issue #405
4060
4069
  }
4061
- data.iExact = !regex.type.test( typeof data.exact ) && wo.filter_ignoreCase ?
4070
+ data.iExact = !tsfRegex.type.test( typeof data.exact ) && wo.filter_ignoreCase ?
4062
4071
  data.exact.toLowerCase() : data.exact;
4063
4072
 
4064
4073
  data.isMatch = c.$headerIndexed[ data.index ].hasClass( 'filter-match' );
@@ -4076,21 +4085,13 @@
4076
4085
  data.filter = ts.replaceAccents( data.filter );
4077
4086
  }
4078
4087
 
4079
- val = true;
4080
- if ( wo.filter_defaultFilter && regex.iQuery.test( vars.defaultColFilter[ columnIndex ] ) ) {
4081
- data.filter = tsf.defaultFilter( data.filter, vars.defaultColFilter[ columnIndex ] );
4082
- // val is used to indicate that a filter select is using a default filter;
4083
- // so we override the exact & partial matches
4084
- val = false;
4085
- }
4086
4088
  // data.iFilter = case insensitive ( if wo.filter_ignoreCase is true ),
4087
4089
  // data.filter = case sensitive
4088
4090
  data.iFilter = wo.filter_ignoreCase ? ( data.filter || '' ).toLowerCase() : data.filter;
4089
4091
  fxn = vars.functions[ columnIndex ];
4090
- hasSelect = c.$headerIndexed[ columnIndex ].hasClass( 'filter-select' );
4091
4092
  filterMatched = null;
4092
- if ( fxn || ( hasSelect && val ) ) {
4093
- if ( fxn === true || hasSelect ) {
4093
+ if ( fxn ) {
4094
+ if ( fxn === true ) {
4094
4095
  // default selector uses exact match unless 'filter-match' class is found
4095
4096
  filterMatched = data.isMatch ?
4096
4097
  // data.iExact may be a number
@@ -4116,7 +4117,7 @@
4116
4117
  // Look for match, and add child row data for matching
4117
4118
  } else {
4118
4119
  txt = ( data.iExact + data.childRowText )
4119
- .indexOf( tsf.parseFilter( c, data.iFilter, columnIndex, data.parsed[ columnIndex ] ) );
4120
+ .indexOf( tsf.parseFilter( c, data.iFilter, data ) );
4120
4121
  result = ( ( !wo.filter_startsWith && txt >= 0 ) || ( wo.filter_startsWith && txt === 0 ) );
4121
4122
  }
4122
4123
  } else {
@@ -4136,7 +4137,6 @@
4136
4137
  isChild, childRow, lastSearch, showRow, showParent, time, val, indx,
4137
4138
  notFiltered, searchFiltered, query, injected, res, id, txt,
4138
4139
  storedFilters = $.extend( [], filters ),
4139
- regex = tsf.regex,
4140
4140
  c = table.config,
4141
4141
  wo = c.widgetOptions,
4142
4142
  // data object passed to filters; anyMatch is a flag for the filters
@@ -4218,7 +4218,7 @@
4218
4218
  );
4219
4219
  if ( wo.filter_columnAnyMatch ) {
4220
4220
  // specific columns search
4221
- query = data.anyMatchFilter.split( regex.andSplit );
4221
+ query = data.anyMatchFilter.split( tsfRegex.andSplit );
4222
4222
  injected = false;
4223
4223
  for ( indx = 0; indx < query.length; indx++ ) {
4224
4224
  res = query[ indx ].split( ':' );
@@ -4253,12 +4253,12 @@
4253
4253
  // there are no changes from beginning of filter
4254
4254
  val.indexOf( lastSearch[indx] || '' ) === 0 &&
4255
4255
  // if there is NOT a logical 'or', or range ( 'to' or '-' ) in the string
4256
- !regex.alreadyFiltered.test( val ) &&
4256
+ !tsfRegex.alreadyFiltered.test( val ) &&
4257
4257
  // if we are not doing exact matches, using '|' ( logical or ) or not '!'
4258
- !regex.exactTest.test( val ) &&
4258
+ !tsfRegex.exactTest.test( val ) &&
4259
4259
  // don't search only filtered if the value is negative
4260
4260
  // ( '> -10' => '> -100' will ignore hidden rows )
4261
- !( regex.isNeg1.test( val ) || regex.isNeg2.test( val ) ) &&
4261
+ !( tsfRegex.isNeg1.test( val ) || tsfRegex.isNeg2.test( val ) ) &&
4262
4262
  // if filtering using a select without a 'filter-match' class ( exact match ) - fixes #593
4263
4263
  !( val !== '' && c.$filters && c.$filters.filter( '[data-column="' + indx + '"]' ).find( 'select' ).length &&
4264
4264
  !c.$headerIndexed[indx].hasClass( 'filter-match' ) );
@@ -4276,7 +4276,7 @@
4276
4276
  // replace accents
4277
4277
  data.anyMatchFilter = ts.replaceAccents( data.anyMatchFilter );
4278
4278
  }
4279
- if ( wo.filter_defaultFilter && regex.iQuery.test( vars.defaultAnyFilter ) ) {
4279
+ if ( wo.filter_defaultFilter && tsfRegex.iQuery.test( vars.defaultAnyFilter ) ) {
4280
4280
  data.anyMatchFilter = tsf.defaultFilter( data.anyMatchFilter, vars.defaultAnyFilter );
4281
4281
  // clear search filtered flag because default filters are not saved to the last search
4282
4282
  searchFiltered = false;
@@ -4293,9 +4293,9 @@
4293
4293
 
4294
4294
  txt = $rows[ rowIndex ].className;
4295
4295
  // the first row can never be a child row
4296
- isChild = rowIndex && regex.child.test( txt );
4296
+ isChild = rowIndex && tsfRegex.child.test( txt );
4297
4297
  // skip child rows & already filtered rows
4298
- if ( isChild || ( searchFiltered && regex.filtered.test( txt ) ) ) {
4298
+ if ( isChild || ( searchFiltered && tsfRegex.filtered.test( txt ) ) ) {
4299
4299
  continue;
4300
4300
  }
4301
4301
 
@@ -4405,7 +4405,6 @@
4405
4405
  // custom select source function for a SPECIFIC COLUMN
4406
4406
  arry = fxn( table, column, onlyAvail );
4407
4407
  }
4408
-
4409
4408
  if ( arry === false ) {
4410
4409
  // fall back to original method
4411
4410
  arry = tsf.getOptions( table, column, onlyAvail );
@@ -4419,18 +4418,19 @@
4419
4418
  return false;
4420
4419
  }
4421
4420
  table = $( table )[0];
4422
- var cts, txt, indx, len,
4421
+ var cts, txt, indx, len, parsedTxt, str,
4423
4422
  c = table.config,
4424
4423
  validColumn = typeof column !== 'undefined' && column !== null && column >= 0 && column < c.columns,
4425
4424
  parsed = [];
4426
-
4427
4425
  // get unique elements and sort the list
4428
4426
  // if $.tablesorter.sortText exists ( not in the original tablesorter ),
4429
4427
  // then natural sort the list otherwise use a basic sort
4430
4428
  arry = $.grep( arry, function( value, indx ) {
4429
+ if ( value.text ) {
4430
+ return true;
4431
+ }
4431
4432
  return $.inArray( value, arry ) === indx;
4432
4433
  });
4433
-
4434
4434
  if ( validColumn && c.$headerIndexed[ column ].hasClass( 'filter-select-nosort' ) ) {
4435
4435
  // unsorted select options
4436
4436
  return arry;
@@ -4439,22 +4439,30 @@
4439
4439
  // parse select option values
4440
4440
  for ( indx = 0; indx < len; indx++ ) {
4441
4441
  txt = arry[ indx ];
4442
+ // check for object
4443
+ str = txt.text ? txt.text : txt;
4444
+ // sortNatural breaks if you don't pass it strings
4445
+ parsedTxt = ( validColumn && c.parsers && c.parsers.length &&
4446
+ c.parsers[ column ].format( str, table, [], column ) || str ).toString();
4447
+ parsedTxt = c.widgetOptions.filter_ignoreCase ? parsedTxt.toLowerCase() : parsedTxt;
4442
4448
  // parse array data using set column parser; this DOES NOT pass the original
4443
4449
  // table cell to the parser format function
4444
- parsed.push({
4445
- t : txt,
4446
- // check parser length - fixes #934
4447
- p : validColumn && c.parsers && c.parsers.length &&
4448
- c.parsers[ column ].format( txt, table, [], column ) || txt
4449
- });
4450
+ if ( txt.text ) {
4451
+ txt.parsed = parsedTxt;
4452
+ parsed.push( txt );
4453
+ } else {
4454
+ parsed.push({
4455
+ text : txt,
4456
+ // check parser length - fixes #934
4457
+ parsed : parsedTxt
4458
+ });
4459
+ }
4450
4460
  }
4451
-
4452
4461
  // sort parsed select options
4453
4462
  cts = c.textSorter || '';
4454
4463
  parsed.sort( function( a, b ) {
4455
- // sortNatural breaks if you don't pass it strings
4456
- var x = a.p.toString(),
4457
- y = b.p.toString();
4464
+ var x = a.parsed,
4465
+ y = b.parsed;
4458
4466
  if ( validColumn && typeof cts === 'function' ) {
4459
4467
  // custom OVERALL text sorter
4460
4468
  return cts( x, y, true, column, table );
@@ -4472,7 +4480,7 @@
4472
4480
  arry = [];
4473
4481
  len = parsed.length;
4474
4482
  for ( indx = 0; indx < len; indx++ ) {
4475
- arry.push( parsed[indx].t );
4483
+ arry.push( parsed[indx] );
4476
4484
  }
4477
4485
  return arry;
4478
4486
  }
@@ -4532,7 +4540,7 @@
4532
4540
  return;
4533
4541
  }
4534
4542
 
4535
- var indx, val, txt, t, $filters, $filter,
4543
+ var indx, val, txt, t, $filters, $filter, option,
4536
4544
  c = table.config,
4537
4545
  wo = c.widgetOptions,
4538
4546
  node = c.$headerIndexed[ column ],
@@ -4557,23 +4565,45 @@
4557
4565
  if ( $.isArray( arry ) ) {
4558
4566
  // build option list
4559
4567
  for ( indx = 0; indx < arry.length; indx++ ) {
4560
- txt = arry[indx] = ( '' + arry[indx] ).replace( tsf.regex.quote, '&quot;' );
4561
- val = txt;
4562
- // allow including a symbol in the selectSource array
4563
- // 'a-z|A through Z' so that 'a-z' becomes the option value
4564
- // and 'A through Z' becomes the option text
4565
- if ( txt.indexOf( wo.filter_selectSourceSeparator ) >= 0 ) {
4566
- t = txt.split( wo.filter_selectSourceSeparator );
4567
- val = t[0];
4568
- txt = t[1];
4568
+ option = arry[ indx ];
4569
+ if ( option.text ) {
4570
+ // OBJECT!! add data-function-name in case the value is set in filter_functions
4571
+ option['data-function-name'] = typeof option.value === 'undefined' ? option.text : option.value;
4572
+
4573
+ // support jQuery < v1.8, otherwise the below code could be shortened to
4574
+ // options += $( '<option>', option )[ 0 ].outerHTML;
4575
+ options += '<option';
4576
+ for ( val in option ) {
4577
+ if ( option.hasOwnProperty( val ) && val !== 'text' ) {
4578
+ options += ' ' + val + '="' + option[ val ] + '"';
4579
+ }
4580
+ }
4581
+ if ( !option.value ) {
4582
+ options += ' value="' + option.text + '"';
4583
+ }
4584
+ options += '>' + option.text + '</option>';
4585
+ // above code is needed in jQuery < v1.8
4586
+
4587
+ // make sure we don't turn an object into a string (objects without a "text" property)
4588
+ } else if ( '' + option !== '[object Object]' ) {
4589
+ txt = option = ( '' + option ).replace( tsfRegex.quote, '&quot;' );
4590
+ val = txt;
4591
+ // allow including a symbol in the selectSource array
4592
+ // 'a-z|A through Z' so that 'a-z' becomes the option value
4593
+ // and 'A through Z' becomes the option text
4594
+ if ( txt.indexOf( wo.filter_selectSourceSeparator ) >= 0 ) {
4595
+ t = txt.split( wo.filter_selectSourceSeparator );
4596
+ val = t[0];
4597
+ txt = t[1];
4598
+ }
4599
+ // replace quotes - fixes #242 & ignore empty strings
4600
+ // see http://stackoverflow.com/q/14990971/145346
4601
+ options += option !== '' ?
4602
+ '<option ' +
4603
+ ( val === txt ? '' : 'data-function-name="' + option + '" ' ) +
4604
+ 'value="' + val + '">' + txt +
4605
+ '</option>' : '';
4569
4606
  }
4570
- // replace quotes - fixes #242 & ignore empty strings
4571
- // see http://stackoverflow.com/q/14990971/145346
4572
- options += arry[indx] !== '' ?
4573
- '<option ' +
4574
- ( val === txt ? '' : 'data-function-name="' + arry[indx] + '" ' ) +
4575
- 'value="' + val + '">' + txt +
4576
- '</option>' : '';
4577
4607
  }
4578
4608
  // clear arry so it doesn't get appended twice
4579
4609
  arry = [];
@@ -4619,6 +4649,9 @@
4619
4649
  }
4620
4650
  };
4621
4651
 
4652
+ // filter regex variable
4653
+ tsfRegex = tsf.regex;
4654
+
4622
4655
  ts.getFilters = function( table, getRaw, setFilters, skipFirst ) {
4623
4656
  var i, $filters, $column, cols,
4624
4657
  filters = false,