jquery-tablesorter 1.17.1 → 1.17.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.
Files changed (21) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/lib/jquery-tablesorter/version.rb +1 -1
  4. data/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +6 -2
  5. data/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.combined.js +353 -228
  6. data/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +41 -37
  7. data/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +313 -192
  8. data/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js +2 -1
  9. data/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-metric.js +63 -48
  10. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-alignChar.js +1 -1
  11. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columnSelector.js +17 -12
  12. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js +10 -5
  13. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +114 -71
  14. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-output.js +95 -66
  15. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +3 -2
  16. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-repeatheaders.js +1 -1
  17. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js +120 -59
  18. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-scroller.js +510 -242
  19. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-sortTbodies.js +228 -0
  20. data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js +76 -59
  21. metadata +3 -2
@@ -54,6 +54,7 @@
54
54
  format : function( txt, table, cell, cellIndex ) {
55
55
  var $cell = $( cell ),
56
56
  wo = table.config.widgetOptions,
57
+ checkedClass = table.config.checkboxClass || 'checked',
57
58
  // returning plain language here because this is what is shown in the
58
59
  // group headers - change it as desired
59
60
  status = wo.group_checkbox ? wo.group_checkbox : [ 'checked', 'unchecked' ],
@@ -61,7 +62,7 @@
61
62
  isChecked = $input.length ? $input[ 0 ].checked : '';
62
63
  // adding class to row, indicating that a checkbox is checked; includes
63
64
  // a column index in case more than one checkbox happens to be in a row
64
- $cell.closest( 'tr' ).toggleClass( 'checked checked-' + cellIndex, isChecked );
65
+ $cell.closest( 'tr' ).toggleClass( checkedClass + ' ' + checkedClass + '-' + cellIndex, isChecked );
65
66
  return $input.length ? status[ isChecked ? 0 : 1 ] : txt;
66
67
  },
67
68
  parsed : true, // filter widget flag
@@ -1,75 +1,90 @@
1
1
  /*! Parser: metric *//*
2
2
  * Demo: http://jsfiddle.net/Mottie/abkNM/382/
3
- * Set the metric name in the header (defaults to "m|meter"), e.g.
4
- * <th data-metric-name="b|byte">HDD Size</th>
5
- * <th data-metric-name="m|meter">Distance</th>
3
+ * Set the metric name in the header (defaults to 'm|meter'), e.g.
4
+ * <th data-metric-name-abbr="b|B" data-metric-name-full="byte|Byte|BYTE">HDD Size</th>
5
+ * <th data-metric-name="m|meter">Distance</th> <!-- data-metric-name is deprecated in v2.22.2 -->
6
6
  */
7
7
  /*jshint jquery:true */
