jquery-tablesorter 1.9.5 → 1.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/MIT-LICENSE +1 -1
- data/README.markdown +7 -2
- data/lib/jquery-tablesorter/version.rb +1 -1
- data/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +73 -29
- data/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +156 -90
- data/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets-filter-formatter.js +437 -235
- data/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +257 -167
- data/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css +1 -0
- data/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css +1 -0
- data/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap.css +8 -1
- data/vendor/assets/stylesheets/jquery-tablesorter/theme.bootstrap_2.css +1 -0
- data/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css +1 -0
- data/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css +1 -0
- data/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css +1 -0
- data/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css +1 -0
- data/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css +1 -0
- data/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css +1 -0
- data/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css +1 -0
- metadata +55 -55
@@ -1,4 +1,4 @@
|
|
1
|
-
/*! tableSorter 2.8+ widgets - updated
|
1
|
+
/*! tableSorter 2.8+ widgets - updated 2/19/2014 (v2.15.0)
|
2
2
|
*
|
3
3
|
* Column Styles
|
4
4
|
* Column Filters
|
@@ -49,6 +49,16 @@ ts.themes = {
|
|
49
49
|
}
|
50
50
|
};
|
51
51
|
|
52
|
+
$.extend(ts.css, {
|
53
|
+
filterRow : 'tablesorter-filter-row', // filter
|
54
|
+
filter : 'tablesorter-filter',
|
55
|
+
wrapper : 'tablesorter-wrapper', // ui theme & resizable
|
56
|
+
resizer : 'tablesorter-resizer', // resizable
|
57
|
+
grip : 'tablesorter-resizer-grip',
|
58
|
+
sticky : 'tablesorter-stickyHeader', // stickyHeader
|
59
|
+
stickyVis : 'tablesorter-sticky-visible'
|
60
|
+
});
|
61
|
+
|
52
62
|
// *** Store data in local storage, with a cookie fallback ***
|
53
63
|
/* IE7 needs JSON library for JSON.stringify - (http://caniuse.com/#search=json)
|
54
64
|
if you need it, then include https://github.com/douglascrockford/JSON-js
|
@@ -200,16 +210,16 @@ ts.addWidget({
|
|
200
210
|
// toggleClass with switch added in jQuery 1.3
|
201
211
|
$(this)[ event.type === 'mouseenter' ? 'addClass' : 'removeClass' ](themes.hover);
|
202
212
|
});
|
203
|
-
if (!$headers.find('.
|
213
|
+
if (!$headers.find('.' + ts.css.wrapper).length) {
|
204
214
|
// Firefox needs this inner div to position the resizer correctly
|
205
|
-
$headers.wrapInner('<div class="
|
215
|
+
$headers.wrapInner('<div class="' + ts.css.wrapper + '" style="position:relative;height:100%;width:100%"></div>');
|
206
216
|
}
|
207
217
|
if (c.cssIcon) {
|
208
218
|
// if c.cssIcon is '', then no <i> is added to the header
|
209
219
|
$headers.find('.' + ts.css.icon).addClass(themes.icons);
|
210
220
|
}
|
211
221
|
if ($table.hasClass('hasFilters')) {
|
212
|
-
$headers.find('.
|
222
|
+
$headers.find('.' + ts.css.filterRow).addClass(themes.filterRow);
|
213
223
|
}
|
214
224
|
}
|
215
225
|
$.each($headers, function() {
|
@@ -218,7 +228,7 @@ ts.addWidget({
|
|
218
228
|
if (this.sortDisabled) {
|
219
229
|
// no sort arrows for disabled columns!
|
220
230
|
$header.removeClass(remove);
|
221
|
-
$icon.removeClass(remove + '
|
231
|
+
$icon.removeClass(remove + ' ' + themes.icons);
|
222
232
|
} else {
|
223
233
|
classes = ($header.hasClass(ts.css.sortAsc)) ?
|
224
234
|
themes.sortAsc :
|
@@ -244,9 +254,9 @@ ts.addWidget({
|
|
244
254
|
$headers
|
245
255
|
.unbind('mouseenter.tsuitheme mouseleave.tsuitheme') // remove hover
|
246
256
|
.removeClass(themes.hover + ' ' + remove + ' ' + themes.active)
|
247
|
-
.find('.
|
257
|
+
.find('.' + ts.css.filterRow)
|
248
258
|
.removeClass(themes.filterRow);
|
249
|
-
$headers.find('.
|
259
|
+
$headers.find('.' + ts.css.icon).removeClass(themes.icons);
|
250
260
|
}
|
251
261
|
});
|
252
262
|
|
@@ -338,13 +348,14 @@ ts.addWidget({
|
|
338
348
|
id: "filter",
|
339
349
|
priority: 50,
|
340
350
|
options : {
|
341
|
-
filter_anyMatch : false, // if true overrides default find rows behaviours and if any column matches query it returns that row
|
342
351
|
filter_childRows : false, // if true, filter includes child row content in the search
|
343
352
|
filter_columnFilters : true, // if true, a filter will be added to the top of each table column
|
344
353
|
filter_cssFilter : '', // css class name added to the filter row & each input in the row (tablesorter-filter is ALWAYS added)
|
354
|
+
filter_external : '', // jQuery selector string (or jQuery object) of external filters
|
345
355
|
filter_filteredRow : 'filtered', // class added to filtered rows; needed by pager plugin
|
346
356
|
filter_formatter : null, // add custom filter elements to the filter row
|
347
357
|
filter_functions : null, // add custom filter functions using this option
|
358
|
+
filter_hideEmpty : true, // hide filter row when table is empty
|
348
359
|
filter_hideFilters : false, // collapse filter row when mouse leaves the area
|
349
360
|
filter_ignoreCase : true, // if true, make all searches case-insensitive
|
350
361
|
filter_liveSearch : true, // if true, search column content while the user types (with a delay)
|
@@ -359,9 +370,7 @@ ts.addWidget({
|
|
359
370
|
},
|
360
371
|
format: function(table, c, wo) {
|
361
372
|
if (!c.$table.hasClass('hasFilters')) {
|
362
|
-
|
363
|
-
ts.filter.init(table, c, wo);
|
364
|
-
}
|
373
|
+
ts.filter.init(table, c, wo);
|
365
374
|
}
|
366
375
|
},
|
367
376
|
remove: function(table, c, wo) {
|
@@ -372,7 +381,7 @@ ts.addWidget({
|
|
372
381
|
.removeClass('hasFilters')
|
373
382
|
// add .tsfilter namespace to all BUT search
|
374
383
|
.unbind('addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search '.split(' ').join('.tsfilter '))
|
375
|
-
.find('.
|
384
|
+
.find('.' + ts.css.filterRow).remove();
|
376
385
|
for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) {
|
377
386
|
$tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); // remove tbody
|
378
387
|
$tbody.children().removeClass(wo.filter_filteredRow).show();
|
@@ -419,23 +428,6 @@ ts.filter = {
|
|
419
428
|
}
|
420
429
|
return null;
|
421
430
|
},
|
422
|
-
// Look for quotes or equals to get an exact match; ignore type since iExact could be numeric
|
423
|
-
exact: function( filter, iFilter, exact, iExact ) {
|
424
|
-
/*jshint eqeqeq:false */
|
425
|
-
if (ts.filter.regex.exact.test(iFilter)) {
|
426
|
-
return iFilter.replace(ts.filter.regex.exact, '') == iExact;
|
427
|
-
}
|
428
|
-
return null;
|
429
|
-
},
|
430
|
-
// Look for a not match
|
431
|
-
notMatch: function( filter, iFilter, exact, iExact, cached, index, table, wo ) {
|
432
|
-
if ( /^\!/.test(iFilter) ) {
|
433
|
-
iFilter = iFilter.replace('!', '');
|
434
|
-
var indx = iExact.search( $.trim(iFilter) );
|
435
|
-
return iFilter === '' ? true : !(wo.filter_startsWith ? indx === 0 : indx >= 0);
|
436
|
-
}
|
437
|
-
return null;
|
438
|
-
},
|
439
431
|
// Look for operators >, >=, < or <=
|
440
432
|
operators: function( filter, iFilter, exact, iExact, cached, index, table, wo, parsed ) {
|
441
433
|
if ( /^[<>]=?/.test(iFilter) ) {
|
@@ -462,6 +454,24 @@ ts.filter = {
|
|
462
454
|
}
|
463
455
|
return null;
|
464
456
|
},
|
457
|
+
// Look for quotes or equals to get an exact match; ignore type since iExact could be numeric
|
458
|
+
exact: function( filter, iFilter, exact, iExact, cached, index, table, wo, parsed, rowArray ) {
|
459
|
+
/*jshint eqeqeq:false */
|
460
|
+
if (ts.filter.regex.exact.test(iFilter)) {
|
461
|
+
var fltr = iFilter.replace(ts.filter.regex.exact, '');
|
462
|
+
return rowArray ? $.inArray(fltr, rowArray) >= 0 : fltr == iExact;
|
463
|
+
}
|
464
|
+
return null;
|
465
|
+
},
|
466
|
+
// Look for a not match
|
467
|
+
notMatch: function( filter, iFilter, exact, iExact, cached, index, table, wo ) {
|
468
|
+
if ( /^\!/.test(iFilter) ) {
|
469
|
+
iFilter = iFilter.replace('!', '');
|
470
|
+
var indx = iExact.search( $.trim(iFilter) );
|
471
|
+
return iFilter === '' ? true : !(wo.filter_startsWith ? indx === 0 : indx >= 0);
|
472
|
+
}
|
473
|
+
return null;
|
474
|
+
},
|
465
475
|
// Look for an AND or && operator (logical and)
|
466
476
|
and : function( filter, iFilter, exact, iExact ) {
|
467
477
|
if ( /\s+(AND|&&)\s+/g.test(filter) ) {
|
@@ -500,13 +510,13 @@ ts.filter = {
|
|
500
510
|
return null;
|
501
511
|
},
|
502
512
|
// Look for wild card: ? = single, * = multiple, or | = logical OR
|
503
|
-
wild : function( filter, iFilter, exact, iExact, cached, index, table ) {
|
504
|
-
if ( /[\?|\*]/.test(iFilter) || /\s+OR\s
|
513
|
+
wild : function( filter, iFilter, exact, iExact, cached, index, table, wo, parsed, rowArray ) {
|
514
|
+
if ( /[\?|\*]/.test(iFilter) || /\s+OR\s+/i.test(filter) ) {
|
505
515
|
var c = table.config,
|
506
516
|
query = iFilter.replace(/\s+OR\s+/gi,"|");
|
507
517
|
// look for an exact match with the "or" unless the "filter-match" class is found
|
508
518
|
if (!c.$headers.filter('[data-column="' + index + '"]:last').hasClass('filter-match') && /\|/.test(query)) {
|
509
|
-
query = '^(' + query + ')$';
|
519
|
+
query = $.isArray(rowArray) ? '(' + query + ')' : '^(' + query + ')$';
|
510
520
|
}
|
511
521
|
return new RegExp( query.replace(/\?/g, '\\S{1}').replace(/\*/g, '\\S*') ).test(iExact);
|
512
522
|
}
|
@@ -549,7 +559,8 @@ ts.filter = {
|
|
549
559
|
}
|
550
560
|
|
551
561
|
c.$table.bind('addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search '.split(' ').join('.tsfilter '), function(event, filter) {
|
552
|
-
|
562
|
+
c.$table.find('.' + ts.css.filterRow).toggle( !(wo.filter_hideEmpty && $.isEmptyObject(c.cache)) ); // fixes #450
|
563
|
+
if ( !/(search|filter)/.test(event.type) ) {
|
553
564
|
event.stopPropagation();
|
554
565
|
ts.filter.buildDefault(table, true);
|
555
566
|
}
|
@@ -560,15 +571,26 @@ ts.filter = {
|
|
560
571
|
} else {
|
561
572
|
// send false argument to force a new search; otherwise if the filter hasn't changed, it will return
|
562
573
|
filter = event.type === 'search' ? filter : event.type === 'updateComplete' ? c.$table.data('lastSearch') : '';
|
563
|
-
|
574
|
+
if (/(update|add)/.test(event.type)) {
|
575
|
+
// force a new search since content has changed
|
576
|
+
c.lastCombinedFilter = null;
|
577
|
+
}
|
578
|
+
// pass true (dontSkip) to prevent the tablesorter.setFilters function from skipping the first input
|
579
|
+
// ensures all inputs are updated when a search is triggered on the table $('table').trigger('search', [...]);
|
580
|
+
ts.filter.searching(table, filter, true);
|
564
581
|
}
|
565
582
|
return false;
|
566
583
|
});
|
567
|
-
ts.filter.bindSearch( table, c.$table.find('input.
|
584
|
+
ts.filter.bindSearch( table, c.$table.find('input.' + ts.css.filter), true );
|
585
|
+
if (wo.filter_external) {
|
586
|
+
ts.filter.bindSearch( table, wo.filter_external );
|
587
|
+
}
|
568
588
|
|
569
589
|
// reset button/link
|
570
590
|
if (wo.filter_reset) {
|
571
|
-
$(document)
|
591
|
+
$(document)
|
592
|
+
.undelegate(wo.filter_reset, 'click.tsfilter')
|
593
|
+
.delegate(wo.filter_reset, 'click.tsfilter', function() {
|
572
594
|
// trigger a reset event, so other functions (filterFormatter) know when to reset
|
573
595
|
c.$table.trigger('filterReset');
|
574
596
|
});
|
@@ -590,7 +612,7 @@ ts.filter = {
|
|
590
612
|
options += '<option value="' + string + '">' + string + '</option>';
|
591
613
|
}
|
592
614
|
}
|
593
|
-
c.$table.find('thead').find('select.
|
615
|
+
c.$table.find('thead').find('select.' + ts.css.filter + '[data-column="' + column + '"]').append(options);
|
594
616
|
}
|
595
617
|
}
|
596
618
|
}
|
@@ -599,8 +621,8 @@ ts.filter = {
|
|
599
621
|
// it would append the same options twice.
|
600
622
|
ts.filter.buildDefault(table, true);
|
601
623
|
|
602
|
-
c.$table.find('select.
|
603
|
-
ts.filter.checkFilters(table, filter);
|
624
|
+
c.$table.find('select.' + ts.css.filter).bind('change search', function(event, filter) {
|
625
|
+
ts.filter.checkFilters(table, filter, true);
|
604
626
|
});
|
605
627
|
|
606
628
|
if (wo.filter_hideFilters) {
|
@@ -627,36 +649,31 @@ ts.filter = {
|
|
627
649
|
if (filters.length) {
|
628
650
|
ts.setFilters(table, filters, true);
|
629
651
|
}
|
652
|
+
c.$table.trigger('filterFomatterUpdate');
|
630
653
|
ts.filter.checkFilters(table, filters);
|
631
654
|
});
|
632
655
|
// filter widget initialized
|
633
|
-
wo.
|
656
|
+
wo.filter_initialized = true;
|
634
657
|
c.$table.trigger('filterInit');
|
635
658
|
},
|
636
659
|
setDefaults: function(table, c, wo) {
|
637
|
-
var
|
638
|
-
|
639
|
-
|
660
|
+
var isArray, saved,
|
661
|
+
// get current (default) filters
|
662
|
+
filters = ts.getFilters(table);
|
640
663
|
if (wo.filter_saveFilters && ts.storage) {
|
641
|
-
|
642
|
-
isArray = $.isArray(
|
643
|
-
// make sure we're not just
|
644
|
-
if (isArray &&
|
645
|
-
}
|
646
|
-
// if not filters saved, then check default settings
|
647
|
-
if (!filters.length) {
|
648
|
-
for (indx = 0; indx < columns; indx++) {
|
649
|
-
filters[indx] = c.$headers.filter('[data-column="' + indx + '"]:last').attr(wo.filter_defaultAttrib) || filters[indx];
|
650
|
-
}
|
664
|
+
saved = ts.storage( table, 'tablesorter-filters' ) || [];
|
665
|
+
isArray = $.isArray(saved);
|
666
|
+
// make sure we're not just getting an empty array
|
667
|
+
if ( !(isArray && saved.join('') === '' || !isArray) ) { filters = saved; }
|
651
668
|
}
|
652
|
-
|
669
|
+
c.$table.data('lastSearch', filters);
|
653
670
|
return filters;
|
654
671
|
},
|
655
672
|
buildRow: function(table, c, wo) {
|
656
|
-
var column, $header, buildSelect, disabled,
|
673
|
+
var column, $header, buildSelect, disabled, name,
|
657
674
|
// c.columns defined in computeThIndexes()
|
658
675
|
columns = c.columns,
|
659
|
-
buildFilter = '<tr class="
|
676
|
+
buildFilter = '<tr class="' + ts.css.filterRow + '">';
|
660
677
|
for (column = 0; column < columns; column++) {
|
661
678
|
buildFilter += '<td></td>';
|
662
679
|
}
|
@@ -699,19 +716,41 @@ ts.filter = {
|
|
699
716
|
}
|
700
717
|
}
|
701
718
|
if (buildFilter) {
|
702
|
-
|
719
|
+
// add filter class name
|
720
|
+
name = ( $.isArray(wo.filter_cssFilter) ?
|
721
|
+
(typeof wo.filter_cssFilter[column] !== 'undefined' ? wo.filter_cssFilter[column] || '' : '') :
|
722
|
+
wo.filter_cssFilter ) || '';
|
723
|
+
buildFilter.addClass( ts.css.filter + ' ' + name ).attr('data-column', column);
|
703
724
|
if (disabled) {
|
704
725
|
buildFilter.addClass('disabled')[0].disabled = true; // disabled!
|
705
726
|
}
|
706
727
|
}
|
707
728
|
}
|
708
729
|
},
|
709
|
-
bindSearch: function(table, $el) {
|
730
|
+
bindSearch: function(table, $el, internal) {
|
710
731
|
table = $(table)[0];
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
732
|
+
$el = $($el); // allow passing a selector string
|
733
|
+
if (!$el.length) { return; }
|
734
|
+
var c = table.config,
|
735
|
+
wo = c.widgetOptions,
|
736
|
+
$ext = wo.filter_$externalFilters;
|
737
|
+
if (internal !== true) {
|
738
|
+
// save anyMatch element
|
739
|
+
wo.filter_$anyMatch = $el.filter('[data-column="all"]');
|
740
|
+
if ($ext && $ext.length) {
|
741
|
+
wo.filter_$externalFilters = wo.filter_$externalFilters.add( $el );
|
742
|
+
} else {
|
743
|
+
wo.filter_$externalFilters = $el;
|
744
|
+
}
|
745
|
+
// update values (external filters added after table initialization)
|
746
|
+
ts.setFilters(table, c.$table.data('lastSearch') || [], internal === false);
|
747
|
+
}
|
748
|
+
$el
|
749
|
+
.data('lastSearchTime', new Date().getTime())
|
750
|
+
.unbind('keyup search change')
|
751
|
+
// include change for select - fixes #473
|
752
|
+
.bind('keyup search change', function(event, filters) {
|
753
|
+
$(this).data('lastSearchTime', new Date().getTime());
|
715
754
|
// emulate what webkit does.... escape clears the filter
|
716
755
|
if (event.which === 27) {
|
717
756
|
this.value = '';
|
@@ -721,23 +760,14 @@ ts.filter = {
|
|
721
760
|
( event.which >= 37 && event.which <= 40 ) || (event.which !== 13 && wo.filter_liveSearch === false) ) ) ) {
|
722
761
|
return;
|
723
762
|
}
|
724
|
-
//
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
external = [];
|
729
|
-
$el.each(function(){
|
730
|
-
// target the appropriate column if the external input has a data-column attribute
|
731
|
-
external[ $(this).data('column') || 0 ] = $(this).val();
|
732
|
-
});
|
733
|
-
}
|
734
|
-
ts.filter.searching(table, filter, external);
|
735
|
-
})
|
736
|
-
.bind('filterReset', function(){
|
763
|
+
// true flag in getFilters forces obtaining the latest values
|
764
|
+
ts.filter.searching( table, filters || ts.getFilters( table, true ), true );
|
765
|
+
});
|
766
|
+
c.$table.bind('filterReset', function(){
|
737
767
|
$el.val('');
|
738
768
|
});
|
739
769
|
},
|
740
|
-
checkFilters: function(table, filter) {
|
770
|
+
checkFilters: function(table, filter, dontSkip) {
|
741
771
|
var c = table.config,
|
742
772
|
wo = c.widgetOptions,
|
743
773
|
filterArray = $.isArray(filter),
|
@@ -745,11 +775,11 @@ ts.filter = {
|
|
745
775
|
combinedFilters = (filters || []).join(''); // combined filter values
|
746
776
|
// add filter array back into inputs
|
747
777
|
if (filterArray) {
|
748
|
-
ts.setFilters( table, filters );
|
778
|
+
ts.setFilters( table, filters, false, dontSkip !== true );
|
749
779
|
}
|
750
780
|
if (wo.filter_hideFilters) {
|
751
781
|
// show/hide filter row as needed
|
752
|
-
c.$table.find('.
|
782
|
+
c.$table.find('.' + ts.css.filterRow).trigger( combinedFilters === '' ? 'mouseleave' : 'mouseenter' );
|
753
783
|
}
|
754
784
|
// return if the last search is the same; but filter === false when updating the search
|
755
785
|
// see example-widget-filter.html filter toggle buttons
|
@@ -774,7 +804,7 @@ ts.filter = {
|
|
774
804
|
hideFilters: function(table, c) {
|
775
805
|
var $filterRow, $filterRow2, timer;
|
776
806
|
c.$table
|
777
|
-
.find('.
|
807
|
+
.find('.' + ts.css.filterRow)
|
778
808
|
.addClass('hideme')
|
779
809
|
.bind('mouseenter mouseleave', function(e) {
|
780
810
|
// save event object - http://bugs.jquery.com/ticket/12140
|
@@ -810,9 +840,10 @@ ts.filter = {
|
|
810
840
|
},
|
811
841
|
findRows: function(table, filters, combinedFilters) {
|
812
842
|
if (table.config.lastCombinedFilter === combinedFilters) { return; }
|
813
|
-
var cached, len, $rows, rowIndex, tbodyIndex, $tbody, $cells, columnIndex,
|
843
|
+
var cached, len, $rows, cacheIndex, rowIndex, tbodyIndex, $tbody, $cells, columnIndex,
|
814
844
|
childRow, childRowText, exact, iExact, iFilter, lastSearch, matches, result,
|
815
845
|
searchFiltered, filterMatched, showRow, time,
|
846
|
+
anyMatch, iAnyMatch, rowArray, rowText, iRowText, rowCache,
|
816
847
|
c = table.config,
|
817
848
|
wo = c.widgetOptions,
|
818
849
|
columns = c.columns,
|
@@ -821,19 +852,19 @@ ts.filter = {
|
|
821
852
|
anyMatchNotAllowedTypes = [ 'range', 'notMatch', 'operators' ],
|
822
853
|
// parse columns after formatter, in case the class is added at that point
|
823
854
|
parsed = c.$headers.map(function(columnIndex) {
|
824
|
-
return (ts.getData
|
855
|
+
return c.parsers[columnIndex].parsed || ( ts.getData ?
|
825
856
|
ts.getData(c.$headers.filter('[data-column="' + columnIndex + '"]:last'), c.headers[columnIndex], 'filter') === 'parsed' :
|
826
|
-
$(this).hasClass('filter-parsed');
|
857
|
+
$(this).hasClass('filter-parsed') );
|
827
858
|
}).get();
|
828
859
|
if (c.debug) { time = new Date(); }
|
829
860
|
for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) {
|
830
861
|
if ($tbodies.eq(tbodyIndex).hasClass(ts.css.info)) { continue; } // ignore info blocks, issue #264
|
831
862
|
$tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true);
|
832
863
|
// skip child rows & widget added (removable) rows - fixes #448 thanks to @hempel!
|
833
|
-
$rows = $tbody.children('tr').not(
|
864
|
+
$rows = $tbody.children('tr').not(c.selectorRemove);
|
834
865
|
len = $rows.length;
|
835
866
|
if (combinedFilters === '' || wo.filter_serversideFiltering) {
|
836
|
-
$tbody.children().show().removeClass(wo.filter_filteredRow);
|
867
|
+
$tbody.children().not('.' + c.cssChildRow).show().removeClass(wo.filter_filteredRow);
|
837
868
|
} else {
|
838
869
|
// optimize searching only through already filtered rows - see #313
|
839
870
|
searchFiltered = true;
|
@@ -844,7 +875,17 @@ ts.filter = {
|
|
844
875
|
});
|
845
876
|
// can't search when all rows are hidden - this happens when looking for exact matches
|
846
877
|
if (searchFiltered && $rows.filter(':visible').length === 0) { searchFiltered = false; }
|
878
|
+
if ((wo.filter_$anyMatch && wo.filter_$anyMatch.length) || filters[c.columns]) {
|
879
|
+
anyMatch = wo.filter_$anyMatch && wo.filter_$anyMatch.val() || filters[c.columns] || '';
|
880
|
+
if (c.sortLocaleCompare) {
|
881
|
+
// replace accents
|
882
|
+
anyMatch = ts.replaceAccents(anyMatch);
|
883
|
+
}
|
884
|
+
iAnyMatch = anyMatch.toLowerCase();
|
885
|
+
}
|
886
|
+
|
847
887
|
// loop through the rows
|
888
|
+
cacheIndex = 0;
|
848
889
|
for (rowIndex = 0; rowIndex < len; rowIndex++) {
|
849
890
|
childRow = $rows[rowIndex].className;
|
850
891
|
// skip child rows & already filtered rows
|
@@ -858,10 +899,44 @@ ts.filter = {
|
|
858
899
|
childRowText = (childRow.length && wo.filter_childRows) ? childRow.text() : '';
|
859
900
|
childRowText = wo.filter_ignoreCase ? childRowText.toLocaleLowerCase() : childRowText;
|
860
901
|
$cells = $rows.eq(rowIndex).children('td');
|
902
|
+
|
903
|
+
if (anyMatch) {
|
904
|
+
rowArray = $cells.map(function(i){
|
905
|
+
var txt;
|
906
|
+
if (parsed[i]) {
|
907
|
+
txt = c.cache[tbodyIndex].normalized[cacheIndex][i];
|
908
|
+
} else {
|
909
|
+
txt = wo.filter_ignoreCase ? $(this).text().toLowerCase() : $(this).text();
|
910
|
+
if (c.sortLocaleCompare) {
|
911
|
+
txt = ts.replaceAccents(txt);
|
912
|
+
}
|
913
|
+
}
|
914
|
+
return txt;
|
915
|
+
}).get();
|
916
|
+
rowText = rowArray.join(' ');
|
917
|
+
iRowText = rowText.toLowerCase();
|
918
|
+
rowCache = c.cache[tbodyIndex].normalized[cacheIndex].join(' ');
|
919
|
+
filterMatched = null;
|
920
|
+
$.each(ts.filter.types, function(type, typeFunction) {
|
921
|
+
if ($.inArray(type, anyMatchNotAllowedTypes) < 0) {
|
922
|
+
matches = typeFunction( anyMatch, iAnyMatch, rowText, iRowText, rowCache, columns, table, wo, parsed, rowArray );
|
923
|
+
if (matches !== null) {
|
924
|
+
filterMatched = matches;
|
925
|
+
return false;
|
926
|
+
}
|
927
|
+
}
|
928
|
+
});
|
929
|
+
if (filterMatched !== null) {
|
930
|
+
showRow = filterMatched;
|
931
|
+
} else {
|
932
|
+
showRow = (iRowText + childRowText).indexOf(iAnyMatch) >= 0;
|
933
|
+
}
|
934
|
+
}
|
935
|
+
|
861
936
|
for (columnIndex = 0; columnIndex < columns; columnIndex++) {
|
862
937
|
// ignore if filter is empty or disabled
|
863
|
-
if (filters[columnIndex]
|
864
|
-
cached = c.cache[tbodyIndex].normalized[
|
938
|
+
if (filters[columnIndex]) {
|
939
|
+
cached = c.cache[tbodyIndex].normalized[cacheIndex][columnIndex];
|
865
940
|
// check if column data should be from the cell or from parsed data
|
866
941
|
if (wo.filter_useParsedData || parsed[columnIndex]) {
|
867
942
|
exact = cached;
|
@@ -873,14 +948,10 @@ ts.filter = {
|
|
873
948
|
iExact = !ts.filter.regex.type.test(typeof exact) && wo.filter_ignoreCase ? exact.toLocaleLowerCase() : exact;
|
874
949
|
result = showRow; // if showRow is true, show that row
|
875
950
|
|
876
|
-
if (typeof filters[columnIndex] === "undefined" || filters[columnIndex] === null) {
|
877
|
-
filters[columnIndex] = wo.filter_anyMatch ? combinedFilters : filters[columnIndex];
|
878
|
-
}
|
879
|
-
|
880
951
|
// replace accents - see #357
|
881
952
|
filters[columnIndex] = c.sortLocaleCompare ? ts.replaceAccents(filters[columnIndex]) : filters[columnIndex];
|
882
953
|
// val = case insensitive, filters[columnIndex] = case sensitive
|
883
|
-
iFilter = wo.filter_ignoreCase ? filters[columnIndex].toLocaleLowerCase() : filters[columnIndex];
|
954
|
+
iFilter = wo.filter_ignoreCase ? (filters[columnIndex] || '').toLocaleLowerCase() : filters[columnIndex];
|
884
955
|
if (wo.filter_functions && wo.filter_functions[columnIndex]) {
|
885
956
|
if (wo.filter_functions[columnIndex] === true) {
|
886
957
|
// default selector; no "filter-select" class
|
@@ -898,12 +969,10 @@ ts.filter = {
|
|
898
969
|
// cycle through the different filters
|
899
970
|
// filters return a boolean or null if nothing matches
|
900
971
|
$.each(ts.filter.types, function(type, typeFunction) {
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
return false;
|
906
|
-
}
|
972
|
+
matches = typeFunction( filters[columnIndex], iFilter, exact, iExact, cached, columnIndex, table, wo, parsed );
|
973
|
+
if (matches !== null) {
|
974
|
+
filterMatched = matches;
|
975
|
+
return false;
|
907
976
|
}
|
908
977
|
});
|
909
978
|
if (filterMatched !== null) {
|
@@ -914,14 +983,7 @@ ts.filter = {
|
|
914
983
|
result = ( (!wo.filter_startsWith && exact >= 0) || (wo.filter_startsWith && exact === 0) );
|
915
984
|
}
|
916
985
|
}
|
917
|
-
|
918
|
-
showRow = result;
|
919
|
-
if (showRow){
|
920
|
-
break;
|
921
|
-
}
|
922
|
-
} else {
|
923
|
-
showRow = (result) ? showRow : false;
|
924
|
-
}
|
986
|
+
showRow = (result) ? showRow : false;
|
925
987
|
}
|
926
988
|
}
|
927
989
|
$rows[rowIndex].style.display = (showRow ? '' : 'none');
|
@@ -932,6 +994,7 @@ ts.filter = {
|
|
932
994
|
}
|
933
995
|
childRow.toggle(showRow);
|
934
996
|
}
|
997
|
+
cacheIndex++;
|
935
998
|
}
|
936
999
|
}
|
937
1000
|
ts.processTbody(table, $tbody, false);
|
@@ -984,7 +1047,7 @@ ts.filter = {
|
|
984
1047
|
arry = (ts.sortNatural) ? arry.sort(function(a, b) { return ts.sortNatural(a, b); }) : arry.sort(true);
|
985
1048
|
|
986
1049
|
// Get curent filter value
|
987
|
-
currentValue = c.$table.find('thead').find('select.
|
1050
|
+
currentValue = c.$table.find('thead').find('select.' + ts.css.filter + '[data-column="' + column + '"]').val();
|
988
1051
|
|
989
1052
|
// build option list
|
990
1053
|
for (indx = 0; indx < arry.length; indx++) {
|
@@ -993,7 +1056,7 @@ ts.filter = {
|
|
993
1056
|
options += arry[indx] !== '' ? '<option value="' + txt + '"' + (currentValue === txt ? ' selected="selected"' : '') +
|
994
1057
|
'>' + arry[indx] + '</option>' : '';
|
995
1058
|
}
|
996
|
-
c.$table.find('thead').find('select.
|
1059
|
+
c.$table.find('thead').find('select.' + ts.css.filter + '[data-column="' + column + '"]')[ updating ? 'html' : 'append' ](options);
|
997
1060
|
},
|
998
1061
|
buildDefault: function(table, updating) {
|
999
1062
|
var columnIndex, $header,
|
@@ -1012,39 +1075,75 @@ ts.filter = {
|
|
1012
1075
|
}
|
1013
1076
|
}
|
1014
1077
|
},
|
1015
|
-
searching: function(table, filter,
|
1016
|
-
if (typeof filter === 'undefined' || filter === true
|
1078
|
+
searching: function(table, filter, dontSkip) {
|
1079
|
+
if (typeof filter === 'undefined' || filter === true) {
|
1017
1080
|
var wo = table.config.widgetOptions;
|
1018
1081
|
// delay filtering
|
1019
1082
|
clearTimeout(wo.searchTimer);
|
1020
1083
|
wo.searchTimer = setTimeout(function() {
|
1021
|
-
ts.filter.checkFilters(table,
|
1084
|
+
ts.filter.checkFilters(table, filter, dontSkip );
|
1022
1085
|
}, wo.filter_liveSearch ? wo.filter_searchDelay : 10);
|
1023
1086
|
} else {
|
1024
1087
|
// skip delay
|
1025
|
-
ts.filter.checkFilters(table, filter);
|
1088
|
+
ts.filter.checkFilters(table, filter, dontSkip);
|
1026
1089
|
}
|
1027
1090
|
}
|
1028
1091
|
};
|
1029
1092
|
|
1030
|
-
ts.getFilters = function(table) {
|
1031
|
-
var
|
1032
|
-
|
1033
|
-
|
1093
|
+
ts.getFilters = function(table, getRaw, setFilters, skipFirst) {
|
1094
|
+
var i, $filters, $column,
|
1095
|
+
filters = false,
|
1096
|
+
c = table ? $(table)[0].config : '',
|
1097
|
+
wo = table ? c.widgetOptions : '';
|
1098
|
+
if (getRaw !== true && wo && !wo.filter_columnFilters) {
|
1034
1099
|
return $(table).data('lastSearch');
|
1035
1100
|
}
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1101
|
+
if (c) {
|
1102
|
+
if (c.$filters) {
|
1103
|
+
$filters = c.$filters.find('.' + ts.css.filter);
|
1104
|
+
}
|
1105
|
+
if (wo.filter_$externalFilters) {
|
1106
|
+
$filters = $filters && $filters.length ? $filters.add(wo.filter_$externalFilters) : wo.filter_$externalFilters;
|
1107
|
+
}
|
1108
|
+
if ($filters && $filters.length) {
|
1109
|
+
filters = setFilters || [];
|
1110
|
+
for (i = 0; i < c.columns + 1; i++) {
|
1111
|
+
$column = $filters.filter('[data-column="' + (i === c.columns ? 'all' : i) + '"]');
|
1112
|
+
if ($column.length) {
|
1113
|
+
// move the latest search to the first slot in the array
|
1114
|
+
$column = $column.sort(function(a, b){
|
1115
|
+
return $(a).data('lastSearchTime') <= $(b).data('lastSearchTime');
|
1116
|
+
});
|
1117
|
+
if ($.isArray(setFilters)) {
|
1118
|
+
// skip first (latest input) to maintain cursor position while typing
|
1119
|
+
(skipFirst ? $column.slice(1) : $column).val( setFilters[i] ).trigger('change.tsfilter');
|
1120
|
+
} else {
|
1121
|
+
filters[i] = $column.val() || '';
|
1122
|
+
// don't change the first... it will move the cursor
|
1123
|
+
$column.slice(1).val( filters[i] );
|
1124
|
+
}
|
1125
|
+
// save any match input dynamically
|
1126
|
+
if (i === c.columns && $column.length) {
|
1127
|
+
wo.filter_$anyMatch = $column;
|
1128
|
+
}
|
1129
|
+
}
|
1130
|
+
}
|
1131
|
+
}
|
1132
|
+
}
|
1133
|
+
if (filters.length === 0) {
|
1134
|
+
filters = false;
|
1135
|
+
}
|
1136
|
+
return filters;
|
1039
1137
|
};
|
1040
1138
|
|
1041
|
-
ts.setFilters = function(table, filter, apply) {
|
1042
|
-
var
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1139
|
+
ts.setFilters = function(table, filter, apply, skipFirst) {
|
1140
|
+
var c = table ? $(table)[0].config : '',
|
1141
|
+
valid = ts.getFilters(table, true, filter, skipFirst);
|
1142
|
+
if (c && apply) {
|
1143
|
+
// ensure new set filters are applied, even if the search is the same
|
1144
|
+
c.lastCombinedFilter = null;
|
1145
|
+
c.$table.trigger('search', [filter, false]).trigger('filterFomatterUpdate');
|
1146
|
+
}
|
1048
1147
|
return !!valid;
|
1049
1148
|
};
|
1050
1149
|
|
@@ -1076,9 +1175,8 @@ ts.addWidget({
|
|
1076
1175
|
$thead = $table.children('thead:first'),
|
1077
1176
|
$win = $attach.length ? $attach : $(window),
|
1078
1177
|
$header = $thead.children('tr').not('.sticky-false').children(),
|
1079
|
-
innerHeader = '.
|
1178
|
+
innerHeader = '.' + ts.css.headerIn,
|
1080
1179
|
$tfoot = $table.find('tfoot'),
|
1081
|
-
filterInputs = '.tablesorter-filter',
|
1082
1180
|
$stickyOffset = isNaN(wo.stickyHeaders_offset) ? $(wo.stickyHeaders_offset) : '',
|
1083
1181
|
stickyOffset = $attach.length ? 0 : $stickyOffset.length ?
|
1084
1182
|
$stickyOffset.height() || 0 : parseInt(wo.stickyHeaders_offset, 10) || 0,
|
@@ -1092,7 +1190,7 @@ ts.addWidget({
|
|
1092
1190
|
visibility : 'hidden',
|
1093
1191
|
zIndex : wo.stickyHeaders_zIndex ? wo.stickyHeaders_zIndex : 2
|
1094
1192
|
}),
|
1095
|
-
$stickyThead = $stickyTable.children('thead:first').addClass('
|
1193
|
+
$stickyThead = $stickyTable.children('thead:first').addClass(ts.css.sticky + ' ' + wo.stickyHeaders),
|
1096
1194
|
$stickyCells,
|
1097
1195
|
laststate = '',
|
1098
1196
|
spacing = 0,
|
@@ -1108,8 +1206,8 @@ ts.addWidget({
|
|
1108
1206
|
spacing = parseInt($header.eq(0).css('border-left-width'), 10) * 2;
|
1109
1207
|
}
|
1110
1208
|
$stickyTable.css({
|
1111
|
-
left : $attach.length ? parseInt($attach.css('padding-left'), 10) +
|
1112
|
-
parseInt(
|
1209
|
+
left : $attach.length ? (parseInt($attach.css('padding-left'), 10) || 0) + parseInt(c.$table.css('padding-left'), 10) +
|
1210
|
+
parseInt(c.$table.css('margin-left'), 10) + parseInt($table.css('border-left-width'), 10) :
|
1113
1211
|
$thead.offset().left - $win.scrollLeft() - spacing,
|
1114
1212
|
width: $table.width()
|
1115
1213
|
});
|
@@ -1139,7 +1237,7 @@ ts.addWidget({
|
|
1139
1237
|
$stickyCells = $stickyThead.children().children();
|
1140
1238
|
$stickyTable.css({ height:0, width:0, padding:0, margin:0, border:0 });
|
1141
1239
|
// remove resizable block
|
1142
|
-
$stickyCells.find('.
|
1240
|
+
$stickyCells.find('.' + ts.css.resizer).remove();
|
1143
1241
|
// update sticky header class names to match real header after sorting
|
1144
1242
|
$table
|
1145
1243
|
.addClass('hasStickyHeaders')
|
@@ -1159,27 +1257,11 @@ ts.addWidget({
|
|
1159
1257
|
.bind('pagerComplete.tsSticky', function() {
|
1160
1258
|
resizeHeader();
|
1161
1259
|
});
|
1162
|
-
|
1163
|
-
|
1164
|
-
|
1165
|
-
// clicking on sticky will trigger sort
|
1166
|
-
$cell = $stickyThead.children('tr.tablesorter-headerRow').children().eq(indx).bind('mouseup', function(event) {
|
1167
|
-
$header.trigger(event, true); // external mouseup flag (click timer is ignored)
|
1168
|
-
});
|
1169
|
-
// prevent sticky header text selection
|
1170
|
-
if (c.cancelSelection) {
|
1171
|
-
$cell
|
1172
|
-
.attr('unselectable', 'on')
|
1173
|
-
.bind('selectstart', false)
|
1174
|
-
.css({
|
1175
|
-
'user-select': 'none',
|
1176
|
-
'MozUserSelect': 'none'
|
1177
|
-
});
|
1178
|
-
}
|
1179
|
-
});
|
1260
|
+
|
1261
|
+
ts.bindEvents(table, $stickyThead.children().children('.tablesorter-header'));
|
1262
|
+
|
1180
1263
|
// add stickyheaders AFTER the table. If the table is selected by ID, the original one (first) will be returned.
|
1181
1264
|
$table.after( $stickyTable );
|
1182
|
-
|
1183
1265
|
// make it sticky!
|
1184
1266
|
$win.bind('scroll.tsSticky resize.tsSticky', function(event) {
|
1185
1267
|
if (!$table.is(':visible')) { return; } // fixes #278
|
@@ -1212,17 +1294,22 @@ ts.addWidget({
|
|
1212
1294
|
|
1213
1295
|
// look for filter widget
|
1214
1296
|
if ($table.hasClass('hasFilters')) {
|
1297
|
+
// scroll table into view after filtering, if sticky header is active - #482
|
1215
1298
|
$table.bind('filterEnd', function() {
|
1216
1299
|
// $(':focus') needs jQuery 1.6+
|
1217
|
-
|
1218
|
-
|
1219
|
-
|
1220
|
-
|
1221
|
-
|
1300
|
+
var $td = $(document.activeElement).closest('td'),
|
1301
|
+
column = $td.parent().children().index($td);
|
1302
|
+
// only scroll if sticky header is active
|
1303
|
+
if ($stickyTable.hasClass(ts.css.stickyVis)) {
|
1304
|
+
// scroll to original table (not sticky clone)
|
1305
|
+
window.scrollTo(0, $table.position().top);
|
1306
|
+
// give same input/select focus
|
1307
|
+
if (column >= 0) {
|
1308
|
+
c.$filters.eq(column).find('a, select, input').filter(':visible').focus();
|
1309
|
+
}
|
1222
1310
|
}
|
1223
1311
|
});
|
1224
|
-
|
1225
|
-
ts.filter.bindSearch( $table, $stickyCells.find('.tablesorter-filter').addClass('tablesorter-external-filter') );
|
1312
|
+
ts.filter.bindSearch( $table, $stickyCells.find('.' + ts.css.filter) );
|
1226
1313
|
}
|
1227
1314
|
|
1228
1315
|
$table.trigger('stickyHeadersInit');
|
@@ -1232,7 +1319,7 @@ ts.addWidget({
|
|
1232
1319
|
c.$table
|
1233
1320
|
.removeClass('hasStickyHeaders')
|
1234
1321
|
.unbind('sortEnd.tsSticky pagerComplete.tsSticky')
|
1235
|
-
.find('.
|
1322
|
+
.find('.' + ts.css.sticky).remove();
|
1236
1323
|
if (wo.$sticky && wo.$sticky.length) { wo.$sticky.remove(); } // remove cloned table
|
1237
1324
|
// don't unbind if any table on the page still has stickyheaders applied
|
1238
1325
|
if (!$('.hasStickyHeaders').length) {
|
@@ -1264,7 +1351,8 @@ ts.addWidget({
|
|
1264
1351
|
$next = null,
|
1265
1352
|
fullWidth = Math.abs($table.parent().width() - $table.width()) < 20,
|
1266
1353
|
stopResize = function() {
|
1267
|
-
if (ts.storage && $target) {
|
1354
|
+
if (ts.storage && $target && $next) {
|
1355
|
+
storedSizes = {};
|
1268
1356
|
storedSizes[$target.index()] = $target.width();
|
1269
1357
|
storedSizes[$next.index()] = $next.width();
|
1270
1358
|
$target.width( storedSizes[$target.index()] );
|
@@ -1298,9 +1386,9 @@ ts.addWidget({
|
|
1298
1386
|
// add wrapper inside each cell to allow for positioning of the resizable target block
|
1299
1387
|
$rows.each(function() {
|
1300
1388
|
$column = $(this).children().not('.resizable-false');
|
1301
|
-
if (!$(this).find('.
|
1389
|
+
if (!$(this).find('.' + ts.css.wrapper).length) {
|
1302
1390
|
// Firefox needs this inner div to position the resizer correctly
|
1303
|
-
$column.wrapInner('<div class="
|
1391
|
+
$column.wrapInner('<div class="' + ts.css.wrapper + '" style="position:relative;height:100%;width:100%"></div>');
|
1304
1392
|
}
|
1305
1393
|
// don't include the last column of the row
|
1306
1394
|
if (!wo.resizable_addLastColumn) { $column = $column.slice(0,-1); }
|
@@ -1311,8 +1399,8 @@ ts.addWidget({
|
|
1311
1399
|
var $column = $(this),
|
1312
1400
|
padding = parseInt($column.css('padding-right'), 10) + 10; // 10 is 1/2 of the 20px wide resizer grip
|
1313
1401
|
$column
|
1314
|
-
.find('.
|
1315
|
-
.append('<div class="
|
1402
|
+
.find('.' + ts.css.wrapper)
|
1403
|
+
.append('<div class="' + ts.css.resizer + '" style="cursor:w-resize;position:absolute;z-index:1;right:-' +
|
1316
1404
|
padding + 'px;top:0;height:100%;width:20px;"></div>');
|
1317
1405
|
})
|
1318
1406
|
.bind('mousemove.tsresize', function(event) {
|
@@ -1330,7 +1418,7 @@ ts.addWidget({
|
|
1330
1418
|
.bind('mouseup.tsresize', function() {
|
1331
1419
|
stopResize();
|
1332
1420
|
})
|
1333
|
-
.find('.
|
1421
|
+
.find('.' + ts.css.resizer + ',.' + ts.css.grip)
|
1334
1422
|
.bind('mousedown', function(event) {
|
1335
1423
|
// save header cell and mouse position; closest() not supported by jQuery v1.2.6
|
1336
1424
|
$target = $(event.target).closest('th');
|
@@ -1361,13 +1449,15 @@ ts.addWidget({
|
|
1361
1449
|
.children('tr').children()
|
1362
1450
|
.unbind('mousemove.tsresize mouseup.tsresize')
|
1363
1451
|
// don't remove "tablesorter-wrapper" as uitheme uses it too
|
1364
|
-
.find('.
|
1452
|
+
.find('.' + ts.css.resizer + ',.' + ts.css.grip).remove();
|
1365
1453
|
ts.resizableReset(table);
|
1366
1454
|
}
|
1367
1455
|
});
|
1368
1456
|
ts.resizableReset = function(table) {
|
1369
|
-
table
|
1370
|
-
|
1457
|
+
$(table).each(function(){
|
1458
|
+
this.config.$headers.not('.resizable-false').css('width','');
|
1459
|
+
if (ts.storage) { ts.storage(this, 'tablesorter-resizable', {}); }
|
1460
|
+
});
|
1371
1461
|
};
|
1372
1462
|
|
1373
1463
|
// Save table sort widget
|