jquery-tablesorter 1.19.1 → 1.19.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -4,7 +4,7 @@
4
4
  ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██
5
5
  █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀
6
6
  */
7
- /*! tablesorter (FORK) - updated 11-04-2015 (v2.24.3)*/
7
+ /*! tablesorter (FORK) - updated 11-10-2015 (v2.24.4)*/
8
8
  /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */
9
9
  (function(factory) {
10
10
  if (typeof define === 'function' && define.amd) {
@@ -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,