Compare commits
981 commits
Author | SHA1 | Date | |
---|---|---|---|
7afe45f1e6 | |||
6e01e45dfb | |||
12c30629af | |||
3e763efac9 | |||
825b915ae8 | |||
8e48bf2943 | |||
be7a5e585f | |||
c44bef22a2 | |||
6dcebbf67e | |||
44cb6f9124 | |||
571c31e8da | |||
8633891fdb | |||
8eddc6f35c | |||
577fb0b90a | |||
88a5bfe425 | |||
cac2900444 | |||
5dad4dae8e | |||
134851525c | |||
9a66748bb8 | |||
c52004421f | |||
f53b392c59 | |||
c496b0916d | |||
25b89b19e7 | |||
8b4a05be02 | |||
1c4961a25b | |||
40daf54768 | |||
c43debc7ac | |||
3ac2603e47 | |||
185c119342 | |||
80c9b3d734 | |||
e8605e5e19 | |||
bd95afb3eb | |||
c16f885951 | |||
6be9ad491f | |||
447a3119aa | |||
2025aa4010 | |||
bdc03755e8 | |||
2dceed888e | |||
982769a020 | |||
4318826ead | |||
66b8fa9d94 | |||
79cea3e27f | |||
7af493da27 | |||
90c3b7f937 | |||
85dce5847a | |||
65863b635a | |||
3c001f52e2 | |||
e3957977b6 | |||
8923c4db94 | |||
c7d1a25467 | |||
ebdd817199 | |||
27365e25d4 | |||
2e960867ab | |||
3d35d53005 | |||
02bc5e3ae1 | |||
09c1f8ff52 | |||
6b339f7bd7 | |||
a1efe26a85 | |||
64e6c6597f | |||
9363a7a661 | |||
41e220efa9 | |||
64d3808a2b | |||
eb15788965 | |||
fede2f6e86 | |||
9738c8ed91 | |||
b285d01b54 | |||
37eed72f2c | |||
71103cd04b | |||
42a347550b | |||
bed04b5b9b | |||
43fc68df31 | |||
d116525f73 | |||
d7ff36514d | |||
0d0abf3206 | |||
4cd004e423 | |||
459829a214 | |||
b8c0c1bc3f | |||
5686e8a470 | |||
b04d4f55ed | |||
3004963058 | |||
3454187be6 | |||
1dac7736b1 | |||
2c7ad88346 | |||
6e41c4a8c6 | |||
859f6b1eeb | |||
7204c4b546 | |||
f29b59fdbd | |||
6cc887e806 | |||
f7ad9741ca | |||
323a80d1d7 | |||
fc46f0b4ae | |||
0471570985 | |||
20243b641f | |||
99722cbdb0 | |||
64cc6825c5 | |||
bcb541fcad | |||
1c3c1bbb0a | |||
bba009c505 | |||
48659ec1d5 | |||
1d32dcea7d | |||
0e1c617b06 | |||
5c705fe591 | |||
3c501e3aef | |||
78ae09c861 | |||
2e1af5efb3 | |||
da270df5d6 | |||
8a8419bf02 | |||
a6c24c7885 | |||
ed2fef999a | |||
47c66d71bb | |||
f99f850c4d | |||
10c06b5100 | |||
1cc810c341 | |||
c707978e19 | |||
32b541239e | |||
2963962250 | |||
bbec44cc15 | |||
352a700fb2 | |||
fae4ec9e07 | |||
226f18a6f6 | |||
1d58191127 | |||
1559c74fd9 | |||
2cd30b1bfb | |||
cd5e5589a4 | |||
5499093810 | |||
df54ddda33 | |||
80ebc0aa8c | |||
a5fa336a08 | |||
36d4f85f6c | |||
e886a6f1a8 | |||
7204746e5f | |||
ecd33d0631 | |||
f97394fc32 | |||
eef0ab3bbd | |||
c48b48f924 | |||
d699d02914 | |||
09b3f7875d | |||
9c54974338 | |||
b89a52fb77 | |||
ef46fd4295 | |||
8d8c8ae74c | |||
fafa983dac | |||
a19c5a106f | |||
1e391233dc | |||
aade342976 | |||
0443442916 | |||
ed4979331c | |||
0e1a1ff3d1 | |||
e71b3a5c93 | |||
a22b009c57 | |||
f6a9a058c9 | |||
f7c231b99f | |||
6f4e5a91ab | |||
f405a45d79 | |||
fbe43b1db4 | |||
e13201733d | |||
002c46297e | |||
017337b2e2 | |||
51679ad61b | |||
bc34e8b187 | |||
58e90cb766 | |||
3243fad4da | |||
ea2a8be8b6 | |||
5e1fe901b7 | |||
c001e90617 | |||
a446c0afc0 | |||
ff88f6c7d7 | |||
d46265eb57 | |||
3057b1949c | |||
bca2cee175 | |||
077eb3fe87 | |||
90e4367951 | |||
589b356bc6 | |||
7664e054e5 | |||
2dcad20d0c | |||
86369ff116 | |||
f1bdd8997f | |||
eefbf3fe41 | |||
0b455e2ec4 | |||
de6745821f | |||
f79234ea7e | |||
8bcf731d3c | |||
5247493035 | |||
69a1fd3ee9 | |||
9c80252935 | |||
c22f9981e6 | |||
04012e5a97 | |||
02997076d4 | |||
9117097750 | |||
e3f9851848 | |||
f1febe1688 | |||
cf9a566e92 | |||
34e8b4099b | |||
495b64ecf0 | |||
0a69a99dc5 | |||
2d9671e10d | |||
0f380c03da | |||
df85babf34 | |||
131280c1a3 | |||
372473227c | |||
cb7fc0d4fa | |||
c3b5ef529c | |||
89b1814c2d | |||
9a1202c0b5 | |||
95475b022d | |||
8b1d3a6e23 | |||
6b2fbd1eef | |||
8d1dc88f6a | |||
34c56e5765 | |||
87ee312fc5 | |||
df0221d5e7 | |||
528d587a80 | |||
b8bdff83aa | |||
d40244b7e5 | |||
ec7799b57b | |||
fa28abbee8 | |||
e06dfbdc35 | |||
64f56c22a4 | |||
ecfa6c64ad | |||
37f6db7025 | |||
bfa440b265 | |||
bf9cbd0a69 | |||
967ae801b4 | |||
066c5fee56 | |||
807c191a01 | |||
9382a07c7b | |||
8b09b6feab | |||
d733766621 | |||
ceba24d727 | |||
c25cae1679 | |||
04342ac316 | |||
0ad57767f8 | |||
fd56dabea8 | |||
0b11de4f90 | |||
bbd0ce06ce | |||
500344c64a | |||
5845f8c52d | |||
d232f64fa5 | |||
53a77d1b97 | |||
44ce01be4b | |||
1f17364a00 | |||
a437dad841 | |||
99b34c07b4 | |||
ff977281c7 | |||
bf667cdd6f | |||
94d6d59593 | |||
1fc5fa5f8a | |||
34d35a3495 | |||
9750b07e06 | |||
bfe6b6dc2c | |||
67b1d4b65e | |||
c7482bec2d | |||
b38bc836ee | |||
79d06ff09e | |||
bd6afe484c | |||
6560392771 | |||
599717ae14 | |||
e96273e95f | |||
dcc0a43258 | |||
31ece0ff81 | |||
db0c95e197 | |||
2945c27ae9 | |||
786bb519b0 | |||
db1ecaec42 | |||
3f74756d58 | |||
dca79c8391 | |||
dd6de7f219 | |||
4e222fd98c | |||
e45c7ce863 | |||
b45815cfc7 | |||
481c7c3970 | |||
6b47928c92 | |||
259a88d0c5 | |||
91ec79d226 | |||
6630a87263 | |||
4d4d1f4212 | |||
7631b51f3d | |||
5bde707a0c | |||
2cea817247 | |||
68acd1fcb7 | |||
4d424d1d7e | |||
50fda2a900 | |||
335f97a9de | |||
a7e0f235a3 | |||
d6a79e14fc | |||
7e780f3463 | |||
439f183d95 | |||
10b80612fe | |||
72327b84cc | |||
ba5323ab3b | |||
a9c0fd9c2f | |||
e6049975ee | |||
62a55c8d95 | |||
5d4def11fc | |||
13eb677b8e | |||
d48bbeaab5 | |||
6fec4947c4 | |||
d3634bce5f | |||
6813debb6e | |||
b14667a345 | |||
2f825a25b4 | |||
cca223c4d4 | |||
09243a9bdb | |||
24e0f3d184 | |||
d70cf23624 | |||
2d963e782f | |||
c100f0f63f | |||
875fdc2603 | |||
4bdf873ec8 | |||
d1875f19f2 | |||
5254983778 | |||
7e13d59d0f | |||
a66402e139 | |||
8cb8f74fa3 | |||
c5775ae8aa | |||
a10d3a2830 | |||
2e089a1823 | |||
3231da9760 | |||
7533499c40 | |||
80cf351eab | |||
d790156373 | |||
70d791f41c | |||
f1f28b51cb | |||
8a94f2ffd2 | |||
a0f9f28397 | |||
71292db999 | |||
42a40a295b | |||
7a90cb5b13 | |||
7eecd95122 | |||
a45217bc9f | |||
d14a0a359a | |||
e09bab000b | |||
efffa40248 | |||
50afb94340 | |||
d35ea7cb26 | |||
52705e56f7 | |||
b34e5696f3 | |||
606cc00393 | |||
c728209c92 | |||
2e3bdda8bc | |||
7bc7b16cfc | |||
c5bc7cbb33 | |||
ec6d0779a1 | |||
bcbfe5251f | |||
453ff1c59c | |||
948055da09 | |||
f534a8b23f | |||
6c23ae883f | |||
55e4d6b511 | |||
2d5f7401f9 | |||
00143a686b | |||
46f7028580 | |||
313336ab5f | |||
89851eae3b | |||
e49538c442 | |||
cb0aa73a6d | |||
9b89b78f38 | |||
1a32ef216b | |||
bcf7189bbc | |||
9a55c86e5f | |||
c710fa3585 | |||
a5164f4ba5 | |||
0babe0249e | |||
ca4f0f2afa | |||
00150bc9e1 | |||
46b21b4a20 | |||
89b0cf843a | |||
946f600260 | |||
0cd8fb2aa3 | |||
ad8c86c599 | |||
bf1bdc5a0e | |||
ed01c6e201 | |||
b4905f7703 | |||
2cc28e8072 | |||
521d02c374 | |||
fe51fdecb8 | |||
bfa23c3b6e | |||
2abd81906b | |||
f1e046e331 | |||
ea8ca2a125 | |||
6bc6c8a8eb | |||
c0520dd4bb | |||
3d7945b09c | |||
84c964bf13 | |||
c7d6de72f7 | |||
81f31a3106 | |||
3995dee394 | |||
7540169fcd | |||
4bff89b70c | |||
c8df8992b3 | |||
4889b29cda | |||
b3101a8871 | |||
bf2f2bd747 | |||
b08b517fa7 | |||
f2c5edbfb8 | |||
ca6ced712a | |||
0d95699fa6 | |||
4378141d7d | |||
0631fd2ef7 | |||
7cbb38d9ba | |||
edd2494899 | |||
49eb4b7bd6 | |||
5d4627277a | |||
907ccd5e4a | |||
c26e6d3c4b | |||
e1106f23a8 | |||
7909cde78b | |||
bc416e0e39 | |||
934ba4add0 | |||
73fc78e9e3 | |||
c1000b7684 | |||
a34009f1e9 | |||
51e27c4a2f | |||
2fee4d95f2 | |||
5b6919ba39 | |||
974bb3cc77 | |||
1a1560d636 | |||
2d3f67d504 | |||
81c593382a | |||
62cab3ac2c | |||
4ac92a3e73 | |||
da8977433d | |||
cde2e8a326 | |||
7b03e23f3c | |||
dc3d40b12c | |||
48674c3b31 | |||
a92acfa615 | |||
be1bdf72cd | |||
b6d9bddc6f | |||
025717f61c | |||
e09d30c2cd | |||
39bbe6a19a | |||
2f7b650f5b | |||
85092bbd1c | |||
3bed943a42 | |||
2452a0bd9e | |||
98aca44cb9 | |||
33ed3cc194 | |||
2da8a56659 | |||
71d5cd7a49 | |||
05c2d6b666 | |||
50f4a0014b | |||
07eebbd528 | |||
9ccc4dbfd4 | |||
9d184df49a | |||
d664ebb780 | |||
0226300af8 | |||
1e8e4a6467 | |||
0ec96ed520 | |||
11d01b1661 | |||
2d0219b54d | |||
317d795974 | |||
f2f853c517 | |||
9468602f3d | |||
cc0932f5f7 | |||
f97ff3a52d | |||
8575d0ea68 | |||
d274876976 | |||
a9737da2f6 | |||
62964c2478 | |||
71e7012ba0 | |||
4a3fbbb736 | |||
ee128f9d80 | |||
9b29d1632e | |||
1946d75125 | |||
83ff0aab79 | |||
8dbc307a39 | |||
c7eefc8ddc | |||
0e2d17714d | |||
650959237b | |||
4a10876a96 | |||
d5ef7afa1e | |||
4cf9a4081d | |||
c1e26349ef | |||
16b2997a71 | |||
ab273644e5 | |||
731909a267 | |||
7f75e23b1b | |||
55eadd72eb | |||
4855c9064b | |||
032bdd53b0 | |||
f73aa02733 | |||
029f2c35e8 | |||
52a514088b | |||
c7756d8d58 | |||
054423f10d | |||
cccc4b71cf | |||
db2bb7de18 | |||
740cce0551 | |||
f6a93f7f00 | |||
b6db436bca | |||
158c8835f9 | |||
7a2bc50a56 | |||
74cea36219 | |||
2deeb5d57a | |||
619c272469 | |||
7862bc68d0 | |||
e2e8331a08 | |||
3134ee804b | |||
1c930a0184 | |||
51f188e63f | |||
2ca11374a0 | |||
79367df924 | |||
fe384896d5 | |||
f8c5d081d4 | |||
3ff774d78f | |||
33ae62cab7 | |||
8be5990a02 | |||
88d31090b4 | |||
fbfa2fdf96 | |||
f447199905 | |||
be67034e02 | |||
a01c914f0a | |||
c288fe44ce | |||
dc05239d57 | |||
42fb561866 | |||
95c8206eb7 | |||
26beced5c7 | |||
cf7a53354d | |||
653b1ce006 | |||
d69f60aced | |||
949f21f446 | |||
722c420dc6 | |||
590dd8316c | |||
376102c801 | |||
a243c96dd3 | |||
631f6bdb5c | |||
fc089f1b73 | |||
dfa93b378b | |||
0affe2c187 | |||
8231252f34 | |||
75faca6e8d | |||
4a6ae393a5 | |||
0e669bcc45 | |||
08fb37b982 | |||
bde954e570 | |||
78f6df704b | |||
7cf9be1342 | |||
86a650ff6d | |||
13adb6fb3a | |||
f2d54839c3 | |||
9abc9802bd | |||
82a03d428c | |||
0b5160b45d | |||
fca1fcf23c | |||
e311aa36f1 | |||
9b54c10bc6 | |||
eb62703e50 | |||
81062452e2 | |||
6e72912c90 | |||
52999f000e | |||
6999b0c7f9 | |||
e205a6b15a | |||
fbeb52bda2 | |||
465b216e01 | |||
ff622ea585 | |||
6a0179681b | |||
68adc86cbc | |||
e661005ff8 | |||
a861cff760 | |||
12e4052a9f | |||
e9441fe1d5 | |||
175a23934f | |||
a757bd02fa | |||
b03ec21a28 | |||
5f66df7e6a | |||
5a10841f84 | |||
2ef37ed58d | |||
828a42f938 | |||
4428f7b48b | |||
e855fc5bbd | |||
a7f0f442d8 | |||
2a424e7f6f | |||
50f537fbfc | |||
7ba78c94ae | |||
763fbcc86e | |||
3abca0ef22 | |||
7888a8ee24 | |||
abec798b27 | |||
eb9ac51b5a | |||
d86601e1a1 | |||
fd48f8c3ec | |||
fa28d6ce17 | |||
4250fda098 | |||
4f9e3dee01 | |||
e05f0b2c73 | |||
7c7a5fc1b7 | |||
a54abc6ea1 | |||
e4ed4d5567 | |||
29c4c107bc | |||
0b242aadc7 | |||
44a39e7e1f | |||
e82640c7da | |||
f89bf8859b | |||
6b5c432ce7 | |||
409a3bc6eb | |||
d406a09a9e | |||
36b045c6d2 | |||
59902adcf5 | |||
41842eede4 | |||
62c7cb1918 | |||
d23f6f4cc8 | |||
80f1fc3371 | |||
ea87137a82 | |||
aa8df36e28 | |||
0947a87d6c | |||
72c47a9b0d | |||
f0c9a800a5 | |||
ad5405e699 | |||
cc5c8b9ed5 | |||
04d8fd3d14 | |||
fb0a6a0a01 | |||
78059d1bf8 | |||
3352765fed | |||
f96166be48 | |||
3a878c9e90 | |||
7b8c3c7412 | |||
9f6751533e | |||
82e9b27598 | |||
237fff1351 | |||
e00c560c80 | |||
407e772fdb | |||
645437b5e3 | |||
7f6221cd8e | |||
d9c770855a | |||
70a7d6540c | |||
acf6678619 | |||
f2242c4e57 | |||
8fb7c76e2d | |||
72e78fd4bc | |||
2f1e065571 | |||
761c3730ed | |||
cdf7b926ba | |||
d3addb09c4 | |||
471203b0a6 | |||
7cac5b105e | |||
e8c3f27d93 | |||
97d2ed0d64 | |||
7278aab649 | |||
e33e634d0d | |||
92af5d8842 | |||
140f949190 | |||
c5d043f85b | |||
06cea02484 | |||
287b7413e3 | |||
3c330ac4f0 | |||
51bc126c8d | |||
edcd166cd8 | |||
aaead0e5ca | |||
990ef45e45 | |||
9ae607d802 | |||
3c07d8dcc4 | |||
bb8b8a9d50 | |||
08303e93ca | |||
01bdad6368 | |||
8145ef116f | |||
ab9ff96684 | |||
9858ac84c1 | |||
c6b876d7f9 | |||
05f1c62ad0 | |||
bf631efd24 | |||
f86e73b374 | |||
7cd85bbd34 | |||
38424bfdca | |||
9dde61e7a0 | |||
5fc1c78d1b | |||
2cbfc0b1ec | |||
5043378c2c | |||
92e743a67e | |||
9262e7fd13 | |||
5adfa8a1be | |||
0d64d5aa00 | |||
11e15f0482 | |||
07355d7442 | |||
e8392b8fa5 | |||
bfcf221a91 | |||
c29cbda581 | |||
5294a31e30 | |||
ed7ead95d5 | |||
aee6063410 | |||
45e78777e9 | |||
1de6a6288c | |||
4b7af32eb1 | |||
955c49a2c1 | |||
eeb5afe97b | |||
f5d818c91a | |||
0a38e422b8 | |||
f059faecbc | |||
195918f088 | |||
022e52e31c | |||
b249399b1d | |||
132d46fed5 | |||
49e16a3631 | |||
06c3b3f025 | |||
39e4340dee | |||
cb3a633807 | |||
b0bc11007f | |||
7781bcfe4b | |||
308ea2ce25 | |||
d66c7906c0 | |||
a0bf340378 | |||
1b8bb8a9ea | |||
25101b278a | |||
5c4c8c0fe9 | |||
7a861eabee | |||
b8f549a432 | |||
5a82498d60 | |||
890d004c3c | |||
c7c20cdacf | |||
3f2bc41176 | |||
70779f192c | |||
6fc121a314 | |||
29960cada8 | |||
6dc1c9e53b | |||
5b7b1527d5 | |||
6bfccca291 | |||
39c8933a3f | |||
5411843f9f | |||
88b0dc5215 | |||
2a8571d940 | |||
9882a99417 | |||
2f8fb874d8 | |||
5f229311f0 | |||
4a88f6cc56 | |||
151ec256fa | |||
37c90ed12b | |||
c21863c71a | |||
508fc8fd1c | |||
9f6b3160fc | |||
650de1d7e3 | |||
68ebfcd537 | |||
4cc870b1bb | |||
a15fb5719e | |||
6926ef3b9a | |||
76a61d5a01 | |||
f24c5da431 | |||
b9ab9582a8 | |||
f9426c9ed0 | |||
067cce1d7b | |||
b03445ea46 | |||
9d7f49dda9 | |||
dfd78be5b3 | |||
f0e2ec6a8a | |||
06ed30de3b | |||
5cfc6c573b | |||
ec940d428a | |||
4f593cb2d3 | |||
611fc1ba24 | |||
4f7249bfa4 | |||
8f8e468278 | |||
23916b753a | |||
e8ba629217 | |||
ef2c2917db | |||
6daed0f4a8 | |||
8624920e2c | |||
e303cccd2a | |||
88c4f2d8d0 | |||
41feee7a22 | |||
c0101f45b5 | |||
c49eb2e50f | |||
238af84a97 | |||
87d166c4a4 | |||
d05a3c4dc0 | |||
a2ccc2668a | |||
e93df29cc0 | |||
aafe5da783 | |||
35174fb849 | |||
1905d89a75 | |||
d566d6a315 | |||
08529d0d59 | |||
ce946b9aed | |||
f7aa5cd65c | |||
d9c0fd6d0b | |||
41db8d67df | |||
7a5985ba8f | |||
574e2c3761 | |||
90b5e3cdca | |||
b062d44b87 | |||
ca1c801d64 | |||
c3a6c61739 | |||
5a496f228a | |||
e5f8013034 | |||
a1581a6ea1 | |||
f81ce3f533 | |||
d3cece2424 | |||
7351798ea4 | |||
ccc7c27032 | |||
e17b34d98a | |||
8cb947a6e5 | |||
00b339954b | |||
2fa3ef4140 | |||
fe3e2dcb67 | |||
f9bd89af04 | |||
2742716de4 | |||
b3910ca837 | |||
b608d8c778 | |||
2e3e68bb6f | |||
787f97eeb2 | |||
9a7e8f6982 | |||
44af1dc552 | |||
8c9a033c67 | |||
0d6530fe8d | |||
b91cf53ac1 | |||
7e5cd343d2 | |||
b15f3ed396 | |||
385317eeff | |||
2aecbfd424 | |||
47d350470c | |||
4acb352edc | |||
ff99b14f6b | |||
51f8521ddd | |||
0fcfe0208d | |||
ed164cc64f | |||
c773927945 | |||
77e606cf12 | |||
dcfe5d44c7 | |||
1f5ad88a39 | |||
b4a1d207b7 | |||
5ea5a88560 | |||
98f230e14f | |||
c2fef2aac8 | |||
995eda0498 | |||
d1daa51391 | |||
9787edf857 | |||
926d4e80b5 | |||
12af93cd17 | |||
284da0fde1 | |||
dc5b7ab41a | |||
1d9c1a435b | |||
a3716371f8 | |||
b2700c9972 | |||
2cc5b597ec | |||
9d8744392a | |||
231b8bffaa | |||
c4a1c47176 | |||
222587f1f3 | |||
0c89a3eef9 | |||
90b3f21332 | |||
b802d7bd50 | |||
312d8b1b9c | |||
556bf1c264 | |||
c1f0d71bcb | |||
c9c4d351a1 | |||
7e779a0242 | |||
72dbf6ba27 | |||
8af1cab7ff | |||
ed30bc7d93 | |||
f41f268975 | |||
7aaf192386 | |||
c75fbe169c | |||
8481b69061 | |||
8af59ef676 | |||
9e5216ebc1 | |||
b6b798b548 | |||
4711b71b80 | |||
6b43172199 | |||
bdb728a817 | |||
7ef31a4c62 | |||
47deeb1893 | |||
dd6d03a521 | |||
ec36d4c39d | |||
f9f99798e4 | |||
9b93b2fc26 | |||
558236a6ac | |||
0f337c8fe9 | |||
11ca10cdfd | |||
5dcd756414 | |||
54866f958f | |||
e54ac4b84f | |||
d7da92f9e4 | |||
5adf82abc7 | |||
8dee7d0cfb | |||
143cbffd63 | |||
0b1f4b67f5 | |||
d096de8852 | |||
b1a67d8a80 | |||
87fb91cea5 | |||
4b1146c385 | |||
7b2b040cc9 | |||
5d8f81ab78 | |||
5cebe6c217 | |||
29a8265efd | |||
d5ede81ba7 | |||
2d60de24b6 | |||
7ede842828 | |||
99a814db94 | |||
f44ce0678d | |||
a3b9d0e37c | |||
606d2e0fed | |||
afa4d68563 | |||
0cd9854634 | |||
5e7ff39b04 | |||
27a083444f | |||
57548bd3c0 | |||
873d79c58a | |||
d7413372f8 | |||
0ed1ac7d1e | |||
f59b0f056b | |||
061270774c | |||
a5178ecace | |||
f52f52f0ae | |||
88a6ed17fc | |||
eb5a733f03 | |||
0b95fdc79d | |||
7a78e9d980 | |||
fd9c257e52 | |||
782073fa14 | |||
f73f3d62d9 | |||
cbe5d8ef71 | |||
777d8df76e | |||
a50e0fa4bc | |||
7370173811 | |||
9db430c1d1 | |||
3e977fa6e5 | |||
f4960e250b | |||
e8a2ada208 | |||
ff975005b4 | |||
f6a2377be9 | |||
3ff52002e4 | |||
15c2035064 | |||
b99405997a | |||
df6a18b3d9 | |||
ad3335e488 | |||
0644368cd6 | |||
adb9539ee3 | |||
904e47957d | |||
eeffb5b102 | |||
597be6faed | |||
21305e2e62 | |||
d9f28e4908 | |||
85c62a7bff | |||
0b72eb55b0 | |||
3299742898 | |||
94ea4a9d69 | |||
81d7a529ff | |||
4af3db82df | |||
c4c90e3c15 | |||
2eedc8dd60 | |||
111a4dd246 | |||
d57ede865f | |||
ef7abbf0d6 | |||
a885c8caab | |||
9cbc364a92 | |||
f5a989d977 | |||
32fa832a6e | |||
66f36a7531 | |||
c5b4251039 | |||
d26ed8e2b9 | |||
288af8b94e | |||
5d0671f445 | |||
95236a22ae | |||
715a768203 | |||
028932e53c | |||
42fb664228 | |||
fedf365400 | |||
0f0ea9f2c4 | |||
e19fc2839e | |||
83de328c2b | |||
b6a4a35bfa | |||
b0f62e5015 | |||
aa457177c3 | |||
1c4c21b085 | |||
e276729f1d | |||
b882404fe3 | |||
e73471e10a | |||
07ba2b7718 | |||
6f1742d412 | |||
91b72ed3a2 | |||
36e6a92fb5 | |||
8b0bad54cc | |||
0be164c99f | |||
8fa502b2c4 | |||
494e4656ef | |||
033a6516cf | |||
bfe7293619 | |||
7ecb53a701 | |||
a0707554bb | |||
d656ec90b0 | |||
c70aa29523 | |||
a3c4f2158b | |||
035801dc7e |
14
.esdoc.json
Normal file
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"source": "src",
|
||||
"destination": "docs/docs",
|
||||
"plugins": [{
|
||||
"name": "esdoc-standard-plugin",
|
||||
"option": {
|
||||
"lint": {"enable": false},
|
||||
"accessor": {
|
||||
"access": ["public", "protected"],
|
||||
"autoPrivate": true
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
11
.eslintrc
|
@ -1,18 +1,20 @@
|
|||
{
|
||||
"parser": "babel-eslint",
|
||||
"parserOptions": {
|
||||
"sourceType": "module"
|
||||
"sourceType": "module",
|
||||
"ecmaFeatures": { "modules": true }
|
||||
},
|
||||
"rules": {
|
||||
"max-len": [2, 80, 2, {"ignoreUrls": true}],
|
||||
"indent": [2, 4, {"SwitchCase": 1}],
|
||||
"semi": ["error", "always"],
|
||||
"no-trailing-spaces": 2,
|
||||
"no-multi-spaces": 2,
|
||||
"array-bracket-spacing": 2,
|
||||
"keyword-spacing": ["error", { "after": true, "before": true }],
|
||||
"max-depth": [2, 7],
|
||||
"max-statements": [2, 150],
|
||||
"complexity": [2, 83],
|
||||
"max-statements": [2, 133],
|
||||
"complexity": [2, 45],
|
||||
"no-unused-vars": 2,
|
||||
"no-eval": 2,
|
||||
"no-underscore-dangle": 0,
|
||||
|
@ -24,9 +26,6 @@
|
|||
"new-cap": 2,
|
||||
"radix": [2, "always"]
|
||||
},
|
||||
"ecmaFeatures": {
|
||||
"modules": true
|
||||
},
|
||||
"env": {
|
||||
"es6": true,
|
||||
"browser": true,
|
||||
|
|
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
|
@ -0,0 +1,38 @@
|
|||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**Reproduction steps:**
|
||||
Steps to reproduce the behavior:
|
||||
1. [First Step]
|
||||
2. [Second Step]
|
||||
3. [Other Steps...]
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Observed behavior:**
|
||||
A clear and concise description of observed behavior.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
![Screenshots and GIFs which follow reproduction steps to demonstrate the problem](url)
|
||||
|
||||
**TableFilter version:** [Enter TableFilter version here]
|
||||
**Browser and version:** [Enter Browser name and version here]
|
||||
**OS and version:** [Enter OS name and version here]
|
||||
**Device:** [e.g. iPhone6]
|
||||
|
||||
**Additional information/context:**
|
||||
Add any other context about the problem here.
|
||||
|
||||
* Problem started happening recently, didn't happen in an older version of TableFilter: [Yes/No] if yes [version here]
|
||||
* Problem can be reliably reproduced, doesn't happen randomly: [Yes/No]
|
36
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
|
@ -0,0 +1,36 @@
|
|||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Enhancement/feature description**
|
||||
[A clear and concise description of what the problem is. Ex. I'm always frustrated when ...]
|
||||
|
||||
**Steps which explain the enhancement/feature**
|
||||
1. [First Step]
|
||||
2. [Second Step]
|
||||
3. [Other Steps...]
|
||||
|
||||
**Current and suggested behavior**
|
||||
[Describe current and suggested behavior here]
|
||||
|
||||
**Why would the enhancement be useful to most users**
|
||||
[Explain why the enhancement would be useful to most users]
|
||||
|
||||
**Screenshots and GIFs**
|
||||
![Screenshots and GIFs which demonstrate the steps or part of TableFilter the enhancement suggestion is related to](url)
|
||||
|
||||
**Additional context**
|
||||
[Add any other context or screenshots about the feature request here.]
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**TableFilter Version:** [Enter TableFilter version here]
|
||||
**Browser and version:** [Enter Browser name and version here]
|
||||
**OS and version:** [Enter OS name and version here]
|
||||
**Device:** [e.g. iPhone6]
|
17
.github/ISSUE_TEMPLATE/support.md
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
---
|
||||
name: Support
|
||||
about: I need support with TableFilter
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
For usage and support questions, please check out resources below, you might find an answer:
|
||||
|
||||
- https://www.tablefilter.com/
|
||||
- https://www.tablefilter.com/docs/
|
||||
- https://www.tablefilter.com/examples.html
|
||||
- https://github.com/koalyptus/TableFilter/wiki/
|
||||
- https://github.com/koalyptus/TableFilter/issues?q=is%3Aissue+is%3Aclosed
|
||||
- https://codepen.io/koalyptus/pens/public/
|
2
.gitignore
vendored
|
@ -1,10 +1,10 @@
|
|||
demos
|
||||
docs
|
||||
node_modules
|
||||
report
|
||||
.grunt
|
||||
|
||||
npm-debug.log
|
||||
*.js.map
|
||||
.codio
|
||||
.settings
|
||||
.vscode
|
||||
|
|
46
.travis.yml
|
@ -1,13 +1,53 @@
|
|||
language: node_js
|
||||
node_js:
|
||||
- '4.4.5'
|
||||
- '8.9.4'
|
||||
before_script:
|
||||
- npm install grunt-cli -g
|
||||
- npm install grunt-cli -g
|
||||
- npm install codecov -g
|
||||
script:
|
||||
- grunt test deploy
|
||||
- npm test
|
||||
- npm run codecov
|
||||
- npm run build:all
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- /^greenkeeper/.*$/
|
||||
before_deploy:
|
||||
- git config --global user.email "maxgug@hotmail.com"
|
||||
- git config --global user.name "koalyptus"
|
||||
- export GIT_TAG=$(git describe --tags --abbrev=0)
|
||||
- echo -n $GIT_TAG
|
||||
- export NEW_GIT_TAG=$(node -p -e "require('./package.json').version")
|
||||
- echo -n $NEW_GIT_TAG
|
||||
- |
|
||||
if [ $GIT_TAG != $NEW_GIT_TAG ]; then
|
||||
git tag $NEW_GIT_TAG -am "Generated tag from TravisCI build $TRAVIS_BUILD_NUMBER"
|
||||
fi
|
||||
deploy:
|
||||
- provider: releases
|
||||
on:
|
||||
branch: master
|
||||
api_key: $GITHUB_OAUTH_ACCESS_TOKEN
|
||||
skip_cleanup: true
|
||||
name: $NEW_GIT_TAG
|
||||
- provider: s3
|
||||
on:
|
||||
branch: master
|
||||
access_key_id: $AWS_ACCESS_KEY_ID
|
||||
secret_access_key: $AWS_SECRET_ACCESS_KEY
|
||||
region: $AWS_DEFAULT_REGION
|
||||
bucket: www.tablefilter.com
|
||||
skip_cleanup: true
|
||||
local_dir: dist
|
||||
- provider: s3
|
||||
on:
|
||||
branch: master
|
||||
access_key_id: $AWS_ACCESS_KEY_ID
|
||||
secret_access_key: $AWS_SECRET_ACCESS_KEY
|
||||
region: $AWS_DEFAULT_REGION
|
||||
bucket: www.tablefilter.com
|
||||
skip_cleanup: true
|
||||
local_dir: docs
|
||||
env:
|
||||
global:
|
||||
- secure: A1G8GvJkV0rjy7XCTVdOpTHy3xaoSZZAbMWhI+ikrqBqd8mRz+sB71FhRusouTcYdsT5VfF9Io2doS8LKAeP0TNC34Pp0uvjtsvarzn8a/oNEOuqR3Ub0ws2bmbZIZc+wOpgErKOj1H1QSJAUpd6ZjIuEAbOVXlhGBJz3zUCmcpRDh32CpFKC62oFWeGlvttxPciLLzBfKgkVKEGhPtdGP/xCHL1MCQptYVHZiXwWsaIQ5wHFO6KCVlRrPgdfOL+Yce3mT02iXH6ZjW6U6zA6vYQVQZVD873AkU5RmirYblW+jW1wdvu4UXI71lSH6Z3uXRVnrw1b0TsLVTjP9ZUbCtkTHtLbxYzeDjEukxKoCjpAppIhOtaNIxrdA8oKJAabQYp5X+QK6lkosy0zdT5u2B1+g8unZhsf0y//7lgLUe04iQ7sc1Q6AHiiEGtByaXg4BHNW53bUfKgNnbV4+IbXf8rz5wWOxL2/yWAU/GoiszjqRQfajAXCpSf6SyMjXjhhvQdeFn+Cz6FwdtaxH+tOIY0Hq9Gqy1xrLIkv/httd3O+AbhLrU1c/M0MwlFQue7GeJb7ZyF3KsK7bXvoz2dEqvzHd98NZXiQEqFXCIs77uVh4eZMoYrbEyrkOAgkUZNQYhHh9fuvfynJ/zgUvyA0v3GUvBebq3ybYKD/vqX7s=
|
||||
|
|
128
CONTRIBUTING.md
Normal file
|
@ -0,0 +1,128 @@
|
|||
# Contributing to TableFilter
|
||||
:+1::tada: First off, thanks for taking the time to contribute! :tada::+1:
|
||||
|
||||
## Reporting bugs
|
||||
Bugs are tracked as [GitHub issues](https://guides.github.com/features/issues/).
|
||||
Create an issue and provide the following information.
|
||||
|
||||
Explain the problem and include additional details to help maintainers reproduce
|
||||
the problem:
|
||||
|
||||
* **Use a clear, concise and descriptive title** for the issue to identify the
|
||||
problem.
|
||||
* **Describe the exact steps which reproduce the problem** in as many details
|
||||
as possible.
|
||||
* **Provide specific examples to demonstrate the steps**. Include links to
|
||||
files or GitHub projects, or copy/pasteable snippets, which you use in those
|
||||
examples. If you're providing snippets in the issue, use
|
||||
[Markdown code blocks](https://guides.github.com/features/mastering-markdown/#GitHub-flavored-markdown).
|
||||
Do not paste large snippets completely unrelated to the issue.
|
||||
* **Describe the behavior you observed after following the steps** and point
|
||||
out what exactly is the problem with that behavior.
|
||||
* **Explain which behavior you expected to see instead and why.**
|
||||
* Do not hesitate to **include screenshots and animated GIFs** which show you
|
||||
following the described steps and clearly demonstrate the problem.
|
||||
You can use [this tool](http://www.cockos.com/licecap/) to record GIFs on macOS
|
||||
and Windows, and [this tool](https://github.com/colinkeenan/silentcast) or
|
||||
[this tool](https://github.com/GNOME/byzanz) on Linux.
|
||||
|
||||
### Template for submitting bug reports
|
||||
|
||||
[Short description of problem here]
|
||||
|
||||
**Reproduction steps:**
|
||||
|
||||
1. [First Step]
|
||||
2. [Second Step]
|
||||
3. [Other Steps...]
|
||||
|
||||
**Expected behavior:**
|
||||
|
||||
[Describe expected behavior here]
|
||||
|
||||
**Observed behavior:**
|
||||
|
||||
[Describe observed behavior here]
|
||||
|
||||
**Screenshots and GIFs**
|
||||
|
||||
![Screenshots and GIFs which follow reproduction steps to demonstrate the
|
||||
problem](url)
|
||||
|
||||
**TableFilter version:** [Enter TableFilter version here]
|
||||
**Browser and version:** [Enter Browser name and version here]
|
||||
**OS and version:** [Enter OS name and version here]
|
||||
|
||||
**Additional information:**
|
||||
|
||||
* Problem started happening recently, didn't happen in an older version of
|
||||
TableFilter: [Yes/No]
|
||||
* Problem can be reliably reproduced, doesn't happen randomly: [Yes/No]
|
||||
|
||||
## Suggesting enhancements and features
|
||||
Enhancement and feature suggestions are tracked as
|
||||
[GitHub issues](https://guides.github.com/features/issues/).
|
||||
Create an issue and provide the following information:
|
||||
|
||||
* **Use a clear and descriptive title** for the issue to identify the
|
||||
suggestion.
|
||||
* **Provide a step-by-step description of the suggested enhancement/feature**
|
||||
in as many details as possible.
|
||||
* When applicable **describe the current behavior** and
|
||||
**explain which behavior you expected to see instead** and why.
|
||||
* **Include screenshots and animated GIFs** which help you demonstrate the
|
||||
steps or point out the part of TableFilter which the suggestion is related to.
|
||||
You can use [this tool](http://www.cockos.com/licecap/) to record GIFs on macOS
|
||||
and Windows, and [this tool](https://github.com/colinkeenan/silentcast) or
|
||||
[this tool](https://github.com/GNOME/byzanz) on Linux.
|
||||
* **Explain why this enhancement would be useful** to most TableFilter users.
|
||||
|
||||
### Template for submitting enhancement and feature suggestions
|
||||
|
||||
[Short description of suggestion]
|
||||
|
||||
**Steps which explain the enhancement/feature**
|
||||
|
||||
1. [First Step]
|
||||
2. [Second Step]
|
||||
3. [Other Steps...]
|
||||
|
||||
**Current and suggested behavior**
|
||||
|
||||
[Describe current and suggested behavior here]
|
||||
|
||||
**Why would the enhancement be useful to most users**
|
||||
|
||||
[Explain why the enhancement would be useful to most users]
|
||||
|
||||
**Screenshots and GIFs**
|
||||
|
||||
![Screenshots and GIFs which demonstrate the steps or part of TableFilter
|
||||
the enhancement suggestion is related to](url)
|
||||
|
||||
**TableFilter Version:** [Enter TableFilter version here]
|
||||
**Browser and version:** [Enter Browser name and version here]
|
||||
**OS and version:** [Enter OS name and version here]
|
||||
|
||||
## Code contribution
|
||||
TableFilter welcomes contributions from anyone and everyone. If you want to get
|
||||
your hands dirty:
|
||||
|
||||
### Pull requests
|
||||
In general, we follow the "fork-and-pull" Git workflow.
|
||||
|
||||
1. **Fork** the repo on GitHub
|
||||
2. **Clone** the project to your own machine
|
||||
3. **Commit** changes to your own branch
|
||||
- 3.1. **Add** unit tests covering your change in the `test` folder
|
||||
- 3.2. **Run** the tests with the `npm run eslint test` command
|
||||
- 3.3. **Ensure** build is working with `npm run dist` command
|
||||
4. **Push** your work back up to your fork
|
||||
5. Submit a **Pull request** so that we can review your changes
|
||||
|
||||
NOTE: Be sure to merge the latest from "upstream" before making a pull request!
|
||||
|
||||
## Donations
|
||||
Support this project by donating [here](http://www.tablefilter.com/donate.html).
|
||||
Thanks to all those who supported this project in the past and those who will
|
||||
in the future!
|
270
Gruntfile.js
|
@ -1,29 +1,26 @@
|
|||
module.exports = function (grunt) {
|
||||
|
||||
var webpack = require('webpack');
|
||||
var webpackConfig = require('./webpack.config.js');
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var testDir = 'test';
|
||||
var testHost = 'http://localhost:8000/';
|
||||
var pkg = grunt.file.readJSON('package.json');
|
||||
var repo = 'github.com/koalyptus/TableFilter';
|
||||
|
||||
grunt.initConfig({
|
||||
|
||||
eslint: {
|
||||
options: {
|
||||
configFile: '.eslintrc'
|
||||
},
|
||||
target: [
|
||||
'Gruntfile.js',
|
||||
'webpack.config.js',
|
||||
'src/**/*.js',
|
||||
'test/*.js'
|
||||
]
|
||||
},
|
||||
|
||||
qunit: {
|
||||
options: {
|
||||
'--web-security': 'no',
|
||||
coverage: {
|
||||
disposeCollector: true,
|
||||
src: ['dist/tablefilter/*.js'],
|
||||
instrumentedFiles: 'report/temp/',
|
||||
htmlReport: 'report/coverage',
|
||||
coberturaReport: 'report/',
|
||||
lcovReport: 'report/',
|
||||
jsonReport: 'report',
|
||||
linesThresholdPct: 80
|
||||
}
|
||||
},
|
||||
all: {
|
||||
options: {
|
||||
urls: getTestFiles(testDir, testHost)
|
||||
|
@ -110,26 +107,6 @@ module.exports = function (grunt) {
|
|||
|
||||
clean: ['demos/starter.html'],
|
||||
|
||||
'webpack-dev-server': {
|
||||
options: {
|
||||
webpack: webpack.dev,
|
||||
publicPath: '/dist/'
|
||||
},
|
||||
start: {
|
||||
keepAlive: true,
|
||||
webpack: {
|
||||
devtool: 'eval',
|
||||
debug: true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
webpack: {
|
||||
options: webpackConfig,
|
||||
build: webpackConfig.build,
|
||||
dev: webpackConfig.dev
|
||||
},
|
||||
|
||||
watch: {
|
||||
app: {
|
||||
files: ['src/**/*', 'static/style/**/*'],
|
||||
|
@ -147,188 +124,65 @@ module.exports = function (grunt) {
|
|||
}
|
||||
},
|
||||
|
||||
babel: {
|
||||
options: {
|
||||
sourceMap: true,
|
||||
modules: 'amd',
|
||||
compact: false,
|
||||
presets: ['es2015']
|
||||
// temporary shell commands while decommissioning grunt
|
||||
shell: {
|
||||
eslint: {
|
||||
command: 'npm run lint'
|
||||
},
|
||||
dist: {
|
||||
files: [{
|
||||
expand: true,
|
||||
cwd: 'src',
|
||||
src: ['**/*.js'],
|
||||
dest: 'dist/tablefilter'
|
||||
}]
|
||||
}
|
||||
},
|
||||
|
||||
esdoc: {
|
||||
dist: {
|
||||
options: {
|
||||
source: 'src',
|
||||
destination: 'docs/docs',
|
||||
title: pkg.name + ' v' + pkg.version
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
stylus: {
|
||||
compile: {
|
||||
options: {
|
||||
banner: '/** \n' +
|
||||
' *\t ' + pkg.name + ' v' + pkg.version +
|
||||
' by ' + pkg.author.name + ' \n' +
|
||||
' *\t build date: ' + new Date().toISOString() + ' \n' +
|
||||
' *\t MIT License \n' +
|
||||
' */ \n'
|
||||
},
|
||||
files: [
|
||||
{
|
||||
src: ['static/style/*.styl'],
|
||||
dest: 'dist/tablefilter/style/tablefilter.css'
|
||||
}, {
|
||||
src: ['static/style/extensions/colsVisibility.styl'],
|
||||
dest: 'dist/tablefilter/style/colsVisibility.css'
|
||||
}, {
|
||||
src: ['static/style/extensions/filtersVisibility.styl'],
|
||||
dest: 'dist/tablefilter/style/filtersVisibility.css'
|
||||
}, {
|
||||
src: ['static/style/themes/default/*.styl'],
|
||||
dest:
|
||||
'dist/tablefilter/style/themes/default/default.css'
|
||||
}, {
|
||||
src: ['static/style/themes/mytheme/*.styl'],
|
||||
dest:
|
||||
'dist/tablefilter/style/themes/mytheme/mytheme.css'
|
||||
}, {
|
||||
src: ['static/style/themes/skyblue/*.styl'],
|
||||
dest:
|
||||
'dist/tablefilter/style/themes/skyblue/skyblue.css'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
'gh-pages': {
|
||||
options: {
|
||||
branch: 'gh-pages',
|
||||
add: true
|
||||
esdoc: {
|
||||
command: 'npm run esdoc'
|
||||
},
|
||||
'publish-lib': {
|
||||
options: {
|
||||
base: 'dist',
|
||||
repo: 'https://' + repo,
|
||||
message: 'publish TableFilter lib to gh-pages (cli)'
|
||||
},
|
||||
src: ['**/*']
|
||||
build: {
|
||||
command: 'npm run build'
|
||||
},
|
||||
'publish-readme': {
|
||||
options: {
|
||||
base: './',
|
||||
repo: 'https://' + repo,
|
||||
message: 'publish README and LICENSE to gh-pages (cli)'
|
||||
},
|
||||
src: ['README.md', 'LICENSE']
|
||||
dev: {
|
||||
command: 'npm run dev'
|
||||
},
|
||||
'publish-docs': {
|
||||
options: {
|
||||
base: 'docs',
|
||||
repo: 'https://' + repo,
|
||||
message: 'publish Docs to gh-pages (cli)'
|
||||
},
|
||||
src: ['**/*']
|
||||
test: {
|
||||
command: 'npm run build:test'
|
||||
},
|
||||
'deploy-lib': {
|
||||
options: {
|
||||
user: {
|
||||
name: 'koalyptus'
|
||||
},
|
||||
base: 'dist',
|
||||
repo: 'https://' + process.env.GH_TOKEN + '@' + repo,
|
||||
message: 'publish TableFilter to gh-pages (auto)' +
|
||||
getDeployMessage(),
|
||||
silent: true
|
||||
},
|
||||
src: ['**/*']
|
||||
},
|
||||
'deploy-readme': {
|
||||
options: {
|
||||
user: {
|
||||
name: 'koalyptus'
|
||||
},
|
||||
base: './',
|
||||
repo: 'https://' + process.env.GH_TOKEN + '@' + repo,
|
||||
message: 'publish README to gh-pages (auto)' +
|
||||
getDeployMessage(),
|
||||
silent: true
|
||||
},
|
||||
src: ['README.md', 'LICENSE']
|
||||
},
|
||||
'deploy-docs': {
|
||||
options: {
|
||||
user: {
|
||||
name: 'koalyptus'
|
||||
},
|
||||
base: 'docs',
|
||||
repo: 'https://' + process.env.GH_TOKEN + '@' + repo,
|
||||
message: 'publish Docs to gh-pages (auto)' +
|
||||
getDeployMessage(),
|
||||
silent: true
|
||||
},
|
||||
src: ['**/*']
|
||||
'build-css': {
|
||||
command: 'npm run build:css'
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
grunt.loadNpmTasks('grunt-eslint');
|
||||
grunt.loadNpmTasks('grunt-contrib-qunit');
|
||||
grunt.loadNpmTasks('grunt-contrib-copy');
|
||||
grunt.loadNpmTasks('grunt-contrib-clean');
|
||||
grunt.loadNpmTasks('grunt-contrib-watch');
|
||||
grunt.loadNpmTasks('grunt-string-replace');
|
||||
grunt.loadNpmTasks('grunt-contrib-connect');
|
||||
grunt.loadNpmTasks('grunt-webpack');
|
||||
grunt.loadNpmTasks('grunt-babel');
|
||||
grunt.loadNpmTasks('grunt-esdoc');
|
||||
grunt.loadNpmTasks('grunt-contrib-stylus');
|
||||
grunt.loadNpmTasks('grunt-gh-pages');
|
||||
grunt.loadNpmTasks('grunt-shell');
|
||||
grunt.loadNpmTasks('grunt-qunit-istanbul');
|
||||
|
||||
grunt.registerTask('default', ['build', 'test', 'build-demos']);
|
||||
grunt.registerTask('eslint', ['shell:eslint']);
|
||||
grunt.registerTask('esdoc', ['shell:esdoc']);
|
||||
|
||||
// Development server
|
||||
grunt.registerTask('server', ['webpack-dev-server:start']);
|
||||
grunt.registerTask('default', ['test', 'build', 'build-demos']);
|
||||
|
||||
// Dev dev/build/watch cycle
|
||||
grunt.registerTask('dev',
|
||||
['eslint', 'webpack:dev', 'copy:dist', 'stylus:compile', 'watch:app']);
|
||||
['eslint', 'shell:dev', 'copy:dist', 'shell:build-css', 'watch:app']);
|
||||
|
||||
// Production build
|
||||
grunt.registerTask('build',
|
||||
['eslint', 'webpack:build', 'copy:dist', 'stylus:compile']);
|
||||
['eslint', 'shell:build', 'copy:dist', 'shell:build-css']);
|
||||
|
||||
// Build demos
|
||||
grunt.registerTask('dev-demos', ['build-demos', 'watch:templates']);
|
||||
grunt.registerTask('build-demos', ['copy:templates', 'copy:assets',
|
||||
'string-replace:demos', 'copy:starter', 'clean']);
|
||||
|
||||
// Transpile with Babel
|
||||
grunt.registerTask('dev-modules', ['babel', 'copy:dist']);
|
||||
// Build tests
|
||||
grunt.registerTask('build-test',
|
||||
['eslint', 'shell:test', 'copy:dist', 'shell:build-css']);
|
||||
|
||||
// Tests
|
||||
grunt.registerTask('test', ['eslint', 'connect', 'qunit:all']);
|
||||
// Tests with coverage
|
||||
grunt.registerTask('test', ['build-test', 'connect', 'qunit:all']);
|
||||
|
||||
// Publish to gh-pages
|
||||
grunt.registerTask('publish', 'Publish from CLI', [
|
||||
'build', 'build-demos', 'esdoc', 'gh-pages:publish-lib',
|
||||
'gh-pages:publish-readme', 'gh-pages:publish-docs'
|
||||
]);
|
||||
|
||||
// Deploy to gh-pages
|
||||
grunt.registerTask('deploy', 'Publish from Travis', [
|
||||
'build', 'esdoc', 'check-deploy'
|
||||
// Build all for deployment from travis
|
||||
grunt.registerTask('build-all', 'Prepare for deployment', [
|
||||
'build', 'build-demos', 'esdoc'
|
||||
]);
|
||||
|
||||
// Custom task running QUnit tests for specified files.
|
||||
|
@ -374,7 +228,6 @@ module.exports = function (grunt) {
|
|||
|
||||
// Returns the list of test files from the test folder for qunit task
|
||||
function getTestFiles(testDir, host) {
|
||||
|
||||
var getFiles = function (dir, host) {
|
||||
var res = [];
|
||||
var items = fs.readdirSync(dir);
|
||||
|
@ -396,43 +249,4 @@ module.exports = function (grunt) {
|
|||
|
||||
return getFiles(testDir, host);
|
||||
}
|
||||
|
||||
grunt.registerTask('check-deploy', function () {
|
||||
var env = process.env;
|
||||
// need this
|
||||
this.requires(['build', 'esdoc']);
|
||||
|
||||
// only deploy under these conditions
|
||||
if (env.TRAVIS === 'true' &&
|
||||
env.TRAVIS_SECURE_ENV_VARS === 'true' &&
|
||||
env.TRAVIS_PULL_REQUEST === 'false') {
|
||||
grunt.log.writeln('executing deployment');
|
||||
// queue deploy
|
||||
grunt.task.run([
|
||||
'gh-pages:deploy-lib',
|
||||
'gh-pages:deploy-readme',
|
||||
'gh-pages:deploy-docs'
|
||||
]);
|
||||
} else {
|
||||
grunt.log.writeln('skipped deployment');
|
||||
}
|
||||
});
|
||||
|
||||
// Get a formatted commit message to review changes from the commit log
|
||||
// github will turn some of these into clickable links
|
||||
function getDeployMessage() {
|
||||
var ret = '\n\n';
|
||||
var env = process.env;
|
||||
if (env.TRAVIS !== 'true') {
|
||||
ret += 'missing env vars for travis-ci';
|
||||
return ret;
|
||||
}
|
||||
ret += 'branch: ' + env.TRAVIS_BRANCH + '\n';
|
||||
ret += 'SHA: ' + env.TRAVIS_COMMIT + '\n';
|
||||
ret += 'range SHA: ' + env.TRAVIS_COMMIT_RANGE + '\n';
|
||||
ret += 'build id: ' + env.TRAVIS_BUILD_ID + '\n';
|
||||
ret += 'build number: ' + env.TRAVIS_BUILD_NUMBER + '\n';
|
||||
return ret;
|
||||
}
|
||||
|
||||
};
|
||||
|
|
24
PULL_REQUEST_TEMPLATE.md
Normal file
|
@ -0,0 +1,24 @@
|
|||
# Description
|
||||
|
||||
Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context.
|
||||
Fixes # (issue)
|
||||
|
||||
## Type of change
|
||||
|
||||
Please delete options that are not relevant.
|
||||
|
||||
- [ ] Bug fix (non-breaking change which fixes an issue)
|
||||
- [ ] New feature (non-breaking change which adds functionality)
|
||||
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
||||
- [ ] This change requires a documentation update
|
||||
- [ ] Development tool configuration change
|
||||
|
||||
# Checklist:
|
||||
|
||||
- [ ] My code follows the style guidelines of this project
|
||||
- [ ] I have performed a self-review of my own code
|
||||
- [ ] I have commented my code, particularly in hard-to-understand areas
|
||||
- [ ] I have made corresponding changes to the documentation
|
||||
- [ ] My changes do not lower current test coverage
|
||||
- [ ] I have added tests that prove my fix is effective or that my feature works
|
||||
- [ ] New and existing unit tests pass locally with my changes
|
108
README.md
|
@ -1,5 +1,8 @@
|
|||
[![Build Status](https://api.travis-ci.org/koalyptus/TableFilter.svg?branch=master)](https://travis-ci.org/koalyptus/TableFilter)
|
||||
[![Document](http://koalyptus.github.io/TableFilter/docs/badge.svg)](https://koalyptus.github.io/TableFilter/docs/source)
|
||||
[![Document](https://www.tablefilter.com/docs/badge.svg)](https://www.tablefilter.com/docs/source.html)
|
||||
[![codecov](https://codecov.io/gh/koalyptus/TableFilter/branch/master/graph/badge.svg)](https://codecov.io/gh/koalyptus/TableFilter)
|
||||
[![Greenkeeper badge](https://badges.greenkeeper.io/koalyptus/TableFilter.svg)](https://greenkeeper.io/)
|
||||
[![Donate](https://img.shields.io/badge/donate-%24-green.svg)](https://www.tablefilter.com/donate.html)
|
||||
|
||||
# TableFilter
|
||||
|
||||
|
@ -12,7 +15,7 @@ users to filter and limit the data displayed within a long table. By default, th
|
|||
## Features
|
||||
* Convert a regular HTML table into an advanced grid component providing:
|
||||
* Advanced columns filtering model
|
||||
* Sorting and pagination facilities
|
||||
* Sorting and pagination capabilities
|
||||
* Complete selection model ([ezEditTable](http://codecanyon.net/item/ezedittable-enhance-html-tables/2425123?ref=koalyptus) extension)
|
||||
* Extended keyboard navigation ([ezEditTable](http://codecanyon.net/item/ezedittable-enhance-html-tables/2425123?ref=koalyptus) extension)
|
||||
* Inline cell or row editing ([ezEditTable](http://codecanyon.net/item/ezedittable-enhance-html-tables/2425123?ref=koalyptus) extension)
|
||||
|
@ -21,33 +24,50 @@ users to filter and limit the data displayed within a long table. By default, th
|
|||
* Attach to an existing HTML table
|
||||
* Integration with any server-side technology as this is a pure client-side
|
||||
solution
|
||||
* Exhaustive documentation and poweful API
|
||||
* Exhaustive documentation and powerful API
|
||||
|
||||
## Getting started
|
||||
* Clone the repo using Git:
|
||||
```shell
|
||||
git clone --bare https://github.com/koalyptus/TableFilter.git
|
||||
git clone https://github.com/koalyptus/TableFilter.git
|
||||
```
|
||||
|
||||
* You can [download](https://github.com/koalyptus/TableFilter/archive/master.zip) this repository.
|
||||
|
||||
* Alternatively, install TableFilter files in your npm enabled project using:
|
||||
* TableFilter is available on [npm repository](https://www.npmjs.com/package/tablefilter), you can install it from the command line using the following command:
|
||||
```shell
|
||||
npm install tablefilter --save
|
||||
```
|
||||
npm install tablefilter --save-dev
|
||||
```
|
||||
* or get the future features from the ``next`` release channel:
|
||||
```shell
|
||||
npm install tablefilter@next --save
|
||||
npm install tablefilter@next --save-dev
|
||||
```
|
||||
* If you don't use `npm`, you can also
|
||||
[access these files on unpkg](https://unpkg.com/tablefilter/), download them
|
||||
or point your package manager to them.
|
||||
* Alternatively you can also [access these files from unpkg CDN](https://unpkg.com/tablefilter/), download them or point your package manager to them.
|
||||
|
||||
## Setup
|
||||
### Using modules
|
||||
Require `TableFilter`:
|
||||
```javascript
|
||||
// ES2015 modules
|
||||
import TableFilter from 'tablefilter';
|
||||
|
||||
// CommonJS or AMD modules
|
||||
var TableFilter = require('tablefilter');
|
||||
```
|
||||
|
||||
### Using distribution scripts
|
||||
If you are not using a module system, you can reference the distribution scripts directly in your html pages:
|
||||
```html
|
||||
<script src="path_to/node_modules/tablefilter/dist/tablefilter/tablefilter.js"></script>
|
||||
```
|
||||
|
||||
### Placing manually the distribution scripts in your project
|
||||
Copy the ``tablefilter`` directory under ``dist`` and place it at desired location in your project. Then include the main js file in your page:
|
||||
```shell
|
||||
<script src="path/to/my/scripts/tablefilter/tablefilter.js"></script>
|
||||
```
|
||||
|
||||
### Usage
|
||||
Place the following snippet just under the HTML table and always define a ``base_path`` property in the configuration object to reflect the path to the script
|
||||
```shell
|
||||
<script>
|
||||
|
@ -61,11 +81,11 @@ If the ``base_path`` property is not specified, it will default to ``/tablefilte
|
|||
```shell
|
||||
your-page.html
|
||||
|— tablefilter
|
||||
```
|
||||
```
|
||||
|
||||
## Development
|
||||
This project requires node.js and Grunt to be installed:
|
||||
- install [node.js](https://nodejs.org/)
|
||||
- install [node.js](https://nodejs.org/) v8.9.4 or higher
|
||||
- install [Grunt](http://gruntjs.com/getting-started) from the command line using npm (comes with node.js):
|
||||
```shell
|
||||
npm install -g grunt-cli
|
||||
|
@ -76,46 +96,52 @@ Start by installing any dependencies.
|
|||
```shell
|
||||
npm install
|
||||
```
|
||||
Use the Grunt ``dev`` task to launch a build / watch cycle and start the local
|
||||
sever on port ``8080``:
|
||||
Use
|
||||
```shell
|
||||
npm run dev
|
||||
```
|
||||
command to launch a build / watch cycle and start the local
|
||||
sever on port ``8080``.
|
||||
|
||||
Use
|
||||
```shell
|
||||
npm run build
|
||||
```
|
||||
command to generate a production build.
|
||||
|
||||
The
|
||||
```shell
|
||||
npm run dist
|
||||
```
|
||||
command will create a production build, run the tests and finally generate
|
||||
the demos:
|
||||
|
||||
To run all the tests and generate the coverage report:
|
||||
|
||||
```shell
|
||||
grunt dev
|
||||
npm test
|
||||
```
|
||||
|
||||
Use the ``build`` task to generate a production build:
|
||||
|
||||
```shell
|
||||
grunt build
|
||||
```
|
||||
|
||||
The ``default`` Grunt task will create a production build, run the tests and finally generate the demos:
|
||||
|
||||
```shell
|
||||
grunt
|
||||
```
|
||||
|
||||
To run all the tests:
|
||||
|
||||
```shell
|
||||
grunt test
|
||||
```
|
||||
|
||||
and to run specific test(s):
|
||||
or to run specific test(s):
|
||||
|
||||
```shell
|
||||
grunt test-only:test.html
|
||||
grunt test-only:test.html,test-sort.html
|
||||
```
|
||||
|
||||
to view the coverage report(s), open the `index.html` under the
|
||||
`report/coverage` folder or
|
||||
[online](https://codecov.io/gh/koalyptus/TableFilter).
|
||||
|
||||
## Demos
|
||||
Check out the online [examples](http://koalyptus.github.io/TableFilter/examples) or generate the demos locally:
|
||||
Check out the online [examples](http://www.tablefilter.com/examples.html)
|
||||
or generate the demos locally:
|
||||
```shell
|
||||
grunt build-demos
|
||||
npm run build:demos
|
||||
```
|
||||
then run the local webserver:
|
||||
```shell
|
||||
grunt server
|
||||
npm start
|
||||
```
|
||||
then pick a demo from:
|
||||
```shell
|
||||
|
@ -125,18 +151,18 @@ http://localhost:8080/demos/
|
|||
## Documentation
|
||||
Find exhaustive documentation on the configuration options in the [WIKI](https://github.com/koalyptus/TableFilter/wiki) section.
|
||||
|
||||
Autogenerated documentation of the ES6 modules is available on the website: [docs](http://koalyptus.github.io/TableFilter/docs)
|
||||
Autogenerated documentation of the ES6 modules is available on the website: [docs](http://www.tablefilter.com/docs)
|
||||
|
||||
If you previously used the HTML Table Filter Generator plugin, verify the configuration
|
||||
options you are using are still supported: [Obsolete](https://github.com/koalyptus/TableFilter/wiki/Obsolete)
|
||||
|
||||
Run this task to generate the documentation in the ``docs/docs`` directory:
|
||||
```shell
|
||||
grunt esdoc
|
||||
npm run esdoc
|
||||
```
|
||||
|
||||
## Support
|
||||
* GitHub for reporting bugs and feature requests.
|
||||
* GitHub for [reporting bugs](https://github.com/koalyptus/TableFilter/blob/master/CONTRIBUTING.md#reporting-bugs) and [feature requests](https://github.com/koalyptus/TableFilter/blob/master/CONTRIBUTING.md#suggesting-enhancements-and-features).
|
||||
|
||||
## License
|
||||
[MIT](LICENSE)
|
||||
|
|
14
dist/starter.html
vendored
|
@ -1,10 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>tablefilter v0.2.65 - Starter</title>
|
||||
<title>tablefilter v0.7.3 - Starter</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>tablefilter v0.2.65</h1>
|
||||
<h1>tablefilter v0.7.3</h1>
|
||||
|
||||
|
||||
|
||||
|
@ -460,10 +460,10 @@
|
|||
status_bar: true,
|
||||
mark_active_columns: true,
|
||||
highlight_keywords: true,
|
||||
col_number_format: [
|
||||
null, null, 'US',
|
||||
'US', 'US', 'US',
|
||||
'US', 'US', 'US'
|
||||
col_types: [
|
||||
'string', 'string', 'number',
|
||||
'number', 'number', 'number',
|
||||
'number', 'number', 'number'
|
||||
],
|
||||
custom_options: {
|
||||
cols:[3],
|
||||
|
@ -479,7 +479,7 @@
|
|||
},
|
||||
col_widths: [
|
||||
'150px', '100px', '100px',
|
||||
'70px', '70px', '70px',
|
||||
'100px', '100px', '100px',
|
||||
'70px', '60px', '60px'
|
||||
],
|
||||
extensions:[{ name: 'sort' }]
|
||||
|
|
7
dist/tablefilter/style/colsVisibility.css
vendored
|
@ -1,6 +1 @@
|
|||
/**
|
||||
* tablefilter v0.2.65 by Max Guglielmi
|
||||
* build date: 2016-09-07T11:33:04.352Z
|
||||
* MIT License
|
||||
*/
|
||||
span.colVisSpan{text-align:left;}span.colVisSpan a.colVis{display:inline-block;padding:7px 5px 0;font-size:inherit;font-weight:inherit;vertical-align:top}div.colVisCont{position:relative;background:#fff;-webkit-box-shadow:3px 3px 2px #888;-moz-box-shadow:3px 3px 2px #888;box-shadow:3px 3px 2px #888;position:absolute;display:none;border:1px solid #ccc;height:auto;width:250px;background-color:#fff;margin:35px 0 0 -100px;z-index:10000;padding:10px 10px 10px 10px;text-align:left;font-size:12px;}div.colVisCont:after,div.colVisCont:before{bottom:100%;left:50%;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}div.colVisCont:after{border-color:rgba(255,255,255,0);border-bottom-color:#fff;border-width:10px;margin-left:-10px}div.colVisCont:before{border-color:rgba(255,255,255,0);border-bottom-color:#ccc;border-width:12px;margin-left:-12px}div.colVisCont p{margin:6px auto 6px auto}div.colVisCont a.colVis{display:initial;font-weight:inherit}ul.cols_checklist{padding:0;margin:0;list-style:none;}ul.cols_checklist label{display:block}ul.cols_checklist input{vertical-align:middle;margin:2px 5px 2px 1px}li.cols_checklist_item{padding:4px;margin:0;}li.cols_checklist_item:hover{background-color:#335ea8;color:#fff}.cols_checklist_slc_item{background-color:#335ea8;color:#fff}
|
||||
span.colVisSpan{text-align:left;}span.colVisSpan a.colVis{display:inline-block;padding:7px 5px 0;font-size:inherit;font-weight:inherit;vertical-align:top}div.colVisCont{position:relative;background:#fff;-webkit-box-shadow:3px 3px 2px #888;-moz-box-shadow:3px 3px 2px #888;box-shadow:3px 3px 2px #888;position:absolute;display:none;border:1px solid #ccc;height:auto;width:250px;background-color:#fff;margin:35px 0 0 -100px;z-index:10000;padding:10px 10px 10px 10px;text-align:left;font-size:inherit;}div.colVisCont:after,div.colVisCont:before{bottom:100%;left:50%;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}div.colVisCont:after{border-color:rgba(255,255,255,0);border-bottom-color:#fff;border-width:10px;margin-left:-10px}div.colVisCont:before{border-color:rgba(255,255,255,0);border-bottom-color:#ccc;border-width:12px;margin-left:-12px}div.colVisCont p{margin:6px auto 6px auto}div.colVisCont a.colVis{display:initial;font-weight:inherit}ul.cols_checklist{padding:0;margin:0;list-style-type:none;}ul.cols_checklist label{display:block}ul.cols_checklist input{vertical-align:middle;margin:2px 5px 2px 1px}li.cols_checklist_item{padding:4px;margin:0;}li.cols_checklist_item:hover{background-color:#335ea8;color:#fff}.cols_checklist_slc_item{background-color:#335ea8;color:#fff}
|
5
dist/tablefilter/style/filtersVisibility.css
vendored
|
@ -1,6 +1 @@
|
|||
/**
|
||||
* tablefilter v0.2.65 by Max Guglielmi
|
||||
* build date: 2016-09-07T11:33:04.352Z
|
||||
* MIT License
|
||||
*/
|
||||
span.expClpFlt a.btnExpClpFlt{width:35px;height:35px;display:inline-block;}span.expClpFlt a.btnExpClpFlt:hover{background-color:#f4f4f4}span.expClpFlt img{padding:8px 11px 11px 11px}
|
24
dist/tablefilter/style/tablefilter.css
vendored
|
@ -1,6 +1 @@
|
|||
/**
|
||||
* tablefilter v0.2.65 by Max Guglielmi
|
||||
* build date: 2016-09-07T11:33:04.352Z
|
||||
* MIT License
|
||||
*/
|
||||
table.TF{border-left:1px solid #ccc;border-top:none;border-right:none;border-bottom:none;}table.TF th{background:#ebecee url("images/bg_th.jpg") left top repeat-x;border-bottom:1px solid #d0d0d0;border-right:1px solid #d0d0d0;border-left:1px solid #fff;border-top:1px solid #fff;color:#333}table.TF td{border-bottom:1px dotted #999;padding:5px}.fltrow{background-color:#ebecee !important;}.fltrow th,.fltrow td{border-bottom:1px dotted #666 !important;padding:1px 3px 1px 3px !important}.flt,select.flt,select.flt_multi,.flt_s,.single_flt,.div_checklist{border:1px solid #999 !important}input.flt{width:99% !important}.inf{height:$min-height;background:#d7d7d7 url("images/bg_infDiv.jpg") 0 0 repeat-x !important}input.reset{background:transparent url("images/btn_eraser.gif") center center no-repeat !important}.helpBtn:hover{background-color:transparent}.nextPage{background:transparent url("images/btn_next_page.gif") center center no-repeat !important;}.nextPage:hover{background:transparent url("images/btn_over_next_page.gif") center center no-repeat !important}.previousPage{background:transparent url("images/btn_previous_page.gif") center center no-repeat !important;}.previousPage:hover{background:transparent url("images/btn_over_previous_page.gif") center center no-repeat !important}.firstPage{background:transparent url("images/btn_first_page.gif") center center no-repeat !important;}.firstPage:hover{background:transparent url("images/btn_over_first_page.gif") center center no-repeat !important}.lastPage{background:transparent url("images/btn_last_page.gif") center center no-repeat !important;}.lastPage:hover{background:transparent url("images/btn_over_last_page.gif") center center no-repeat !important}div.grd_Cont{background-color:#ebecee !important;border:1px solid #ccc !important;padding:0 !important;}div.grd_Cont .even{background-color:#fff}div.grd_Cont .odd{background-color:#d5d5d5}div.grd_headTblCont{background-color:#ebecee !important;border-bottom:none !important;}div.grd_headTblCont table{border-right:none !important}div.grd_tblCont table th,div.grd_headTblCont table th,div.grd_headTblCont table td{background:#ebecee url("images/bg_th.jpg") left top repeat-x !important;border-bottom:1px solid #d0d0d0 !important;border-right:1px solid #d0d0d0 !important;border-left:1px solid #fff !important;border-top:1px solid #fff !important}div.grd_tblCont table td{border-bottom:1px solid #999 !important}.grd_inf{background:#d7d7d7 url("images/bg_infDiv.jpg") 0 0 repeat-x !important;border-top:1px solid #d0d0d0 !important}.loader{border:1px solid #999}.defaultLoader{width:32px;height:32px;background:transparent url("images/img_loading.gif") 0 0 no-repeat !important}.even{background-color:#fff}.odd{background-color:#d5d5d5}span.expClpFlt a.btnExpClpFlt:hover{background-color:transparent !important}.activeHeader{background:#999 !important}
|
|
@ -1,6 +1 @@
|
|||
/**
|
||||
* tablefilter v0.2.65 by Max Guglielmi
|
||||
* build date: 2016-09-07T11:33:04.352Z
|
||||
* MIT License
|
||||
*/
|
||||
table.TF{border-left:1px dotted #81963b;border-top:none;border-right:0;border-bottom:none;}table.TF th{background:#39424b url("images/bg_headers.jpg") left top repeat-x;border-bottom:0;border-right:1px dotted #d0d0d0;border-left:0;border-top:0;color:#fff}table.TF td{border-bottom:1px dotted #81963b;border-right:1px dotted #81963b;padding:5px}.fltrow{background-color:#81963b !important;}.fltrow th,.fltrow td{border-bottom:1px dotted #39424b !important;border-right:1px dotted #fff !important;border-left:0 !important;border-top:0 !important;padding:1px 3px 1px 3px !important}.flt,select.flt,select.flt_multi,.flt_s,.single_flt,.div_checklist{border:1px solid #687830 !important}input.flt{width:99% !important}.inf{background:#d8d8d8;height:$min-height}input.reset{width:53px;background:transparent url("images/btn_filter.png") center center no-repeat !important}.helpBtn:hover{background-color:transparent}.nextPage{background:transparent url("images/btn_next_page.gif") center center no-repeat !important}.previousPage{background:transparent url("images/btn_previous_page.gif") center center no-repeat !important}.firstPage{background:transparent url("images/btn_first_page.gif") center center no-repeat !important}.lastPage{background:transparent url("images/btn_last_page.gif") center center no-repeat !important}div.grd_Cont{background:#81963b url("images/bg_headers.jpg") left top repeat-x !important;border:1px solid #ccc !important;padding:0 1px 1px 1px !important;}div.grd_Cont .even{background-color:#bccd83}div.grd_Cont .odd{background-color:#fff}div.grd_headTblCont{background-color:#ebecee !important;border-bottom:none !important}div.grd_tblCont table{border-right:none !important;}div.grd_tblCont table td{border-bottom:1px dotted #81963b;border-right:1px dotted #81963b}div.grd_tblCont table th,div.grd_headTblCont table th{background:transparent url("images/bg_headers.jpg") 0 0 repeat-x !important;border-bottom:0 !important;border-right:1px dotted #d0d0d0 !important;border-left:0 !important;border-top:0 !important;padding:0 4px 0 4px !important;color:#fff !important;height:35px !important}div.grd_headTblCont table td{border-bottom:1px dotted #39424b !important;border-right:1px dotted #fff !important;border-left:0 !important;border-top:0 !important;background-color:#81963b !important;padding:1px 3px 1px 3px !important}.grd_inf{background-color:#d8d8d8;border-top:1px solid #d0d0d0 !important}.loader{border:0 !important;background:#81963b !important}.defaultLoader{width:32px;height:32px;background:transparent url("images/img_loading.gif") 0 0 no-repeat !important}.even{background-color:#bccd83}.odd{background-color:#fff}span.expClpFlt a.btnExpClpFlt:hover{background-color:transparent !important}.activeHeader{background:#81963b !important}
|
|
@ -1,6 +1 @@
|
|||
/**
|
||||
* tablefilter v0.2.65 by Max Guglielmi
|
||||
* build date: 2016-09-07T11:33:04.352Z
|
||||
* MIT License
|
||||
*/
|
||||
table.TF{padding:0;color:#000;border-right:1px solid #a4bed4;border-top:1px solid #a4bed4;border-left:1px solid #a4bed4;border-bottom:0;}table.TF th{margin:0;color:inherit;background:#d1e5fe url("images/bg_skyblue.gif") 0 0 repeat-x;border-color:#fdfdfd #a4bed4 #a4bed4 #fdfdfd;border-width:1px;border-style:solid}table.TF td{margin:0;padding:5px;color:inherit;border-bottom:1px solid #a4bed4;border-left:0;border-top:0;border-right:0}.fltrow{background-color:#d1e5fe !important;}.fltrow th,.fltrow td{padding:1px 3px 1px 3px !important}.flt,select.flt,select.flt_multi,.flt_s,.single_flt,.div_checklist{border:1px solid #a4bed4 !important}input.flt{width:99% !important}.inf{background-color:#e3efff !important;border:1px solid #a4bed4;height:$min-height;color:#004a6f}div.tot,div.status{border-right:0 !important}.helpBtn:hover{background-color:transparent}input.reset{background:transparent url("images/icn_clear_filters.png") center center no-repeat !important}.nextPage{background:transparent url("images/btn_next_page.gif") center center no-repeat !important;border:1px solid transparent !important;}.nextPage:hover{background:#ffe4ab url("images/btn_next_page.gif") center center no-repeat !important;border:1px solid #ffb552 !important}.previousPage{background:transparent url("images/btn_prev_page.gif") center center no-repeat !important;border:1px solid transparent !important;}.previousPage:hover{background:#ffe4ab url("images/btn_prev_page.gif") center center no-repeat !important;border:1px solid #ffb552 !important}.firstPage{background:transparent url("images/btn_first_page.gif") center center no-repeat !important;border:1px solid transparent !important;}.firstPage:hover{background:#ffe4ab url("images/btn_first_page.gif") center center no-repeat !important;border:1px solid #ffb552 !important}.lastPage{background:transparent url("images/btn_last_page.gif") center center no-repeat !important;border:1px solid transparent !important;}.lastPage:hover{background:#ffe4ab url("images/btn_last_page.gif") center center no-repeat !important;border:1px solid #ffb552 !important}.activeHeader{background:#ffe4ab !important;border:1px solid #ffb552 !important;color:inherit !important}div.grd_Cont{background-color:#d9eaed !important;border:1px solid #9cc !important;padding:0 !important;}div.grd_Cont .even{background-color:#fff}div.grd_Cont .odd{background-color:#e3efff}div.grd_headTblCont{background-color:#d9eaed !important;border-bottom:none !important}div.grd_tblCont table{border-right:none !important}div.grd_tblCont table th,div.grd_headTblCont table th,div.grd_headTblCont table td{background:#d9eaed url("images/bg_skyblue.gif") left top repeat-x;border-bottom:1px solid #a4bed4;border-right:1px solid #a4bed4 !important;border-left:1px solid #fff !important;border-top:1px solid #fff !important}div.grd_tblCont table td{border-bottom:1px solid #a4bed4 !important;border-right:0 !important;border-left:0 !important;border-top:0 !important}.grd_inf{background-color:#cce2fe;color:#004a6f;border-top:1px solid #9cc !important;}.grd_inf a{text-decoration:none;font-weight:bold}.loader{background-color:#2d8eef;border:1px solid #cce2fe;border-radius:5px}.even{background-color:#fff}.odd{background-color:#e3efff}span.expClpFlt a.btnExpClpFlt:hover{background-color:transparent !important}.ezActiveRow{background-color:#ffdc61 !important;color:inherit}.ezSelectedRow{background-color:#ffe4ab !important;color:inherit}.ezActiveCell{background-color:#fff !important;color:#000 !important;font-weight:bold}.ezETSelectedCell{background-color:#fff !important;font-weight:bold;color:#000 !important}
|
BIN
dist/tablefilter/style/themes/transparent/images/btn_first_page.gif
vendored
Normal file
After Width: | Height: | Size: 63 B |
BIN
dist/tablefilter/style/themes/transparent/images/btn_last_page.gif
vendored
Normal file
After Width: | Height: | Size: 61 B |
BIN
dist/tablefilter/style/themes/transparent/images/btn_next_page.gif
vendored
Normal file
After Width: | Height: | Size: 59 B |
BIN
dist/tablefilter/style/themes/transparent/images/btn_prev_page.gif
vendored
Normal file
After Width: | Height: | Size: 58 B |
BIN
dist/tablefilter/style/themes/transparent/images/icn_clear_filters.png
vendored
Normal file
After Width: | Height: | Size: 601 B |
BIN
dist/tablefilter/style/themes/transparent/images/img_loading.gif
vendored
Normal file
After Width: | Height: | Size: 847 B |
1
dist/tablefilter/style/themes/transparent/transparent.css
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
table.TF{padding:0;color:inherit;border-right:1px solid transparent;border-top:1px solid transparent;border-left:1px solid transparent;border-bottom:0;}table.TF th{margin:0;color:inherit;background-color:transparent;border-color:transparent;border-width:1px;border-style:solid;}table.TF th:last-child{border-right:1px solid transparent}table.TF td{margin:0;padding:5px;color:inherit;border-bottom:1px solid transparent;border-left:0;border-top:0;border-right:0}.fltrow{background-color:transparent;}.fltrow th,.fltrow td{padding:1px 3px 1px 3px;border-bottom:1px solid transparent !important;}.fltrow th:last-child,.fltrow td:last-child{border-right:1px solid transparent}.flt,select.flt,select.flt_multi,.flt_s,.single_flt,.div_checklist{border:1px solid #a4bed4}input.flt{width:99% !important}.inf{background-color:transparent;border:1px solid transparent;height:$min-height;color:inherit}div.tot,div.status{border-right:0 !important}.helpBtn:hover{background-color:transparent}input.reset{background:transparent url("images/icn_clear_filters.png") center center no-repeat !important}.nextPage{background:transparent url("images/btn_next_page.gif") center center no-repeat !important;border:1px solid transparent !important;}.nextPage:hover{background:#f7f7f7 url("images/btn_next_page.gif") center center no-repeat !important;border:1px solid #f7f7f7 !important}.previousPage{background:transparent url("images/btn_prev_page.gif") center center no-repeat !important;border:1px solid transparent !important;}.previousPage:hover{background:#f7f7f7 url("images/btn_prev_page.gif") center center no-repeat !important;border:1px solid #f7f7f7 !important}.firstPage{background:transparent url("images/btn_first_page.gif") center center no-repeat !important;border:1px solid transparent !important;}.firstPage:hover{background:#f7f7f7 url("images/btn_first_page.gif") center center no-repeat !important;border:1px solid #f7f7f7 !important}.lastPage{background:transparent url("images/btn_last_page.gif") center center no-repeat !important;border:1px solid transparent !important;}.lastPage:hover{background:#f7f7f7 url("images/btn_last_page.gif") center center no-repeat !important;border:1px solid #f7f7f7 !important}.activeHeader{background:#f7f7f7 !important;border:1px solid transparent;color:inherit !important}div.grd_Cont{-webkit-box-shadow:0 0 0 0 rgba(50,50,50,0.75);-moz-box-shadow:0 0 0 0 rgba(50,50,50,0.75);box-shadow:0 0 0 0 rgba(50,50,50,0.75);background-color:transparent;border:1px solid transparent;padding:0 !important;}div.grd_Cont .even{background-color:transparent}div.grd_Cont .odd{background-color:#f7f7f7}div.grd_headTblCont{background-color:transparent;border-bottom:none !important}div.grd_tblCont table{border-right:none !important}div.grd_tblCont table th,div.grd_headTblCont table th,div.grd_headTblCont table td{background:transparent;border-bottom:1px solid transparent;border-right:1px solid transparent !important;border-left:1px solid transparent;border-top:1px solid transparent}div.grd_tblCont table td{border-bottom:1px solid transparent;border-right:0 !important;border-left:0 !important;border-top:0 !important}.grd_inf{background-color:transparent;color:inherit;border-top:1px solid transparent;}.grd_inf a{text-decoration:none;font-weight:bold}.loader{background-color:#f7f7f7;border:1px solid #f7f7f7;border-radius:5px;color:#000;text-shadow:none}.even{background-color:transparent}.odd{background-color:#f7f7f7}span.expClpFlt a.btnExpClpFlt:hover{background-color:transparent !important}.ezActiveRow{background-color:#ccc !important;color:inherit}.ezSelectedRow{background-color:#ccc !important;color:inherit}.ezActiveCell{background-color:transparent;color:inherit;font-weight:bold}.ezETSelectedCell{background-color:transparent;font-weight:bold;color:inherit}
|
11
dist/tablefilter/tablefilter.js
vendored
1
dist/tablefilter/tf-1-2aa33b10e0e549020c12.js
vendored
Normal file
8
dist/tablefilter/tf-1.js
vendored
12319
package-lock.json
generated
Normal file
63
package.json
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "tablefilter",
|
||||
"version": "0.2.65",
|
||||
"version": "0.7.3",
|
||||
"description": "A Javascript library making HTML tables filterable and a bit more",
|
||||
"license": "MIT",
|
||||
"author": {
|
||||
|
@ -22,39 +22,58 @@
|
|||
"pagination"
|
||||
],
|
||||
"scripts": {
|
||||
"test": "grunt test"
|
||||
"lint": "eslint src/**/*.js test/*.js *.js",
|
||||
"dev": "webpack --colors --config webpack.dev.config.js",
|
||||
"build": "webpack --colors --config webpack.config.js",
|
||||
"build:css": "stylus -c static/style/tablefilter.styl -o dist/tablefilter/style && stylus -c static/style/extensions -o dist/tablefilter/style && stylus -c static/style/themes/default -o dist/tablefilter/style/themes/default && stylus -c static/style/themes/mytheme -o dist/tablefilter/style/themes/mytheme && stylus -c static/style/themes/skyblue -o dist/tablefilter/style/themes/skyblue && stylus -c static/style/themes/transparent -o dist/tablefilter/style/themes/transparent",
|
||||
"build:demos": "grunt build-demos",
|
||||
"build:test": "webpack --colors --config webpack.test.config.js",
|
||||
"server": "webpack-dev-server --colors --hot --config ./webpack.dev.config.js",
|
||||
"test": "grunt test",
|
||||
"codecov": "./node_modules/.bin/codecov",
|
||||
"esdoc": "esdoc",
|
||||
"dist": "grunt",
|
||||
"build:all": "grunt build-all",
|
||||
"start": "npm run server"
|
||||
},
|
||||
"publishConfig": {
|
||||
"tag": "next"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-core": "^6.14.0",
|
||||
"babel-eslint": "6.1.2",
|
||||
"babel-loader": "^6.2.5",
|
||||
"babel-preset-es2015": "^6.14.0",
|
||||
"babel-plugin-transform-es2015-classes": "^6.14.0",
|
||||
"clean-webpack-plugin": "^0.1.9",
|
||||
"@babel/core": "7.10.0",
|
||||
"@babel/preset-env": "7.10.0",
|
||||
"babel-eslint": "10.1.0",
|
||||
"babel-loader": "^8.0.2",
|
||||
"babel-preset-env": "1.7.0",
|
||||
"clean-webpack-plugin": "^3.0.0",
|
||||
"codecov": "3.7.1",
|
||||
"diacritics": "1.3.0",
|
||||
"esdoc": "1.1.0",
|
||||
"esdoc-standard-plugin": "1.0.0",
|
||||
"eslint": "6.5.0",
|
||||
"format-number": "3.0.0",
|
||||
"grunt": "^1.0.1",
|
||||
"grunt-babel": "^6.0.0",
|
||||
"grunt-contrib-clean": "^1.0.0",
|
||||
"grunt-contrib-connect": "^1.0.2",
|
||||
"grunt-cli": "1.3.2",
|
||||
"grunt-contrib-clean": "^2.0.0",
|
||||
"grunt-contrib-connect": "^2.0.0",
|
||||
"grunt-contrib-copy": "^1.0.0",
|
||||
"grunt-contrib-qunit": "^1.1.0",
|
||||
"grunt-contrib-stylus": "^1.2.0",
|
||||
"grunt-contrib-watch": "^1.0.0",
|
||||
"grunt-esdoc": "^0.0.4",
|
||||
"grunt-eslint": "19.0.0",
|
||||
"grunt-gh-pages": "^2.0.0",
|
||||
"grunt-string-replace": "^1.3.0",
|
||||
"grunt-webpack": "^1.0.14",
|
||||
"grunt-qunit-istanbul": "1.1.0",
|
||||
"grunt-shell": "3.0.0",
|
||||
"grunt-string-replace": "^1.3.1",
|
||||
"isparta-loader": "2.0.0",
|
||||
"script-loader": "^0.7.0",
|
||||
"string-replace-webpack-plugin": "^0.0.3",
|
||||
"webpack": "^1.13.2",
|
||||
"webpack-dev-server": "^1.15.1"
|
||||
"string-replace-webpack-plugin": "^0.1.3",
|
||||
"stylus": "^0.54.5",
|
||||
"sugar-date": "2.0.6",
|
||||
"uglifyjs-webpack-plugin": "2.2.0",
|
||||
"webpack": "^4.38.0",
|
||||
"webpack-cli": "^3.0.8",
|
||||
"webpack-dev-server": "^3.1.11"
|
||||
},
|
||||
"dependencies": {},
|
||||
"bugs": {
|
||||
"url": "https://github.com/koalyptus/TableFilter/issues"
|
||||
},
|
||||
"homepage": "http://koalyptus.github.io/TableFilter"
|
||||
"homepage": "https://www.tablefilter.com"
|
||||
}
|
||||
|
|
|
@ -19,4 +19,4 @@ export const has = (arr, val, caseSensitive) => {
|
|||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
|
34
src/const.js
|
@ -73,6 +73,40 @@ export const HEADER_TAG = 'TH';
|
|||
*/
|
||||
export const CELL_TAG = 'TD';
|
||||
|
||||
/**
|
||||
* Data types
|
||||
*/
|
||||
|
||||
/**
|
||||
* String
|
||||
* @type {String}
|
||||
*/
|
||||
export const STRING = 'string';
|
||||
|
||||
/**
|
||||
* Number
|
||||
* @type {String}
|
||||
*/
|
||||
export const NUMBER = 'number';
|
||||
|
||||
/**
|
||||
* Formatted number
|
||||
* @type {String}
|
||||
*/
|
||||
export const FORMATTED_NUMBER = 'formatted-number';
|
||||
|
||||
/**
|
||||
* Date
|
||||
* @type {String}
|
||||
*/
|
||||
export const DATE = 'date';
|
||||
|
||||
/**
|
||||
* IP address
|
||||
* @type {String}
|
||||
*/
|
||||
export const IP_ADDRESS = 'ipaddress';
|
||||
|
||||
/**
|
||||
* Default values
|
||||
*/
|
||||
|
|
183
src/date.js
|
@ -1,183 +0,0 @@
|
|||
/**
|
||||
* Date utilities
|
||||
*/
|
||||
|
||||
/**
|
||||
* Verifies passed formatted date string is valid
|
||||
* @param {String} dateStr Formatted date string
|
||||
* @param {String} format accepted formats: 'DMY', 'MDY', 'YMD', 'DDMMMYYYY'
|
||||
* @return {Boolean}
|
||||
*/
|
||||
export const isValidDate = (dateStr, format) => {
|
||||
if (!format) {
|
||||
format = 'DMY';
|
||||
}
|
||||
format = format.toUpperCase();
|
||||
if (format.length !== 3) {
|
||||
if (format === 'DDMMMYYYY') {
|
||||
let d = formatDate(dateStr, format);
|
||||
dateStr = d.getDate() + '/' + (d.getMonth() + 1) + '/' +
|
||||
d.getFullYear();
|
||||
format = 'DMY';
|
||||
}
|
||||
}
|
||||
if ((format.indexOf('M') === -1) || (format.indexOf('D') === -1) ||
|
||||
(format.indexOf('Y') === -1)) {
|
||||
format = 'DMY';
|
||||
}
|
||||
let reg1, reg2;
|
||||
// If the year is first
|
||||
if (format.substring(0, 1) === 'Y') {
|
||||
reg1 = /^\d{2}(\-|\/|\.)\d{1,2}\1\d{1,2}$/;
|
||||
reg2 = /^\d{4}(\-|\/|\.)\d{1,2}\1\d{1,2}$/;
|
||||
} else if (format.substring(1, 2) === 'Y') { // If the year is second
|
||||
reg1 = /^\d{1,2}(\-|\/|\.)\d{2}\1\d{1,2}$/;
|
||||
reg2 = /^\d{1,2}(\-|\/|\.)\d{4}\1\d{1,2}$/;
|
||||
} else { // The year must be third
|
||||
reg1 = /^\d{1,2}(\-|\/|\.)\d{1,2}\1\d{2}$/;
|
||||
reg2 = /^\d{1,2}(\-|\/|\.)\d{1,2}\1\d{4}$/;
|
||||
}
|
||||
// If it doesn't conform to the right format (with either a 2 digit year
|
||||
// or 4 digit year), fail
|
||||
if (reg1.test(dateStr) === false && reg2.test(dateStr) === false) {
|
||||
return false;
|
||||
}
|
||||
// Split into 3 parts based on what the divider was
|
||||
let parts = dateStr.split(RegExp.$1);
|
||||
let mm, dd, yy;
|
||||
// Check to see if the 3 parts end up making a valid date
|
||||
if (format.substring(0, 1) === 'M') {
|
||||
mm = parts[0];
|
||||
} else if (format.substring(1, 2) === 'M') {
|
||||
mm = parts[1];
|
||||
} else {
|
||||
mm = parts[2];
|
||||
}
|
||||
if (format.substring(0, 1) === 'D') {
|
||||
dd = parts[0];
|
||||
} else if (format.substring(1, 2) === 'D') {
|
||||
dd = parts[1];
|
||||
} else {
|
||||
dd = parts[2];
|
||||
}
|
||||
if (format.substring(0, 1) === 'Y') {
|
||||
yy = parts[0];
|
||||
} else if (format.substring(1, 2) === 'Y') {
|
||||
yy = parts[1];
|
||||
} else {
|
||||
yy = parts[2];
|
||||
}
|
||||
if (parseInt(yy, 10) <= 50) {
|
||||
yy = (parseInt(yy, 10) + 2000).toString();
|
||||
}
|
||||
if (parseInt(yy, 10) <= 99) {
|
||||
yy = (parseInt(yy, 10) + 1900).toString();
|
||||
}
|
||||
let dt = new Date(
|
||||
parseInt(yy, 10), parseInt(mm, 10) - 1, parseInt(dd, 10),
|
||||
0, 0, 0, 0);
|
||||
if (parseInt(dd, 10) !== dt.getDate()) {
|
||||
return false;
|
||||
}
|
||||
if (parseInt(mm, 10) - 1 !== dt.getMonth()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts formatted date into a Date object
|
||||
* @param {String} dateStr Formatted date string
|
||||
* @param {String} format accepted formats: 'DMY', 'MDY', 'YMD', 'DDMMMYYYY'
|
||||
* @return {Object} date object
|
||||
*/
|
||||
export const formatDate = (dateStr, formatStr) => {
|
||||
if (!formatStr) {
|
||||
formatStr = 'DMY';
|
||||
}
|
||||
if (!dateStr || dateStr === '') {
|
||||
return new Date(1001, 0, 1);
|
||||
}
|
||||
let oDate;
|
||||
let parts;
|
||||
|
||||
switch (formatStr.toUpperCase()) {
|
||||
case 'DDMMMYYYY':
|
||||
parts = dateStr.replace(/[- \/.]/g, ' ').split(' ');
|
||||
oDate = new Date(y2kDate(parts[2]), mmm2mm(parts[1]) - 1, parts[0]);
|
||||
break;
|
||||
case 'DMY':
|
||||
/* eslint-disable */
|
||||
parts = dateStr.replace(
|
||||
/^(0?[1-9]|[12][0-9]|3[01])([- \/.])(0?[1-9]|1[012])([- \/.])((\d\d)?\d\d)$/, '$1 $3 $5').split(' ');
|
||||
oDate = new Date(y2kDate(parts[2]), parts[1] - 1, parts[0]);
|
||||
/* eslint-enable */
|
||||
break;
|
||||
case 'MDY':
|
||||
/* eslint-disable */
|
||||
parts = dateStr.replace(
|
||||
/^(0?[1-9]|1[012])([- \/.])(0?[1-9]|[12][0-9]|3[01])([- \/.])((\d\d)?\d\d)$/, '$1 $3 $5').split(' ');
|
||||
oDate = new Date(y2kDate(parts[2]), parts[0] - 1, parts[1]);
|
||||
/* eslint-enable */
|
||||
break;
|
||||
case 'YMD':
|
||||
/* eslint-disable */
|
||||
parts = dateStr.replace(/^((\d\d)?\d\d)([- \/.])(0?[1-9]|1[012])([- \/.])(0?[1-9]|[12][0-9]|3[01])$/, '$1 $4 $6').split(' ');
|
||||
oDate = new Date(y2kDate(parts[0]), parts[1] - 1, parts[2]);
|
||||
/* eslint-enable */
|
||||
break;
|
||||
default: //in case format is not correct
|
||||
/* eslint-disable */
|
||||
parts = dateStr.replace(/^(0?[1-9]|[12][0-9]|3[01])([- \/.])(0?[1-9]|1[012])([- \/.])((\d\d)?\d\d)$/, '$1 $3 $5').split(' ');
|
||||
oDate = new Date(y2kDate(parts[2]), parts[1] - 1, parts[0]);
|
||||
/* eslint-enable */
|
||||
break;
|
||||
}
|
||||
return oDate;
|
||||
}
|
||||
|
||||
function y2kDate(yr) {
|
||||
if (yr === undefined) {
|
||||
return 0;
|
||||
}
|
||||
if (yr.length > 2) {
|
||||
return yr;
|
||||
}
|
||||
let y;
|
||||
//>50 belong to 1900
|
||||
if (yr <= 99 && yr > 50) {
|
||||
y = '19' + yr;
|
||||
}
|
||||
//<50 belong to 2000
|
||||
if (yr < 50 || yr === '00') {
|
||||
y = '20' + yr;
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
function mmm2mm(mmm) {
|
||||
if (mmm === undefined) {
|
||||
return 0;
|
||||
}
|
||||
let mondigit;
|
||||
let MONTH_NAMES = [
|
||||
'january', 'february', 'march', 'april', 'may', 'june', 'july',
|
||||
'august', 'september', 'october', 'november', 'december',
|
||||
'jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct',
|
||||
'nov', 'dec'
|
||||
];
|
||||
for (let m_i = 0; m_i < MONTH_NAMES.length; m_i++) {
|
||||
let month_name = MONTH_NAMES[m_i];
|
||||
if (mmm.toLowerCase() === month_name) {
|
||||
mondigit = m_i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (mondigit > 11 || mondigit < 23) {
|
||||
mondigit = mondigit - 12;
|
||||
}
|
||||
if (mondigit < 1 || mondigit > 12) {
|
||||
return 0;
|
||||
}
|
||||
return mondigit;
|
||||
}
|
47
src/dom.js
|
@ -13,30 +13,30 @@ const doc = root.document;
|
|||
* @param {NodeElement} node
|
||||
* @return {String}
|
||||
*/
|
||||
export const getText = node => {
|
||||
export const getText = (node) => {
|
||||
if (isUndef(node.textContent)) {
|
||||
return trim(node.innerText);
|
||||
}
|
||||
return trim(node.textContent);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the first text node contained in the supplied node
|
||||
* @param {NodeElement} node node
|
||||
* @return {String}
|
||||
*/
|
||||
export const getFirstTextNode = node => {
|
||||
export const getFirstTextNode = (node) => {
|
||||
for (let i = 0; i < node.childNodes.length; i++) {
|
||||
let n = node.childNodes[i];
|
||||
if (n.nodeType === 3) {
|
||||
return n.data;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates an html element with given collection of attributes
|
||||
* @param {String} tag a string of the html tag to create
|
||||
* @param {String} tag html tag name
|
||||
* @param {Array} an undetermined number of arrays containing the with 2
|
||||
* items, the attribute name and its value ['id','myId']
|
||||
* @return {Object} created element
|
||||
|
@ -55,23 +55,22 @@ export const createElm = (...args) => {
|
|||
el.setAttribute(arg[0], arg[1]);
|
||||
}
|
||||
}
|
||||
|
||||
return el;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes passed node from DOM
|
||||
* @param {DOMElement} node
|
||||
* @return {DOMElement} old node reference
|
||||
*/
|
||||
export const removeElm = node => node.parentNode.removeChild(node);
|
||||
export const removeElm = (node) => node.parentNode.removeChild(node);
|
||||
|
||||
/**
|
||||
* Returns a text node with given text
|
||||
* @param {String} txt
|
||||
* @return {Object}
|
||||
*/
|
||||
export const createText = txt => doc.createTextNode(txt);
|
||||
export const createText = (txt) => doc.createTextNode(txt);
|
||||
|
||||
/**
|
||||
* Determine whether the passed elements is assigned the given class
|
||||
|
@ -88,7 +87,7 @@ export const hasClass = (ele, cls) => {
|
|||
return ele.classList.contains(cls);
|
||||
}
|
||||
return ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)'));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds the specified class to the passed element
|
||||
|
@ -111,7 +110,7 @@ export const addClass = (ele, cls) => {
|
|||
else if (!hasClass(ele, cls)) {
|
||||
ele.className += ' ' + cls;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes the specified class to the passed element
|
||||
|
@ -129,7 +128,7 @@ export const removeClass = (ele, cls) => {
|
|||
}
|
||||
let reg = new RegExp('(\\s|^)' + cls + '(\\s|$)', 'g');
|
||||
ele.className = ele.className.replace(reg, '');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates and returns an option element
|
||||
|
@ -141,27 +140,29 @@ export const removeClass = (ele, cls) => {
|
|||
export const createOpt = (text, value, isSel) => {
|
||||
let isSelected = isSel ? true : false;
|
||||
let opt = isSelected ?
|
||||
createElm('option', ['value', value], ['selected', 'true']) :
|
||||
createElm('option', ['value', value]);
|
||||
createElm('option', ['value', value], ['selected', 'true']) :
|
||||
createElm('option', ['value', value]);
|
||||
opt.appendChild(createText(text));
|
||||
return opt;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates and returns a checklist item
|
||||
* @param {Number} chkIndex index of check item
|
||||
* @param {String} id index of check item
|
||||
* @param {String} chkValue check item value
|
||||
* @param {String} labelText check item label text
|
||||
* @param {Array} extraAttr array containing attribute name and its value
|
||||
* @return {Object} li DOM element
|
||||
*/
|
||||
export const createCheckItem = (chkIndex, chkValue, labelText) => {
|
||||
export const createCheckItem = (id, chkValue, labelText, extraAttr = []) => {
|
||||
let li = createElm('li');
|
||||
let label = createElm('label', ['for', chkIndex]);
|
||||
let label = createElm('label', ['for', id]);
|
||||
let check = createElm('input',
|
||||
['id', chkIndex],
|
||||
['name', chkIndex],
|
||||
['id', id],
|
||||
['name', id],
|
||||
['type', 'checkbox'],
|
||||
['value', chkValue]
|
||||
['value', chkValue],
|
||||
extraAttr
|
||||
);
|
||||
label.appendChild(check);
|
||||
label.appendChild(createText(labelText));
|
||||
|
@ -169,14 +170,14 @@ export const createCheckItem = (chkIndex, chkValue, labelText) => {
|
|||
li.label = label;
|
||||
li.check = check;
|
||||
return li;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the element matching the supplied Id
|
||||
* @param {String} id Element identifier
|
||||
* @return {DOMElement}
|
||||
*/
|
||||
export const elm = id => doc.getElementById(id);
|
||||
export const elm = (id) => doc.getElementById(id);
|
||||
|
||||
/**
|
||||
* Returns list of element matching the supplied tag name
|
||||
|
|
40
src/event.js
|
@ -33,12 +33,11 @@ export const addEvt = (obj, type, func, capture) => {
|
|||
* @param {Boolean} capture Specifiy whether the event should be executed in
|
||||
* the capturing or in the bubbling phase
|
||||
*/
|
||||
export const removeEvt = (obj, type, func, capture) =>{
|
||||
if (obj.detachEvent) {
|
||||
obj.detachEvent('on' + type, func);
|
||||
}
|
||||
else if (obj.removeEventListener) {
|
||||
export const removeEvt = (obj, type, func, capture) => {
|
||||
if (obj.removeEventListener) {
|
||||
obj.removeEventListener(type, func, capture);
|
||||
} else if (obj.detachEvent) {
|
||||
obj.detachEvent('on' + type, func);
|
||||
} else {
|
||||
obj['on' + type] = null;
|
||||
}
|
||||
|
@ -49,7 +48,7 @@ export const removeEvt = (obj, type, func, capture) =>{
|
|||
*
|
||||
* @param {Event} evt Event on the DOM
|
||||
*/
|
||||
export const stopEvt = evt =>{
|
||||
export const stopEvt = (evt) => {
|
||||
if (!evt) {
|
||||
evt = root.event;
|
||||
}
|
||||
|
@ -66,7 +65,7 @@ export const stopEvt = evt =>{
|
|||
*
|
||||
* @param {Event} evt Event on the DOM
|
||||
*/
|
||||
export const cancelEvt = evt => {
|
||||
export const cancelEvt = (evt) => {
|
||||
if (!evt) {
|
||||
evt = root.event;
|
||||
}
|
||||
|
@ -83,7 +82,7 @@ export const cancelEvt = evt => {
|
|||
* @param {Event} evt Event on the DOM
|
||||
* @returns {DOMElement}
|
||||
*/
|
||||
export const targetEvt = evt => {
|
||||
export const targetEvt = (evt) => {
|
||||
if (!evt) {
|
||||
evt = root.event;
|
||||
}
|
||||
|
@ -96,7 +95,30 @@ export const targetEvt = evt => {
|
|||
* @param {Event} evt Event on the DOM
|
||||
* @returns {Number}
|
||||
*/
|
||||
export const keyCode = evt => {
|
||||
export const keyCode = (evt) => {
|
||||
return evt.charCode ? evt.charCode :
|
||||
(evt.keyCode ? evt.keyCode : (evt.which ? evt.which : 0));
|
||||
};
|
||||
|
||||
/**
|
||||
* Check code of pressed key is one of the expected key codes
|
||||
*
|
||||
* @param {Event} evt key event
|
||||
* @param {Array} keyCodes list of keycodes to check
|
||||
*/
|
||||
export const isKeyPressed = (evt, keyCodes = []) => {
|
||||
return keyCodes.indexOf(keyCode(evt)) !== -1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Bind passed function to passed scope
|
||||
* @param {Function} fn function
|
||||
* @param {Object} scope object instance
|
||||
*/
|
||||
export function bound(fn, scope) {
|
||||
let boundFnName = `${fn.name}_bound`;
|
||||
if (!scope[boundFnName]) {
|
||||
scope[boundFnName] = fn.bind(scope);
|
||||
}
|
||||
return scope[boundFnName];
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import {Feature} from '../../feature';
|
||||
import {tag} from '../../dom';
|
||||
import {INPUT} from '../../const';
|
||||
import {defaultsStr} from '../../settings';
|
||||
import {root} from '../../root';
|
||||
|
||||
const INSTANTIATION_ERROR = `Failed to instantiate EditTable object.
|
||||
|
@ -20,19 +21,19 @@ export default class AdapterEzEditTable extends Feature {
|
|||
* @param {Object} cfg Configuration options for ezEditTable library
|
||||
*/
|
||||
constructor(tf, cfg) {
|
||||
super(tf, cfg.name);
|
||||
super(tf, AdapterEzEditTable);
|
||||
|
||||
/**
|
||||
* Module description
|
||||
* @type {String}
|
||||
*/
|
||||
this.desc = cfg.description || 'ezEditTable adapter';
|
||||
this.desc = defaultsStr(cfg.description, 'ezEditTable adapter');
|
||||
|
||||
/**
|
||||
* Filename of ezEditTable library
|
||||
* @type {String}
|
||||
*/
|
||||
this.filename = cfg.filename || 'ezEditTable.js';
|
||||
this.filename = defaultsStr(cfg.filename, 'ezEditTable.js');
|
||||
|
||||
/**
|
||||
* Path to ezEditTable library
|
||||
|
@ -50,13 +51,15 @@ export default class AdapterEzEditTable extends Feature {
|
|||
* Path to ezEditTable stylesheet
|
||||
* @type {String}
|
||||
*/
|
||||
this.stylesheet = cfg.stylesheet || this.vendorPath + 'ezEditTable.css';
|
||||
this.stylesheet = defaultsStr(cfg.stylesheet,
|
||||
this.vendorPath + 'ezEditTable.css');
|
||||
|
||||
/**
|
||||
* Name of ezEditTable stylesheet
|
||||
* @type {String}
|
||||
*/
|
||||
this.stylesheetName = cfg.stylesheet_name || 'ezEditTableCss';
|
||||
this.stylesheetName = defaultsStr(cfg.stylesheet_name,
|
||||
'ezEditTableCss');
|
||||
|
||||
// Enable the ezEditTable's scroll into view behaviour if grid layout on
|
||||
cfg.scroll_into_view = cfg.scroll_into_view === false ?
|
||||
|
@ -117,7 +120,7 @@ export default class AdapterEzEditTable extends Feature {
|
|||
//start row for EditTable constructor needs to be calculated
|
||||
let startRow,
|
||||
cfg = this.cfg,
|
||||
thead = tag(tf.tbl, 'thead');
|
||||
thead = tag(tf.dom(), 'thead');
|
||||
|
||||
//if thead exists and startRow not specified, startRow is calculated
|
||||
//automatically by EditTable
|
||||
|
@ -158,7 +161,7 @@ export default class AdapterEzEditTable extends Feature {
|
|||
et.ClearSelections();
|
||||
/* eslint-enable */
|
||||
let cellIndex = selectedElm.cellIndex,
|
||||
row = tf.tbl.rows[nextRowIndex];
|
||||
row = tf.dom().rows[nextRowIndex];
|
||||
if (et.defaultSelection === 'both') {
|
||||
/* eslint-disable */
|
||||
slc.SelectRowByIndex(nextRowIndex);
|
||||
|
@ -172,7 +175,7 @@ export default class AdapterEzEditTable extends Feature {
|
|||
}
|
||||
//Table is filtered
|
||||
if (tf.validRowsIndex.length !== tf.getRowsNb()) {
|
||||
let r = tf.tbl.rows[nextRowIndex];
|
||||
let r = tf.dom().rows[nextRowIndex];
|
||||
if (r) {
|
||||
r.scrollIntoView(false);
|
||||
}
|
||||
|
@ -207,9 +210,9 @@ export default class AdapterEzEditTable extends Feature {
|
|||
nextRowIndex,
|
||||
paging = tf.feature('paging'),
|
||||
//pgup/pgdown keys
|
||||
d = (keyCode === 34 || keyCode === 33 ?
|
||||
(paging && paging.pagingLength || et.nbRowsPerPage) :
|
||||
1);
|
||||
d = keyCode === 34 || keyCode === 33 ?
|
||||
(paging && paging.pageLength || et.nbRowsPerPage) :
|
||||
1;
|
||||
|
||||
//If next row is not valid, next valid filtered row needs to be
|
||||
//calculated
|
||||
|
@ -278,11 +281,11 @@ export default class AdapterEzEditTable extends Feature {
|
|||
if (tf.feature('paging').nbPages > 1) {
|
||||
let paging = tf.feature('paging');
|
||||
//page length is re-assigned in case it has changed
|
||||
et.nbRowsPerPage = paging.pagingLength;
|
||||
et.nbRowsPerPage = paging.pageLength;
|
||||
let validIndexes = tf.validRowsIndex,
|
||||
validIdxLen = validIndexes.length,
|
||||
pagingEndRow = parseInt(paging.startPagingRow, 10) +
|
||||
parseInt(paging.pagingLength, 10);
|
||||
parseInt(paging.pageLength, 10);
|
||||
let rowIndex = row.rowIndex;
|
||||
|
||||
if ((rowIndex === validIndexes[validIdxLen - 1]) &&
|
||||
|
@ -332,35 +335,35 @@ export default class AdapterEzEditTable extends Feature {
|
|||
if (cfg.default_selection === 'row') {
|
||||
let fnB = cfg.on_before_selected_row;
|
||||
cfg.on_before_selected_row = function () {
|
||||
onBeforeSelection(arguments[0], arguments[1], arguments[2]);
|
||||
var args = arguments;
|
||||
onBeforeSelection(args[0], args[1], args[2]);
|
||||
if (fnB) {
|
||||
fnB.call(
|
||||
null, arguments[0], arguments[1], arguments[2]);
|
||||
fnB.call(null, args[0], args[1], args[2]);
|
||||
}
|
||||
};
|
||||
let fnA = cfg.on_after_selected_row;
|
||||
cfg.on_after_selected_row = function () {
|
||||
onAfterSelection(arguments[0], arguments[1], arguments[2]);
|
||||
var args = arguments;
|
||||
onAfterSelection(args[0], args[1], args[2]);
|
||||
if (fnA) {
|
||||
fnA.call(
|
||||
null, arguments[0], arguments[1], arguments[2]);
|
||||
fnA.call(null, args[0], args[1], args[2]);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
let fnD = cfg.on_before_selected_cell;
|
||||
cfg.on_before_selected_cell = function () {
|
||||
onBeforeSelection(arguments[0], arguments[1], arguments[2]);
|
||||
var args = arguments;
|
||||
onBeforeSelection(args[0], args[1], args[2]);
|
||||
if (fnD) {
|
||||
fnD.call(
|
||||
null, arguments[0], arguments[1], arguments[2]);
|
||||
fnD.call(null, args[0], args[1], args[2]);
|
||||
}
|
||||
};
|
||||
let fnC = cfg.on_after_selected_cell;
|
||||
cfg.on_after_selected_cell = function () {
|
||||
onAfterSelection(arguments[0], arguments[1], arguments[2]);
|
||||
var args = arguments;
|
||||
onAfterSelection(args[0], args[1], args[2]);
|
||||
if (fnC) {
|
||||
fnC.call(
|
||||
null, arguments[0], arguments[1], arguments[2]);
|
||||
fnC.call(null, args[0], args[1], args[2]);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -369,6 +372,7 @@ export default class AdapterEzEditTable extends Feature {
|
|||
//Added or removed rows, TF rows number needs to be re-calculated
|
||||
let fnE = cfg.on_added_dom_row;
|
||||
cfg.on_added_dom_row = function () {
|
||||
var args = arguments;
|
||||
tf.nbFilterableRows++;
|
||||
if (!tf.paging) {
|
||||
tf.emitter.emit('rows-changed', tf, this);
|
||||
|
@ -382,12 +386,13 @@ export default class AdapterEzEditTable extends Feature {
|
|||
tf.feature('alternateRows').init();
|
||||
}
|
||||
if (fnE) {
|
||||
fnE.call(null, arguments[0], arguments[1], arguments[2]);
|
||||
fnE.call(null, args[0], args[1], args[2]);
|
||||
}
|
||||
};
|
||||
if (cfg.actions && cfg.actions['delete']) {
|
||||
let fnF = cfg.actions['delete'].on_after_submit;
|
||||
cfg.actions['delete'].on_after_submit = function () {
|
||||
var args = arguments;
|
||||
tf.nbFilterableRows--;
|
||||
if (!tf.paging) {
|
||||
tf.emitter.emit('rows-changed', tf, this);
|
||||
|
@ -401,7 +406,7 @@ export default class AdapterEzEditTable extends Feature {
|
|||
tf.feature('alternateRows').init();
|
||||
}
|
||||
if (fnF) {
|
||||
fnF.call(null, arguments[0], arguments[1]);
|
||||
fnF.call(null, args[0], args[1]);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -500,3 +505,5 @@ export default class AdapterEzEditTable extends Feature {
|
|||
this.initialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
AdapterEzEditTable.meta = {altName: 'advancedGrid'};
|
||||
|
|
|
@ -1,6 +1,25 @@
|
|||
import {Feature} from '../../feature';
|
||||
import {createText, elm} from '../../dom';
|
||||
import {isArray, isFn, isUndef} from '../../types';
|
||||
import {isArray, isEmpty, EMPTY_FN} from '../../types';
|
||||
import {numSortAsc} from '../../sort';
|
||||
import {FORMATTED_NUMBER} from '../../const';
|
||||
import formatNumber from 'format-number';
|
||||
import {defaultsFn, defaultsArr} from '../../settings';
|
||||
import {bound} from '../../event';
|
||||
|
||||
const EVENTS = [
|
||||
'after-filtering',
|
||||
'after-page-change',
|
||||
'after-page-length-change'
|
||||
];
|
||||
|
||||
const SUM = 'sum';
|
||||
const MEAN = 'mean';
|
||||
const MIN = 'min';
|
||||
const MAX = 'max';
|
||||
const MEDIAN = 'median';
|
||||
const Q1 = 'q1';
|
||||
const Q3 = 'q3';
|
||||
|
||||
/**
|
||||
* Column calculations extension
|
||||
|
@ -14,21 +33,19 @@ export default class ColOps extends Feature {
|
|||
* @param {Object} opts Configuration object
|
||||
*/
|
||||
constructor(tf, opts) {
|
||||
super(tf, opts.name);
|
||||
super(tf, ColOps);
|
||||
|
||||
/**
|
||||
* Callback fired before columns operations start
|
||||
* @type {Function}
|
||||
*/
|
||||
this.onBeforeOperation = isFn(opts.on_before_operation) ?
|
||||
opts.on_before_operation : null;
|
||||
this.onBeforeOperation = defaultsFn(opts.on_before_operation, EMPTY_FN);
|
||||
|
||||
/**
|
||||
* Callback fired after columns operations are completed
|
||||
* @type {Function}
|
||||
*/
|
||||
this.onAfterOperation = isFn(opts.on_after_operation) ?
|
||||
opts.on_after_operation : null;
|
||||
this.onAfterOperation = defaultsFn(opts.on_after_operation, EMPTY_FN);
|
||||
|
||||
/**
|
||||
* Configuration options
|
||||
|
@ -36,6 +53,58 @@ export default class ColOps extends Feature {
|
|||
*/
|
||||
this.opts = opts;
|
||||
|
||||
/**
|
||||
* List of DOM element IDs containing column's calculation result
|
||||
* @type {Array}
|
||||
*/
|
||||
this.labelIds = defaultsArr(opts.id, []);
|
||||
|
||||
/**
|
||||
* List of columns' indexes for calculations
|
||||
* @type {Array}
|
||||
*/
|
||||
this.colIndexes = defaultsArr(opts.col, []);
|
||||
|
||||
/**
|
||||
* List of operations - possible values: 'sum', 'mean', 'min', 'max',
|
||||
* 'median', 'q1', 'q3'
|
||||
* @type {Array}
|
||||
*/
|
||||
this.operations = defaultsArr(opts.operation, []);
|
||||
|
||||
/**
|
||||
* List of write methods used to write the result - possible values:
|
||||
* 'innerHTML', 'setValue', 'createTextNode'
|
||||
* @type {Array}
|
||||
*/
|
||||
this.outputTypes = defaultsArr(opts.write_method, []);
|
||||
|
||||
/**
|
||||
* List of format objects used for formatting the result -
|
||||
* refer to https://github.com/componitable/format-number to check
|
||||
* configuration options
|
||||
* @type {Array}
|
||||
*/
|
||||
this.formatResults = defaultsArr(opts.format_result, []);
|
||||
|
||||
/**
|
||||
* List of row indexes displaying the results
|
||||
* @type {Array}
|
||||
*/
|
||||
this.totRowIndexes = defaultsArr(opts.tot_row_index, []);
|
||||
|
||||
/**
|
||||
* List of row indexes excluded from calculations
|
||||
* @type {Array}
|
||||
*/
|
||||
this.excludeRows = defaultsArr(opts.exclude_row, []);
|
||||
|
||||
/**
|
||||
* List of decimal precision for calculation results
|
||||
* @type {Array}
|
||||
*/
|
||||
this.decimalPrecisions = defaultsArr(opts.decimal_precision, 2);
|
||||
|
||||
this.enable();
|
||||
}
|
||||
|
||||
|
@ -47,13 +116,11 @@ export default class ColOps extends Feature {
|
|||
return;
|
||||
}
|
||||
// subscribe to events
|
||||
this.emitter.on(['after-filtering'], () => this.calc());
|
||||
this.emitter.on(EVENTS, bound(this.calcAll, this));
|
||||
|
||||
this.calc();
|
||||
this.calcAll();
|
||||
|
||||
/**
|
||||
* @inherited
|
||||
*/
|
||||
/** @inherited */
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
|
@ -73,285 +140,340 @@ export default class ColOps extends Feature {
|
|||
* (1) optimized the routine (now it will only process each column once),
|
||||
* (2) added calculations for the median, lower and upper quartile.
|
||||
*/
|
||||
calc() {
|
||||
calcAll() {
|
||||
let tf = this.tf;
|
||||
if (!tf.isInitialized()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.onBeforeOperation) {
|
||||
this.onBeforeOperation.call(null, tf, this);
|
||||
}
|
||||
this.onBeforeOperation(tf, this);
|
||||
this.emitter.emit('before-column-operation', tf, this);
|
||||
|
||||
let opts = this.opts,
|
||||
labelId = opts.id,
|
||||
colIndex = opts.col,
|
||||
operation = opts.operation,
|
||||
outputType = opts.write_method,
|
||||
totRowIndex = opts.tot_row_index,
|
||||
excludeRow = opts.exclude_row,
|
||||
decimalPrecision = isUndef(opts.decimal_precision) ?
|
||||
2 : opts.decimal_precision;
|
||||
let { colIndexes, operations: colOperations, outputTypes,
|
||||
totRowIndexes, excludeRows, formatResults,
|
||||
decimalPrecisions } = this;
|
||||
|
||||
//nuovella: determine unique list of columns to operate on
|
||||
let ucolIndex = [],
|
||||
ucolMax = 0;
|
||||
ucolIndex[ucolMax] = colIndex[0];
|
||||
|
||||
for (let ii = 1; ii < colIndex.length; ii++) {
|
||||
let saved = 0;
|
||||
//see if colIndex[ii] is already in the list of unique indexes
|
||||
for (let jj = 0; jj <= ucolMax; jj++) {
|
||||
if (ucolIndex[jj] === colIndex[ii]) {
|
||||
saved = 1;
|
||||
}
|
||||
let uIndexes = [];
|
||||
colIndexes.forEach((val) => {
|
||||
if (uIndexes.indexOf(val) === -1) {
|
||||
uIndexes.push(val);
|
||||
}
|
||||
//if not saved then, save the index;
|
||||
if (saved === 0) {
|
||||
ucolMax++;
|
||||
ucolIndex[ucolMax] = colIndex[ii];
|
||||
});
|
||||
|
||||
let nbCols = uIndexes.length,
|
||||
rows = tf.dom().rows,
|
||||
colValues = [];
|
||||
|
||||
for (let u = 0; u < nbCols; u++) {
|
||||
//this retrieves col values
|
||||
//use uIndexes because we only want to pass through this loop
|
||||
//once for each column get the values in this unique column
|
||||
colValues.push(
|
||||
tf.getVisibleColumnData(uIndexes[u], false, excludeRows)
|
||||
);
|
||||
|
||||
let curValues = colValues[u];
|
||||
|
||||
//next: calculate all operations for this column
|
||||
let result = 0,
|
||||
operations = [],
|
||||
precisions = [],
|
||||
labels = [],
|
||||
writeType,
|
||||
formatResult = [],
|
||||
idx = 0;
|
||||
|
||||
for (let k = 0; k < colIndexes.length; k++) {
|
||||
if (colIndexes[k] !== uIndexes[u]) {
|
||||
continue;
|
||||
}
|
||||
operations[idx] = (colOperations[k] || 'sum').toLowerCase();
|
||||
precisions[idx] = decimalPrecisions[k];
|
||||
labels[idx] = this.labelIds[k];
|
||||
writeType = isArray(outputTypes) ? outputTypes[k] : null;
|
||||
formatResult[idx] =
|
||||
this.configureFormat(uIndexes[u], formatResults[k]);
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
if (isArray(labelId) && isArray(colIndex) && isArray(operation)) {
|
||||
let rows = tf.tbl.rows,
|
||||
colvalues = [];
|
||||
for (let i = 0; i < idx; i++) {
|
||||
// emit values before column calculation
|
||||
this.emitter.emit(
|
||||
'before-column-calc',
|
||||
tf,
|
||||
this,
|
||||
uIndexes[u],
|
||||
curValues,
|
||||
operations[i],
|
||||
precisions[i]
|
||||
);
|
||||
|
||||
for (let ucol = 0; ucol <= ucolMax; ucol++) {
|
||||
//this retrieves col values
|
||||
//use ucolIndex because we only want to pass through this loop
|
||||
//once for each column get the values in this unique column
|
||||
colvalues.push(
|
||||
tf.getColValues(ucolIndex[ucol], false, true, excludeRow));
|
||||
result = Number(this.calc(curValues, operations[i], null));
|
||||
|
||||
//next: calculate all operations for this column
|
||||
let result,
|
||||
nbvalues = 0,
|
||||
temp,
|
||||
meanValue = 0,
|
||||
sumValue = 0,
|
||||
minValue = null,
|
||||
maxValue = null,
|
||||
q1Value = null,
|
||||
medValue = null,
|
||||
q3Value = null,
|
||||
meanFlag = 0,
|
||||
sumFlag = 0,
|
||||
minFlag = 0,
|
||||
maxFlag = 0,
|
||||
q1Flag = 0,
|
||||
medFlag = 0,
|
||||
q3Flag = 0,
|
||||
theList = [],
|
||||
opsThisCol = [],
|
||||
decThisCol = [],
|
||||
labThisCol = [],
|
||||
oTypeThisCol = [],
|
||||
mThisCol = -1;
|
||||
// emit column calculation result
|
||||
this.emitter.emit(
|
||||
'column-calc',
|
||||
tf,
|
||||
this,
|
||||
uIndexes[u],
|
||||
result,
|
||||
operations[i],
|
||||
precisions[i]
|
||||
);
|
||||
|
||||
for (let k = 0; k < colIndex.length; k++) {
|
||||
if (colIndex[k] === ucolIndex[ucol]) {
|
||||
mThisCol++;
|
||||
opsThisCol[mThisCol] = operation[k].toLowerCase();
|
||||
decThisCol[mThisCol] = decimalPrecision[k];
|
||||
labThisCol[mThisCol] = labelId[k];
|
||||
oTypeThisCol = isArray(outputType) ?
|
||||
outputType[k] : null;
|
||||
// write result in expected DOM element
|
||||
this.writeResult(
|
||||
result,
|
||||
labels[i],
|
||||
writeType,
|
||||
precisions[i],
|
||||
formatResult[i]
|
||||
);
|
||||
|
||||
switch (opsThisCol[mThisCol]) {
|
||||
case 'mean':
|
||||
meanFlag = 1;
|
||||
break;
|
||||
case 'sum':
|
||||
sumFlag = 1;
|
||||
break;
|
||||
case 'min':
|
||||
minFlag = 1;
|
||||
break;
|
||||
case 'max':
|
||||
maxFlag = 1;
|
||||
break;
|
||||
case 'median':
|
||||
medFlag = 1;
|
||||
break;
|
||||
case 'q1':
|
||||
q1Flag = 1;
|
||||
break;
|
||||
case 'q3':
|
||||
q3Flag = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}//for i
|
||||
|
||||
for (let j = 0; j < colvalues[ucol].length; j++) {
|
||||
//sort the list for calculation of median and quartiles
|
||||
if ((q1Flag === 1) || (q3Flag === 1) || (medFlag === 1)) {
|
||||
if (j < colvalues[ucol].length - 1) {
|
||||
for (k = j + 1; k < colvalues[ucol].length; k++) {
|
||||
/* eslint-disable */
|
||||
if (eval(colvalues[ucol][k]) <
|
||||
eval(colvalues[ucol][j])) {
|
||||
/* eslint-enable */
|
||||
temp = colvalues[ucol][j];
|
||||
colvalues[ucol][j] = colvalues[ucol][k];
|
||||
colvalues[ucol][k] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let cvalue = parseFloat(colvalues[ucol][j]);
|
||||
theList[j] = parseFloat(cvalue);
|
||||
// row(s) with result are always visible
|
||||
let totRow = totRowIndexes && totRowIndexes[u] ?
|
||||
rows[totRowIndexes[u]] : null;
|
||||
if (totRow) {
|
||||
totRow.style.display = '';
|
||||
}
|
||||
}//for u
|
||||
|
||||
if (!isNaN(cvalue)) {
|
||||
nbvalues++;
|
||||
if (sumFlag === 1 || meanFlag === 1) {
|
||||
sumValue += parseFloat(cvalue);
|
||||
}
|
||||
if (minFlag === 1) {
|
||||
if (minValue === null) {
|
||||
minValue = parseFloat(cvalue);
|
||||
} else {
|
||||
minValue = parseFloat(cvalue) < minValue ?
|
||||
parseFloat(cvalue) : minValue;
|
||||
}
|
||||
}
|
||||
if (maxFlag === 1) {
|
||||
if (maxValue === null) {
|
||||
maxValue = parseFloat(cvalue);
|
||||
} else {
|
||||
maxValue = parseFloat(cvalue) > maxValue ?
|
||||
parseFloat(cvalue) : maxValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}//for j
|
||||
if (meanFlag === 1) {
|
||||
meanValue = sumValue / nbvalues;
|
||||
}
|
||||
if (medFlag === 1) {
|
||||
let aux = 0;
|
||||
if (nbvalues % 2 === 1) {
|
||||
aux = Math.floor(nbvalues / 2);
|
||||
medValue = theList[aux];
|
||||
} else {
|
||||
medValue = (theList[nbvalues / 2] +
|
||||
theList[((nbvalues / 2) - 1)]) / 2;
|
||||
}
|
||||
}
|
||||
let posa;
|
||||
if (q1Flag === 1) {
|
||||
posa = 0.0;
|
||||
posa = Math.floor(nbvalues / 4);
|
||||
if (4 * posa === nbvalues) {
|
||||
q1Value = (theList[posa - 1] + theList[posa]) / 2;
|
||||
} else {
|
||||
q1Value = theList[posa];
|
||||
}
|
||||
}
|
||||
if (q3Flag === 1) {
|
||||
posa = 0.0;
|
||||
let posb = 0.0;
|
||||
posa = Math.floor(nbvalues / 4);
|
||||
if (4 * posa === nbvalues) {
|
||||
posb = 3 * posa;
|
||||
q3Value = (theList[posb] + theList[posb - 1]) / 2;
|
||||
} else {
|
||||
q3Value = theList[nbvalues - posa - 1];
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i <= mThisCol; i++) {
|
||||
switch (opsThisCol[i]) {
|
||||
case 'mean':
|
||||
result = meanValue;
|
||||
break;
|
||||
case 'sum':
|
||||
result = sumValue;
|
||||
break;
|
||||
case 'min':
|
||||
result = minValue;
|
||||
break;
|
||||
case 'max':
|
||||
result = maxValue;
|
||||
break;
|
||||
case 'median':
|
||||
result = medValue;
|
||||
break;
|
||||
case 'q1':
|
||||
result = q1Value;
|
||||
break;
|
||||
case 'q3':
|
||||
result = q3Value;
|
||||
break;
|
||||
}
|
||||
|
||||
let precision = !isNaN(decThisCol[i]) ? decThisCol[i] : 2;
|
||||
|
||||
//if outputType is defined
|
||||
if (oTypeThisCol && result) {
|
||||
result = result.toFixed(precision);
|
||||
|
||||
if (elm(labThisCol[i])) {
|
||||
switch (oTypeThisCol.toLowerCase()) {
|
||||
case 'innerhtml':
|
||||
if (isNaN(result) || !isFinite(result) ||
|
||||
nbvalues === 0) {
|
||||
elm(labThisCol[i]).innerHTML = '.';
|
||||
} else {
|
||||
elm(labThisCol[i]).innerHTML = result;
|
||||
}
|
||||
break;
|
||||
case 'setvalue':
|
||||
elm(labThisCol[i]).value = result;
|
||||
break;
|
||||
case 'createtextnode':
|
||||
let oldnode =
|
||||
elm(labThisCol[i]).firstChild;
|
||||
let txtnode = createText(result);
|
||||
elm(labThisCol[i])
|
||||
.replaceChild(txtnode, oldnode);
|
||||
break;
|
||||
}//switch
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
if (isNaN(result) || !isFinite(result) ||
|
||||
nbvalues === 0) {
|
||||
elm(labThisCol[i]).innerHTML = '.';
|
||||
} else {
|
||||
elm(labThisCol[i]).innerHTML =
|
||||
result.toFixed(precision);
|
||||
}
|
||||
} catch (e) { }//catch
|
||||
}//else
|
||||
}//for i
|
||||
|
||||
// row(s) with result are always visible
|
||||
let totRow = totRowIndex && totRowIndex[ucol] ?
|
||||
rows[totRowIndex[ucol]] : null;
|
||||
if (totRow) {
|
||||
totRow.style.display = '';
|
||||
}
|
||||
}//for ucol
|
||||
}//if typeof
|
||||
|
||||
if (this.onAfterOperation) {
|
||||
this.onAfterOperation.call(null, tf, this);
|
||||
}
|
||||
this.onAfterOperation(tf, this);
|
||||
this.emitter.emit('after-column-operation', tf, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove extension
|
||||
* Make desired calculation on specified column.
|
||||
* @param {Number} colIndex Column index
|
||||
* @param {String} [operation=SUM] Operation type
|
||||
* @param {Number} precision Decimal precision
|
||||
* @returns {Number}
|
||||
*/
|
||||
columnCalc(colIndex, operation = SUM, precision) {
|
||||
let excludeRows = this.excludeRows || [];
|
||||
let colValues = tf.getVisibleColumnData(colIndex, false, excludeRows);
|
||||
|
||||
return Number(this.calc(colValues, operation, precision));
|
||||
}
|
||||
|
||||
/**
|
||||
* Make calculation on passed values.
|
||||
* @param {Array} values List of values
|
||||
* @param {String} [operation=SUM] Optional operation type
|
||||
* @param {Number} precision Optional result precision
|
||||
* @returns {Number}
|
||||
* @private
|
||||
*/
|
||||
calc(colValues, operation = SUM, precision) {
|
||||
let result = 0;
|
||||
|
||||
if (operation === Q1 || operation === Q3 || operation === MEDIAN) {
|
||||
colValues = this.sortColumnValues(colValues, numSortAsc);
|
||||
}
|
||||
|
||||
switch (operation) {
|
||||
case MEAN:
|
||||
result = this.calcMean(colValues);
|
||||
break;
|
||||
case SUM:
|
||||
result = this.calcSum(colValues);
|
||||
break;
|
||||
case MIN:
|
||||
result = this.calcMin(colValues);
|
||||
break;
|
||||
case MAX:
|
||||
result = this.calcMax(colValues);
|
||||
break;
|
||||
case MEDIAN:
|
||||
result = this.calcMedian(colValues);
|
||||
break;
|
||||
case Q1:
|
||||
result = this.calcQ1(colValues);
|
||||
break;
|
||||
case Q3:
|
||||
result = this.calcQ3(colValues);
|
||||
break;
|
||||
}
|
||||
|
||||
return isEmpty(precision) ? result : result.toFixed(precision);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the sum of passed values.
|
||||
* @param {Array} [values=[]] List of values
|
||||
* @returns {Number}
|
||||
*/
|
||||
calcSum(values = []) {
|
||||
if (isEmpty(values)) {
|
||||
return 0;
|
||||
}
|
||||
let result = values.reduce((x, y) => Number(x) + Number(y));
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the mean of passed values.
|
||||
* @param {Array} [values=[]] List of values
|
||||
* @returns {Number}
|
||||
*/
|
||||
calcMean(values = []) {
|
||||
let result = this.calcSum(values) / values.length;
|
||||
return Number(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the max value of passed values.
|
||||
* @param {Array} [values=[]] List of values
|
||||
* @returns {Number}
|
||||
*/
|
||||
calcMax(values = []) {
|
||||
return Math.max.apply(null, values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the min value of passed values.
|
||||
* @param {Array} [values=[]] List of values
|
||||
* @returns {Number}
|
||||
*/
|
||||
calcMin(values = []) {
|
||||
return Math.min.apply(null, values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the median of passed values.
|
||||
* @param {Array} [values=[]] List of values
|
||||
* @returns {Number}
|
||||
*/
|
||||
calcMedian(values = []) {
|
||||
let nbValues = values.length;
|
||||
let aux = 0;
|
||||
if (nbValues % 2 === 1) {
|
||||
aux = Math.floor(nbValues / 2);
|
||||
return Number(values[aux]);
|
||||
}
|
||||
return (Number(values[nbValues / 2]) +
|
||||
Number(values[((nbValues / 2) - 1)])) / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the lower quartile of passed values.
|
||||
* @param {Array} [values=[]] List of values
|
||||
* @returns {Number}
|
||||
*/
|
||||
calcQ1(values = []) {
|
||||
let nbValues = values.length;
|
||||
let posa = 0.0;
|
||||
posa = Math.floor(nbValues / 4);
|
||||
if (4 * posa === nbValues) {
|
||||
return (Number(values[posa - 1]) +
|
||||
Number(values[posa])) / 2;
|
||||
}
|
||||
return Number(values[posa]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the upper quartile of passed values.
|
||||
* @param {Array} [values=[]] List of values
|
||||
* @returns {Number}
|
||||
*/
|
||||
calcQ3(values = []) {
|
||||
let nbValues = values.length;
|
||||
let posa = 0.0;
|
||||
let posb = 0.0;
|
||||
posa = Math.floor(nbValues / 4);
|
||||
if (4 * posa === nbValues) {
|
||||
posb = 3 * posa;
|
||||
return (Number(values[posb]) +
|
||||
Number(values[posb - 1])) / 2;
|
||||
}
|
||||
return Number(values[nbValues - posa - 1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort passed values with supplied sorter function.
|
||||
* @param {Array} [values=[]] List of values to be sorted
|
||||
* @param {Function} sorter Sorter function
|
||||
* @returns {Array}
|
||||
*/
|
||||
sortColumnValues(values = [], sorter) {
|
||||
return values.sort(sorter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write calculation result in passed DOM element with supplied write method
|
||||
* and decimal precision.
|
||||
* @param {Number} [result=0] Calculation result
|
||||
* @param {DOMElement} label DOM element
|
||||
* @param {String} [writeType='innerhtml'] Write method
|
||||
* @param {Number} [precision=2] Applied decimal precision
|
||||
* @private
|
||||
*/
|
||||
writeResult(result = 0, label, writeType = 'innerhtml',
|
||||
precision = 2, format = {}) {
|
||||
let labelElm = elm(label);
|
||||
|
||||
if (!labelElm) {
|
||||
return;
|
||||
}
|
||||
|
||||
result = result.toFixed(precision);
|
||||
if (isNaN(result) || !isFinite(result)) {
|
||||
result = '';
|
||||
} else {
|
||||
result = formatNumber(format)(result);
|
||||
}
|
||||
|
||||
switch (writeType.toLowerCase()) {
|
||||
case 'innerhtml':
|
||||
labelElm.innerHTML = result;
|
||||
break;
|
||||
case 'setvalue':
|
||||
labelElm.value = result;
|
||||
break;
|
||||
case 'createtextnode':
|
||||
let oldNode = labelElm.firstChild;
|
||||
let txtNode = createText(result);
|
||||
labelElm.replaceChild(txtNode, oldNode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the format options used to format the operation result based
|
||||
* on column type.
|
||||
* @param {Number} colIndex Column index
|
||||
* @param {Object} [format={}] Format object
|
||||
* @returns {Object}
|
||||
* @private
|
||||
*/
|
||||
configureFormat(colIndex, format = {}) {
|
||||
let tf = this.tf;
|
||||
if (tf.hasType(colIndex, [FORMATTED_NUMBER])) {
|
||||
let colType = tf.colTypes[colIndex];
|
||||
if (colType.decimal && !format.decimal) {
|
||||
format.decimal = colType.decimal;
|
||||
}
|
||||
if (colType.thousands && !format.integerSeparator) {
|
||||
format.integerSeparator = colType.thousands;
|
||||
}
|
||||
} else {
|
||||
format.decimal = format.decimal || '';
|
||||
format.integerSeparator = format.integerSeparator || '';
|
||||
}
|
||||
return format;
|
||||
}
|
||||
|
||||
/** Remove extension */
|
||||
destroy() {
|
||||
if (!this.initialized) {
|
||||
return;
|
||||
}
|
||||
// unsubscribe to events
|
||||
this.emitter.off(['after-filtering'], () => this.calc());
|
||||
this.emitter.off(EVENTS, bound(this.calcAll, this));
|
||||
|
||||
this.initialized = false;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,16 @@
|
|||
import {Feature} from '../../feature';
|
||||
import {
|
||||
addClass, removeClass, createCheckItem, createElm, elm, removeElm,
|
||||
getText
|
||||
getText, tag
|
||||
} from '../../dom';
|
||||
import {isFn} from '../../types';
|
||||
import {addEvt, targetEvt} from '../../event';
|
||||
import {isUndef, EMPTY_FN, isNull} from '../../types';
|
||||
import {addEvt, targetEvt, removeEvt} from '../../event';
|
||||
import {root} from '../../root';
|
||||
import {NONE} from '../../const';
|
||||
import {
|
||||
defaultsBool, defaultsStr, defaultsFn, defaultsNb, defaultsArr
|
||||
} from '../../settings';
|
||||
import {RIGHT} from '../../modules/toolbar';
|
||||
|
||||
/**
|
||||
* Columns Visibility extension
|
||||
|
@ -17,7 +23,7 @@ export default class ColsVisibility extends Feature {
|
|||
* @param {Object} Configuration object
|
||||
*/
|
||||
constructor(tf, f) {
|
||||
super(tf, f.name);
|
||||
super(tf, ColsVisibility);
|
||||
|
||||
// Configuration object
|
||||
let cfg = this.config;
|
||||
|
@ -32,7 +38,7 @@ export default class ColsVisibility extends Feature {
|
|||
* Module description
|
||||
* @type {String}
|
||||
*/
|
||||
this.desc = f.description || 'Columns visibility manager';
|
||||
this.desc = defaultsStr(f.description, 'Columns visibility manager');
|
||||
|
||||
/**
|
||||
* show/hide columns container element
|
||||
|
@ -56,13 +62,13 @@ export default class ColsVisibility extends Feature {
|
|||
* Enable tick to hide a column, defaults to true
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this.tickToHide = f.tick_to_hide === false ? false : true;
|
||||
this.tickToHide = defaultsBool(f.tick_to_hide, true);
|
||||
|
||||
/**
|
||||
* Enable columns manager UI, defaults to true
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this.manager = f.manager === false ? false : true;
|
||||
this.manager = defaultsBool(f.manager, true);
|
||||
|
||||
/**
|
||||
* Headers HTML table reference only if headers are external
|
||||
|
@ -74,124 +80,115 @@ export default class ColsVisibility extends Feature {
|
|||
* Headers row index only if headers are external
|
||||
* @type {Number}
|
||||
*/
|
||||
this.headersIndex = f.headers_index || 1;
|
||||
this.headersIndex = defaultsNb(f.headers_index, 1);
|
||||
|
||||
/**
|
||||
* ID of main container element
|
||||
* @type {String}
|
||||
*/
|
||||
this.contElTgtId = f.container_target_id || null;
|
||||
this.contElTgtId = defaultsStr(f.container_target_id, null);
|
||||
|
||||
/**
|
||||
* Alternative text for column headers in column manager UI
|
||||
* @type {Array}
|
||||
*/
|
||||
this.headersText = f.headers_text || null;
|
||||
this.headersText = defaultsArr(f.headers_text, []);
|
||||
|
||||
/**
|
||||
* ID of button's container element
|
||||
* @type {String}
|
||||
*/
|
||||
this.btnTgtId = f.btn_target_id || null;
|
||||
this.btnTgtId = defaultsStr(f.btn_target_id, null);
|
||||
|
||||
/**
|
||||
* Button's text, defaults to Columns▼
|
||||
* @type {String}
|
||||
*/
|
||||
this.btnText = f.btn_text || 'Columns▼';
|
||||
this.btnText = defaultsStr(f.btn_text, 'Columns▼');
|
||||
|
||||
/**
|
||||
* Button's inner HTML
|
||||
* @type {String}
|
||||
*/
|
||||
this.btnHtml = f.btn_html || null;
|
||||
this.btnHtml = defaultsStr(f.btn_html, null);
|
||||
|
||||
/**
|
||||
* Css class for button
|
||||
* @type {String}
|
||||
*/
|
||||
this.btnCssClass = f.btn_css_class || 'colVis';
|
||||
this.btnCssClass = defaultsStr(f.btn_css_class, 'colVis');
|
||||
|
||||
/**
|
||||
* Columns manager UI close link text, defaults to 'Close'
|
||||
* @type {String}
|
||||
*/
|
||||
this.btnCloseText = f.btn_close_text || 'Close';
|
||||
this.btnCloseText = defaultsStr(f.btn_close_text, 'Close');
|
||||
|
||||
/**
|
||||
* Columns manager UI close link HTML
|
||||
* @type {String}
|
||||
*/
|
||||
this.btnCloseHtml = f.btn_close_html || null;
|
||||
this.btnCloseHtml = defaultsStr(f.btn_close_html, null);
|
||||
|
||||
/**
|
||||
* Css for columns manager UI close link
|
||||
* @type {String}
|
||||
*/
|
||||
this.btnCloseCssClass = f.btn_close_css_class || this.btnCssClass;
|
||||
this.btnCloseCssClass = defaultsStr(f.btn_close_css_class,
|
||||
this.btnCssClass);
|
||||
|
||||
/**
|
||||
* Extension's stylesheet filename
|
||||
* @type {String}
|
||||
*/
|
||||
this.stylesheet = f.stylesheet || 'colsVisibility.css';
|
||||
|
||||
/**
|
||||
* Extension's prefix
|
||||
* @private
|
||||
*/
|
||||
this.prfx = 'colVis_';
|
||||
this.stylesheet = defaultsStr(f.stylesheet, 'colsVisibility.css');
|
||||
|
||||
/**
|
||||
* Css for columns manager UI span
|
||||
* @type {String}
|
||||
*/
|
||||
this.spanCssClass = f.span_css_class || 'colVisSpan';
|
||||
|
||||
/**
|
||||
* Main container prefix
|
||||
* @private
|
||||
*/
|
||||
this.prfxCont = this.prfx + 'Cont_';
|
||||
this.spanCssClass = defaultsStr(f.span_css_class, 'colVisSpan');
|
||||
|
||||
/**
|
||||
* Css for columns manager UI main container
|
||||
* @type {String}
|
||||
*/
|
||||
this.contCssClass = f.cont_css_class || 'colVisCont';
|
||||
this.contCssClass = defaultsStr(f.cont_css_class, 'colVisCont');
|
||||
|
||||
/**
|
||||
* Css for columns manager UI checklist (ul)
|
||||
* @type {String}
|
||||
*/
|
||||
this.listCssClass = cfg.list_css_class || 'cols_checklist';
|
||||
this.listCssClass = defaultsStr(cfg.list_css_class, 'cols_checklist');
|
||||
|
||||
/**
|
||||
* Css for columns manager UI checklist item (li)
|
||||
* @type {String}
|
||||
*/
|
||||
this.listItemCssClass = cfg.checklist_item_css_class ||
|
||||
'cols_checklist_item';
|
||||
this.listItemCssClass = defaultsStr(cfg.checklist_item_css_class,
|
||||
'cols_checklist_item');
|
||||
|
||||
/**
|
||||
* Css for columns manager UI checklist item selected state (li)
|
||||
* @type {String}
|
||||
*/
|
||||
this.listSlcItemCssClass = cfg.checklist_selected_item_css_class ||
|
||||
'cols_checklist_slc_item';
|
||||
this.listSlcItemCssClass = defaultsStr(
|
||||
cfg.checklist_selected_item_css_class,
|
||||
'cols_checklist_slc_item'
|
||||
);
|
||||
|
||||
/**
|
||||
* Text preceding the columns list, defaults to 'Hide' or 'Show'
|
||||
* depending on tick mode (tick_to_hide option)
|
||||
* @type {String}
|
||||
*/
|
||||
this.text = f.text || (this.tickToHide ? 'Hide: ' : 'Show: ');
|
||||
this.text = defaultsStr(f.text, this.tickToHide ? 'Hide: ' : 'Show: ');
|
||||
|
||||
/**
|
||||
* List of columns indexes to be hidden at initialization
|
||||
* @type {Array}
|
||||
*/
|
||||
this.atStart = f.at_start || [];
|
||||
this.atStart = defaultsArr(f.at_start, []);
|
||||
|
||||
/**
|
||||
* Enable hover behaviour on columns manager button/link
|
||||
|
@ -209,7 +206,13 @@ export default class ColsVisibility extends Feature {
|
|||
* Text for select all option, defaults to 'Select all:'
|
||||
* @type {String}
|
||||
*/
|
||||
this.tickAllText = f.tick_all_text || 'Select all:';
|
||||
this.tickAllText = defaultsStr(f.tick_all_text, 'Select all:');
|
||||
|
||||
/**
|
||||
* Default position in toolbar ('left'|'center'|'right')
|
||||
* @type {String}
|
||||
*/
|
||||
this.toolbarPosition = defaultsStr(f.toolbar_position, RIGHT);
|
||||
|
||||
/**
|
||||
* List of indexes of hidden columns
|
||||
|
@ -217,104 +220,125 @@ export default class ColsVisibility extends Feature {
|
|||
*/
|
||||
this.hiddenCols = [];
|
||||
|
||||
/**
|
||||
* Bound mouseup wrapper
|
||||
* @private
|
||||
*/
|
||||
this.boundMouseup = null;
|
||||
|
||||
/**
|
||||
* Callback fired when the extension is initialized
|
||||
* @type {Function}
|
||||
*/
|
||||
this.onLoaded = isFn(f.on_loaded) ? f.on_loaded : null;
|
||||
this.onLoaded = defaultsFn(f.on_loaded, EMPTY_FN);
|
||||
|
||||
/**
|
||||
* Callback fired before the columns manager is opened
|
||||
* @type {Function}
|
||||
*/
|
||||
this.onBeforeOpen = isFn(f.on_before_open) ? f.on_before_open : null;
|
||||
this.onBeforeOpen = defaultsFn(f.on_before_open, EMPTY_FN);
|
||||
|
||||
/**
|
||||
* Callback fired after the columns manager is opened
|
||||
* @type {Function}
|
||||
*/
|
||||
this.onAfterOpen = isFn(f.on_after_open) ? f.on_after_open : null;
|
||||
this.onAfterOpen = defaultsFn(f.on_after_open, EMPTY_FN);
|
||||
|
||||
/**
|
||||
* Callback fired before the columns manager is closed
|
||||
* @type {Function}
|
||||
*/
|
||||
this.onBeforeClose = isFn(f.on_before_close) ? f.on_before_close : null;
|
||||
this.onBeforeClose = defaultsFn(f.on_before_close, EMPTY_FN);
|
||||
|
||||
/**
|
||||
* Callback fired after the columns manager is closed
|
||||
* @type {Function}
|
||||
*/
|
||||
this.onAfterClose = isFn(f.on_after_close) ? f.on_after_close : null;
|
||||
this.onAfterClose = defaultsFn(f.on_after_close, EMPTY_FN);
|
||||
|
||||
/**
|
||||
* Callback fired before a column is hidden
|
||||
* @type {Function}
|
||||
*/
|
||||
this.onBeforeColHidden = isFn(f.on_before_col_hidden) ?
|
||||
f.on_before_col_hidden : null;
|
||||
this.onBeforeColHidden = defaultsFn(f.on_before_col_hidden, EMPTY_FN);
|
||||
|
||||
/**
|
||||
* Callback fired after a column is hidden
|
||||
* @type {Function}
|
||||
*/
|
||||
this.onAfterColHidden = isFn(f.on_after_col_hidden) ?
|
||||
f.on_after_col_hidden : null;
|
||||
this.onAfterColHidden = defaultsFn(f.on_after_col_hidden, EMPTY_FN);
|
||||
|
||||
/**
|
||||
* Callback fired before a column is displayed
|
||||
* @type {Function}
|
||||
*/
|
||||
this.onBeforeColDisplayed = isFn(f.on_before_col_displayed) ?
|
||||
f.on_before_col_displayed : null;
|
||||
this.onBeforeColDisplayed = defaultsFn(f.on_before_col_displayed,
|
||||
EMPTY_FN);
|
||||
|
||||
/**
|
||||
* Callback fired after a column is displayed
|
||||
* @type {Function}
|
||||
*/
|
||||
this.onAfterColDisplayed = isFn(f.on_after_col_displayed) ?
|
||||
f.on_after_col_displayed : null;
|
||||
this.onAfterColDisplayed = defaultsFn(f.on_after_col_displayed,
|
||||
EMPTY_FN);
|
||||
|
||||
//Grid layout support
|
||||
if (tf.gridLayout) {
|
||||
this.headersTbl = tf.feature('gridLayout').headTbl; //headers table
|
||||
this.headersIndex = 0; //headers index
|
||||
this.onAfterColDisplayed = function () { };
|
||||
this.onAfterColHidden = function () { };
|
||||
}
|
||||
|
||||
//Loads extension stylesheet
|
||||
tf.import(f.name + 'Style', tf.stylePath + this.stylesheet, null,
|
||||
tf.import(f.name + 'Style', tf.getStylePath() + this.stylesheet, null,
|
||||
'link');
|
||||
|
||||
this.enable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mouse-up event handler handling popup auto-close behaviour
|
||||
* @private
|
||||
*/
|
||||
onMouseup(evt) {
|
||||
let targetElm = targetEvt(evt);
|
||||
|
||||
while (targetElm && targetElm !== this.contEl
|
||||
&& targetElm !== this.btnEl) {
|
||||
targetElm = targetElm.parentNode;
|
||||
}
|
||||
|
||||
if (targetElm !== this.contEl && targetElm !== this.btnEl) {
|
||||
this.toggle();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle columns manager UI
|
||||
*/
|
||||
toggle() {
|
||||
let contDisplay = this.contEl.style.display;
|
||||
let onBeforeOpen = this.onBeforeOpen;
|
||||
let onBeforeClose = this.onBeforeClose;
|
||||
let onAfterOpen = this.onAfterOpen;
|
||||
let onAfterClose = this.onAfterClose;
|
||||
// ensure mouseup event handler is removed
|
||||
removeEvt(root, 'mouseup', this.boundMouseup);
|
||||
|
||||
if (onBeforeOpen && contDisplay !== 'inline') {
|
||||
onBeforeOpen.call(null, this);
|
||||
let contDisplay = this.contEl.style.display;
|
||||
|
||||
if (contDisplay !== 'inline') {
|
||||
this.onBeforeOpen(this);
|
||||
}
|
||||
if (onBeforeClose && contDisplay === 'inline') {
|
||||
onBeforeClose.call(null, this);
|
||||
if (contDisplay === 'inline') {
|
||||
this.onBeforeClose(this);
|
||||
}
|
||||
|
||||
this.contEl.style.display = contDisplay === 'inline' ?
|
||||
'none' : 'inline';
|
||||
NONE : 'inline';
|
||||
|
||||
if (onAfterOpen && contDisplay !== 'inline') {
|
||||
onAfterOpen.call(null, this);
|
||||
if (contDisplay !== 'inline') {
|
||||
this.onAfterOpen(this);
|
||||
addEvt(root, 'mouseup', this.boundMouseup);
|
||||
}
|
||||
if (onAfterClose && contDisplay === 'inline') {
|
||||
onAfterClose.call(null, this);
|
||||
if (contDisplay === 'inline') {
|
||||
this.onAfterClose(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -352,18 +376,22 @@ export default class ColsVisibility extends Feature {
|
|||
return;
|
||||
}
|
||||
|
||||
this.emitter.emit('initializing-extension', this,
|
||||
!isNull(this.btnTgtId));
|
||||
|
||||
this.emitter.on(['hide-column'],
|
||||
(tf, colIndex) => this.hideCol(colIndex));
|
||||
|
||||
this.buildBtn();
|
||||
this.buildManager();
|
||||
|
||||
/**
|
||||
* @inherited
|
||||
*/
|
||||
/** @inherited */
|
||||
this.initialized = true;
|
||||
|
||||
this.boundMouseup = this.onMouseup.bind(this);
|
||||
|
||||
this.emitter.emit('columns-visibility-initialized', this.tf, this);
|
||||
this.emitter.emit('extension-initialized', this);
|
||||
|
||||
// Hide columns at start at very end of initialization, do not move
|
||||
// as order is important
|
||||
|
@ -378,14 +406,13 @@ export default class ColsVisibility extends Feature {
|
|||
return;
|
||||
}
|
||||
let tf = this.tf;
|
||||
let span = createElm('span', ['id', this.prfx + tf.id]);
|
||||
let span = createElm('span');
|
||||
span.className = this.spanCssClass;
|
||||
|
||||
//Container element (rdiv or custom element)
|
||||
if (!this.btnTgtId) {
|
||||
tf.setToolbar();
|
||||
}
|
||||
let targetEl = !this.btnTgtId ? tf.rDiv : elm(this.btnTgtId);
|
||||
// Container element (rdiv or custom element)
|
||||
let targetEl = !this.btnTgtId ?
|
||||
tf.feature('toolbar').container(this.toolbarPosition) :
|
||||
elm(this.btnTgtId);
|
||||
|
||||
if (!this.btnTgtId) {
|
||||
let firstChild = targetEl.firstChild;
|
||||
|
@ -406,7 +433,7 @@ export default class ColsVisibility extends Feature {
|
|||
} else {
|
||||
addEvt(btn, 'mouseover', (evt) => this.toggle(evt));
|
||||
}
|
||||
} else { //Custom html
|
||||
} else { // Custom html
|
||||
span.innerHTML = this.btnHtml;
|
||||
let colVisEl = span.firstChild;
|
||||
if (!this.enableHover) {
|
||||
|
@ -419,9 +446,7 @@ export default class ColsVisibility extends Feature {
|
|||
this.spanEl = span;
|
||||
this.btnEl = this.spanEl.firstChild;
|
||||
|
||||
if (this.onLoaded) {
|
||||
this.onLoaded.call(null, this);
|
||||
}
|
||||
this.onLoaded(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -431,7 +456,7 @@ export default class ColsVisibility extends Feature {
|
|||
let tf = this.tf;
|
||||
|
||||
let container = !this.contElTgtId ?
|
||||
createElm('div', ['id', this.prfxCont + tf.id]) :
|
||||
createElm('div') :
|
||||
elm(this.contElTgtId);
|
||||
container.className = this.contCssClass;
|
||||
|
||||
|
@ -441,10 +466,10 @@ export default class ColsVisibility extends Feature {
|
|||
container.appendChild(extNameLabel);
|
||||
|
||||
//Headers list
|
||||
let ul = createElm('ul', ['id', 'ul' + this.name + '_' + tf.id]);
|
||||
let ul = createElm('ul');
|
||||
ul.className = this.listCssClass;
|
||||
|
||||
let tbl = this.headersTbl ? this.headersTbl : tf.tbl;
|
||||
let tbl = this.headersTbl || tf.dom();
|
||||
let headerIndex = this.headersTbl ?
|
||||
this.headersIndex : tf.getHeadersRowIndex();
|
||||
let headerRow = tbl.rows[headerIndex];
|
||||
|
@ -470,8 +495,7 @@ export default class ColsVisibility extends Feature {
|
|||
|
||||
for (let i = 0; i < headerRow.cells.length; i++) {
|
||||
let cell = headerRow.cells[i];
|
||||
let cellText = this.headersText && this.headersText[i] ?
|
||||
this.headersText[i] : this._getHeaderText(cell);
|
||||
let cellText = this.headersText[i] || this._getHeaderText(cell);
|
||||
let liElm = createCheckItem('col_' + i + '_' + tf.id, cellText,
|
||||
cellText);
|
||||
addClass(liElm, this.listItemCssClass);
|
||||
|
@ -520,18 +544,17 @@ export default class ColsVisibility extends Feature {
|
|||
*/
|
||||
setHidden(colIndex, hide) {
|
||||
let tf = this.tf;
|
||||
let tbl = tf.tbl;
|
||||
let tbl = tf.dom();
|
||||
|
||||
if (this.onBeforeColHidden && hide) {
|
||||
this.onBeforeColHidden.call(null, this, colIndex);
|
||||
}
|
||||
if (this.onBeforeColDisplayed && !hide) {
|
||||
this.onBeforeColDisplayed.call(null, this, colIndex);
|
||||
if (hide) {
|
||||
this.onBeforeColHidden(this, colIndex);
|
||||
} else {
|
||||
this.onBeforeColDisplayed(this, colIndex);
|
||||
}
|
||||
|
||||
this._hideCells(tbl, colIndex, hide);
|
||||
this._hideElements(tbl, colIndex, hide);
|
||||
if (this.headersTbl) {
|
||||
this._hideCells(this.headersTbl, colIndex, hide);
|
||||
this._hideElements(this.headersTbl, colIndex, hide);
|
||||
}
|
||||
|
||||
let hiddenCols = this.hiddenCols;
|
||||
|
@ -546,49 +569,12 @@ export default class ColsVisibility extends Feature {
|
|||
}
|
||||
}
|
||||
|
||||
let gridLayout;
|
||||
let headTbl;
|
||||
let gridColElms;
|
||||
if (hide) {
|
||||
//This event is fired just after a column is displayed for
|
||||
//grid_layout support
|
||||
//TODO: grid layout module should be responsible for those
|
||||
//calculations
|
||||
if (tf.gridLayout) {
|
||||
gridLayout = tf.feature('gridLayout');
|
||||
headTbl = gridLayout.headTbl;
|
||||
gridColElms = gridLayout.colElms;
|
||||
let hiddenWidth = parseInt(
|
||||
gridColElms[colIndex].style.width, 10);
|
||||
|
||||
let headTblW = parseInt(headTbl.style.width, 10);
|
||||
headTbl.style.width = headTblW - hiddenWidth + 'px';
|
||||
tbl.style.width = headTbl.style.width;
|
||||
}
|
||||
if (this.onAfterColHidden) {
|
||||
this.onAfterColHidden.call(null, this, colIndex);
|
||||
}
|
||||
this.onAfterColHidden(this, colIndex);
|
||||
this.emitter.emit('column-hidden', tf, this, colIndex,
|
||||
this.hiddenCols);
|
||||
}
|
||||
|
||||
if (!hide) {
|
||||
//This event is fired just after a column is displayed for
|
||||
//grid_layout support
|
||||
//TODO: grid layout module should be responsible for those
|
||||
//calculations
|
||||
if (tf.gridLayout) {
|
||||
gridLayout = tf.feature('gridLayout');
|
||||
headTbl = gridLayout.headTbl;
|
||||
gridColElms = gridLayout.colElms;
|
||||
let width = parseInt(gridColElms[colIndex].style.width, 10);
|
||||
headTbl.style.width =
|
||||
(parseInt(headTbl.style.width, 10) + width) + 'px';
|
||||
tf.tbl.style.width = headTbl.style.width;
|
||||
}
|
||||
if (this.onAfterColDisplayed) {
|
||||
this.onAfterColDisplayed.call(null, this, colIndex);
|
||||
}
|
||||
} else {
|
||||
this.onAfterColDisplayed(this, colIndex);
|
||||
this.emitter.emit('column-shown', tf, this, colIndex,
|
||||
this.hiddenCols);
|
||||
}
|
||||
|
@ -599,7 +585,7 @@ export default class ColsVisibility extends Feature {
|
|||
* @param {Number} colIndex Column index
|
||||
*/
|
||||
showCol(colIndex) {
|
||||
if (colIndex === undefined || !this.isColHidden(colIndex)) {
|
||||
if (isUndef(colIndex) || !this.isColHidden(colIndex)) {
|
||||
return;
|
||||
}
|
||||
if (this.manager && this.contEl) {
|
||||
|
@ -617,7 +603,7 @@ export default class ColsVisibility extends Feature {
|
|||
* @param {Number} colIndex Column index
|
||||
*/
|
||||
hideCol(colIndex) {
|
||||
if (colIndex === undefined || this.isColHidden(colIndex)) {
|
||||
if (isUndef(colIndex) || this.isColHidden(colIndex)) {
|
||||
return;
|
||||
}
|
||||
if (this.manager && this.contEl) {
|
||||
|
@ -646,7 +632,7 @@ export default class ColsVisibility extends Feature {
|
|||
* @param {Number} colIndex Column index
|
||||
*/
|
||||
toggleCol(colIndex) {
|
||||
if (colIndex === undefined || this.isColHidden(colIndex)) {
|
||||
if (isUndef(colIndex) || this.isColHidden(colIndex)) {
|
||||
this.showCol(colIndex);
|
||||
} else {
|
||||
this.hideCol(colIndex);
|
||||
|
@ -682,6 +668,8 @@ export default class ColsVisibility extends Feature {
|
|||
this.emitter.off(['hide-column'],
|
||||
(tf, colIndex) => this.hideCol(colIndex));
|
||||
|
||||
this.boundMouseup = null;
|
||||
|
||||
this.initialized = false;
|
||||
}
|
||||
|
||||
|
@ -706,16 +694,29 @@ export default class ColsVisibility extends Feature {
|
|||
return '';
|
||||
}
|
||||
|
||||
_hideCells(tbl, colIndex, hide) {
|
||||
_hideElements(tbl, colIdx, hide) {
|
||||
this._hideCells(tbl, colIdx, hide);
|
||||
this._hideCol(tbl, colIdx, hide);
|
||||
}
|
||||
|
||||
_hideCells(tbl, colIdx, hide) {
|
||||
for (let i = 0; i < tbl.rows.length; i++) {
|
||||
let row = tbl.rows[i];
|
||||
let cell = row.cells[colIndex];
|
||||
let cell = row.cells[colIdx];
|
||||
if (cell) {
|
||||
cell.style.display = hide ? 'none' : '';
|
||||
cell.style.display = hide ? NONE : '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_hideCol(tbl, colIdx, hide) {
|
||||
let colElms = tag(tbl, 'col');
|
||||
if (colElms.length === 0) {
|
||||
return;
|
||||
}
|
||||
colElms[colIdx].style.display = hide ? NONE : '';
|
||||
}
|
||||
|
||||
_hideAtStart() {
|
||||
this.atStart.forEach((colIdx) => {
|
||||
this.hideCol(colIdx);
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
import {Feature} from '../../feature';
|
||||
import {createElm, removeElm, elm} from '../../dom';
|
||||
import {isFn, isUndef} from '../../types';
|
||||
import {EMPTY_FN, isNull} from '../../types';
|
||||
import {addEvt} from '../../event';
|
||||
import {
|
||||
defaultsBool, defaultsStr, defaultsFn, defaultsNb,
|
||||
} from '../../settings';
|
||||
import {RIGHT} from '../../modules/toolbar';
|
||||
|
||||
/**
|
||||
* Filters Visibility extension
|
||||
|
@ -14,7 +18,7 @@ export default class FiltersVisibility extends Feature {
|
|||
* @param {Object} Configuration object
|
||||
*/
|
||||
constructor(tf, f) {
|
||||
super(tf, f.name);
|
||||
super(tf, FiltersVisibility);
|
||||
|
||||
/**
|
||||
* Module name
|
||||
|
@ -26,25 +30,26 @@ export default class FiltersVisibility extends Feature {
|
|||
* Module description
|
||||
* @type {String}
|
||||
*/
|
||||
this.desc = f.description || 'Filters row visibility manager';
|
||||
this.desc = defaultsStr(f.description,
|
||||
'Filters row visibility manager');
|
||||
|
||||
/**
|
||||
* Extension's stylesheet filename
|
||||
* @type {String}
|
||||
*/
|
||||
this.stylesheet = f.stylesheet || 'filtersVisibility.css';
|
||||
this.stylesheet = defaultsStr(f.stylesheet , 'filtersVisibility.css');
|
||||
|
||||
/**
|
||||
* Expand icon filename
|
||||
* @type {String}
|
||||
*/
|
||||
this.icnExpand = f.expand_icon_name || 'icn_exp.png';
|
||||
this.icnExpand = defaultsStr(f.expand_icon_name, 'icn_exp.png');
|
||||
|
||||
/**
|
||||
* Collapse icon filename
|
||||
* @type {String}
|
||||
*/
|
||||
this.icnCollapse = f.collapse_icon_name || 'icn_clp.png';
|
||||
this.icnCollapse = defaultsStr(f.collapse_icon_name, 'icn_clp.png');
|
||||
|
||||
/**
|
||||
* Main container element
|
||||
|
@ -88,13 +93,13 @@ export default class FiltersVisibility extends Feature {
|
|||
* Enable expand/collapse icon, defaults to true
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this.enableIcon = f.enable_icon === false ? false : true;
|
||||
this.enableIcon = defaultsBool(f.enable_icon, true);
|
||||
|
||||
/**
|
||||
* Custom text for button
|
||||
* @type {String}
|
||||
*/
|
||||
this.btnText = f.btn_text || '';
|
||||
this.btnText = defaultsStr(f.btn_text, '');
|
||||
|
||||
/**
|
||||
* Collapse button HTML
|
||||
|
@ -116,66 +121,65 @@ export default class FiltersVisibility extends Feature {
|
|||
* Button's custom HTML
|
||||
* @type {String}
|
||||
*/
|
||||
this.btnHtml = f.btn_html || null;
|
||||
this.btnHtml = defaultsStr(f.btn_html, null);
|
||||
|
||||
/**
|
||||
* Css class for expand/collapse filters button
|
||||
* @type {String}
|
||||
*/
|
||||
this.btnCssClass = f.btn_css_class || 'btnExpClpFlt';
|
||||
this.btnCssClass = defaultsStr(f.btn_css_class, 'btnExpClpFlt');
|
||||
|
||||
/**
|
||||
* Css class for main container
|
||||
* @type {String}
|
||||
*/
|
||||
this.contCssClass = f.cont_css_class || 'expClpFlt';
|
||||
this.contCssClass = defaultsStr(f.cont_css_class, 'expClpFlt');
|
||||
|
||||
/**
|
||||
* Filters row index
|
||||
* @type {Number}
|
||||
*/
|
||||
this.filtersRowIndex = !isUndef(f.filters_row_index) ?
|
||||
f.filters_row_index : tf.getFiltersRowIndex();
|
||||
this.filtersRowIndex = defaultsNb(f.filters_row_index,
|
||||
tf.getFiltersRowIndex());
|
||||
|
||||
/**
|
||||
* Make filters visible at initialization, defaults to true
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this.visibleAtStart = !isUndef(f.visible_at_start) ?
|
||||
Boolean(f.visible_at_start) : true;
|
||||
this.visibleAtStart = defaultsNb(f.visible_at_start, true);
|
||||
|
||||
/**
|
||||
* Extension's prefix
|
||||
* @private
|
||||
* Default position in toolbar ('left'|'center'|'right')
|
||||
* @type {String}
|
||||
*/
|
||||
this.prfx = 'fltsVis_';
|
||||
this.toolbarPosition = defaultsStr(f.toolbar_position, RIGHT);
|
||||
|
||||
/**
|
||||
* Callback fired before filters row is shown
|
||||
* @type {Function}
|
||||
*/
|
||||
this.onBeforeShow = isFn(f.on_before_show) ? f.on_before_show : null;
|
||||
this.onBeforeShow = defaultsFn(f.on_before_show, EMPTY_FN);
|
||||
|
||||
/**
|
||||
* Callback fired after filters row is shown
|
||||
* @type {Function}
|
||||
*/
|
||||
this.onAfterShow = isFn(f.on_after_show) ? f.on_after_show : null;
|
||||
this.onAfterShow = defaultsFn(f.on_after_show, EMPTY_FN);
|
||||
|
||||
/**
|
||||
* Callback fired before filters row is hidden
|
||||
* @type {Function}
|
||||
*/
|
||||
this.onBeforeHide = isFn(f.on_before_hide) ? f.on_before_hide : null;
|
||||
this.onBeforeHide = defaultsFn(f.on_before_hide, EMPTY_FN);
|
||||
|
||||
/**
|
||||
* Callback fired after filters row is hidden
|
||||
* @type {Function}
|
||||
*/
|
||||
this.onAfterHide = isFn(f.on_after_hide) ? f.on_after_hide : null;
|
||||
this.onAfterHide = defaultsFn(f.on_after_hide, EMPTY_FN);
|
||||
|
||||
//Import extension's stylesheet
|
||||
tf.import(f.name + 'Style', tf.stylePath + this.stylesheet, null,
|
||||
tf.import(f.name + 'Style', tf.getStylePath() + this.stylesheet, null,
|
||||
'link');
|
||||
|
||||
this.enable();
|
||||
|
@ -189,15 +193,17 @@ export default class FiltersVisibility extends Feature {
|
|||
return;
|
||||
}
|
||||
|
||||
this.emitter.emit('initializing-extension', this,
|
||||
!isNull(this.targetId));
|
||||
|
||||
this.buildUI();
|
||||
|
||||
/**
|
||||
* @inherited
|
||||
*/
|
||||
/** @inherited */
|
||||
this.initialized = true;
|
||||
|
||||
this.emitter.on(['show-filters'], (tf, visible) => this.show(visible));
|
||||
this.emitter.emit('filters-visibility-initialized', this.tf, this);
|
||||
this.emitter.emit('extension-initialized', this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -205,14 +211,13 @@ export default class FiltersVisibility extends Feature {
|
|||
*/
|
||||
buildUI() {
|
||||
let tf = this.tf;
|
||||
let span = createElm('span', ['id', this.prfx + tf.id]);
|
||||
let span = createElm('span');
|
||||
span.className = this.contCssClass;
|
||||
|
||||
//Container element (rdiv or custom element)
|
||||
if (!this.targetId) {
|
||||
tf.setToolbar();
|
||||
}
|
||||
let targetEl = !this.targetId ? tf.rDiv : elm(this.targetId);
|
||||
// Container element (rdiv or custom element)
|
||||
let targetEl = !this.targetId ?
|
||||
tf.feature('toolbar').container(this.toolbarPosition) :
|
||||
elm(this.targetId);
|
||||
|
||||
if (!this.targetId) {
|
||||
let firstChild = targetEl.firstChild;
|
||||
|
@ -228,7 +233,7 @@ export default class FiltersVisibility extends Feature {
|
|||
btn.title = this.btnText || this.defaultText;
|
||||
btn.innerHTML = this.collapseBtnHtml;
|
||||
span.appendChild(btn);
|
||||
} else { //Custom html
|
||||
} else { // Custom html
|
||||
span.innerHTML = this.btnHtml;
|
||||
btn = span.firstChild;
|
||||
}
|
||||
|
@ -248,7 +253,7 @@ export default class FiltersVisibility extends Feature {
|
|||
*/
|
||||
toggle() {
|
||||
let tf = this.tf;
|
||||
let tbl = tf.gridLayout ? tf.feature('gridLayout').headTbl : tf.tbl;
|
||||
let tbl = tf.gridLayout ? tf.feature('gridLayout').headTbl : tf.dom();
|
||||
let fltRow = tbl.rows[this.filtersRowIndex];
|
||||
let isDisplayed = fltRow.style.display === '';
|
||||
|
||||
|
@ -262,14 +267,14 @@ export default class FiltersVisibility extends Feature {
|
|||
*/
|
||||
show(visible = true) {
|
||||
let tf = this.tf;
|
||||
let tbl = tf.gridLayout ? tf.feature('gridLayout').headTbl : tf.tbl;
|
||||
let tbl = tf.gridLayout ? tf.feature('gridLayout').headTbl : tf.dom();
|
||||
let fltRow = tbl.rows[this.filtersRowIndex];
|
||||
|
||||
if (this.onBeforeShow && visible) {
|
||||
this.onBeforeShow.call(this, this);
|
||||
if (visible) {
|
||||
this.onBeforeShow(this);
|
||||
}
|
||||
if (this.onBeforeHide && !visible) {
|
||||
this.onBeforeHide.call(null, this);
|
||||
if (!visible) {
|
||||
this.onBeforeHide(this);
|
||||
}
|
||||
|
||||
fltRow.style.display = visible ? '' : 'none';
|
||||
|
@ -278,11 +283,11 @@ export default class FiltersVisibility extends Feature {
|
|||
this.collapseBtnHtml : this.expandBtnHtml;
|
||||
}
|
||||
|
||||
if (this.onAfterShow && visible) {
|
||||
this.onAfterShow.call(null, this);
|
||||
if (visible) {
|
||||
this.onAfterShow(this);
|
||||
}
|
||||
if (this.onAfterHide && !visible) {
|
||||
this.onAfterHide.call(null, this);
|
||||
if (!visible) {
|
||||
this.onAfterHide(this);
|
||||
}
|
||||
|
||||
this.emitter.emit('filters-toggled', tf, this, visible);
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
import {Feature} from '../../feature';
|
||||
import {isArray, isFn, isUndef} from '../../types';
|
||||
import {createElm, elm, getText, tag} from '../../dom';
|
||||
import {addEvt} from '../../event';
|
||||
import {formatDate} from '../../date';
|
||||
import {removeNbFormat} from '../../helpers';
|
||||
import {NONE, CELL_TAG, HEADER_TAG} from '../../const';
|
||||
import {isUndef, isObj, EMPTY_FN} from '../../types';
|
||||
import {createElm, elm, tag} from '../../dom';
|
||||
import {addEvt, bound} from '../../event';
|
||||
import {parse as parseNb} from '../../number';
|
||||
import {
|
||||
NONE, CELL_TAG, HEADER_TAG, STRING, NUMBER, DATE, FORMATTED_NUMBER,
|
||||
IP_ADDRESS
|
||||
} from '../../const';
|
||||
import {defaultsStr, defaultsFn, defaultsArr} from '../../settings';
|
||||
|
||||
/**
|
||||
* SortableTable Adapter module
|
||||
|
@ -17,7 +20,7 @@ export default class AdapterSortableTable extends Feature {
|
|||
* @param {Object} opts Configuration object
|
||||
*/
|
||||
constructor(tf, opts) {
|
||||
super(tf, opts.name);
|
||||
super(tf, AdapterSortableTable);
|
||||
|
||||
/**
|
||||
* Module name
|
||||
|
@ -29,7 +32,7 @@ export default class AdapterSortableTable extends Feature {
|
|||
* Module description
|
||||
* @type {String}
|
||||
*/
|
||||
this.desc = opts.description || 'Sortable table';
|
||||
this.desc = defaultsStr(opts.description, 'Sortable table');
|
||||
|
||||
/**
|
||||
* Indicate whether table previously sorted
|
||||
|
@ -42,15 +45,14 @@ export default class AdapterSortableTable extends Feature {
|
|||
* List of sort type per column basis
|
||||
* @type {Array}
|
||||
*/
|
||||
this.sortTypes = isArray(opts.types) ? opts.types : [];
|
||||
this.sortTypes = defaultsArr(opts.types, tf.colTypes);
|
||||
|
||||
/**
|
||||
* Column to be sorted at initialization, ie:
|
||||
* sort_col_at_start: [1, true]
|
||||
* @type {Array}
|
||||
*/
|
||||
this.sortColAtStart = isArray(opts.sort_col_at_start) ?
|
||||
opts.sort_col_at_start : null;
|
||||
this.sortColAtStart = defaultsArr(opts.sort_col_at_start, null);
|
||||
|
||||
/**
|
||||
* Enable asynchronous sort, if triggers are external
|
||||
|
@ -62,7 +64,7 @@ export default class AdapterSortableTable extends Feature {
|
|||
* List of element IDs triggering sort on a per column basis
|
||||
* @type {Array}
|
||||
*/
|
||||
this.triggerIds = isArray(opts.trigger_ids) ? opts.trigger_ids : [];
|
||||
this.triggerIds = defaultsArr(opts.trigger_ids, []);
|
||||
|
||||
// edit .sort-arrow.descending / .sort-arrow.ascending in
|
||||
// tablefilter.css to reflect any path change
|
||||
|
@ -70,57 +72,57 @@ export default class AdapterSortableTable extends Feature {
|
|||
* Path to images
|
||||
* @type {String}
|
||||
*/
|
||||
this.imgPath = opts.images_path || tf.themesPath;
|
||||
this.imgPath = defaultsStr(opts.images_path, tf.themesPath);
|
||||
|
||||
/**
|
||||
* Blank image file name
|
||||
* @type {String}
|
||||
*/
|
||||
this.imgBlank = opts.image_blank || 'blank.png';
|
||||
this.imgBlank = defaultsStr(opts.image_blank, 'blank.png');
|
||||
|
||||
/**
|
||||
* Css class for sort indicator image
|
||||
* @type {String}
|
||||
*/
|
||||
this.imgClassName = opts.image_class_name || 'sort-arrow';
|
||||
this.imgClassName = defaultsStr(opts.image_class_name, 'sort-arrow');
|
||||
|
||||
/**
|
||||
* Css class for ascending sort indicator image
|
||||
* @type {String}
|
||||
*/
|
||||
this.imgAscClassName = opts.image_asc_class_name || 'ascending';
|
||||
this.imgAscClassName = defaultsStr(opts.image_asc_class_name,
|
||||
'ascending');
|
||||
|
||||
/**
|
||||
* Css class for descending sort indicator image
|
||||
* @type {String}
|
||||
*/
|
||||
this.imgDescClassName = opts.image_desc_class_name || 'descending';
|
||||
this.imgDescClassName = defaultsStr(opts.image_desc_class_name,
|
||||
'descending');
|
||||
|
||||
/**
|
||||
* Cell attribute key storing custom value used for sorting
|
||||
* @type {String}
|
||||
*/
|
||||
this.customKey = opts.custom_key || 'data-tf-sortKey';
|
||||
this.customKey = defaultsStr(opts.custom_key, 'data-tf-sortKey');
|
||||
|
||||
/**
|
||||
* Callback fired when sort extension is instanciated
|
||||
* @type {Function}
|
||||
*/
|
||||
this.onSortLoaded = isFn(opts.on_sort_loaded) ?
|
||||
opts.on_sort_loaded : null;
|
||||
this.onSortLoaded = defaultsFn(opts.on_sort_loaded, EMPTY_FN);
|
||||
|
||||
/**
|
||||
* Callback fired before a table column is sorted
|
||||
* @type {Function}
|
||||
*/
|
||||
this.onBeforeSort = isFn(opts.on_before_sort) ?
|
||||
opts.on_before_sort : null;
|
||||
this.onBeforeSort = defaultsFn(opts.on_before_sort, EMPTY_FN);
|
||||
|
||||
/**
|
||||
* Callback fired after a table column is sorted
|
||||
* @type {Function}
|
||||
*/
|
||||
this.onAfterSort = isFn(opts.on_after_sort) ? opts.on_after_sort : null;
|
||||
this.onAfterSort = defaultsFn(opts.on_after_sort, EMPTY_FN);
|
||||
|
||||
/**
|
||||
* SortableTable instance
|
||||
|
@ -146,24 +148,17 @@ export default class AdapterSortableTable extends Feature {
|
|||
throw new Error('SortableTable class not found.');
|
||||
}
|
||||
|
||||
// Add any date format if needed
|
||||
this.emitter.emit('add-date-type-formats', this.tf, this.sortTypes);
|
||||
|
||||
this.overrideSortableTable();
|
||||
this.setSortTypes();
|
||||
|
||||
//Column sort at start
|
||||
let sortColAtStart = adpt.sortColAtStart;
|
||||
if (sortColAtStart) {
|
||||
this.stt.sort(sortColAtStart[0], sortColAtStart[1]);
|
||||
}
|
||||
|
||||
if (this.onSortLoaded) {
|
||||
this.onSortLoaded.call(null, tf, this);
|
||||
}
|
||||
this.onSortLoaded(tf, this);
|
||||
|
||||
/*** SortableTable callbacks ***/
|
||||
this.stt.onbeforesort = function () {
|
||||
if (adpt.onBeforeSort) {
|
||||
adpt.onBeforeSort.call(null, tf, adpt.stt.sortColumn);
|
||||
}
|
||||
adpt.onBeforeSort(tf, adpt.stt.sortColumn);
|
||||
|
||||
/*** sort behaviour for paging ***/
|
||||
if (tf.paging) {
|
||||
|
@ -183,17 +178,18 @@ export default class AdapterSortableTable extends Feature {
|
|||
paginator.setPage(paginator.getPage());
|
||||
}
|
||||
|
||||
if (adpt.onAfterSort) {
|
||||
adpt.onAfterSort.call(null, tf, adpt.stt.sortColumn,
|
||||
adpt.stt.descending);
|
||||
}
|
||||
|
||||
adpt.onAfterSort(tf, adpt.stt.sortColumn, adpt.stt.descending);
|
||||
adpt.emitter.emit('column-sorted', tf, adpt.stt.sortColumn,
|
||||
adpt.stt.descending);
|
||||
};
|
||||
|
||||
this.emitter.on(['sort'],
|
||||
(tf, colIdx, desc) => this.sortByColumnIndex(colIdx, desc));
|
||||
// Column sort at start
|
||||
let sortColAtStart = adpt.sortColAtStart;
|
||||
if (sortColAtStart) {
|
||||
this.stt.sort(sortColAtStart[0], sortColAtStart[1]);
|
||||
}
|
||||
|
||||
this.emitter.on(['sort'], bound(this.sortByColumnIndexHandler, this));
|
||||
|
||||
/** @inherited */
|
||||
this.initialized = true;
|
||||
|
@ -210,6 +206,11 @@ export default class AdapterSortableTable extends Feature {
|
|||
this.stt.sort(colIdx, desc);
|
||||
}
|
||||
|
||||
/** @private */
|
||||
sortByColumnIndexHandler(tf, colIdx, desc) {
|
||||
this.sortByColumnIndex(colIdx, desc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set SortableTable overrides for TableFilter integration
|
||||
*/
|
||||
|
@ -360,17 +361,17 @@ export default class AdapterSortableTable extends Feature {
|
|||
/**
|
||||
* Overrides getInnerText in order to avoid Firefox unexpected sorting
|
||||
* behaviour with untrimmed text elements
|
||||
* @param {Object} oNode DOM element
|
||||
* @param {Object} cell DOM element
|
||||
* @return {String} DOM element inner text
|
||||
*/
|
||||
SortableTable.getInnerText = function (oNode) {
|
||||
if (!oNode) {
|
||||
SortableTable.getInnerText = function (cell) {
|
||||
if (!cell) {
|
||||
return;
|
||||
}
|
||||
if (oNode.getAttribute(adpt.customKey)) {
|
||||
return oNode.getAttribute(adpt.customKey);
|
||||
if (cell.getAttribute(adpt.customKey)) {
|
||||
return cell.getAttribute(adpt.customKey);
|
||||
} else {
|
||||
return getText(oNode);
|
||||
return tf.getCellValue(cell);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -378,9 +379,10 @@ export default class AdapterSortableTable extends Feature {
|
|||
/**
|
||||
* Adds a sort type
|
||||
*/
|
||||
addSortType() {
|
||||
var args = arguments;
|
||||
SortableTable.prototype.addSortType(args[0], args[1], args[2], args[3]);
|
||||
addSortType(...args) {
|
||||
// Extract the arguments
|
||||
let [id, caster, sorter, getRowValue] = args;
|
||||
SortableTable.prototype.addSortType(id, caster, sorter, getRowValue);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -392,42 +394,46 @@ export default class AdapterSortableTable extends Feature {
|
|||
sortTypes = this.sortTypes,
|
||||
_sortTypes = [];
|
||||
|
||||
for (let i = 0; i < tf.nbCells; i++) {
|
||||
tf.eachCol((i) => {
|
||||
let colType;
|
||||
|
||||
if (sortTypes[i]) {
|
||||
colType = sortTypes[i].toLowerCase();
|
||||
if (colType === NONE) {
|
||||
colType = 'None';
|
||||
}
|
||||
} else { // resolve column types
|
||||
if (tf.hasColNbFormat && tf.colNbFormat[i] !== null) {
|
||||
colType = tf.colNbFormat[i].toLowerCase();
|
||||
} else if (tf.hasColDateType && tf.colDateType[i] !== null) {
|
||||
colType = tf.colDateType[i].toLowerCase() + 'date';
|
||||
colType = sortTypes[i];
|
||||
if (isObj(colType)) {
|
||||
if (colType.type === DATE) {
|
||||
colType = this._addDateType(i, sortTypes);
|
||||
}
|
||||
else if (colType.type === FORMATTED_NUMBER) {
|
||||
let decimal = colType.decimal || tf.decimalSeparator;
|
||||
colType = this._addNumberType(i, decimal);
|
||||
}
|
||||
} else {
|
||||
colType = 'String';
|
||||
colType = colType.toLowerCase();
|
||||
if (colType === DATE) {
|
||||
colType = this._addDateType(i, sortTypes);
|
||||
}
|
||||
else if (colType === FORMATTED_NUMBER ||
|
||||
colType === NUMBER) {
|
||||
colType = this._addNumberType(i, tf.decimalSeparator);
|
||||
}
|
||||
else if (colType === NONE) {
|
||||
// TODO: normalise 'none' vs 'None'
|
||||
colType = 'None';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
colType = STRING;
|
||||
}
|
||||
_sortTypes.push(colType);
|
||||
}
|
||||
});
|
||||
|
||||
//Public TF method to add sort type
|
||||
|
||||
//Custom sort types
|
||||
this.addSortType('number', Number);
|
||||
this.addSortType('caseinsensitivestring', SortableTable.toUpperCase);
|
||||
this.addSortType('date', SortableTable.toDate);
|
||||
this.addSortType('string');
|
||||
this.addSortType('us', usNumberConverter);
|
||||
this.addSortType('eu', euNumberConverter);
|
||||
this.addSortType('dmydate', dmyDateConverter);
|
||||
this.addSortType('ymddate', ymdDateConverter);
|
||||
this.addSortType('mdydate', mdyDateConverter);
|
||||
this.addSortType('ddmmmyyyydate', ddmmmyyyyDateConverter);
|
||||
this.addSortType('ipaddress', ipAddress, sortIP);
|
||||
this.addSortType(STRING);
|
||||
this.addSortType(IP_ADDRESS, ipAddress, sortIP);
|
||||
|
||||
this.stt = new SortableTable(tf.tbl, _sortTypes);
|
||||
this.stt = new SortableTable(tf.dom(), _sortTypes);
|
||||
|
||||
/*** external table headers adapter ***/
|
||||
if (this.asyncSort && this.triggerIds.length > 0) {
|
||||
|
@ -453,6 +459,29 @@ export default class AdapterSortableTable extends Feature {
|
|||
}
|
||||
}
|
||||
|
||||
_addDateType(colIndex, types) {
|
||||
let tf = this.tf;
|
||||
let dateType = tf.feature('dateType');
|
||||
let locale = dateType.getOptions(colIndex, types).locale || tf.locale;
|
||||
let colType = `${DATE}-${locale}`;
|
||||
|
||||
this.addSortType(colType, (value) => {
|
||||
let parsedDate = dateType.parse(value, locale);
|
||||
// Invalid date defaults to Wed Feb 04 -768 11:00:00
|
||||
return isNaN(+parsedDate) ? new Date(-86400000000000) : parsedDate;
|
||||
});
|
||||
return colType;
|
||||
}
|
||||
|
||||
_addNumberType(colIndex, decimal) {
|
||||
let colType = `${FORMATTED_NUMBER}${decimal === '.' ? '' : '-custom'}`;
|
||||
|
||||
this.addSortType(colType, (value) => {
|
||||
return parseNb(value, decimal);
|
||||
});
|
||||
return colType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove extension
|
||||
*/
|
||||
|
@ -461,10 +490,8 @@ export default class AdapterSortableTable extends Feature {
|
|||
return;
|
||||
}
|
||||
let tf = this.tf;
|
||||
this.emitter.off(['sort'],
|
||||
(tf, colIdx, desc) => this.sortByColumnIndex(colIdx, desc));
|
||||
this.emitter.off(['sort'], bound(this.sortByColumnIndexHandler, this));
|
||||
this.sorted = false;
|
||||
this.initialized = false;
|
||||
this.stt.destroy();
|
||||
|
||||
let ids = tf.getFiltersId();
|
||||
|
@ -481,31 +508,12 @@ export default class AdapterSortableTable extends Feature {
|
|||
|
||||
}
|
||||
|
||||
//Converters
|
||||
function usNumberConverter(s) {
|
||||
return removeNbFormat(s, 'us');
|
||||
}
|
||||
function euNumberConverter(s) {
|
||||
return removeNbFormat(s, 'eu');
|
||||
}
|
||||
function dateConverter(s, format) {
|
||||
return formatDate(s, format);
|
||||
}
|
||||
function dmyDateConverter(s) {
|
||||
return dateConverter(s, 'DMY');
|
||||
}
|
||||
function mdyDateConverter(s) {
|
||||
return dateConverter(s, 'MDY');
|
||||
}
|
||||
function ymdDateConverter(s) {
|
||||
return dateConverter(s, 'YMD');
|
||||
}
|
||||
function ddmmmyyyyDateConverter(s) {
|
||||
return dateConverter(s, 'DDMMMYYYY');
|
||||
}
|
||||
AdapterSortableTable.meta = {altName: 'sort'};
|
||||
|
||||
//Converters
|
||||
function ipAddress(value) {
|
||||
let vals = value.split('.');
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
for (let x in vals) {
|
||||
let val = vals[x];
|
||||
while (3 > val.length) {
|
||||
|
|
|
@ -2,7 +2,7 @@ import AdapterSortableTable from './adapterSortabletable';
|
|||
import {root} from '../../root';
|
||||
|
||||
if (!root.SortableTable) {
|
||||
require('script!sortabletable');
|
||||
require('script-loader!sortabletable');
|
||||
}
|
||||
|
||||
export default AdapterSortableTable;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import {toCamelCase} from './string';
|
||||
|
||||
const NOTIMPLEMENTED = 'Not implemented.';
|
||||
const NOT_IMPLEMENTED = 'Not implemented.';
|
||||
|
||||
/**
|
||||
* Base class defining the interface of a TableFilter feature
|
||||
|
@ -8,9 +9,11 @@ export class Feature {
|
|||
/**
|
||||
* Creates an instance of Feature
|
||||
* @param {Object} tf TableFilter instance
|
||||
* @param {String} feature Feature name known by TableFilter
|
||||
* @param {Class} feature Feature class for TableFilter registration
|
||||
*/
|
||||
constructor(tf, feature) {
|
||||
constructor(tf, cls) {
|
||||
cls.meta = cls.meta || {};
|
||||
|
||||
/**
|
||||
* TableFilter instance
|
||||
* @type {TableFilter}
|
||||
|
@ -18,16 +21,18 @@ export class Feature {
|
|||
this.tf = tf;
|
||||
|
||||
/**
|
||||
* Feature name
|
||||
* Feature name is the camelised class name as per TableFilter's
|
||||
* convention
|
||||
* @type {String}
|
||||
*/
|
||||
this.feature = feature;
|
||||
this.feature = cls.meta.altName || cls.meta.name
|
||||
|| toCamelCase(cls.name);
|
||||
|
||||
/**
|
||||
* TableFilter feature setting
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this.enabled = tf[feature];
|
||||
this.enabled = tf[this.feature];
|
||||
|
||||
/**
|
||||
* TableFilter configuration
|
||||
|
@ -46,13 +51,16 @@ export class Feature {
|
|||
* @type {Boolean}
|
||||
*/
|
||||
this.initialized = false;
|
||||
|
||||
/** Subscribe to destroy event */
|
||||
this.emitter.on(['destroy'], () => this.destroy());
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the feature
|
||||
*/
|
||||
init() {
|
||||
throw new Error(NOTIMPLEMENTED);
|
||||
throw new Error(NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -67,7 +75,7 @@ export class Feature {
|
|||
* Destroy the feature
|
||||
*/
|
||||
destroy() {
|
||||
throw new Error(NOTIMPLEMENTED);
|
||||
throw new Error(NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -89,6 +97,6 @@ export class Feature {
|
|||
* @returns {Boolean}
|
||||
*/
|
||||
isEnabled() {
|
||||
return this.enabled;
|
||||
return this.enabled === true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
/**
|
||||
* Misc helpers
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns a unformatted number
|
||||
* @param {String} Formatted number
|
||||
* @param {String} Format type, currently 'us' or 'eu'
|
||||
* @return {String} Unformatted number
|
||||
*/
|
||||
export const removeNbFormat = (data, format = 'us') => {
|
||||
let n = data;
|
||||
if (format.toLowerCase() === 'us') {
|
||||
n = + n.replace(/[^\d\.-]/g, '');
|
||||
} else {
|
||||
n = + n.replace(/[^\d\,-]/g, '').replace(',', '.');
|
||||
}
|
||||
return n;
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
import {Feature} from '../feature';
|
||||
import {addClass, removeClass} from '../dom';
|
||||
import {defaultsStr} from '../settings';
|
||||
import {bound} from '../event';
|
||||
|
||||
/**
|
||||
* Rows with alternating background color for improved readability
|
||||
|
@ -12,20 +14,21 @@ export class AlternateRows extends Feature {
|
|||
* @param {Object} tf TableFilter instance
|
||||
*/
|
||||
constructor(tf) {
|
||||
super(tf, 'alternateRows');
|
||||
super(tf, AlternateRows);
|
||||
|
||||
let config = this.config;
|
||||
|
||||
/**
|
||||
* Css class for even rows (default: 'even')
|
||||
* @type {String}
|
||||
*/
|
||||
this.evenCss = config.even_row_css_class || 'even';
|
||||
this.evenCss = defaultsStr(config.even_row_css_class, 'even');
|
||||
|
||||
/**
|
||||
* Css class for odd rows (default: 'odd')
|
||||
* @type {String}
|
||||
*/
|
||||
this.oddCss = config.odd_row_css_class || 'odd';
|
||||
this.oddCss = defaultsStr(config.odd_row_css_class, 'odd');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -40,13 +43,11 @@ export class AlternateRows extends Feature {
|
|||
|
||||
// Subscribe to events
|
||||
this.emitter.on(['row-processed', 'row-paged'],
|
||||
(tf, rowIndex, arrIndex, isValid) =>
|
||||
this.processRow(rowIndex, arrIndex, isValid));
|
||||
this.emitter.on(['column-sorted'], () => this.processAll());
|
||||
bound(this.processRowHandler, this));
|
||||
this.emitter.on(['column-sorted', 'rows-changed'],
|
||||
bound(this.processAll, this));
|
||||
|
||||
/**
|
||||
* @inherited
|
||||
*/
|
||||
/** @inherited */
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
|
@ -95,7 +96,7 @@ export class AlternateRows extends Feature {
|
|||
if (!this.isEnabled() || isNaN(rowIdx)) {
|
||||
return;
|
||||
}
|
||||
let rows = this.tf.tbl.rows;
|
||||
let rows = this.tf.dom().rows;
|
||||
let i = isNaN(idx) ? rowIdx : idx;
|
||||
this.removeRowBg(rowIdx);
|
||||
|
||||
|
@ -111,11 +112,16 @@ export class AlternateRows extends Feature {
|
|||
if (isNaN(idx)) {
|
||||
return;
|
||||
}
|
||||
let rows = this.tf.tbl.rows;
|
||||
let rows = this.tf.dom().rows;
|
||||
removeClass(rows[idx], this.oddCss);
|
||||
removeClass(rows[idx], this.evenCss);
|
||||
}
|
||||
|
||||
/** @private */
|
||||
processRowHandler(tf, rowIndex, arrIndex, isValid) {
|
||||
this.processRow(rowIndex, arrIndex, isValid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all alternating backgrounds
|
||||
*/
|
||||
|
@ -123,16 +129,15 @@ export class AlternateRows extends Feature {
|
|||
if (!this.initialized) {
|
||||
return;
|
||||
}
|
||||
let nbRows = this.tf.getRowsNb(true);
|
||||
for (let i = 0; i < nbRows; i++) {
|
||||
this.removeRowBg(i);
|
||||
}
|
||||
|
||||
let eachRow = this.tf.eachRow(0);
|
||||
eachRow((row, i) => this.removeRowBg(i));
|
||||
|
||||
// Unsubscribe to events
|
||||
this.emitter.off(['row-processed', 'row-paged'],
|
||||
(tf, rowIndex, arrIndex, isValid) =>
|
||||
this.processRow(rowIndex, arrIndex, isValid));
|
||||
this.emitter.off(['column-sorted'], () => this.processAll());
|
||||
bound(this.processRowHandler, this));
|
||||
this.emitter.off(['column-sorted', 'rows-changed'],
|
||||
bound(this.processAll, this));
|
||||
|
||||
this.initialized = false;
|
||||
}
|
||||
|
|
160
src/modules/baseDropdown.js
Normal file
|
@ -0,0 +1,160 @@
|
|||
import {Feature} from '../feature';
|
||||
import {
|
||||
ignoreCase, numSortAsc, numSortDesc,
|
||||
dateSortAsc, dateSortDesc, sortNumberStr, sortDateStr
|
||||
} from '../sort';
|
||||
import {isArray, isObj, isEmpty} from '../types';
|
||||
import {NUMBER, FORMATTED_NUMBER, DATE} from '../const';
|
||||
|
||||
/**
|
||||
* Base class for Dropdown and CheckList UI components
|
||||
* @export
|
||||
* @class BaseDropdown
|
||||
* @extends {Feature}
|
||||
*/
|
||||
export class BaseDropdown extends Feature {
|
||||
|
||||
/**
|
||||
* Creates an instance of BaseDropdown
|
||||
* @param {TableFilter} tf
|
||||
*/
|
||||
constructor(tf, cls) {
|
||||
super(tf, cls);
|
||||
|
||||
let f = this.config;
|
||||
|
||||
/**
|
||||
* Filter options custom sorter on a column basis
|
||||
* @type {Object}
|
||||
*/
|
||||
this.customSorter = isObj(f.filter_options_sorter) &&
|
||||
isArray(f.filter_options_sorter.col) &&
|
||||
isArray(f.filter_options_sorter.comparer) ?
|
||||
f.filter_options_sorter :
|
||||
null;
|
||||
|
||||
// TODO: move here all properties shared by Dropdown and CheckList
|
||||
|
||||
/**
|
||||
* Has custom options
|
||||
* @type {Boolean}
|
||||
* @private
|
||||
*/
|
||||
this.isCustom = false;
|
||||
|
||||
/**
|
||||
* List of options values
|
||||
* @type {Array}
|
||||
* @private
|
||||
*/
|
||||
this.opts = [];
|
||||
|
||||
/**
|
||||
* List of options texts for custom values
|
||||
* @type {Array}
|
||||
* @private
|
||||
*/
|
||||
this.optsTxt = [];
|
||||
|
||||
/**
|
||||
* List of options to be excluded from the checklist filter
|
||||
* @type {Array}
|
||||
* @private
|
||||
*/
|
||||
this.excludedOpts = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort passed options based on the type of the specified column
|
||||
* @param {Number} colIndex Column index
|
||||
* @param {Array} [options=[]] Collection of values
|
||||
* @return {Array} Sorted values
|
||||
* @private
|
||||
*/
|
||||
sortOptions(colIndex, options = []) {
|
||||
let {tf} = this;
|
||||
|
||||
if (tf.isCustomOptions(colIndex) || !tf.sortSlc ||
|
||||
(isArray(tf.sortSlc) && tf.sortSlc.indexOf(colIndex) === -1)) {
|
||||
return options;
|
||||
}
|
||||
|
||||
let { caseSensitive, sortFilterOptionsDesc } = tf;
|
||||
let isSortDesc = sortFilterOptionsDesc.indexOf(colIndex) !== -1;
|
||||
let compareFn;
|
||||
|
||||
if (this.customSorter &&
|
||||
this.customSorter.col.indexOf(colIndex) !== -1) {
|
||||
var idx = this.customSorter.col.indexOf(colIndex);
|
||||
compareFn = this.customSorter.comparer[idx];
|
||||
}
|
||||
else if (tf.hasType(colIndex, [NUMBER, FORMATTED_NUMBER])) {
|
||||
let decimal = tf.getDecimal(colIndex);
|
||||
let comparer = isSortDesc ? numSortDesc : numSortAsc;
|
||||
compareFn = sortNumberStr(comparer, decimal);
|
||||
}
|
||||
else if (tf.hasType(colIndex, [DATE])) {
|
||||
let locale = tf.feature('dateType').getLocale(colIndex);
|
||||
let comparer = isSortDesc ? dateSortDesc : dateSortAsc;
|
||||
compareFn = sortDateStr(comparer, locale);
|
||||
} else { // string
|
||||
compareFn = caseSensitive ? undefined : ignoreCase;
|
||||
if (isSortDesc) {
|
||||
return options.sort(compareFn).reverse();
|
||||
}
|
||||
}
|
||||
|
||||
return options.sort(compareFn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Regenerate filters of specified columns and maintain selection if any
|
||||
* @param {Array} colIndexes Collection of column indexes
|
||||
* @private
|
||||
*/
|
||||
refreshFilters(colIndexes) {
|
||||
colIndexes.forEach((colIdx) => {
|
||||
let values = this.getValues(colIdx);
|
||||
this.build(colIdx, this.tf.linkedFilters);
|
||||
this.selectOptions(colIdx, values);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check passed row contains a valid linked value
|
||||
* @param {Number} rowIdx Row index
|
||||
* @param {Number} activeFilterIdx Current active filter index
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
isValidLinkedValue(rowIdx, activeFilterIdx) {
|
||||
let tf = this.tf;
|
||||
|
||||
if (tf.disableExcludedOptions) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (tf.paging) {
|
||||
if (!isEmpty(activeFilterIdx) && tf.isRowValid(rowIdx)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (tf.isRowDisplayed(rowIdx)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh linked filters to offer only selected options
|
||||
*/
|
||||
linkFilters() {
|
||||
let tf = this.tf;
|
||||
if (!tf.linkedFilters || !tf.activeFilterId) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.refreshAll();
|
||||
}
|
||||
}
|
|
@ -1,29 +1,28 @@
|
|||
import {Feature} from '../feature';
|
||||
import {BaseDropdown} from './baseDropdown';
|
||||
import {
|
||||
addClass, createCheckItem, createText, createElm, elm, getText,
|
||||
removeClass, tag
|
||||
addClass, createCheckItem, createText, createElm, elm, removeClass, tag
|
||||
} from '../dom';
|
||||
import {has} from '../array';
|
||||
import {matchCase, trim, rgxEsc} from '../string';
|
||||
import {ignoreCase, numSortAsc, numSortDesc} from '../sort';
|
||||
import {addEvt, removeEvt, targetEvt} from '../event';
|
||||
import {isEmpty} from '../types';
|
||||
import {CHECKLIST, NONE} from '../const';
|
||||
|
||||
const SORT_ERROR = 'Filter options for column {0} cannot be sorted in ' +
|
||||
'{1} manner.';
|
||||
import {defaultsStr, defaultsBool} from '../settings';
|
||||
|
||||
/**
|
||||
* Checklist filter UI component
|
||||
* @export
|
||||
* @class CheckList
|
||||
* @extends {BaseDropdown}
|
||||
*/
|
||||
export class CheckList extends Feature {
|
||||
export class CheckList extends BaseDropdown {
|
||||
|
||||
/**
|
||||
* Creates an instance of CheckList
|
||||
* @param {TableFilter} tf TableFilter instance
|
||||
*/
|
||||
constructor(tf) {
|
||||
super(tf, 'checkList');
|
||||
super(tf, CheckList);
|
||||
|
||||
let f = this.config;
|
||||
|
||||
|
@ -37,48 +36,57 @@ export class CheckList extends Feature {
|
|||
* Css class for the container of the checklist filter (div)
|
||||
* @type {String}
|
||||
*/
|
||||
this.containerCssClass = f.div_checklist_css_class || 'div_checklist';
|
||||
this.containerCssClass = defaultsStr(f.div_checklist_css_class,
|
||||
'div_checklist');
|
||||
|
||||
/**
|
||||
* Css class for the checklist filter element (ul)
|
||||
* @type {String}
|
||||
*/
|
||||
this.filterCssClass = f.checklist_css_class || 'flt_checklist';
|
||||
this.filterCssClass = defaultsStr(f.checklist_css_class,
|
||||
'flt_checklist');
|
||||
|
||||
/**
|
||||
* Css class for the item of a checklist (li)
|
||||
* @type {String}
|
||||
*/
|
||||
this.itemCssClass = f.checklist_item_css_class || 'flt_checklist_item';
|
||||
this.itemCssClass = defaultsStr(f.checklist_item_css_class,
|
||||
'flt_checklist_item');
|
||||
|
||||
/**
|
||||
* Css class for a selected item of a checklist (li)
|
||||
* @type {String}
|
||||
*/
|
||||
this.selectedItemCssClass =
|
||||
f.checklist_selected_item_css_class || 'flt_checklist_slc_item';
|
||||
this.selectedItemCssClass = defaultsStr(
|
||||
f.checklist_selected_item_css_class,
|
||||
'flt_checklist_slc_item'
|
||||
);
|
||||
|
||||
/**
|
||||
* Text placed in the filter's container when load filter on demand
|
||||
* feature is enabled
|
||||
* @type {String}
|
||||
*/
|
||||
this.activateText =
|
||||
f.activate_checklist_text || 'Click to load filter data';
|
||||
this.activateText = defaultsStr(
|
||||
f.activate_checklist_text,
|
||||
'Click to load filter data'
|
||||
);
|
||||
|
||||
/**
|
||||
* Css class for a disabled item of a checklist (li)
|
||||
* @type {String}
|
||||
*/
|
||||
this.disabledItemCssClass = f.checklist_item_disabled_css_class ||
|
||||
'flt_checklist_item_disabled';
|
||||
this.disabledItemCssClass = defaultsStr(
|
||||
f.checklist_item_disabled_css_class,
|
||||
'flt_checklist_item_disabled'
|
||||
);
|
||||
|
||||
/**
|
||||
* Enable the reset filter option as first item
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this.enableResetOption = f.enable_checklist_reset_filter === false ?
|
||||
false : true;
|
||||
this.enableResetOption = defaultsBool(f.enable_checklist_reset_filter,
|
||||
true);
|
||||
|
||||
/**
|
||||
* Prefix for container element ID
|
||||
|
@ -86,52 +94,26 @@ export class CheckList extends Feature {
|
|||
* @private
|
||||
*/
|
||||
this.prfx = 'chkdiv_';
|
||||
|
||||
/**
|
||||
* Has custom options
|
||||
* @type {Boolean}
|
||||
* @private
|
||||
*/
|
||||
this.isCustom = false;
|
||||
|
||||
/**
|
||||
* List of options values
|
||||
* @type {Array}
|
||||
* @private
|
||||
*/
|
||||
this.opts = [];
|
||||
|
||||
/**
|
||||
* List of options texts for custom values
|
||||
* @type {Array}
|
||||
* @private
|
||||
*/
|
||||
this.optsTxt = [];
|
||||
|
||||
/**
|
||||
* List of options to be excluded from the checklist filter
|
||||
* @type {Array}
|
||||
* @private
|
||||
*/
|
||||
this.excludedOpts = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checklist option click event handler
|
||||
* @param {Event} evt
|
||||
* @private
|
||||
*/
|
||||
optionClick(evt) {
|
||||
let elm = targetEvt(evt);
|
||||
let tf = this.tf;
|
||||
|
||||
this.emitter.emit('filter-focus', tf, elm);
|
||||
this.setCheckListValues(elm);
|
||||
this.setItemOption(elm);
|
||||
tf.filter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checklist container click event handler for load-on-demand feature
|
||||
* @param {Event} evt
|
||||
* @private
|
||||
*/
|
||||
onCheckListClick(evt) {
|
||||
let elm = targetEvt(evt);
|
||||
|
@ -143,6 +125,14 @@ export class CheckList extends Feature {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh all checklist filters
|
||||
*/
|
||||
refreshAll() {
|
||||
let colIdxs = this.tf.getFiltersByType(CHECKLIST, true);
|
||||
this.refreshFilters(colIdxs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize checklist filter
|
||||
* @param {Number} colIndex Column index
|
||||
|
@ -152,10 +142,10 @@ export class CheckList extends Feature {
|
|||
init(colIndex, isExternal, container) {
|
||||
let tf = this.tf;
|
||||
let externalFltTgtId = isExternal ?
|
||||
tf.externalFltTgtIds[colIndex] : null;
|
||||
tf.externalFltIds[colIndex] : null;
|
||||
|
||||
let divCont = createElm('div',
|
||||
['id', this.prfx + colIndex + '_' + tf.id],
|
||||
['id', `${this.prfx}${colIndex}_${tf.id}`],
|
||||
['ct', colIndex], ['filled', '0']);
|
||||
divCont.className = this.containerCssClass;
|
||||
|
||||
|
@ -167,7 +157,7 @@ export class CheckList extends Feature {
|
|||
}
|
||||
|
||||
this.containers[colIndex] = divCont;
|
||||
tf.fltIds.push(tf.prfxFlt + colIndex + '_' + tf.id);
|
||||
tf.fltIds.push(tf.buildFilterId(colIndex));
|
||||
|
||||
if (!tf.loadFltOnDemand) {
|
||||
this.build(colIndex);
|
||||
|
@ -178,7 +168,7 @@ export class CheckList extends Feature {
|
|||
|
||||
this.emitter.on(
|
||||
['build-checklist-filter'],
|
||||
(tf, colIndex) => this.build(colIndex)
|
||||
(tf, colIndex, isLinked) => this.build(colIndex, isLinked)
|
||||
);
|
||||
|
||||
this.emitter.on(
|
||||
|
@ -186,97 +176,40 @@ export class CheckList extends Feature {
|
|||
(tf, colIndex, values) => this.selectOptions(colIndex, values)
|
||||
);
|
||||
|
||||
/**
|
||||
* @inherited
|
||||
*/
|
||||
this.emitter.on(['rows-changed'], () => this.refreshAll());
|
||||
|
||||
this.emitter.on(['after-filtering'], () => this.linkFilters());
|
||||
|
||||
/** @inherited */
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build checklist UI
|
||||
* @param {Number} colIndex Column index
|
||||
* @param {Boolean} isLinked Enable linked filters behaviour
|
||||
*/
|
||||
build(colIndex) {
|
||||
build(colIndex, isLinked = false) {
|
||||
let tf = this.tf;
|
||||
colIndex = parseInt(colIndex, 10);
|
||||
colIndex = Number(colIndex);
|
||||
|
||||
this.emitter.emit('before-populating-filter', tf, colIndex);
|
||||
|
||||
/** @inherited */
|
||||
this.opts = [];
|
||||
/** @inherited */
|
||||
this.optsTxt = [];
|
||||
|
||||
let flt = this.containers[colIndex];
|
||||
let ul = createElm('ul', ['id', tf.fltIds[colIndex]],
|
||||
let ul = createElm('ul',
|
||||
['id', tf.fltIds[colIndex]],
|
||||
['colIndex', colIndex]);
|
||||
ul.className = this.filterCssClass;
|
||||
|
||||
let rows = tf.tbl.rows;
|
||||
let nbRows = tf.getRowsNb(true);
|
||||
let caseSensitive = tf.caseSensitive;
|
||||
/** @inherited */
|
||||
this.isCustom = tf.isCustomOptions(colIndex);
|
||||
|
||||
let activeIdx;
|
||||
let activeFilterId = tf.getActiveFilterId();
|
||||
if (tf.linkedFilters && activeFilterId) {
|
||||
activeIdx = tf.getColumnIndexFromFilterId(activeFilterId);
|
||||
}
|
||||
|
||||
let filteredDataCol = [];
|
||||
if (tf.linkedFilters && tf.disableExcludedOptions) {
|
||||
this.excludedOpts = [];
|
||||
}
|
||||
|
||||
flt.innerHTML = '';
|
||||
|
||||
for (let k = tf.refRow; k < nbRows; k++) {
|
||||
// always visible rows don't need to appear on selects as always
|
||||
// valid
|
||||
if (tf.hasVisibleRows && tf.visibleRows.indexOf(k) !== -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let cells = rows[k].cells;
|
||||
let ncells = cells.length;
|
||||
|
||||
// checks if row has exact cell #
|
||||
if (ncells !== tf.nbCells || this.isCustom) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// this loop retrieves cell data
|
||||
for (let j = 0; j < ncells; j++) {
|
||||
// WTF: cyclomatic complexity hell :)
|
||||
if ((colIndex === j && (!tf.linkedFilters ||
|
||||
(tf.linkedFilters && tf.disableExcludedOptions))) ||
|
||||
(colIndex === j && tf.linkedFilters &&
|
||||
((rows[k].style.display === '' && !tf.paging) ||
|
||||
(tf.paging && ((!activeIdx ||
|
||||
activeIdx === colIndex) ||
|
||||
(activeIdx !== colIndex &&
|
||||
tf.validRowsIndex.indexOf(k) !== -1)))))) {
|
||||
|
||||
let cellData = tf.getCellData(cells[j]);
|
||||
//Vary Peter's patch
|
||||
let cellString = matchCase(cellData, caseSensitive);
|
||||
// checks if celldata is already in array
|
||||
if (!has(this.opts, cellString, caseSensitive)) {
|
||||
this.opts.push(cellData);
|
||||
}
|
||||
let filteredCol = filteredDataCol[j];
|
||||
if (tf.linkedFilters && tf.disableExcludedOptions) {
|
||||
if (!filteredCol) {
|
||||
filteredCol = tf.getFilteredDataCol(j);
|
||||
}
|
||||
if (!has(filteredCol, cellString, caseSensitive) &&
|
||||
!has(this.excludedOpts, cellString,
|
||||
caseSensitive)) {
|
||||
this.excludedOpts.push(cellData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Retrieves custom values
|
||||
if (this.isCustom) {
|
||||
let customValues = tf.getCustomOptions(colIndex);
|
||||
|
@ -284,48 +217,64 @@ export class CheckList extends Feature {
|
|||
this.optsTxt = customValues[1];
|
||||
}
|
||||
|
||||
if (tf.sortSlc && !this.isCustom) {
|
||||
if (!caseSensitive) {
|
||||
this.opts.sort(ignoreCase);
|
||||
if (this.excludedOpts) {
|
||||
this.excludedOpts.sort(ignoreCase);
|
||||
let activeIdx;
|
||||
let activeFilterId = tf.getActiveFilterId();
|
||||
|
||||
if (isLinked && activeFilterId) {
|
||||
activeIdx = tf.getColumnIndexFromFilterId(activeFilterId);
|
||||
}
|
||||
|
||||
let filteredDataCol = [];
|
||||
if (isLinked && tf.disableExcludedOptions) {
|
||||
/** @inherited */
|
||||
this.excludedOpts = [];
|
||||
}
|
||||
|
||||
flt.innerHTML = '';
|
||||
|
||||
let eachRow = tf.eachRow();
|
||||
eachRow(
|
||||
(row) => {
|
||||
let cellValue = tf.getCellValue(row.cells[colIndex]);
|
||||
//Vary Peter's patch
|
||||
let cellString = matchCase(cellValue, caseSensitive);
|
||||
// checks if celldata is already in array
|
||||
if (!has(this.opts, cellString, caseSensitive)) {
|
||||
this.opts.push(cellValue);
|
||||
}
|
||||
} else {
|
||||
this.opts.sort();
|
||||
if (this.excludedOpts) {
|
||||
this.excludedOpts.sort();
|
||||
let filteredCol = filteredDataCol[colIndex];
|
||||
if (isLinked && tf.disableExcludedOptions) {
|
||||
if (!filteredCol) {
|
||||
filteredCol = tf.getVisibleColumnValues(colIndex);
|
||||
}
|
||||
if (!has(filteredCol, cellString, caseSensitive) &&
|
||||
!has(this.excludedOpts, cellString, caseSensitive)) {
|
||||
this.excludedOpts.push(cellValue);
|
||||
}
|
||||
}
|
||||
},
|
||||
// continue conditions function
|
||||
(row, k) => {
|
||||
// excluded rows don't need to appear on selects as always valid
|
||||
if (tf.excludeRows.indexOf(k) !== -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// checks if row has expected number of cells
|
||||
if (row.cells.length !== tf.nbCells || this.isCustom) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isLinked && !this.isValidLinkedValue(k, activeIdx)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
//asc sort
|
||||
if (tf.sortNumAsc.indexOf(colIndex) !== -1) {
|
||||
try {
|
||||
this.opts.sort(numSortAsc);
|
||||
if (this.excludedOpts) {
|
||||
this.excludedOpts.sort(numSortAsc);
|
||||
}
|
||||
if (this.isCustom) {
|
||||
this.optsTxt.sort(numSortAsc);
|
||||
}
|
||||
} catch (e) {
|
||||
throw new Error(SORT_ERROR.replace('{0}', colIndex)
|
||||
.replace('{1}', 'ascending'));
|
||||
}//in case there are alphanumeric values
|
||||
}
|
||||
//desc sort
|
||||
if (tf.sortNumDesc.indexOf(colIndex) !== -1) {
|
||||
try {
|
||||
this.opts.sort(numSortDesc);
|
||||
if (this.excludedOpts) {
|
||||
this.excludedOpts.sort(numSortDesc);
|
||||
}
|
||||
if (this.isCustom) {
|
||||
this.optsTxt.sort(numSortDesc);
|
||||
}
|
||||
} catch (e) {
|
||||
throw new Error(SORT_ERROR.replace('{0}', colIndex)
|
||||
.replace('{1}', 'descending'));
|
||||
}//in case there are alphanumeric values
|
||||
);
|
||||
|
||||
//sort options
|
||||
this.opts = this.sortOptions(colIndex, this.opts);
|
||||
if (this.excludedOpts) {
|
||||
this.excludedOpts = this.sortOptions(colIndex, this.excludedOpts);
|
||||
}
|
||||
|
||||
this.addChecks(colIndex, ul);
|
||||
|
@ -343,6 +292,7 @@ export class CheckList extends Feature {
|
|||
* Add checklist options
|
||||
* @param {Number} colIndex Column index
|
||||
* @param {Object} ul Ul element
|
||||
* @private
|
||||
*/
|
||||
addChecks(colIndex, ul) {
|
||||
let tf = this.tf;
|
||||
|
@ -351,8 +301,10 @@ export class CheckList extends Feature {
|
|||
for (let y = 0; y < this.opts.length; y++) {
|
||||
let val = this.opts[y]; //item value
|
||||
let lbl = this.isCustom ? this.optsTxt[y] : val; //item text
|
||||
let li = createCheckItem(tf.fltIds[colIndex] + '_' + (y + chkCt),
|
||||
val, lbl);
|
||||
let fltId = tf.fltIds[colIndex];
|
||||
let lblIdx = y + chkCt;
|
||||
let li = createCheckItem(`${fltId}_${lblIdx}`, val, lbl,
|
||||
['data-idx', lblIdx]);
|
||||
li.className = this.itemCssClass;
|
||||
|
||||
if (tf.linkedFilters && tf.disableExcludedOptions &&
|
||||
|
@ -377,12 +329,14 @@ export class CheckList extends Feature {
|
|||
* Add checklist header option
|
||||
* @param {Number} colIndex Column index
|
||||
* @param {Object} ul Ul element
|
||||
* @private
|
||||
*/
|
||||
addTChecks(colIndex, ul) {
|
||||
let tf = this.tf;
|
||||
let chkCt = 1;
|
||||
let li0 = createCheckItem(tf.fltIds[colIndex] + '_0', '',
|
||||
tf.displayAllText);
|
||||
let fltId = tf.fltIds[colIndex];
|
||||
let li0 = createCheckItem(`${fltId}_0`, '',
|
||||
tf.getClearFilterText(colIndex), ['data-idx', 0]);
|
||||
li0.className = this.itemCssClass;
|
||||
ul.appendChild(li0);
|
||||
|
||||
|
@ -393,8 +347,8 @@ export class CheckList extends Feature {
|
|||
}
|
||||
|
||||
if (tf.enableEmptyOption) {
|
||||
let li1 = createCheckItem(tf.fltIds[colIndex] + '_1',
|
||||
tf.emOperator, tf.emptyText);
|
||||
let li1 = createCheckItem(`${fltId}_1`, tf.emOperator,
|
||||
tf.emptyText, ['data-idx', 1]);
|
||||
li1.className = this.itemCssClass;
|
||||
ul.appendChild(li1);
|
||||
addEvt(li1.check, 'click', evt => this.optionClick(evt));
|
||||
|
@ -402,8 +356,8 @@ export class CheckList extends Feature {
|
|||
}
|
||||
|
||||
if (tf.enableNonEmptyOption) {
|
||||
let li2 = createCheckItem(tf.fltIds[colIndex] + '_2', tf.nmOperator,
|
||||
tf.nonEmptyText);
|
||||
let li2 = createCheckItem(`${fltId}_2`, tf.nmOperator,
|
||||
tf.nonEmptyText, ['data-idx', 2]);
|
||||
li2.className = this.itemCssClass;
|
||||
ul.appendChild(li2);
|
||||
addEvt(li2.check, 'click', evt => this.optionClick(evt));
|
||||
|
@ -413,80 +367,73 @@ export class CheckList extends Feature {
|
|||
}
|
||||
|
||||
/**
|
||||
* Store checked options in DOM element attribute
|
||||
* Set/unset value of passed item option in filter's DOM element attribute
|
||||
* @param {Object} o checklist option DOM element
|
||||
* @private
|
||||
*/
|
||||
setCheckListValues(o) {
|
||||
setItemOption(o) {
|
||||
if (!o) {
|
||||
return;
|
||||
}
|
||||
|
||||
let tf = this.tf;
|
||||
let chkValue = o.value; //checked item value
|
||||
// TODO: provide helper to extract column index, ugly!
|
||||
let chkIndex = parseInt(o.id.split('_')[2], 10);
|
||||
let chkIndex = o.dataset.idx;
|
||||
let colIdx = tf.getColumnIndexFromFilterId(o.id);
|
||||
let itemTag = 'LI';
|
||||
|
||||
let n = tf.getFilterElement(parseInt(colIdx, 10));
|
||||
let li = n.childNodes[chkIndex];
|
||||
let colIndex = n.getAttribute('colIndex');
|
||||
let fltValue = n.getAttribute('value'); //filter value (ul tag)
|
||||
let fltIndexes = n.getAttribute('indexes'); //selected items (ul tag)
|
||||
let items = n.childNodes;
|
||||
let li = items[chkIndex];
|
||||
//selected values (ul tag)
|
||||
let slcValues = n.getAttribute('value') || '';
|
||||
//selected items indexes (ul tag)
|
||||
let slcIndexes = n.getAttribute('indexes') || '';
|
||||
|
||||
if (o.checked) {
|
||||
//show all item
|
||||
if (chkValue === '') {
|
||||
if ((fltIndexes && fltIndexes !== '')) {
|
||||
//items indexes
|
||||
let indSplit = fltIndexes.split(tf.separator);
|
||||
//checked items loop
|
||||
for (let u = 0; u < indSplit.length; u++) {
|
||||
//checked item
|
||||
let cChk = elm(tf.fltIds[colIndex] + '_' +
|
||||
indSplit[u]);
|
||||
if (cChk) {
|
||||
cChk.checked = false;
|
||||
removeClass(n.childNodes[indSplit[u]],
|
||||
this.selectedItemCssClass);
|
||||
}
|
||||
//items indexes
|
||||
let indexes = slcIndexes.split(tf.separator);
|
||||
indexes.forEach(idx => {
|
||||
idx = Number(idx);
|
||||
let li = items[idx];
|
||||
let chx = tag(li, 'input')[0];
|
||||
if (chx && idx > 0) {
|
||||
chx.checked = false;
|
||||
removeClass(li, this.selectedItemCssClass);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
n.setAttribute('value', '');
|
||||
n.setAttribute('indexes', '');
|
||||
|
||||
} else {
|
||||
fltValue = (fltValue) ? fltValue : '';
|
||||
chkValue = trim(fltValue + ' ' + chkValue + ' ' +
|
||||
tf.orOperator);
|
||||
chkIndex = fltIndexes + chkIndex + tf.separator;
|
||||
n.setAttribute('value', chkValue);
|
||||
n.setAttribute('indexes', chkIndex);
|
||||
//1st option unchecked
|
||||
if (elm(tf.fltIds[colIndex] + '_0')) {
|
||||
elm(tf.fltIds[colIndex] + '_0').checked = false;
|
||||
let indexes = slcIndexes + chkIndex + tf.separator;
|
||||
let values =
|
||||
trim(slcValues + ' ' + chkValue + ' ' + tf.orOperator);
|
||||
|
||||
n.setAttribute('value', values);
|
||||
n.setAttribute('indexes', indexes);
|
||||
|
||||
//uncheck first option
|
||||
let chx0 = tag(items[0], 'input')[0];
|
||||
if (chx0) {
|
||||
chx0.checked = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (li.nodeName === itemTag) {
|
||||
removeClass(n.childNodes[0], this.selectedItemCssClass);
|
||||
addClass(li, this.selectedItemCssClass);
|
||||
}
|
||||
removeClass(items[0], this.selectedItemCssClass);
|
||||
addClass(li, this.selectedItemCssClass);
|
||||
} else { //removes values and indexes
|
||||
if (chkValue !== '') {
|
||||
let replaceValue = new RegExp(
|
||||
rgxEsc(chkValue + ' ' + tf.orOperator));
|
||||
fltValue = fltValue.replace(replaceValue, '');
|
||||
n.setAttribute('value', trim(fltValue));
|
||||
let replaceValue =
|
||||
new RegExp(rgxEsc(chkValue + ' ' + tf.orOperator));
|
||||
let values = slcValues.replace(replaceValue, '');
|
||||
let replaceIndex = new RegExp(rgxEsc(chkIndex + tf.separator));
|
||||
let indexes = slcIndexes.replace(replaceIndex, '');
|
||||
|
||||
let replaceIndex = new RegExp(
|
||||
rgxEsc(chkIndex + tf.separator));
|
||||
fltIndexes = fltIndexes.replace(replaceIndex, '');
|
||||
n.setAttribute('indexes', fltIndexes);
|
||||
}
|
||||
if (li.nodeName === itemTag) {
|
||||
removeClass(li, this.selectedItemCssClass);
|
||||
}
|
||||
n.setAttribute('value', trim(values));
|
||||
n.setAttribute('indexes', indexes);
|
||||
|
||||
removeClass(li, this.selectedItemCssClass);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -498,38 +445,36 @@ export class CheckList extends Feature {
|
|||
selectOptions(colIndex, values = []) {
|
||||
let tf = this.tf;
|
||||
let flt = tf.getFilterElement(colIndex);
|
||||
if (tf.getFilterType(colIndex) !== CHECKLIST || !flt) {
|
||||
if (!flt || values.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let lisNb = tag(flt, 'li').length;
|
||||
let lis = tag(flt, 'li');
|
||||
|
||||
flt.setAttribute('value', '');
|
||||
flt.setAttribute('indexes', '');
|
||||
|
||||
for (let k = 0; k < lisNb; k++) {
|
||||
let li = tag(flt, 'li')[k];
|
||||
let lbl = tag(li, 'label')[0];
|
||||
[].forEach.call(lis, (li) => {
|
||||
let chk = tag(li, 'input')[0];
|
||||
let lblTxt = matchCase(getText(lbl), tf.caseSensitive);
|
||||
let chkVal = matchCase(chk.value, tf.caseSensitive);
|
||||
|
||||
if (lblTxt !== '' && has(values, lblTxt, tf.caseSensitive)) {
|
||||
if (chkVal !== '' && has(values, chkVal, tf.caseSensitive)) {
|
||||
chk.checked = true;
|
||||
} else {
|
||||
// Check non-empty-text or empty-text option
|
||||
if (values.indexOf(tf.nmOperator) !== -1 &&
|
||||
lblTxt === matchCase(tf.nonEmptyText, tf.caseSensitive)) {
|
||||
chkVal === matchCase(tf.nonEmptyText, tf.caseSensitive)) {
|
||||
chk.checked = true;
|
||||
}
|
||||
else if (values.indexOf(tf.emOperator) !== -1 &&
|
||||
lblTxt === matchCase(tf.emptyText, tf.caseSensitive)) {
|
||||
chkVal === matchCase(tf.emptyText, tf.caseSensitive)) {
|
||||
chk.checked = true;
|
||||
} else {
|
||||
chk.checked = false;
|
||||
}
|
||||
}
|
||||
this.setCheckListValues(chk);
|
||||
}
|
||||
this.setItemOption(chk);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -540,9 +485,12 @@ export class CheckList extends Feature {
|
|||
getValues(colIndex) {
|
||||
let tf = this.tf;
|
||||
let flt = tf.getFilterElement(colIndex);
|
||||
if (!flt) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let fltAttr = flt.getAttribute('value');
|
||||
let values = isEmpty(fltAttr) ? '' : fltAttr;
|
||||
|
||||
//removes last operator ||
|
||||
values = values.substr(0, values.length - 3);
|
||||
//turn || separated values into array
|
||||
|
@ -557,11 +505,15 @@ export class CheckList extends Feature {
|
|||
destroy() {
|
||||
this.emitter.off(
|
||||
['build-checklist-filter'],
|
||||
(tf, colIndex, isExternal) => this.build(colIndex, isExternal)
|
||||
(tf, colIndex, isLinked) => this.build(colIndex, isLinked)
|
||||
);
|
||||
this.emitter.off(
|
||||
['select-checklist-options'],
|
||||
(tf, colIndex, values) => this.selectOptions(colIndex, values)
|
||||
);
|
||||
this.emitter.off(['rows-changed'], () => this.refreshAll());
|
||||
this.emitter.off(['after-filtering'], () => this.linkFilters());
|
||||
|
||||
this.initialized = false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
import {Feature} from '../feature';
|
||||
import {createElm, createText, elm, removeElm} from '../dom';
|
||||
import {addEvt} from '../event';
|
||||
import {defaultsStr} from '../settings';
|
||||
import {isNull} from '../types';
|
||||
import {RIGHT} from './toolbar';
|
||||
|
||||
/**
|
||||
* Clear button UI component
|
||||
|
@ -12,15 +15,55 @@ export class ClearButton extends Feature {
|
|||
* @param {TableFilter} tf TableFilter instance
|
||||
*/
|
||||
constructor(tf) {
|
||||
super(tf, 'btnReset');
|
||||
super(tf, ClearButton);
|
||||
|
||||
let f = this.config;
|
||||
let f = this.config.btn_reset || {};
|
||||
|
||||
/**
|
||||
* Container element ID
|
||||
* @type {String}
|
||||
*/
|
||||
this.targetId = f.btn_reset_target_id || null;
|
||||
this.targetId = defaultsStr(f.target_id, null);
|
||||
|
||||
/**
|
||||
* Text for the clear button
|
||||
* @type {String}
|
||||
*/
|
||||
this.text = defaultsStr(f.text, null);
|
||||
|
||||
/**
|
||||
* Css class for reset button
|
||||
* @type {String}
|
||||
*/
|
||||
this.cssClass = defaultsStr(f.css_class, 'reset');
|
||||
|
||||
/**
|
||||
* Tooltip text for the clear button
|
||||
* @type {String}
|
||||
*/
|
||||
this.tooltip = f.tooltip || 'Clear filters';
|
||||
|
||||
/**
|
||||
* Custom Html string for the clear button
|
||||
* @type {String}
|
||||
*/
|
||||
this.html = defaultsStr(f.html,
|
||||
(!tf.enableIcons || this.text ? null :
|
||||
'<input type="button" value="" class="' + this.cssClass +
|
||||
'" ' + 'title="' + this.tooltip + '" />'));
|
||||
|
||||
/**
|
||||
* Default position in toolbar ('left'|'center'|'right')
|
||||
* @type {String}
|
||||
*/
|
||||
this.toolbarPosition = defaultsStr(f.toolbar_position, RIGHT);
|
||||
|
||||
/**
|
||||
* Clear button container element
|
||||
* @type {DOMElement}
|
||||
* @private
|
||||
*/
|
||||
this.container = null;
|
||||
|
||||
/**
|
||||
* Clear button element
|
||||
|
@ -28,40 +71,6 @@ export class ClearButton extends Feature {
|
|||
* @private
|
||||
*/
|
||||
this.element = null;
|
||||
|
||||
/**
|
||||
* Text for the clear button
|
||||
* @type {String}
|
||||
*/
|
||||
this.text = f.btn_reset_text || 'Reset';
|
||||
|
||||
/**
|
||||
* Css class for reset button
|
||||
* @type {String}
|
||||
*/
|
||||
this.cssClass = f.btn_reset_css_class || 'reset';
|
||||
|
||||
/**
|
||||
* Tooltip text for the clear button
|
||||
* @type {String}
|
||||
*/
|
||||
this.tooltip = f.btn_reset_tooltip || 'Clear filters';
|
||||
|
||||
/**
|
||||
* Custom Html string for the clear button
|
||||
* @type {String}
|
||||
*/
|
||||
this.html = f.btn_reset_html ||
|
||||
(!tf.enableIcons ? null :
|
||||
'<input type="button" value="" class="' + this.cssClass +
|
||||
'" ' + 'title="' + this.tooltip + '" />');
|
||||
|
||||
/**
|
||||
* Prefix for container ID
|
||||
* @type {String}
|
||||
* @private
|
||||
*/
|
||||
this.prfxCont = 'resetspan_';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -85,49 +94,49 @@ export class ClearButton extends Feature {
|
|||
return;
|
||||
}
|
||||
|
||||
let resetspan = createElm('span', ['id', this.prfxCont + tf.id]);
|
||||
this.emitter.emit('initializing-feature', this, !isNull(this.targetId));
|
||||
|
||||
// reset button is added to defined element
|
||||
if (!this.targetId) {
|
||||
tf.setToolbar();
|
||||
}
|
||||
let targetEl = !this.targetId ? tf.rDiv : elm(this.targetId);
|
||||
targetEl.appendChild(resetspan);
|
||||
let cont = createElm('span');
|
||||
|
||||
let targetEl = !this.targetId ?
|
||||
tf.feature('toolbar').container(this.toolbarPosition) :
|
||||
elm(this.targetId);
|
||||
targetEl.appendChild(cont);
|
||||
|
||||
if (!this.html) {
|
||||
let fltReset = createElm('a', ['href', 'javascript:void(0);']);
|
||||
fltReset.className = this.cssClass;
|
||||
fltReset.appendChild(createText(this.text));
|
||||
resetspan.appendChild(fltReset);
|
||||
cont.appendChild(fltReset);
|
||||
addEvt(fltReset, 'click', () => this.onClick());
|
||||
} else {
|
||||
resetspan.innerHTML = this.html;
|
||||
let resetEl = resetspan.firstChild;
|
||||
cont.innerHTML = this.html;
|
||||
let resetEl = cont.firstChild;
|
||||
addEvt(resetEl, 'click', () => this.onClick());
|
||||
}
|
||||
this.element = resetspan.firstChild;
|
||||
this.element = cont.firstChild;
|
||||
this.container = cont;
|
||||
|
||||
/**
|
||||
* @inherited
|
||||
*/
|
||||
/** @inherited */
|
||||
this.initialized = true;
|
||||
|
||||
this.emitter.emit('feature-initialized', this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy ClearButton instance
|
||||
*/
|
||||
destroy() {
|
||||
let tf = this.tf;
|
||||
|
||||
if (!this.initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
let resetspan = elm(this.prfxCont + tf.id);
|
||||
if (resetspan) {
|
||||
removeElm(resetspan);
|
||||
}
|
||||
removeElm(this.element);
|
||||
removeElm(this.container);
|
||||
this.element = null;
|
||||
this.container = null;
|
||||
this.initialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: remove as soon as feature name is fixed
|
||||
ClearButton.meta = {altName: 'btnReset'};
|
||||
|
|
157
src/modules/dateType.js
Normal file
|
@ -0,0 +1,157 @@
|
|||
import {Date as SugarDate} from 'sugar-date';
|
||||
import 'sugar-date/locales';
|
||||
import {Feature} from '../feature';
|
||||
import {isObj, isArray} from '../types';
|
||||
import {DATE} from '../const';
|
||||
import {root} from '../root';
|
||||
|
||||
/**
|
||||
* Wrapper for Sugar Date module providing datetime helpers and locales
|
||||
* @export
|
||||
* @class DateType
|
||||
*/
|
||||
export class DateType extends Feature {
|
||||
|
||||
/**
|
||||
* Creates an instance of DateType
|
||||
* @param {TableFilter} tf TableFilter instance
|
||||
*/
|
||||
constructor(tf) {
|
||||
super(tf, DateType);
|
||||
|
||||
/**
|
||||
* Global locale
|
||||
* @type {String}
|
||||
*/
|
||||
this.locale = tf.locale;
|
||||
|
||||
/**
|
||||
* Sugar Date instance
|
||||
* @type {Object}
|
||||
*/
|
||||
this.datetime = SugarDate;
|
||||
|
||||
this.enable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize DateType instance
|
||||
*/
|
||||
init() {
|
||||
if (this.initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set global locale
|
||||
this.datetime.setLocale(this.locale);
|
||||
|
||||
// Add formats from column types configuration if any
|
||||
this.addConfigFormats(this.tf.colTypes);
|
||||
|
||||
this.emitter.on(
|
||||
['add-date-type-formats'],
|
||||
(tf, types) => this.addConfigFormats(types)
|
||||
);
|
||||
|
||||
// Broadcast date-type initialization
|
||||
this.emitter.emit('date-type-initialized', this.tf, this);
|
||||
|
||||
/** @inherited */
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a string representation of a date for a specified locale and return
|
||||
* a date object
|
||||
* @param {String} dateStr String representation of a date
|
||||
* @param {String} localeCode Locale code (ie 'en-us')
|
||||
* @returns {Date}
|
||||
*/
|
||||
parse(dateStr, localeCode) {
|
||||
return this.datetime.create(dateStr, localeCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check string representation of a date for a specified locale is valid
|
||||
* @param {any} dateStr String representation of a date
|
||||
* @param {any} localeCode Locale code (ie 'en-us')
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
isValid(dateStr, localeCode) {
|
||||
return this.datetime.isValid(this.parse(dateStr, localeCode));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the type object of a specified column as per configuration or
|
||||
* passed collection
|
||||
* @param {Number} colIndex Column index
|
||||
* @param {Array} types Collection of column types, optional
|
||||
* @returns {Object}
|
||||
*/
|
||||
getOptions(colIndex, types) {
|
||||
types = types || this.tf.colTypes;
|
||||
let colType = types[colIndex];
|
||||
return isObj(colType) ? colType : {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the locale code for supplied column index as per configuration
|
||||
* or global setting
|
||||
* @param {Number} colIndex Column index
|
||||
* @returns {String} Locale code (ie: 'en-us')
|
||||
*/
|
||||
getLocale(colIndex) {
|
||||
return this.getOptions(colIndex).locale || this.locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add date time format(s) to a locale as specified by the passed
|
||||
* collection of column types, ie:
|
||||
* [
|
||||
* 'string',
|
||||
* 'number',
|
||||
* { type: 'date', locale: 'en', format: ['{dd}/{MM}/{yyyy}']}
|
||||
* ]
|
||||
*
|
||||
* @param {Array} [types=[]] Collection of column types
|
||||
*/
|
||||
addConfigFormats(types=[]) {
|
||||
types.forEach((type, idx) => {
|
||||
let options = this.getOptions(idx, types);
|
||||
if (options.type === DATE && options.hasOwnProperty('format')) {
|
||||
let locale = this.datetime.getLocale(
|
||||
options.locale || this.locale
|
||||
);
|
||||
let formats = isArray(options.format) ?
|
||||
options.format : [options.format];
|
||||
|
||||
// Sugar date module throws exceptions with locale.addFormat
|
||||
try {
|
||||
formats.forEach((format) => {
|
||||
locale.addFormat(format);
|
||||
});
|
||||
} catch (ex) {
|
||||
root.console.error(ex);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove DateType instance
|
||||
*/
|
||||
destroy() {
|
||||
if (!this.initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: remove added formats
|
||||
|
||||
this.emitter.off(
|
||||
['add-date-type-formats'],
|
||||
(tf, types) => this.addConfigFormats(types)
|
||||
);
|
||||
|
||||
this.initialized = false;
|
||||
}
|
||||
}
|
|
@ -1,25 +1,25 @@
|
|||
import {Feature} from '../feature';
|
||||
import {BaseDropdown} from './baseDropdown';
|
||||
import {createElm, createOpt, elm} from '../dom';
|
||||
import {has} from '../array';
|
||||
import {matchCase} from '../string';
|
||||
import {ignoreCase, numSortAsc, numSortDesc} from '../sort';
|
||||
import {addEvt, targetEvt} from '../event';
|
||||
import {SELECT, MULTIPLE, NONE} from '../const';
|
||||
|
||||
const SORT_ERROR = 'Filter options for column {0} cannot be sorted in ' +
|
||||
'{1} manner.';
|
||||
import {defaultsStr, defaultsBool} from '../settings';
|
||||
|
||||
/**
|
||||
* Dropdown filter UI component
|
||||
* @export
|
||||
* @class Dropdown
|
||||
* @extends {BaseDropdown}
|
||||
*/
|
||||
export class Dropdown extends Feature {
|
||||
export class Dropdown extends BaseDropdown {
|
||||
|
||||
/**
|
||||
* Creates an instance of Dropdown
|
||||
* @param {TableFilter} tf TableFilter instance
|
||||
*/
|
||||
constructor(tf) {
|
||||
super(tf, 'dropdown');
|
||||
super(tf, Dropdown);
|
||||
|
||||
// Configuration object
|
||||
let f = this.config;
|
||||
|
@ -28,41 +28,21 @@ export class Dropdown extends Feature {
|
|||
* Enable the reset filter option as first item
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this.enableSlcResetFilter = f.enable_slc_reset_filter === false ?
|
||||
false : true;
|
||||
this.enableSlcResetFilter =
|
||||
defaultsBool(f.enable_slc_reset_filter, true);
|
||||
|
||||
/**
|
||||
* Non empty option text
|
||||
* @type {String}
|
||||
*/
|
||||
this.nonEmptyText = f.non_empty_text || '(Non empty)';
|
||||
this.nonEmptyText = defaultsStr(f.non_empty_text, '(Non empty)');
|
||||
|
||||
/**
|
||||
* Tooltip text appearing on multiple select
|
||||
* @type {String}
|
||||
*/
|
||||
this.multipleSlcTooltip = f.multiple_slc_tooltip ||
|
||||
'Use Ctrl/Cmd key for multiple selections';
|
||||
|
||||
/**
|
||||
* Indicates drop-down has custom options
|
||||
* @private
|
||||
*/
|
||||
this.isCustom = null;
|
||||
|
||||
/**
|
||||
* List of options values
|
||||
* @type {Array}
|
||||
* @private
|
||||
*/
|
||||
this.opts = null;
|
||||
|
||||
/**
|
||||
* List of options texts for custom values
|
||||
* @type {Array}
|
||||
* @private
|
||||
*/
|
||||
this.optsTxt = null;
|
||||
this.multipleSlcTooltip = defaultsStr(f.multiple_slc_tooltip,
|
||||
'Use Ctrl/Cmd key for multiple selections');
|
||||
}
|
||||
|
||||
|
||||
|
@ -92,6 +72,16 @@ export class Dropdown extends Feature {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh all drop-down filters
|
||||
*/
|
||||
refreshAll() {
|
||||
let selectFlts = this.tf.getFiltersByType(SELECT, true);
|
||||
let multipleFlts = this.tf.getFiltersByType(MULTIPLE, true);
|
||||
let colIdxs = selectFlts.concat(multipleFlts);
|
||||
this.refreshFilters(colIdxs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize drop-down filter
|
||||
* @param {Number} colIndex Column index
|
||||
|
@ -102,10 +92,10 @@ export class Dropdown extends Feature {
|
|||
let tf = this.tf;
|
||||
let col = tf.getFilterType(colIndex);
|
||||
let externalFltTgtId = isExternal ?
|
||||
tf.externalFltTgtIds[colIndex] : null;
|
||||
tf.externalFltIds[colIndex] : null;
|
||||
|
||||
let slc = createElm(SELECT,
|
||||
['id', tf.prfxFlt + colIndex + '_' + tf.id],
|
||||
['id', tf.buildFilterId(colIndex)],
|
||||
['ct', colIndex], ['filled', '0']
|
||||
);
|
||||
|
||||
|
@ -129,7 +119,7 @@ export class Dropdown extends Feature {
|
|||
this.build(colIndex);
|
||||
} else {
|
||||
//1st option is created here since build isn't invoked
|
||||
let opt0 = createOpt(tf.displayAllText, '');
|
||||
let opt0 = createOpt(tf.getClearFilterText(colIndex), '');
|
||||
slc.appendChild(opt0);
|
||||
}
|
||||
|
||||
|
@ -145,35 +135,43 @@ export class Dropdown extends Feature {
|
|||
['select-options'],
|
||||
(tf, colIndex, values) => this.selectOptions(colIndex, values)
|
||||
);
|
||||
this.emitter.on(['rows-changed'], () => this.refreshAll());
|
||||
|
||||
/**
|
||||
* @inherited
|
||||
*/
|
||||
this.emitter.on(['after-filtering'], () => this.linkFilters());
|
||||
|
||||
/** @inherited */
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build drop-down filter UI
|
||||
* @param {Number} colIndex Column index
|
||||
* @param {Boolean} isLinked Enable linked refresh behaviour
|
||||
* @param {Boolean} isLinked Enable linked filters behaviour
|
||||
*/
|
||||
build(colIndex, isLinked = false) {
|
||||
let tf = this.tf;
|
||||
colIndex = parseInt(colIndex, 10);
|
||||
colIndex = Number(colIndex);
|
||||
|
||||
this.emitter.emit('before-populating-filter', tf, colIndex);
|
||||
|
||||
/** @inherited */
|
||||
this.opts = [];
|
||||
/** @inherited */
|
||||
this.optsTxt = [];
|
||||
|
||||
let slcId = tf.fltIds[colIndex];
|
||||
let slc = elm(slcId);
|
||||
let rows = tf.tbl.rows;
|
||||
let nbRows = tf.getRowsNb(true);
|
||||
let slc = tf.getFilterElement(colIndex);
|
||||
|
||||
//custom select test
|
||||
/** @inherited */
|
||||
this.isCustom = tf.isCustomOptions(colIndex);
|
||||
|
||||
//Retrieves custom values
|
||||
if (this.isCustom) {
|
||||
let customValues = tf.getCustomOptions(colIndex);
|
||||
this.opts = customValues[0];
|
||||
this.optsTxt = customValues[1];
|
||||
}
|
||||
|
||||
//custom selects text
|
||||
let activeIdx;
|
||||
let activeFilterId = tf.getActiveFilterId();
|
||||
|
@ -188,108 +186,51 @@ export class Dropdown extends Feature {
|
|||
filteredDataCol = [];
|
||||
}
|
||||
|
||||
for (let k = tf.refRow; k < nbRows; k++) {
|
||||
// always visible rows don't need to appear on selects as always
|
||||
// valid
|
||||
if (tf.hasVisibleRows && tf.visibleRows.indexOf(k) !== -1) {
|
||||
continue;
|
||||
}
|
||||
let eachRow = tf.eachRow();
|
||||
eachRow(
|
||||
(row) => {
|
||||
let cellValue = tf.getCellValue(row.cells[colIndex]);
|
||||
//Vary Peter's patch
|
||||
let cellString = matchCase(cellValue, tf.caseSensitive);
|
||||
|
||||
let cell = rows[k].cells,
|
||||
nchilds = cell.length;
|
||||
// checks if celldata is already in array
|
||||
if (!has(this.opts, cellString, tf.caseSensitive)) {
|
||||
this.opts.push(cellValue);
|
||||
}
|
||||
|
||||
// checks if row has exact cell #
|
||||
if (nchilds !== tf.nbCells || this.isCustom) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// this loop retrieves cell data
|
||||
for (let j = 0; j < nchilds; j++) {
|
||||
// WTF: cyclomatic complexity hell
|
||||
// TODO: simplify hell below
|
||||
if ((colIndex === j &&
|
||||
(!isLinked ||
|
||||
(isLinked && tf.disableExcludedOptions))) ||
|
||||
(colIndex === j && isLinked &&
|
||||
((rows[k].style.display === '' && !tf.paging) ||
|
||||
(tf.paging && (!tf.validRowsIndex ||
|
||||
(tf.validRowsIndex &&
|
||||
tf.validRowsIndex.indexOf(k) !== -1)) &&
|
||||
((activeIdx === undefined ||
|
||||
activeIdx === colIndex) ||
|
||||
(activeIdx !== colIndex &&
|
||||
tf.validRowsIndex.indexOf(k) !== -1)))))) {
|
||||
let cellData = tf.getCellData(cell[j]),
|
||||
//Vary Peter's patch
|
||||
cellString = matchCase(cellData, tf.caseSensitive);
|
||||
|
||||
// checks if celldata is already in array
|
||||
if (!has(this.opts, cellString, tf.caseSensitive)) {
|
||||
this.opts.push(cellData);
|
||||
if (isLinked && tf.disableExcludedOptions) {
|
||||
let filteredCol = filteredDataCol[colIndex];
|
||||
if (!filteredCol) {
|
||||
filteredCol = tf.getVisibleColumnValues(colIndex);
|
||||
}
|
||||
|
||||
if (isLinked && tf.disableExcludedOptions) {
|
||||
let filteredCol = filteredDataCol[j];
|
||||
if (!filteredCol) {
|
||||
filteredCol = tf.getFilteredDataCol(j);
|
||||
}
|
||||
if (!has(filteredCol, cellString, tf.caseSensitive) &&
|
||||
!has(excludedOpts, cellString, tf.caseSensitive)) {
|
||||
excludedOpts.push(cellData);
|
||||
}
|
||||
if (!has(filteredCol, cellString, tf.caseSensitive) &&
|
||||
!has(excludedOpts, cellString, tf.caseSensitive)) {
|
||||
excludedOpts.push(cellValue);
|
||||
}
|
||||
}//if colIndex==j
|
||||
}//for j
|
||||
}//for k
|
||||
|
||||
//Retrieves custom values
|
||||
if (this.isCustom) {
|
||||
let customValues = tf.getCustomOptions(colIndex);
|
||||
this.opts = customValues[0];
|
||||
this.optsTxt = customValues[1];
|
||||
}
|
||||
|
||||
if (tf.sortSlc && !this.isCustom) {
|
||||
if (!tf.caseSensitive) {
|
||||
this.opts.sort(ignoreCase);
|
||||
if (excludedOpts) {
|
||||
excludedOpts.sort(ignoreCase);
|
||||
}
|
||||
} else {
|
||||
this.opts.sort();
|
||||
if (excludedOpts) { excludedOpts.sort(); }
|
||||
},
|
||||
// continue conditions function
|
||||
(row, k) => {
|
||||
// excluded rows don't need to appear on selects as always valid
|
||||
if (tf.excludeRows.indexOf(k) !== -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// checks if row has expected number of cells
|
||||
if (row.cells.length !== tf.nbCells || this.isCustom) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isLinked && !this.isValidLinkedValue(k, activeIdx)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
//asc sort
|
||||
if (tf.sortNumAsc.indexOf(colIndex) !== -1) {
|
||||
try {
|
||||
this.opts.sort(numSortAsc);
|
||||
if (excludedOpts) {
|
||||
excludedOpts.sort(numSortAsc);
|
||||
}
|
||||
if (this.isCustom) {
|
||||
this.optsTxt.sort(numSortAsc);
|
||||
}
|
||||
} catch (e) {
|
||||
throw new Error(SORT_ERROR.replace('{0}', colIndex)
|
||||
.replace('{1}', 'ascending'));
|
||||
}//in case there are alphanumeric values
|
||||
}
|
||||
//desc sort
|
||||
if (tf.sortNumDesc.indexOf(colIndex) !== -1) {
|
||||
try {
|
||||
this.opts.sort(numSortDesc);
|
||||
if (excludedOpts) {
|
||||
excludedOpts.sort(numSortDesc);
|
||||
}
|
||||
if (this.isCustom) {
|
||||
this.optsTxt.sort(numSortDesc);
|
||||
}
|
||||
} catch (e) {
|
||||
throw new Error(SORT_ERROR.replace('{0}', colIndex)
|
||||
.replace('{1}', 'ascending'));
|
||||
}//in case there are alphanumeric values
|
||||
//sort options
|
||||
this.opts = this.sortOptions(colIndex, this.opts);
|
||||
if (excludedOpts) {
|
||||
excludedOpts = this.sortOptions(colIndex, excludedOpts);
|
||||
}
|
||||
|
||||
//populates drop-down
|
||||
|
@ -348,9 +289,9 @@ export class Dropdown extends Feature {
|
|||
*/
|
||||
addFirstOption(slc) {
|
||||
let tf = this.tf;
|
||||
|
||||
let opt0 = createOpt(
|
||||
(!this.enableSlcResetFilter ? '' : tf.displayAllText), '');
|
||||
let colIdx = tf.getColumnIndexFromFilterId(slc.id);
|
||||
let opt0 = createOpt((!this.enableSlcResetFilter ?
|
||||
'' : tf.getClearFilterText(colIdx)), '');
|
||||
if (!this.enableSlcResetFilter) {
|
||||
opt0.style.display = NONE;
|
||||
}
|
||||
|
@ -373,7 +314,7 @@ export class Dropdown extends Feature {
|
|||
*/
|
||||
selectOptions(colIndex, values = []) {
|
||||
let tf = this.tf;
|
||||
if (tf.getFilterType(colIndex) !== MULTIPLE || values.length === 0) {
|
||||
if (values.length === 0) {
|
||||
return;
|
||||
}
|
||||
let slc = tf.getFilterElement(colIndex);
|
||||
|
@ -428,5 +369,8 @@ export class Dropdown extends Feature {
|
|||
['select-options'],
|
||||
(tf, colIndex, values) => this.selectOptions(colIndex, values)
|
||||
);
|
||||
this.emitter.off(['rows-changed'], () => this.refreshAll());
|
||||
this.emitter.off(['after-filtering'], () => this.linkFilters());
|
||||
this.initialized = false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,9 @@ import {createElm, removeElm, elm, tag} from '../dom';
|
|||
import {addEvt, targetEvt} from '../event';
|
||||
import {contains} from '../string';
|
||||
import {NONE} from '../const';
|
||||
import {
|
||||
defaultsBool, defaultsStr, defaultsNb, defaultsArr
|
||||
} from '../settings';
|
||||
|
||||
/**
|
||||
* Grid layout, table with fixed headers
|
||||
|
@ -14,77 +17,76 @@ export class GridLayout extends Feature {
|
|||
* @param {TableFilter} tf TableFilter instance
|
||||
*/
|
||||
constructor(tf) {
|
||||
super(tf, 'gridLayout');
|
||||
super(tf, GridLayout);
|
||||
|
||||
let f = this.config;
|
||||
let f = this.config.grid_layout || {};
|
||||
|
||||
/**
|
||||
* Grid-layout container width as CSS string
|
||||
* @type {String}
|
||||
*/
|
||||
this.width = f.grid_width || null;
|
||||
this.width = defaultsStr(f.width, null);
|
||||
|
||||
/**
|
||||
* Grid-layout container height as CSS string
|
||||
* @type {String}
|
||||
*/
|
||||
this.height = f.grid_height || null;
|
||||
this.height = defaultsStr(f.height, null);
|
||||
|
||||
/**
|
||||
* Css class for main container element
|
||||
* @type {String}
|
||||
*/
|
||||
this.mainContCssClass = f.grid_cont_css_class || 'grd_Cont';
|
||||
this.mainContCssClass = defaultsStr(f.cont_css_class, 'grd_Cont');
|
||||
|
||||
/**
|
||||
* Css class for body table container element
|
||||
* @type {String}
|
||||
*/
|
||||
this.contCssClass = f.grid_tbl_cont_css_class || 'grd_tblCont';
|
||||
this.contCssClass = defaultsStr(f.tbl_cont_css_class, 'grd_tblCont');
|
||||
|
||||
/**
|
||||
* Css class for headers table container element
|
||||
* @type {String}
|
||||
*/
|
||||
this.headContCssClass = f.grid_tblHead_cont_css_class ||
|
||||
'grd_headTblCont';
|
||||
this.headContCssClass = defaultsStr(f.tbl_head_css_class,
|
||||
'grd_headTblCont');
|
||||
|
||||
/**
|
||||
* Css class for toolbar container element (rows counter, paging etc.)
|
||||
* @type {String}
|
||||
*/
|
||||
this.infDivCssClass = f.grid_inf_grid_css_class || 'grd_inf';
|
||||
this.infDivCssClass = defaultsStr(f.inf_grid_css_class, 'grd_inf');
|
||||
|
||||
/**
|
||||
* Index of the headers row, default: 0
|
||||
* @type {Number}
|
||||
*/
|
||||
this.headRowIndex = f.grid_headers_row_index || 0;
|
||||
this.headRowIndex = defaultsNb(f.headers_row_index, 0);
|
||||
|
||||
/**
|
||||
* Collection of the header row indexes to be moved into headers table
|
||||
* @type {Array}
|
||||
*/
|
||||
this.headRows = f.grid_headers_rows || [0];
|
||||
this.headRows = defaultsArr(f.headers_rows, [0]);
|
||||
|
||||
/**
|
||||
* Enable or disable column filters generation, default: true
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this.enableFilters = f.grid_enable_default_filters === false ?
|
||||
false : true;
|
||||
this.filters = defaultsBool(f.filters, true);
|
||||
|
||||
/**
|
||||
* Enable or disable column headers, default: false
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this.noHeaders = Boolean(f.grid_no_headers);
|
||||
this.noHeaders = Boolean(f.no_headers);
|
||||
|
||||
/**
|
||||
* Grid-layout default column widht as CSS string
|
||||
* @type {String}
|
||||
*/
|
||||
this.defaultColWidth = f.grid_default_col_width || '100px';
|
||||
this.defaultColWidth = defaultsStr(f.default_col_width, '100px');
|
||||
|
||||
/**
|
||||
* List of column elements
|
||||
|
@ -93,34 +95,6 @@ export class GridLayout extends Feature {
|
|||
*/
|
||||
this.colElms = [];
|
||||
|
||||
/**
|
||||
* Prefix for grid-layout main container ID
|
||||
* @type {String}
|
||||
* @private
|
||||
*/
|
||||
this.prfxMainTblCont = 'gridCont_';
|
||||
|
||||
/**
|
||||
* Prefix for grid-layout body table container ID
|
||||
* @type {String}
|
||||
* @private
|
||||
*/
|
||||
this.prfxTblCont = 'tblCont_';
|
||||
|
||||
/**
|
||||
* Prefix for grid-layout headers table container ID
|
||||
* @type {String}
|
||||
* @private
|
||||
*/
|
||||
this.prfxHeadTblCont = 'tblHeadCont_';
|
||||
|
||||
/**
|
||||
* Prefix for grid-layout headers table ID
|
||||
* @type {String}
|
||||
* @private
|
||||
*/
|
||||
this.prfxHeadTbl = 'tblHead_';
|
||||
|
||||
/**
|
||||
* Prefix for grid-layout filter's cell ID
|
||||
* @type {String}
|
||||
|
@ -140,14 +114,14 @@ export class GridLayout extends Feature {
|
|||
* @type {String}
|
||||
* @private
|
||||
*/
|
||||
this.sourceTblHtml = tf.tbl.outerHTML;
|
||||
this.sourceTblHtml = tf.dom().outerHTML;
|
||||
|
||||
/**
|
||||
* Indicates if working table has column elements
|
||||
* @type {Boolean}
|
||||
* @private
|
||||
*/
|
||||
this.tblHasColTag = tag(tf.tbl, 'col').length > 0 ? true : false;
|
||||
this.tblHasColTag = tag(tf.dom(), 'col').length > 0 ? true : false;
|
||||
|
||||
/**
|
||||
* Main container element
|
||||
|
@ -174,7 +148,7 @@ export class GridLayout extends Feature {
|
|||
this.headTbl = null;
|
||||
|
||||
// filters flag at TF level
|
||||
tf.fltGrid = this.enableFilters;
|
||||
tf.fltGrid = this.filters;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -183,7 +157,7 @@ export class GridLayout extends Feature {
|
|||
*/
|
||||
init() {
|
||||
let tf = this.tf;
|
||||
let tbl = tf.tbl;
|
||||
let tbl = tf.dom();
|
||||
|
||||
if (this.initialized) {
|
||||
return;
|
||||
|
@ -195,11 +169,8 @@ export class GridLayout extends Feature {
|
|||
// Assign default column widths
|
||||
this.setDefaultColWidths();
|
||||
|
||||
// Initial table width
|
||||
let tblW = this.initialTableWidth();
|
||||
|
||||
//Main container: it will contain all the elements
|
||||
this.tblMainCont = this.createContainer(this.prfxMainTblCont + tf.id,
|
||||
this.tblMainCont = this.createContainer(
|
||||
'div', this.mainContCssClass);
|
||||
if (this.width) {
|
||||
this.tblMainCont.style.width = this.width;
|
||||
|
@ -207,8 +178,7 @@ export class GridLayout extends Feature {
|
|||
tbl.parentNode.insertBefore(this.tblMainCont, tbl);
|
||||
|
||||
//Table container: div wrapping content table
|
||||
this.tblCont = this.createContainer(this.prfxTblCont + tf.id, 'div',
|
||||
this.contCssClass);
|
||||
this.tblCont = this.createContainer('div', this.contCssClass);
|
||||
this.setConfigWidth(this.tblCont);
|
||||
if (this.height) {
|
||||
this.tblCont.style.height = this.height;
|
||||
|
@ -219,6 +189,7 @@ export class GridLayout extends Feature {
|
|||
|
||||
//In case table width is expressed in %
|
||||
if (tbl.style.width === '') {
|
||||
let tblW = this.initialTableWidth();
|
||||
tbl.style.width = (contains('%', tblW) ?
|
||||
tbl.clientWidth : tblW) + 'px';
|
||||
}
|
||||
|
@ -227,12 +198,11 @@ export class GridLayout extends Feature {
|
|||
this.tblMainCont.appendChild(d);
|
||||
|
||||
//Headers table container: div wrapping headers table
|
||||
this.headTblCont = this.createContainer(this.prfxHeadTblCont + tf.id,
|
||||
this.headTblCont = this.createContainer(
|
||||
'div', this.headContCssClass);
|
||||
this.setConfigWidth(this.headTblCont);
|
||||
|
||||
//Headers table
|
||||
this.headTbl = createElm('table', ['id', this.prfxHeadTbl + tf.id]);
|
||||
this.headTbl = createElm('table');
|
||||
let tH = createElm('tHead');
|
||||
|
||||
//1st row should be headers row, ids are added if not set
|
||||
|
@ -262,22 +232,16 @@ export class GridLayout extends Feature {
|
|||
tbl.removeChild(thead[0]);
|
||||
}
|
||||
|
||||
//Headers table style
|
||||
// ensure table layout is always set even if already set in css
|
||||
// definitions, potentially with custom css class this could be lost
|
||||
this.headTbl.style.tableLayout = 'fixed';
|
||||
tbl.style.tableLayout = 'fixed';
|
||||
this.headTbl.cellPadding = tbl.cellPadding;
|
||||
this.headTbl.cellSpacing = tbl.cellSpacing;
|
||||
// this.headTbl.style.width = tbl.style.width;
|
||||
|
||||
//content table without headers needs col widths to be reset
|
||||
tf.setColWidths(this.headTbl);
|
||||
|
||||
//Headers container width
|
||||
// this.headTblCont.style.width = this.tblCont.clientWidth+'px';
|
||||
|
||||
tbl.style.width = '';
|
||||
//
|
||||
this.headTbl.style.width = tbl.clientWidth + 'px';
|
||||
this.headTbl.style.width = tbl.style.width;
|
||||
//
|
||||
|
||||
//scroll synchronisation
|
||||
|
@ -317,13 +281,7 @@ export class GridLayout extends Feature {
|
|||
filtersRow.style.display = NONE;
|
||||
}
|
||||
|
||||
if (tbl.clientWidth !== this.headTbl.clientWidth) {
|
||||
tbl.style.width = this.headTbl.clientWidth + 'px';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inherited
|
||||
*/
|
||||
/** @inherited */
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
|
@ -336,7 +294,6 @@ export class GridLayout extends Feature {
|
|||
tf.refRow = 0;
|
||||
tf.headersRow = 0;
|
||||
tf.filtersRowIndex = 1;
|
||||
tf.isExternalFlt = true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -345,12 +302,13 @@ export class GridLayout extends Feature {
|
|||
*/
|
||||
setDefaultColWidths() {
|
||||
let tf = this.tf;
|
||||
if (tf.hasColWidths) {
|
||||
if (tf.colWidths.length > 0) {
|
||||
return;
|
||||
}
|
||||
for (let k = 0, len = tf.getCellsNb(); k < len; k++) {
|
||||
|
||||
tf.eachCol((k) => {
|
||||
let colW;
|
||||
let cell = tf.tbl.rows[tf.getHeadersRowIndex()].cells[k];
|
||||
let cell = tf.dom().rows[tf.getHeadersRowIndex()].cells[k];
|
||||
if (cell.width !== '') {
|
||||
colW = cell.width;
|
||||
} else if (cell.style.width !== '') {
|
||||
|
@ -359,8 +317,8 @@ export class GridLayout extends Feature {
|
|||
colW = this.defaultColWidth;
|
||||
}
|
||||
tf.colWidths[k] = colW;
|
||||
}
|
||||
tf.hasColWidths = true;
|
||||
});
|
||||
|
||||
tf.setColWidths();
|
||||
}
|
||||
|
||||
|
@ -370,7 +328,7 @@ export class GridLayout extends Feature {
|
|||
* @private
|
||||
*/
|
||||
initialTableWidth() {
|
||||
let tbl = this.tf.tbl;
|
||||
let tbl = this.tf.dom();
|
||||
let width; //initial table width
|
||||
|
||||
if (tbl.width !== '') {
|
||||
|
@ -386,14 +344,13 @@ export class GridLayout extends Feature {
|
|||
|
||||
/**
|
||||
* Creates container element
|
||||
* @param {String} id Element ID
|
||||
* @param {String} tag Tag name
|
||||
* @param {String} className Css class to assign to element
|
||||
* @returns {DOMElement}
|
||||
* @private
|
||||
*/
|
||||
createContainer(id, tag, className) {
|
||||
let element = createElm(tag, ['id', id]);
|
||||
createContainer(tag, className) {
|
||||
let element = createElm(tag);
|
||||
element.className = className;
|
||||
return element;
|
||||
}
|
||||
|
@ -406,14 +363,14 @@ export class GridLayout extends Feature {
|
|||
createFiltersRow() {
|
||||
let tf = this.tf;
|
||||
let filtersRow = createElm('tr');
|
||||
if (this.enableFilters && tf.fltGrid) {
|
||||
tf.externalFltTgtIds = [];
|
||||
for (let j = 0; j < tf.getCellsNb(); j++) {
|
||||
let fltTdId = tf.prfxFlt + j + this.prfxGridFltTd + tf.id;
|
||||
if (this.filters && tf.fltGrid) {
|
||||
tf.externalFltIds = [];
|
||||
tf.eachCol((j) => {
|
||||
let fltTdId = `${tf.prfxFlt + j + this.prfxGridFltTd + tf.id}`;
|
||||
let cl = createElm(tf.fltCellTag, ['id', fltTdId]);
|
||||
filtersRow.appendChild(cl);
|
||||
tf.externalFltTgtIds[j] = fltTdId;
|
||||
}
|
||||
tf.externalFltIds[j] = fltTdId;
|
||||
});
|
||||
}
|
||||
return filtersRow;
|
||||
}
|
||||
|
@ -424,19 +381,17 @@ export class GridLayout extends Feature {
|
|||
*/
|
||||
setColumnElements() {
|
||||
let tf = this.tf;
|
||||
let cols = tag(tf.tbl, 'col');
|
||||
let cols = tag(tf.dom(), 'col');
|
||||
this.tblHasColTag = cols.length > 0;
|
||||
|
||||
for (let k = (tf.nbCells - 1); k >= 0; k--) {
|
||||
for (let k = (tf.getCellsNb() - 1); k >= 0; k--) {
|
||||
let col;
|
||||
let id = `${tf.id}_col_${k}`;
|
||||
|
||||
if (!this.tblHasColTag) {
|
||||
col = createElm('col', ['id', id]);
|
||||
tf.tbl.insertBefore(col, tf.tbl.firstChild);
|
||||
col = createElm('col');
|
||||
tf.dom().insertBefore(col, tf.dom().firstChild);
|
||||
} else {
|
||||
col = cols[k];
|
||||
col.setAttribute('id', id);
|
||||
}
|
||||
col.style.width = tf.colWidths[k];
|
||||
this.colElms[k] = col;
|
||||
|
@ -457,7 +412,7 @@ export class GridLayout extends Feature {
|
|||
} else {
|
||||
// Headers row are moved from content table to headers table
|
||||
for (let i = 0; i < this.headRows.length; i++) {
|
||||
let row = this.tf.tbl.rows[this.headRows[i]];
|
||||
let row = this.tf.dom().rows[this.headRows[i]];
|
||||
tableHead.appendChild(row);
|
||||
}
|
||||
}
|
||||
|
@ -488,15 +443,15 @@ export class GridLayout extends Feature {
|
|||
getSortTriggerIds(row) {
|
||||
let tf = this.tf;
|
||||
let sortTriggers = [];
|
||||
for (let n = 0; n < tf.getCellsNb(); n++) {
|
||||
tf.eachCol((n) => {
|
||||
let c = row.cells[n];
|
||||
let thId = c.getAttribute('id');
|
||||
if (!thId || thId === '') {
|
||||
thId = this.prfxGridTh + n + '_' + tf.id;
|
||||
thId = `${this.prfxGridTh + n}_${tf.id}`;
|
||||
c.setAttribute('id', thId);
|
||||
}
|
||||
sortTriggers.push(thId);
|
||||
}
|
||||
});
|
||||
return sortTriggers;
|
||||
}
|
||||
|
||||
|
@ -505,7 +460,7 @@ export class GridLayout extends Feature {
|
|||
*/
|
||||
destroy() {
|
||||
let tf = this.tf;
|
||||
let tbl = tf.tbl;
|
||||
let tbl = tf.dom();
|
||||
|
||||
if (!this.initialized) {
|
||||
return;
|
||||
|
|
|
@ -86,7 +86,7 @@ export class Hash {
|
|||
}
|
||||
|
||||
/**
|
||||
* Converts a URL hash into a state JSON object
|
||||
* Converts a URL hash into a JSON object
|
||||
*
|
||||
* @param {String} hash URL hash fragment
|
||||
* @returns {Object} JSON object
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
import {Feature} from '../feature';
|
||||
import {createElm, createText, elm, removeElm} from '../dom';
|
||||
import {addEvt} from '../event';
|
||||
import {addEvt, targetEvt, removeEvt} from '../event';
|
||||
import {NONE} from '../const';
|
||||
import {root} from '../root';
|
||||
import {isEmpty, isNull} from '../types';
|
||||
import {defaultsStr, defaultsNb} from '../settings';
|
||||
import {RIGHT} from './toolbar';
|
||||
|
||||
const WIKI_URL = 'https://github.com/koalyptus/TableFilter/wiki/' +
|
||||
'4.-Filter-operators';
|
||||
const WEBSITE_URL = 'http://koalyptus.github.io/TableFilter/';
|
||||
const WEBSITE_URL = 'https://www.tablefilter.com/';
|
||||
|
||||
/**
|
||||
* Help UI component
|
||||
|
@ -13,33 +17,31 @@ const WEBSITE_URL = 'http://koalyptus.github.io/TableFilter/';
|
|||
export class Help extends Feature {
|
||||
|
||||
/**
|
||||
* Creates an instance of Help.
|
||||
* Creates an instance of Help
|
||||
* @param {TableFilter} tf TableFilter instance
|
||||
*/
|
||||
constructor(tf) {
|
||||
super(tf, 'help');
|
||||
super(tf, Help);
|
||||
|
||||
var f = this.config;
|
||||
let f = this.config.help_instructions || {};
|
||||
|
||||
/**
|
||||
* ID of main custom container element
|
||||
* @type {String}
|
||||
*/
|
||||
this.tgtId = f.help_instructions_target_id || null;
|
||||
this.tgtId = defaultsStr(f.target_id, null);
|
||||
|
||||
/**
|
||||
* ID of custom container element for instructions
|
||||
* @type {String}
|
||||
*/
|
||||
this.contTgtId = f.help_instructions_container_target_id ||
|
||||
null;
|
||||
this.contTgtId = defaultsStr(f.container_target_id, null);
|
||||
|
||||
/**
|
||||
* Instructions text (accepts HTML)
|
||||
* @type {String}
|
||||
*/
|
||||
this.instrText = f.help_instructions_text ?
|
||||
f.help_instructions_text :
|
||||
this.instrText = !isEmpty(f.text) ? f.text :
|
||||
'Use the filters above each column to filter and limit table ' +
|
||||
'data. Advanced searches can be performed by using the following ' +
|
||||
'operators: <br /><b><</b>, <b><=</b>, <b>></b>, ' +
|
||||
|
@ -52,45 +54,59 @@ export class Help extends Feature {
|
|||
* Instructions HTML
|
||||
* @type {String}
|
||||
*/
|
||||
this.instrHtml = f.help_instructions_html || null;
|
||||
this.instrHtml = defaultsStr(f.html, null);
|
||||
|
||||
/**
|
||||
* Help button text ('?')
|
||||
* @type {String}
|
||||
*/
|
||||
this.btnText = f.help_instructions_btn_text || '?';
|
||||
this.btnText = defaultsStr(f.btn_text, '?');
|
||||
|
||||
/**
|
||||
* Custom help button HTML
|
||||
* @type {String}
|
||||
*/
|
||||
this.btnHtml = f.help_instructions_btn_html || null;
|
||||
this.btnHtml = defaultsStr(f.btn_html, null);
|
||||
|
||||
/**
|
||||
* Css class for help button
|
||||
* @type {String}
|
||||
*/
|
||||
this.btnCssClass = f.help_instructions_btn_css_class || 'helpBtn';
|
||||
this.btnCssClass = defaultsStr(f.btn_css_class, 'helpBtn');
|
||||
|
||||
/**
|
||||
* Css class for help container element
|
||||
* @type {String}
|
||||
*/
|
||||
this.contCssClass = f.help_instructions_container_css_class ||
|
||||
'helpCont';
|
||||
this.contCssClass = defaultsStr(f.container_css_class, 'helpCont');
|
||||
|
||||
/**
|
||||
* Stores button DOM element
|
||||
* Button DOM element
|
||||
* @type {DOMElement}
|
||||
*/
|
||||
this.btn = null;
|
||||
|
||||
/**
|
||||
* Stores help container DOM element
|
||||
* Help container DOM element
|
||||
* @type {DOMElement}
|
||||
*/
|
||||
this.cont = null;
|
||||
|
||||
/**
|
||||
* Adjust container left position when table's horizontal scroll is
|
||||
* on, typically when `responsive` option is enabled.
|
||||
* @type {Number}
|
||||
* @defaultValue 25
|
||||
*/
|
||||
this.contAdjustLeftPosition =
|
||||
defaultsNb(f.container_adjust_left_position, 25);
|
||||
|
||||
/**
|
||||
* Bound mouseup wrapper
|
||||
* @private
|
||||
*/
|
||||
this.boundMouseup = null;
|
||||
|
||||
/**
|
||||
* Default HTML appended to instructions text
|
||||
* @type {String}
|
||||
|
@ -103,22 +119,32 @@ export class Help extends Feature {
|
|||
'<a href="javascript:void(0);" class="close">Close</a></div></div>';
|
||||
|
||||
/**
|
||||
* Prefix for help main container ID
|
||||
* Default position in toolbar ('left'|'center'|'right')
|
||||
* @type {String}
|
||||
* @private
|
||||
*/
|
||||
this.prfxHelpSpan = 'helpSpan_';
|
||||
|
||||
/**
|
||||
* Prefix for help instructions container ID
|
||||
* @type {String}
|
||||
* @private
|
||||
*/
|
||||
this.prfxHelpDiv = 'helpDiv_';
|
||||
this.toolbarPosition = defaultsStr(f.toolbar_position, RIGHT);
|
||||
|
||||
this.emitter.on(['init-help'], () => this.init());
|
||||
}
|
||||
|
||||
/**
|
||||
* Mouse-up event handler handling popup auto-close behaviour
|
||||
* @private
|
||||
*/
|
||||
onMouseup(evt) {
|
||||
let targetElm = targetEvt(evt);
|
||||
|
||||
while (targetElm && targetElm !== this.cont && targetElm !== this.btn) {
|
||||
targetElm = targetElm.parentNode;
|
||||
}
|
||||
|
||||
if (targetElm !== this.cont && targetElm !== this.btn) {
|
||||
this.toggle();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise Help instance
|
||||
*/
|
||||
|
@ -127,73 +153,91 @@ export class Help extends Feature {
|
|||
return;
|
||||
}
|
||||
|
||||
var tf = this.tf;
|
||||
this.emitter.emit('initializing-feature', this, !isNull(this.tgtId));
|
||||
|
||||
var helpspan = createElm('span', ['id', this.prfxHelpSpan + tf.id]);
|
||||
var helpdiv = createElm('div', ['id', this.prfxHelpDiv + tf.id]);
|
||||
let tf = this.tf;
|
||||
|
||||
let btn = createElm('span');
|
||||
let cont = createElm('div');
|
||||
|
||||
this.boundMouseup = this.onMouseup.bind(this);
|
||||
|
||||
//help button is added to defined element
|
||||
if (!this.tgtId) {
|
||||
tf.setToolbar();
|
||||
}
|
||||
var targetEl = !this.tgtId ? tf.rDiv : elm(this.tgtId);
|
||||
targetEl.appendChild(helpspan);
|
||||
let targetEl = !this.tgtId ?
|
||||
tf.feature('toolbar').container(this.toolbarPosition) :
|
||||
elm(this.tgtId);
|
||||
targetEl.appendChild(btn);
|
||||
|
||||
var divContainer = !this.contTgtId ? helpspan : elm(this.contTgtId);
|
||||
let divContainer = !this.contTgtId ? btn : elm(this.contTgtId);
|
||||
|
||||
if (!this.btnHtml) {
|
||||
divContainer.appendChild(helpdiv);
|
||||
var helplink = createElm('a', ['href', 'javascript:void(0);']);
|
||||
divContainer.appendChild(cont);
|
||||
let helplink = createElm('a', ['href', 'javascript:void(0);']);
|
||||
helplink.className = this.btnCssClass;
|
||||
helplink.appendChild(createText(this.btnText));
|
||||
helpspan.appendChild(helplink);
|
||||
btn.appendChild(helplink);
|
||||
addEvt(helplink, 'click', () => this.toggle());
|
||||
} else {
|
||||
helpspan.innerHTML = this.btnHtml;
|
||||
var helpEl = helpspan.firstChild;
|
||||
btn.innerHTML = this.btnHtml;
|
||||
let helpEl = btn.firstChild;
|
||||
addEvt(helpEl, 'click', () => this.toggle());
|
||||
divContainer.appendChild(helpdiv);
|
||||
divContainer.appendChild(cont);
|
||||
}
|
||||
|
||||
if (!this.instrHtml) {
|
||||
helpdiv.innerHTML = this.instrText;
|
||||
helpdiv.className = this.contCssClass;
|
||||
addEvt(helpdiv, 'dblclick', () => this.toggle());
|
||||
cont.innerHTML = this.instrText;
|
||||
cont.className = this.contCssClass;
|
||||
} else {
|
||||
if (this.contTgtId) {
|
||||
divContainer.appendChild(helpdiv);
|
||||
divContainer.appendChild(cont);
|
||||
}
|
||||
helpdiv.innerHTML = this.instrHtml;
|
||||
cont.innerHTML = this.instrHtml;
|
||||
if (!this.contTgtId) {
|
||||
helpdiv.className = this.contCssClass;
|
||||
addEvt(helpdiv, 'dblclick', () => this.toggle());
|
||||
cont.className = this.contCssClass;
|
||||
}
|
||||
}
|
||||
helpdiv.innerHTML += this.defaultHtml;
|
||||
addEvt(helpdiv, 'click', () => this.toggle());
|
||||
cont.innerHTML += this.defaultHtml;
|
||||
addEvt(cont, 'click', () => this.toggle());
|
||||
|
||||
this.cont = helpdiv;
|
||||
this.btn = helpspan;
|
||||
/**
|
||||
* @inherited
|
||||
*/
|
||||
this.cont = cont;
|
||||
this.btn = btn;
|
||||
/** @inherited */
|
||||
this.initialized = true;
|
||||
|
||||
this.emitter.emit('feature-initialized', this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle help pop-up
|
||||
*/
|
||||
toggle() {
|
||||
// check only if explicitily set to false as in this case undefined
|
||||
// check only if explicitily disabled as in this case undefined
|
||||
// signifies the help feature is enabled by default
|
||||
if (this.enabled === false) {
|
||||
if (!this.isEnabled()) {
|
||||
return;
|
||||
}
|
||||
var divDisplay = this.cont.style.display;
|
||||
|
||||
// ensure mouseup event handler is removed
|
||||
removeEvt(root, 'mouseup', this.boundMouseup);
|
||||
|
||||
let divDisplay = this.cont.style.display;
|
||||
if (divDisplay === '' || divDisplay === NONE) {
|
||||
this.cont.style.display = 'inline';
|
||||
|
||||
// if table element has an horizontal scrollbar adjust container
|
||||
// left position accordingly
|
||||
if (this.tf.dom().scrollLeft > 0) {
|
||||
this.cont.style.left = `${
|
||||
this.btn.offsetLeft
|
||||
- this.tf.dom().scrollLeft
|
||||
+ this.contAdjustLeftPosition
|
||||
}px`;
|
||||
}
|
||||
|
||||
addEvt(root, 'mouseup', this.boundMouseup);
|
||||
} else {
|
||||
this.cont.style.display = NONE;
|
||||
this.cont.style.left = '';
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -206,12 +250,15 @@ export class Help extends Feature {
|
|||
}
|
||||
removeElm(this.btn);
|
||||
this.btn = null;
|
||||
if (!this.cont) {
|
||||
return;
|
||||
}
|
||||
|
||||
removeElm(this.cont);
|
||||
this.cont = null;
|
||||
|
||||
this.boundMouseup = null;
|
||||
this.initialized = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TODO: remove as soon as feature name is fixed
|
||||
Help.meta = {alwaysInstantiate: true};
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import {createText, createElm, getText} from '../dom';
|
||||
import {isArray} from '../types';
|
||||
import {isNull} from '../types';
|
||||
import {rgxEsc} from '../string';
|
||||
import {defaultsStr} from '../settings';
|
||||
|
||||
/**
|
||||
* Highlight matched keywords upon filtering
|
||||
|
@ -20,7 +22,7 @@ export class HighlightKeyword {
|
|||
* Css class for highlighted term
|
||||
* @type {String}
|
||||
*/
|
||||
this.highlightCssClass = f.highlight_css_class || 'keyword';
|
||||
this.highlightCssClass = defaultsStr(f.highlight_css_class, 'keyword');
|
||||
|
||||
/**
|
||||
* TableFilter instance
|
||||
|
@ -45,47 +47,46 @@ export class HighlightKeyword {
|
|||
);
|
||||
this.emitter.on(
|
||||
['highlight-keyword'],
|
||||
(tf, cell, word) =>
|
||||
this.highlight(cell, word, this.highlightCssClass)
|
||||
(tf, cell, term) => this._processTerm(cell, term)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Highlight occurences of searched term in passed node
|
||||
* @param {Node} node
|
||||
* @param {String} word Searched term
|
||||
* @param {String} term Searched term
|
||||
* @param {String} cssClass Css class name
|
||||
*
|
||||
* TODO: refactor this method
|
||||
*/
|
||||
highlight(node, word, cssClass) {
|
||||
highlight(node, term, cssClass) {
|
||||
// Iterate into this nodes childNodes
|
||||
if (node.hasChildNodes) {
|
||||
let children = node.childNodes;
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
this.highlight(children[i], word, cssClass);
|
||||
this.highlight(children[i], term, cssClass);
|
||||
}
|
||||
}
|
||||
|
||||
if (node.nodeType === 3) {
|
||||
let tempNodeVal = node.nodeValue.toLowerCase();
|
||||
let tempWordVal = word.toLowerCase();
|
||||
if (tempNodeVal.indexOf(tempWordVal) !== -1) {
|
||||
let nodeVal = node.nodeValue.toLowerCase();
|
||||
let termIdx = nodeVal.indexOf(term.toLowerCase());
|
||||
|
||||
if (termIdx !== -1) {
|
||||
let pn = node.parentNode;
|
||||
if (pn && pn.className !== cssClass) {
|
||||
// word not highlighted yet
|
||||
// term not highlighted yet
|
||||
let nv = node.nodeValue,
|
||||
ni = tempNodeVal.indexOf(tempWordVal),
|
||||
// Create a load of replacement nodes
|
||||
before = createText(nv.substr(0, ni)),
|
||||
docWordVal = nv.substr(ni, word.length),
|
||||
after = createText(nv.substr(ni + word.length)),
|
||||
hiwordtext = createText(docWordVal),
|
||||
hiword = createElm('span');
|
||||
hiword.className = cssClass;
|
||||
hiword.appendChild(hiwordtext);
|
||||
before = createText(nv.substr(0, termIdx)),
|
||||
value = nv.substr(termIdx, term.length),
|
||||
after = createText(nv.substr(termIdx + term.length)),
|
||||
text = createText(value),
|
||||
container = createElm('span');
|
||||
container.className = cssClass;
|
||||
container.appendChild(text);
|
||||
pn.insertBefore(before, node);
|
||||
pn.insertBefore(hiword, node);
|
||||
pn.insertBefore(container, node);
|
||||
pn.insertBefore(after, node);
|
||||
pn.removeChild(node);
|
||||
}
|
||||
|
@ -95,19 +96,20 @@ export class HighlightKeyword {
|
|||
|
||||
/**
|
||||
* Removes highlight to nodes matching passed string
|
||||
* @param {String} word
|
||||
* @param {String} term
|
||||
* @param {String} cssClass Css class to remove
|
||||
*/
|
||||
unhighlight(word, cssClass) {
|
||||
let highlightedNodes = this.tf.tbl.querySelectorAll(`.${cssClass}`);
|
||||
unhighlight(term, cssClass) {
|
||||
let highlightedNodes = this.tf.dom().querySelectorAll(`.${cssClass}`);
|
||||
for (let i = 0; i < highlightedNodes.length; i++) {
|
||||
let n = highlightedNodes[i];
|
||||
let nodeVal = getText(n),
|
||||
tempNodeVal = nodeVal.toLowerCase(),
|
||||
tempWordVal = word.toLowerCase();
|
||||
let nodeVal = getText(n);
|
||||
|
||||
if (tempNodeVal.indexOf(tempWordVal) !== -1) {
|
||||
n.parentNode.replaceChild(createText(nodeVal), n);
|
||||
if (isNull(term) ||
|
||||
nodeVal.toLowerCase().indexOf(term.toLowerCase()) !== -1) {
|
||||
let parentNode = n.parentNode;
|
||||
parentNode.replaceChild(createText(nodeVal), n);
|
||||
parentNode.normalize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -119,20 +121,11 @@ export class HighlightKeyword {
|
|||
if (!this.tf.highlightKeywords) {
|
||||
return;
|
||||
}
|
||||
// iterate filters values to unhighlight all values
|
||||
this.tf.getFiltersValue().forEach((val) => {
|
||||
if (isArray(val)) {
|
||||
val.forEach((item) =>
|
||||
this.unhighlight(item, this.highlightCssClass));
|
||||
} else {
|
||||
this.unhighlight(val, this.highlightCssClass);
|
||||
}
|
||||
});
|
||||
|
||||
this.unhighlight(null, this.highlightCssClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove feature
|
||||
*/
|
||||
/** Remove feature */
|
||||
destroy() {
|
||||
this.emitter.off(
|
||||
['before-filtering', 'destroy'],
|
||||
|
@ -140,8 +133,48 @@ export class HighlightKeyword {
|
|||
);
|
||||
this.emitter.off(
|
||||
['highlight-keyword'],
|
||||
(tf, cell, word) =>
|
||||
this.highlight(cell, word, this.highlightCssClass)
|
||||
(tf, cell, term) => this._processTerm(cell, term)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure filtering operators are handled before highlighting any match
|
||||
* @param {any} Table cell to look searched term into
|
||||
* @param {any} Searched termIdx
|
||||
*/
|
||||
_processTerm(cell, term) {
|
||||
let tf = this.tf;
|
||||
let reLk = new RegExp(rgxEsc(tf.lkOperator));
|
||||
let reEq = new RegExp(tf.eqOperator);
|
||||
let reSt = new RegExp(tf.stOperator);
|
||||
let reEn = new RegExp(tf.enOperator);
|
||||
let reLe = new RegExp(tf.leOperator);
|
||||
let reGe = new RegExp(tf.geOperator);
|
||||
let reL = new RegExp(tf.lwOperator);
|
||||
let reG = new RegExp(tf.grOperator);
|
||||
let reD = new RegExp(tf.dfOperator);
|
||||
|
||||
term = term
|
||||
.replace(reLk, '')
|
||||
.replace(reEq, '')
|
||||
.replace(reSt, '')
|
||||
.replace(reEn, '');
|
||||
|
||||
if (reLe.test(term) || reGe.test(term) || reL.test(term) ||
|
||||
reG.test(term) || reD.test(term)) {
|
||||
term = getText(cell);
|
||||
}
|
||||
|
||||
if (term === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
this.highlight(cell, term, this.highlightCssClass);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: remove as soon as feature name is fixed
|
||||
HighlightKeyword.meta = {
|
||||
name: 'highlightKeyword',
|
||||
altName: 'highlightKeywords'
|
||||
};
|
||||
|
|
|
@ -1,8 +1,33 @@
|
|||
import {Feature} from '../feature';
|
||||
import {createElm, createText, elm, removeElm} from '../dom';
|
||||
import {isFn} from '../types';
|
||||
import {EMPTY_FN} from '../types';
|
||||
import {root} from '../root';
|
||||
import {NONE} from '../const';
|
||||
import {defaultsStr, defaultsFn} from '../settings';
|
||||
|
||||
const BEFORE_ACTION_EVENTS = [
|
||||
'before-filtering',
|
||||
'before-populating-filter',
|
||||
'before-page-change',
|
||||
'before-clearing-filters',
|
||||
'before-page-length-change',
|
||||
'before-reset-page',
|
||||
'before-reset-page-length',
|
||||
'before-loading-extensions',
|
||||
'before-loading-themes'
|
||||
];
|
||||
|
||||
const AFTER_ACTION_EVENTS = [
|
||||
'after-filtering',
|
||||
'after-populating-filter',
|
||||
'after-page-change',
|
||||
'after-clearing-filters',
|
||||
'after-page-length-change',
|
||||
'after-reset-page',
|
||||
'after-reset-page-length',
|
||||
'after-loading-extensions',
|
||||
'after-loading-themes'
|
||||
];
|
||||
|
||||
/**
|
||||
* Activity indicator
|
||||
|
@ -19,15 +44,15 @@ export class Loader extends Feature {
|
|||
* @param {TableFilter} tf TableFilter instance
|
||||
*/
|
||||
constructor(tf) {
|
||||
super(tf, 'loader');
|
||||
super(tf, Loader);
|
||||
|
||||
let f = this.config;
|
||||
let f = this.config.loader || {};
|
||||
|
||||
/**
|
||||
* ID of custom container element
|
||||
* @type {String}
|
||||
*/
|
||||
this.targetId = f.loader_target_id || null;
|
||||
this.targetId = defaultsStr(f.target_id, null);
|
||||
|
||||
/**
|
||||
* Loader container DOM element
|
||||
|
@ -39,19 +64,19 @@ export class Loader extends Feature {
|
|||
* Text displayed when indicator is visible
|
||||
* @type {String}
|
||||
*/
|
||||
this.text = f.loader_text || 'Loading...';
|
||||
this.text = defaultsStr(f.text, 'Loading...');
|
||||
|
||||
/**
|
||||
* Custom HTML injected in Loader's container element
|
||||
* @type {String}
|
||||
*/
|
||||
this.html = f.loader_html || null;
|
||||
this.html = defaultsStr(f.html, null);
|
||||
|
||||
/**
|
||||
* Css class for Loader's container element
|
||||
* @type {String}
|
||||
*/
|
||||
this.cssClass = f.loader_css_class || 'loader';
|
||||
this.cssClass = defaultsStr(f.css_class, 'loader');
|
||||
|
||||
/**
|
||||
* Close delay in milliseconds
|
||||
|
@ -63,20 +88,13 @@ export class Loader extends Feature {
|
|||
* Callback fired when loader is displayed
|
||||
* @type {Function}
|
||||
*/
|
||||
this.onShow = isFn(f.on_show_loader) ? f.on_show_loader : null;
|
||||
this.onShow = defaultsFn(f.on_show_loader, EMPTY_FN);
|
||||
|
||||
/**
|
||||
* Callback fired when loader is closed
|
||||
* @type {Function}
|
||||
*/
|
||||
this.onHide = isFn(f.on_hide_loader) ? f.on_hide_loader : null;
|
||||
|
||||
/**
|
||||
* Prefix for container ID
|
||||
* @type {String}
|
||||
* @private
|
||||
*/
|
||||
this.prfx = 'load_';
|
||||
this.onHide = defaultsFn(f.on_hide_loader, EMPTY_FN);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -90,13 +108,13 @@ export class Loader extends Feature {
|
|||
let tf = this.tf;
|
||||
let emitter = this.emitter;
|
||||
|
||||
let containerDiv = createElm('div', ['id', this.prfx + tf.id]);
|
||||
let containerDiv = createElm('div');
|
||||
containerDiv.className = this.cssClass;
|
||||
|
||||
let targetEl = !this.targetId ?
|
||||
tf.tbl.parentNode : elm(this.targetId);
|
||||
tf.dom().parentNode : elm(this.targetId);
|
||||
if (!this.targetId) {
|
||||
targetEl.insertBefore(containerDiv, tf.tbl);
|
||||
targetEl.insertBefore(containerDiv, tf.dom());
|
||||
} else {
|
||||
targetEl.appendChild(containerDiv);
|
||||
}
|
||||
|
@ -110,34 +128,10 @@ export class Loader extends Feature {
|
|||
this.show(NONE);
|
||||
|
||||
// Subscribe to events
|
||||
emitter.on([
|
||||
'before-filtering',
|
||||
'before-populating-filter',
|
||||
'before-page-change',
|
||||
'before-clearing-filters',
|
||||
'before-page-length-change',
|
||||
'before-reset-page',
|
||||
'before-reset-page-length',
|
||||
'before-loading-extensions',
|
||||
'before-loading-themes'],
|
||||
() => this.show('')
|
||||
);
|
||||
emitter.on([
|
||||
'after-filtering',
|
||||
'after-populating-filter',
|
||||
'after-page-change',
|
||||
'after-clearing-filters',
|
||||
'after-page-length-change',
|
||||
'after-reset-page',
|
||||
'after-reset-page-length',
|
||||
'after-loading-extensions',
|
||||
'after-loading-themes'],
|
||||
() => this.show(NONE)
|
||||
);
|
||||
emitter.on(BEFORE_ACTION_EVENTS, () => this.show(''));
|
||||
emitter.on(AFTER_ACTION_EVENTS, () => this.show(NONE));
|
||||
|
||||
/**
|
||||
* @inherited
|
||||
*/
|
||||
/** @inherited */
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
|
@ -150,21 +144,21 @@ export class Loader extends Feature {
|
|||
return;
|
||||
}
|
||||
|
||||
let displayLoader = () => {
|
||||
function displayLoader() {
|
||||
if (!this.cont) {
|
||||
return;
|
||||
}
|
||||
if (this.onShow && p !== NONE) {
|
||||
this.onShow.call(null, this);
|
||||
if (p !== NONE) {
|
||||
this.onShow(this);
|
||||
}
|
||||
this.cont.style.display = p;
|
||||
if (this.onHide && p === NONE) {
|
||||
this.onHide.call(null, this);
|
||||
if (p === NONE) {
|
||||
this.onHide(this);
|
||||
}
|
||||
};
|
||||
|
||||
let t = p === NONE ? this.closeDelay : 1;
|
||||
root.setTimeout(displayLoader, t);
|
||||
root.setTimeout(displayLoader.bind(this), t);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -181,30 +175,8 @@ export class Loader extends Feature {
|
|||
this.cont = null;
|
||||
|
||||
// Unsubscribe to events
|
||||
emitter.off([
|
||||
'before-filtering',
|
||||
'before-populating-filter',
|
||||
'before-page-change',
|
||||
'before-clearing-filters',
|
||||
'before-page-length-change',
|
||||
'before-reset-page',
|
||||
'before-reset-page-length',
|
||||
'before-loading-extensions',
|
||||
'before-loading-themes'],
|
||||
() => this.show('')
|
||||
);
|
||||
emitter.off([
|
||||
'after-filtering',
|
||||
'after-populating-filter',
|
||||
'after-page-change',
|
||||
'after-clearing-filters',
|
||||
'after-page-length-change',
|
||||
'after-reset-page',
|
||||
'after-reset-page-length',
|
||||
'after-loading-extensions',
|
||||
'after-loading-themes'],
|
||||
() => this.show(NONE)
|
||||
);
|
||||
emitter.off(BEFORE_ACTION_EVENTS, () => this.show(''));
|
||||
emitter.off(AFTER_ACTION_EVENTS, () => this.show(NONE));
|
||||
|
||||
this.initialized = false;
|
||||
}
|
||||
|
|
146
src/modules/markActiveColumns.js
Normal file
|
@ -0,0 +1,146 @@
|
|||
import {Feature} from '../feature';
|
||||
import {addClass, removeClass, hasClass} from '../dom';
|
||||
import {EMPTY_FN} from '../types';
|
||||
import {defaultsStr, defaultsFn} from '../settings';
|
||||
|
||||
/**
|
||||
* Visual indicator for filtered columns
|
||||
* @export
|
||||
* @class MarkActiveColumns
|
||||
* @extends {Feature}
|
||||
*/
|
||||
export class MarkActiveColumns extends Feature {
|
||||
|
||||
/**
|
||||
* Create an instance of MarkActiveColumns
|
||||
* @param {TableFilter} tf TableFilter instance
|
||||
*/
|
||||
constructor(tf) {
|
||||
super(tf, MarkActiveColumns);
|
||||
|
||||
let config = this.config.mark_active_columns || {};
|
||||
|
||||
/**
|
||||
* Css class for filtered (active) columns
|
||||
* @type {String}
|
||||
*/
|
||||
this.headerCssClass = defaultsStr(config.header_css_class,
|
||||
'activeHeader');
|
||||
|
||||
/**
|
||||
* Css class for filtered (active) column cells
|
||||
* @type {String}
|
||||
*/
|
||||
this.cellCssClass = defaultsStr(config.cell_css_class,
|
||||
'activeCell');
|
||||
|
||||
/**
|
||||
* Enable/disable column highlighting
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this.highlightColumn = Boolean(config.highlight_column);
|
||||
|
||||
/**
|
||||
* Callback fired before a column is marked as filtered
|
||||
* @type {Function}
|
||||
*/
|
||||
this.onBeforeActiveColumn = defaultsFn(config.on_before_active_column,
|
||||
EMPTY_FN);
|
||||
|
||||
/**
|
||||
* Callback fired after a column is marked as filtered
|
||||
* @type {Function}
|
||||
*/
|
||||
this.onAfterActiveColumn = defaultsFn(config.on_after_active_column,
|
||||
EMPTY_FN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise MarkActiveColumns instance
|
||||
*/
|
||||
init() {
|
||||
if (this.initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.emitter.on(['before-filtering'], () => this.clearActiveColumns());
|
||||
this.emitter.on(
|
||||
['cell-processed'],
|
||||
(tf, colIndex) => this.markActiveColumn(colIndex)
|
||||
);
|
||||
|
||||
/** @inherited */
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear filtered columns visual indicator (background color)
|
||||
*/
|
||||
clearActiveColumns() {
|
||||
let tf = this.tf;
|
||||
tf.eachCol((idx) => {
|
||||
removeClass(tf.getHeaderElement(idx), this.headerCssClass);
|
||||
|
||||
if (this.highlightColumn) {
|
||||
this.eachColumnCell(idx,
|
||||
(cell) => removeClass(cell, this.cellCssClass));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark currently filtered column
|
||||
* @param {Number} colIndex Column index
|
||||
*/
|
||||
markActiveColumn(colIndex) {
|
||||
let tf = this.tf;
|
||||
let header = tf.getHeaderElement(colIndex);
|
||||
if (hasClass(header, this.headerCssClass)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.onBeforeActiveColumn(this, colIndex);
|
||||
|
||||
addClass(header, this.headerCssClass);
|
||||
|
||||
if (this.highlightColumn) {
|
||||
this.eachColumnCell(colIndex,
|
||||
(cell) => addClass(cell, this.cellCssClass));
|
||||
}
|
||||
|
||||
this.onAfterActiveColumn(this, colIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Column cells iterator
|
||||
* TODO: make public and move into TableFilter if used elsewhere
|
||||
* @param {Number} colIndex
|
||||
* @param {Function} fn
|
||||
* @param {DOMElement} tbl
|
||||
* @private
|
||||
*/
|
||||
eachColumnCell(colIndex, fn = EMPTY_FN, tbl = this.tf.dom()) {
|
||||
// TODO: remove [].forEach when polyfill for PhanthomJs is available
|
||||
[].forEach.call(
|
||||
tbl.querySelectorAll(`tbody td:nth-child(${colIndex + 1})`), fn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove feature
|
||||
*/
|
||||
destroy() {
|
||||
if (!this.initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.clearActiveColumns();
|
||||
this.emitter.off(['before-filtering'], () => this.clearActiveColumns());
|
||||
this.emitter.off(
|
||||
['cell-processed'],
|
||||
(tf, colIndex) => this.markActiveColumn(colIndex)
|
||||
);
|
||||
|
||||
/** @inherited */
|
||||
this.initialized = false;
|
||||
}
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
import {Feature} from '../feature';
|
||||
import {createElm, elm, removeElm} from '../dom';
|
||||
import {isEmpty, isFn} from '../types';
|
||||
import {isEmpty, EMPTY_FN} from '../types';
|
||||
import {NONE} from '../const';
|
||||
import {defaultsStr, defaultsFn} from '../settings';
|
||||
|
||||
/**
|
||||
* UI when filtering yields no matches
|
||||
|
@ -16,28 +17,28 @@ export class NoResults extends Feature {
|
|||
* @param {TableFilter} tf TableFilter instance
|
||||
*/
|
||||
constructor(tf) {
|
||||
super(tf, 'noResults');
|
||||
super(tf, NoResults);
|
||||
|
||||
//configuration object
|
||||
let f = this.config.no_results_message;
|
||||
let f = this.config.no_results_message || {};
|
||||
|
||||
/**
|
||||
* Text (accepts HTML)
|
||||
* @type {String}
|
||||
*/
|
||||
this.content = f.content || 'No results';
|
||||
this.content = defaultsStr(f.content, 'No results');
|
||||
|
||||
/**
|
||||
* Custom container DOM element
|
||||
* @type {DOMElement}
|
||||
*/
|
||||
this.customContainer = f.custom_container || null;
|
||||
this.customContainer = defaultsStr(f.custom_container, null);
|
||||
|
||||
/**
|
||||
* ID of custom container element
|
||||
* @type {String}
|
||||
*/
|
||||
this.customContainerId = f.custom_container_id || null;
|
||||
this.customContainerId = defaultsStr(f.custom_container_id, null);
|
||||
|
||||
/**
|
||||
* Indicates if UI is contained in a external element
|
||||
|
@ -51,7 +52,7 @@ export class NoResults extends Feature {
|
|||
* Css class assigned to container element
|
||||
* @type {String}
|
||||
*/
|
||||
this.cssClass = f.css_class || 'no-results';
|
||||
this.cssClass = defaultsStr(f.css_class, 'no-results');
|
||||
|
||||
/**
|
||||
* Stores container DOM element
|
||||
|
@ -63,36 +64,25 @@ export class NoResults extends Feature {
|
|||
* Callback fired before the message is displayed
|
||||
* @type {Function}
|
||||
*/
|
||||
this.onBeforeShow = isFn(f.on_before_show_msg) ?
|
||||
f.on_before_show_msg : null;
|
||||
this.onBeforeShow = defaultsFn(f.on_before_show_msg, EMPTY_FN);
|
||||
|
||||
/**
|
||||
* Callback fired after the message is displayed
|
||||
* @type {Function}
|
||||
*/
|
||||
this.onAfterShow = isFn(f.on_after_show_msg) ?
|
||||
f.on_after_show_msg : null;
|
||||
this.onAfterShow = defaultsFn(f.on_after_show_msg, EMPTY_FN);
|
||||
|
||||
/**
|
||||
* Callback fired before the message is hidden
|
||||
* @type {Function}
|
||||
*/
|
||||
this.onBeforeHide = isFn(f.on_before_hide_msg) ?
|
||||
f.on_before_hide_msg : null;
|
||||
this.onBeforeHide = defaultsFn(f.on_before_hide_msg, EMPTY_FN);
|
||||
|
||||
/**
|
||||
* Callback fired after the message is hidden
|
||||
* @type {Function}
|
||||
*/
|
||||
this.onAfterHide = isFn(f.on_after_hide_msg) ?
|
||||
f.on_after_hide_msg : null;
|
||||
|
||||
/**
|
||||
* Prefix for container ID
|
||||
* @type {String}
|
||||
* @private
|
||||
*/
|
||||
this.prfx = 'nores_';
|
||||
this.onAfterHide = defaultsFn(f.on_after_hide_msg, EMPTY_FN);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -104,10 +94,10 @@ export class NoResults extends Feature {
|
|||
}
|
||||
let tf = this.tf;
|
||||
let target = this.customContainer || elm(this.customContainerId) ||
|
||||
tf.tbl;
|
||||
tf.dom();
|
||||
|
||||
//container
|
||||
let cont = createElm('div', ['id', this.prfx + tf.id]);
|
||||
let cont = createElm('div');
|
||||
cont.className = this.cssClass;
|
||||
cont.innerHTML = this.content;
|
||||
|
||||
|
@ -120,14 +110,13 @@ export class NoResults extends Feature {
|
|||
this.cont = cont;
|
||||
|
||||
// subscribe to after-filtering event
|
||||
this.emitter.on(['after-filtering'], () => this.toggle());
|
||||
this.emitter.on(
|
||||
['initialized', 'after-filtering'],
|
||||
() => this.toggle()
|
||||
);
|
||||
|
||||
/**
|
||||
* @inherited
|
||||
*/
|
||||
/** @inherited */
|
||||
this.initialized = true;
|
||||
|
||||
this.hide();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -148,17 +137,12 @@ export class NoResults extends Feature {
|
|||
if (!this.initialized || !this.isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.onBeforeShow) {
|
||||
this.onBeforeShow.call(null, this.tf, this);
|
||||
}
|
||||
this.onBeforeShow(this.tf, this);
|
||||
|
||||
this.setWidth();
|
||||
this.cont.style.display = 'block';
|
||||
|
||||
if (this.onAfterShow) {
|
||||
this.onAfterShow.call(null, this.tf, this);
|
||||
}
|
||||
this.onAfterShow(this.tf, this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -168,16 +152,11 @@ export class NoResults extends Feature {
|
|||
if (!this.initialized || !this.isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.onBeforeHide) {
|
||||
this.onBeforeHide.call(null, this.tf, this);
|
||||
}
|
||||
this.onBeforeHide(this.tf, this);
|
||||
|
||||
this.cont.style.display = NONE;
|
||||
|
||||
if (this.onAfterHide) {
|
||||
this.onAfterHide.call(null, this.tf, this);
|
||||
}
|
||||
this.onAfterHide(this.tf, this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -191,16 +170,15 @@ export class NoResults extends Feature {
|
|||
let tf = this.tf;
|
||||
if (tf.gridLayout) {
|
||||
let gridLayout = tf.feature('gridLayout');
|
||||
this.cont.style.width = gridLayout.tblCont.clientWidth + 'px';
|
||||
this.cont.style.width = gridLayout.headTbl.clientWidth + 'px';
|
||||
} else {
|
||||
this.cont.style.width = (tf.tbl.tHead ? tf.tbl.tHead.clientWidth :
|
||||
tf.tbl.tBodies[0].clientWidth) + 'px';
|
||||
this.cont.style.width = (tf.dom().tHead ?
|
||||
tf.dom().tHead.clientWidth :
|
||||
tf.dom().tBodies[0].clientWidth) + 'px';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove feature
|
||||
*/
|
||||
/** Remove feature */
|
||||
destroy() {
|
||||
if (!this.initialized) {
|
||||
return;
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import {Feature} from '../feature';
|
||||
import {isFn} from '../types';
|
||||
import {isUndef, EMPTY_FN} from '../types';
|
||||
import {createElm, removeElm} from '../dom';
|
||||
import {addEvt, cancelEvt, stopEvt, targetEvt} from '../event';
|
||||
import {INPUT, NONE} from '../const';
|
||||
import {addEvt, cancelEvt, stopEvt, targetEvt, removeEvt} from '../event';
|
||||
import {INPUT, NONE, CHECKLIST, MULTIPLE} from '../const';
|
||||
import {root} from '../root';
|
||||
import {defaultsStr, defaultsBool, defaultsArr, defaultsFn} from '../settings';
|
||||
|
||||
/**
|
||||
* Pop-up filter component
|
||||
|
@ -17,69 +19,81 @@ export class PopupFilter extends Feature {
|
|||
* @param {TableFilter} tf TableFilter instance
|
||||
*/
|
||||
constructor(tf) {
|
||||
super(tf, 'popupFilters');
|
||||
super(tf, PopupFilter);
|
||||
|
||||
// Configuration object
|
||||
let f = this.config;
|
||||
let f = this.config.popup_filters || {};
|
||||
|
||||
// Enable external filters
|
||||
tf.isExternalFlt = true;
|
||||
tf.externalFltTgtIds = [];
|
||||
/**
|
||||
* Close active popup filter upon filtering, enabled by default
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this.closeOnFiltering = defaultsBool(f.close_on_filtering, true);
|
||||
|
||||
/**
|
||||
* Filter icon path
|
||||
* @type {String}
|
||||
*/
|
||||
this.iconPath = f.popup_filters_image ||
|
||||
tf.themesPath + 'icn_filter.gif';
|
||||
this.iconPath = defaultsStr(f.image, tf.themesPath + 'icn_filter.gif');
|
||||
|
||||
/**
|
||||
* Active filter icon path
|
||||
* @type {string}
|
||||
*/
|
||||
this.activeIconPath = f.popup_filters_image_active ||
|
||||
tf.themesPath + 'icn_filterActive.gif';
|
||||
this.activeIconPath = defaultsStr(f.image_active,
|
||||
tf.themesPath + 'icn_filterActive.gif');
|
||||
|
||||
/**
|
||||
* HTML for the filter icon
|
||||
* @type {string}
|
||||
*/
|
||||
this.iconHtml = f.popup_filters_image_html ||
|
||||
'<img src="' + this.iconPath + '" alt="Column filter" />';
|
||||
this.iconHtml = defaultsStr(f.image_html,
|
||||
'<img src="' + this.iconPath + '" alt="Column filter" />');
|
||||
|
||||
/**
|
||||
* Css class assigned to the popup container element
|
||||
* @type {String}
|
||||
*/
|
||||
this.placeholderCssClass = defaultsStr(f.placeholder_css_class,
|
||||
'popUpPlaceholder');
|
||||
|
||||
/**
|
||||
* Css class assigned to filter container element
|
||||
* @type {String}
|
||||
*/
|
||||
this.containerCssClass = f.popup_div_css_class || 'popUpFilter';
|
||||
this.containerCssClass = defaultsStr(f.div_css_class, 'popUpFilter');
|
||||
|
||||
/**
|
||||
* Ensure filter's container element width matches column width, enabled
|
||||
* by default
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this.adjustToContainer = defaultsBool(f.adjust_to_container, true);
|
||||
|
||||
/**
|
||||
* Callback fired before a popup filter is opened
|
||||
* @type {Function}
|
||||
*/
|
||||
this.onBeforeOpen = isFn(f.on_before_popup_filter_open) ?
|
||||
f.on_before_popup_filter_open : null;
|
||||
this.onBeforeOpen = defaultsFn(f.on_before_popup_filter_open, EMPTY_FN);
|
||||
|
||||
/**
|
||||
* Callback fired after a popup filter is opened
|
||||
* @type {Function}
|
||||
*/
|
||||
this.onAfterOpen = isFn(f.on_after_popup_filter_open) ?
|
||||
f.on_after_popup_filter_open : null;
|
||||
this.onAfterOpen = defaultsFn(f.on_after_popup_filter_open, EMPTY_FN);
|
||||
|
||||
/**
|
||||
* Callback fired before a popup filter is closed
|
||||
* @type {Function}
|
||||
*/
|
||||
this.onBeforeClose = isFn(f.on_before_popup_filter_close) ?
|
||||
f.on_before_popup_filter_close : null;
|
||||
this.onBeforeClose = defaultsFn(f.on_before_popup_filter_close,
|
||||
EMPTY_FN);
|
||||
|
||||
/**
|
||||
* Callback fired after a popup filter is closed
|
||||
* @type {Function}
|
||||
*/
|
||||
this.onAfterClose = isFn(f.on_after_popup_filter_close) ?
|
||||
f.on_after_popup_filter_close : null;
|
||||
this.onAfterClose = defaultsFn(f.on_after_popup_filter_close, EMPTY_FN);
|
||||
|
||||
/**
|
||||
* Collection of filters spans
|
||||
|
@ -107,27 +121,21 @@ export class PopupFilter extends Feature {
|
|||
* @type {Array}
|
||||
* @private
|
||||
*/
|
||||
this.fltElms = this.filtersCache || [];
|
||||
|
||||
/**
|
||||
* Ensure filter's container element width matches column width
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this.adjustToContainer = true;
|
||||
|
||||
/**
|
||||
* Prefix for pop-up filter span ID
|
||||
* @type {String}
|
||||
* @private
|
||||
*/
|
||||
this.prfxSpan = 'popUpSpan_';
|
||||
this.fltElms = defaultsArr(this.filtersCache, []);
|
||||
|
||||
/**
|
||||
* Prefix for pop-up filter container ID
|
||||
* @type {String}
|
||||
* @private
|
||||
*/
|
||||
this.prfxDiv = 'popUpDiv_';
|
||||
this.prfxDiv = 'popup_';
|
||||
|
||||
/**
|
||||
* Column index of popup filter currently active
|
||||
* @type {Number}
|
||||
* @private
|
||||
*/
|
||||
this.activeFilterIdx = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -135,22 +143,49 @@ export class PopupFilter extends Feature {
|
|||
* @private
|
||||
*/
|
||||
onClick(evt) {
|
||||
let elm = targetEvt(evt).parentNode,
|
||||
colIndex = parseInt(elm.getAttribute('ci'), 10);
|
||||
let elm = targetEvt(evt).parentNode;
|
||||
let colIndex = parseInt(elm.getAttribute('ci'), 10);
|
||||
|
||||
this.closeAll(colIndex);
|
||||
this.toggle(colIndex);
|
||||
|
||||
if (this.adjustToContainer) {
|
||||
let popUpDiv = this.fltElms[colIndex],
|
||||
let cont = this.fltElms[colIndex],
|
||||
header = this.tf.getHeaderElement(colIndex),
|
||||
headerWidth = header.clientWidth * 0.95;
|
||||
popUpDiv.style.width = parseInt(headerWidth, 10) + 'px';
|
||||
cont.style.width = parseInt(headerWidth, 10) + 'px';
|
||||
}
|
||||
cancelEvt(evt);
|
||||
stopEvt(evt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mouse-up event handler handling popup filter auto-close behaviour
|
||||
* @private
|
||||
*/
|
||||
onMouseup(evt) {
|
||||
if (this.activeFilterIdx === -1) {
|
||||
return;
|
||||
}
|
||||
let targetElm = targetEvt(evt);
|
||||
let activeFlt = this.fltElms[this.activeFilterIdx];
|
||||
let icon = this.fltIcons[this.activeFilterIdx];
|
||||
|
||||
if (icon === targetElm) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (targetElm && targetElm !== activeFlt) {
|
||||
targetElm = targetElm.parentNode;
|
||||
}
|
||||
|
||||
if (targetElm !== activeFlt) {
|
||||
this.close(this.activeFilterIdx);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize DOM elements
|
||||
*/
|
||||
|
@ -161,40 +196,37 @@ export class PopupFilter extends Feature {
|
|||
|
||||
let tf = this.tf;
|
||||
|
||||
// Enable external filters
|
||||
tf.externalFltIds = [''];
|
||||
|
||||
// Override filters row index supplied by configuration
|
||||
tf.filtersRowIndex = 0;
|
||||
|
||||
// Override headers row index if no grouped headers
|
||||
if (tf.headersRow <= 1) {
|
||||
// TODO: Because of the filters row generation, headers row index needs
|
||||
// adjusting: prevent useless row generation
|
||||
if (tf.headersRow <= 1 && isNaN(tf.config().headers_row_index)) {
|
||||
tf.headersRow = 0;
|
||||
}
|
||||
|
||||
for (let i = 0; i < tf.nbCells; i++) {
|
||||
if (tf.getFilterType(i) === NONE) {
|
||||
continue;
|
||||
}
|
||||
let popUpSpan = createElm(
|
||||
'span',
|
||||
['id', this.prfxSpan + tf.id + '_' + i],
|
||||
['ci', i]
|
||||
);
|
||||
popUpSpan.innerHTML = this.iconHtml;
|
||||
let header = tf.getHeaderElement(i);
|
||||
header.appendChild(popUpSpan);
|
||||
addEvt(popUpSpan, 'click', (evt) => this.onClick(evt));
|
||||
this.fltSpans[i] = popUpSpan;
|
||||
this.fltIcons[i] = popUpSpan.firstChild;
|
||||
// Adjust headers row index for grid-layout mode
|
||||
// TODO: Because of the filters row generation, headers row index needs
|
||||
// adjusting: prevent useless row generation
|
||||
if (tf.gridLayout) {
|
||||
tf.headersRow--;
|
||||
this.buildIcons();
|
||||
}
|
||||
|
||||
// subscribe to events
|
||||
this.emitter.on(['before-filtering'], () => this.buildIcons());
|
||||
this.emitter.on(['before-filtering'], () => this.setIconsState());
|
||||
this.emitter.on(['after-filtering'], () => this.closeAll());
|
||||
this.emitter.on(['cell-processed'],
|
||||
(tf, cellIndex) => this.buildIcon(cellIndex, true));
|
||||
this.emitter.on(['filters-row-inserted'], () => this.tf.headersRow++);
|
||||
(tf, cellIndex) => this.changeState(cellIndex, true));
|
||||
this.emitter.on(['filters-row-inserted'], () => this.buildIcons());
|
||||
this.emitter.on(['before-filter-init'],
|
||||
(tf, colIndex) => this.build(colIndex));
|
||||
|
||||
/**
|
||||
* @inherited
|
||||
*/
|
||||
/** @inherited */
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
|
@ -204,9 +236,35 @@ export class PopupFilter extends Feature {
|
|||
reset() {
|
||||
this.enable();
|
||||
this.init();
|
||||
this.buildIcons();
|
||||
this.buildAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build all filters icons
|
||||
*/
|
||||
buildIcons() {
|
||||
let tf = this.tf;
|
||||
|
||||
// TODO: Because of the filters row generation, headers row index needs
|
||||
// adjusting: prevent useless row generation
|
||||
tf.headersRow++;
|
||||
|
||||
tf.eachCol(
|
||||
(i) => {
|
||||
let icon = createElm('span', ['ci', i]);
|
||||
icon.innerHTML = this.iconHtml;
|
||||
let header = tf.getHeaderElement(i);
|
||||
header.appendChild(icon);
|
||||
addEvt(icon, 'click', (evt) => this.onClick(evt));
|
||||
this.fltSpans[i] = icon;
|
||||
this.fltIcons[i] = icon.firstChild;
|
||||
},
|
||||
// continue condition function
|
||||
(i) => tf.getFilterType(i) === NONE
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build all pop-up filters elements
|
||||
*/
|
||||
|
@ -223,54 +281,80 @@ export class PopupFilter extends Feature {
|
|||
*/
|
||||
build(colIndex, div) {
|
||||
let tf = this.tf;
|
||||
let popUpDiv = !div ?
|
||||
createElm('div',
|
||||
['id', this.prfxDiv + tf.id + '_' + colIndex]) :
|
||||
div;
|
||||
popUpDiv.className = this.containerCssClass;
|
||||
tf.externalFltTgtIds.push(popUpDiv.id);
|
||||
let contId = `${this.prfxDiv}${tf.id}_${colIndex}`;
|
||||
let placeholder = createElm('div', ['class', this.placeholderCssClass]);
|
||||
let cont = div ||
|
||||
createElm('div', ['id', contId], ['class', this.containerCssClass]);
|
||||
tf.externalFltIds[colIndex] = cont.id;
|
||||
placeholder.appendChild(cont);
|
||||
|
||||
let header = tf.getHeaderElement(colIndex);
|
||||
header.insertBefore(popUpDiv, header.firstChild);
|
||||
addEvt(popUpDiv, 'click', (evt) => stopEvt(evt));
|
||||
this.fltElms[colIndex] = popUpDiv;
|
||||
header.insertBefore(placeholder, header.firstChild);
|
||||
addEvt(cont, 'click', (evt) => stopEvt(evt));
|
||||
this.fltElms[colIndex] = cont;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toogle visibility of specified filter
|
||||
* Toggle visibility of specified filter
|
||||
* @param {Number} colIndex Column index
|
||||
*/
|
||||
toggle(colIndex) {
|
||||
let tf = this.tf,
|
||||
popUpFltElm = this.fltElms[colIndex];
|
||||
|
||||
if (popUpFltElm.style.display === NONE ||
|
||||
popUpFltElm.style.display === '') {
|
||||
if (this.onBeforeOpen) {
|
||||
this.onBeforeOpen.call(
|
||||
null, this, this.fltElms[colIndex], colIndex);
|
||||
}
|
||||
popUpFltElm.style.display = 'block';
|
||||
if (tf.getFilterType(colIndex) === INPUT) {
|
||||
let flt = tf.getFilterElement(colIndex);
|
||||
if (flt) {
|
||||
flt.focus();
|
||||
}
|
||||
}
|
||||
if (this.onAfterOpen) {
|
||||
this.onAfterOpen.call(
|
||||
null, this, this.fltElms[colIndex], colIndex);
|
||||
}
|
||||
if (!this.isOpen(colIndex)) {
|
||||
this.open(colIndex);
|
||||
} else {
|
||||
if (this.onBeforeClose) {
|
||||
this.onBeforeClose.call(
|
||||
null, this, this.fltElms[colIndex], colIndex);
|
||||
}
|
||||
popUpFltElm.style.display = NONE;
|
||||
if (this.onAfterClose) {
|
||||
this.onAfterClose.call(
|
||||
null, this, this.fltElms[colIndex], colIndex);
|
||||
this.close(colIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Open popup filter of specified column
|
||||
* @param {Number} colIndex Column index
|
||||
*/
|
||||
open(colIndex) {
|
||||
let tf = this.tf,
|
||||
container = this.fltElms[colIndex];
|
||||
|
||||
this.onBeforeOpen(this, container, colIndex);
|
||||
|
||||
container.style.display = 'block';
|
||||
this.activeFilterIdx = colIndex;
|
||||
addEvt(root, 'mouseup', (evt) => this.onMouseup(evt));
|
||||
|
||||
if (tf.getFilterType(colIndex) === INPUT) {
|
||||
let flt = tf.getFilterElement(colIndex);
|
||||
if (flt) {
|
||||
flt.focus();
|
||||
}
|
||||
}
|
||||
|
||||
this.onAfterOpen(this, container, colIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close popup filter of specified column
|
||||
* @param {Number} colIndex Column index
|
||||
*/
|
||||
close(colIndex) {
|
||||
let container = this.fltElms[colIndex];
|
||||
|
||||
this.onBeforeClose(this, container, colIndex);
|
||||
|
||||
container.style.display = NONE;
|
||||
if (this.activeFilterIdx === colIndex) {
|
||||
this.activeFilterIdx = -1;
|
||||
}
|
||||
removeEvt(root, 'mouseup', (evt) => this.onMouseup(evt));
|
||||
|
||||
this.onAfterClose(this, container, colIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if popup filter for specified column is open
|
||||
* @param {Number} colIndex Column index
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
isOpen(colIndex) {
|
||||
return this.fltElms[colIndex].style.display === 'block';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -278,13 +362,23 @@ export class PopupFilter extends Feature {
|
|||
* @param {Number} exceptIdx Column index of the filter to not close
|
||||
*/
|
||||
closeAll(exceptIdx) {
|
||||
// Do not close filters only if argument is undefined and close on
|
||||
// filtering option is disabled
|
||||
if (isUndef(exceptIdx) && !this.closeOnFiltering) {
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < this.fltElms.length; i++) {
|
||||
if (i === exceptIdx) {
|
||||
continue;
|
||||
}
|
||||
let popUpFltElm = this.fltElms[i];
|
||||
if (popUpFltElm) {
|
||||
popUpFltElm.style.display = NONE;
|
||||
let fltType = this.tf.getFilterType(i);
|
||||
let isMultipleFilter =
|
||||
(fltType === CHECKLIST || fltType === MULTIPLE);
|
||||
|
||||
// Always hide all single selection filter types but hide multiple
|
||||
// selection filter types only if index set
|
||||
if (!isMultipleFilter || !isUndef(exceptIdx)) {
|
||||
this.close(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -292,9 +386,9 @@ export class PopupFilter extends Feature {
|
|||
/**
|
||||
* Build all the icons representing the pop-up filters
|
||||
*/
|
||||
buildIcons() {
|
||||
setIconsState() {
|
||||
for (let i = 0; i < this.fltIcons.length; i++) {
|
||||
this.buildIcon(i, false);
|
||||
this.changeState(i, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -303,10 +397,10 @@ export class PopupFilter extends Feature {
|
|||
* @param {Number} colIndex Column index
|
||||
* @param {Boolean} active Apply active state
|
||||
*/
|
||||
buildIcon(colIndex, active) {
|
||||
if (this.fltIcons[colIndex]) {
|
||||
this.fltIcons[colIndex].src = active ?
|
||||
this.activeIconPath : this.iconPath;
|
||||
changeState(colIndex, active) {
|
||||
let icon = this.fltIcons[colIndex];
|
||||
if (icon) {
|
||||
icon.src = active ? this.activeIconPath : this.iconPath;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -320,33 +414,41 @@ export class PopupFilter extends Feature {
|
|||
|
||||
this.filtersCache = [];
|
||||
for (let i = 0; i < this.fltElms.length; i++) {
|
||||
let popUpFltElm = this.fltElms[i],
|
||||
popUpFltSpan = this.fltSpans[i],
|
||||
popUpFltImg = this.fltIcons[i];
|
||||
if (popUpFltElm) {
|
||||
removeElm(popUpFltElm);
|
||||
this.filtersCache[i] = popUpFltElm;
|
||||
let container = this.fltElms[i],
|
||||
placeholder = container.parentNode,
|
||||
icon = this.fltSpans[i],
|
||||
iconImg = this.fltIcons[i];
|
||||
if (container) {
|
||||
removeElm(container);
|
||||
this.filtersCache[i] = container;
|
||||
}
|
||||
popUpFltElm = null;
|
||||
if (popUpFltSpan) {
|
||||
removeElm(popUpFltSpan);
|
||||
container = null;
|
||||
if (placeholder) {
|
||||
removeElm(placeholder);
|
||||
}
|
||||
popUpFltSpan = null;
|
||||
if (popUpFltImg) {
|
||||
removeElm(popUpFltImg);
|
||||
placeholder = null;
|
||||
if (icon) {
|
||||
removeElm(icon);
|
||||
}
|
||||
popUpFltImg = null;
|
||||
icon = null;
|
||||
if (iconImg) {
|
||||
removeElm(iconImg);
|
||||
}
|
||||
iconImg = null;
|
||||
}
|
||||
this.fltElms = [];
|
||||
this.fltSpans = [];
|
||||
this.fltIcons = [];
|
||||
|
||||
// TODO: expose an API to handle external filter IDs
|
||||
this.tf.externalFltIds = [];
|
||||
|
||||
// unsubscribe to events
|
||||
this.emitter.off(['before-filtering'], () => this.buildIcons());
|
||||
this.emitter.off(['before-filtering'], () => this.setIconsState());
|
||||
this.emitter.off(['after-filtering'], () => this.closeAll());
|
||||
this.emitter.off(['cell-processed'],
|
||||
(tf, cellIndex) => this.buildIcon(cellIndex, true));
|
||||
this.emitter.off(['filters-row-inserted'], () => this.tf.headersRow++);
|
||||
(tf, cellIndex) => this.changeState(cellIndex, true));
|
||||
this.emitter.off(['filters-row-inserted'], () => this.buildIcons());
|
||||
this.emitter.off(['before-filter-init'],
|
||||
(tf, colIndex) => this.build(colIndex));
|
||||
|
||||
|
@ -354,3 +456,6 @@ export class PopupFilter extends Feature {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
// TODO: remove as soon as feature name is fixed
|
||||
PopupFilter.meta = {altName: 'popupFilters'};
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import {Feature} from '../feature';
|
||||
import {createElm, createText, elm, removeElm} from '../dom';
|
||||
import {isFn} from '../types';
|
||||
import {EMPTY_FN, isNull} from '../types';
|
||||
import {defaultsStr, defaultsFn} from '../settings';
|
||||
import {LEFT} from './toolbar';
|
||||
|
||||
/**
|
||||
* Rows counter UI component
|
||||
|
@ -15,16 +17,16 @@ export class RowsCounter extends Feature {
|
|||
* @param {TableFilter} tf TableFilter instance
|
||||
*/
|
||||
constructor(tf) {
|
||||
super(tf, 'rowsCounter');
|
||||
super(tf, RowsCounter);
|
||||
|
||||
// TableFilter configuration
|
||||
let f = this.config;
|
||||
let f = this.config.rows_counter || {};
|
||||
|
||||
/**
|
||||
* ID of custom container element
|
||||
* @type {String}
|
||||
*/
|
||||
this.targetId = f.rows_counter_target_id || null;
|
||||
this.targetId = defaultsStr(f.target_id, null);
|
||||
|
||||
/**
|
||||
* Container DOM element
|
||||
|
@ -44,14 +46,14 @@ export class RowsCounter extends Feature {
|
|||
* Text preceding the total number of rows
|
||||
* @type {String}
|
||||
*/
|
||||
this.text = f.rows_counter_text || 'Rows: ';
|
||||
this.text = defaultsStr(f.text, 'Rows: ');
|
||||
|
||||
/**
|
||||
* Separator symbol appearing between the first and last visible rows of
|
||||
* current page when paging is enabled. ie: Rows: 31-40 / 70
|
||||
* @type {String}
|
||||
*/
|
||||
this.fromToTextSeparator = f.from_to_text_separator || '-';
|
||||
this.fromToTextSeparator = defaultsStr(f.separator, '-');
|
||||
|
||||
/**
|
||||
* Separator symbol appearing between the first and last visible rows of
|
||||
|
@ -59,48 +61,33 @@ export class RowsCounter extends Feature {
|
|||
* enabled. ie: Rows: 31-40 / 70
|
||||
* @type {String}
|
||||
*/
|
||||
this.overText = f.over_text || ' / ';
|
||||
this.overText = defaultsStr(f.over_text, ' / ');
|
||||
|
||||
/**
|
||||
* Css class for container element
|
||||
* @type {String}
|
||||
*/
|
||||
this.cssClass = f.tot_rows_css_class || 'tot';
|
||||
this.cssClass = defaultsStr(f.css_class, 'tot');
|
||||
|
||||
/**
|
||||
* Prefix for container ID
|
||||
* Default position in toolbar ('left'|'center'|'right')
|
||||
* @type {String}
|
||||
* @private
|
||||
*/
|
||||
this.prfxCounter = 'counter_';
|
||||
|
||||
/**
|
||||
* Prefix for DOM element containing the counter
|
||||
* @type {String}
|
||||
* @private
|
||||
*/
|
||||
this.prfxLabel = 'totrows_span_';
|
||||
|
||||
/**
|
||||
* Prefix for label preceding the counter
|
||||
* @type {String}
|
||||
* @private
|
||||
*/
|
||||
this.prfxText = 'totRowsTextSpan_';
|
||||
this.toolbarPosition = defaultsStr(f.toolbar_position, LEFT);
|
||||
|
||||
/**
|
||||
* Callback fired before the counter is refreshed
|
||||
* @type {Function}
|
||||
*/
|
||||
this.onBeforeRefreshCounter = isFn(f.on_before_refresh_counter) ?
|
||||
f.on_before_refresh_counter : null;
|
||||
this.onBeforeRefreshCounter = defaultsFn(f.on_before_refresh_counter,
|
||||
EMPTY_FN);
|
||||
|
||||
/**
|
||||
* Callback fired after the counter is refreshed
|
||||
* @type {Function}
|
||||
*/
|
||||
this.onAfterRefreshCounter = isFn(f.on_after_refresh_counter) ?
|
||||
f.on_after_refresh_counter : null;
|
||||
this.onAfterRefreshCounter = defaultsFn(f.on_after_refresh_counter,
|
||||
EMPTY_FN);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -111,29 +98,29 @@ export class RowsCounter extends Feature {
|
|||
return;
|
||||
}
|
||||
|
||||
this.emitter.emit('initializing-feature', this, !isNull(this.targetId));
|
||||
|
||||
let tf = this.tf;
|
||||
|
||||
//rows counter container
|
||||
let countDiv = createElm('div', ['id', this.prfxCounter + tf.id]);
|
||||
let countDiv = createElm('div');
|
||||
countDiv.className = this.cssClass;
|
||||
//rows counter label
|
||||
let countSpan = createElm('span', ['id', this.prfxLabel + tf.id]);
|
||||
let countText = createElm('span', ['id', this.prfxText + tf.id]);
|
||||
let countSpan = createElm('span');
|
||||
let countText = createElm('span');
|
||||
countText.appendChild(createText(this.text));
|
||||
|
||||
// counter is added to defined element
|
||||
if (!this.targetId) {
|
||||
tf.setToolbar();
|
||||
}
|
||||
let targetEl = !this.targetId ? tf.lDiv : elm(this.targetId);
|
||||
let targetEl = !this.targetId ?
|
||||
tf.feature('toolbar').container(this.toolbarPosition) :
|
||||
elm(this.targetId);
|
||||
|
||||
//default container: 'lDiv'
|
||||
if (!this.targetId) {
|
||||
countDiv.appendChild(countText);
|
||||
countDiv.appendChild(countSpan);
|
||||
targetEl.appendChild(countDiv);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
//custom container, no need to append statusDiv
|
||||
targetEl.appendChild(countText);
|
||||
targetEl.appendChild(countSpan);
|
||||
|
@ -146,17 +133,16 @@ export class RowsCounter extends Feature {
|
|||
() => this.refresh(tf.getValidRowsNb()));
|
||||
this.emitter.on(['rows-changed'], () => this.refresh());
|
||||
|
||||
/**
|
||||
* @inherited
|
||||
*/
|
||||
/** @inherited */
|
||||
this.initialized = true;
|
||||
this.refresh();
|
||||
|
||||
this.emitter.emit('feature-initialized', this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the rows counter
|
||||
* @param {Number} p Optional parameter the total number of rows to display
|
||||
* @returns
|
||||
*/
|
||||
refresh(p) {
|
||||
if (!this.initialized || !this.isEnabled()) {
|
||||
|
@ -165,9 +151,7 @@ export class RowsCounter extends Feature {
|
|||
|
||||
let tf = this.tf;
|
||||
|
||||
if (this.onBeforeRefreshCounter) {
|
||||
this.onBeforeRefreshCounter.call(null, tf, this.label);
|
||||
}
|
||||
this.onBeforeRefreshCounter(tf, this.label);
|
||||
|
||||
let totTxt;
|
||||
if (!tf.paging) {
|
||||
|
@ -179,23 +163,22 @@ export class RowsCounter extends Feature {
|
|||
} else {
|
||||
let paging = tf.feature('paging');
|
||||
if (paging) {
|
||||
let nbValidRows = tf.getValidRowsNb();
|
||||
//paging start row
|
||||
let pagingStartRow = parseInt(paging.startPagingRow, 10) +
|
||||
((tf.getValidRowsNb() > 0) ? 1 : 0);
|
||||
((nbValidRows > 0) ? 1 : 0);
|
||||
let pagingEndRow =
|
||||
(pagingStartRow + paging.pagingLength) - 1 <=
|
||||
tf.getValidRowsNb() ?
|
||||
pagingStartRow + paging.pagingLength - 1 :
|
||||
tf.getValidRowsNb();
|
||||
(pagingStartRow + paging.pageLength) - 1 <=
|
||||
nbValidRows ?
|
||||
pagingStartRow + paging.pageLength - 1 :
|
||||
nbValidRows;
|
||||
totTxt = pagingStartRow + this.fromToTextSeparator +
|
||||
pagingEndRow + this.overText + tf.getValidRowsNb();
|
||||
pagingEndRow + this.overText + nbValidRows;
|
||||
}
|
||||
}
|
||||
|
||||
this.label.innerHTML = totTxt;
|
||||
if (this.onAfterRefreshCounter) {
|
||||
this.onAfterRefreshCounter.call(null, tf, this.label, totTxt);
|
||||
}
|
||||
this.onAfterRefreshCounter(tf, this.label, totTxt);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -3,6 +3,7 @@ import {Hash} from './hash';
|
|||
import {Storage} from './storage';
|
||||
import {isEmpty} from '../string';
|
||||
import {isArray, isNull, isString, isUndef} from '../types';
|
||||
import {defaultsBool, defaultsNb} from '../settings';
|
||||
|
||||
/**
|
||||
* Features state object persistable with localStorage, cookie or URL hash
|
||||
|
@ -18,9 +19,9 @@ export class State extends Feature {
|
|||
* @param {TableFilter} tf TableFilter instance
|
||||
*/
|
||||
constructor(tf) {
|
||||
super(tf, 'state');
|
||||
super(tf, State);
|
||||
|
||||
let cfg = this.config.state;
|
||||
let cfg = this.config.state || {};
|
||||
|
||||
/**
|
||||
* Determines whether state is persisted with URL hash
|
||||
|
@ -47,7 +48,7 @@ export class State extends Feature {
|
|||
* Persist filters values, enabled by default
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this.persistFilters = cfg.filters === false ? false : true;
|
||||
this.persistFilters = defaultsBool(cfg.filters, true);
|
||||
|
||||
/**
|
||||
* Persist current page number when paging is enabled
|
||||
|
@ -83,8 +84,8 @@ export class State extends Feature {
|
|||
* Cookie duration in hours
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this.cookieDuration = !isNaN(cfg.cookie_duration) ?
|
||||
parseInt(cfg.cookie_duration, 10) : 87600;
|
||||
this.cookieDuration = defaultsNb(parseInt(cfg.cookie_duration, 10),
|
||||
87600);
|
||||
|
||||
/**
|
||||
* Enable Storage if localStorage or cookie is required
|
||||
|
@ -212,9 +213,7 @@ export class State extends Feature {
|
|||
this.storage.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inherited
|
||||
*/
|
||||
/** @inherited */
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
|
@ -366,6 +365,7 @@ export class State extends Feature {
|
|||
*/
|
||||
override(state) {
|
||||
this.state = state;
|
||||
this.emitter.emit('state-changed', this.tf, state);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -421,6 +421,10 @@ export class State extends Feature {
|
|||
let state = this.state;
|
||||
let tf = this.tf;
|
||||
|
||||
// clear all filters
|
||||
// TODO: use tf.clearFilters() once it allows to not filter the table
|
||||
tf.eachCol((colIdx) => tf.setFilterValue(colIdx, ''));
|
||||
|
||||
Object.keys(state).forEach((key) => {
|
||||
if (key.indexOf(this.prfxCol) !== -1) {
|
||||
let colIdx = parseInt(key.replace(this.prfxCol, ''), 10);
|
||||
|
|
|
@ -1,7 +1,21 @@
|
|||
import {Feature} from '../feature';
|
||||
import {root} from '../root';
|
||||
import {createElm, createText, elm, removeElm} from '../dom';
|
||||
import {isFn} from '../types';
|
||||
import {EMPTY_FN, isNull} from '../types';
|
||||
import {defaultsStr, defaultsFn} from '../settings';
|
||||
import {LEFT} from './toolbar';
|
||||
|
||||
const EVENTS = [
|
||||
'after-filtering',
|
||||
'after-populating-filter',
|
||||
'after-page-change',
|
||||
'after-clearing-filters',
|
||||
'after-page-length-change',
|
||||
'after-reset-page',
|
||||
'after-reset-page-length',
|
||||
'after-loading-extensions',
|
||||
'after-loading-themes'
|
||||
];
|
||||
|
||||
/**
|
||||
* Status bar UI component
|
||||
|
@ -16,16 +30,16 @@ export class StatusBar extends Feature {
|
|||
* @param {TableFilter} tf TableFilter instance
|
||||
*/
|
||||
constructor(tf) {
|
||||
super(tf, 'statusBar');
|
||||
super(tf, StatusBar);
|
||||
|
||||
// Configuration object
|
||||
let f = this.config;
|
||||
let f = this.config.status_bar || {};
|
||||
|
||||
/**
|
||||
* ID of custom container element
|
||||
* @type {String}
|
||||
*/
|
||||
this.targetId = f.status_bar_target_id || null;
|
||||
this.targetId = defaultsStr(f.target_id, null);
|
||||
|
||||
/**
|
||||
* Container DOM element
|
||||
|
@ -52,13 +66,13 @@ export class StatusBar extends Feature {
|
|||
* Text preceding status message
|
||||
* @type {String}
|
||||
*/
|
||||
this.text = f.status_bar_text || '';
|
||||
this.text = defaultsStr(f.text, '');
|
||||
|
||||
/**
|
||||
* Css class for container element
|
||||
* @type {String}
|
||||
*/
|
||||
this.cssClass = f.status_bar_css_class || 'status';
|
||||
this.cssClass = defaultsStr(f.css_class, 'status');
|
||||
|
||||
/**
|
||||
* Message visibility duration in milliseconds
|
||||
|
@ -71,106 +85,91 @@ export class StatusBar extends Feature {
|
|||
* Callback fired before the message is displayed
|
||||
* @type {Function}
|
||||
*/
|
||||
this.onBeforeShowMsg = isFn(f.on_before_show_msg) ?
|
||||
f.on_before_show_msg : null;
|
||||
this.onBeforeShowMsg = defaultsFn(f.on_before_show_msg, EMPTY_FN);
|
||||
|
||||
/**
|
||||
* Callback fired after the message is displayed
|
||||
* @type {Function}
|
||||
*/
|
||||
this.onAfterShowMsg = isFn(f.on_after_show_msg) ?
|
||||
f.on_after_show_msg : null;
|
||||
this.onAfterShowMsg = defaultsFn(f.on_after_show_msg, EMPTY_FN);
|
||||
|
||||
/**
|
||||
* Message appearing upon filtering
|
||||
* @type {String}
|
||||
*/
|
||||
this.msgFilter = f.msg_filter || 'Filtering data...';
|
||||
this.msgFilter = defaultsStr(f.msg_filter, 'Filtering data...');
|
||||
|
||||
/**
|
||||
* Message appearing when a drop-down filter is populated
|
||||
* @type {String}
|
||||
*/
|
||||
this.msgPopulate = f.msg_populate || 'Populating filter...';
|
||||
this.msgPopulate = defaultsStr(f.msg_populate, 'Populating filter...');
|
||||
|
||||
/**
|
||||
* Message appearing when a checklist filter is populated
|
||||
* @type {String}
|
||||
*/
|
||||
this.msgPopulateCheckList = f.msg_populate_checklist ||
|
||||
'Populating list...';
|
||||
this.msgPopulateCheckList = defaultsStr(f.msg_populate_checklist,
|
||||
'Populating list...');
|
||||
|
||||
/**
|
||||
* Message appearing when a pagination page is changed
|
||||
* @type {String}
|
||||
*/
|
||||
this.msgChangePage = f.msg_change_page || 'Collecting paging data...';
|
||||
this.msgChangePage = defaultsStr(f.msg_change_page,
|
||||
'Collecting paging data...');
|
||||
|
||||
/**
|
||||
* Message appearing when filters are cleared
|
||||
* @type {String}
|
||||
*/
|
||||
this.msgClear = f.msg_clear || 'Clearing filters...';
|
||||
this.msgClear = defaultsStr(f.msg_clear, 'Clearing filters...');
|
||||
|
||||
/**
|
||||
* Message appearing when the page length is changed
|
||||
* @type {String}
|
||||
*/
|
||||
this.msgChangeResults = f.msg_change_results ||
|
||||
'Changing results per page...';
|
||||
this.msgChangeResults = defaultsStr(f.msg_change_results,
|
||||
'Changing results per page...');
|
||||
|
||||
/**
|
||||
* Message appearing when the page is re-set
|
||||
* @type {String}
|
||||
*/
|
||||
this.msgResetPage = f.msg_reset_page || 'Re-setting page...';
|
||||
this.msgResetPage = defaultsStr(f.msg_reset_page, 'Re-setting page...');
|
||||
|
||||
/**
|
||||
* Message appearing when the page length is re-set
|
||||
* @type {String}
|
||||
*/
|
||||
this.msgResetPageLength = f.msg_reset_page_length ||
|
||||
'Re-setting page length...';
|
||||
this.msgResetPageLength = defaultsStr(f.msg_reset_page_length,
|
||||
'Re-setting page length...');
|
||||
|
||||
/**
|
||||
* Message appearing upon column sorting
|
||||
* @type {String}
|
||||
*/
|
||||
this.msgSort = f.msg_sort || 'Sorting data...';
|
||||
this.msgSort = defaultsStr(f.msg_sort, 'Sorting data...');
|
||||
|
||||
/**
|
||||
* Message appearing when extensions are loading
|
||||
* @type {String}
|
||||
*/
|
||||
this.msgLoadExtensions = f.msg_load_extensions ||
|
||||
'Loading extensions...';
|
||||
this.msgLoadExtensions = defaultsStr(f.msg_load_extensions,
|
||||
'Loading extensions...');
|
||||
|
||||
/**
|
||||
* Message appearing when themes are loading
|
||||
* @type {String}
|
||||
*/
|
||||
this.msgLoadThemes = f.msg_load_themes || 'Loading theme(s)...';
|
||||
this.msgLoadThemes = defaultsStr(f.msg_load_themes,
|
||||
'Loading theme(s)...');
|
||||
|
||||
/**
|
||||
* Prefix for container ID
|
||||
* Default position in toolbar ('left'|'center'|'right')
|
||||
* @type {String}
|
||||
* @private
|
||||
*/
|
||||
this.prfxCont = 'status_';
|
||||
|
||||
/**
|
||||
* Prefix for label container ID
|
||||
* @type {String}
|
||||
* @private
|
||||
*/
|
||||
this.prfxLabel = 'statusSpan_';
|
||||
|
||||
/**
|
||||
* Prefix for text preceding the message
|
||||
* @type {String}
|
||||
* @private
|
||||
*/
|
||||
this.prfxText = 'statusText_';
|
||||
this.toolbarPosition = defaultsStr(f.toolbar_position, LEFT);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -184,23 +183,24 @@ export class StatusBar extends Feature {
|
|||
let tf = this.tf;
|
||||
let emitter = this.emitter;
|
||||
|
||||
//status bar container
|
||||
let statusDiv = createElm('div', ['id', this.prfxCont + tf.id]);
|
||||
emitter.emit('initializing-feature', this, !isNull(this.targetId));
|
||||
|
||||
// status bar container
|
||||
let statusDiv = createElm('div');
|
||||
statusDiv.className = this.cssClass;
|
||||
|
||||
//status bar label
|
||||
let statusSpan = createElm('span', ['id', this.prfxLabel + tf.id]);
|
||||
//preceding text
|
||||
let statusSpanText = createElm('span', ['id', this.prfxText + tf.id]);
|
||||
// status bar label
|
||||
let statusSpan = createElm('span');
|
||||
// preceding text
|
||||
let statusSpanText = createElm('span');
|
||||
statusSpanText.appendChild(createText(this.text));
|
||||
|
||||
// target element container
|
||||
if (!this.targetId) {
|
||||
tf.setToolbar();
|
||||
}
|
||||
let targetEl = (!this.targetId) ? tf.lDiv : elm(this.targetId);
|
||||
let targetEl = (!this.targetId) ?
|
||||
tf.feature('toolbar').container(this.toolbarPosition) :
|
||||
elm(this.targetId);
|
||||
|
||||
//default container: 'lDiv'
|
||||
// default container
|
||||
if (!this.targetId) {
|
||||
statusDiv.appendChild(statusSpanText);
|
||||
statusDiv.appendChild(statusSpan);
|
||||
|
@ -215,7 +215,7 @@ export class StatusBar extends Feature {
|
|||
this.msgContainer = statusSpan;
|
||||
this.labelContainer = statusSpanText;
|
||||
|
||||
// Subscribe to events
|
||||
// subscribe to events
|
||||
emitter.on(['before-filtering'], () => this.message(this.msgFilter));
|
||||
emitter.on(['before-populating-filter'],
|
||||
() => this.message(this.msgPopulate));
|
||||
|
@ -234,23 +234,12 @@ export class StatusBar extends Feature {
|
|||
emitter.on(['before-loading-themes'],
|
||||
() => this.message(this.msgLoadThemes));
|
||||
|
||||
emitter.on([
|
||||
'after-filtering',
|
||||
'after-populating-filter',
|
||||
'after-page-change',
|
||||
'after-clearing-filters',
|
||||
'after-page-length-change',
|
||||
'after-reset-page',
|
||||
'after-reset-page-length',
|
||||
'after-loading-extensions',
|
||||
'after-loading-themes'],
|
||||
() => this.message('')
|
||||
);
|
||||
emitter.on(EVENTS, () => this.message(''));
|
||||
|
||||
/**
|
||||
* @inherited
|
||||
*/
|
||||
/** @inherited */
|
||||
this.initialized = true;
|
||||
|
||||
emitter.emit('feature-initialized', this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -262,9 +251,7 @@ export class StatusBar extends Feature {
|
|||
return;
|
||||
}
|
||||
|
||||
if (this.onBeforeShowMsg) {
|
||||
this.onBeforeShowMsg.call(null, this.tf, t);
|
||||
}
|
||||
this.onBeforeShowMsg(this.tf, t);
|
||||
|
||||
let d = t === '' ? this.delay : 1;
|
||||
root.setTimeout(() => {
|
||||
|
@ -272,9 +259,8 @@ export class StatusBar extends Feature {
|
|||
return;
|
||||
}
|
||||
this.msgContainer.innerHTML = t;
|
||||
if (this.onAfterShowMsg) {
|
||||
this.onAfterShowMsg.call(null, this.tf, t);
|
||||
}
|
||||
|
||||
this.onAfterShowMsg(this.tf, t);
|
||||
}, d);
|
||||
}
|
||||
|
||||
|
@ -315,18 +301,7 @@ export class StatusBar extends Feature {
|
|||
emitter.off(['before-loading-themes'],
|
||||
() => this.message(this.msgLoadThemes));
|
||||
|
||||
emitter.off([
|
||||
'after-filtering',
|
||||
'after-populating-filter',
|
||||
'after-page-change',
|
||||
'after-clearing-filters',
|
||||
'after-page-length-change',
|
||||
'after-reset-page',
|
||||
'after-reset-page-length',
|
||||
'after-loading-extensions',
|
||||
'after-loading-themes'],
|
||||
() => this.message('')
|
||||
);
|
||||
emitter.off(EVENTS, () => this.message(''));
|
||||
|
||||
this.initialized = false;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
import Cookie from '../cookie';
|
||||
import {root} from '../root';
|
||||
|
||||
|
|
224
src/modules/toolbar.js
Normal file
|
@ -0,0 +1,224 @@
|
|||
import {Feature} from '../feature';
|
||||
import {createElm, removeElm, elm, tag} from '../dom';
|
||||
import {defaultsStr} from '../settings';
|
||||
import {isUndef} from '../types';
|
||||
|
||||
const EVENTS = [
|
||||
'initializing-feature',
|
||||
'initializing-extension'
|
||||
];
|
||||
|
||||
/** Left position in toolbar */
|
||||
export const LEFT = 'left';
|
||||
/** Right position in toolbar */
|
||||
export const RIGHT = 'right';
|
||||
/** Center position in toolbar */
|
||||
export const CENTER = 'center';
|
||||
|
||||
/**
|
||||
* Toolbar UI component
|
||||
* @export
|
||||
* @class Toolbar
|
||||
* @extends {Feature}
|
||||
*/
|
||||
export class Toolbar extends Feature {
|
||||
|
||||
/**
|
||||
* Create an instance of Toolbar
|
||||
* @param {TableFilter} tf TableFilter instance
|
||||
* @memberof Toolbar
|
||||
*/
|
||||
constructor(tf) {
|
||||
super(tf, Toolbar);
|
||||
|
||||
// Configuration object
|
||||
let f = this.config.toolbar || {};
|
||||
|
||||
/**
|
||||
* Css class for toolbar's container DOM element
|
||||
* @type {String}
|
||||
*/
|
||||
this.contCssClass = defaultsStr(f.container_css_class, 'inf');
|
||||
|
||||
/**
|
||||
* Css class for left-side inner container DOM element
|
||||
* @type {String}
|
||||
*/
|
||||
this.lContCssClass = defaultsStr(f.left_cont_css_class, 'ldiv');
|
||||
|
||||
/**
|
||||
* Css class for right-side inner container DOM element
|
||||
* @type {String}
|
||||
*/
|
||||
this.rContCssClass = defaultsStr(f.right_cont_css_class, 'rdiv');
|
||||
|
||||
/**
|
||||
* Css class for middle inner container DOM element
|
||||
* @type {String}
|
||||
*/
|
||||
this.cContCssClass = defaultsStr(f.center_cont_css_class, 'mdiv');
|
||||
|
||||
/**
|
||||
* Toolbar's custom container ID
|
||||
* @type {String}
|
||||
*/
|
||||
this.tgtId = defaultsStr(f.target_id, null);
|
||||
|
||||
/**
|
||||
* Toolbar's container DOM element
|
||||
* @type {DOMElement}
|
||||
* @private
|
||||
*/
|
||||
this.cont = null;
|
||||
|
||||
/**
|
||||
* Left-side inner container DOM element (rows counter in toolbar)
|
||||
* @type {DOMElement}
|
||||
* @private
|
||||
*/
|
||||
this.lCont = null;
|
||||
|
||||
/**
|
||||
* Right-side inner container DOM element (reset button,
|
||||
* page length selector in toolbar)
|
||||
* @type {DOMElement}
|
||||
* @private
|
||||
*/
|
||||
this.rCont = null;
|
||||
|
||||
/**
|
||||
* Middle inner container DOM element (paging elements in toolbar)
|
||||
* @type {DOMElement}
|
||||
* @private
|
||||
*/
|
||||
this.cCont = null;
|
||||
|
||||
/**
|
||||
* Container elements inside toolbar
|
||||
* @private
|
||||
*/
|
||||
this.innerCont = {
|
||||
left: null,
|
||||
center: null,
|
||||
right: null
|
||||
};
|
||||
|
||||
this.emitter.on(EVENTS,
|
||||
(feature, isExternal) => this.init(isExternal));
|
||||
|
||||
/** @inherited */
|
||||
this.enabled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize toolbar components
|
||||
* @param {Boolean} isExternal initialize only if component belongs
|
||||
* to toolbar
|
||||
*/
|
||||
init(isExternal) {
|
||||
if (this.initialized || isExternal) {
|
||||
return;
|
||||
}
|
||||
|
||||
let tf = this.tf;
|
||||
|
||||
// default container
|
||||
let container = createElm('div');
|
||||
container.className = this.contCssClass;
|
||||
|
||||
// custom container
|
||||
if (this.tgtId) {
|
||||
elm(this.tgtId).appendChild(container);
|
||||
}
|
||||
// grid-layout
|
||||
else if (tf.gridLayout) {
|
||||
let gridLayout = tf.Mod.gridLayout;
|
||||
gridLayout.tblMainCont.appendChild(container);
|
||||
container.className = gridLayout.infDivCssClass;
|
||||
}
|
||||
// default location: just above the table
|
||||
else {
|
||||
let cont = createElm('caption');
|
||||
cont.appendChild(container);
|
||||
tf.dom().insertBefore(cont, tf.dom().firstChild);
|
||||
}
|
||||
this.cont = container;
|
||||
|
||||
// left container
|
||||
this.lCont = this.createContainer(container, this.lContCssClass);
|
||||
|
||||
// right container
|
||||
this.rCont = this.createContainer(container, this.rContCssClass);
|
||||
|
||||
// middle container
|
||||
this.cCont = this.createContainer(container, this.cContCssClass);
|
||||
|
||||
this.innerCont = {
|
||||
left: this.lCont,
|
||||
center: this.cCont,
|
||||
right: this.rCont
|
||||
};
|
||||
|
||||
/** @inherited */
|
||||
this.initialized = true;
|
||||
|
||||
// emit help initialisation only if undefined
|
||||
if (isUndef(tf.help)) {
|
||||
// explicitily enable help to initialise feature by
|
||||
// default, only if setting is undefined
|
||||
tf.Mod.help.enable();
|
||||
this.emitter.emit('init-help', tf);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the container based on requested position inside the toolbar
|
||||
* @param {String} [position=RIGHT] 3 possible positions: 'left', 'center',
|
||||
* 'right'
|
||||
* @param {DOMElement} el optional DOM element to be inserter in container
|
||||
* @returns {DOMElement}
|
||||
*/
|
||||
container(position = RIGHT, el) {
|
||||
let cont = this.innerCont[position];
|
||||
if (el) {
|
||||
cont.appendChild(el);
|
||||
}
|
||||
return cont;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create DOM element inside passed container
|
||||
* @param {DOMElement} container
|
||||
* @param {String} css
|
||||
* @private
|
||||
*/
|
||||
createContainer(container, css) {
|
||||
let div = createElm('div', ['class', css]);
|
||||
container.appendChild(div);
|
||||
return div;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy Toolbar instance
|
||||
*/
|
||||
destroy() {
|
||||
if (!this.initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
let tf = this.tf;
|
||||
|
||||
removeElm(this.cont);
|
||||
this.cont = null;
|
||||
|
||||
let tbl = tf.dom();
|
||||
let captions = tag(tbl, 'caption');
|
||||
[].forEach.call(captions, (el) => removeElm(el));
|
||||
|
||||
/** @inherited */
|
||||
this.initialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: remove as soon as feature name is fixed
|
||||
Toolbar.meta = {alwaysInstantiate: true};
|
32
src/number.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
import {isNumber} from './types';
|
||||
|
||||
/**
|
||||
* Takes a string, removes all formatting/cruft and returns the raw float value
|
||||
* @param {String} Formatted number
|
||||
* @param {String} Decimal type '.' or ','
|
||||
* @return {Number} Unformatted number
|
||||
*
|
||||
* https://github.com/openexchangerates/accounting.js/blob/master/accounting.js
|
||||
*/
|
||||
export const parse = (value, decimal = '.') => {
|
||||
// Return the value as-is if it's already a number
|
||||
if (isNumber(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
// Build regex to strip out everything except digits, decimal point and
|
||||
// minus sign
|
||||
let regex = new RegExp('[^0-9-' + decimal + ']', ['g']);
|
||||
let unformatted = parseFloat(
|
||||
('' + value)
|
||||
// replace bracketed values with negatives
|
||||
.replace(/\((.*)\)/, '-$1')
|
||||
// strip out any cruft
|
||||
.replace(regex, '')
|
||||
// make sure decimal point is standard
|
||||
.replace(decimal, '.')
|
||||
);
|
||||
|
||||
// This will fail silently
|
||||
return !isNaN(unformatted) ? unformatted : 0;
|
||||
};
|
53
src/settings.js
Normal file
|
@ -0,0 +1,53 @@
|
|||
import {isBoolean, isString, isFn, isArray} from './types';
|
||||
|
||||
/** Configuration settings helpers */
|
||||
|
||||
/**
|
||||
* If passed value is not of boolean type return the default value
|
||||
* otherwise return the value itself
|
||||
* @param {Boolean|Any} value
|
||||
* @param {Boolean} default value
|
||||
* @return {Boolean|Any}
|
||||
*/
|
||||
export const defaultsBool =
|
||||
(val, defaultVal) => isBoolean(val) ? val : defaultVal;
|
||||
|
||||
/**
|
||||
* If passed value is not of string type return the default value
|
||||
* otherwise return the value itself
|
||||
* @param {String|Any} value
|
||||
* @param {String} default value
|
||||
* @return {String|Any}
|
||||
*/
|
||||
export const defaultsStr =
|
||||
(val, defaultVal) => isString(val) ? val : defaultVal;
|
||||
|
||||
/**
|
||||
* If passed value is not of number type return the default value
|
||||
* otherwise return the value itself
|
||||
* @param {Number|Any} value
|
||||
* @param {Number} default value
|
||||
* @return {Number|Any}
|
||||
*/
|
||||
export const defaultsNb =
|
||||
(val, defaultVal) => isNaN(val) ? defaultVal : val;
|
||||
|
||||
/**
|
||||
* If passed value is not of array type return the default value
|
||||
* otherwise return the value itself
|
||||
* @param {Array|Any} value
|
||||
* @param {Array} default value
|
||||
* @return {Array|Any}
|
||||
*/
|
||||
export const defaultsArr =
|
||||
(val, defaultVal) => isArray(val) ? val : defaultVal;
|
||||
|
||||
/**
|
||||
* If passed value is not of function type return the default value
|
||||
* otherwise return the value itself
|
||||
* @param {Function|Any} value
|
||||
* @param {Function} default value
|
||||
* @return {Function|Any}
|
||||
*/
|
||||
export const defaultsFn =
|
||||
(val, defaultVal) => isFn(val) ? val : defaultVal;
|
67
src/sort.js
|
@ -1,6 +1,7 @@
|
|||
/**
|
||||
* Sorting utilities
|
||||
*/
|
||||
import {parse as parseNb} from './number';
|
||||
import {Date as SugarDate} from 'sugar-date';
|
||||
|
||||
/** Sorting utilities */
|
||||
|
||||
/**
|
||||
* Case insensitive compare function for passed strings
|
||||
|
@ -13,21 +14,69 @@
|
|||
export const ignoreCase = (a, b) => {
|
||||
let x = a.toLowerCase();
|
||||
let y = b.toLowerCase();
|
||||
return ((x < y) ? -1 : ((x > y) ? 1 : 0));
|
||||
}
|
||||
return x < y ? -1 : (x > y ? 1 : 0);
|
||||
};
|
||||
|
||||
/**
|
||||
* Sorts passed numbers in a ascending manner
|
||||
* Compare function for sorting passed numbers in ascending manner
|
||||
* @param {Number} First number
|
||||
* @param {Number} Second number
|
||||
* @param {Number} Negative, zero or positive number
|
||||
* @return {Number} Negative, zero or positive number
|
||||
*/
|
||||
export const numSortAsc = (a, b) => (a - b);
|
||||
|
||||
/**
|
||||
* Sorts passed numbers in a descending manner
|
||||
* Compare function for sorting passed numbers in descending manner
|
||||
* @param {Number} First number
|
||||
* @param {Number} Second number
|
||||
* @param {Number} Negative, zero or positive number
|
||||
* @return {Number} Negative, zero or positive number
|
||||
*/
|
||||
export const numSortDesc = (a, b) => (b - a);
|
||||
|
||||
/**
|
||||
* Compare function for sorting passed dates in ascending manner according to
|
||||
* the corresponding UTC numeric value (returned by getTime)
|
||||
* @param {Date} First date object
|
||||
* @param {Date} Second date object
|
||||
* @return {Number} Negative, zero or positive number
|
||||
*/
|
||||
export const dateSortAsc = (date1, date2) => date1.getTime() - date2.getTime();
|
||||
|
||||
/**
|
||||
* Compare function for sorting passed dates in descending manner according to
|
||||
* the corresponding UTC numeric value (returned by getTime)
|
||||
* @param {Date} First date object
|
||||
* @param {Date} Second date object
|
||||
* @return {Number} Negative, zero or positive number
|
||||
*/
|
||||
export const dateSortDesc = (date1, date2) => date2.getTime() - date1.getTime();
|
||||
|
||||
/**
|
||||
* Curried compare function for sorting passed formatted numbers in desired
|
||||
* fashion according to supplied compare function and decimal separator
|
||||
* @param {Function} Compare function
|
||||
* @param {String} [decimal=','] Decimal separator
|
||||
* @return {Function} Compare function receiving parsed numeric arguments
|
||||
*/
|
||||
export const sortNumberStr = (compareFn, decimal = ',') => {
|
||||
return (numStr1, numStr2) => {
|
||||
let num1 = parseNb(numStr1, decimal);
|
||||
let num2 = parseNb(numStr2, decimal);
|
||||
return compareFn(num1, num2);
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Curried compare function for sorting passed formatted dates in desired
|
||||
* fashion according to supplied compare function and locale
|
||||
* @param {Function} Compare function
|
||||
* @param {String} [locale='en-us'] Locale code
|
||||
* @return {Function} Compare function receiving parsed date arguments
|
||||
*/
|
||||
export const sortDateStr = (compareFn, locale = 'en-us') => {
|
||||
return (dateStr1, dateStr2) => {
|
||||
let date1 = SugarDate.create(dateStr1, locale);
|
||||
let date2 = SugarDate.create(dateStr2, locale);
|
||||
return compareFn(date1, date2);
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import {remove as removeDiacritics} from 'diacritics';
|
||||
|
||||
/**
|
||||
* String utilities
|
||||
*/
|
||||
|
@ -7,12 +9,12 @@
|
|||
* @param {String} text
|
||||
* @return {String}
|
||||
*/
|
||||
export const trim = text => {
|
||||
export const trim = (text) => {
|
||||
if (text.trim) {
|
||||
return text.trim();
|
||||
}
|
||||
return text.replace(/^\s*|\s*$/g, '');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if passed string is empty
|
||||
|
@ -26,11 +28,11 @@ export const isEmpty = (text) => trim(text) === '';
|
|||
* @param {String} text
|
||||
* @return {String} escaped string
|
||||
*/
|
||||
export const rgxEsc = text => {
|
||||
export const rgxEsc = (text) => {
|
||||
let chars = /[-\/\\^$*+?.()|[\]{}]/g;
|
||||
let escMatch = '\\$&';
|
||||
return String(text).replace(chars, escMatch);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns passed string as lowercase if caseSensitive flag set false. By
|
||||
|
@ -43,27 +45,68 @@ export const matchCase = (text, caseSensitive = false) => {
|
|||
return text.toLowerCase();
|
||||
}
|
||||
return text;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if passed data contains the searched term
|
||||
* @param {String} term Searched term
|
||||
* @param {String} data Data string
|
||||
* @param {Boolean} exactMatch Exact match
|
||||
* @param {Boolean} caseSensitive Case sensitive
|
||||
* @param {String} term Searched term
|
||||
* @param {String} data Data string
|
||||
* @param {Boolean} exactMatch Exact match
|
||||
* @param {Boolean} caseSensitive Case sensitive
|
||||
* @param {Boolean} ignoreDiacritics Ignore diacritics
|
||||
* @return {Boolean}
|
||||
*/
|
||||
export const contains =
|
||||
(term, data, exactMatch = false, caseSensitive = false) => {
|
||||
// Improved by Cedric Wartel (cwl) automatic exact match for selects and
|
||||
// special characters are now filtered
|
||||
let regexp;
|
||||
let modifier = caseSensitive ? 'g' : 'gi';
|
||||
if (exactMatch) {
|
||||
regexp = new RegExp('(^\\s*)' + rgxEsc(term) + '(\\s*$)',
|
||||
modifier);
|
||||
} else {
|
||||
regexp = new RegExp(rgxEsc(term), modifier);
|
||||
}
|
||||
return regexp.test(data);
|
||||
export const contains = (term, data, exactMatch = false, caseSensitive = false,
|
||||
ignoreDiacritics = false) => {
|
||||
// Improved by Cedric Wartel (cwl) automatic exact match for selects and
|
||||
// special characters are now filtered
|
||||
let regexp;
|
||||
let modifier = caseSensitive ? 'g' : 'gi';
|
||||
if (ignoreDiacritics) {
|
||||
term = removeDiacritics(term);
|
||||
data = removeDiacritics(data);
|
||||
}
|
||||
if (exactMatch) {
|
||||
regexp = new RegExp('(^\\s*)' + rgxEsc(term) + '(\\s*$)',
|
||||
modifier);
|
||||
} else {
|
||||
regexp = new RegExp(rgxEsc(term), modifier);
|
||||
}
|
||||
return regexp.test(data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Camelize a string, cutting the string by multiple separators like
|
||||
* hyphens, underscores and spaces.
|
||||
* @param {String} text text to camelize
|
||||
* @return {String} camelized text
|
||||
*/
|
||||
export const toCamelCase = (text = '') => {
|
||||
return text.replace(/^([A-Z])|[\s-_]+(\w)/g, (match, p1, p2) => {
|
||||
if (p2) {
|
||||
return p2.toUpperCase();
|
||||
}
|
||||
return p1.toLowerCase();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate a string in the format of a UUID (Universally Unique IDentifier).
|
||||
* NOTE: This format of 8 chars, followed by 3 groups of 4 chars, followed by 12
|
||||
* chars is known as a UUID and is defined in RFC4122 and is a standard for
|
||||
* generating unique IDs. This function DOES NOT implement this standard.
|
||||
* It simply outputs a string that looks similar. The standard is found here:
|
||||
* https://www.ietf.org/rfc/rfc4122.txt
|
||||
* source: https://gist.github.com/gordonbrander/2230317
|
||||
* @return {String}
|
||||
*/
|
||||
export const uuid = () => {
|
||||
const chr4 = () => Math.random().toString(16).slice(-4);
|
||||
|
||||
return chr4() + chr4()
|
||||
+ '-' + chr4()
|
||||
+ '-' + chr4()
|
||||
+ '-' + chr4()
|
||||
+ '-' + chr4()
|
||||
+ chr4() + chr4();
|
||||
};
|
||||
|
|
2516
src/tablefilter.js
32
src/types.js
|
@ -5,13 +5,19 @@
|
|||
|
||||
const UNDEFINED = void 0;
|
||||
|
||||
/**
|
||||
* Return an empty function
|
||||
* @return {Function}
|
||||
*/
|
||||
export const EMPTY_FN = function() {};
|
||||
|
||||
/**
|
||||
* Check passed argument is an object
|
||||
* @param {Object} obj
|
||||
* @return {Boolean}
|
||||
*/
|
||||
export const isObj =
|
||||
obj => Object.prototype.toString.call(obj) === '[object Object]';
|
||||
(obj) => Object.prototype.toString.call(obj) === '[object Object]';
|
||||
|
||||
/**
|
||||
* Check passed argument is a function
|
||||
|
@ -19,7 +25,7 @@ export const isObj =
|
|||
* @return {Boolean}
|
||||
*/
|
||||
export const isFn =
|
||||
obj => Object.prototype.toString.call(obj) === '[object Function]';
|
||||
(obj) => Object.prototype.toString.call(obj) === '[object Function]';
|
||||
|
||||
/**
|
||||
* Check passed argument is an array
|
||||
|
@ -27,15 +33,15 @@ export const isFn =
|
|||
* @return {Boolean}
|
||||
*/
|
||||
export const isArray =
|
||||
obj => Object.prototype.toString.call(obj) === '[object Array]';
|
||||
(obj) => Object.prototype.toString.call(obj) === '[object Array]';
|
||||
|
||||
/**
|
||||
* Check passed argument is a string
|
||||
* @param {String} obj objue
|
||||
* @param {String} obj obj
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export const isString =
|
||||
obj => Object.prototype.toString.call(obj) === '[object String]';
|
||||
(obj) => Object.prototype.toString.call(obj) === '[object String]';
|
||||
|
||||
/**
|
||||
* Check passed argument is a number
|
||||
|
@ -43,25 +49,33 @@ export const isString =
|
|||
* @returns {Boolean}
|
||||
*/
|
||||
export const isNumber =
|
||||
obj => Object.prototype.toString.call(obj) === '[object Number]';
|
||||
(obj) => Object.prototype.toString.call(obj) === '[object Number]';
|
||||
|
||||
/**
|
||||
* Check passed argument is a boolean
|
||||
* @param {Boolean} obj
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export const isBoolean =
|
||||
(obj) => Object.prototype.toString.call(obj) === '[object Boolean]';
|
||||
|
||||
/**
|
||||
* Check passed argument is undefined
|
||||
* @param {Any} obj
|
||||
* @return {Boolean}
|
||||
*/
|
||||
export const isUndef = obj => obj === UNDEFINED;
|
||||
export const isUndef = (obj) => obj === UNDEFINED;
|
||||
|
||||
/**
|
||||
* Check passed argument is null
|
||||
* @param {Any} obj
|
||||
* @return {Boolean}
|
||||
*/
|
||||
export const isNull = obj => obj === null;
|
||||
export const isNull = (obj) => obj === null;
|
||||
|
||||
/**
|
||||
* Check passed argument is empty (undefined, null or empty string)
|
||||
* @param {Any} obj
|
||||
* @return {Boolean}
|
||||
*/
|
||||
export const isEmpty = obj => isUndef(obj) || isNull(obj) || obj.length === 0;
|
||||
export const isEmpty = (obj) => isUndef(obj) || isNull(obj) || obj.length === 0;
|
||||
|
|
|
@ -350,4 +350,4 @@
|
|||
<td>88.233.19.89</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</table>
|
||||
|
|
|
@ -5,3 +5,6 @@
|
|||
.activeHeader
|
||||
background-color #66AFE9 !important
|
||||
color #fff !important
|
||||
|
||||
.activeCell
|
||||
background-color rgba(0,0,0,0.075)
|
||||
|
|
|
@ -34,7 +34,7 @@ div.colVisCont
|
|||
z-index 10000
|
||||
padding 10px 10px 10px 10px
|
||||
text-align left
|
||||
font-size 12px
|
||||
font-size inherit
|
||||
|
||||
p
|
||||
margin 6px auto 6px auto
|
||||
|
@ -47,7 +47,7 @@ div.colVisCont
|
|||
ul.cols_checklist
|
||||
padding 0
|
||||
margin 0
|
||||
list-style none
|
||||
list-style-type none
|
||||
|
||||
label
|
||||
display block
|
||||
|
|
|
@ -11,11 +11,12 @@ $item-font-color = #fff
|
|||
.div_checklist
|
||||
width 100%
|
||||
height 90px
|
||||
line-height 30px
|
||||
border 1px solid $filter-border-color
|
||||
overflow auto
|
||||
text-align left
|
||||
background-color $filter-bg-color
|
||||
color: $filter-font-color
|
||||
color $filter-font-color
|
||||
|
||||
ul.flt_checklist
|
||||
padding 0 !important
|
||||
|
@ -26,19 +27,20 @@ $item-font-color = #fff
|
|||
li.flt_checklist_item
|
||||
padding 1px !important
|
||||
margin 0 !important
|
||||
font-size 10px !important
|
||||
font-size inherit
|
||||
border-bottom 1px solid $filter-border-color !important
|
||||
|
||||
&:hover
|
||||
background-color $item-bg-color !important
|
||||
color $item-font-color !important;
|
||||
color $item-font-color !important
|
||||
|
||||
label
|
||||
display block !important;
|
||||
display block !important
|
||||
font-weight inherit !important
|
||||
|
||||
input
|
||||
vertical-align middle !important;
|
||||
margin 2px 5px 2px 1px !important;
|
||||
vertical-align middle !important
|
||||
margin 2px 5px 3px 1px !important
|
||||
|
||||
// disabled checklist item
|
||||
.flt_checklist_item_disabled
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
// input and select filter type
|
||||
.flt
|
||||
font-family inherit
|
||||
font-size inherit
|
||||
display block
|
||||
color $filter-font-color
|
||||
background-color $filter-bg-color
|
||||
|
@ -64,6 +65,7 @@
|
|||
// multiple select type filter
|
||||
select.flt_multi
|
||||
font-family inherit
|
||||
font-size inherit
|
||||
color $filter-font-color
|
||||
background-color $filter-bg-color
|
||||
border 1px solid $filter-border-color
|
||||
|
@ -74,6 +76,10 @@ select.flt_multi
|
|||
vertical-align middle
|
||||
box-sizing border-box
|
||||
|
||||
option
|
||||
padding-top 5px
|
||||
padding-bottom 5px
|
||||
|
||||
// tiny input type filter
|
||||
.flt_s
|
||||
@extend .flt
|
||||
|
@ -88,7 +94,7 @@ select.flt_multi
|
|||
box-sizing initial
|
||||
display initial
|
||||
|
||||
// Pop-up filters elements
|
||||
// pop-up filters elements
|
||||
div.popUpFilter
|
||||
arrow(top, 10px white, 2px $th-bg-color)
|
||||
box-shadow(3px 3px 2px #888)
|
||||
|
@ -99,3 +105,7 @@ div.popUpFilter
|
|||
background-color $th-bg-color
|
||||
border 1px solid $filter-row-bg-color
|
||||
padding 0
|
||||
|
||||
// pop-up container
|
||||
.popUpPlaceholder
|
||||
position relative
|
||||
|
|
|
@ -11,7 +11,6 @@ div.grd_Cont
|
|||
width 800px
|
||||
height auto
|
||||
overflow hidden
|
||||
padding 3px 3px 3px 3px
|
||||
background-color $grid-layout-bg-color
|
||||
border 1px solid $grid-layout-border-color
|
||||
|
||||
|
@ -50,8 +49,9 @@ div.grd_tblCont
|
|||
|
||||
// headers' table container
|
||||
div.grd_headTblCont
|
||||
display block
|
||||
margin-right 20px
|
||||
height auto
|
||||
width 800px
|
||||
overflow hidden
|
||||
border-bottom 1px solid $grid-layout-border-color
|
||||
background-color $grid-layout-bg-color
|
||||
|
@ -63,10 +63,6 @@ div.grd_headTblCont table
|
|||
table-layout fixed
|
||||
box-sizing initial
|
||||
|
||||
div.grd_tblCont table
|
||||
border-right 1px solid $grid-layout-border-color
|
||||
box-sizing initial
|
||||
|
||||
// headers
|
||||
div.grd_tblCont table th
|
||||
div.grd_headTblCont table th
|
||||
|
|
|
@ -32,6 +32,7 @@ div.helpCont
|
|||
color #333
|
||||
background #fff
|
||||
text-align left
|
||||
z-index 1000
|
||||
|
||||
a
|
||||
color #cc0000
|
||||
|
|
7
static/style/mixins/empty-content.styl
Normal file
|
@ -0,0 +1,7 @@
|
|||
/**
|
||||
* Empty content mixin
|
||||
*/
|
||||
|
||||
empty-content()
|
||||
&:empty:after
|
||||
content "\00A0"
|
|
@ -14,3 +14,8 @@ input.reset
|
|||
|
||||
&:hover
|
||||
background-color $toolbar-hover-color
|
||||
|
||||
a.reset
|
||||
font-weight normal !important
|
||||
line-height $min-height
|
||||
padding 5px 5px
|
||||
|
|
|
@ -26,11 +26,19 @@ table.TF
|
|||
margin 0
|
||||
padding $padding
|
||||
border-bottom 1px solid $td-border-color
|
||||
text-overflow ellipsis
|
||||
|
||||
// responsiveness
|
||||
// responsive
|
||||
&.resp
|
||||
display block
|
||||
overflow-x auto
|
||||
overflow-y hidden
|
||||
|
||||
.sort-arrow
|
||||
position initial
|
||||
|
||||
// stick headers
|
||||
&.sticky
|
||||
th
|
||||
position sticky
|
||||
top -1px
|
||||
|
|
18
static/style/tablefilter.styl
Normal file
|
@ -0,0 +1,18 @@
|
|||
/** Main stylesheet */
|
||||
@import 'active-header'
|
||||
@import 'alternating-rows'
|
||||
@import 'ezedittable'
|
||||
@import 'filter-checklist'
|
||||
@import 'filters'
|
||||
@import 'grid-layout'
|
||||
@import 'help'
|
||||
@import 'keyword'
|
||||
@import 'loader'
|
||||
@import 'noresults'
|
||||
@import 'paging'
|
||||
@import 'reset-button'
|
||||
@import 'rows-counter'
|
||||
@import 'sort'
|
||||
@import 'status-bar'
|
||||
@import 'table'
|
||||
@import 'toolbar'
|
BIN
static/style/themes/transparent/images/btn_first_page.gif
Normal file
After Width: | Height: | Size: 63 B |
BIN
static/style/themes/transparent/images/btn_last_page.gif
Normal file
After Width: | Height: | Size: 61 B |
BIN
static/style/themes/transparent/images/btn_next_page.gif
Normal file
After Width: | Height: | Size: 59 B |
BIN
static/style/themes/transparent/images/btn_prev_page.gif
Normal file
After Width: | Height: | Size: 58 B |
BIN
static/style/themes/transparent/images/icn_clear_filters.png
Normal file
After Width: | Height: | Size: 601 B |
BIN
static/style/themes/transparent/images/img_loading.gif
Normal file
After Width: | Height: | Size: 847 B |
205
static/style/themes/transparent/transparent.styl
Normal file
|
@ -0,0 +1,205 @@
|
|||
/**
|
||||
* transparent theme overrides
|
||||
*/
|
||||
|
||||
@import '../../mixins/box-shadow'
|
||||
|
||||
// table
|
||||
table.TF
|
||||
padding 0
|
||||
color inherit
|
||||
border-right 1px solid transparent
|
||||
border-top 1px solid transparent
|
||||
border-left 1px solid transparent
|
||||
border-bottom 0
|
||||
|
||||
th
|
||||
margin 0
|
||||
color inherit
|
||||
background-color transparent
|
||||
border-color transparent
|
||||
border-width 1px
|
||||
border-style solid
|
||||
&:last-child
|
||||
border-right 1px solid transparent
|
||||
|
||||
td
|
||||
margin 0
|
||||
padding 5px
|
||||
color inherit
|
||||
border-bottom 1px solid transparent
|
||||
border-left 0
|
||||
border-top 0
|
||||
border-right 0
|
||||
|
||||
// filters
|
||||
.fltrow
|
||||
background-color transparent
|
||||
|
||||
th, td
|
||||
padding 1px 3px 1px 3px
|
||||
border-bottom 1px solid transparent !important
|
||||
&:last-child
|
||||
border-right 1px solid transparent
|
||||
|
||||
.flt,
|
||||
select.flt,
|
||||
select.flt_multi,
|
||||
.flt_s,
|
||||
.single_flt,
|
||||
.div_checklist
|
||||
border 1px solid #A4BED4
|
||||
|
||||
input.flt
|
||||
width 99% !important
|
||||
|
||||
// toolbar
|
||||
.inf
|
||||
background-color transparent
|
||||
border 1px solid transparent
|
||||
height $min-height
|
||||
color inherit
|
||||
|
||||
div.tot,
|
||||
div.status
|
||||
border-right 0 !important
|
||||
|
||||
// help
|
||||
.helpBtn
|
||||
&:hover
|
||||
background-color transparent
|
||||
|
||||
// reset button
|
||||
input.reset
|
||||
background transparent url(images/icn_clear_filters.png) center center no-repeat !important
|
||||
|
||||
.nextPage
|
||||
background transparent url(images/btn_next_page.gif) center center no-repeat !important
|
||||
border 1px solid transparent !important
|
||||
|
||||
&:hover
|
||||
background #F7F7F7 url(images/btn_next_page.gif) center center no-repeat !important
|
||||
border 1px solid #F7F7F7 !important
|
||||
|
||||
.previousPage
|
||||
background transparent url(images/btn_prev_page.gif) center center no-repeat !important
|
||||
border 1px solid transparent !important
|
||||
|
||||
&:hover
|
||||
background #F7F7F7 url(images/btn_prev_page.gif) center center no-repeat !important
|
||||
border 1px solid #F7F7F7 !important
|
||||
|
||||
.firstPage
|
||||
background transparent url(images/btn_first_page.gif) center center no-repeat !important
|
||||
border 1px solid transparent !important
|
||||
|
||||
&:hover
|
||||
background #F7F7F7 url(images/btn_first_page.gif) center center no-repeat !important
|
||||
border 1px solid #F7F7F7 !important
|
||||
|
||||
.lastPage
|
||||
background transparent url(images/btn_last_page.gif) center center no-repeat !important
|
||||
border 1px solid transparent !important
|
||||
|
||||
&:hover
|
||||
background #F7F7F7 url(images/btn_last_page.gif) center center no-repeat !important
|
||||
border 1px solid #F7F7F7 !important
|
||||
|
||||
// active header
|
||||
.activeHeader
|
||||
background #F7F7F7 !important
|
||||
border 1px solid transparent
|
||||
color inherit !important
|
||||
|
||||
// grid-layout
|
||||
|
||||
// main container
|
||||
div.grd_Cont
|
||||
box-shadow(0 0 0 0 rgba(50, 50, 50, 0.75))
|
||||
background-color transparent
|
||||
border 1px solid transparent
|
||||
padding 0 !important
|
||||
|
||||
// alternating background colors
|
||||
.even
|
||||
background-color transparent
|
||||
|
||||
.odd
|
||||
background-color #F7F7F7
|
||||
|
||||
// headers' table container
|
||||
div.grd_headTblCont
|
||||
background-color transparent
|
||||
border-bottom none !important
|
||||
|
||||
|
||||
div.grd_tblCont table
|
||||
border-right none !important
|
||||
|
||||
// headers
|
||||
div.grd_tblCont table th,
|
||||
div.grd_headTblCont table th,
|
||||
div.grd_headTblCont table td
|
||||
background transparent
|
||||
border-bottom 1px solid transparent
|
||||
border-right 1px solid transparent !important
|
||||
border-left 1px solid transparent
|
||||
border-top 1px solid transparent
|
||||
|
||||
div.grd_tblCont table td
|
||||
border-bottom 1px solid transparent
|
||||
border-right 0 !important
|
||||
border-left 0 !important
|
||||
border-top 0 !important
|
||||
|
||||
// toolbar containing left, middle and right divs
|
||||
.grd_inf
|
||||
background-color transparent
|
||||
color inherit
|
||||
border-top 1px solid transparent
|
||||
|
||||
a
|
||||
text-decoration none
|
||||
font-weight bold
|
||||
|
||||
// loader
|
||||
.loader
|
||||
background-color #F7F7F7
|
||||
border 1px solid #F7F7F7
|
||||
border-radius 5px
|
||||
color #000000
|
||||
text-shadow none
|
||||
|
||||
// alternating background colors
|
||||
.even
|
||||
background-color transparent
|
||||
|
||||
.odd
|
||||
background-color #F7F7F7
|
||||
|
||||
// filters visibility
|
||||
span.expClpFlt
|
||||
a.btnExpClpFlt
|
||||
&:hover
|
||||
background-color transparent !important
|
||||
|
||||
// ezedittable
|
||||
|
||||
// selection
|
||||
.ezActiveRow
|
||||
background-color #CCCCCC !important
|
||||
color inherit
|
||||
|
||||
.ezSelectedRow
|
||||
background-color #CCCCCC !important
|
||||
color inherit
|
||||
|
||||
.ezActiveCell
|
||||
background-color transparent
|
||||
color inherit
|
||||
font-weight bold
|
||||
|
||||
.ezETSelectedCell
|
||||
background-color transparent
|
||||
font-weight bold
|
||||
color inherit
|
|
@ -3,12 +3,14 @@
|
|||
*/
|
||||
|
||||
@import 'config'
|
||||
@import 'mixins/empty-content'
|
||||
|
||||
// left, middle and right divs container (toolbar)
|
||||
.inf
|
||||
clear both
|
||||
width auto
|
||||
height $min-height
|
||||
min-width 400px
|
||||
background-color $toolbar-bg-color
|
||||
font-size $toolbar-font-size
|
||||
margin 0
|
||||
|
@ -32,25 +34,25 @@
|
|||
|
||||
// left div
|
||||
.ldiv
|
||||
empty-content()
|
||||
float left
|
||||
width 30%
|
||||
// height 100%
|
||||
position inherit
|
||||
text-align left
|
||||
|
||||
// middle div
|
||||
.mdiv
|
||||
empty-content()
|
||||
float left
|
||||
width 38%
|
||||
// height 100%
|
||||
position inherit
|
||||
text-align center
|
||||
padding 0
|
||||
|
||||
// right div
|
||||
.rdiv
|
||||
empty-content()
|
||||
float right
|
||||
width 30%
|
||||
// height 100%
|
||||
position inherit
|
||||
text-align right
|
||||
|
|
|
@ -49,8 +49,9 @@
|
|||
<script data-config>
|
||||
var filtersConfig = {
|
||||
base_path: '../dist/tablefilter/',
|
||||
grid_layout: true,
|
||||
grid_width: '900px',
|
||||
grid_layout: {
|
||||
width: '900px'
|
||||
},
|
||||
alternate_rows: true,
|
||||
btn_reset: true,
|
||||
rows_counter: true,
|
||||
|
|
|
@ -19,15 +19,18 @@
|
|||
<script data-config>
|
||||
var filtersConfig = {
|
||||
base_path: '../dist/tablefilter/',
|
||||
auto_filter: true,
|
||||
auto_filter_delay: 750, //milliseconds
|
||||
auto_filter: {
|
||||
delay: 750 // milliseconds
|
||||
},
|
||||
filters_row_index: 1,
|
||||
state: true,
|
||||
alternate_rows: true,
|
||||
rows_counter: true,
|
||||
rows_counter_text: 'Rows: ',
|
||||
rows_counter: {
|
||||
text: 'Rows: '
|
||||
},
|
||||
btn_reset: true,
|
||||
status_bar: true,
|
||||
sticky_headers: true,
|
||||
msg_filter: 'Filtering...'
|
||||
};
|
||||
|
||||
|
|
|
@ -27,6 +27,10 @@
|
|||
<script data-config>
|
||||
var tfConfig = {
|
||||
base_path: '../dist/tablefilter/',
|
||||
col_types: [
|
||||
'number', 'string', 'string',
|
||||
'formatted-number', 'formatted-number', 'string'
|
||||
],
|
||||
paging: true,
|
||||
state: {
|
||||
types: ['cookie'],
|
||||
|
@ -35,8 +39,9 @@ var tfConfig = {
|
|||
},
|
||||
alternate_rows: true,
|
||||
rows_counter: true,
|
||||
btn_reset: true,
|
||||
btn_reset_text: 'Clear',
|
||||
btn_reset: {
|
||||
text: 'Clear'
|
||||
},
|
||||
status_bar: true,
|
||||
col_1: 'select',
|
||||
col_2: 'select',
|
||||
|
@ -49,11 +54,7 @@ var tfConfig = {
|
|||
text: 'Columns: ',
|
||||
enable_tick_all: true
|
||||
}, {
|
||||
name: 'sort',
|
||||
types: [
|
||||
'number', 'string', 'string',
|
||||
'US', 'none', 'string'
|
||||
]
|
||||
name: 'sort'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
|
|
@ -8,16 +8,17 @@
|
|||
<h1>{NAME} v{VERSION}</h1>
|
||||
<h2>Data types demo</h2>
|
||||
|
||||
<p>This example features 3 date types and 2 numeric formatting for the currency
|
||||
columns:</p>
|
||||
<p>
|
||||
This example shows how to define column types with the
|
||||
<code>col_types</code> option and specifically numeric formats with:
|
||||
</p>
|
||||
<ul>
|
||||
<li>"." thousands separator and "," decimal separator for €</li>
|
||||
<li>"," thousands separator and "." decimal separator for US$</li>
|
||||
<li>"." thousands separator and "," decimal separator for € column</li>
|
||||
<li>"," thousands separator and "." decimal separator for US$ column</li>
|
||||
</ul>
|
||||
<p>
|
||||
Use the <code>col_number_format</code> property to set numeric formats
|
||||
('EU', 'US' or 'IPADDRESS') and <code>col_date_type</code> to set date
|
||||
types.
|
||||
along with date types with <code>locale</code> and <code>format</code> specified on
|
||||
a column basis.
|
||||
</p>
|
||||
|
||||
<!-- @import partials/pre.html -->
|
||||
|
@ -31,40 +32,6 @@ var id = function (id){
|
|||
var table = id('demo-tot');
|
||||
var totRowIndex = table.getElementsByTagName('tr').length;
|
||||
|
||||
function addCommas(nStr){
|
||||
nStr += '';
|
||||
var x = nStr.split('.');
|
||||
var x1 = x[0];
|
||||
var x2 = x.length > 1 ? '.' + x[1] : '';
|
||||
var rgx = /(\d+)(\d{3})/;
|
||||
while (rgx.test(x1)) {
|
||||
x1 = x1.replace(rgx, '$1' + ',' + '$2');
|
||||
}
|
||||
return x1 + x2;
|
||||
}
|
||||
|
||||
function addDots(nStr){
|
||||
nStr += '';
|
||||
var x = nStr.split('.');
|
||||
var x1 = x[0];
|
||||
var x2 = x.length > 1 ? ',' + x[1] : '';
|
||||
var rgx = /(\d+)(\d{3})/;
|
||||
while (rgx.test(x1)) {
|
||||
x1 = x1.replace(rgx, '$1' + '.' + '$2');
|
||||
}
|
||||
return x1 + x2;
|
||||
}
|
||||
|
||||
function formatTotals(){
|
||||
var tot1 = id('sum1').innerHTML;
|
||||
tot1 = addDots(tot1);
|
||||
id('sum1').innerHTML = tot1;
|
||||
|
||||
var tot2 = id('sum2').innerHTML;
|
||||
tot2 = addCommas(tot2);
|
||||
id('sum2').innerHTML = tot2;
|
||||
}
|
||||
|
||||
/* EXAMPLE 1
|
||||
*********************** */
|
||||
var tfConfig = {
|
||||
|
@ -75,29 +42,29 @@ var tfConfig = {
|
|||
btn_reset: true,
|
||||
loader: true,
|
||||
status_bar: true,
|
||||
col_number_format: [
|
||||
null, null, null,
|
||||
'EU', 'US', null,
|
||||
null, null, null,
|
||||
'IpAddress'
|
||||
],
|
||||
col_date_type: [
|
||||
null, null, null,
|
||||
null, null, null,
|
||||
'dmy', 'mdy', 'ddmmmyyyy',
|
||||
null
|
||||
col_types: [
|
||||
'string',
|
||||
'string',
|
||||
'string',
|
||||
{ type: 'formatted-number', decimal: ',', thousands: '.' },
|
||||
'formatted-number', // defaults to '.' for decimal and ',' for thousands
|
||||
'string',
|
||||
{ type: 'date', locale: 'fr' },
|
||||
{ type: 'date', locale: 'en', format: '{dd}-{MM}-{yyyy|yy}' },
|
||||
{ type: 'date', locale: 'en', format: ['{dd}-{months}-{yyyy|yy}'] },
|
||||
'ipaddress'
|
||||
],
|
||||
rows_always_visible: [totRowIndex],
|
||||
on_filters_loaded: function(o){
|
||||
o.setFilterValue(3, '>1.000');
|
||||
o.setFilterValue(4, '<2,500');
|
||||
o.setFilterValue(6, '>23-01-95');
|
||||
o.filter();
|
||||
on_filters_loaded: function(tf){
|
||||
tf.setFilterValue(3, '>1.000');
|
||||
tf.setFilterValue(4, '<2,500');
|
||||
tf.setFilterValue(6, '>23-01-95');
|
||||
tf.setFilterValue(8, '<2000');
|
||||
tf.filter();
|
||||
},
|
||||
extensions:[
|
||||
{ name: 'sort' },
|
||||
{
|
||||
name: 'sort'
|
||||
},{
|
||||
name: 'colOps',
|
||||
id: ['sum1', 'sum2'],
|
||||
col: [3, 4],
|
||||
|
@ -106,7 +73,10 @@ var tfConfig = {
|
|||
exclude_row: [totRowIndex],
|
||||
decimal_precision: [2, 2],
|
||||
tot_row_index: [totRowIndex],
|
||||
on_after_operation: formatTotals
|
||||
format_result: [
|
||||
{ prefix: '€ ' },
|
||||
{ prefix: '$ ' }
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
@ -116,8 +86,11 @@ tf.init();
|
|||
|
||||
</script>
|
||||
|
||||
<p>Below an example of ISO date support (YMD). Use <code>default_date_type</code>
|
||||
to set a date type for a whole table ('DMY','MDY','YMD').</p>
|
||||
<p>
|
||||
Below an example of ISO date support ({yyyy|yy}/{MM}/{dd}).
|
||||
Use <code>locale</code> to set a locale globally
|
||||
(defaults to 'en').
|
||||
</p>
|
||||
|
||||
<!-- @import partials/dummy-table.html -->
|
||||
<script data-config>
|
||||
|
@ -130,12 +103,17 @@ var tf2Config = {
|
|||
btn_reset: true,
|
||||
loader: true,
|
||||
status_bar: true,
|
||||
default_date_type: 'YMD',
|
||||
on_filters_loaded: function(o){
|
||||
o.setFilterValue(5, '>95-05-18');
|
||||
o.filter();
|
||||
locale: 'en-US',
|
||||
col_types: [
|
||||
'string', 'number', 'string',
|
||||
'number', 'string', 'date'
|
||||
],
|
||||
on_filters_loaded: function(tf){
|
||||
tf.setFilterValue(5, '>95-05-18');
|
||||
tf.filter();
|
||||
}
|
||||
};
|
||||
|
||||
var tf2 = new TableFilter('demo', tf2Config);
|
||||
tf2.init();
|
||||
</script>
|
||||
|
|
|
@ -23,14 +23,10 @@
|
|||
btn_reset: true,
|
||||
loader: true,
|
||||
status_bar: true,
|
||||
mark_active_columns: true,
|
||||
mark_active_columns: {
|
||||
highlight_column: true
|
||||
},
|
||||
no_results_message: true,
|
||||
responsive: true,
|
||||
col_number_format: [
|
||||
null, null, 'US',
|
||||
'US', 'US', 'US',
|
||||
'US', 'US', 'US'
|
||||
],
|
||||
custom_options: {
|
||||
cols:[3],
|
||||
texts: [
|
||||
|
@ -51,8 +47,8 @@
|
|||
},
|
||||
col_widths: [
|
||||
'150px', '100px', '100px',
|
||||
'70px', '70px', '70px',
|
||||
'70px', '60px', '60px'
|
||||
'90px', '90px', '90px',
|
||||
'90px', '70px', '70px'
|
||||
],
|
||||
extensions:[{ name: 'sort' }]
|
||||
};
|
||||
|
|
|
@ -82,18 +82,23 @@
|
|||
var tfConfig = {
|
||||
base_path: '../dist/tablefilter/',
|
||||
alternate_rows: true,
|
||||
rows_counter: true,
|
||||
rows_counter_text: "Displayed rows: ",
|
||||
rows_counter: {
|
||||
text: 'Displayed rows: '
|
||||
},
|
||||
loader: true,
|
||||
status: true,
|
||||
status_bar: true,
|
||||
status_bar_target_id: 'statusDiv',
|
||||
col_number_format: [
|
||||
null, null, 'US',
|
||||
'US', 'US', 'US',
|
||||
'US', 'US', 'US'
|
||||
col_types: [
|
||||
'string', 'string', 'number',
|
||||
'number', 'number', 'number',
|
||||
'number', 'number', 'number'
|
||||
],
|
||||
clear_filter_text: [
|
||||
' [ Show all ] ',
|
||||
'<Clear>',
|
||||
'Clear:'
|
||||
],
|
||||
display_all_text: " [ Show all ] ",
|
||||
col_0: "multiple",
|
||||
col_1: "select",
|
||||
col_2: "checklist",
|
||||
|
@ -101,8 +106,7 @@ var tfConfig = {
|
|||
col_8: "none",
|
||||
|
||||
/* external filters */
|
||||
external_flt_grid: true,
|
||||
external_flt_grid_ids: [
|
||||
external_flt_ids: [
|
||||
'slcCountry',
|
||||
'slcCode',
|
||||
'slcYear',
|
||||
|
|
|
@ -22,16 +22,13 @@ var tfConfig = {
|
|||
base_path: '../dist/tablefilter/',
|
||||
filters_row_index: 2,
|
||||
headers_row_index: 1,
|
||||
col_number_format: [
|
||||
null, null, null,
|
||||
'US', null, null, null
|
||||
col_types: [
|
||||
'string', 'string', 'string',
|
||||
'number', 'string', 'string', 'date'
|
||||
],
|
||||
col_date_type: [
|
||||
null, null, null,
|
||||
null, null, null, 'mdy'
|
||||
],
|
||||
rows_counter: true,
|
||||
rows_counter_text: 'Books: ',
|
||||
rows_counter: {
|
||||
text: 'Books: '
|
||||
},
|
||||
alternate_rows: true,
|
||||
btn_reset: true,
|
||||
|
||||
|
@ -40,22 +37,23 @@ var tfConfig = {
|
|||
col_4: 'select',
|
||||
col_5: 'select',
|
||||
|
||||
/* custom_cell_data delegate used for filtering
|
||||
images in a column */
|
||||
custom_cell_data_cols: [0, 4],
|
||||
custom_cell_data: function(o, cell, colIndex){
|
||||
if(colIndex === 0){
|
||||
var img = cell.getElementsByTagName('img')[0];
|
||||
if(!img){
|
||||
return '';
|
||||
}
|
||||
return img.alt;
|
||||
} else if(colIndex === 4){
|
||||
var chk = cell.getElementsByTagName('input')[0];
|
||||
if(chk.checked){
|
||||
return 'yes';
|
||||
} else {
|
||||
return 'no';
|
||||
/* cell parser for filtering images in a column */
|
||||
cell_parser: {
|
||||
cols: [0, 4],
|
||||
parse: function(o, cell, colIndex){
|
||||
if(colIndex === 0){
|
||||
var img = cell.getElementsByTagName('img')[0];
|
||||
if(!img){
|
||||
return '';
|
||||
}
|
||||
return img.alt;
|
||||
} else if(colIndex === 4){
|
||||
var chk = cell.getElementsByTagName('input')[0];
|
||||
if(chk.checked){
|
||||
return 'yes';
|
||||
} else {
|
||||
return 'no';
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -22,7 +22,7 @@ var tfConfig = {
|
|||
col_2: 'checklist',
|
||||
col_widths: [
|
||||
'75px', '200px', '200px',
|
||||
'100px', '75px', ''
|
||||
'100px', '75px', '400px'
|
||||
],
|
||||
paging: true,
|
||||
alternate_rows: true,
|
||||
|
|
|
@ -22,6 +22,9 @@
|
|||
loader: true,
|
||||
status_bar: true,
|
||||
no_results_message: true,
|
||||
mark_active_columns: {
|
||||
highlight_column: true
|
||||
},
|
||||
col_1: 'select',
|
||||
col_2: 'select',
|
||||
col_widths: [
|
||||
|
@ -29,13 +32,13 @@
|
|||
'120px', '120px', '100px',
|
||||
'100px', '100px', '100px'
|
||||
],
|
||||
col_types: [
|
||||
'string', 'string', 'number',
|
||||
'number', 'number', 'number',
|
||||
'number', 'number', 'number'
|
||||
],
|
||||
extensions:[{
|
||||
name: 'sort',
|
||||
types: [
|
||||
'string', 'string', 'number',
|
||||
'number', 'number', 'number',
|
||||
'number', 'number', 'number'
|
||||
]
|
||||
name: 'sort'
|
||||
}]
|
||||
};
|
||||
|
||||
|
|
|
@ -40,10 +40,10 @@ var tfConfig = {
|
|||
col_0: 'multiple',
|
||||
col_1: 'select',
|
||||
col_2: 'checklist',
|
||||
col_number_format: [
|
||||
null, null, 'US',
|
||||
'US', 'US', 'US',
|
||||
'US', 'US', 'US'
|
||||
col_types: [
|
||||
'string', 'string', 'number',
|
||||
'number', 'number', 'number',
|
||||
'number', 'number', 'number'
|
||||
],
|
||||
filters_row_index: 3,
|
||||
headers_row_index: 2,
|
||||
|
@ -58,8 +58,9 @@ var tfConfig = {
|
|||
rows_counter: true,
|
||||
btn_reset: true,
|
||||
status_bar: true,
|
||||
paging: true,
|
||||
paging_length: 15,
|
||||
paging: {
|
||||
length: 15
|
||||
},
|
||||
enable_empty_option: true,
|
||||
enable_non_empty_option: true,
|
||||
enable_default_theme: true,
|
||||
|
|
|
@ -29,21 +29,21 @@ var filtersConfig = {
|
|||
disable_excluded_options: true,
|
||||
col_1: 'multiple',
|
||||
col_2: 'checklist',
|
||||
col_types: [
|
||||
'number', 'string', 'string',
|
||||
'formatted-number', 'none', 'string'
|
||||
],
|
||||
col_widths: [
|
||||
'60px', '160px', '160px',
|
||||
'85px', '70px', '270px'
|
||||
],
|
||||
btn_reset: true,
|
||||
display_all_text: '< Clear >',
|
||||
clear_filter_text: '< Clear >',
|
||||
alternate_rows: true,
|
||||
rows_counter: true,
|
||||
enable_default_theme: true,
|
||||
extensions:[{
|
||||
name: 'sort',
|
||||
types: [
|
||||
'number', 'string', 'string',
|
||||
'US', 'none', 'string'
|
||||
]
|
||||
name: 'sort'
|
||||
}]
|
||||
};
|
||||
|
||||
|
|