jquery-tablesorter 1.14.1 → 1.15.0
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.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/lib/jquery-tablesorter/version.rb +1 -1
- data/vendor/assets/javascripts/jquery-tablesorter.js +1 -1
- data/vendor/assets/javascripts/jquery-tablesorter/addons/pager/jquery.tablesorter.pager.js +5 -9
- data/vendor/assets/javascripts/jquery-tablesorter/extras/jquery.dragtable.mod.js +1 -4
- data/vendor/assets/javascripts/jquery-tablesorter/{jquery.metadata.js → extras/jquery.metadata.js} +1 -0
- data/vendor/assets/javascripts/jquery-tablesorter/extras/jquery.quicksearch.js +8 -4
- data/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.js +116 -107
- data/vendor/assets/javascripts/jquery-tablesorter/jquery.tablesorter.widgets.js +232 -171
- data/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-file-type.js +1 -1
- data/vendor/assets/javascripts/jquery-tablesorter/parsers/parser-input-select.js +1 -1
- data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-chart.js +2 -3
- data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-columns.js +78 -0
- data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-cssStickyHeaders.js +14 -12
- data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-editable.js +4 -4
- data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter-formatter-html5.js +429 -0
- data/vendor/assets/javascripts/jquery-tablesorter/{jquery.tablesorter.widgets-filter-formatter.js → widgets/widget-filter-formatter-jui.js} +1 -381
- data/vendor/assets/javascripts/jquery-tablesorter/{jquery.tablesorter.widgets-filter-formatter-select2.js → widgets/widget-filter-formatter-select2.js} +0 -0
- data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-filter.js +1307 -0
- data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-formatter.js +3 -3
- data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-grouping.js +4 -4
- data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-math.js +3 -3
- data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-pager.js +4 -8
- data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-resizable.js +176 -0
- data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-saveSort.js +68 -0
- data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-staticRow.js +3 -3
- data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-stickyHeaders.js +269 -0
- data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-storage.js +76 -0
- data/vendor/assets/javascripts/jquery-tablesorter/widgets/widget-uitheme.js +184 -0
- data/vendor/assets/stylesheets/jquery-tablesorter/theme.black-ice.css +1 -1
- data/vendor/assets/stylesheets/jquery-tablesorter/theme.blue.css +1 -1
- data/vendor/assets/stylesheets/jquery-tablesorter/theme.dark.css +1 -1
- data/vendor/assets/stylesheets/jquery-tablesorter/theme.default.css +1 -1
- data/vendor/assets/stylesheets/jquery-tablesorter/theme.dropbox.css +1 -1
- data/vendor/assets/stylesheets/jquery-tablesorter/theme.green.css +1 -1
- data/vendor/assets/stylesheets/jquery-tablesorter/theme.grey.css +1 -1
- data/vendor/assets/stylesheets/jquery-tablesorter/theme.ice.css +1 -1
- data/vendor/assets/stylesheets/jquery-tablesorter/theme.jui.css +1 -1
- data/vendor/assets/stylesheets/jquery-tablesorter/theme.metro-dark.css +1 -1
- metadata +15 -7
@@ -1,4 +1,4 @@
|
|
1
|
-
/*! Filter widget formatter functions
|
1
|
+
/*! Filter widget formatter jQuery UI functions *//* updated 7/17/2014 (v2.17.5)
|
2
2
|
* requires: tableSorter (FORK) 2.15+ and jQuery 1.4.3+
|
3
3
|
*
|
4
4
|
* uiSpinner (jQuery UI spinner)
|
@@ -6,9 +6,6 @@
|
|
6
6
|
* uiRange (jQuery UI range slider)
|
7
7
|
* uiDateCompare (jQuery UI datepicker; 1 input)
|
8
8
|
* uiDatepicker (jQuery UI datepicker; 2 inputs, filter range)
|
9
|
-
* html5Number (spinner)
|
10
|
-
* html5Range (slider)
|
11
|
-
* html5Color (color)
|
12
9
|
*/
|
13
10
|
/*jshint browser:true, jquery:true, unused:false */
|
14
11
|
/*global jQuery: false */
|
@@ -761,383 +758,6 @@ tsff = ts.filterFormatter = {
|
|
761
758
|
|
762
759
|
// return the hidden input so the filter widget has a reference to it
|
763
760
|
return $input.val( o.from ? ( o.to ? o.from + ' - ' + o.to : '>=' + o.from ) : (o.to ? '<=' + o.to : '') );
|
764
|
-
},
|
765
|
-
|
766
|
-
/**********************\
|
767
|
-
HTML5 Number (spinner)
|
768
|
-
\**********************/
|
769
|
-
html5Number : function($cell, indx, def5Num) {
|
770
|
-
var t, o = $.extend({
|
771
|
-
value : 0,
|
772
|
-
min : 0,
|
773
|
-
max : 100,
|
774
|
-
step : 1,
|
775
|
-
delayed : true,
|
776
|
-
disabled : false,
|
777
|
-
addToggle : false,
|
778
|
-
exactMatch : false,
|
779
|
-
cellText : '',
|
780
|
-
compare : '',
|
781
|
-
skipTest: false
|
782
|
-
}, def5Num),
|
783
|
-
|
784
|
-
$input,
|
785
|
-
// test browser for HTML5 range support
|
786
|
-
$number = $('<input type="number" style="visibility:hidden;" value="test">').appendTo($cell),
|
787
|
-
// test if HTML5 number is supported - from Modernizr
|
788
|
-
numberSupported = o.skipTest || $number.attr('type') === 'number' && $number.val() !== 'test',
|
789
|
-
$shcell = [],
|
790
|
-
c = $cell.closest('table')[0].config,
|
791
|
-
|
792
|
-
updateNumber = function(delayed, notrigger){
|
793
|
-
var chkd = o.addToggle ? $cell.find('.toggle').is(':checked') : true,
|
794
|
-
v = $cell.find('.number').val(),
|
795
|
-
compare = ($.isArray(o.compare) ? $cell.find(compareSelect).val() || o.compare[ o.selected || 0] : o.compare) || '',
|
796
|
-
searchType = c.$table[0].hasInitialized ? (delayed ? delayed : o.delayed) || '' : true;
|
797
|
-
$input
|
798
|
-
// add equal to the beginning, so we filter exact numbers
|
799
|
-
.val( !o.addToggle || chkd ? (compare ? compare : o.exactMatch ? '=' : '') + v : '' )
|
800
|
-
.trigger( notrigger ? '' : 'search', searchType ).end()
|
801
|
-
.find('.number').val(v);
|
802
|
-
if ($cell.find('.number').length) {
|
803
|
-
$cell.find('.number')[0].disabled = (o.disabled || !chkd);
|
804
|
-
}
|
805
|
-
// update sticky header cell
|
806
|
-
if ($shcell.length) {
|
807
|
-
$shcell.find('.number').val(v)[0].disabled = (o.disabled || !chkd);
|
808
|
-
$shcell.find(compareSelect).val(compare);
|
809
|
-
if (o.addToggle) {
|
810
|
-
$shcell.find('.toggle')[0].checked = chkd;
|
811
|
-
}
|
812
|
-
}
|
813
|
-
};
|
814
|
-
$number.remove();
|
815
|
-
|
816
|
-
if (numberSupported) {
|
817
|
-
t = o.addToggle ? '<div class="button"><input id="html5button' + indx + '" type="checkbox" class="toggle" />' +
|
818
|
-
'<label for="html5button' + indx + '"></label></div>' : '';
|
819
|
-
t += '<input class="number" type="number" min="' + o.min + '" max="' + o.max + '" value="' +
|
820
|
-
o.value + '" step="' + o.step + '" />';
|
821
|
-
// add HTML5 number (spinner)
|
822
|
-
$cell
|
823
|
-
.append(t + '<input type="hidden" />')
|
824
|
-
.find('.toggle, .number').bind('change', function(){
|
825
|
-
updateNumber();
|
826
|
-
})
|
827
|
-
.closest('thead').find('th[data-column=' + indx + ']')
|
828
|
-
.addClass('filter-parsed') // get exact numbers from column
|
829
|
-
// on reset
|
830
|
-
.closest('table').bind('filterReset', function(){
|
831
|
-
if ($.isArray(o.compare)) {
|
832
|
-
$cell.add($shcell).find(compareSelect).val( o.compare[ o.selected || 0 ] );
|
833
|
-
}
|
834
|
-
// turn off the toggle checkbox
|
835
|
-
if (o.addToggle) {
|
836
|
-
$cell.find('.toggle')[0].checked = false;
|
837
|
-
if ($shcell.length) {
|
838
|
-
$shcell.find('.toggle')[0].checked = false;
|
839
|
-
}
|
840
|
-
}
|
841
|
-
$cell.find('.number').val( o.value );
|
842
|
-
setTimeout(function(){
|
843
|
-
updateNumber();
|
844
|
-
}, 0);
|
845
|
-
});
|
846
|
-
$input = $cell.find('input[type=hidden]').bind('change', function(){
|
847
|
-
$cell.find('.number').val( this.value );
|
848
|
-
updateNumber();
|
849
|
-
});
|
850
|
-
|
851
|
-
// update slider from hidden input, in case of saved filters
|
852
|
-
c.$table.bind('filterFomatterUpdate', function(){
|
853
|
-
var val = tsff.updateCompare($cell, $input, o)[0] || o.value;
|
854
|
-
$cell.find('.number').val( ((val || '') + '').replace(/[><=]/g,'') );
|
855
|
-
updateNumber(false, true);
|
856
|
-
ts.filter.formatterUpdated($cell, indx);
|
857
|
-
});
|
858
|
-
|
859
|
-
if (o.compare) {
|
860
|
-
// add compare select
|
861
|
-
tsff.addCompare($cell, indx, o);
|
862
|
-
$cell.find(compareSelect).bind('change', function(){
|
863
|
-
updateNumber();
|
864
|
-
});
|
865
|
-
}
|
866
|
-
|
867
|
-
// has sticky headers?
|
868
|
-
c.$table.bind('stickyHeadersInit', function(){
|
869
|
-
$shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty();
|
870
|
-
$shcell
|
871
|
-
.append(t)
|
872
|
-
.find('.toggle, .number').bind('change', function(){
|
873
|
-
$cell.find('.number').val( $(this).val() );
|
874
|
-
updateNumber();
|
875
|
-
});
|
876
|
-
|
877
|
-
if (o.compare) {
|
878
|
-
// add compare select
|
879
|
-
tsff.addCompare($shcell, indx, o);
|
880
|
-
$shcell.find(compareSelect).bind('change', function(){
|
881
|
-
$cell.find(compareSelect).val( $(this).val() );
|
882
|
-
updateNumber();
|
883
|
-
});
|
884
|
-
}
|
885
|
-
|
886
|
-
updateNumber();
|
887
|
-
});
|
888
|
-
|
889
|
-
updateNumber();
|
890
|
-
|
891
|
-
}
|
892
|
-
|
893
|
-
return numberSupported ? $cell.find('input[type="hidden"]') : $('<input type="search">');
|
894
|
-
},
|
895
|
-
|
896
|
-
/**********************\
|
897
|
-
HTML5 range slider
|
898
|
-
\**********************/
|
899
|
-
html5Range : function($cell, indx, def5Range) {
|
900
|
-
var o = $.extend({
|
901
|
-
value : 0,
|
902
|
-
min : 0,
|
903
|
-
max : 100,
|
904
|
-
step : 1,
|
905
|
-
delayed : true,
|
906
|
-
valueToHeader : true,
|
907
|
-
exactMatch : true,
|
908
|
-
cellText : '',
|
909
|
-
compare : '',
|
910
|
-
allText : 'all',
|
911
|
-
skipTest : false
|
912
|
-
}, def5Range),
|
913
|
-
|
914
|
-
$input,
|
915
|
-
// test browser for HTML5 range support
|
916
|
-
$range = $('<input type="range" style="visibility:hidden;" value="test">').appendTo($cell),
|
917
|
-
// test if HTML5 range is supported - from Modernizr (but I left out the method to detect in Safari 2-4)
|
918
|
-
// see https://github.com/Modernizr/Modernizr/blob/master/feature-detects/inputtypes.js
|
919
|
-
rangeSupported = o.skipTest || $range.attr('type') === 'range' && $range.val() !== 'test',
|
920
|
-
$shcell = [],
|
921
|
-
c = $cell.closest('table')[0].config,
|
922
|
-
|
923
|
-
updateRange = function(v, delayed, notrigger){
|
924
|
-
/*jshint eqeqeq:false */
|
925
|
-
// hidden input changes may include compare symbols
|
926
|
-
v = ( typeof v === "undefined" ? $input.val() : v ).toString().replace(/[<>=]/g,'') || o.value;
|
927
|
-
var compare = ($.isArray(o.compare) ? $cell.find(compareSelect).val() || o.compare[ o.selected || 0] : o.compare) || '',
|
928
|
-
t = ' (' + (compare ? compare + v : v == o.min ? o.allText : v) + ')',
|
929
|
-
searchType = c.$table[0].hasInitialized ? (delayed ? delayed : o.delayed) || '' : true;
|
930
|
-
$cell.find('input[type=hidden]')
|
931
|
-
// add equal to the beginning, so we filter exact numbers
|
932
|
-
.val( ( compare ? compare + v : ( v == o.min ? '' : ( o.exactMatch ? '=' : '' ) + v ) ) )
|
933
|
-
//( val == o.min ? '' : val + (o.exactMatch ? '=' : ''))
|
934
|
-
.trigger( notrigger ? '' : 'search', searchType ).end()
|
935
|
-
.find('.range').val(v);
|
936
|
-
// or add current value to the header cell, if desired
|
937
|
-
$cell.closest('thead').find('th[data-column=' + indx + ']').find('.curvalue').html(t);
|
938
|
-
// update sticky header cell
|
939
|
-
if ($shcell.length) {
|
940
|
-
$shcell
|
941
|
-
.find('.range').val(v).end()
|
942
|
-
.find(compareSelect).val( compare );
|
943
|
-
$shcell.closest('thead').find('th[data-column=' + indx + ']').find('.curvalue').html(t);
|
944
|
-
}
|
945
|
-
};
|
946
|
-
$range.remove();
|
947
|
-
|
948
|
-
if (rangeSupported) {
|
949
|
-
// add HTML5 range
|
950
|
-
$cell
|
951
|
-
.html('<input type="hidden"><input class="range" type="range" min="' + o.min + '" max="' + o.max + '" value="' + o.value + '" />')
|
952
|
-
.closest('thead').find('th[data-column=' + indx + ']')
|
953
|
-
.addClass('filter-parsed') // get exact numbers from column
|
954
|
-
// add span to header for the current slider value
|
955
|
-
.find('.tablesorter-header-inner').append('<span class="curvalue" />');
|
956
|
-
// hidden filter update namespace trigger by filter widget
|
957
|
-
$input = $cell.find('input[type=hidden]').bind('change' + c.namespace + 'filter', function(){
|
958
|
-
/*jshint eqeqeq:false */
|
959
|
-
var v = this.value,
|
960
|
-
compare = ($.isArray(o.compare) ? $cell.find(compareSelect).val() || o.compare[ o.selected || 0] : o.compare) || '';
|
961
|
-
if (v !== this.lastValue) {
|
962
|
-
this.lastValue = ( compare ? compare + v : ( v == o.min ? '' : ( o.exactMatch ? '=' : '' ) + v ) );
|
963
|
-
this.value = this.lastValue;
|
964
|
-
updateRange( v );
|
965
|
-
}
|
966
|
-
});
|
967
|
-
|
968
|
-
$cell.find('.range').bind('change', function(){
|
969
|
-
updateRange( this.value );
|
970
|
-
});
|
971
|
-
|
972
|
-
// update spinner from hidden input, in case of saved filters
|
973
|
-
c.$table.bind('filterFomatterUpdate', function(){
|
974
|
-
var val = tsff.updateCompare($cell, $input, o)[0];
|
975
|
-
$cell.find('.range').val( val );
|
976
|
-
updateRange(val, false, true);
|
977
|
-
ts.filter.formatterUpdated($cell, indx);
|
978
|
-
});
|
979
|
-
|
980
|
-
if (o.compare) {
|
981
|
-
// add compare select
|
982
|
-
tsff.addCompare($cell, indx, o);
|
983
|
-
$cell.find(compareSelect).bind('change', function(){
|
984
|
-
updateRange();
|
985
|
-
});
|
986
|
-
}
|
987
|
-
|
988
|
-
// has sticky headers?
|
989
|
-
c.$table.bind('stickyHeadersInit', function(){
|
990
|
-
$shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty();
|
991
|
-
$shcell
|
992
|
-
.html('<input class="range" type="range" min="' + o.min + '" max="' + o.max + '" value="' + o.value + '" />')
|
993
|
-
.find('.range').bind('change', function(){
|
994
|
-
updateRange( $shcell.find('.range').val() );
|
995
|
-
});
|
996
|
-
updateRange();
|
997
|
-
|
998
|
-
if (o.compare) {
|
999
|
-
// add compare select
|
1000
|
-
tsff.addCompare($shcell, indx, o);
|
1001
|
-
$shcell.find(compareSelect).bind('change', function(){
|
1002
|
-
$cell.find(compareSelect).val( $(this).val() );
|
1003
|
-
updateRange();
|
1004
|
-
});
|
1005
|
-
}
|
1006
|
-
|
1007
|
-
});
|
1008
|
-
|
1009
|
-
// on reset
|
1010
|
-
$cell.closest('table').bind('filterReset', function(){
|
1011
|
-
if ($.isArray(o.compare)) {
|
1012
|
-
$cell.add($shcell).find(compareSelect).val( o.compare[ o.selected || 0 ] );
|
1013
|
-
}
|
1014
|
-
setTimeout(function(){
|
1015
|
-
updateRange(o.value, false, true);
|
1016
|
-
}, 0);
|
1017
|
-
});
|
1018
|
-
updateRange();
|
1019
|
-
|
1020
|
-
}
|
1021
|
-
|
1022
|
-
return rangeSupported ? $cell.find('input[type="hidden"]') : $('<input type="search">');
|
1023
|
-
},
|
1024
|
-
|
1025
|
-
/**********************\
|
1026
|
-
HTML5 Color picker
|
1027
|
-
\**********************/
|
1028
|
-
html5Color: function($cell, indx, defColor) {
|
1029
|
-
var t, o = $.extend({
|
1030
|
-
value : '#000000',
|
1031
|
-
disabled : false,
|
1032
|
-
addToggle : true,
|
1033
|
-
exactMatch : true,
|
1034
|
-
valueToHeader : false,
|
1035
|
-
skipTest : false
|
1036
|
-
}, defColor),
|
1037
|
-
$input,
|
1038
|
-
// Add a hidden input to hold the range values
|
1039
|
-
$color = $('<input type="color" style="visibility:hidden;" value="test">').appendTo($cell),
|
1040
|
-
// test if HTML5 color is supported - from Modernizr
|
1041
|
-
colorSupported = o.skipTest || $color.attr('type') === 'color' && $color.val() !== 'test',
|
1042
|
-
$shcell = [],
|
1043
|
-
c = $cell.closest('table')[0].config,
|
1044
|
-
|
1045
|
-
updateColor = function(v, notrigger){
|
1046
|
-
v = ( typeof v === "undefined" ? $input.val() : v ).toString().replace('=','') || o.value;
|
1047
|
-
var chkd = true,
|
1048
|
-
t = ' (' + v + ')';
|
1049
|
-
if (o.addToggle) {
|
1050
|
-
chkd = $cell.find('.toggle').is(':checked');
|
1051
|
-
}
|
1052
|
-
if ($cell.find('.colorpicker').length) {
|
1053
|
-
$cell.find('.colorpicker').val(v)[0].disabled = (o.disabled || !chkd);
|
1054
|
-
}
|
1055
|
-
|
1056
|
-
$input
|
1057
|
-
.val( chkd ? v + (o.exactMatch ? '=' : '') : '' )
|
1058
|
-
.trigger( !c.$table[0].hasInitialized || notrigger ? '' : 'search' );
|
1059
|
-
if (o.valueToHeader) {
|
1060
|
-
// add current color to the header cell
|
1061
|
-
$cell.closest('thead').find('th[data-column=' + indx + ']').find('.curcolor').html(t);
|
1062
|
-
} else {
|
1063
|
-
// current color to span in cell
|
1064
|
-
$cell.find('.currentColor').html(t);
|
1065
|
-
}
|
1066
|
-
|
1067
|
-
// update sticky header cell
|
1068
|
-
if ($shcell.length) {
|
1069
|
-
$shcell.find('.colorpicker').val(v)[0].disabled = (o.disabled || !chkd);
|
1070
|
-
if (o.addToggle) {
|
1071
|
-
$shcell.find('.toggle')[0].checked = chkd;
|
1072
|
-
}
|
1073
|
-
if (o.valueToHeader) {
|
1074
|
-
// add current color to the header cell
|
1075
|
-
$shcell.closest('thead').find('th[data-column=' + indx + ']').find('.curcolor').html(t);
|
1076
|
-
} else {
|
1077
|
-
// current color to span in cell
|
1078
|
-
$shcell.find('.currentColor').html(t);
|
1079
|
-
}
|
1080
|
-
}
|
1081
|
-
};
|
1082
|
-
$color.remove();
|
1083
|
-
|
1084
|
-
if (colorSupported) {
|
1085
|
-
t = '' + indx + Math.round(Math.random() * 100);
|
1086
|
-
// add HTML5 color picker
|
1087
|
-
t = '<div class="color-controls-wrapper">' +
|
1088
|
-
(o.addToggle ? '<div class="button"><input id="colorbutton' + t + '" type="checkbox" class="toggle" /><label for="colorbutton' +
|
1089
|
-
t + '"></label></div>' : '') +
|
1090
|
-
'<input type="hidden"><input class="colorpicker" type="color" />' +
|
1091
|
-
(o.valueToHeader ? '' : '<span class="currentColor">(#000000)</span>') + '</div>';
|
1092
|
-
$cell.html(t);
|
1093
|
-
// add span to header for the current color value - only works if the line in the updateColor() function is also un-commented out
|
1094
|
-
if (o.valueToHeader) {
|
1095
|
-
$cell.closest('thead').find('th[data-column=' + indx + ']').find('.tablesorter-header-inner').append('<span class="curcolor" />');
|
1096
|
-
}
|
1097
|
-
|
1098
|
-
$cell.find('.toggle, .colorpicker').bind('change', function(){
|
1099
|
-
updateColor( $cell.find('.colorpicker').val() );
|
1100
|
-
});
|
1101
|
-
|
1102
|
-
// hidden filter update namespace trigger by filter widget
|
1103
|
-
$input = $cell.find('input[type=hidden]').bind('change' + c.namespace + 'filter', function(){
|
1104
|
-
updateColor( this.value );
|
1105
|
-
});
|
1106
|
-
|
1107
|
-
// update slider from hidden input, in case of saved filters
|
1108
|
-
c.$table.bind('filterFomatterUpdate', function(){
|
1109
|
-
updateColor( $input.val(), true );
|
1110
|
-
ts.filter.formatterUpdated($cell, indx);
|
1111
|
-
});
|
1112
|
-
|
1113
|
-
// on reset
|
1114
|
-
$cell.closest('table').bind('filterReset', function(){
|
1115
|
-
// just turn off the colorpicker
|
1116
|
-
if (o.addToggle) {
|
1117
|
-
$cell.find('.toggle')[0].checked = false;
|
1118
|
-
}
|
1119
|
-
// delay needed because default color needs to be set in the filter
|
1120
|
-
// there is no compare option here, so if addToggle = false,
|
1121
|
-
// default color is #000000 (even with no value set)
|
1122
|
-
setTimeout(function(){
|
1123
|
-
updateColor();
|
1124
|
-
}, 0);
|
1125
|
-
});
|
1126
|
-
|
1127
|
-
// has sticky headers?
|
1128
|
-
c.$table.bind('stickyHeadersInit', function(){
|
1129
|
-
$shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx);
|
1130
|
-
$shcell
|
1131
|
-
.html(t)
|
1132
|
-
.find('.toggle, .colorpicker').bind('change', function(){
|
1133
|
-
updateColor( $shcell.find('.colorpicker').val() );
|
1134
|
-
});
|
1135
|
-
updateColor( $shcell.find('.colorpicker').val() );
|
1136
|
-
});
|
1137
|
-
|
1138
|
-
updateColor( o.value );
|
1139
|
-
}
|
1140
|
-
return colorSupported ? $cell.find('input[type="hidden"]') : $('<input type="search">');
|
1141
761
|
}
|
1142
762
|
|
1143
763
|
};
|
File without changes
|
@@ -0,0 +1,1307 @@
|
|
1
|
+
/*! Widget: filter */
|
2
|
+
;(function ($) {
|
3
|
+
'use strict';
|
4
|
+
var ts = $.tablesorter = $.tablesorter || {};
|
5
|
+
|
6
|
+
$.extend(ts.css, {
|
7
|
+
filterRow : 'tablesorter-filter-row',
|
8
|
+
filter : 'tablesorter-filter'
|
9
|
+
});
|
10
|
+
|
11
|
+
ts.addWidget({
|
12
|
+
id: "filter",
|
13
|
+
priority: 50,
|
14
|
+
options : {
|
15
|
+
filter_childRows : false, // if true, filter includes child row content in the search
|
16
|
+
filter_columnFilters : true, // if true, a filter will be added to the top of each table column
|
17
|
+
filter_columnAnyMatch: true, // if true, allows using "#:{query}" in AnyMatch searches (column:query)
|
18
|
+
filter_cellFilter : '', // css class name added to the filter cell (string or array)
|
19
|
+
filter_cssFilter : '', // css class name added to the filter row & each input in the row (tablesorter-filter is ALWAYS added)
|
20
|
+
filter_defaultFilter : {}, // add a default column filter type "~{query}" to make fuzzy searches default; "{q1} AND {q2}" to make all searches use a logical AND.
|
21
|
+
filter_excludeFilter : {}, // filters to exclude, per column
|
22
|
+
filter_external : '', // jQuery selector string (or jQuery object) of external filters
|
23
|
+
filter_filteredRow : 'filtered', // class added to filtered rows; needed by pager plugin
|
24
|
+
filter_formatter : null, // add custom filter elements to the filter row
|
25
|
+
filter_functions : null, // add custom filter functions using this option
|
26
|
+
filter_hideEmpty : true, // hide filter row when table is empty
|
27
|
+
filter_hideFilters : false, // collapse filter row when mouse leaves the area
|
28
|
+
filter_ignoreCase : true, // if true, make all searches case-insensitive
|
29
|
+
filter_liveSearch : true, // if true, search column content while the user types (with a delay)
|
30
|
+
filter_onlyAvail : 'filter-onlyAvail', // a header with a select dropdown & this class name will only show available (visible) options within the drop down
|
31
|
+
filter_placeholder : { search : '', select : '' }, // default placeholder text (overridden by any header "data-placeholder" setting)
|
32
|
+
filter_reset : null, // jQuery selector string of an element used to reset the filters
|
33
|
+
filter_saveFilters : false, // Use the $.tablesorter.storage utility to save the most recent filters
|
34
|
+
filter_searchDelay : 300, // typing delay in milliseconds before starting a search
|
35
|
+
filter_searchFiltered: true, // allow searching through already filtered rows in special circumstances; will speed up searching in large tables if true
|
36
|
+
filter_selectSource : null, // include a function to return an array of values to be added to the column filter select
|
37
|
+
filter_startsWith : false, // if true, filter start from the beginning of the cell contents
|
38
|
+
filter_useParsedData : false, // filter all data using parsed content
|
39
|
+
filter_serversideFiltering : false, // if true, server-side filtering should be performed because client-side filtering will be disabled, but the ui and events will still be used.
|
40
|
+
filter_defaultAttrib : 'data-value', // data attribute in the header cell that contains the default filter value
|
41
|
+
filter_selectSourceSeparator : '|' // filter_selectSource array text left of the separator is added to the option value, right into the option text
|
42
|
+
},
|
43
|
+
format: function(table, c, wo) {
|
44
|
+
if (!c.$table.hasClass('hasFilters')) {
|
45
|
+
ts.filter.init(table, c, wo);
|
46
|
+
}
|
47
|
+
},
|
48
|
+
remove: function(table, c, wo, refreshing) {
|
49
|
+
var tbodyIndex, $tbody,
|
50
|
+
$table = c.$table,
|
51
|
+
$tbodies = c.$tbodies,
|
52
|
+
events = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search '.split(' ').join(c.namespace + 'filter ');
|
53
|
+
$table
|
54
|
+
.removeClass('hasFilters')
|
55
|
+
// add .tsfilter namespace to all BUT search
|
56
|
+
.unbind( events.replace(/\s+/g, ' ') )
|
57
|
+
// remove the filter row even if refreshing, because the column might have been moved
|
58
|
+
.find('.' + ts.css.filterRow).remove();
|
59
|
+
if (refreshing) { return; }
|
60
|
+
for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) {
|
61
|
+
$tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); // remove tbody
|
62
|
+
$tbody.children().removeClass(wo.filter_filteredRow).show();
|
63
|
+
ts.processTbody(table, $tbody, false); // restore tbody
|
64
|
+
}
|
65
|
+
if (wo.filter_reset) {
|
66
|
+
$(document).undelegate(wo.filter_reset, 'click.tsfilter');
|
67
|
+
}
|
68
|
+
}
|
69
|
+
});
|
70
|
+
|
71
|
+
ts.filter = {
|
72
|
+
|
73
|
+
// regex used in filter "check" functions - not for general use and not documented
|
74
|
+
regex: {
|
75
|
+
regex : /^\/((?:\\\/|[^\/])+)\/([mig]{0,3})?$/, // regex to test for regex
|
76
|
+
child : /tablesorter-childRow/, // child row class name; this gets updated in the script
|
77
|
+
filtered : /filtered/, // filtered (hidden) row class name; updated in the script
|
78
|
+
type : /undefined|number/, // check type
|
79
|
+
exact : /(^[\"\'=]+)|([\"\'=]+$)/g, // exact match (allow '==')
|
80
|
+
nondigit : /[^\w,. \-()]/g, // replace non-digits (from digit & currency parser)
|
81
|
+
operators : /[<>=]/g, // replace operators
|
82
|
+
query : '(q|query)' // replace filter queries
|
83
|
+
},
|
84
|
+
// function( c, data ) { }
|
85
|
+
// c = table.config
|
86
|
+
// data.filter = array of filter input values;
|
87
|
+
// data.iFilter = same array, except lowercase (if wo.filter_ignoreCase is true)
|
88
|
+
// data.exact = table cell text (or parsed data if column parser enabled)
|
89
|
+
// data.iExact = same as data.exact, except lowercase (if wo.filter_ignoreCase is true)
|
90
|
+
// data.cache = table cell text from cache, so it has been parsed (& in all lower case if config.ignoreCase is true)
|
91
|
+
// data.index = column index; table = table element (DOM)
|
92
|
+
// data.parsed = array (by column) of boolean values (from filter_useParsedData or "filter-parsed" class)
|
93
|
+
types: {
|
94
|
+
// Look for regex
|
95
|
+
regex: function( c, data ) {
|
96
|
+
if ( ts.filter.regex.regex.test(data.iFilter) ) {
|
97
|
+
var matches,
|
98
|
+
regex = ts.filter.regex.regex.exec(data.iFilter);
|
99
|
+
try {
|
100
|
+
matches = new RegExp(regex[1], regex[2]).test( data.iExact );
|
101
|
+
} catch (error) {
|
102
|
+
matches = false;
|
103
|
+
}
|
104
|
+
return matches;
|
105
|
+
}
|
106
|
+
return null;
|
107
|
+
},
|
108
|
+
// Look for operators >, >=, < or <=
|
109
|
+
operators: function( c, data ) {
|
110
|
+
if ( /^[<>]=?/.test(data.iFilter) ) {
|
111
|
+
var cachedValue, result,
|
112
|
+
table = c.table,
|
113
|
+
index = data.index,
|
114
|
+
parsed = data.parsed[index],
|
115
|
+
query = ts.formatFloat( data.iFilter.replace(ts.filter.regex.operators, ''), table ),
|
116
|
+
parser = c.parsers[index],
|
117
|
+
savedSearch = query;
|
118
|
+
// parse filter value in case we're comparing numbers (dates)
|
119
|
+
if (parsed || parser.type === 'numeric') {
|
120
|
+
result = ts.filter.parseFilter(c, $.trim('' + data.iFilter.replace(ts.filter.regex.operators, '')), index, parsed, true);
|
121
|
+
query = ( typeof result === "number" && result !== '' && !isNaN(result) ) ? result : query;
|
122
|
+
}
|
123
|
+
|
124
|
+
// iExact may be numeric - see issue #149;
|
125
|
+
// check if cached is defined, because sometimes j goes out of range? (numeric columns)
|
126
|
+
cachedValue = ( parsed || parser.type === 'numeric' ) && !isNaN(query) && typeof data.cache !== 'undefined' ? data.cache :
|
127
|
+
isNaN(data.iExact) ? ts.formatFloat( data.iExact.replace(ts.filter.regex.nondigit, ''), table) :
|
128
|
+
ts.formatFloat( data.iExact, table );
|
129
|
+
|
130
|
+
if ( />/.test(data.iFilter) ) { result = />=/.test(data.iFilter) ? cachedValue >= query : cachedValue > query; }
|
131
|
+
if ( /</.test(data.iFilter) ) { result = /<=/.test(data.iFilter) ? cachedValue <= query : cachedValue < query; }
|
132
|
+
// keep showing all rows if nothing follows the operator
|
133
|
+
if ( !result && savedSearch === '' ) { result = true; }
|
134
|
+
return result;
|
135
|
+
}
|
136
|
+
return null;
|
137
|
+
},
|
138
|
+
// Look for a not match
|
139
|
+
notMatch: function( c, data ) {
|
140
|
+
if ( /^\!/.test(data.iFilter) ) {
|
141
|
+
var indx,
|
142
|
+
filter = ts.filter.parseFilter(c, data.iFilter.replace('!', ''), data.index, data.parsed[data.index]);
|
143
|
+
if (ts.filter.regex.exact.test(filter)) {
|
144
|
+
// look for exact not matches - see #628
|
145
|
+
filter = filter.replace(ts.filter.regex.exact, '');
|
146
|
+
return filter === '' ? true : $.trim(filter) !== data.iExact;
|
147
|
+
} else {
|
148
|
+
indx = data.iExact.search( $.trim(filter) );
|
149
|
+
return filter === '' ? true : !(c.widgetOptions.filter_startsWith ? indx === 0 : indx >= 0);
|
150
|
+
}
|
151
|
+
}
|
152
|
+
return null;
|
153
|
+
},
|
154
|
+
// Look for quotes or equals to get an exact match; ignore type since iExact could be numeric
|
155
|
+
exact: function( c, data ) {
|
156
|
+
/*jshint eqeqeq:false */
|
157
|
+
if (ts.filter.regex.exact.test(data.iFilter)) {
|
158
|
+
var filter = ts.filter.parseFilter(c, data.iFilter.replace(ts.filter.regex.exact, ''), data.index, data.parsed[data.index]);
|
159
|
+
return data.anyMatch ? $.inArray(filter, data.rowArray) >= 0 : filter == data.iExact;
|
160
|
+
}
|
161
|
+
return null;
|
162
|
+
},
|
163
|
+
// Look for an AND or && operator (logical and)
|
164
|
+
and : function( c, data ) {
|
165
|
+
if ( ts.filter.regex.andTest.test(data.filter) ) {
|
166
|
+
var index = data.index,
|
167
|
+
parsed = data.parsed[index],
|
168
|
+
query = data.iFilter.split( ts.filter.regex.andSplit ),
|
169
|
+
result = data.iExact.search( $.trim( ts.filter.parseFilter(c, query[0], index, parsed) ) ) >= 0,
|
170
|
+
indx = query.length - 1;
|
171
|
+
while (result && indx) {
|
172
|
+
result = result && data.iExact.search( $.trim( ts.filter.parseFilter(c, query[indx], index, parsed) ) ) >= 0;
|
173
|
+
indx--;
|
174
|
+
}
|
175
|
+
return result;
|
176
|
+
}
|
177
|
+
return null;
|
178
|
+
},
|
179
|
+
// Look for a range (using " to " or " - ") - see issue #166; thanks matzhu!
|
180
|
+
range : function( c, data ) {
|
181
|
+
if ( ts.filter.regex.toTest.test(data.iFilter) ) {
|
182
|
+
var result, tmp,
|
183
|
+
table = c.table,
|
184
|
+
index = data.index,
|
185
|
+
parsed = data.parsed[index],
|
186
|
+
// make sure the dash is for a range and not indicating a negative number
|
187
|
+
query = data.iFilter.split( ts.filter.regex.toSplit ),
|
188
|
+
range1 = ts.formatFloat( ts.filter.parseFilter(c, query[0].replace(ts.filter.regex.nondigit, ''), index, parsed), table ),
|
189
|
+
range2 = ts.formatFloat( ts.filter.parseFilter(c, query[1].replace(ts.filter.regex.nondigit, ''), index, parsed), table );
|
190
|
+
// parse filter value in case we're comparing numbers (dates)
|
191
|
+
if (parsed || c.parsers[index].type === 'numeric') {
|
192
|
+
result = c.parsers[index].format('' + query[0], table, c.$headers.eq(index), index);
|
193
|
+
range1 = (result !== '' && !isNaN(result)) ? result : range1;
|
194
|
+
result = c.parsers[index].format('' + query[1], table, c.$headers.eq(index), index);
|
195
|
+
range2 = (result !== '' && !isNaN(result)) ? result : range2;
|
196
|
+
}
|
197
|
+
result = ( parsed || c.parsers[index].type === 'numeric' ) && !isNaN(range1) && !isNaN(range2) ? data.cache :
|
198
|
+
isNaN(data.iExact) ? ts.formatFloat( data.iExact.replace(ts.filter.regex.nondigit, ''), table) :
|
199
|
+
ts.formatFloat( data.iExact, table );
|
200
|
+
if (range1 > range2) { tmp = range1; range1 = range2; range2 = tmp; } // swap
|
201
|
+
return (result >= range1 && result <= range2) || (range1 === '' || range2 === '');
|
202
|
+
}
|
203
|
+
return null;
|
204
|
+
},
|
205
|
+
// Look for wild card: ? = single, * = multiple, or | = logical OR
|
206
|
+
wild : function( c, data ) {
|
207
|
+
if ( /[\?\*\|]/.test(data.iFilter) || ts.filter.regex.orReplace.test(data.filter) ) {
|
208
|
+
var index = data.index,
|
209
|
+
parsed = data.parsed[index],
|
210
|
+
query = ts.filter.parseFilter(c, data.iFilter.replace(ts.filter.regex.orReplace, "|"), index, parsed);
|
211
|
+
// look for an exact match with the "or" unless the "filter-match" class is found
|
212
|
+
if (!c.$headers.filter('[data-column="' + index + '"]:last').hasClass('filter-match') && /\|/.test(query)) {
|
213
|
+
// show all results while using filter match. Fixes #727
|
214
|
+
if (query[ query.length - 1 ] === '|') { query += '*'; }
|
215
|
+
query = data.anyMatch && $.isArray(data.rowArray) ? '(' + query + ')' : '^(' + query + ')$';
|
216
|
+
}
|
217
|
+
// parsing the filter may not work properly when using wildcards =/
|
218
|
+
return new RegExp( query.replace(/\?/g, '\\S{1}').replace(/\*/g, '\\S*') ).test(data.iExact);
|
219
|
+
}
|
220
|
+
return null;
|
221
|
+
},
|
222
|
+
// fuzzy text search; modified from https://github.com/mattyork/fuzzy (MIT license)
|
223
|
+
fuzzy: function( c, data ) {
|
224
|
+
if ( /^~/.test(data.iFilter) ) {
|
225
|
+
var indx,
|
226
|
+
patternIndx = 0,
|
227
|
+
len = data.iExact.length,
|
228
|
+
pattern = ts.filter.parseFilter(c, data.iFilter.slice(1), data.index, data.parsed[data.index]);
|
229
|
+
for (indx = 0; indx < len; indx++) {
|
230
|
+
if (data.iExact[indx] === pattern[patternIndx]) {
|
231
|
+
patternIndx += 1;
|
232
|
+
}
|
233
|
+
}
|
234
|
+
if (patternIndx === pattern.length) {
|
235
|
+
return true;
|
236
|
+
}
|
237
|
+
return false;
|
238
|
+
}
|
239
|
+
return null;
|
240
|
+
}
|
241
|
+
},
|
242
|
+
init: function(table, c, wo) {
|
243
|
+
// filter language options
|
244
|
+
ts.language = $.extend(true, {}, {
|
245
|
+
to : 'to',
|
246
|
+
or : 'or',
|
247
|
+
and : 'and'
|
248
|
+
}, ts.language);
|
249
|
+
|
250
|
+
var options, string, txt, $header, column, filters, val, fxn, noSelect,
|
251
|
+
regex = ts.filter.regex;
|
252
|
+
c.$table.addClass('hasFilters');
|
253
|
+
|
254
|
+
// define timers so using clearTimeout won't cause an undefined error
|
255
|
+
wo.searchTimer = null;
|
256
|
+
wo.filter_initTimer = null;
|
257
|
+
wo.filter_formatterCount = 0;
|
258
|
+
wo.filter_formatterInit = [];
|
259
|
+
wo.filter_anyColumnSelector = '[data-column="all"],[data-column="any"]';
|
260
|
+
wo.filter_multipleColumnSelector = '[data-column*="-"],[data-column*=","]';
|
261
|
+
|
262
|
+
txt = '\\{' + ts.filter.regex.query + '\\}';
|
263
|
+
$.extend( regex, {
|
264
|
+
child : new RegExp(c.cssChildRow),
|
265
|
+
filtered : new RegExp(wo.filter_filteredRow),
|
266
|
+
alreadyFiltered : new RegExp('(\\s+(' + ts.language.or + '|-|' + ts.language.to + ')\\s+)', 'i'),
|
267
|
+
toTest : new RegExp('\\s+(-|' + ts.language.to + ')\\s+', 'i'),
|
268
|
+
toSplit : new RegExp('(?:\\s+(?:-|' + ts.language.to + ')\\s+)' ,'gi'),
|
269
|
+
andTest : new RegExp('\\s+(' + ts.language.and + '|&&)\\s+', 'i'),
|
270
|
+
andSplit : new RegExp('(?:\\s+(?:' + ts.language.and + '|&&)\\s+)', 'gi'),
|
271
|
+
orReplace : new RegExp('\\s+(' + ts.language.or + ')\\s+', 'gi'),
|
272
|
+
iQuery : new RegExp(txt, 'i'),
|
273
|
+
igQuery : new RegExp(txt, 'ig')
|
274
|
+
});
|
275
|
+
|
276
|
+
// don't build filter row if columnFilters is false or all columns are set to "filter-false" - issue #156
|
277
|
+
if (wo.filter_columnFilters !== false && c.$headers.filter('.filter-false, .parser-false').length !== c.$headers.length) {
|
278
|
+
// build filter row
|
279
|
+
ts.filter.buildRow(table, c, wo);
|
280
|
+
}
|
281
|
+
|
282
|
+
txt = 'addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search '.split(' ').join(c.namespace + 'filter ');
|
283
|
+
c.$table.bind( txt, function(event, filter) {
|
284
|
+
val = (wo.filter_hideEmpty && $.isEmptyObject(c.cache) && !(c.delayInit && event.type === 'appendCache'));
|
285
|
+
// hide filter row using the "filtered" class name
|
286
|
+
c.$table.find('.' + ts.css.filterRow).toggleClass(wo.filter_filteredRow, val ); // fixes #450
|
287
|
+
if ( !/(search|filter)/.test(event.type) ) {
|
288
|
+
event.stopPropagation();
|
289
|
+
ts.filter.buildDefault(table, true);
|
290
|
+
}
|
291
|
+
if (event.type === 'filterReset') {
|
292
|
+
c.$table.find('.' + ts.css.filter).add(wo.filter_$externalFilters).val('');
|
293
|
+
ts.filter.searching(table, []);
|
294
|
+
} else if (event.type === 'filterEnd') {
|
295
|
+
ts.filter.buildDefault(table, true);
|
296
|
+
} else {
|
297
|
+
// send false argument to force a new search; otherwise if the filter hasn't changed, it will return
|
298
|
+
filter = event.type === 'search' ? filter : event.type === 'updateComplete' ? c.$table.data('lastSearch') : '';
|
299
|
+
if (/(update|add)/.test(event.type) && event.type !== "updateComplete") {
|
300
|
+
// force a new search since content has changed
|
301
|
+
c.lastCombinedFilter = null;
|
302
|
+
c.lastSearch = [];
|
303
|
+
}
|
304
|
+
// pass true (skipFirst) to prevent the tablesorter.setFilters function from skipping the first input
|
305
|
+
// ensures all inputs are updated when a search is triggered on the table $('table').trigger('search', [...]);
|
306
|
+
ts.filter.searching(table, filter, true);
|
307
|
+
}
|
308
|
+
return false;
|
309
|
+
});
|
310
|
+
|
311
|
+
// reset button/link
|
312
|
+
if (wo.filter_reset) {
|
313
|
+
if (wo.filter_reset instanceof $) {
|
314
|
+
// reset contains a jQuery object, bind to it
|
315
|
+
wo.filter_reset.click(function(){
|
316
|
+
c.$table.trigger('filterReset');
|
317
|
+
});
|
318
|
+
} else if ($(wo.filter_reset).length) {
|
319
|
+
// reset is a jQuery selector, use event delegation
|
320
|
+
$(document)
|
321
|
+
.undelegate(wo.filter_reset, 'click.tsfilter')
|
322
|
+
.delegate(wo.filter_reset, 'click.tsfilter', function() {
|
323
|
+
// trigger a reset event, so other functions (filter_formatter) know when to reset
|
324
|
+
c.$table.trigger('filterReset');
|
325
|
+
});
|
326
|
+
}
|
327
|
+
}
|
328
|
+
if (wo.filter_functions) {
|
329
|
+
for (column = 0; column < c.columns; column++) {
|
330
|
+
fxn = ts.getColumnData( table, wo.filter_functions, column );
|
331
|
+
if (fxn) {
|
332
|
+
// remove "filter-select" from header otherwise the options added here are replaced with all options
|
333
|
+
$header = c.$headers.filter('[data-column="' + column + '"]:last').removeClass('filter-select');
|
334
|
+
// don't build select if "filter-false" or "parser-false" set
|
335
|
+
noSelect = !($header.hasClass('filter-false') || $header.hasClass('parser-false'));
|
336
|
+
options = '';
|
337
|
+
if ( fxn === true && noSelect ) {
|
338
|
+
ts.filter.buildSelect(table, column);
|
339
|
+
} else if ( typeof fxn === 'object' && noSelect ) {
|
340
|
+
// add custom drop down list
|
341
|
+
for (string in fxn) {
|
342
|
+
if (typeof string === 'string') {
|
343
|
+
options += options === '' ?
|
344
|
+
'<option value="">' + ($header.data('placeholder') || $header.attr('data-placeholder') || wo.filter_placeholder.select || '') + '</option>' : '';
|
345
|
+
val = string;
|
346
|
+
txt = string;
|
347
|
+
if (string.indexOf(wo.filter_selectSourceSeparator) >= 0) {
|
348
|
+
val = string.split(wo.filter_selectSourceSeparator);
|
349
|
+
txt = val[1];
|
350
|
+
val = val[0];
|
351
|
+
}
|
352
|
+
options += '<option ' + (txt === val ? '' : 'data-function-name="' + string + '" ') + 'value="' + val + '">' + txt + '</option>';
|
353
|
+
}
|
354
|
+
}
|
355
|
+
c.$table.find('thead').find('select.' + ts.css.filter + '[data-column="' + column + '"]').append(options);
|
356
|
+
}
|
357
|
+
}
|
358
|
+
}
|
359
|
+
}
|
360
|
+
// not really updating, but if the column has both the "filter-select" class & filter_functions set to true,
|
361
|
+
// it would append the same options twice.
|
362
|
+
ts.filter.buildDefault(table, true);
|
363
|
+
|
364
|
+
ts.filter.bindSearch( table, c.$table.find('.' + ts.css.filter), true );
|
365
|
+
if (wo.filter_external) {
|
366
|
+
ts.filter.bindSearch( table, wo.filter_external );
|
367
|
+
}
|
368
|
+
|
369
|
+
if (wo.filter_hideFilters) {
|
370
|
+
ts.filter.hideFilters(table, c);
|
371
|
+
}
|
372
|
+
|
373
|
+
// show processing icon
|
374
|
+
if (c.showProcessing) {
|
375
|
+
c.$table
|
376
|
+
.unbind( ('filterStart filterEnd '.split(' ').join(c.namespace + 'filter ')).replace(/\s+/g, ' ') )
|
377
|
+
.bind( 'filterStart filterEnd '.split(' ').join(c.namespace + 'filter '), function(event, columns) {
|
378
|
+
// only add processing to certain columns to all columns
|
379
|
+
$header = (columns) ? c.$table.find('.' + ts.css.header).filter('[data-column]').filter(function() {
|
380
|
+
return columns[$(this).data('column')] !== '';
|
381
|
+
}) : '';
|
382
|
+
ts.isProcessing(table, event.type === 'filterStart', columns ? $header : '');
|
383
|
+
});
|
384
|
+
}
|
385
|
+
|
386
|
+
// set filtered rows count (intially unfiltered)
|
387
|
+
c.filteredRows = c.totalRows;
|
388
|
+
|
389
|
+
// add default values
|
390
|
+
c.$table
|
391
|
+
.unbind( ('tablesorter-initialized pagerBeforeInitialized '.split(' ').join(c.namespace + 'filter ')).replace(/\s+/g, ' ') )
|
392
|
+
.bind( 'tablesorter-initialized pagerBeforeInitialized '.split(' ').join(c.namespace + 'filter '), function() {
|
393
|
+
// redefine "wo" as it does not update properly inside this callback
|
394
|
+
var wo = this.config.widgetOptions;
|
395
|
+
filters = ts.filter.setDefaults(table, c, wo) || [];
|
396
|
+
if (filters.length) {
|
397
|
+
// prevent delayInit from triggering a cache build if filters are empty
|
398
|
+
if ( !(c.delayInit && filters.join('') === '') ) {
|
399
|
+
ts.setFilters(table, filters, true);
|
400
|
+
}
|
401
|
+
}
|
402
|
+
c.$table.trigger('filterFomatterUpdate');
|
403
|
+
// trigger init after setTimeout to prevent multiple filterStart/End/Init triggers
|
404
|
+
setTimeout(function(){
|
405
|
+
if (!wo.filter_initialized) {
|
406
|
+
ts.filter.filterInitComplete(c);
|
407
|
+
}
|
408
|
+
}, 100);
|
409
|
+
});
|
410
|
+
// if filter widget is added after pager has initialized; then set filter init flag
|
411
|
+
if (c.pager && c.pager.initialized && !wo.filter_initialized) {
|
412
|
+
c.$table.trigger('filterFomatterUpdate');
|
413
|
+
setTimeout(function(){
|
414
|
+
ts.filter.filterInitComplete(c);
|
415
|
+
}, 100);
|
416
|
+
}
|
417
|
+
},
|
418
|
+
// $cell parameter, but not the config, is passed to the
|
419
|
+
// filter_formatters, so we have to work with it instead
|
420
|
+
formatterUpdated: function($cell, column) {
|
421
|
+
var wo = $cell.closest('table')[0].config.widgetOptions;
|
422
|
+
if (!wo.filter_initialized) {
|
423
|
+
// add updates by column since this function
|
424
|
+
// may be called numerous times before initialization
|
425
|
+
wo.filter_formatterInit[column] = 1;
|
426
|
+
}
|
427
|
+
},
|
428
|
+
filterInitComplete: function(c){
|
429
|
+
var wo = c.widgetOptions,
|
430
|
+
count = 0,
|
431
|
+
completed = function(){
|
432
|
+
wo.filter_initialized = true;
|
433
|
+
c.$table.trigger('filterInit', c);
|
434
|
+
ts.filter.findRows(c.table, c.$table.data('lastSearch') || []);
|
435
|
+
};
|
436
|
+
if ( $.isEmptyObject( wo.filter_formatter ) ) {
|
437
|
+
completed();
|
438
|
+
} else {
|
439
|
+
$.each( wo.filter_formatterInit, function(i, val) {
|
440
|
+
if (val === 1) {
|
441
|
+
count++;
|
442
|
+
}
|
443
|
+
});
|
444
|
+
clearTimeout(wo.filter_initTimer);
|
445
|
+
if (!wo.filter_initialized && count === wo.filter_formatterCount) {
|
446
|
+
// filter widget initialized
|
447
|
+
completed();
|
448
|
+
} else if (!wo.filter_initialized) {
|
449
|
+
// fall back in case a filter_formatter doesn't call
|
450
|
+
// $.tablesorter.filter.formatterUpdated($cell, column), and the count is off
|
451
|
+
wo.filter_initTimer = setTimeout(function(){
|
452
|
+
completed();
|
453
|
+
}, 500);
|
454
|
+
}
|
455
|
+
}
|
456
|
+
},
|
457
|
+
|
458
|
+
setDefaults: function(table, c, wo) {
|
459
|
+
var isArray, saved, indx,
|
460
|
+
// get current (default) filters
|
461
|
+
filters = ts.getFilters(table) || [];
|
462
|
+
if (wo.filter_saveFilters && ts.storage) {
|
463
|
+
saved = ts.storage( table, 'tablesorter-filters' ) || [];
|
464
|
+
isArray = $.isArray(saved);
|
465
|
+
// make sure we're not just getting an empty array
|
466
|
+
if ( !(isArray && saved.join('') === '' || !isArray) ) { filters = saved; }
|
467
|
+
}
|
468
|
+
// if no filters saved, then check default settings
|
469
|
+
if (filters.join('') === '') {
|
470
|
+
for (indx = 0; indx < c.columns; indx++) {
|
471
|
+
filters[indx] = c.$headers.filter('[data-column="' + indx + '"]:last').attr(wo.filter_defaultAttrib) || filters[indx];
|
472
|
+
}
|
473
|
+
}
|
474
|
+
c.$table.data('lastSearch', filters);
|
475
|
+
return filters;
|
476
|
+
},
|
477
|
+
parseFilter: function(c, filter, column, parsed, forceParse){
|
478
|
+
return forceParse || parsed ?
|
479
|
+
c.parsers[column].format( filter, c.table, [], column ) :
|
480
|
+
filter;
|
481
|
+
},
|
482
|
+
buildRow: function(table, c, wo) {
|
483
|
+
var col, column, $header, buildSelect, disabled, name, ffxn,
|
484
|
+
// c.columns defined in computeThIndexes()
|
485
|
+
columns = c.columns,
|
486
|
+
arry = $.isArray(wo.filter_cellFilter),
|
487
|
+
buildFilter = '<tr role="row" class="' + ts.css.filterRow + '">';
|
488
|
+
for (column = 0; column < columns; column++) {
|
489
|
+
if (arry) {
|
490
|
+
buildFilter += '<td' + ( wo.filter_cellFilter[column] ? ' class="' + wo.filter_cellFilter[column] + '"' : '' ) + '></td>';
|
491
|
+
} else {
|
492
|
+
buildFilter += '<td' + ( wo.filter_cellFilter !== '' ? ' class="' + wo.filter_cellFilter + '"' : '' ) + '></td>';
|
493
|
+
}
|
494
|
+
}
|
495
|
+
c.$filters = $(buildFilter += '</tr>').appendTo( c.$table.children('thead').eq(0) ).find('td');
|
496
|
+
// build each filter input
|
497
|
+
for (column = 0; column < columns; column++) {
|
498
|
+
disabled = false;
|
499
|
+
// assuming last cell of a column is the main column
|
500
|
+
$header = c.$headers.filter('[data-column="' + column + '"]:last');
|
501
|
+
ffxn = ts.getColumnData( table, wo.filter_functions, column );
|
502
|
+
buildSelect = (wo.filter_functions && ffxn && typeof ffxn !== "function" ) ||
|
503
|
+
$header.hasClass('filter-select');
|
504
|
+
// get data from jQuery data, metadata, headers option or header class name
|
505
|
+
col = ts.getColumnData( table, c.headers, column );
|
506
|
+
disabled = ts.getData($header[0], col, 'filter') === 'false' || ts.getData($header[0], col, 'parser') === 'false';
|
507
|
+
|
508
|
+
if (buildSelect) {
|
509
|
+
buildFilter = $('<select>').appendTo( c.$filters.eq(column) );
|
510
|
+
} else {
|
511
|
+
ffxn = ts.getColumnData( table, wo.filter_formatter, column );
|
512
|
+
if (ffxn) {
|
513
|
+
wo.filter_formatterCount++;
|
514
|
+
buildFilter = ffxn( c.$filters.eq(column), column );
|
515
|
+
// no element returned, so lets go find it
|
516
|
+
if (buildFilter && buildFilter.length === 0) {
|
517
|
+
buildFilter = c.$filters.eq(column).children('input');
|
518
|
+
}
|
519
|
+
// element not in DOM, so lets attach it
|
520
|
+
if ( buildFilter && (buildFilter.parent().length === 0 ||
|
521
|
+
(buildFilter.parent().length && buildFilter.parent()[0] !== c.$filters[column])) ) {
|
522
|
+
c.$filters.eq(column).append(buildFilter);
|
523
|
+
}
|
524
|
+
} else {
|
525
|
+
buildFilter = $('<input type="search">').appendTo( c.$filters.eq(column) );
|
526
|
+
}
|
527
|
+
if (buildFilter) {
|
528
|
+
buildFilter.attr('placeholder', $header.data('placeholder') || $header.attr('data-placeholder') || wo.filter_placeholder.search || '');
|
529
|
+
}
|
530
|
+
}
|
531
|
+
if (buildFilter) {
|
532
|
+
// add filter class name
|
533
|
+
name = ( $.isArray(wo.filter_cssFilter) ?
|
534
|
+
(typeof wo.filter_cssFilter[column] !== 'undefined' ? wo.filter_cssFilter[column] || '' : '') :
|
535
|
+
wo.filter_cssFilter ) || '';
|
536
|
+
buildFilter.addClass( ts.css.filter + ' ' + name ).attr('data-column', column);
|
537
|
+
if (disabled) {
|
538
|
+
buildFilter.attr('placeholder', '').addClass('disabled')[0].disabled = true; // disabled!
|
539
|
+
}
|
540
|
+
}
|
541
|
+
}
|
542
|
+
},
|
543
|
+
bindSearch: function(table, $el, internal) {
|
544
|
+
table = $(table)[0];
|
545
|
+
$el = $($el); // allow passing a selector string
|
546
|
+
if (!$el.length) { return; }
|
547
|
+
var c = table.config,
|
548
|
+
wo = c.widgetOptions,
|
549
|
+
$ext = wo.filter_$externalFilters;
|
550
|
+
if (internal !== true) {
|
551
|
+
// save anyMatch element
|
552
|
+
wo.filter_$anyMatch = $el.filter(wo.filter_anyColumnSelector + ',' + wo.filter_multipleColumnSelector);
|
553
|
+
if ($ext && $ext.length) {
|
554
|
+
wo.filter_$externalFilters = wo.filter_$externalFilters.add( $el );
|
555
|
+
} else {
|
556
|
+
wo.filter_$externalFilters = $el;
|
557
|
+
}
|
558
|
+
// update values (external filters added after table initialization)
|
559
|
+
ts.setFilters(table, c.$table.data('lastSearch') || [], internal === false);
|
560
|
+
}
|
561
|
+
$el
|
562
|
+
// use data attribute instead of jQuery data since the head is cloned without including the data/binding
|
563
|
+
.attr('data-lastSearchTime', new Date().getTime())
|
564
|
+
.unbind( ('keypress keyup search change '.split(' ').join(c.namespace + 'filter ')).replace(/\s+/g, ' ') )
|
565
|
+
// include change for select - fixes #473
|
566
|
+
.bind('keyup' + c.namespace + 'filter', function(event) {
|
567
|
+
$(this).attr('data-lastSearchTime', new Date().getTime());
|
568
|
+
// emulate what webkit does.... escape clears the filter
|
569
|
+
if (event.which === 27) {
|
570
|
+
this.value = '';
|
571
|
+
// live search
|
572
|
+
} else if ( wo.filter_liveSearch === false ) {
|
573
|
+
return;
|
574
|
+
// don't return if the search value is empty (all rows need to be revealed)
|
575
|
+
} else if ( this.value !== '' && (
|
576
|
+
// liveSearch can contain a min value length; ignore arrow and meta keys, but allow backspace
|
577
|
+
( typeof wo.filter_liveSearch === 'number' && this.value.length < wo.filter_liveSearch ) ||
|
578
|
+
// let return & backspace continue on, but ignore arrows & non-valid characters
|
579
|
+
( event.which !== 13 && event.which !== 8 && ( event.which < 32 || (event.which >= 37 && event.which <= 40) ) ) ) ) {
|
580
|
+
return;
|
581
|
+
}
|
582
|
+
// change event = no delay; last true flag tells getFilters to skip newest timed input
|
583
|
+
ts.filter.searching( table, true, true );
|
584
|
+
})
|
585
|
+
.bind( 'search change keypress '.split(' ').join(c.namespace + 'filter '), function(event){
|
586
|
+
var column = $(this).data('column');
|
587
|
+
// don't allow "change" event to process if the input value is the same - fixes #685
|
588
|
+
if (event.which === 13 || event.type === 'search' || event.type === 'change' && this.value !== c.lastSearch[column]) {
|
589
|
+
event.preventDefault();
|
590
|
+
// init search with no delay
|
591
|
+
$(this).attr('data-lastSearchTime', new Date().getTime());
|
592
|
+
ts.filter.searching( table, false, true );
|
593
|
+
}
|
594
|
+
});
|
595
|
+
},
|
596
|
+
searching: function(table, filter, skipFirst) {
|
597
|
+
var wo = table.config.widgetOptions;
|
598
|
+
clearTimeout(wo.searchTimer);
|
599
|
+
if (typeof filter === 'undefined' || filter === true) {
|
600
|
+
// delay filtering
|
601
|
+
wo.searchTimer = setTimeout(function() {
|
602
|
+
ts.filter.checkFilters(table, filter, skipFirst );
|
603
|
+
}, wo.filter_liveSearch ? wo.filter_searchDelay : 10);
|
604
|
+
} else {
|
605
|
+
// skip delay
|
606
|
+
ts.filter.checkFilters(table, filter, skipFirst);
|
607
|
+
}
|
608
|
+
},
|
609
|
+
checkFilters: function(table, filter, skipFirst) {
|
610
|
+
var c = table.config,
|
611
|
+
wo = c.widgetOptions,
|
612
|
+
filterArray = $.isArray(filter),
|
613
|
+
filters = (filterArray) ? filter : ts.getFilters(table, true),
|
614
|
+
combinedFilters = (filters || []).join(''); // combined filter values
|
615
|
+
// prevent errors if delay init is set
|
616
|
+
if ($.isEmptyObject(c.cache)) {
|
617
|
+
// update cache if delayInit set & pager has initialized (after user initiates a search)
|
618
|
+
if (c.delayInit && c.pager && c.pager.initialized) {
|
619
|
+
c.$table.trigger('updateCache', [function(){
|
620
|
+
ts.filter.checkFilters(table, false, skipFirst);
|
621
|
+
}] );
|
622
|
+
}
|
623
|
+
return;
|
624
|
+
}
|
625
|
+
// add filter array back into inputs
|
626
|
+
if (filterArray) {
|
627
|
+
ts.setFilters( table, filters, false, skipFirst !== true );
|
628
|
+
if (!wo.filter_initialized) { c.lastCombinedFilter = ''; }
|
629
|
+
}
|
630
|
+
if (wo.filter_hideFilters) {
|
631
|
+
// show/hide filter row as needed
|
632
|
+
c.$table.find('.' + ts.css.filterRow).trigger( combinedFilters === '' ? 'mouseleave' : 'mouseenter' );
|
633
|
+
}
|
634
|
+
// return if the last search is the same; but filter === false when updating the search
|
635
|
+
// see example-widget-filter.html filter toggle buttons
|
636
|
+
if (c.lastCombinedFilter === combinedFilters && filter !== false) {
|
637
|
+
return;
|
638
|
+
} else if (filter === false) {
|
639
|
+
// force filter refresh
|
640
|
+
c.lastCombinedFilter = null;
|
641
|
+
c.lastSearch = [];
|
642
|
+
}
|
643
|
+
if (wo.filter_initialized) { c.$table.trigger('filterStart', [filters]); }
|
644
|
+
if (c.showProcessing) {
|
645
|
+
// give it time for the processing icon to kick in
|
646
|
+
setTimeout(function() {
|
647
|
+
ts.filter.findRows(table, filters, combinedFilters);
|
648
|
+
return false;
|
649
|
+
}, 30);
|
650
|
+
} else {
|
651
|
+
ts.filter.findRows(table, filters, combinedFilters);
|
652
|
+
return false;
|
653
|
+
}
|
654
|
+
},
|
655
|
+
hideFilters: function(table, c) {
|
656
|
+
var $filterRow, $filterRow2, timer;
|
657
|
+
$(table)
|
658
|
+
.find('.' + ts.css.filterRow)
|
659
|
+
.addClass('hideme')
|
660
|
+
.bind('mouseenter mouseleave', function(e) {
|
661
|
+
// save event object - http://bugs.jquery.com/ticket/12140
|
662
|
+
var event = e;
|
663
|
+
$filterRow = $(this);
|
664
|
+
clearTimeout(timer);
|
665
|
+
timer = setTimeout(function() {
|
666
|
+
if ( /enter|over/.test(event.type) ) {
|
667
|
+
$filterRow.removeClass('hideme');
|
668
|
+
} else {
|
669
|
+
// don't hide if input has focus
|
670
|
+
// $(':focus') needs jQuery 1.6+
|
671
|
+
if ( $(document.activeElement).closest('tr')[0] !== $filterRow[0] ) {
|
672
|
+
// don't hide row if any filter has a value
|
673
|
+
if (c.lastCombinedFilter === '') {
|
674
|
+
$filterRow.addClass('hideme');
|
675
|
+
}
|
676
|
+
}
|
677
|
+
}
|
678
|
+
}, 200);
|
679
|
+
})
|
680
|
+
.find('input, select').bind('focus blur', function(e) {
|
681
|
+
$filterRow2 = $(this).closest('tr');
|
682
|
+
clearTimeout(timer);
|
683
|
+
var event = e;
|
684
|
+
timer = setTimeout(function() {
|
685
|
+
// don't hide row if any filter has a value
|
686
|
+
if (ts.getFilters(c.$table).join('') === '') {
|
687
|
+
$filterRow2[ event.type === 'focus' ? 'removeClass' : 'addClass']('hideme');
|
688
|
+
}
|
689
|
+
}, 200);
|
690
|
+
});
|
691
|
+
},
|
692
|
+
defaultFilter: function(filter, mask){
|
693
|
+
if (filter === '') { return filter; }
|
694
|
+
var regex = ts.filter.regex.iQuery,
|
695
|
+
maskLen = mask.match( ts.filter.regex.igQuery ).length,
|
696
|
+
query = maskLen > 1 ? $.trim(filter).split(/\s/) : [ $.trim(filter) ],
|
697
|
+
len = query.length - 1,
|
698
|
+
indx = 0,
|
699
|
+
val = mask;
|
700
|
+
if ( len < 1 && maskLen > 1 ) {
|
701
|
+
// only one "word" in query but mask has >1 slots
|
702
|
+
query[1] = query[0];
|
703
|
+
}
|
704
|
+
// replace all {query} with query words...
|
705
|
+
// if query = "Bob", then convert mask from "!{query}" to "!Bob"
|
706
|
+
// if query = "Bob Joe Frank", then convert mask "{q} OR {q}" to "Bob OR Joe OR Frank"
|
707
|
+
while (regex.test(val)) {
|
708
|
+
val = val.replace(regex, query[indx++] || '');
|
709
|
+
if (regex.test(val) && indx < len && (query[indx] || '') !== '') {
|
710
|
+
val = mask.replace(regex, val);
|
711
|
+
}
|
712
|
+
}
|
713
|
+
return val;
|
714
|
+
},
|
715
|
+
getLatestSearch: function( $input ) {
|
716
|
+
if ($input) {
|
717
|
+
return $input.sort(function(a, b) {
|
718
|
+
return $(b).attr('data-lastSearchTime') - $(a).attr('data-lastSearchTime');
|
719
|
+
});
|
720
|
+
}
|
721
|
+
return $();
|
722
|
+
},
|
723
|
+
multipleColumns: function( c, $input ) {
|
724
|
+
// look for multiple columns "1-3,4-6,8" in data-column
|
725
|
+
var ranges, singles, indx,
|
726
|
+
wo = c.widgetOptions,
|
727
|
+
// only target "all" column inputs on initialization
|
728
|
+
// & don't target "all" column inputs if they don't exist
|
729
|
+
targets = wo.filter_initialized || !$input.filter(wo.filter_anyColumnSelector).length,
|
730
|
+
columns = [],
|
731
|
+
val = $.trim( ts.filter.getLatestSearch( $input ).attr('data-column') || '' );
|
732
|
+
// process column range
|
733
|
+
if ( targets && /-/.test( val ) ) {
|
734
|
+
ranges = val.match( /(\d+)\s*-\s*(\d+)/g );
|
735
|
+
$.each(ranges, function(i,v){
|
736
|
+
var t,
|
737
|
+
range = v.split( /\s*-\s*/ ),
|
738
|
+
start = parseInt( range[0], 10 ) || 0,
|
739
|
+
end = parseInt( range[1], 10 ) || ( c.columns - 1 );
|
740
|
+
if ( start > end ) { t = start; start = end; end = t; } // swap
|
741
|
+
if ( end >= c.columns ) { end = c.columns - 1; }
|
742
|
+
for ( ; start <= end; start++ ) {
|
743
|
+
columns.push(start);
|
744
|
+
}
|
745
|
+
// remove processed range from val
|
746
|
+
val = val.replace( v, '' );
|
747
|
+
});
|
748
|
+
}
|
749
|
+
// process single columns
|
750
|
+
if ( targets && /,/.test( val ) ) {
|
751
|
+
singles = val.split( /\s*,\s*/ );
|
752
|
+
$.each( singles, function(i,v) {
|
753
|
+
if (v !== '') {
|
754
|
+
indx = parseInt( v, 10 );
|
755
|
+
if ( indx < c.columns ) {
|
756
|
+
columns.push( indx );
|
757
|
+
}
|
758
|
+
}
|
759
|
+
});
|
760
|
+
}
|
761
|
+
// return all columns
|
762
|
+
if (!columns.length) {
|
763
|
+
for ( indx = 0; indx < c.columns; indx++ ) {
|
764
|
+
columns.push( indx );
|
765
|
+
}
|
766
|
+
}
|
767
|
+
return columns;
|
768
|
+
},
|
769
|
+
findRows: function(table, filters, combinedFilters) {
|
770
|
+
if (table.config.lastCombinedFilter === combinedFilters || !table.config.widgetOptions.filter_initialized) { return; }
|
771
|
+
var len, norm_rows, $rows, rowIndex, tbodyIndex, $tbody, $cells, $cell, columnIndex,
|
772
|
+
childRow, lastSearch, hasSelect, matches, result, showRow, time, val, indx,
|
773
|
+
notFiltered, searchFiltered, filterMatched, excludeMatch, fxn, ffxn,
|
774
|
+
query, injected, res, id,
|
775
|
+
regex = ts.filter.regex,
|
776
|
+
c = table.config,
|
777
|
+
wo = c.widgetOptions,
|
778
|
+
// data object passed to filters; anyMatch is a flag for the filters
|
779
|
+
data = { anyMatch: false },
|
780
|
+
// anyMatch really screws up with these types of filters
|
781
|
+
noAnyMatch = [ 'range', 'notMatch', 'operators' ];
|
782
|
+
|
783
|
+
// parse columns after formatter, in case the class is added at that point
|
784
|
+
data.parsed = c.$headers.map(function(columnIndex) {
|
785
|
+
return c.parsers && c.parsers[columnIndex] && c.parsers[columnIndex].parsed ||
|
786
|
+
// getData won't return "parsed" if other "filter-" class names exist (e.g. <th class="filter-select filter-parsed">)
|
787
|
+
ts.getData && ts.getData(c.$headers.filter('[data-column="' + columnIndex + '"]:last'), ts.getColumnData( table, c.headers, columnIndex ), 'filter') === 'parsed' ||
|
788
|
+
$(this).hasClass('filter-parsed');
|
789
|
+
}).get();
|
790
|
+
|
791
|
+
if (c.debug) {
|
792
|
+
ts.log('Starting filter widget search', filters);
|
793
|
+
time = new Date();
|
794
|
+
}
|
795
|
+
// filtered rows count
|
796
|
+
c.filteredRows = 0;
|
797
|
+
c.totalRows = 0;
|
798
|
+
// combindedFilters are undefined on init
|
799
|
+
combinedFilters = (filters || []).join('');
|
800
|
+
|
801
|
+
for (tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) {
|
802
|
+
$tbody = ts.processTbody(table, c.$tbodies.eq(tbodyIndex), true);
|
803
|
+
// skip child rows & widget added (removable) rows - fixes #448 thanks to @hempel!
|
804
|
+
// $rows = $tbody.children('tr').not(c.selectorRemove);
|
805
|
+
columnIndex = c.columns;
|
806
|
+
// convert stored rows into a jQuery object
|
807
|
+
norm_rows = c.cache[tbodyIndex].normalized;
|
808
|
+
$rows = $( $.map(norm_rows, function(el){ return el[columnIndex].$row.get(); }) );
|
809
|
+
|
810
|
+
if (combinedFilters === '' || wo.filter_serversideFiltering) {
|
811
|
+
$rows.removeClass(wo.filter_filteredRow).not('.' + c.cssChildRow).show();
|
812
|
+
} else {
|
813
|
+
// filter out child rows
|
814
|
+
$rows = $rows.not('.' + c.cssChildRow);
|
815
|
+
len = $rows.length;
|
816
|
+
|
817
|
+
if ( (wo.filter_$anyMatch && wo.filter_$anyMatch.length) || typeof filters[c.columns] !== 'undefined' ) {
|
818
|
+
data.anyMatchFlag = true;
|
819
|
+
data.anyMatchFilter = wo.filter_$anyMatch && ts.filter.getLatestSearch( wo.filter_$anyMatch ).val() || ( '' + filters[c.columns] ) || '';
|
820
|
+
if (wo.filter_columnAnyMatch) {
|
821
|
+
// specific columns search
|
822
|
+
query = data.anyMatchFilter.split( ts.filter.regex.andSplit );
|
823
|
+
injected = false;
|
824
|
+
for (indx = 0; indx < query.length; indx++) {
|
825
|
+
res = query[indx].split(':');
|
826
|
+
if ( res.length > 1 ) {
|
827
|
+
// make the column a one-based index ( non-developers start counting from one :P )
|
828
|
+
id = parseInt( res[0], 10 ) - 1;
|
829
|
+
if ( id >= 0 && id < c.columns ) { // if id is an integer
|
830
|
+
filters[id] = res[1];
|
831
|
+
query.splice(indx, 1);
|
832
|
+
indx--;
|
833
|
+
injected = true;
|
834
|
+
}
|
835
|
+
}
|
836
|
+
}
|
837
|
+
if (injected) {
|
838
|
+
data.anyMatchFilter = query.join(' && ');
|
839
|
+
}
|
840
|
+
}
|
841
|
+
}
|
842
|
+
|
843
|
+
// optimize searching only through already filtered rows - see #313
|
844
|
+
searchFiltered = wo.filter_searchFiltered;
|
845
|
+
lastSearch = c.lastSearch || c.$table.data('lastSearch') || [];
|
846
|
+
if (searchFiltered) {
|
847
|
+
// cycle through all filters; include last (columnIndex + 1 = match any column). Fixes #669
|
848
|
+
for (indx = 0; indx < columnIndex + 1; indx++) {
|
849
|
+
val = filters[indx] || '';
|
850
|
+
// break out of loop if we've already determined not to search filtered rows
|
851
|
+
if (!searchFiltered) { indx = columnIndex; }
|
852
|
+
// search already filtered rows if...
|
853
|
+
searchFiltered = searchFiltered && lastSearch.length &&
|
854
|
+
// there are no changes from beginning of filter
|
855
|
+
val.indexOf(lastSearch[indx] || '') === 0 &&
|
856
|
+
// if there is NOT a logical "or", or range ("to" or "-") in the string
|
857
|
+
!regex.alreadyFiltered.test(val) &&
|
858
|
+
// if we are not doing exact matches, using "|" (logical or) or not "!"
|
859
|
+
!/[=\"\|!]/.test(val) &&
|
860
|
+
// don't search only filtered if the value is negative ('> -10' => '> -100' will ignore hidden rows)
|
861
|
+
!(/(>=?\s*-\d)/.test(val) || /(<=?\s*\d)/.test(val)) &&
|
862
|
+
// if filtering using a select without a "filter-match" class (exact match) - fixes #593
|
863
|
+
!( val !== '' && c.$filters && c.$filters.eq(indx).find('select').length && !c.$headers.filter('[data-column="' + indx + '"]:last').hasClass('filter-match') );
|
864
|
+
}
|
865
|
+
}
|
866
|
+
notFiltered = $rows.not('.' + wo.filter_filteredRow).length;
|
867
|
+
// can't search when all rows are hidden - this happens when looking for exact matches
|
868
|
+
if (searchFiltered && notFiltered === 0) { searchFiltered = false; }
|
869
|
+
if (c.debug) {
|
870
|
+
ts.log( "Searching through " + ( searchFiltered && notFiltered < len ? notFiltered : "all" ) + " rows" );
|
871
|
+
}
|
872
|
+
if (data.anyMatchFlag) {
|
873
|
+
if (c.sortLocaleCompare) {
|
874
|
+
// replace accents
|
875
|
+
data.anyMatchFilter = ts.replaceAccents(data.anyMatchFilter);
|
876
|
+
}
|
877
|
+
if (wo.filter_defaultFilter && regex.iQuery.test( ts.getColumnData( table, wo.filter_defaultFilter, c.columns, true ) || '')) {
|
878
|
+
data.anyMatchFilter = ts.filter.defaultFilter( data.anyMatchFilter, ts.getColumnData( table, wo.filter_defaultFilter, c.columns, true ) );
|
879
|
+
// clear search filtered flag because default filters are not saved to the last search
|
880
|
+
searchFiltered = false;
|
881
|
+
}
|
882
|
+
// make iAnyMatchFilter lowercase unless both filter widget & core ignoreCase options are true
|
883
|
+
// when c.ignoreCase is true, the cache contains all lower case data
|
884
|
+
data.iAnyMatchFilter = !(wo.filter_ignoreCase && c.ignoreCase) ? data.anyMatchFilter : data.anyMatchFilter.toLocaleLowerCase();
|
885
|
+
}
|
886
|
+
|
887
|
+
// loop through the rows
|
888
|
+
for (rowIndex = 0; rowIndex < len; rowIndex++) {
|
889
|
+
|
890
|
+
data.cacheArray = norm_rows[rowIndex];
|
891
|
+
|
892
|
+
childRow = $rows[rowIndex].className;
|
893
|
+
// skip child rows & already filtered rows
|
894
|
+
if ( regex.child.test(childRow) || (searchFiltered && regex.filtered.test(childRow)) ) { continue; }
|
895
|
+
showRow = true;
|
896
|
+
// *** nextAll/nextUntil not supported by Zepto! ***
|
897
|
+
childRow = $rows.eq(rowIndex).nextUntil('tr:not(.' + c.cssChildRow + ')');
|
898
|
+
// so, if "table.config.widgetOptions.filter_childRows" is true and there is
|
899
|
+
// a match anywhere in the child row, then it will make the row visible
|
900
|
+
// checked here so the option can be changed dynamically
|
901
|
+
data.childRowText = (childRow.length && wo.filter_childRows) ? childRow.text() : '';
|
902
|
+
data.childRowText = wo.filter_ignoreCase ? data.childRowText.toLocaleLowerCase() : data.childRowText;
|
903
|
+
$cells = $rows.eq(rowIndex).children();
|
904
|
+
if (data.anyMatchFlag) {
|
905
|
+
// look for multiple columns "1-3,4-6,8"
|
906
|
+
columnIndex = ts.filter.multipleColumns( c, wo.filter_$anyMatch );
|
907
|
+
data.anyMatch = true;
|
908
|
+
data.rowArray = $cells.map(function(i){
|
909
|
+
if ( $.inArray(i, columnIndex) > -1 ) {
|
910
|
+
var txt;
|
911
|
+
if (data.parsed[i]) {
|
912
|
+
txt = data.cacheArray[i];
|
913
|
+
} else {
|
914
|
+
txt = this ? this.getAttribute( c.textAttribute ) || this.textContent || $(this).text() : '';
|
915
|
+
txt = $.trim( wo.filter_ignoreCase ? txt.toLowerCase() : txt );
|
916
|
+
if (c.sortLocaleCompare) {
|
917
|
+
txt = ts.replaceAccents(txt);
|
918
|
+
}
|
919
|
+
}
|
920
|
+
return txt;
|
921
|
+
}
|
922
|
+
}).get();
|
923
|
+
data.filter = data.anyMatchFilter;
|
924
|
+
data.iFilter = data.iAnyMatchFilter;
|
925
|
+
data.exact = data.rowArray.join(' ');
|
926
|
+
data.iExact = wo.filter_ignoreCase ? data.exact.toLowerCase() : data.exact;
|
927
|
+
data.cache = data.cacheArray.slice(0,-1).join(' ');
|
928
|
+
filterMatched = null;
|
929
|
+
$.each(ts.filter.types, function(type, typeFunction) {
|
930
|
+
if ($.inArray(type, noAnyMatch) < 0) {
|
931
|
+
matches = typeFunction( c, data );
|
932
|
+
if (matches !== null) {
|
933
|
+
filterMatched = matches;
|
934
|
+
return false;
|
935
|
+
}
|
936
|
+
}
|
937
|
+
});
|
938
|
+
if (filterMatched !== null) {
|
939
|
+
showRow = filterMatched;
|
940
|
+
} else {
|
941
|
+
if (wo.filter_startsWith) {
|
942
|
+
showRow = false;
|
943
|
+
columnIndex = c.columns;
|
944
|
+
while (!showRow && columnIndex > 0) {
|
945
|
+
columnIndex--;
|
946
|
+
showRow = showRow || data.rowArray[columnIndex].indexOf(data.iFilter) === 0;
|
947
|
+
}
|
948
|
+
} else {
|
949
|
+
showRow = (data.iExact + data.childRowText).indexOf(data.iFilter) >= 0;
|
950
|
+
}
|
951
|
+
}
|
952
|
+
data.anyMatch = false;
|
953
|
+
}
|
954
|
+
|
955
|
+
for (columnIndex = 0; columnIndex < c.columns; columnIndex++) {
|
956
|
+
data.filter = filters[columnIndex];
|
957
|
+
data.index = columnIndex;
|
958
|
+
|
959
|
+
// filter types to exclude, per column
|
960
|
+
excludeMatch = ( ts.getColumnData( table, wo.filter_excludeFilter, columnIndex, true ) || '' ).split(/\s+/);
|
961
|
+
|
962
|
+
// ignore if filter is empty or disabled
|
963
|
+
if (data.filter) {
|
964
|
+
data.cache = data.cacheArray[columnIndex];
|
965
|
+
// check if column data should be from the cell or from parsed data
|
966
|
+
if (wo.filter_useParsedData || data.parsed[columnIndex]) {
|
967
|
+
data.exact = data.cache;
|
968
|
+
} else {
|
969
|
+
val = $cells[columnIndex];
|
970
|
+
result = val ? $.trim( val.getAttribute( c.textAttribute ) || val.textContent || $cells.eq(columnIndex).text() ) : '';
|
971
|
+
data.exact = c.sortLocaleCompare ? ts.replaceAccents(result) : result; // issue #405
|
972
|
+
}
|
973
|
+
data.iExact = !regex.type.test(typeof data.exact) && wo.filter_ignoreCase ? data.exact.toLocaleLowerCase() : data.exact;
|
974
|
+
result = showRow; // if showRow is true, show that row
|
975
|
+
|
976
|
+
// in case select filter option has a different value vs text "a - z|A through Z"
|
977
|
+
ffxn = wo.filter_columnFilters ?
|
978
|
+
c.$filters.add(c.$externalFilters).filter('[data-column="'+ columnIndex + '"]').find('select option:selected').attr('data-function-name') || '' : '';
|
979
|
+
// replace accents - see #357
|
980
|
+
if (c.sortLocaleCompare) {
|
981
|
+
data.filter = ts.replaceAccents(data.filter);
|
982
|
+
}
|
983
|
+
|
984
|
+
val = true;
|
985
|
+
if (wo.filter_defaultFilter && regex.iQuery.test( ts.getColumnData( table, wo.filter_defaultFilter, columnIndex ) || '')) {
|
986
|
+
data.filter = ts.filter.defaultFilter( data.filter, ts.getColumnData( table, wo.filter_defaultFilter, columnIndex ) );
|
987
|
+
// val is used to indicate that a filter select is using a default filter; so we override the exact & partial matches
|
988
|
+
val = false;
|
989
|
+
}
|
990
|
+
// data.iFilter = case insensitive (if wo.filter_ignoreCase is true), data.filter = case sensitive
|
991
|
+
data.iFilter = wo.filter_ignoreCase ? (data.filter || '').toLocaleLowerCase() : data.filter;
|
992
|
+
fxn = ts.getColumnData( table, wo.filter_functions, columnIndex );
|
993
|
+
$cell = c.$headers.filter('[data-column="' + columnIndex + '"]:last');
|
994
|
+
hasSelect = $cell.hasClass('filter-select');
|
995
|
+
if ( fxn || ( hasSelect && val ) ) {
|
996
|
+
if (fxn === true || hasSelect) {
|
997
|
+
// default selector uses exact match unless "filter-match" class is found
|
998
|
+
result = ($cell.hasClass('filter-match')) ? data.iExact.search(data.iFilter) >= 0 : data.filter === data.exact;
|
999
|
+
} else if (typeof fxn === 'function') {
|
1000
|
+
// filter callback( exact cell content, parser normalized content, filter input value, column index, jQuery row object )
|
1001
|
+
result = fxn(data.exact, data.cache, data.filter, columnIndex, $rows.eq(rowIndex));
|
1002
|
+
} else if (typeof fxn[ffxn || data.filter] === 'function') {
|
1003
|
+
// selector option function
|
1004
|
+
result = fxn[ffxn || data.filter](data.exact, data.cache, data.filter, columnIndex, $rows.eq(rowIndex));
|
1005
|
+
}
|
1006
|
+
} else {
|
1007
|
+
filterMatched = null;
|
1008
|
+
// cycle through the different filters
|
1009
|
+
// filters return a boolean or null if nothing matches
|
1010
|
+
$.each(ts.filter.types, function(type, typeFunction) {
|
1011
|
+
if ($.inArray(type, excludeMatch) < 0) {
|
1012
|
+
matches = typeFunction( c, data );
|
1013
|
+
if (matches !== null) {
|
1014
|
+
filterMatched = matches;
|
1015
|
+
return false;
|
1016
|
+
}
|
1017
|
+
}
|
1018
|
+
});
|
1019
|
+
if (filterMatched !== null) {
|
1020
|
+
result = filterMatched;
|
1021
|
+
// Look for match, and add child row data for matching
|
1022
|
+
} else {
|
1023
|
+
data.exact = (data.iExact + data.childRowText).indexOf( ts.filter.parseFilter(c, data.iFilter, columnIndex, data.parsed[columnIndex]) );
|
1024
|
+
result = ( (!wo.filter_startsWith && data.exact >= 0) || (wo.filter_startsWith && data.exact === 0) );
|
1025
|
+
}
|
1026
|
+
}
|
1027
|
+
showRow = (result) ? showRow : false;
|
1028
|
+
}
|
1029
|
+
}
|
1030
|
+
$rows.eq(rowIndex)
|
1031
|
+
.toggle(showRow)
|
1032
|
+
.toggleClass(wo.filter_filteredRow, !showRow);
|
1033
|
+
if (childRow.length) {
|
1034
|
+
childRow.toggleClass(wo.filter_filteredRow, !showRow);
|
1035
|
+
}
|
1036
|
+
}
|
1037
|
+
}
|
1038
|
+
c.filteredRows += $rows.not('.' + wo.filter_filteredRow).length;
|
1039
|
+
c.totalRows += $rows.length;
|
1040
|
+
ts.processTbody(table, $tbody, false);
|
1041
|
+
}
|
1042
|
+
c.lastCombinedFilter = combinedFilters; // save last search
|
1043
|
+
c.lastSearch = filters;
|
1044
|
+
c.$table.data('lastSearch', filters);
|
1045
|
+
if (wo.filter_saveFilters && ts.storage) {
|
1046
|
+
ts.storage( table, 'tablesorter-filters', filters );
|
1047
|
+
}
|
1048
|
+
if (c.debug) {
|
1049
|
+
ts.benchmark("Completed filter widget search", time);
|
1050
|
+
}
|
1051
|
+
if (wo.filter_initialized) { c.$table.trigger('filterEnd', c ); }
|
1052
|
+
setTimeout(function(){
|
1053
|
+
c.$table.trigger('applyWidgets'); // make sure zebra widget is applied
|
1054
|
+
}, 0);
|
1055
|
+
},
|
1056
|
+
getOptionSource: function(table, column, onlyAvail) {
|
1057
|
+
table = $(table)[0];
|
1058
|
+
var cts,
|
1059
|
+
c = table.config,
|
1060
|
+
wo = c.widgetOptions,
|
1061
|
+
parsed = [],
|
1062
|
+
arry = false,
|
1063
|
+
source = wo.filter_selectSource,
|
1064
|
+
last = c.$table.data('lastSearch') || [],
|
1065
|
+
fxn = $.isFunction(source) ? true : ts.getColumnData( table, source, column );
|
1066
|
+
|
1067
|
+
if (onlyAvail && last[column] !== '') {
|
1068
|
+
onlyAvail = false;
|
1069
|
+
}
|
1070
|
+
|
1071
|
+
// filter select source option
|
1072
|
+
if (fxn === true) {
|
1073
|
+
// OVERALL source
|
1074
|
+
arry = source(table, column, onlyAvail);
|
1075
|
+
} else if ( fxn instanceof $ || ($.type(fxn) === 'string' && fxn.indexOf('</option>') >= 0) ) {
|
1076
|
+
// selectSource is a jQuery object or string of options
|
1077
|
+
return fxn;
|
1078
|
+
} else if ($.isArray(fxn)) {
|
1079
|
+
arry = fxn;
|
1080
|
+
} else if ($.type(source) === 'object' && fxn) {
|
1081
|
+
// custom select source function for a SPECIFIC COLUMN
|
1082
|
+
arry = fxn(table, column, onlyAvail);
|
1083
|
+
}
|
1084
|
+
if (arry === false) {
|
1085
|
+
// fall back to original method
|
1086
|
+
arry = ts.filter.getOptions(table, column, onlyAvail);
|
1087
|
+
}
|
1088
|
+
|
1089
|
+
// get unique elements and sort the list
|
1090
|
+
// if $.tablesorter.sortText exists (not in the original tablesorter),
|
1091
|
+
// then natural sort the list otherwise use a basic sort
|
1092
|
+
arry = $.grep(arry, function(value, indx) {
|
1093
|
+
return $.inArray(value, arry) === indx;
|
1094
|
+
});
|
1095
|
+
|
1096
|
+
if (c.$headers.filter('[data-column="' + column + '"]:last').hasClass('filter-select-nosort')) {
|
1097
|
+
// unsorted select options
|
1098
|
+
return arry;
|
1099
|
+
} else {
|
1100
|
+
// parse select option values
|
1101
|
+
$.each(arry, function(i, v){
|
1102
|
+
// parse array data using set column parser; this DOES NOT pass the original
|
1103
|
+
// table cell to the parser format function
|
1104
|
+
parsed.push({ t : v, p : c.parsers && c.parsers[column].format( v, table, [], column ) });
|
1105
|
+
});
|
1106
|
+
|
1107
|
+
// sort parsed select options
|
1108
|
+
cts = c.textSorter || '';
|
1109
|
+
parsed.sort(function(a, b){
|
1110
|
+
// sortNatural breaks if you don't pass it strings
|
1111
|
+
var x = a.p.toString(), y = b.p.toString();
|
1112
|
+
if ($.isFunction(cts)) {
|
1113
|
+
// custom OVERALL text sorter
|
1114
|
+
return cts(x, y, true, column, table);
|
1115
|
+
} else if (typeof(cts) === 'object' && cts.hasOwnProperty(column)) {
|
1116
|
+
// custom text sorter for a SPECIFIC COLUMN
|
1117
|
+
return cts[column](x, y, true, column, table);
|
1118
|
+
} else if (ts.sortNatural) {
|
1119
|
+
// fall back to natural sort
|
1120
|
+
return ts.sortNatural(x, y);
|
1121
|
+
}
|
1122
|
+
// using an older version! do a basic sort
|
1123
|
+
return true;
|
1124
|
+
});
|
1125
|
+
// rebuild arry from sorted parsed data
|
1126
|
+
arry = [];
|
1127
|
+
$.each(parsed, function(i, v){
|
1128
|
+
arry.push(v.t);
|
1129
|
+
});
|
1130
|
+
return arry;
|
1131
|
+
}
|
1132
|
+
},
|
1133
|
+
getOptions: function(table, column, onlyAvail) {
|
1134
|
+
table = $(table)[0];
|
1135
|
+
var rowIndex, tbodyIndex, len, row, cache, cell,
|
1136
|
+
c = table.config,
|
1137
|
+
wo = c.widgetOptions,
|
1138
|
+
arry = [];
|
1139
|
+
for (tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) {
|
1140
|
+
cache = c.cache[tbodyIndex];
|
1141
|
+
len = c.cache[tbodyIndex].normalized.length;
|
1142
|
+
// loop through the rows
|
1143
|
+
for (rowIndex = 0; rowIndex < len; rowIndex++) {
|
1144
|
+
// get cached row from cache.row (old) or row data object (new; last item in normalized array)
|
1145
|
+
row = cache.row ? cache.row[rowIndex] : cache.normalized[rowIndex][c.columns].$row[0];
|
1146
|
+
// check if has class filtered
|
1147
|
+
if (onlyAvail && row.className.match(wo.filter_filteredRow)) { continue; }
|
1148
|
+
// get non-normalized cell content
|
1149
|
+
if (wo.filter_useParsedData || c.parsers[column].parsed || c.$headers.filter('[data-column="' + column + '"]:last').hasClass('filter-parsed')) {
|
1150
|
+
arry.push( '' + cache.normalized[rowIndex][column] );
|
1151
|
+
} else {
|
1152
|
+
cell = row.cells[column];
|
1153
|
+
if (cell) {
|
1154
|
+
arry.push( $.trim( cell.getAttribute( c.textAttribute ) || cell.textContent || $(cell).text() ) );
|
1155
|
+
}
|
1156
|
+
}
|
1157
|
+
}
|
1158
|
+
}
|
1159
|
+
return arry;
|
1160
|
+
},
|
1161
|
+
buildSelect: function(table, column, arry, updating, onlyAvail) {
|
1162
|
+
table = $(table)[0];
|
1163
|
+
column = parseInt(column, 10);
|
1164
|
+
if (!table.config.cache || $.isEmptyObject(table.config.cache)) { return; }
|
1165
|
+
var indx, val, txt, t, $filters, $filter,
|
1166
|
+
c = table.config,
|
1167
|
+
wo = c.widgetOptions,
|
1168
|
+
node = c.$headers.filter('[data-column="' + column + '"]:last'),
|
1169
|
+
// t.data('placeholder') won't work in jQuery older than 1.4.3
|
1170
|
+
options = '<option value="">' + ( node.data('placeholder') || node.attr('data-placeholder') || wo.filter_placeholder.select || '' ) + '</option>',
|
1171
|
+
// Get curent filter value
|
1172
|
+
currentValue = c.$table.find('thead').find('select.' + ts.css.filter + '[data-column="' + column + '"]').val();
|
1173
|
+
// nothing included in arry (external source), so get the options from filter_selectSource or column data
|
1174
|
+
if (typeof arry === 'undefined' || arry === '') {
|
1175
|
+
arry = ts.filter.getOptionSource(table, column, onlyAvail);
|
1176
|
+
}
|
1177
|
+
|
1178
|
+
if ($.isArray(arry)) {
|
1179
|
+
// build option list
|
1180
|
+
for (indx = 0; indx < arry.length; indx++) {
|
1181
|
+
txt = arry[indx] = ('' + arry[indx]).replace(/\"/g, """);
|
1182
|
+
val = txt;
|
1183
|
+
// allow including a symbol in the selectSource array
|
1184
|
+
// "a-z|A through Z" so that "a-z" becomes the option value
|
1185
|
+
// and "A through Z" becomes the option text
|
1186
|
+
if (txt.indexOf(wo.filter_selectSourceSeparator) >= 0) {
|
1187
|
+
t = txt.split(wo.filter_selectSourceSeparator);
|
1188
|
+
val = t[0];
|
1189
|
+
txt = t[1];
|
1190
|
+
}
|
1191
|
+
// replace quotes - fixes #242 & ignore empty strings - see http://stackoverflow.com/q/14990971/145346
|
1192
|
+
options += arry[indx] !== '' ? '<option ' + (val === txt ? '' : 'data-function-name="' + arry[indx] + '" ') + 'value="' + val + '">' + txt + '</option>' : '';
|
1193
|
+
}
|
1194
|
+
// clear arry so it doesn't get appended twice
|
1195
|
+
arry = [];
|
1196
|
+
}
|
1197
|
+
|
1198
|
+
// update all selects in the same column (clone thead in sticky headers & any external selects) - fixes 473
|
1199
|
+
$filters = ( c.$filters ? c.$filters : c.$table.children('thead') ).find('.' + ts.css.filter);
|
1200
|
+
if (wo.filter_$externalFilters) {
|
1201
|
+
$filters = $filters && $filters.length ? $filters.add(wo.filter_$externalFilters) : wo.filter_$externalFilters;
|
1202
|
+
}
|
1203
|
+
$filter = $filters.filter('select[data-column="' + column + '"]');
|
1204
|
+
|
1205
|
+
// make sure there is a select there!
|
1206
|
+
if ($filter.length) {
|
1207
|
+
$filter[ updating ? 'html' : 'append' ](options);
|
1208
|
+
if (!$.isArray(arry)) {
|
1209
|
+
// append options if arry is provided externally as a string or jQuery object
|
1210
|
+
// options (default value) was already added
|
1211
|
+
$filter.append(arry).val(currentValue);
|
1212
|
+
}
|
1213
|
+
$filter.val(currentValue);
|
1214
|
+
}
|
1215
|
+
},
|
1216
|
+
buildDefault: function(table, updating) {
|
1217
|
+
var columnIndex, $header, noSelect,
|
1218
|
+
c = table.config,
|
1219
|
+
wo = c.widgetOptions,
|
1220
|
+
columns = c.columns;
|
1221
|
+
// build default select dropdown
|
1222
|
+
for (columnIndex = 0; columnIndex < columns; columnIndex++) {
|
1223
|
+
$header = c.$headers.filter('[data-column="' + columnIndex + '"]:last');
|
1224
|
+
noSelect = !($header.hasClass('filter-false') || $header.hasClass('parser-false'));
|
1225
|
+
// look for the filter-select class; build/update it if found
|
1226
|
+
if (($header.hasClass('filter-select') || ts.getColumnData( table, wo.filter_functions, columnIndex ) === true) && noSelect) {
|
1227
|
+
ts.filter.buildSelect(table, columnIndex, '', updating, $header.hasClass(wo.filter_onlyAvail));
|
1228
|
+
}
|
1229
|
+
}
|
1230
|
+
}
|
1231
|
+
};
|
1232
|
+
|
1233
|
+
ts.getFilters = function(table, getRaw, setFilters, skipFirst) {
|
1234
|
+
var i, $filters, $column, cols,
|
1235
|
+
filters = false,
|
1236
|
+
c = table ? $(table)[0].config : '',
|
1237
|
+
wo = c ? c.widgetOptions : '';
|
1238
|
+
if (getRaw !== true && wo && !wo.filter_columnFilters) {
|
1239
|
+
return $(table).data('lastSearch');
|
1240
|
+
}
|
1241
|
+
if (c) {
|
1242
|
+
if (c.$filters) {
|
1243
|
+
$filters = c.$filters.find('.' + ts.css.filter);
|
1244
|
+
}
|
1245
|
+
if (wo.filter_$externalFilters) {
|
1246
|
+
$filters = $filters && $filters.length ? $filters.add(wo.filter_$externalFilters) : wo.filter_$externalFilters;
|
1247
|
+
}
|
1248
|
+
if ($filters && $filters.length) {
|
1249
|
+
filters = setFilters || [];
|
1250
|
+
for (i = 0; i < c.columns + 1; i++) {
|
1251
|
+
cols = ( i === c.columns ?
|
1252
|
+
// "all" columns can now include a range or set of columms (data-column="0-2,4,6-7")
|
1253
|
+
wo.filter_anyColumnSelector + ',' + wo.filter_multipleColumnSelector :
|
1254
|
+
'[data-column="' + i + '"]' );
|
1255
|
+
$column = $filters.filter(cols);
|
1256
|
+
if ($column.length) {
|
1257
|
+
// move the latest search to the first slot in the array
|
1258
|
+
$column = ts.filter.getLatestSearch( $column );
|
1259
|
+
if ($.isArray(setFilters)) {
|
1260
|
+
// skip first (latest input) to maintain cursor position while typing
|
1261
|
+
if (skipFirst) { $column.slice(1); }
|
1262
|
+
if (i === c.columns) {
|
1263
|
+
// prevent data-column="all" from filling data-column="0,1" (etc)
|
1264
|
+
cols = $column.filter(wo.filter_anyColumnSelector);
|
1265
|
+
$column = cols.length ? cols : $column;
|
1266
|
+
}
|
1267
|
+
$column
|
1268
|
+
.val( setFilters[i] )
|
1269
|
+
.trigger('change.tsfilter');
|
1270
|
+
} else {
|
1271
|
+
filters[i] = $column.val() || '';
|
1272
|
+
// don't change the first... it will move the cursor
|
1273
|
+
if (i === c.columns) {
|
1274
|
+
// don't update range columns from "all" setting
|
1275
|
+
$column.slice(1).filter('[data-column*="' + $column.attr('data-column') + '"]').val( filters[i] );
|
1276
|
+
} else {
|
1277
|
+
$column.slice(1).val( filters[i] );
|
1278
|
+
}
|
1279
|
+
}
|
1280
|
+
// save any match input dynamically
|
1281
|
+
if (i === c.columns && $column.length) {
|
1282
|
+
wo.filter_$anyMatch = $column;
|
1283
|
+
}
|
1284
|
+
}
|
1285
|
+
}
|
1286
|
+
}
|
1287
|
+
}
|
1288
|
+
if (filters.length === 0) {
|
1289
|
+
filters = false;
|
1290
|
+
}
|
1291
|
+
return filters;
|
1292
|
+
};
|
1293
|
+
|
1294
|
+
ts.setFilters = function(table, filter, apply, skipFirst) {
|
1295
|
+
var c = table ? $(table)[0].config : '',
|
1296
|
+
valid = ts.getFilters(table, true, filter, skipFirst);
|
1297
|
+
if (c && apply) {
|
1298
|
+
// ensure new set filters are applied, even if the search is the same
|
1299
|
+
c.lastCombinedFilter = null;
|
1300
|
+
c.lastSearch = [];
|
1301
|
+
ts.filter.searching(c.table, filter, skipFirst);
|
1302
|
+
c.$table.trigger('filterFomatterUpdate');
|
1303
|
+
}
|
1304
|
+
return !!valid;
|
1305
|
+
};
|
1306
|
+
|
1307
|
+
})(jQuery);
|