8
- ;(function($){
9
- "use strict";
8
+ ;( function( $ ) {
9
+ 'use strict';
10
10
 
11
11
  var prefixes = {
12
- // "prefix" : [ base 10, base 2 ]
12
+ // 'prefix' : [ base 10, base 2 ]
13
13
  // skipping IEEE 1541 defined prefixes: kibibyte, mebibyte, etc, for now.
14
- "Y|Yotta|yotta" : [ 1e24, Math.pow(1024, 8) ], // 1024^8
15
- "Z|Zetta|zetta" : [ 1e21, Math.pow(1024, 7) ], // 1024^7
16
- "E|Exa|exa" : [ 1e18, Math.pow(1024, 6) ], // 1024^6
17
- "P|Peta|peta" : [ 1e15, Math.pow(1024, 5) ], // 1024^5
18
- "T|Tera|tera" : [ 1e12, Math.pow(1024, 4) ], // 1024^4
19
- "G|Giga|giga" : [ 1e9, Math.pow(1024, 3) ], // 1024^3
20
- "M|Mega|mega" : [ 1e6, Math.pow(1024, 2) ], // 1024^2
21
- "k|Kilo|kilo" : [ 1e3, 1024 ], // 1024
14
+ 'Y|Yotta|yotta' : [ 1e24, Math.pow(1024, 8) ], // 1024^8
15
+ 'Z|Zetta|zetta' : [ 1e21, Math.pow(1024, 7) ], // 1024^7
16
+ 'E|Exa|exa' : [ 1e18, Math.pow(1024, 6) ], // 1024^6
17
+ 'P|Peta|peta' : [ 1e15, Math.pow(1024, 5) ], // 1024^5
18
+ 'T|Tera|tera' : [ 1e12, Math.pow(1024, 4) ], // 1024^4
19
+ 'G|Giga|giga' : [ 1e9, Math.pow(1024, 3) ], // 1024^3
20
+ 'M|Mega|mega' : [ 1e6, Math.pow(1024, 2) ], // 1024^2
21
+ 'k|Kilo|kilo' : [ 1e3, 1024 ], // 1024
22
22
  // prefixes below here are rarely, if ever, used in binary
23
- "h|hecto" : [ 1e2, 1e2 ],
24
- "da|deka" : [ 1e1, 1e1 ],
25
- "d|deci" : [ 1e-1, 1e-1 ],
26
- "c|centi" : [ 1e-2, 1e-2],
27
- "m|milli" : [ 1e-3, 1e-3 ],
28
- "µ|micro" : [ 1e-6, 1e-6 ],
29
- "n|nano" : [ 1e-9, 1e-9 ],
30
- "p|pico" : [ 1e-12, 1e-12 ],
31
- "f|femto" : [ 1e-15, 1e-15 ],
32
- "a|atto" : [ 1e-18, 1e-18 ],
33
- "z|zepto" : [ 1e-21, 1e-21 ],
34
- "y|yocto" : [ 1e-24, 1e-24 ]
23
+ 'h|hecto' : [ 1e2, 1e2 ],
24
+ 'da|deka' : [ 1e1, 1e1 ],
25
+ 'd|deci' : [ 1e-1, 1e-1 ],
26
+ 'c|centi' : [ 1e-2, 1e-2],
27
+ 'm|milli' : [ 1e-3, 1e-3 ],
28
+ 'µ|micro' : [ 1e-6, 1e-6 ],
29
+ 'n|nano' : [ 1e-9, 1e-9 ],
30
+ 'p|pico' : [ 1e-12, 1e-12 ],
31
+ 'f|femto' : [ 1e-15, 1e-15 ],
32
+ 'a|atto' : [ 1e-18, 1e-18 ],
33
+ 'z|zepto' : [ 1e-21, 1e-21 ],
34
+ 'y|yocto' : [ 1e-24, 1e-24 ]
35
35
  },
36
36
  // the \\d+ will not catch digits with spaces, commas or decimals; so use the value from n instead
37
- RegLong = "(\\d+)(\\s+)?([Zz]etta|[Ee]xa|[Pp]eta|[Tt]era|[Gg]iga|[Mm]ega|kilo|hecto|deka|deci|centi|milli|micro|nano|pico|femto|atto|zepto|yocto)(",
38
- RegAbbr = "(\\d+)(\\s+)?(Z|E|P|T|G|M|k|h|da|d|c|m|µ|n|p|f|a|z|y)(";
37
+ RegLong = '(\\d+)(\\s+)?([Zz]etta|[Ee]xa|[Pp]eta|[Tt]era|[Gg]iga|[Mm]ega|kilo|hecto|deka|deci|centi|milli|micro|nano|pico|femto|atto|zepto|yocto)(',
38
+ RegAbbr = '(\\d+)(\\s+)?(Z|E|P|T|G|M|k|h|da|d|c|m|µ|n|p|f|a|z|y)(',
39
+ // make these case-insensitive because we all forget the case for these binary values
40
+ byteTest = /^[b|bit|byte|o|octet]/i;
39
41
 
40
42
  $.tablesorter.addParser({
41
43
  id: 'metric',
42
44
  is: function() {
43
45
  return false;
44
46
  },
45
- format: function(s, table, cell, cellIndex) {
46
- var v = 'm|meter',
47
- b, t,
48
- // process number here to get a numerical format (us or eu)
49
- n = $.tablesorter.formatFloat(s.replace(/[^\w,. \-()]/g, ""), table),
50
- $t = table.config.$headerIndexed[cellIndex],
51
- m = $t.data('metric');
52
- if (!m) {
47
+ format: function(txt, table, cell, cellIndex) {
48
+ var unit, isBinary, nameLong, nameAbbr,
49
+ // default base unit name
50
+ base = 'm|meter',
51
+ // process number here to get a numerical format (us or eu)
52
+ num = $.tablesorter.formatFloat( txt.replace(/[^\w,. \-()]/g, ''), table ),
53
+ $t = table.config.$headerIndexed[ cellIndex ],
54
+ regex = $t.data( 'metric' );
55
+ if ( !regex ) {
53
56
  // stored values
54
- t = ($t.attr('data-metric-name') || v).split('|');
55
- m = [ t[1] || t[0].substring(1), t[0] ];
56
- m[2] = new RegExp(RegLong + m[0] + "|" + m[1] + ")");
57
- m[3] = new RegExp(RegAbbr + m[1] + ")");
58
- $t.data('metric', m);
57
+ unit = ( $t.attr('data-metric-name') || base ).split( '|' );
58
+ nameLong = $t.attr( 'data-metric-name-full' ) || '';
59
+ nameAbbr = $t.attr( 'data-metric-name-abbr' ) || '';
60
+ regex = [ nameLong || unit[1] || unit[0].substring(1), nameAbbr || unit[0] ];
61
+ isBinary = byteTest.test( regex.join( '' ) );
62
+ // adding 'data-metric-name-full' which would contain 'byte|BYTE|Byte' etc
63
+ regex[2] = new RegExp( RegLong + (
64
+ ( nameLong === '' ? '' : nameLong + '|' + nameAbbr ) ||
65
+ // with data-metric-name='b|byte', we end up with 'b|B|byte|BYTE' - maybe not the best solution for case-insensitivity
66
+ ( ( isBinary ? regex[0].toLowerCase() + '|' + regex[0].toUpperCase() : regex[0] ) + '|' +
67
+ ( isBinary ? regex[1].toLowerCase() + '|' + regex[1].toUpperCase() : regex[1] ) ) ) +
68
+ ')' );
69
+ // adding 'data-metric-name-abbr' which would contain 'b|B' etc
70
+ regex[3] = new RegExp( RegAbbr + ( nameAbbr ||
71
+ ( ( isBinary ? regex[1].toLowerCase() + '|' + regex[1].toUpperCase() : regex[1] ) ) ) +
72
+ ')' );
73
+ $t.data( 'metric', regex );
59
74
  }
60
75
  // find match to full name or abbreviation
61
- t = s.match(m[2]) || s.match(m[3]);
62
- if (t) {
63
- for (v in prefixes) {
64
- if (t[3].match(v)) {
76
+ unit = txt.match( regex[2] ) || txt.match( regex[3] );
77
+ if ( unit ) {
78
+ for ( base in prefixes ) {
79
+ if ( unit[3].match( base ) ) {
65
80
  // exception when using binary prefix
66
81
  // change base for binary use
67
- b = /^[b|bit|byte|o|octet]/.test(t[4]) ? 1 : 0;
68
- return n * prefixes[v][b];
82
+ isBinary = byteTest.test( unit[4] ) ? 1 : 0;
83
+ return num * prefixes[ base ][ isBinary ];
69
84
  }
70
85
  }
71
86
  }
72
- return n;
87
+ return num;
73
88
  },
74
89
  type: 'numeric'
75
90
  });
@@ -17,7 +17,7 @@ ts.alignChar = {
17
17
  column : this.column,
18
18
  align : $this.attr(wo.alignChar_charAttrib),
19
19
  alignIndex : parseInt( $this.attr(wo.alignChar_indexAttrib) || 0, 10),
20
- adjust : parseFloat($this.attr(wo.alignChar_adjustAttrib)) || 0,
20
+ adjust : parseFloat($this.attr(wo.alignChar_adjustAttrib)) || 0
21
21
  };
22
22
  vars.regex = new RegExp('\\' + vars.align, 'g');
23
23
  if (typeof vars.align !== 'undefined') {
@@ -27,8 +27,7 @@ tsColSel = ts.columnSelector = {
27
27
  }
28
28
 
29
29
  // unique table class name
30
- c.tableId = 'tablesorter' + new Date().getTime();
31
- c.$table.addClass( c.tableId );
30
+ c.$table.addClass( c.namespace.slice(1) + 'columnselector' );
32
31
 
33
32
  // build column selector/state array
34
33
  colSel = c.selector = { $container : $(wo.columnSelector_container || '<div>') };
@@ -205,9 +204,9 @@ tsColSel = ts.columnSelector = {
205
204
  },
206
205
 
207
206
  updateBreakpoints: function(c, wo) {
208
- var priority, column, breaks,
207
+ var priority, column, breaks, temp,
209
208
  colSel = c.selector,
210
- prefix = '.' + c.tableId,
209
+ prefix = c.namespace + 'columnselector',
211
210
  mediaAll = [],
212
211
  breakpts = '';
213
212
  if (wo.columnSelector_mediaquery && !colSel.auto) {
@@ -222,9 +221,12 @@ tsColSel = ts.columnSelector = {
222
221
  breaks = [];
223
222
  c.$headers.filter('[' + wo.columnSelector_priority + '=' + (priority + 1) + ']').each(function(){
224
223
  column = parseInt($(this).attr('data-column'), 10) + 1;
225
- breaks.push(prefix + ' col:nth-child(' + column + ')');
226
- breaks.push(prefix + ' tr th:nth-child(' + column + ')');
227
- breaks.push(prefix + ' tr td:nth-child(' + column + ')');
224
+ temp = ' col:nth-child(' + column + ')';
225
+ breaks.push(prefix + temp + ',' + prefix + '_extra_table' + temp);
226
+ temp = ' tr th:nth-child(' + column + ')';
227
+ breaks.push(prefix + temp + ',' + prefix + '_extra_table' + temp);
228
+ temp = ' tr td:nth-child(' + column + ')';
229
+ breaks.push(prefix + temp + ',' + prefix + '_extra_table' + temp);
228
230
  });
229
231
  if (breaks.length) {
230
232
  mediaAll = mediaAll.concat( breaks );
@@ -247,16 +249,19 @@ tsColSel = ts.columnSelector = {
247
249
  if (wo.columnSelector_mediaquery && c.selector.auto || c.selector.isInitializing) {
248
250
  return;
249
251
  }
250
- var column,
252
+ var column, temp,
251
253
  colSel = c.selector,
252
254
  styles = [],
253
- prefix = '.' + c.tableId;
255
+ prefix = c.namespace + 'columnselector';
254
256
  colSel.$container.find('input[data-column]').filter('[data-column!="auto"]').each(function(){
255
257
  if (!this.checked) {
256
258
  column = parseInt( $(this).attr('data-column'), 10 ) + 1;
257
- styles.push(prefix + ' col:nth-child(' + column + ')');
258
- styles.push(prefix + ' tr th:nth-child(' + column + ')');
259
- styles.push(prefix + ' tr td:nth-child(' + column + ')');
259
+ temp = ' col:nth-child(' + column + ')';
260
+ styles.push(prefix + temp + ',' + prefix + '_extra_table' + temp);
261
+ temp = ' tr th:nth-child(' + column + ')';
262
+ styles.push(prefix + temp + ',' + prefix + '_extra_table' + temp);
263
+ temp = ' tr td:nth-child(' + column + ')';
264
+ styles.push(prefix + temp + ',' + prefix + '_extra_table' + temp);
260
265
  }
261
266
  $(this).toggleClass( wo.columnSelector_cssChecked, this.checked );
262
267
  });
@@ -13,7 +13,8 @@ var tse = $.tablesorter.editable = {
13
13
  lastEdited: 'tseditable-last-edited-cell',
14
14
 
15
15
  editComplete: function( c, wo, $cell, refocus ) {
16
- $cell
16
+ c.$table
17
+ .find( '.' + tse.lastEdited )
17
18
  .removeClass( tse.lastEdited )
18
19
  .trigger( wo.editable_editComplete, [ c ] );
19
20
  // restore focus last cell after updating
@@ -139,6 +140,7 @@ var tse = $.tablesorter.editable = {
139
140
  .on( 'focus' + namespace, '[contenteditable]', function( e ) {
140
141
  clearTimeout( $( this ).data( 'timer' ) );
141
142
  c.$table.data( 'contentFocused', e.target );
143
+ c.table.isUpdating = true; // prevent sorting while editing
142
144
  var $this = $( this ),
143
145
  selAll = wo.editable_selectAll,
144
146
  column = $this.closest( 'td' ).index(),
@@ -150,7 +152,7 @@ var tse = $.tablesorter.editable = {
150
152
  $this
151
153
  .off( 'keydown' + namespace )
152
154
  .on( 'keydown' + namespace, function( e ){
153
- if ( wo.editable_enterToAccept && e.which === 13 ) {
155
+ if ( wo.editable_enterToAccept && e.which === 13 && !e.shiftKey ) {
154
156
  e.preventDefault();
155
157
  }
156
158
  });
@@ -184,10 +186,11 @@ var tse = $.tablesorter.editable = {
184
186
  // user cancelled
185
187
  $this.html( $this.data( 'original' ) ).trigger( 'blur' + namespace );
186
188
  c.$table.data( 'contentFocused', false );
189
+ c.table.isUpdating = false;
187
190
  return false;
188
191
  }
189
192
  // accept on enter ( if set ), alt-enter ( always ) or if autoAccept is set and element is blurred or unfocused
190
- t = e.which === 13 && ( wo.editable_enterToAccept || e.altKey ) || wo.editable_autoAccept && e.type !== 'keydown';
193
+ t = e.which === 13 && !e.shiftKey && ( wo.editable_enterToAccept || e.altKey ) || wo.editable_autoAccept && e.type !== 'keydown';
191
194
  // change if new or user hits enter ( if option set )
192
195
  if ( t && $this.data( 'before' ) !== txt ) {
193
196
 
@@ -212,11 +215,11 @@ var tse = $.tablesorter.editable = {
212
215
  if ( wo.editable_autoResort ) {
213
216
  setTimeout( function() {
214
217
  c.$table.trigger( 'sorton', [ c.sortList, function() {
215
- tse.editComplete( c, wo, c.$table.find( '.' + tse.lastEdited ), true );
218
+ tse.editComplete( c, wo, c.$table.data( 'contentFocused' ), true );
216
219
  }, true ] );
217
220
  }, 10 );
218
221
  } else {
219
- tse.editComplete( c, wo, c.$table.find( '.' + tse.lastEdited ) );
222
+ tse.editComplete( c, wo, c.$table.data( 'contentFocused' ) );
220
223
  }
221
224
  } ] );
222
225
  return false;
@@ -224,6 +227,8 @@ var tse = $.tablesorter.editable = {
224
227
  } else if ( !valid && e.type !== 'keydown' ) {
225
228
  clearTimeout( $this.data( 'timer' ) );
226
229
  $this.data( 'timer', setTimeout( function() {
230
+ c.table.isUpdating = false; // clear flag or sorting will be disabled
231
+
227
232
  if ( $.isFunction( wo.editable_blur ) ) {
228
233
  txt = $this.html();
229
234
  wo.editable_blur( wo.editable_trimContent ? $.trim( txt ) : txt, column, $this );
@@ -103,6 +103,66 @@ ts.filter = {
103
103
  // data.index = column index; table = table element ( DOM )
104
104
  // data.parsed = array ( by column ) of boolean values ( from filter_useParsedData or 'filter-parsed' class )
105
105
  types: {
106
+ or : function( c, data, vars ) {
107
+ if ( /\|/.test( data.iFilter ) || ts.filter.regex.orSplit.test( data.filter ) ) {
108
+ var indx, filterMatched, txt, query, regex,
109
+ // duplicate data but split filter
110
+ data2 = $.extend( {}, data ),
111
+ index = data.index,
112
+ parsed = data.parsed[ index ],
113
+ filter = data.filter.split( ts.filter.regex.orSplit ),
114
+ iFilter = data.iFilter.split( ts.filter.regex.orSplit ),
115
+ len = filter.length;
116
+ for ( indx = 0; indx < len; indx++ ) {
117
+ data2.nestedFilters = true;
118
+ data2.filter = '' + ( ts.filter.parseFilter( c, filter[ indx ], index, parsed ) || '' );
119
+ data2.iFilter = '' + ( ts.filter.parseFilter( c, iFilter[ indx ], index, parsed ) || '' );
120
+ query = '(' + ( ts.filter.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')';
121
+ regex = new RegExp( data.isMatch ? query : '^' + query + '$', c.widgetOptions.filter_ignoreCase ? 'i' : '' );
122
+ // filterMatched = data2.filter === '' && indx > 0 ? true
123
+ // look for an exact match with the 'or' unless the 'filter-match' class is found
124
+ filterMatched = regex.test( data2.exact ) || ts.filter.processTypes( c, data2, vars );
125
+ if ( filterMatched ) {
126
+ return filterMatched;
127
+ }
128
+ }
129
+ // may be null from processing types
130
+ return filterMatched || false;
131
+ }
132
+ return null;
133
+ },
134
+ // Look for an AND or && operator ( logical and )
135
+ and : function( c, data, vars ) {
136
+ if ( ts.filter.regex.andTest.test( data.filter ) ) {
137
+ var indx, filterMatched, result, txt, query, regex,
138
+ // duplicate data but split filter
139
+ data2 = $.extend( {}, data ),
140
+ index = data.index,
141
+ parsed = data.parsed[ index ],
142
+ filter = data.filter.split( ts.filter.regex.andSplit ),
143
+ iFilter = data.iFilter.split( ts.filter.regex.andSplit ),
144
+ len = filter.length;
145
+ for ( indx = 0; indx < len; indx++ ) {
146
+ data2.nestedFilters = true;
147
+ data2.filter = '' + ( ts.filter.parseFilter( c, filter[ indx ], index, parsed ) || '' );
148
+ data2.iFilter = '' + ( ts.filter.parseFilter( c, iFilter[ indx ], index, parsed ) || '' );
149
+ query = ( '(' + ( ts.filter.parseFilter( c, data2.filter, index, parsed ) || '' ) + ')' )
150
+ // replace wild cards since /(a*)/i will match anything
151
+ .replace( /\?/g, '\\S{1}' ).replace( /\*/g, '\\S*' );
152
+ regex = new RegExp( data.isMatch ? query : '^' + query + '$', c.widgetOptions.filter_ignoreCase ? 'i' : '' );
153
+ // look for an exact match with the 'and' unless the 'filter-match' class is found
154
+ result = ( regex.test( data2.exact ) || ts.filter.processTypes( c, data2, vars ) );
155
+ if ( indx === 0 ) {
156
+ filterMatched = result;
157
+ } else {
158
+ filterMatched = filterMatched && result;
159
+ }
160
+ }
161
+ // may be null from processing types
162
+ return filterMatched || false;
163
+ }
164
+ return null;
165
+ },
106
166
  // Look for regex
107
167
  regex: function( c, data ) {
108
168
  if ( ts.filter.regex.regex.test( data.filter ) ) {
@@ -190,23 +250,6 @@ ts.filter = {
190
250
  }
191
251
  return null;
192
252
  },
193
- // Look for an AND or && operator ( logical and )
194
- and : function( c, data ) {
195
- if ( ts.filter.regex.andTest.test( data.filter ) ) {
196
- var index = data.index,
197
- parsed = data.parsed[index],
198
- query = data.iFilter.split( ts.filter.regex.andSplit ),
199
- result = data.iExact.search( $.trim( ts.filter.parseFilter( c, query[0], index, parsed ) ) ) >= 0,
200
- indx = query.length - 1;
201
- while ( result && indx ) {
202
- result = result &&
203
- data.iExact.search( $.trim( ts.filter.parseFilter( c, query[indx], index, parsed ) ) ) >= 0;
204
- indx--;
205
- }
206
- return result;
207
- }
208
- return null;
209
- },
210
253
  // Look for a range ( using ' to ' or ' - ' ) - see issue #166; thanks matzhu!
211
254
  range : function( c, data ) {
212
255
  if ( ts.filter.regex.toTest.test( data.iFilter ) ) {
@@ -243,24 +286,20 @@ ts.filter = {
243
286
  },
244
287
  // Look for wild card: ? = single, * = multiple, or | = logical OR
245
288
  wild : function( c, data ) {
246
- if ( /[\?\*\|]/.test( data.iFilter ) || ts.filter.regex.orReplace.test( data.filter ) ) {
289
+ if ( /[\?\*\|]/.test( data.iFilter ) ) {
247
290
  var index = data.index,
248
291
  parsed = data.parsed[ index ],
249
- txt = data.iFilter.replace( ts.filter.regex.orReplace, '|' ),
250
- query = '' + ( ts.filter.parseFilter( c, txt, index, parsed ) || '' );
292
+ query = '' + ( ts.filter.parseFilter( c, data.iFilter, index, parsed ) || '' );
251
293
  // look for an exact match with the 'or' unless the 'filter-match' class is found
252
- if ( !c.$headerIndexed[ index ].hasClass( 'filter-match' ) && /\|/.test( query ) ) {
253
- // show all results while using filter match. Fixes #727
254
- if ( query[ query.length - 1 ] === '|' ) {
255
- query += '*';
256
- }
257
- query = data.anyMatch && $.isArray( data.rowArray ) ?
258
- '(' + query + ')' :
259
- '^(' + query + ')$';
294
+ if ( !/\?\*/.test( query ) && data.nestedFilters ) {
295
+ query = data.isMatch ? query : '^(' + query + ')$';
260
296
  }
261
297
  // parsing the filter may not work properly when using wildcards =/
262
- return new RegExp( query.replace( /\?/g, '\\S{1}' ).replace( /\*/g, '\\S*' ) )
263
- .test( data.iExact );
298
+ return new RegExp(
299
+ query.replace( /\?/g, '\\S{1}' ).replace( /\*/g, '\\S*' ),
300
+ c.widgetOptions.filter_ignoreCase ? 'i' : ''
301
+ )
302
+ .test( data.exact );
264
303
  }
265
304
  return null;
266
305
  },
@@ -314,7 +353,7 @@ ts.filter = {
314
353
  toSplit : new RegExp( '(?:\\s+(?:-|' + ts.language.to + ')\\s+)' ,'gi' ),
315
354
  andTest : new RegExp( '\\s+(' + ts.language.and + '|&&)\\s+', 'i' ),
316
355
  andSplit : new RegExp( '(?:\\s+(?:' + ts.language.and + '|&&)\\s+)', 'gi' ),
317
- orReplace : new RegExp( '\\s+(' + ts.language.or + ')\\s+', 'gi' ),
356
+ orSplit : new RegExp( '(?:\\s+(?:' + ts.language.or + ')\\s+|\\|)', 'gi' ),
318
357
  iQuery : new RegExp( val, 'i' ),
319
358
  igQuery : new RegExp( val, 'ig' )
320
359
  });
@@ -534,7 +573,6 @@ ts.filter = {
534
573
  }
535
574
  }
536
575
  },
537
-
538
576
  setDefaults: function( table, c, wo ) {
539
577
  var isArray, saved, indx, col, $filters,
540
578
  // get current ( default ) filters
@@ -761,14 +799,13 @@ ts.filter = {
761
799
  }
762
800
  },
763
801
  hideFilters: function( table, c ) {
764
- var $filterRow, $filterRow2, timer;
765
- $( table )
802
+ var timer;
803
+ c.$table
766
804
  .find( '.' + tscss.filterRow )
767
- .addClass( tscss.filterRowHide )
768
805
  .bind( 'mouseenter mouseleave', function( e ) {
769
806
  // save event object - http://bugs.jquery.com/ticket/12140
770
- var event = e;
771
- $filterRow = $( this );
807
+ var event = e,
808
+ $filterRow = $( this );
772
809
  clearTimeout( timer );
773
810
  timer = setTimeout( function() {
774
811
  if ( /enter|over/.test( event.type ) ) {
@@ -786,13 +823,14 @@ ts.filter = {
786
823
  }, 200 );
787
824
  })
788
825
  .find( 'input, select' ).bind( 'focus blur', function( e ) {
789
- $filterRow2 = $( this ).closest( 'tr' );
826
+ var event = e,
827
+ $row = $( this ).closest( 'tr' );
790
828
  clearTimeout( timer );
791
- var event = e;
792
829
  timer = setTimeout( function() {
830
+ clearTimeout( timer );
793
831
  // don't hide row if any filter has a value
794
832
  if ( ts.getFilters( c.$table ).join( '' ) === '' ) {
795
- $filterRow2.toggleClass( tscss.filterRowHide, event.type === 'focus' );
833
+ $row.toggleClass( tscss.filterRowHide, event.type !== 'focus' );
796
834
  }
797
835
  }, 200 );
798
836
  });
@@ -826,7 +864,7 @@ ts.filter = {
826
864
  return $( b ).attr( 'data-lastSearchTime' ) - $( a ).attr( 'data-lastSearchTime' );
827
865
  });
828
866
  }
829
- return $();
867
+ return $input || $();
830
868
  },
831
869
  multipleColumns: function( c, $input ) {
832
870
  // look for multiple columns '1-3,4-6,8' in data-column
@@ -879,8 +917,22 @@ ts.filter = {
879
917
  }
880
918
  return columns;
881
919
  },
920
+ processTypes: function( c, data, vars ) {
921
+ var ffxn,
922
+ filterMatched = null,
923
+ matches = null;
924
+ for ( ffxn in ts.filter.types ) {
925
+ if ( $.inArray( ffxn, vars.excludeMatch ) < 0 && matches === null ) {
926
+ matches = ts.filter.types[ffxn]( c, data, vars );
927
+ if ( matches !== null ) {
928
+ filterMatched = matches;
929
+ }
930
+ }
931
+ }
932
+ return filterMatched;
933
+ },
882
934
  processRow: function( c, data, vars ) {
883
- var $cell, columnIndex, hasSelect, matches, result, val, filterMatched, excludeMatch,
935
+ var columnIndex, hasSelect, result, val, filterMatched,
884
936
  fxn, ffxn, txt,
885
937
  regex = ts.filter.regex,
886
938
  wo = c.widgetOptions,
@@ -891,6 +943,7 @@ ts.filter = {
891
943
  // look for multiple columns '1-3,4-6,8'
892
944
  columnIndex = ts.filter.multipleColumns( c, wo.filter_$anyMatch );
893
945
  data.anyMatch = true;
946
+ data.isMatch = true;
894
947
  data.rowArray = data.$cells.map( function( i ) {
895
948
  if ( $.inArray( i, columnIndex ) > -1 ) {
896
949
  if ( data.parsed[ i ] ) {
@@ -910,16 +963,10 @@ ts.filter = {
910
963
  data.exact = data.rowArray.join( ' ' );
911
964
  data.iExact = wo.filter_ignoreCase ? data.exact.toLowerCase() : data.exact;
912
965
  data.cache = data.cacheArray.slice( 0, -1 ).join( ' ' );
913
- filterMatched = null;
914
- matches = null;
915
- for ( ffxn in ts.filter.types ) {
916
- if ( $.inArray( ffxn, vars.noAnyMatch ) < 0 && matches === null ) {
917
- matches = ts.filter.types[ffxn]( c, data );
918
- if ( matches !== null ) {
919
- filterMatched = matches;
920
- }
921
- }
922
- }
966
+
967
+ vars.excludeMatch = vars.noAnyMatch;
968
+ filterMatched = ts.filter.processTypes( c, data, vars );
969
+
923
970
  if ( filterMatched !== null ) {
924
971
  showRow = filterMatched;
925
972
  } else {
@@ -946,7 +993,7 @@ ts.filter = {
946
993
  data.index = columnIndex;
947
994
 
948
995
  // filter types to exclude, per column
949
- excludeMatch = vars.excludeFilter[ columnIndex ];
996
+ vars.excludeMatch = vars.excludeFilter[ columnIndex ];
950
997
 
951
998
  // ignore if filter is empty or disabled
952
999
  if ( data.filter ) {
@@ -960,6 +1007,9 @@ ts.filter = {
960
1007
  }
961
1008
  data.iExact = !regex.type.test( typeof data.exact ) && wo.filter_ignoreCase ?
962
1009
  data.exact.toLowerCase() : data.exact;
1010
+
1011
+ data.isMatch = c.$headerIndexed[ data.index ].hasClass( 'filter-match' );
1012
+
963
1013
  result = showRow; // if showRow is true, show that row
964
1014
 
965
1015
  // in case select filter option has a different value vs text 'a - z|A through Z'
@@ -984,13 +1034,12 @@ ts.filter = {
984
1034
  // data.filter = case sensitive
985
1035
  data.iFilter = wo.filter_ignoreCase ? ( data.filter || '' ).toLowerCase() : data.filter;
986
1036
  fxn = vars.functions[ columnIndex ];
987
- $cell = c.$headerIndexed[ columnIndex ];
988
- hasSelect = $cell.hasClass( 'filter-select' );
1037
+ hasSelect = c.$headerIndexed[ columnIndex ].hasClass( 'filter-select' );
989
1038
  filterMatched = null;
990
1039
  if ( fxn || ( hasSelect && val ) ) {
991
1040
  if ( fxn === true || hasSelect ) {
992
1041
  // default selector uses exact match unless 'filter-match' class is found
993
- filterMatched = $cell.hasClass( 'filter-match' ) ?
1042
+ filterMatched = data.isMatch ?
994
1043
  data.iExact.search( data.iFilter ) >= 0 :
995
1044
  data.filter === data.exact;
996
1045
  } else if ( typeof fxn === 'function' ) {
@@ -1007,15 +1056,7 @@ ts.filter = {
1007
1056
  if ( filterMatched === null ) {
1008
1057
  // cycle through the different filters
1009
1058
  // filters return a boolean or null if nothing matches
1010
- matches = null;
1011
- for ( ffxn in ts.filter.types ) {
1012
- if ( $.inArray( ffxn, excludeMatch ) < 0 && matches === null ) {
1013
- matches = ts.filter.types[ ffxn ]( c, data );
1014
- if ( matches !== null ) {
1015
- filterMatched = matches;
1016
- }
1017
- }
1018
- }
1059
+ filterMatched = ts.filter.processTypes( c, data, vars );
1019
1060
  if ( filterMatched !== null ) {
1020
1061
  result = filterMatched;
1021
1062
  // Look for match, and add child row data for matching
@@ -1049,7 +1090,7 @@ ts.filter = {
1049
1090
  anyMatch: false,
1050
1091
  filters: filters,
1051
1092
  // regex filter type cache
1052
- filter_regexCache : [],
1093
+ filter_regexCache : []
1053
1094
  },
1054
1095
  vars = {
1055
1096
  // anyMatch really screws up with these types of filters
@@ -1270,7 +1311,7 @@ ts.filter = {
1270
1311
  },
1271
1312
  getOptionSource: function( table, column, onlyAvail ) {
1272
1313
  table = $( table )[0];
1273
- var cts, indx, len,
1314
+ var cts, txt, indx, len,
1274
1315
  c = table.config,
1275
1316
  wo = c.widgetOptions,
1276
1317
  parsed = [],
@@ -1315,11 +1356,13 @@ ts.filter = {
1315
1356
  len = arry.length;
1316
1357
  // parse select option values
1317
1358
  for ( indx = 0; indx < len; indx++ ) {
1359
+ txt = arry[ indx ];
1318
1360
  // parse array data using set column parser; this DOES NOT pass the original
1319
1361
  // table cell to the parser format function
1320
1362
  parsed.push({
1321
- t : arry[ indx ],
1322
- p : c.parsers && c.parsers[ column ].format( arry[ indx ], table, [], column )
1363
+ t : txt,
1364
+ // check parser length - fixes #934
1365
+ p : c.parsers && c.parsers.length && c.parsers[ column ].format( txt, table, [], column ) || txt
1323
1366
  });
1324
1367
  }
1325
1368
 
@@ -1509,8 +1552,8 @@ ts.getFilters = function( table, getRaw, setFilters, skipFirst ) {
1509
1552
  $column = ts.filter.getLatestSearch( $column );
1510
1553
  if ( $.isArray( setFilters ) ) {
1511
1554
  // skip first ( latest input ) to maintain cursor position while typing
1512
- if ( skipFirst ) {
1513
- $column.slice( 1 );
1555
+ if ( skipFirst && $column.length > 1 ) {
1556
+ $column = $column.slice( 1 );
1514
1557
  }
1515
1558
  if ( i === c.columns ) {
1516
1559
  // prevent data-column='all' from filling data-column='0,1' ( etc )