mirror of
https://github.com/inventree/InvenTree.git
synced 2025-12-19 13:20:37 -06:00
Compare commits
1642 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8ea2c627c4 | ||
|
|
064a142d76 | ||
|
|
4f1d087654 | ||
|
|
d1cce7df94 | ||
|
|
0e5f10c020 | ||
|
|
1f6cbd7408 | ||
|
|
9e4a599c44 | ||
|
|
e78e649aa9 | ||
|
|
41d6ad2db9 | ||
|
|
38beaff01b | ||
|
|
d348d90fbe | ||
|
|
ae55c81dae | ||
|
|
54bfcff213 | ||
|
|
b2b22762ef | ||
|
|
a18886f196 | ||
|
|
df8d1fb32b | ||
|
|
81ce284264 | ||
|
|
39cfe39172 | ||
|
|
ca6994566d | ||
|
|
2ef8464a83 | ||
|
|
4de6cc3e4f | ||
|
|
b58f7d7461 | ||
|
|
d684ed076b | ||
|
|
d8a74ad8b7 | ||
|
|
f253bf1843 | ||
|
|
f409bfd72b | ||
|
|
8dd8e69c05 | ||
|
|
97b35d9269 | ||
|
|
042956ad48 | ||
|
|
a3f59d8115 | ||
|
|
b76e6a5023 | ||
|
|
3a5014da4b | ||
|
|
8a4597be6a | ||
|
|
fb247c3dd8 | ||
|
|
7dd988a7dc | ||
|
|
120a17241e | ||
|
|
07adee3c51 | ||
|
|
12e43dcc46 | ||
|
|
bc57845aaa | ||
|
|
8f1e116c0b | ||
|
|
5d6e20d897 | ||
|
|
7dbb6c7c8e | ||
|
|
d6bca4d6ca | ||
|
|
f19a727a02 | ||
|
|
89b3290068 | ||
|
|
083bfe05c0 | ||
|
|
5f81d650e9 | ||
|
|
2e1388475e | ||
|
|
2389bb9621 | ||
|
|
b30754f561 | ||
|
|
b9594db832 | ||
|
|
597ab37ba6 | ||
|
|
1b6843e72d | ||
|
|
87d0d872e0 | ||
|
|
b8509f7533 | ||
|
|
11a17fb9b1 | ||
|
|
eaf42b8abe | ||
|
|
a218b6b351 | ||
|
|
86660a5f17 | ||
|
|
6540fb968e | ||
|
|
c55fa13cdb | ||
|
|
ca1281ee10 | ||
|
|
ff7570aea4 | ||
|
|
1a90106bac | ||
|
|
a95dd86540 | ||
|
|
71c0406cf3 | ||
|
|
945c3c214d | ||
|
|
3d597cc3c3 | ||
|
|
8f1b018f0a | ||
|
|
e51fee081b | ||
|
|
805e8daa57 | ||
|
|
95c5c4b575 | ||
|
|
25e73db455 | ||
|
|
d10ba5edc3 | ||
|
|
6ed604bbb8 | ||
|
|
28a55f0a58 | ||
|
|
a9fffe6a73 | ||
|
|
4af6b65ac3 | ||
|
|
472e2d0acd | ||
|
|
303db293b7 | ||
|
|
d674eacd78 | ||
|
|
adaaa1e81d | ||
|
|
32cde5d8f1 | ||
|
|
04c702e1b8 | ||
|
|
eaf87294ce | ||
|
|
aa35c787b2 | ||
|
|
65bc23a516 | ||
|
|
57e395de71 | ||
|
|
7a7db97914 | ||
|
|
36ac268b96 | ||
|
|
8c9491f3ca | ||
|
|
28585644ea | ||
|
|
8198fad6d5 | ||
|
|
35060ce8dc | ||
|
|
896fa131f0 | ||
|
|
700d522c3c | ||
|
|
8e2d568a42 | ||
|
|
10dd8fad80 | ||
|
|
2e5ec5d249 | ||
|
|
0548bee8ad | ||
|
|
27241e217f | ||
|
|
c5efce36b6 | ||
|
|
41071f0ed4 | ||
|
|
8198e919b1 | ||
|
|
f0713ce01d | ||
|
|
87f25eacc5 | ||
|
|
22e30903b6 | ||
|
|
46a8107be5 | ||
|
|
2cf40c0e0b | ||
|
|
f7ad38dad5 | ||
|
|
5f2e4c3790 | ||
|
|
598e15af46 | ||
|
|
db214dfd73 | ||
|
|
42967905bc | ||
|
|
a2efac4dbe | ||
|
|
f737ad2d38 | ||
|
|
2667dc68d7 | ||
|
|
c4989e59b2 | ||
|
|
6ef4390e29 | ||
|
|
2fa284d37b | ||
|
|
440a5fafea | ||
|
|
11b751323e | ||
|
|
34a334236a | ||
|
|
81b50312e2 | ||
|
|
b89588ffb7 | ||
|
|
303572bc03 | ||
|
|
e3d92e1390 | ||
|
|
eaec85398f | ||
|
|
62c26c881d | ||
|
|
104b9d2d44 | ||
|
|
36955d6d4f | ||
|
|
92ec849fc1 | ||
|
|
bb9e8fa97b | ||
|
|
911727f7b7 | ||
|
|
bc2f131144 | ||
|
|
b8856476be | ||
|
|
37a43c8657 | ||
|
|
85f635c975 | ||
|
|
3af704c453 | ||
|
|
55c1ea750c | ||
|
|
cae6fb6731 | ||
|
|
4613730a19 | ||
|
|
74f25ee7f9 | ||
|
|
e6c0a04131 | ||
|
|
1eeaa17085 | ||
|
|
c3c5a86ea5 | ||
|
|
ffbdb0317f | ||
|
|
90d2265dae | ||
|
|
73892e894e | ||
|
|
cd8214ff4a | ||
|
|
baaabd00d6 | ||
|
|
672936e2d4 | ||
|
|
97ed70502c | ||
|
|
35cd3923a5 | ||
|
|
59e7474f75 | ||
|
|
8615cad711 | ||
|
|
fa540c0096 | ||
|
|
eabb99053c | ||
|
|
5c4781b5b4 | ||
|
|
406dbc36fa | ||
|
|
07f7b46a8a | ||
|
|
06552832cb | ||
|
|
6b1696f8df | ||
|
|
7829e3654c | ||
|
|
9c2e959ab0 | ||
|
|
86e675dbbd | ||
|
|
f6d8a3f229 | ||
|
|
2c1d585f8b | ||
|
|
b17c3f6e0f | ||
|
|
27f7745231 | ||
|
|
19329a9627 | ||
|
|
ce866c6d30 | ||
|
|
146dae6d43 | ||
|
|
4be1b2928b | ||
|
|
72899530cf | ||
|
|
9dcc23ee4e | ||
|
|
3a75682878 | ||
|
|
5aa11063a0 | ||
|
|
2037474fad | ||
|
|
02d17378a6 | ||
|
|
6ab34c3d0e | ||
|
|
d44ad541eb | ||
|
|
0b4f732160 | ||
|
|
54d0c4e8a8 | ||
|
|
ee0df29af0 | ||
|
|
60c6d6d33e | ||
|
|
5d6def75cc | ||
|
|
109307858a | ||
|
|
7d6055f06f | ||
|
|
788d497e87 | ||
|
|
cd016d3f7d | ||
|
|
e0a744b01d | ||
|
|
9c2d13b487 | ||
|
|
0da2682c68 | ||
|
|
d5a374f1fd | ||
|
|
9149aa1536 | ||
|
|
946d824995 | ||
|
|
6f89f7dc7f | ||
|
|
b6f9590d55 | ||
|
|
988d263ef1 | ||
|
|
471ece136e | ||
|
|
78826dc20b | ||
|
|
2a4b9c6c5c | ||
|
|
c3bfcad4df | ||
|
|
ca1985e11a | ||
|
|
9a35293126 | ||
|
|
ed5ad3c047 | ||
|
|
c849f618d5 | ||
|
|
b7ae95686e | ||
|
|
f9149d041e | ||
|
|
116d966d29 | ||
|
|
771b2117c4 | ||
|
|
8f1af0f5f9 | ||
|
|
69a3ad3c5b | ||
|
|
23cc3d9b06 | ||
|
|
c31b30bf83 | ||
|
|
f7ed48809c | ||
|
|
eeca3effee | ||
|
|
98017cb8bd | ||
|
|
de86418ede | ||
|
|
017c1ece89 | ||
|
|
d8f9f95cb4 | ||
|
|
ef1e598341 | ||
|
|
513142d487 | ||
|
|
592e87941d | ||
|
|
f5fd0fc5be | ||
|
|
af50e29e2c | ||
|
|
ec1e646de0 | ||
|
|
ab75f85555 | ||
|
|
05fae4be87 | ||
|
|
f4debeac47 | ||
|
|
7a0e1e9574 | ||
|
|
59b3d8b5bc | ||
|
|
25cced3b90 | ||
|
|
9fa13aeae3 | ||
|
|
89e63df1fb | ||
|
|
6fc275ca30 | ||
|
|
142a1462bb | ||
|
|
852a680c09 | ||
|
|
90d34cdfcf | ||
|
|
2f88cc9fe0 | ||
|
|
4f1b469cee | ||
|
|
8e0dfa9c6f | ||
|
|
64ae181bf6 | ||
|
|
5a8804f4bc | ||
|
|
c1956d13b5 | ||
|
|
71f3662ebc | ||
|
|
0aeb7ddbdf | ||
|
|
41c4a5376b | ||
|
|
6930456e2b | ||
|
|
8556bdec6a | ||
|
|
0faba39dfc | ||
|
|
7e829e88af | ||
|
|
3d0012a3bf | ||
|
|
b5bc8935a2 | ||
|
|
f8bb357e6c | ||
|
|
50460ed527 | ||
|
|
13b64c6a04 | ||
|
|
1767ef7a3a | ||
|
|
82903d2bd6 | ||
|
|
4cd54203a2 | ||
|
|
37baec2db6 | ||
|
|
32133fecc7 | ||
|
|
316dfe5e38 | ||
|
|
92ac93aac5 | ||
|
|
ca4d3df287 | ||
|
|
68fb599c73 | ||
|
|
7349b396ca | ||
|
|
561516e8d7 | ||
|
|
476cc5f661 | ||
|
|
badc306f33 | ||
|
|
783a94acce | ||
|
|
84c0ec7937 | ||
|
|
303157c586 | ||
|
|
685a58b807 | ||
|
|
89c7f72caf | ||
|
|
25d90f643a | ||
|
|
e67327953d | ||
|
|
4dcd6ee136 | ||
|
|
ab62f8fc59 | ||
|
|
b5b882d3b6 | ||
|
|
d52278e423 | ||
|
|
da079b23dc | ||
|
|
ccda637e3c | ||
|
|
a4267f76e8 | ||
|
|
2bbc65cc59 | ||
|
|
2f5e3efada | ||
|
|
1807ba4e7b | ||
|
|
30e24f19d9 | ||
|
|
cdde0f8c43 | ||
|
|
2b1d6c268c | ||
|
|
18b3fd3256 | ||
|
|
aefd70ce49 | ||
|
|
eb2422e6be | ||
|
|
a45902bd4f | ||
|
|
dd77cc00b7 | ||
|
|
e5406cae24 | ||
|
|
97103207b6 | ||
|
|
b27ecf54d7 | ||
|
|
318c16f321 | ||
|
|
f1373e3bea | ||
|
|
e1a8772af6 | ||
|
|
d34383e842 | ||
|
|
2f77007dbe | ||
|
|
411e7507a3 | ||
|
|
3c4cfc9a1b | ||
|
|
2bb669d7de | ||
|
|
b8b3a933ab | ||
|
|
2280558303 | ||
|
|
3b01614a38 | ||
|
|
1f5d81ef93 | ||
|
|
cebba6909c | ||
|
|
3336eb9f45 | ||
|
|
0da9e0f834 | ||
|
|
502702b3bc | ||
|
|
bb6b3b965d | ||
|
|
fcfd1f82d6 | ||
|
|
19c036f50a | ||
|
|
ad11681369 | ||
|
|
bc3fda71a4 | ||
|
|
96a3f2920d | ||
|
|
099f56e779 | ||
|
|
402301e165 | ||
|
|
190610c6c9 | ||
|
|
732405f738 | ||
|
|
a7ee323035 | ||
|
|
ad977d4d26 | ||
|
|
3831a04ce0 | ||
|
|
eac53c836c | ||
|
|
2cd6c0b9fc | ||
|
|
8d9cfd3678 | ||
|
|
a2c3c1086c | ||
|
|
ff71fe6e93 | ||
|
|
861a2982db | ||
|
|
54c1f2d9a2 | ||
|
|
459cc03aae | ||
|
|
80173a9d43 | ||
|
|
bdea29df04 | ||
|
|
7b332d93ee | ||
|
|
518ad3eb60 | ||
|
|
0f199556df | ||
|
|
3a266cf322 | ||
|
|
d65d76653c | ||
|
|
08bab0a1b9 | ||
|
|
481eeb8338 | ||
|
|
e0e82dabf3 | ||
|
|
10691b3d03 | ||
|
|
8a1f358505 | ||
|
|
b7f30ee136 | ||
|
|
d90edab59f | ||
|
|
d1d5b45073 | ||
|
|
daab81fa2c | ||
|
|
f386039d5a | ||
|
|
af70dd5dfe | ||
|
|
51c194fb8f | ||
|
|
6994af411d | ||
|
|
5209a7a1b0 | ||
|
|
0152ae79ef | ||
|
|
3b98d85143 | ||
|
|
46586ba960 | ||
|
|
cfd1194a6e | ||
|
|
9b7977a217 | ||
|
|
5f2ca784c9 | ||
|
|
3d7676e292 | ||
|
|
25b7f0b7ee | ||
|
|
3d245c7ce3 | ||
|
|
134a688e4a | ||
|
|
8ac23c080f | ||
|
|
fa3d2d005a | ||
|
|
da70af7ba0 | ||
|
|
930f903f5d | ||
|
|
16bea33695 | ||
|
|
dedc25d681 | ||
|
|
82433db1ea | ||
|
|
2047b2d7d7 | ||
|
|
2e91f64a96 | ||
|
|
1ad4b8b131 | ||
|
|
a63219466f | ||
|
|
9757fb06db | ||
|
|
ab4e6548dc | ||
|
|
351a55c9ea | ||
|
|
e1c503836d | ||
|
|
8cafaa0e62 | ||
|
|
6064c6ceb5 | ||
|
|
b559816dca | ||
|
|
ba94b340dd | ||
|
|
beccbe8583 | ||
|
|
07be08d0f3 | ||
|
|
4bb9c4c559 | ||
|
|
2d9b75c6ae | ||
|
|
3aecb110a5 | ||
|
|
503d5a41b1 | ||
|
|
e943681baa | ||
|
|
0559946791 | ||
|
|
22a8e82108 | ||
|
|
a72ed11cb0 | ||
|
|
1a15b46d65 | ||
|
|
0068cd9825 | ||
|
|
290c0eb225 | ||
|
|
bc31cc550e | ||
|
|
f40a9d1dad | ||
|
|
218971e6f1 | ||
|
|
38dcdcad54 | ||
|
|
aeba189c1a | ||
|
|
09400fd66b | ||
|
|
f3e3838782 | ||
|
|
c7586b83e9 | ||
|
|
b23017b6bf | ||
|
|
a6a58b299b | ||
|
|
b2c302cb2c | ||
|
|
126e42a022 | ||
|
|
22b30a0626 | ||
|
|
84362ea790 | ||
|
|
7be158722f | ||
|
|
f563ac0dcb | ||
|
|
16d261d956 | ||
|
|
15817ce61a | ||
|
|
dedd87f20a | ||
|
|
fcd588cb01 | ||
|
|
6ebce2b2fd | ||
|
|
544e217347 | ||
|
|
9f599546de | ||
|
|
408c6a4040 | ||
|
|
2c7e0bd321 | ||
|
|
96b79397c2 | ||
|
|
ca1526405b | ||
|
|
80019a3ed8 | ||
|
|
d907136264 | ||
|
|
fbd21827fb | ||
|
|
4ddabb82ef | ||
|
|
c1da4d5207 | ||
|
|
a3c0d35b20 | ||
|
|
7c7055c0a0 | ||
|
|
dd9584f487 | ||
|
|
8459de7be2 | ||
|
|
b73044cd44 | ||
|
|
55bfdc000b | ||
|
|
5f3816212f | ||
|
|
a6dab76ff6 | ||
|
|
59d9e430b4 | ||
|
|
774e26eb19 | ||
|
|
f8e36c5468 | ||
|
|
9b7b65374d | ||
|
|
7bcf9bf5be | ||
|
|
d0b3e0bd7c | ||
|
|
04858f8835 | ||
|
|
78990a99e5 | ||
|
|
fe69a8e7bb | ||
|
|
002b896384 | ||
|
|
e128410d54 | ||
|
|
81e38eeb14 | ||
|
|
861e30e8d6 | ||
|
|
03cc6892ea | ||
|
|
3678c940eb | ||
|
|
c0f1966a2c | ||
|
|
c92bb78ae8 | ||
|
|
e63622341f | ||
|
|
669a76c921 | ||
|
|
4cbf2099fa | ||
|
|
046a00026c | ||
|
|
47a4ab2ed8 | ||
|
|
27ca84fd2a | ||
|
|
ae4bfd07da | ||
|
|
8384071ac3 | ||
|
|
1bf696a052 | ||
|
|
835451c02c | ||
|
|
75cdc7eb59 | ||
|
|
9ad54c6579 | ||
|
|
d2c1e9e2b0 | ||
|
|
0d052334e2 | ||
|
|
fdf57891fc | ||
|
|
009adaf528 | ||
|
|
cfc15582c1 | ||
|
|
22220493bd | ||
|
|
95cc3d2a7a | ||
|
|
4292a32ab9 | ||
|
|
68b9a690f2 | ||
|
|
8537dc2a85 | ||
|
|
c44205273c | ||
|
|
e4d10279fa | ||
|
|
01481ef5c9 | ||
|
|
e63342418f | ||
|
|
50987f47b0 | ||
|
|
4bd0872b2c | ||
|
|
203062a67a | ||
|
|
34b40d205e | ||
|
|
5245442b11 | ||
|
|
e6f56cb056 | ||
|
|
71681bfda1 | ||
|
|
1ad7e699a9 | ||
|
|
0ec880290b | ||
|
|
7215a563b1 | ||
|
|
616f17d08a | ||
|
|
d6cad372db | ||
|
|
865a6db828 | ||
|
|
174c4cc591 | ||
|
|
251a23d127 | ||
|
|
cab87a6860 | ||
|
|
b93ba6339a | ||
|
|
c3dcabcaad | ||
|
|
05be4da25c | ||
|
|
70c5b27d22 | ||
|
|
b78fe88c26 | ||
|
|
3d8c059a43 | ||
|
|
5018f899f7 | ||
|
|
51074ef02b | ||
|
|
b121262af1 | ||
|
|
9cb039f685 | ||
|
|
6b73c9e020 | ||
|
|
1cfe445897 | ||
|
|
bf296057b3 | ||
|
|
eb21a9027f | ||
|
|
2f6d03388d | ||
|
|
fc6cad475a | ||
|
|
e8c402ecd9 | ||
|
|
1c67bb6026 | ||
|
|
0bdb62f263 | ||
|
|
fce8e3fe05 | ||
|
|
05e4c8f825 | ||
|
|
18b7baa5b9 | ||
|
|
59dfac2c9b | ||
|
|
915bbef3b4 | ||
|
|
1cc0977816 | ||
|
|
02b0c0831d | ||
|
|
6cb017bbfd | ||
|
|
5f318799c1 | ||
|
|
8ace71ef56 | ||
|
|
b9799e1824 | ||
|
|
e30f6ec374 | ||
|
|
cd0e66e3c6 | ||
|
|
e9ed50fc4b | ||
|
|
66f2c01d5d | ||
|
|
bc8b3a68f0 | ||
|
|
69c748d018 | ||
|
|
95d07cd02b | ||
|
|
4d992ea528 | ||
|
|
f791ac9f57 | ||
|
|
badf9230a9 | ||
|
|
8c8b704e38 | ||
|
|
17f241774f | ||
|
|
1e94a597c9 | ||
|
|
c54cb2b280 | ||
|
|
b3ca6a6ca7 | ||
|
|
327682b719 | ||
|
|
82fe497787 | ||
|
|
05856a72cf | ||
|
|
e23a9c1269 | ||
|
|
3b53437f46 | ||
|
|
608057da69 | ||
|
|
e02b692ab2 | ||
|
|
197a3e6731 | ||
|
|
fd11317409 | ||
|
|
45556058d2 | ||
|
|
247cfcc514 | ||
|
|
d6a56da441 | ||
|
|
a17ab9bfbd | ||
|
|
fe99e92bfc | ||
|
|
f3c71bd96f | ||
|
|
880a0a4af8 | ||
|
|
df91b8cf4d | ||
|
|
2bb9fd9955 | ||
|
|
f39f5e5825 | ||
|
|
e550831efa | ||
|
|
8a99062704 | ||
|
|
a6ad263ee7 | ||
|
|
40735d66a1 | ||
|
|
7190a8ef69 | ||
|
|
6552d011a4 | ||
|
|
3df8f33080 | ||
|
|
3d0bea15ae | ||
|
|
4cb97b1340 | ||
|
|
498ad4162c | ||
|
|
08d177e55f | ||
|
|
8fae32e3c7 | ||
|
|
0ccac09962 | ||
|
|
10762fc1cf | ||
|
|
ea88a03b5a | ||
|
|
2d6c531fda | ||
|
|
0652579312 | ||
|
|
0a78432a0f | ||
|
|
72cfaccac5 | ||
|
|
5b5b848a98 | ||
|
|
0f00205256 | ||
|
|
007d2d4054 | ||
|
|
16aa18429a | ||
|
|
cf9891398a | ||
|
|
5167f54268 | ||
|
|
6175c5408c | ||
|
|
d3758981b4 | ||
|
|
b619f26074 | ||
|
|
08903f357e | ||
|
|
cebfe9a30f | ||
|
|
41eff97c7c | ||
|
|
65f081d252 | ||
|
|
30b5f7d507 | ||
|
|
7143c32fc9 | ||
|
|
db01f3646a | ||
|
|
cedf9a9108 | ||
|
|
dcf00d816d | ||
|
|
a0e1645ca1 | ||
|
|
c26d22e599 | ||
|
|
f5b97da5d2 | ||
|
|
366d4b2143 | ||
|
|
2369b40bbf | ||
|
|
5d716d0cdd | ||
|
|
d4fa7d936e | ||
|
|
2948dad831 | ||
|
|
0851a925fb | ||
|
|
4615fbc0d8 | ||
|
|
e4872d9f67 | ||
|
|
c371a9a8e8 | ||
|
|
b9d0c60189 | ||
|
|
c6cff1fb2e | ||
|
|
18fbe2d2dd | ||
|
|
ac5e26ca0e | ||
|
|
a86513e15d | ||
|
|
d2a7961895 | ||
|
|
65fe6bfaca | ||
|
|
3151612962 | ||
|
|
693af04c11 | ||
|
|
4b1d96faad | ||
|
|
645e5b6449 | ||
|
|
99af98c5a7 | ||
|
|
cfc0145180 | ||
|
|
a7e5a79f4c | ||
|
|
17d0a015f2 | ||
|
|
f36c5137dd | ||
|
|
c5999f7186 | ||
|
|
f71bfb5700 | ||
|
|
00ccfdf11b | ||
|
|
5747a07f7b | ||
|
|
003b384abd | ||
|
|
ea0d32fd3d | ||
|
|
14132a6efa | ||
|
|
64f6238351 | ||
|
|
e83a0610af | ||
|
|
6318b0bddf | ||
|
|
0933cf859c | ||
|
|
bbf6f6f93d | ||
|
|
7afb2a6a75 | ||
|
|
ec3e02af94 | ||
|
|
15011b7b20 | ||
|
|
9823c3c81b | ||
|
|
ce610490b6 | ||
|
|
1c3878829d | ||
|
|
78cc3a9cf3 | ||
|
|
436fb3bf30 | ||
|
|
2c44104c9c | ||
|
|
461d694cf8 | ||
|
|
779c1e1d58 | ||
|
|
5a100962fe | ||
|
|
c505080527 | ||
|
|
53149ab4dc | ||
|
|
85c948ef67 | ||
|
|
0a5a1ec5fd | ||
|
|
54c903f637 | ||
|
|
7430abc237 | ||
|
|
fe87bba577 | ||
|
|
ca1b9e6403 | ||
|
|
29335fa665 | ||
|
|
45fc8ce6bd | ||
|
|
275cd063e1 | ||
|
|
88e28edba9 | ||
|
|
979cf10c66 | ||
|
|
034f70c491 | ||
|
|
9ee9664e65 | ||
|
|
d59f22febb | ||
|
|
1a4f5cf719 | ||
|
|
af6dd83f05 | ||
|
|
314b8fdbff | ||
|
|
853ba825c0 | ||
|
|
6c3dc2a25d | ||
|
|
2c9b76575e | ||
|
|
23bd9afaf8 | ||
|
|
24a816e9a2 | ||
|
|
b1a642918c | ||
|
|
9b1e387a8b | ||
|
|
5160165669 | ||
|
|
fb0ebbb1e4 | ||
|
|
e998098e4f | ||
|
|
b1d33ef3bc | ||
|
|
873c03376a | ||
|
|
b39283a18f | ||
|
|
26f0338f1d | ||
|
|
0199760e1c | ||
|
|
b89002a450 | ||
|
|
937211288e | ||
|
|
0b2c6cfd70 | ||
|
|
6912899f41 | ||
|
|
b0f4475a83 | ||
|
|
094ef38e27 | ||
|
|
a8e3a3f1ac | ||
|
|
5f6599a950 | ||
|
|
6aa6e3b924 | ||
|
|
24a9a1127b | ||
|
|
a51aab6dd0 | ||
|
|
b44dbba150 | ||
|
|
d276c48fef | ||
|
|
9b2045025b | ||
|
|
cc11df917e | ||
|
|
7fca496de8 | ||
|
|
4197e29fce | ||
|
|
44319d24e4 | ||
|
|
a537b6df6e | ||
|
|
4a60da67fd | ||
|
|
acea0d6e92 | ||
|
|
7fff0a7427 | ||
|
|
68bbb372dc | ||
|
|
af00d88555 | ||
|
|
86c00f54b7 | ||
|
|
a345e0e8da | ||
|
|
753fab2498 | ||
|
|
3b42b5e27e | ||
|
|
087001f29d | ||
|
|
df218a5193 | ||
|
|
36ad69091b | ||
|
|
24ad4f5332 | ||
|
|
f84636fc20 | ||
|
|
351e825ba8 | ||
|
|
2be8aad29c | ||
|
|
ee906fc6e6 | ||
|
|
9add3d4409 | ||
|
|
fb70da0331 | ||
|
|
6bdf371490 | ||
|
|
bbecc8c900 | ||
|
|
b913971008 | ||
|
|
440fe155ff | ||
|
|
abbd0bb16a | ||
|
|
5727e27ae6 | ||
|
|
4fd7b24e3c | ||
|
|
b338834146 | ||
|
|
c621780a2a | ||
|
|
394a9208a1 | ||
|
|
79836c77ef | ||
|
|
db9970e5df | ||
|
|
cb3fe0fc35 | ||
|
|
35f48ed899 | ||
|
|
489dfa1823 | ||
|
|
2b99cf353a | ||
|
|
646dd65d27 | ||
|
|
3685ca4b95 | ||
|
|
5e309a62f7 | ||
|
|
9b882f4d17 | ||
|
|
5167117067 | ||
|
|
3c5ba75d27 | ||
|
|
e768ada83b | ||
|
|
4147163418 | ||
|
|
48c51151c8 | ||
|
|
ae4717401f | ||
|
|
1f4bd95d75 | ||
|
|
0892b160c6 | ||
|
|
72c43d0c2d | ||
|
|
50dbebdf59 | ||
|
|
81f789d857 | ||
|
|
4f0efec39f | ||
|
|
77361bd0a2 | ||
|
|
01a68270ea | ||
|
|
181d1d6b91 | ||
|
|
912a3c4b99 | ||
|
|
b0891c921c | ||
|
|
c11b433d94 | ||
|
|
2f0bbecc3d | ||
|
|
4e40d92db7 | ||
|
|
66b6036827 | ||
|
|
d5f3498238 | ||
|
|
b351976ae9 | ||
|
|
c5b93e2392 | ||
|
|
41a4033427 | ||
|
|
429bb688d0 | ||
|
|
9d25ed335c | ||
|
|
ba1d2063af | ||
|
|
77471cb89c | ||
|
|
b45fec221c | ||
|
|
2c9b112562 | ||
|
|
e5fa94b4f8 | ||
|
|
e384f9e94c | ||
|
|
435c13cf7c | ||
|
|
7f020cbbf6 | ||
|
|
426aa9258c | ||
|
|
5d71cf85cc | ||
|
|
c9ea33e22e | ||
|
|
d59c6711bb | ||
|
|
1a0f091e0c | ||
|
|
a803f21e0c | ||
|
|
6112be2df0 | ||
|
|
2a4e903785 | ||
|
|
26d1a25f31 | ||
|
|
fd42149f67 | ||
|
|
6ab03bd05a | ||
|
|
2972aec759 | ||
|
|
b70e79b778 | ||
|
|
5d1754ec32 | ||
|
|
eb7b49784b | ||
|
|
d9698b10cc | ||
|
|
1373425c29 | ||
|
|
2cb1b076f6 | ||
|
|
3a71a4f63a | ||
|
|
6dd79af0b6 | ||
|
|
12daf15406 | ||
|
|
372958d939 | ||
|
|
808a636484 | ||
|
|
79ea744280 | ||
|
|
09ccd6c5e2 | ||
|
|
b75c343236 | ||
|
|
15166c7797 | ||
|
|
4979c690d9 | ||
|
|
cb636e000d | ||
|
|
b40234e403 | ||
|
|
399dcafede | ||
|
|
2c6e8da90e | ||
|
|
0d1919f10b | ||
|
|
89ede3e103 | ||
|
|
a1376eeb9e | ||
|
|
8052a1989c | ||
|
|
7385099194 | ||
|
|
19cd0707a2 | ||
|
|
a06595c152 | ||
|
|
22c96ad2b7 | ||
|
|
3d2e907d5e | ||
|
|
b204618e79 | ||
|
|
617fbf2f02 | ||
|
|
a2c0c7c76a | ||
|
|
34d3dca8b7 | ||
|
|
9e4d09343c | ||
|
|
0c56079b41 | ||
|
|
e12824df2e | ||
|
|
ce1dd88129 | ||
|
|
ebbcff3c7f | ||
|
|
b2569d5cba | ||
|
|
47ada25315 | ||
|
|
1ebf26ab7c | ||
|
|
627c50e465 | ||
|
|
c7fd22924f | ||
|
|
9f97d81e83 | ||
|
|
5901b21e78 | ||
|
|
974c98c95a | ||
|
|
c5166ec845 | ||
|
|
13c7e2af49 | ||
|
|
87d8b4674a | ||
|
|
85e20041c7 | ||
|
|
ff3cc96e0e | ||
|
|
746e9ab983 | ||
|
|
99fcbcc646 | ||
|
|
fee6246a8f | ||
|
|
4ec5e9a907 | ||
|
|
ef66a3b8f3 | ||
|
|
c49cd9ffde | ||
|
|
654f5d348e | ||
|
|
f5c86bc457 | ||
|
|
57fa69f6e6 | ||
|
|
c72fce0cc5 | ||
|
|
b2c40c91b7 | ||
|
|
0334035e77 | ||
|
|
4b1b9df193 | ||
|
|
6a89e0089d | ||
|
|
5233281a24 | ||
|
|
468eba1759 | ||
|
|
ff91c4ec53 | ||
|
|
3a64d0bc8f | ||
|
|
092215918c | ||
|
|
2621c51a7e | ||
|
|
69b8eed028 | ||
|
|
85d1c585c0 | ||
|
|
51e2f5c46b | ||
|
|
9cb1af9587 | ||
|
|
b580df0d30 | ||
|
|
d953f1a31e | ||
|
|
a28b7df9d4 | ||
|
|
880655c141 | ||
|
|
4f3f78f55a | ||
|
|
6e3f603413 | ||
|
|
4822d3fd63 | ||
|
|
2b70b947ee | ||
|
|
37fcb810e4 | ||
|
|
cbfb3b55e6 | ||
|
|
d6d891c16f | ||
|
|
75fed2ebdd | ||
|
|
91012d2788 | ||
|
|
762d735618 | ||
|
|
cf5af4dc77 | ||
|
|
4a99996be8 | ||
|
|
0ee53758b4 | ||
|
|
206d67337f | ||
|
|
7ab58f683f | ||
|
|
539b000460 | ||
|
|
a66334abf2 | ||
|
|
4594f1e2b8 | ||
|
|
10ee8bc666 | ||
|
|
d19e287cb5 | ||
|
|
610f85597f | ||
|
|
c12a482e4d | ||
|
|
44addc9d7f | ||
|
|
d57fed6142 | ||
|
|
a58e2e84f8 | ||
|
|
4d7407ee51 | ||
|
|
7faa0d199d | ||
|
|
e56c018a4a | ||
|
|
bad56f64e3 | ||
|
|
977316cb3a | ||
|
|
ba4a1fd771 | ||
|
|
277b28a7e9 | ||
|
|
f742f32804 | ||
|
|
94e400d0e1 | ||
|
|
5de85defa7 | ||
|
|
4a615e05ae | ||
|
|
70589b06e1 | ||
|
|
9dd8d09158 | ||
|
|
7c9eb90bea | ||
|
|
cb1298847e | ||
|
|
38fab9c681 | ||
|
|
b286a5e30c | ||
|
|
6725709456 | ||
|
|
653d502a73 | ||
|
|
9ff5032020 | ||
|
|
6c761c2d0f | ||
|
|
3b08b962c1 | ||
|
|
44a820e74f | ||
|
|
fb8c0e5180 | ||
|
|
2cbf7d578b | ||
|
|
47530b7d2a | ||
|
|
0e55911a6b | ||
|
|
90ac3a5a8a | ||
|
|
124fab3eee | ||
|
|
b850beb687 | ||
|
|
2e1c4e9792 | ||
|
|
c76c93237e | ||
|
|
80fa40694c | ||
|
|
9c7813992d | ||
|
|
c17c014b33 | ||
|
|
307dd25f87 | ||
|
|
8fa3d77416 | ||
|
|
8c55831321 | ||
|
|
7c892263f8 | ||
|
|
f88d03db88 | ||
|
|
bced189c64 | ||
|
|
14d18e9d95 | ||
|
|
2506aa110b | ||
|
|
696c101628 | ||
|
|
0001b889f0 | ||
|
|
4e3825682a | ||
|
|
04097791bb | ||
|
|
2695368651 | ||
|
|
00315aa855 | ||
|
|
8b366fc4a0 | ||
|
|
2a4e8cd062 | ||
|
|
d05ba9ee1a | ||
|
|
67d9d2bb04 | ||
|
|
edfb19db13 | ||
|
|
094a0fe709 | ||
|
|
906ed7f64d | ||
|
|
a8ceddc8e4 | ||
|
|
a921b3fcee | ||
|
|
5e2ed06c2d | ||
|
|
3c46e12839 | ||
|
|
04cee99791 | ||
|
|
f1f27f08cb | ||
|
|
f248b20ba4 | ||
|
|
6ed482d9eb | ||
|
|
fd8273e3e2 | ||
|
|
a65c80dbf3 | ||
|
|
7503596ea4 | ||
|
|
59778130cd | ||
|
|
de6f2d37e5 | ||
|
|
2560613e36 | ||
|
|
f5a8b110b5 | ||
|
|
0f90e8f522 | ||
|
|
e1c6ad7761 | ||
|
|
e86bc4fa6d | ||
|
|
f06078f4ac | ||
|
|
5e706554b1 | ||
|
|
d606df16f7 | ||
|
|
57d7d729c1 | ||
|
|
e69f6c5b9d | ||
|
|
0fc353ebac | ||
|
|
6a0c5b78ae | ||
|
|
ba7c0bdea0 | ||
|
|
5d70f496a5 | ||
|
|
f0ffb0f8c0 | ||
|
|
a4e45eab13 | ||
|
|
2f71233d62 | ||
|
|
e3102900b6 | ||
|
|
393597612e | ||
|
|
dddaa102b3 | ||
|
|
64f8034a4c | ||
|
|
5d2441776e | ||
|
|
c1b59eeaab | ||
|
|
57c5d6c97a | ||
|
|
33ac34cc40 | ||
|
|
41b208992c | ||
|
|
db8d95dba7 | ||
|
|
0c5f23ef98 | ||
|
|
58636139af | ||
|
|
613dd9d471 | ||
|
|
b2565270a5 | ||
|
|
4256d09e80 | ||
|
|
5d141a0b98 | ||
|
|
5aa43a5a18 | ||
|
|
eeeb04c9f4 | ||
|
|
ba2b1ce581 | ||
|
|
57da521833 | ||
|
|
8da71037ae | ||
|
|
47764ca179 | ||
|
|
bc91975f2c | ||
|
|
5b2665edb1 | ||
|
|
41b3f1d39c | ||
|
|
3e5dc65c49 | ||
|
|
58a0f40889 | ||
|
|
1b3f8a9309 | ||
|
|
f6a1ddf8e7 | ||
|
|
5447bc4356 | ||
|
|
5aec63d9e4 | ||
|
|
1a0a4622a2 | ||
|
|
8786776fd6 | ||
|
|
e94592e42d | ||
|
|
623a0844d3 | ||
|
|
d06018cbbe | ||
|
|
95032141ce | ||
|
|
ae9ef04013 | ||
|
|
e0655f61d8 | ||
|
|
1a233e7949 | ||
|
|
16d3a87e78 | ||
|
|
7fb89e4dbe | ||
|
|
97f605ef55 | ||
|
|
1356718bb7 | ||
|
|
4a02a90954 | ||
|
|
b319041dfa | ||
|
|
b8bbd15c88 | ||
|
|
96547b885e | ||
|
|
93eeeec2f3 | ||
|
|
5956b8cdee | ||
|
|
3eb585df27 | ||
|
|
0840cebd57 | ||
|
|
44c10c45cd | ||
|
|
e6f6a936e0 | ||
|
|
751937e0c2 | ||
|
|
071c317bae | ||
|
|
b8f7555a73 | ||
|
|
7c2af32b38 | ||
|
|
92cb7211ce | ||
|
|
535f1994b1 | ||
|
|
87a09a7220 | ||
|
|
a306ad0bc3 | ||
|
|
789712acbe | ||
|
|
bbe714c8f7 | ||
|
|
b28487760a | ||
|
|
bd407cd226 | ||
|
|
2530313e68 | ||
|
|
fce26c80b7 | ||
|
|
791eb63f35 | ||
|
|
94b9878c8d | ||
|
|
efaa532162 | ||
|
|
3559e81d5f | ||
|
|
7ecd843ca4 | ||
|
|
d1a27d2f7e | ||
|
|
84b1960c7b | ||
|
|
7b494c08ec | ||
|
|
c170d3f87a | ||
|
|
bb844ab94b | ||
|
|
c427f173ed | ||
|
|
ca98cbee68 | ||
|
|
7dd749f394 | ||
|
|
b0c1999922 | ||
|
|
cfd32db570 | ||
|
|
9112196b8f | ||
|
|
64a1f0f0a1 | ||
|
|
d739c2dea8 | ||
|
|
511cdef323 | ||
|
|
91b5911ff2 | ||
|
|
1f172b3a57 | ||
|
|
b4d66c6797 | ||
|
|
428b52693a | ||
|
|
7c09c20725 | ||
|
|
cffb921fb1 | ||
|
|
2d3e7e35af | ||
|
|
8b61acb048 | ||
|
|
0cfb293ca9 | ||
|
|
afa31b3415 | ||
|
|
d505e79be8 | ||
|
|
aee0970e49 | ||
|
|
55e831f4b4 | ||
|
|
7ee94f3574 | ||
|
|
b25df586cd | ||
|
|
92f5648656 | ||
|
|
ccb637773f | ||
|
|
d4da6211be | ||
|
|
fb94949538 | ||
|
|
f5150f549a | ||
|
|
6e65a736e7 | ||
|
|
d17056820b | ||
|
|
7e8664a4dd | ||
|
|
aada5f91cb | ||
|
|
40822a93df | ||
|
|
67a73c1fbf | ||
|
|
9d19029ba9 | ||
|
|
c31b72bde2 | ||
|
|
6919eaa1e1 | ||
|
|
124967ed31 | ||
|
|
570010b99c | ||
|
|
4c96b34c7c | ||
|
|
f07f3b99cf | ||
|
|
b0edd0eb05 | ||
|
|
cdc4c5d6d5 | ||
|
|
34c097c46a | ||
|
|
e28fe6df6a | ||
|
|
0dc6d9d37e | ||
|
|
5aec3df7c9 | ||
|
|
06f28898a0 | ||
|
|
e8e0ab8416 | ||
|
|
5b77ff4a4c | ||
|
|
00eada2c3c | ||
|
|
c0650ba7f4 | ||
|
|
713d7960a8 | ||
|
|
6a78f6d451 | ||
|
|
41bbbdcd43 | ||
|
|
57123283f4 | ||
|
|
864a21ac85 | ||
|
|
99efbd4c40 | ||
|
|
dae45875fb | ||
|
|
d0f71ea6de | ||
|
|
b162c97226 | ||
|
|
ed6abcdf32 | ||
|
|
c6f069028c | ||
|
|
b6bc5e3bff | ||
|
|
dc0a0f9dc2 | ||
|
|
25caec4c53 | ||
|
|
7d095213a5 | ||
|
|
672119fb92 | ||
|
|
7f269898c4 | ||
|
|
82be9db3df | ||
|
|
90aa205057 | ||
|
|
4a259dc146 | ||
|
|
5af2fae120 | ||
|
|
834f80698b | ||
|
|
56a6943438 | ||
|
|
cc41752f9f | ||
|
|
a661d7e1a6 | ||
|
|
02ec1d4fa2 | ||
|
|
3b6ed585ab | ||
|
|
01f1ac49e3 | ||
|
|
5207b2ba21 | ||
|
|
6fd0380196 | ||
|
|
15bc457714 | ||
|
|
3fd0cf67b6 | ||
|
|
daa8496157 | ||
|
|
d8f5255572 | ||
|
|
1259cea2c3 | ||
|
|
6731bc1b06 | ||
|
|
d51ac2f5c2 | ||
|
|
a147ce4284 | ||
|
|
8186e4bab0 | ||
|
|
2b08b0f2b9 | ||
|
|
7424cc1352 | ||
|
|
febd1ad4a7 | ||
|
|
8eaaf62eda | ||
|
|
41259f520f | ||
|
|
b2cb41f879 | ||
|
|
c04aa1bff7 | ||
|
|
3b9f57fc80 | ||
|
|
33ffa2f75f | ||
|
|
388b26dad2 | ||
|
|
1fa4e7f5fb | ||
|
|
4a2fa36e30 | ||
|
|
ef83480f65 | ||
|
|
6c415bc922 | ||
|
|
068c237c6e | ||
|
|
826102e10e | ||
|
|
066d69215f | ||
|
|
49118d8083 | ||
|
|
49d5573f8b | ||
|
|
9e456f5a11 | ||
|
|
0f4d60dceb | ||
|
|
23aebab6d0 | ||
|
|
e483b42df6 | ||
|
|
3715c5d637 | ||
|
|
ae4ebab957 | ||
|
|
4f266958e3 | ||
|
|
750dfcda07 | ||
|
|
0071a29af7 | ||
|
|
64c567474a | ||
|
|
8a42d9f2fa | ||
|
|
5261e96c8a | ||
|
|
0498fd633a | ||
|
|
f03f6c4386 | ||
|
|
0a9f2b37cf | ||
|
|
7dcc94b106 | ||
|
|
33d21594da | ||
|
|
6d80788618 | ||
|
|
8dd8505a2c | ||
|
|
05f7b30ab0 | ||
|
|
d9d21395d9 | ||
|
|
63b70614b6 | ||
|
|
34c3320cd5 | ||
|
|
50fee1bfe5 | ||
|
|
28e9d842bf | ||
|
|
d04fb0d826 | ||
|
|
520b8d7b2b | ||
|
|
c287a0a0b9 | ||
|
|
265ed5115a | ||
|
|
53d1040875 | ||
|
|
5ae7ca71d7 | ||
|
|
564635c368 | ||
|
|
3479528d5b | ||
|
|
31d6c77143 | ||
|
|
882bda46b4 | ||
|
|
f865573e48 | ||
|
|
434d084371 | ||
|
|
bb4c25ba68 | ||
|
|
eecc435c02 | ||
|
|
55aa63dab4 | ||
|
|
77c950a729 | ||
|
|
dee47bdea8 | ||
|
|
8ea1086b03 | ||
|
|
e0e996a6c3 | ||
|
|
d4fe83170f | ||
|
|
534b60d4b8 | ||
|
|
725eb3c538 | ||
|
|
17c10da10e | ||
|
|
a82e219336 | ||
|
|
1327c1d3b1 | ||
|
|
356624a8fb | ||
|
|
66c1a2ef57 | ||
|
|
cb29ff14e0 | ||
|
|
3e977834c5 | ||
|
|
4bd4f2a0a3 | ||
|
|
2949289fab | ||
|
|
bb0c991965 | ||
|
|
0939ffeb76 | ||
|
|
41336bd549 | ||
|
|
d059aff4f8 | ||
|
|
9cef038d6a | ||
|
|
356b6cf15b | ||
|
|
8412885e09 | ||
|
|
4b8e44bc4a | ||
|
|
ef7fca5633 | ||
|
|
244d364575 | ||
|
|
aa210efad6 | ||
|
|
91ca37c84b | ||
|
|
d62d4c9355 | ||
|
|
d0a7a24649 | ||
|
|
5264f816f1 | ||
|
|
22e3dc634b | ||
|
|
2a7bf94793 | ||
|
|
868e005445 | ||
|
|
d827070585 | ||
|
|
e379b44606 | ||
|
|
e03d8f55ea | ||
|
|
d71fd1aad4 | ||
|
|
38be1fc696 | ||
|
|
40037d826d | ||
|
|
a022b8223e | ||
|
|
23c0d68330 | ||
|
|
1a32e441b7 | ||
|
|
20273f1541 | ||
|
|
f88f5a39f8 | ||
|
|
1bdcbd1974 | ||
|
|
908e2ef8bc | ||
|
|
b46151b406 | ||
|
|
0f92468462 | ||
|
|
7ec194a14a | ||
|
|
a7846940c4 | ||
|
|
51fab36074 | ||
|
|
ca9f9e047c | ||
|
|
1f71a93d88 | ||
|
|
88ec40e454 | ||
|
|
f0933f216c | ||
|
|
919662054c | ||
|
|
b9dda51378 | ||
|
|
3c3ae43c18 | ||
|
|
c546ed5dcd | ||
|
|
da01177d23 | ||
|
|
aa2f63830e | ||
|
|
596d06cf1a | ||
|
|
8d924eb300 | ||
|
|
b5f8635794 | ||
|
|
442c6e8b27 | ||
|
|
07f3cd5644 | ||
|
|
8275271ea0 | ||
|
|
bd653f2c49 | ||
|
|
f217650cec | ||
|
|
561cc7a1dd | ||
|
|
a064ce13fc | ||
|
|
e103bd8880 | ||
|
|
07de23f120 | ||
|
|
3def3e1e89 | ||
|
|
61897cb0fc | ||
|
|
067d2be1f0 | ||
|
|
3098f6045f | ||
|
|
740d7678d7 | ||
|
|
b3ec748123 | ||
|
|
881931c6b1 | ||
|
|
d2d5909701 | ||
|
|
43d47686c5 | ||
|
|
dae74a19d3 | ||
|
|
84f8bab418 | ||
|
|
720485709b | ||
|
|
fa480c0558 | ||
|
|
17c048e8dd | ||
|
|
a0534dafec | ||
|
|
6bbe818e9c | ||
|
|
89e60d28b0 | ||
|
|
6cd3b3176c | ||
|
|
fafd0397bc | ||
|
|
71c1faf9ff | ||
|
|
a257f94ac0 | ||
|
|
25e5a64cee | ||
|
|
5e9b012031 | ||
|
|
0ef033f800 | ||
|
|
1a6f06cceb | ||
|
|
f6d5f2e426 | ||
|
|
d5d4cbaec1 | ||
|
|
ef37eada2f | ||
|
|
000229fcbb | ||
|
|
ee063f7508 | ||
|
|
f648433e53 | ||
|
|
0e57a4caec | ||
|
|
bd2a2b5b26 | ||
|
|
9946fbda17 | ||
|
|
bacdb7776b | ||
|
|
6afb657acb | ||
|
|
98374ca466 | ||
|
|
173e1311d4 | ||
|
|
2152cb14b4 | ||
|
|
7f2804dff3 | ||
|
|
3f172cb065 | ||
|
|
cbdea9f18c | ||
|
|
b29e1ded64 | ||
|
|
4b599b7cdb | ||
|
|
7e6c5fae62 | ||
|
|
8b2f1b9313 | ||
|
|
64db28be67 | ||
|
|
381becef79 | ||
|
|
a1f33c4084 | ||
|
|
dbdbe69f7f | ||
|
|
9da8189899 | ||
|
|
e4bfe43c04 | ||
|
|
003a2d9f3c | ||
|
|
0ea8ade26c | ||
|
|
75774771dc | ||
|
|
20755a6dac | ||
|
|
4e1b9efe93 | ||
|
|
6e90ac367e | ||
|
|
400941c10f | ||
|
|
81a226c760 | ||
|
|
5ffbfe8eb8 | ||
|
|
6e54c49142 | ||
|
|
39f9aa6141 | ||
|
|
16f3dfb678 | ||
|
|
b7473be8ef | ||
|
|
2261973331 | ||
|
|
789515e39d | ||
|
|
0effb584b9 | ||
|
|
339126b27a | ||
|
|
56255a98d8 | ||
|
|
d66c20ca94 | ||
|
|
ce6f54aeaa | ||
|
|
b92b7dc825 | ||
|
|
1887463f7f | ||
|
|
f7a1fb0a9d | ||
|
|
54855da522 | ||
|
|
d1c7877713 | ||
|
|
be96a2f7e3 | ||
|
|
871b853b9f | ||
|
|
cfe33deb8e | ||
|
|
f49130862e | ||
|
|
ab25a199ce | ||
|
|
e891f117f3 | ||
|
|
bed2cec5e7 | ||
|
|
b870728125 | ||
|
|
225ad0ffa6 | ||
|
|
427f47310b | ||
|
|
3e2a5263a5 | ||
|
|
b876d31d09 | ||
|
|
30de734dfa | ||
|
|
f2eb66d854 | ||
|
|
fcba00bc69 | ||
|
|
b56a1ade24 | ||
|
|
fcb47fce09 | ||
|
|
eaf910d263 | ||
|
|
9be528a3fb | ||
|
|
40acf90efe | ||
|
|
0ae2fd9246 | ||
|
|
37e9bd8d9b | ||
|
|
3cc79d6def | ||
|
|
0e0405f337 | ||
|
|
6d628a77fd | ||
|
|
fb75617807 | ||
|
|
14217ff648 | ||
|
|
d247ea7589 | ||
|
|
7c1615a2b6 | ||
|
|
41c07fc423 | ||
|
|
52ec213a28 | ||
|
|
cc1e580538 | ||
|
|
b1380687e6 | ||
|
|
8d92960f10 | ||
|
|
0d68dbcfa7 | ||
|
|
21e369e6cc | ||
|
|
d6749dfb24 | ||
|
|
2a31820abe | ||
|
|
ae2e2f36e4 | ||
|
|
2046c12600 | ||
|
|
df41fafefb | ||
|
|
8eaff6a353 | ||
|
|
fcbf0e6e93 | ||
|
|
6f54091354 | ||
|
|
5a9e5dea20 | ||
|
|
508a3fc35c | ||
|
|
b3ea2bfb9a | ||
|
|
3c98cd87a7 | ||
|
|
cf2abb4130 | ||
|
|
41d91a8f9b | ||
|
|
45a321694b | ||
|
|
8a995cc193 | ||
|
|
119cccc318 | ||
|
|
600eac7f1d | ||
|
|
a77fd23fcf | ||
|
|
cb77506111 | ||
|
|
c5a82f4b6e | ||
|
|
4a0be0dfb8 | ||
|
|
0a7ea27e9f | ||
|
|
7c328969c9 | ||
|
|
774872e6a6 | ||
|
|
08f958dd72 | ||
|
|
94cd28ecb9 | ||
|
|
b5b7dc0fbf | ||
|
|
f90aa1d2cf | ||
|
|
1cffd41c07 | ||
|
|
d40fc59616 | ||
|
|
a9d1cadc12 | ||
|
|
55ebf48684 | ||
|
|
628a58e8fc | ||
|
|
fedbb834ee | ||
|
|
420b16104c | ||
|
|
b46d1c2286 | ||
|
|
5107cf5694 | ||
|
|
9af9158c10 | ||
|
|
19522dfb92 | ||
|
|
b9155bbde9 | ||
|
|
2986e995d1 | ||
|
|
e781202daa | ||
|
|
56dda5eff4 | ||
|
|
f64758eb03 | ||
|
|
180df8f110 | ||
|
|
4746a3ccff | ||
|
|
2c1a744c2d | ||
|
|
098cd0ec44 | ||
|
|
02e71bd2ce | ||
|
|
3e33326120 | ||
|
|
8d4e2ce498 | ||
|
|
d0ad3f0e37 | ||
|
|
ee6c922fad | ||
|
|
194ae49914 | ||
|
|
7f5aba423a | ||
|
|
db04f399c1 | ||
|
|
ed20e9d4a1 | ||
|
|
204cd967aa | ||
|
|
03043e67c7 | ||
|
|
2d17f957f1 | ||
|
|
2bc97764c7 | ||
|
|
9c84e9076f | ||
|
|
66e439a836 | ||
|
|
ce099f43f3 | ||
|
|
5fde9777fb | ||
|
|
fa789036e0 | ||
|
|
70e07470db | ||
|
|
4ac8353099 | ||
|
|
ee17d5d3c3 | ||
|
|
4b33b15dd2 | ||
|
|
0846daf1f6 | ||
|
|
8578a3b8d1 | ||
|
|
9b1d0bee3b | ||
|
|
28d49bdd47 | ||
|
|
6a19e94feb | ||
|
|
52eeffc2c4 | ||
|
|
f707dd3430 | ||
|
|
cb5db332d3 | ||
|
|
23b814569a | ||
|
|
37ab3d214d | ||
|
|
c579854e89 | ||
|
|
2bc34853e2 | ||
|
|
c469e48f26 | ||
|
|
bacd70687d | ||
|
|
89acc778f5 | ||
|
|
ac36048230 | ||
|
|
8a68313e5e | ||
|
|
9e1f56cdb8 | ||
|
|
7e9c095edb | ||
|
|
588713467d | ||
|
|
03a42fa360 | ||
|
|
c8be9cb90c | ||
|
|
36ec5e41b0 | ||
|
|
59f102af3c | ||
|
|
6854190ff9 | ||
|
|
d515e2d968 | ||
|
|
7c6901f445 | ||
|
|
2be99be4de | ||
|
|
5672c37c9f | ||
|
|
567826165c | ||
|
|
625ab33bc8 | ||
|
|
9681372a84 | ||
|
|
0683911076 | ||
|
|
d703e48ecb | ||
|
|
015ed66967 | ||
|
|
0a4f7a55b8 | ||
|
|
a6e850e39f | ||
|
|
abecb4856f | ||
|
|
dbf88f8485 | ||
|
|
83af318ad0 | ||
|
|
7be1edd896 | ||
|
|
d6ca21273d | ||
|
|
dd934f0e34 | ||
|
|
612851fd48 | ||
|
|
755962c6a2 | ||
|
|
153dde4129 | ||
|
|
8f3a022b3c | ||
|
|
017bf187f9 | ||
|
|
7b8d7c9fe0 | ||
|
|
d319c79abb | ||
|
|
1e62528d2a | ||
|
|
7ae7c19a24 | ||
|
|
0dce5fab7b | ||
|
|
d1dc0fae19 | ||
|
|
dd33209b1c | ||
|
|
c3fe101571 | ||
|
|
2c1b20b5d5 | ||
|
|
c1ab004c0c | ||
|
|
348f2e1df8 | ||
|
|
3098c8c155 | ||
|
|
55ed483b13 | ||
|
|
84ea95181d | ||
|
|
163f076565 | ||
|
|
7d2d6a79e4 | ||
|
|
40695b3288 | ||
|
|
bd9d5b92dc | ||
|
|
39a62d1cb9 | ||
|
|
fd5b902e1f | ||
|
|
c7877c67ff | ||
|
|
5d0369958f | ||
|
|
6da0106aa2 | ||
|
|
e0bfda2b14 | ||
|
|
e5c2ee5587 | ||
|
|
d4391f99bb | ||
|
|
346c713cd2 | ||
|
|
ec44a480a8 | ||
|
|
f417ddb8e0 | ||
|
|
1b43f151df | ||
|
|
6b5bed7786 | ||
|
|
c906cbe2c2 | ||
|
|
1dea5f1624 | ||
|
|
0fea7f2a22 | ||
|
|
45d19e3a4e | ||
|
|
8a46dda07f | ||
|
|
166b41196b | ||
|
|
a13e50445e | ||
|
|
c00b19bc5b | ||
|
|
9577e4e505 | ||
|
|
72dc75512b | ||
|
|
f4a052ee2b | ||
|
|
080f9da9c8 | ||
|
|
1c1cc670f8 | ||
|
|
141d9b779d | ||
|
|
dbfa812d37 | ||
|
|
026ffde807 | ||
|
|
e07d7e3874 | ||
|
|
413e996e92 | ||
|
|
bebf2c9bee | ||
|
|
afb927258c | ||
|
|
a15f46c972 | ||
|
|
92b0ed6599 | ||
|
|
29ab493cbe | ||
|
|
90915f8dc0 | ||
|
|
1f4471ccc3 | ||
|
|
5d4ec2154b | ||
|
|
9d9f30e41f | ||
|
|
e3a8bb23c1 | ||
|
|
787ab0a2e4 | ||
|
|
c140ecf14b | ||
|
|
980116b33b | ||
|
|
142a7659bd | ||
|
|
6fbe588aee | ||
|
|
17eb8237da | ||
|
|
f11cee7197 | ||
|
|
1b28326c5e | ||
|
|
6ffb6db248 | ||
|
|
9b0825739a | ||
|
|
b42f7fce44 | ||
|
|
30f491795d | ||
|
|
55f7557b5e | ||
|
|
5a0de7088b | ||
|
|
9319b9f433 | ||
|
|
b8942acfed | ||
|
|
a0d8fbede3 | ||
|
|
8e8930d51f | ||
|
|
09b7578c35 | ||
|
|
fdd4d09714 | ||
|
|
0deb6e8bea | ||
|
|
586bad4876 | ||
|
|
47fd317a11 | ||
|
|
0c229ede24 | ||
|
|
64a6ee0f3b | ||
|
|
364f78d20c | ||
|
|
ff0d163a95 | ||
|
|
a7a37437a1 | ||
|
|
e333f5f726 | ||
|
|
c822450792 | ||
|
|
e6d77273c4 | ||
|
|
c203c3542f | ||
|
|
d218f07940 | ||
|
|
0da2999c75 | ||
|
|
67cfd22084 | ||
|
|
d97484b30b | ||
|
|
d5ad888b0a | ||
|
|
a7cec1627f | ||
|
|
e42a1b7b88 | ||
|
|
dc03283e14 | ||
|
|
4aef7643a0 | ||
|
|
ac17cf0121 | ||
|
|
e19061c902 | ||
|
|
db71993905 | ||
|
|
3f8f7ed32c | ||
|
|
b7ac86fab4 | ||
|
|
79e0f972e7 | ||
|
|
255d754045 | ||
|
|
385c13148d | ||
|
|
4de3445c58 | ||
|
|
a2314074d3 | ||
|
|
460ff571e4 | ||
|
|
3a10d235c1 | ||
|
|
207d2bb4d3 | ||
|
|
2076faaa6c | ||
|
|
ec166c906f | ||
|
|
c0e69d7a99 | ||
|
|
a8348aa674 | ||
|
|
b17590942d | ||
|
|
062b3fd0fe | ||
|
|
f2f40e4758 | ||
|
|
922feec7f4 | ||
|
|
64636a6d60 | ||
|
|
ac46a4a413 | ||
|
|
7593d855cb | ||
|
|
27c7f6589c | ||
|
|
edad4f9cf1 | ||
|
|
14e9b71ebb |
12
.gitignore
vendored
12
.gitignore
vendored
@@ -6,9 +6,14 @@ __pycache__/
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
env/
|
||||
inventree-env/
|
||||
./build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
bin/
|
||||
lib64
|
||||
pyvenv.cfg
|
||||
share/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
@@ -27,15 +32,14 @@ var/
|
||||
local_settings.py
|
||||
*.sqlite3
|
||||
*.backup
|
||||
*.old
|
||||
|
||||
# Sphinx files
|
||||
docs/_build
|
||||
|
||||
# Local static and media file storage (only when running in development mode)
|
||||
InvenTree/media
|
||||
InvenTree/static
|
||||
media
|
||||
static
|
||||
inventree_media
|
||||
inventree_static
|
||||
|
||||
# Local config file
|
||||
config.yaml
|
||||
|
||||
29
.travis.yml
29
.travis.yml
@@ -1,5 +1,9 @@
|
||||
dist: xenial
|
||||
|
||||
services:
|
||||
- mysql
|
||||
- postgresql
|
||||
|
||||
language: python
|
||||
python:
|
||||
- 3.6
|
||||
@@ -7,17 +11,30 @@ python:
|
||||
|
||||
addons:
|
||||
apt-packages:
|
||||
-sqlite3
|
||||
- sqlite3
|
||||
|
||||
before_install:
|
||||
- make install
|
||||
- make migrate
|
||||
- sudo apt-get update
|
||||
- sudo apt-get install gettext
|
||||
- sudo apt-get install mysql-server libmysqlclient-dev
|
||||
- sudo apt-get install libpq-dev
|
||||
- pip3 install invoke
|
||||
- pip3 install mysqlclient
|
||||
- pip3 install psycopg2
|
||||
- invoke install
|
||||
- invoke migrate
|
||||
- cd InvenTree && python3 manage.py createsuperuser --username InvenTreeAdmin --email admin@inventree.com --noinput && cd ..
|
||||
- psql -c 'create database inventree_test_db;' -U postgres
|
||||
- mysql -e 'CREATE DATABASE inventree_test_db;'
|
||||
|
||||
script:
|
||||
- git ls-files --exclude-standard --others
|
||||
- make coverage
|
||||
- make style
|
||||
- cd InvenTree && python3 manage.py makemigrations && cd ..
|
||||
- python3 ci/check_migration_files.py
|
||||
- invoke coverage
|
||||
- cd InvenTree && python3 manage.py test --settings=InvenTree.ci_mysql && cd ..
|
||||
- cd InvenTree && python3 manage.py test --settings=InvenTree.ci_postgresql && cd ..
|
||||
- invoke translate
|
||||
- invoke style
|
||||
|
||||
after_success:
|
||||
- coveralls
|
||||
@@ -4,15 +4,26 @@ Contributions to InvenTree are welcomed - please follow the guidelines below.
|
||||
|
||||
No pushing to master! New featues must be submitted in a separate branch (one branch per feature).
|
||||
|
||||
## Include Migration Files
|
||||
|
||||
Any required migration files **must** be included in the commit, or the pull-request will be rejected. If you change the underlying database schema, make sure you run `invoke migrate` and commit the migration files before submitting the PR.
|
||||
|
||||
## Update Translation Files
|
||||
|
||||
Any PRs which update translatable strings (i.e. text strings that will appear in the web-front UI) must also update the translation (locale) files to include hooks for the translated strings.
|
||||
|
||||
*This does not mean that all translations must be provided, but that the translation files must include locations for the translated strings to be written.*
|
||||
|
||||
To perform this step, simply run `invoke translate` from the top level directory before submitting the PR.
|
||||
|
||||
## Testing
|
||||
|
||||
Any new code should be covered by unit tests - a submitted PR may not be accepted if the code coverage is decreased.
|
||||
|
||||
## Documentation
|
||||
|
||||
New features or updates to existing features should be accompanied by user documentation.
|
||||
A PR with associated documentation should link to the matching PR at https://github.com/inventree/InvenTree.github.io
|
||||
New features or updates to existing features should be accompanied by user documentation. A PR with associated documentation should link to the matching PR at https://github.com/inventree/inventree-docs/
|
||||
|
||||
## Code Style
|
||||
|
||||
Sumbitted Python code is automatically checked against PEP style guidelines. Locally you can run `make style` to ensure the style checks will pass, before submitting the PR.
|
||||
Sumbitted Python code is automatically checked against PEP style guidelines. Locally you can run `invoke style` to ensure the style checks will pass, before submitting the PR.
|
||||
|
||||
99
InvenTree/InvenTree/api.py
Normal file
99
InvenTree/InvenTree/api.py
Normal file
@@ -0,0 +1,99 @@
|
||||
"""
|
||||
Main JSON interface views
|
||||
"""
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.http import JsonResponse
|
||||
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
from rest_framework import filters
|
||||
|
||||
from rest_framework import permissions
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from .views import AjaxView
|
||||
from .version import inventreeVersion, inventreeInstanceName
|
||||
|
||||
from plugins import plugins as inventree_plugins
|
||||
|
||||
|
||||
print("Loading action plugins")
|
||||
action_plugins = inventree_plugins.load_action_plugins()
|
||||
|
||||
|
||||
class InfoView(AjaxView):
|
||||
""" Simple JSON endpoint for InvenTree information.
|
||||
Use to confirm that the server is running, etc.
|
||||
"""
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
|
||||
data = {
|
||||
'server': 'InvenTree',
|
||||
'version': inventreeVersion(),
|
||||
'instance': inventreeInstanceName(),
|
||||
}
|
||||
|
||||
return JsonResponse(data)
|
||||
|
||||
|
||||
class AttachmentMixin:
|
||||
"""
|
||||
Mixin for creating attachment objects,
|
||||
and ensuring the user information is saved correctly.
|
||||
"""
|
||||
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
|
||||
filter_backends = [
|
||||
DjangoFilterBackend,
|
||||
filters.OrderingFilter,
|
||||
filters.SearchFilter,
|
||||
]
|
||||
|
||||
def perform_create(self, serializer):
|
||||
""" Save the user information when a file is uploaded """
|
||||
|
||||
attachment = serializer.save()
|
||||
attachment.user = self.request.user
|
||||
attachment.save()
|
||||
|
||||
|
||||
class ActionPluginView(APIView):
|
||||
"""
|
||||
Endpoint for running custom action plugins.
|
||||
"""
|
||||
|
||||
permission_classes = [
|
||||
permissions.IsAuthenticated,
|
||||
]
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
|
||||
action = request.data.get('action', None)
|
||||
|
||||
data = request.data.get('data', None)
|
||||
|
||||
if action is None:
|
||||
return Response({
|
||||
'error': _("No action specified")
|
||||
})
|
||||
|
||||
for plugin_class in action_plugins:
|
||||
if plugin_class.action_name() == action:
|
||||
|
||||
plugin = plugin_class(request.user, data=data)
|
||||
|
||||
plugin.perform_action()
|
||||
|
||||
return Response(plugin.get_response())
|
||||
|
||||
# If we got to here, no matching action was found
|
||||
return Response({
|
||||
'error': _("No matching action found"),
|
||||
"action": action,
|
||||
})
|
||||
18
InvenTree/InvenTree/ci_mysql.py
Normal file
18
InvenTree/InvenTree/ci_mysql.py
Normal file
@@ -0,0 +1,18 @@
|
||||
"""
|
||||
Configuration file for running tests against a MySQL database.
|
||||
"""
|
||||
|
||||
from InvenTree.settings import *
|
||||
|
||||
# Override the 'test' database
|
||||
if 'test' in sys.argv:
|
||||
eprint('InvenTree: Running tests - Using MySQL test database')
|
||||
|
||||
DATABASES['default'] = {
|
||||
# Ensure mysql backend is being used
|
||||
'ENGINE': 'django.db.backends.mysql',
|
||||
'NAME': 'inventree_test_db',
|
||||
'USER': 'travis',
|
||||
'PASSWORD': '',
|
||||
'HOST': '127.0.0.1'
|
||||
}
|
||||
17
InvenTree/InvenTree/ci_postgresql.py
Normal file
17
InvenTree/InvenTree/ci_postgresql.py
Normal file
@@ -0,0 +1,17 @@
|
||||
"""
|
||||
Configuration file for running tests against a MySQL database.
|
||||
"""
|
||||
|
||||
from InvenTree.settings import *
|
||||
|
||||
# Override the 'test' database
|
||||
if 'test' in sys.argv:
|
||||
eprint('InvenTree: Running tests - Using PostGreSQL test database')
|
||||
|
||||
DATABASES['default'] = {
|
||||
# Ensure postgresql backend is being used
|
||||
'ENGINE': 'django.db.backends.postgresql',
|
||||
'NAME': 'inventree_test_db',
|
||||
'USER': 'postgres',
|
||||
'PASSWORD': '',
|
||||
}
|
||||
19
InvenTree/InvenTree/context.py
Normal file
19
InvenTree/InvenTree/context.py
Normal file
@@ -0,0 +1,19 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Provides extra global data to all templates.
|
||||
"""
|
||||
|
||||
from InvenTree.status_codes import SalesOrderStatus, PurchaseOrderStatus
|
||||
from InvenTree.status_codes import BuildStatus, StockStatus
|
||||
|
||||
|
||||
def status_codes(request):
|
||||
|
||||
return {
|
||||
# Expose the StatusCode classes to the templates
|
||||
'SalesOrderStatus': SalesOrderStatus,
|
||||
'PurchaseOrderStatus': PurchaseOrderStatus,
|
||||
'BuildStatus': BuildStatus,
|
||||
'StockStatus': StockStatus,
|
||||
}
|
||||
75
InvenTree/InvenTree/fields.py
Normal file
75
InvenTree/InvenTree/fields.py
Normal file
@@ -0,0 +1,75 @@
|
||||
""" Custom fields used in InvenTree """
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from .validators import allowable_url_schemes
|
||||
|
||||
from django.forms.fields import URLField as FormURLField
|
||||
from django.db import models as models
|
||||
from django.core import validators
|
||||
from django import forms
|
||||
from decimal import Decimal
|
||||
|
||||
import InvenTree.helpers
|
||||
|
||||
|
||||
class InvenTreeURLFormField(FormURLField):
|
||||
""" Custom URL form field with custom scheme validators """
|
||||
|
||||
default_validators = [validators.URLValidator(schemes=allowable_url_schemes())]
|
||||
|
||||
|
||||
class InvenTreeURLField(models.URLField):
|
||||
""" Custom URL field which has custom scheme validators """
|
||||
|
||||
default_validators = [validators.URLValidator(schemes=allowable_url_schemes())]
|
||||
|
||||
def formfield(self, **kwargs):
|
||||
return super().formfield(**{
|
||||
'form_class': InvenTreeURLFormField
|
||||
})
|
||||
|
||||
|
||||
def round_decimal(value, places):
|
||||
"""
|
||||
Round value to the specified number of places.
|
||||
"""
|
||||
|
||||
if value is not None:
|
||||
# see https://docs.python.org/2/library/decimal.html#decimal.Decimal.quantize for options
|
||||
return value.quantize(Decimal(10) ** -places)
|
||||
return value
|
||||
|
||||
|
||||
class RoundingDecimalFormField(forms.DecimalField):
|
||||
def to_python(self, value):
|
||||
value = super(RoundingDecimalFormField, self).to_python(value)
|
||||
value = round_decimal(value, self.decimal_places)
|
||||
return value
|
||||
|
||||
def prepare_value(self, value):
|
||||
"""
|
||||
Override the 'prepare_value' method, to remove trailing zeros when displaying.
|
||||
Why? It looks nice!
|
||||
"""
|
||||
|
||||
if type(value) == Decimal:
|
||||
return InvenTree.helpers.normalize(value)
|
||||
else:
|
||||
return value
|
||||
|
||||
|
||||
class RoundingDecimalField(models.DecimalField):
|
||||
def to_python(self, value):
|
||||
value = super(RoundingDecimalField, self).to_python(value)
|
||||
return round_decimal(value, self.decimal_places)
|
||||
|
||||
def formfield(self, **kwargs):
|
||||
defaults = {
|
||||
'form_class': RoundingDecimalFormField
|
||||
}
|
||||
|
||||
defaults.update(kwargs)
|
||||
|
||||
return super().formfield(**kwargs)
|
||||
@@ -5,20 +5,108 @@ Helper forms which subclass Django forms to provide additional functionality
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from django import forms
|
||||
from crispy_forms.helper import FormHelper
|
||||
from crispy_forms.layout import Layout, Field
|
||||
from crispy_forms.bootstrap import PrependedText, AppendedText, PrependedAppendedText, StrictButton, Div
|
||||
from django.contrib.auth.models import User
|
||||
from common.models import ColorTheme
|
||||
|
||||
|
||||
class HelperForm(forms.ModelForm):
|
||||
""" Provides simple integration of crispy_forms extension. """
|
||||
|
||||
# Custom field decorations can be specified here, per form class
|
||||
field_prefix = {}
|
||||
field_suffix = {}
|
||||
field_placeholder = {}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(forms.ModelForm, self).__init__(*args, **kwargs)
|
||||
self.helper = FormHelper()
|
||||
|
||||
self.helper.form_tag = False
|
||||
|
||||
"""
|
||||
Create a default 'layout' for this form.
|
||||
Ref: https://django-crispy-forms.readthedocs.io/en/latest/layouts.html
|
||||
This is required to do fancy things later (like adding PrependedText, etc).
|
||||
|
||||
Simply create a 'blank' layout for each available field.
|
||||
"""
|
||||
|
||||
self.rebuild_layout()
|
||||
|
||||
def rebuild_layout(self):
|
||||
|
||||
layouts = []
|
||||
|
||||
for field in self.fields:
|
||||
prefix = self.field_prefix.get(field, None)
|
||||
suffix = self.field_suffix.get(field, None)
|
||||
placeholder = self.field_placeholder.get(field, '')
|
||||
|
||||
# Look for font-awesome icons
|
||||
if prefix and prefix.startswith('fa-'):
|
||||
prefix = r"<i class='fas {fa}'/>".format(fa=prefix)
|
||||
|
||||
if suffix and suffix.startswith('fa-'):
|
||||
suffix = r"<i class='fas {fa}'/>".format(fa=suffix)
|
||||
|
||||
if prefix and suffix:
|
||||
layouts.append(
|
||||
Field(
|
||||
PrependedAppendedText(
|
||||
field,
|
||||
prepended_text=prefix,
|
||||
appended_text=suffix,
|
||||
placeholder=placeholder
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
elif prefix:
|
||||
layouts.append(
|
||||
Field(
|
||||
PrependedText(
|
||||
field,
|
||||
prefix,
|
||||
placeholder=placeholder
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
elif suffix:
|
||||
layouts.append(
|
||||
Field(
|
||||
AppendedText(
|
||||
field,
|
||||
suffix,
|
||||
placeholder=placeholder
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
else:
|
||||
layouts.append(Field(field, placeholder=placeholder))
|
||||
|
||||
self.helper.layout = Layout(*layouts)
|
||||
|
||||
|
||||
class ConfirmForm(forms.Form):
|
||||
""" Generic confirmation form """
|
||||
|
||||
confirm = forms.BooleanField(
|
||||
required=False, initial=False,
|
||||
help_text=_("Confirm")
|
||||
)
|
||||
|
||||
class Meta:
|
||||
fields = [
|
||||
'confirm'
|
||||
]
|
||||
|
||||
|
||||
class DeleteForm(forms.Form):
|
||||
""" Generic deletion form which provides simple user confirmation
|
||||
@@ -27,7 +115,7 @@ class DeleteForm(forms.Form):
|
||||
confirm_delete = forms.BooleanField(
|
||||
required=False,
|
||||
initial=False,
|
||||
help_text='Confirm item deletion'
|
||||
help_text=_('Confirm item deletion')
|
||||
)
|
||||
|
||||
class Meta:
|
||||
@@ -43,6 +131,7 @@ class EditUserForm(HelperForm):
|
||||
class Meta:
|
||||
model = User
|
||||
fields = [
|
||||
'username',
|
||||
'first_name',
|
||||
'last_name',
|
||||
'email'
|
||||
@@ -58,14 +147,14 @@ class SetPasswordForm(HelperForm):
|
||||
required=True,
|
||||
initial='',
|
||||
widget=forms.PasswordInput(attrs={'autocomplete': 'off'}),
|
||||
help_text='Enter new password')
|
||||
help_text=_('Enter new password'))
|
||||
|
||||
confirm_password = forms.CharField(max_length=100,
|
||||
min_length=8,
|
||||
required=True,
|
||||
initial='',
|
||||
widget=forms.PasswordInput(attrs={'autocomplete': 'off'}),
|
||||
help_text='Confirm new password')
|
||||
help_text=_('Confirm new password'))
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
@@ -73,3 +162,36 @@ class SetPasswordForm(HelperForm):
|
||||
'enter_password',
|
||||
'confirm_password'
|
||||
]
|
||||
|
||||
|
||||
class ColorThemeSelectForm(forms.ModelForm):
|
||||
""" Form for setting color theme """
|
||||
|
||||
name = forms.ChoiceField(choices=(), required=False)
|
||||
|
||||
class Meta:
|
||||
model = ColorTheme
|
||||
fields = [
|
||||
'name'
|
||||
]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ColorThemeSelectForm, self).__init__(*args, **kwargs)
|
||||
|
||||
# Populate color themes choices
|
||||
self.fields['name'].choices = ColorTheme.get_color_themes_choices()
|
||||
|
||||
self.helper = FormHelper()
|
||||
# Form rendering
|
||||
self.helper.form_show_labels = False
|
||||
self.helper.layout = Layout(
|
||||
Div(
|
||||
Div(Field('name'),
|
||||
css_class='col-sm-6',
|
||||
style='width: 200px;'),
|
||||
Div(StrictButton(_('Apply Theme'), css_class='btn btn-primary', type='submit'),
|
||||
css_class='col-sm-6',
|
||||
style='width: auto;'),
|
||||
css_class='row',
|
||||
),
|
||||
)
|
||||
|
||||
@@ -8,11 +8,68 @@ import json
|
||||
import os.path
|
||||
from PIL import Image
|
||||
|
||||
from decimal import Decimal
|
||||
|
||||
from wsgiref.util import FileWrapper
|
||||
from django.http import StreamingHttpResponse
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
import InvenTree.version
|
||||
|
||||
from .settings import MEDIA_URL, STATIC_URL
|
||||
|
||||
|
||||
def generateTestKey(test_name):
|
||||
"""
|
||||
Generate a test 'key' for a given test name.
|
||||
This must not have illegal chars as it will be used for dict lookup in a template.
|
||||
|
||||
Tests must be named such that they will have unique keys.
|
||||
"""
|
||||
|
||||
key = test_name.strip().lower()
|
||||
key = key.replace(" ", "")
|
||||
|
||||
# Remove any characters that cannot be used to represent a variable
|
||||
key = re.sub(r'[^a-zA-Z0-9]', '', key)
|
||||
|
||||
return key
|
||||
|
||||
|
||||
def getMediaUrl(filename):
|
||||
"""
|
||||
Return the qualified access path for the given file,
|
||||
under the media directory.
|
||||
"""
|
||||
|
||||
return os.path.join(MEDIA_URL, str(filename))
|
||||
|
||||
|
||||
def getStaticUrl(filename):
|
||||
"""
|
||||
Return the qualified access path for the given file,
|
||||
under the static media directory.
|
||||
"""
|
||||
|
||||
return os.path.join(STATIC_URL, str(filename))
|
||||
|
||||
|
||||
def getBlankImage():
|
||||
"""
|
||||
Return the qualified path for the 'blank image' placeholder.
|
||||
"""
|
||||
|
||||
return getStaticUrl("img/blank_image.png")
|
||||
|
||||
|
||||
def getBlankThumbnail():
|
||||
"""
|
||||
Return the qualified path for the 'blank image' thumbnail placeholder.
|
||||
"""
|
||||
|
||||
return getStaticUrl("img/blank_image.thumbnail.png")
|
||||
|
||||
|
||||
def TestIfImage(img):
|
||||
""" Test if an image file is indeed an image """
|
||||
@@ -52,6 +109,120 @@ def str2bool(text, test=True):
|
||||
return str(text).lower() in ['0', 'n', 'no', 'none', 'f', 'false', 'off', ]
|
||||
|
||||
|
||||
def isNull(text):
|
||||
"""
|
||||
Test if a string 'looks' like a null value.
|
||||
This is useful for querying the API against a null key.
|
||||
|
||||
Args:
|
||||
text: Input text
|
||||
|
||||
Returns:
|
||||
True if the text looks like a null value
|
||||
"""
|
||||
|
||||
return str(text).strip().lower() in ['top', 'null', 'none', 'empty', 'false', '-1', '']
|
||||
|
||||
|
||||
def normalize(d):
|
||||
"""
|
||||
Normalize a decimal number, and remove exponential formatting.
|
||||
"""
|
||||
|
||||
if type(d) is not Decimal:
|
||||
d = Decimal(d)
|
||||
|
||||
d = d.normalize()
|
||||
|
||||
# Ref: https://docs.python.org/3/library/decimal.html
|
||||
return d.quantize(Decimal(1)) if d == d.to_integral() else d.normalize()
|
||||
|
||||
|
||||
def increment(n):
|
||||
"""
|
||||
Attempt to increment an integer (or a string that looks like an integer!)
|
||||
|
||||
e.g.
|
||||
|
||||
001 -> 002
|
||||
2 -> 3
|
||||
AB01 -> AB02
|
||||
QQQ -> QQQ
|
||||
|
||||
"""
|
||||
|
||||
value = str(n).strip()
|
||||
|
||||
# Ignore empty strings
|
||||
if not value:
|
||||
return value
|
||||
|
||||
pattern = r"(.*?)(\d+)?$"
|
||||
|
||||
result = re.search(pattern, value)
|
||||
|
||||
# No match!
|
||||
if result is None:
|
||||
return value
|
||||
|
||||
groups = result.groups()
|
||||
|
||||
# If we cannot match the regex, then simply return the provided value
|
||||
if not len(groups) == 2:
|
||||
return value
|
||||
|
||||
prefix, number = groups
|
||||
|
||||
# No number extracted? Simply return the prefix (without incrementing!)
|
||||
if not number:
|
||||
return prefix
|
||||
|
||||
# Record the width of the number
|
||||
width = len(number)
|
||||
|
||||
try:
|
||||
number = int(number) + 1
|
||||
number = str(number)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
number = number.zfill(width)
|
||||
|
||||
return prefix + number
|
||||
|
||||
|
||||
def decimal2string(d):
|
||||
"""
|
||||
Format a Decimal number as a string,
|
||||
stripping out any trailing zeroes or decimal points.
|
||||
Essentially make it look like a whole number if it is one.
|
||||
|
||||
Args:
|
||||
d: A python Decimal object
|
||||
|
||||
Returns:
|
||||
A string representation of the input number
|
||||
"""
|
||||
|
||||
if type(d) is Decimal:
|
||||
d = normalize(d)
|
||||
|
||||
try:
|
||||
# Ensure that the provided string can actually be converted to a float
|
||||
float(d)
|
||||
except ValueError:
|
||||
# Not a number
|
||||
return str(d)
|
||||
|
||||
s = str(d)
|
||||
|
||||
# Return entire number if there is no decimal place
|
||||
if '.' not in s:
|
||||
return s
|
||||
|
||||
return s.rstrip("0").rstrip(".")
|
||||
|
||||
|
||||
def WrapWithQuotes(text, quote='"'):
|
||||
""" Wrap the supplied text with quotes
|
||||
|
||||
@@ -72,7 +243,7 @@ def WrapWithQuotes(text, quote='"'):
|
||||
return text
|
||||
|
||||
|
||||
def MakeBarcode(object_type, object_id, object_url, data={}):
|
||||
def MakeBarcode(object_name, object_pk, object_data, **kwargs):
|
||||
""" Generate a string for a barcode. Adds some global InvenTree parameters.
|
||||
|
||||
Args:
|
||||
@@ -85,11 +256,20 @@ def MakeBarcode(object_type, object_id, object_url, data={}):
|
||||
json string of the supplied data plus some other data
|
||||
"""
|
||||
|
||||
# Add in some generic InvenTree data
|
||||
data['type'] = object_type
|
||||
data['id'] = object_id
|
||||
data['url'] = object_url
|
||||
data['tool'] = 'InvenTree'
|
||||
brief = kwargs.get('brief', False)
|
||||
|
||||
data = {}
|
||||
|
||||
if brief:
|
||||
data[object_name] = object_pk
|
||||
else:
|
||||
data['tool'] = 'InvenTree'
|
||||
data['version'] = InvenTree.version.inventreeVersion()
|
||||
data['instance'] = InvenTree.version.inventreeInstanceName()
|
||||
|
||||
# Ensure PK is included
|
||||
object_data['id'] = object_pk
|
||||
data[object_name] = object_data
|
||||
|
||||
return json.dumps(data, sort_keys=True)
|
||||
|
||||
@@ -103,6 +283,7 @@ def GetExportFormats():
|
||||
'xls',
|
||||
'xlsx',
|
||||
'json',
|
||||
'yaml',
|
||||
]
|
||||
|
||||
|
||||
@@ -191,14 +372,10 @@ def ExtractSerialNumbers(serials, expected_quantity):
|
||||
continue
|
||||
|
||||
else:
|
||||
try:
|
||||
n = int(group)
|
||||
if n in numbers:
|
||||
errors.append(_("Duplicate serial: {n}".format(n=n)))
|
||||
else:
|
||||
numbers.append(n)
|
||||
except ValueError:
|
||||
errors.append(_("Invalid group: {g}".format(g=group)))
|
||||
if group in numbers:
|
||||
errors.append(_("Duplicate serial: {g}".format(g=group)))
|
||||
else:
|
||||
numbers.append(group)
|
||||
|
||||
if len(errors) > 0:
|
||||
raise ValidationError(errors)
|
||||
@@ -211,3 +388,56 @@ def ExtractSerialNumbers(serials, expected_quantity):
|
||||
raise ValidationError([_("Number of unique serial number ({s}) must match quantity ({q})".format(s=len(numbers), q=expected_quantity))])
|
||||
|
||||
return numbers
|
||||
|
||||
|
||||
def validateFilterString(value):
|
||||
"""
|
||||
Validate that a provided filter string looks like a list of comma-separated key=value pairs
|
||||
|
||||
These should nominally match to a valid database filter based on the model being filtered.
|
||||
|
||||
e.g. "category=6, IPN=12"
|
||||
e.g. "part__name=widget"
|
||||
|
||||
The ReportTemplate class uses the filter string to work out which items a given report applies to.
|
||||
For example, an acceptance test report template might only apply to stock items with a given IPN,
|
||||
so the string could be set to:
|
||||
|
||||
filters = "IPN = ACME0001"
|
||||
|
||||
Returns a map of key:value pairs
|
||||
"""
|
||||
|
||||
# Empty results map
|
||||
results = {}
|
||||
|
||||
value = str(value).strip()
|
||||
|
||||
if not value or len(value) == 0:
|
||||
return results
|
||||
|
||||
groups = value.split(',')
|
||||
|
||||
for group in groups:
|
||||
group = group.strip()
|
||||
|
||||
pair = group.split('=')
|
||||
|
||||
if not len(pair) == 2:
|
||||
raise ValidationError(
|
||||
"Invalid group: {g}".format(g=group)
|
||||
)
|
||||
|
||||
k, v = pair
|
||||
|
||||
k = k.strip()
|
||||
v = v.strip()
|
||||
|
||||
if not k or not v:
|
||||
raise ValidationError(
|
||||
"Invalid group: {g}".format(g=group)
|
||||
)
|
||||
|
||||
results[k] = v
|
||||
|
||||
return results
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
from django.shortcuts import HttpResponseRedirect
|
||||
from django.urls import reverse_lazy
|
||||
from django.db import connection
|
||||
from django.shortcuts import redirect
|
||||
import logging
|
||||
import time
|
||||
import operator
|
||||
|
||||
from rest_framework.authtoken.models import Token
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -20,10 +23,57 @@ class AuthRequiredMiddleware(object):
|
||||
|
||||
response = self.get_response(request)
|
||||
|
||||
# Redirect any unauthorized HTTP requests to the login page
|
||||
if not request.user.is_authenticated:
|
||||
if not request.path_info == reverse_lazy('login') and not request.path_info.startswith('/api/'):
|
||||
return HttpResponseRedirect(reverse_lazy('login'))
|
||||
"""
|
||||
Normally, a web-based session would use csrftoken based authentication.
|
||||
However when running an external application (e.g. the InvenTree app),
|
||||
we wish to use token-based auth to grab media files.
|
||||
|
||||
So, we will allow token-based authentication but ONLY for the /media/ directory.
|
||||
|
||||
What problem is this solving?
|
||||
- The InvenTree mobile app does not use csrf token auth
|
||||
- Token auth is used by the Django REST framework, but that is under the /api/ endpoint
|
||||
- Media files (e.g. Part images) are required to be served to the app
|
||||
- We do not want to make /media/ files accessible without login!
|
||||
|
||||
There is PROBABLY a better way of going about this?
|
||||
a) Allow token-based authentication against a user?
|
||||
b) Serve /media/ files in a duplicate location e.g. /api/media/ ?
|
||||
c) Is there a "standard" way of solving this problem?
|
||||
|
||||
My [google|stackoverflow]-fu has failed me. So this hack has been created.
|
||||
"""
|
||||
|
||||
authorized = False
|
||||
|
||||
if 'Authorization' in request.headers.keys():
|
||||
auth = request.headers['Authorization'].strip()
|
||||
|
||||
if auth.startswith('Token') and len(auth.split()) == 2:
|
||||
token = auth.split()[1]
|
||||
|
||||
# Does the provided token match a valid user?
|
||||
if Token.objects.filter(key=token).exists():
|
||||
|
||||
allowed = ['/api/', '/media/', '/static/']
|
||||
|
||||
# Only allow token-auth for /media/ or /static/ dirs!
|
||||
if any([request.path_info.startswith(a) for a in allowed]):
|
||||
authorized = True
|
||||
|
||||
# No authorization was found for the request
|
||||
if not authorized:
|
||||
# A logout request will redirect the user to the login screen
|
||||
if request.path_info == reverse_lazy('logout'):
|
||||
return HttpResponseRedirect(reverse_lazy('login'))
|
||||
|
||||
login = reverse_lazy('login')
|
||||
|
||||
if not request.path_info == login and not request.path_info.startswith('/api/'):
|
||||
# Save the 'next' parameter to pass through to the login view
|
||||
|
||||
return redirect('%s?next=%s' % (login, request.path))
|
||||
|
||||
# Code to be executed for each request/response after
|
||||
# the view is called.
|
||||
@@ -38,7 +88,11 @@ class QueryCountMiddleware(object):
|
||||
status code of 200). It does not currently support
|
||||
multi-db setups.
|
||||
|
||||
To enable this middleware, set 'log_queries: True' in the local InvenTree config file.
|
||||
|
||||
Reference: https://www.dabapps.com/blog/logging-sql-queries-django-13/
|
||||
|
||||
Note: 2020-08-15 - This is no longer used, instead we now rely on the django-debug-toolbar addon
|
||||
"""
|
||||
|
||||
def __init__(self, get_response):
|
||||
|
||||
@@ -4,8 +4,12 @@ Generic models which provide extra functionality over base Django model types.
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import os
|
||||
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from django.db.models.signals import pre_delete
|
||||
from django.dispatch import receiver
|
||||
@@ -15,6 +19,65 @@ from mptt.models import MPTTModel, TreeForeignKey
|
||||
from .validators import validate_tree_name
|
||||
|
||||
|
||||
def rename_attachment(instance, filename):
|
||||
"""
|
||||
Function for renaming an attachment file.
|
||||
The subdirectory for the uploaded file is determined by the implementing class.
|
||||
|
||||
Args:
|
||||
instance: Instance of a PartAttachment object
|
||||
filename: name of uploaded file
|
||||
|
||||
Returns:
|
||||
path to store file, format: '<subdir>/<id>/filename'
|
||||
"""
|
||||
|
||||
# Construct a path to store a file attachment for a given model type
|
||||
return os.path.join(instance.getSubdir(), filename)
|
||||
|
||||
|
||||
class InvenTreeAttachment(models.Model):
|
||||
""" Provides an abstracted class for managing file attachments.
|
||||
|
||||
Attributes:
|
||||
attachment: File
|
||||
comment: String descriptor for the attachment
|
||||
user: User associated with file upload
|
||||
upload_date: Date the file was uploaded
|
||||
"""
|
||||
def getSubdir(self):
|
||||
"""
|
||||
Return the subdirectory under which attachments should be stored.
|
||||
Note: Re-implement this for each subclass of InvenTreeAttachment
|
||||
"""
|
||||
|
||||
return "attachments"
|
||||
|
||||
def __str__(self):
|
||||
return os.path.basename(self.attachment.name)
|
||||
|
||||
attachment = models.FileField(upload_to=rename_attachment,
|
||||
help_text=_('Select file to attach'))
|
||||
|
||||
comment = models.CharField(blank=True, max_length=100, help_text=_('File comment'))
|
||||
|
||||
user = models.ForeignKey(
|
||||
User,
|
||||
on_delete=models.SET_NULL,
|
||||
blank=True, null=True,
|
||||
help_text=_('User'),
|
||||
)
|
||||
|
||||
upload_date = models.DateField(auto_now_add=True, null=True, blank=True)
|
||||
|
||||
@property
|
||||
def basename(self):
|
||||
return os.path.basename(self.attachment.name)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
|
||||
class InvenTreeTree(MPTTModel):
|
||||
""" Provides an abstracted self-referencing tree model for data categories.
|
||||
|
||||
@@ -29,6 +92,8 @@ class InvenTreeTree(MPTTModel):
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
# Names must be unique at any given level in the tree
|
||||
unique_together = ('name', 'parent')
|
||||
|
||||
class MPTTMeta:
|
||||
@@ -37,13 +102,14 @@ class InvenTreeTree(MPTTModel):
|
||||
name = models.CharField(
|
||||
blank=False,
|
||||
max_length=100,
|
||||
unique=True,
|
||||
validators=[validate_tree_name]
|
||||
validators=[validate_tree_name],
|
||||
help_text=_("Name"),
|
||||
)
|
||||
|
||||
description = models.CharField(
|
||||
blank=False,
|
||||
max_length=250
|
||||
blank=True,
|
||||
max_length=250,
|
||||
help_text=_("Description (optional)")
|
||||
)
|
||||
|
||||
# When a category is deleted, graft the children onto its parent
|
||||
|
||||
43
InvenTree/InvenTree/plugins.py
Normal file
43
InvenTree/InvenTree/plugins.py
Normal file
@@ -0,0 +1,43 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import inspect
|
||||
import importlib
|
||||
import pkgutil
|
||||
|
||||
|
||||
def iter_namespace(pkg):
|
||||
|
||||
return pkgutil.iter_modules(pkg.__path__, pkg.__name__ + ".")
|
||||
|
||||
|
||||
def get_modules(pkg):
|
||||
# Return all modules in a given package
|
||||
return [importlib.import_module(name) for finder, name, ispkg in iter_namespace(pkg)]
|
||||
|
||||
|
||||
def get_classes(module):
|
||||
# Return all classes in a given module
|
||||
return inspect.getmembers(module, inspect.isclass)
|
||||
|
||||
|
||||
def get_plugins(pkg, baseclass):
|
||||
"""
|
||||
Return a list of all modules under a given package.
|
||||
|
||||
- Modules must be a subclass of the provided 'baseclass'
|
||||
- Modules must have a non-empty PLUGIN_NAME parameter
|
||||
"""
|
||||
|
||||
plugins = []
|
||||
|
||||
modules = get_modules(pkg)
|
||||
|
||||
# Iterate through each module in the package
|
||||
for mod in modules:
|
||||
# Iterate through each class in the module
|
||||
for item in get_classes(mod):
|
||||
plugin = item[1]
|
||||
if issubclass(plugin, baseclass) and plugin.PLUGIN_NAME:
|
||||
plugins.append(plugin)
|
||||
|
||||
return plugins
|
||||
@@ -8,6 +8,9 @@ from __future__ import unicode_literals
|
||||
|
||||
from rest_framework import serializers
|
||||
|
||||
import os
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
|
||||
@@ -50,3 +53,32 @@ class InvenTreeModelSerializer(serializers.ModelSerializer):
|
||||
instance.clean()
|
||||
|
||||
return data
|
||||
|
||||
|
||||
class InvenTreeAttachmentSerializerField(serializers.FileField):
|
||||
"""
|
||||
Override the DRF native FileField serializer,
|
||||
to remove the leading server path.
|
||||
|
||||
For example, the FileField might supply something like:
|
||||
|
||||
http://127.0.0.1:8000/media/foo/bar.jpg
|
||||
|
||||
Whereas we wish to return:
|
||||
|
||||
/media/foo/bar.jpg
|
||||
|
||||
Why? You can't handle the why!
|
||||
|
||||
Actually, if the server process is serving the data at 127.0.0.1,
|
||||
but a proxy service (e.g. nginx) is then providing DNS lookup to the outside world,
|
||||
then an attachment which prefixes the "address" of the internal server
|
||||
will not be accessible from the outside world.
|
||||
"""
|
||||
|
||||
def to_representation(self, value):
|
||||
|
||||
if not value:
|
||||
return None
|
||||
|
||||
return os.path.join(str(settings.MEDIA_URL), str(value))
|
||||
|
||||
@@ -17,6 +17,10 @@ import logging
|
||||
import tempfile
|
||||
import yaml
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
def eprint(*args, **kwargs):
|
||||
""" Print a warning message to stderr """
|
||||
@@ -68,6 +72,51 @@ if DEBUG:
|
||||
format='%(asctime)s %(levelname)s %(message)s',
|
||||
)
|
||||
|
||||
# Web URL endpoint for served static files
|
||||
STATIC_URL = '/static/'
|
||||
|
||||
# The filesystem location for served static files
|
||||
STATIC_ROOT = os.path.abspath(CONFIG.get('static_root', os.path.join(BASE_DIR, 'static')))
|
||||
|
||||
STATICFILES_DIRS = [
|
||||
os.path.join(BASE_DIR, 'InvenTree', 'static'),
|
||||
]
|
||||
|
||||
# Color Themes Directory
|
||||
STATIC_COLOR_THEMES_DIR = os.path.join(STATIC_ROOT, 'css', 'color-themes')
|
||||
|
||||
# Web URL endpoint for served media files
|
||||
MEDIA_URL = '/media/'
|
||||
|
||||
# The filesystem location for served static files
|
||||
MEDIA_ROOT = os.path.abspath(CONFIG.get('media_root', os.path.join(BASE_DIR, 'media')))
|
||||
|
||||
if DEBUG:
|
||||
print("InvenTree running in DEBUG mode")
|
||||
print("MEDIA_ROOT:", MEDIA_ROOT)
|
||||
print("STATIC_ROOT:", STATIC_ROOT)
|
||||
|
||||
# Does the user wish to use the sentry.io integration?
|
||||
sentry_opts = CONFIG.get('sentry', {})
|
||||
|
||||
if sentry_opts.get('enabled', False):
|
||||
dsn = sentry_opts.get('dsn', None)
|
||||
|
||||
if dsn is not None:
|
||||
# Try to import required modules (exit if not installed)
|
||||
try:
|
||||
import sentry_sdk
|
||||
from sentry_sdk.integrations.django import DjangoIntegration
|
||||
|
||||
sentry_sdk.init(dsn=dsn, integrations=[DjangoIntegration()], send_default_pii=True)
|
||||
|
||||
except ModuleNotFoundError:
|
||||
print("sentry_sdk module not found. Install using 'pip install sentry-sdk'")
|
||||
sys.exit(-1)
|
||||
|
||||
else:
|
||||
print("Warning: Sentry.io DSN not specified")
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
@@ -81,12 +130,14 @@ INSTALLED_APPS = [
|
||||
'django.contrib.staticfiles',
|
||||
|
||||
# InvenTree apps
|
||||
'common.apps.CommonConfig',
|
||||
'part.apps.PartConfig',
|
||||
'stock.apps.StockConfig',
|
||||
'company.apps.CompanyConfig',
|
||||
'build.apps.BuildConfig',
|
||||
'common.apps.CommonConfig',
|
||||
'company.apps.CompanyConfig',
|
||||
'label.apps.LabelConfig',
|
||||
'order.apps.OrderConfig',
|
||||
'part.apps.PartConfig',
|
||||
'report.apps.ReportConfig',
|
||||
'stock.apps.StockConfig',
|
||||
|
||||
# Third part add-ons
|
||||
'django_filters', # Extended filter functionality
|
||||
@@ -99,6 +150,9 @@ INSTALLED_APPS = [
|
||||
'django_cleanup', # Automatically delete orphaned MEDIA files
|
||||
'qr_code', # Generate QR codes
|
||||
'mptt', # Modified Preorder Tree Traversal
|
||||
'markdownx', # Markdown editing
|
||||
'markdownify', # Markdown template rendering
|
||||
'django_tex', # LaTeX output
|
||||
]
|
||||
|
||||
LOGGING = {
|
||||
@@ -112,37 +166,59 @@ LOGGING = {
|
||||
},
|
||||
}
|
||||
|
||||
MIDDLEWARE = [
|
||||
MIDDLEWARE = CONFIG.get('middleware', [
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.locale.LocaleMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'corsheaders.middleware.CorsMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
'InvenTree.middleware.AuthRequiredMiddleware',
|
||||
]
|
||||
'InvenTree.middleware.AuthRequiredMiddleware'
|
||||
])
|
||||
|
||||
if CONFIG.get('log_queries', False):
|
||||
MIDDLEWARE.append('InvenTree.middleware.QueryCountMiddleware')
|
||||
AUTHENTICATION_BACKENDS = CONFIG.get('authentication_backends', [
|
||||
'django.contrib.auth.backends.ModelBackend'
|
||||
])
|
||||
|
||||
# If the debug toolbar is enabled, add the modules
|
||||
if DEBUG and CONFIG.get('debug_toolbar', False):
|
||||
print("Running with DEBUG_TOOLBAR enabled")
|
||||
INSTALLED_APPS.append('debug_toolbar')
|
||||
MIDDLEWARE.append('debug_toolbar.middleware.DebugToolbarMiddleware')
|
||||
|
||||
ROOT_URLCONF = 'InvenTree.urls'
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [os.path.join(BASE_DIR, 'templates')],
|
||||
'DIRS': [
|
||||
os.path.join(BASE_DIR, 'templates'),
|
||||
# Allow templates in the reporting directory to be accessed
|
||||
os.path.join(MEDIA_ROOT, 'report'),
|
||||
],
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
'django.template.context_processors.debug',
|
||||
'django.template.context_processors.request',
|
||||
'django.template.context_processors.i18n',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
'InvenTree.context.status_codes',
|
||||
],
|
||||
},
|
||||
},
|
||||
# Backend for LaTeX report rendering
|
||||
{
|
||||
'NAME': 'tex',
|
||||
'BACKEND': 'django_tex.engine.TeXEngine',
|
||||
'DIRS': [
|
||||
os.path.join(MEDIA_ROOT, 'report'),
|
||||
]
|
||||
},
|
||||
]
|
||||
|
||||
REST_FRAMEWORK = {
|
||||
@@ -158,6 +234,37 @@ REST_FRAMEWORK = {
|
||||
|
||||
WSGI_APPLICATION = 'InvenTree.wsgi.application'
|
||||
|
||||
# Markdownx configuration
|
||||
# Ref: https://neutronx.github.io/django-markdownx/customization/
|
||||
MARKDOWNX_MEDIA_PATH = datetime.now().strftime('markdownx/%Y/%m/%d')
|
||||
|
||||
# Markdownify configuration
|
||||
# Ref: https://django-markdownify.readthedocs.io/en/latest/settings.html
|
||||
|
||||
MARKDOWNIFY_WHITELIST_TAGS = [
|
||||
'a',
|
||||
'abbr',
|
||||
'b',
|
||||
'blockquote',
|
||||
'em',
|
||||
'h1', 'h2', 'h3',
|
||||
'i',
|
||||
'img',
|
||||
'li',
|
||||
'ol',
|
||||
'p',
|
||||
'strong',
|
||||
'ul'
|
||||
]
|
||||
|
||||
MARKDOWNIFY_WHITELIST_ATTRS = [
|
||||
'href',
|
||||
'src',
|
||||
'alt',
|
||||
]
|
||||
|
||||
MARKDOWNIFY_BLEACH = False
|
||||
|
||||
DATABASES = {}
|
||||
|
||||
"""
|
||||
@@ -165,10 +272,12 @@ When running unit tests, enforce usage of sqlite3 database,
|
||||
so that the tests can be run in RAM without any setup requirements
|
||||
"""
|
||||
if 'test' in sys.argv:
|
||||
eprint('Running tests - Using sqlite3 memory database')
|
||||
eprint('InvenTree: Running tests - Using sqlite3 memory database')
|
||||
DATABASES['default'] = {
|
||||
# Ensure sqlite3 backend is being used
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': 'test_db.sqlite3'
|
||||
# Doesn't matter what the database is called, it is executed in RAM
|
||||
'NAME': 'ram_test_db.sqlite3',
|
||||
}
|
||||
|
||||
# Database backend selection
|
||||
@@ -213,11 +322,32 @@ AUTH_PASSWORD_VALIDATORS = [
|
||||
},
|
||||
]
|
||||
|
||||
# Extra (optional) URL validators
|
||||
# See https://docs.djangoproject.com/en/2.2/ref/validators/#django.core.validators.URLValidator
|
||||
|
||||
EXTRA_URL_SCHEMES = CONFIG.get('extra_url_schemes', [])
|
||||
|
||||
if not type(EXTRA_URL_SCHEMES) in [list]:
|
||||
eprint("Warning: extra_url_schemes not correctly formatted")
|
||||
EXTRA_URL_SCHEMES = []
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/1.10/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
LANGUAGE_CODE = CONFIG.get('language', 'en-us')
|
||||
|
||||
# If a new language translation is supported, it must be added here
|
||||
LANGUAGES = [
|
||||
('en', _('English')),
|
||||
('de', _('German')),
|
||||
('fr', _('French')),
|
||||
('pk', _('Polish')),
|
||||
]
|
||||
|
||||
LOCALE_PATHS = (
|
||||
os.path.join(BASE_DIR, 'locale/'),
|
||||
)
|
||||
|
||||
|
||||
TIME_ZONE = 'UTC'
|
||||
|
||||
@@ -227,28 +357,28 @@ USE_L10N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/1.10/howto/static-files/
|
||||
|
||||
# Web URL endpoint for served static files
|
||||
STATIC_URL = '/static/'
|
||||
|
||||
# The filesystem location for served static files
|
||||
STATIC_ROOT = CONFIG.get('static_root', os.path.join(BASE_DIR, 'static'))
|
||||
|
||||
STATICFILES_DIRS = [
|
||||
os.path.join(BASE_DIR, 'InvenTree', 'static'),
|
||||
DATE_INPUT_FORMATS = [
|
||||
"%Y-%m-%d",
|
||||
]
|
||||
|
||||
# Web URL endpoint for served media files
|
||||
MEDIA_URL = '/media/'
|
||||
# LaTeX rendering settings (django-tex)
|
||||
LATEX_SETTINGS = CONFIG.get('latex', {})
|
||||
|
||||
# The filesystem location for served static files
|
||||
MEDIA_ROOT = CONFIG.get('media_root', os.path.join(BASE_DIR, 'media'))
|
||||
# Is LaTeX rendering enabled? (Off by default)
|
||||
LATEX_ENABLED = LATEX_SETTINGS.get('enabled', False)
|
||||
|
||||
# Set the latex interpreter in the config.yaml settings file
|
||||
LATEX_INTERPRETER = LATEX_SETTINGS.get('interpreter', 'pdflatex')
|
||||
|
||||
LATEX_INTERPRETER_OPTIONS = LATEX_SETTINGS.get('options', '')
|
||||
|
||||
LATEX_GRAPHICSPATH = [
|
||||
# Allow LaTeX files to access the report assets directory
|
||||
os.path.join(MEDIA_ROOT, "report", "assets"),
|
||||
]
|
||||
|
||||
# crispy forms use the bootstrap templates
|
||||
CRISPY_TEMPLATE_PACK = 'bootstrap'
|
||||
CRISPY_TEMPLATE_PACK = 'bootstrap3'
|
||||
|
||||
# Use database transactions when importing / exporting data
|
||||
IMPORT_EXPORT_USE_TRANSACTIONS = True
|
||||
@@ -258,3 +388,8 @@ DBBACKUP_STORAGE = 'django.core.files.storage.FileSystemStorage'
|
||||
DBBACKUP_STORAGE_OPTIONS = {
|
||||
'location': CONFIG.get('backup_dir', tempfile.gettempdir()),
|
||||
}
|
||||
|
||||
# Internal IP addresses allowed to see the debug toolbar
|
||||
INTERNAL_IPS = [
|
||||
'127.0.0.1',
|
||||
]
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
/**
|
||||
* @author: Dennis Hernández
|
||||
* @webSite: http://djhvscf.github.io/Blog
|
||||
* @update: zhixin wen <wenzhixin2010@gmail.com>
|
||||
*/
|
||||
|
||||
!($ => {
|
||||
const diacriticsMap = {}
|
||||
const defaultAccentsDiacritics = [
|
||||
{base: 'A', letters: '\u0041\u24B6\uFF21\u00C0\u00C1\u00C2\u1EA6\u1EA4\u1EAA\u1EA8\u00C3\u0100\u0102\u1EB0\u1EAE\u1EB4\u1EB2\u0226\u01E0\u00C4\u01DE\u1EA2\u00C5\u01FA\u01CD\u0200\u0202\u1EA0\u1EAC\u1EB6\u1E00\u0104\u023A\u2C6F'},
|
||||
{base: 'AA',letters: '\uA732'},
|
||||
{base: 'AE',letters: '\u00C6\u01FC\u01E2'},
|
||||
{base: 'AO',letters: '\uA734'},
|
||||
{base: 'AU',letters: '\uA736'},
|
||||
{base: 'AV',letters: '\uA738\uA73A'},
|
||||
{base: 'AY',letters: '\uA73C'},
|
||||
{base: 'B', letters: '\u0042\u24B7\uFF22\u1E02\u1E04\u1E06\u0243\u0182\u0181'},
|
||||
{base: 'C', letters: '\u0043\u24B8\uFF23\u0106\u0108\u010A\u010C\u00C7\u1E08\u0187\u023B\uA73E'},
|
||||
{base: 'D', letters: '\u0044\u24B9\uFF24\u1E0A\u010E\u1E0C\u1E10\u1E12\u1E0E\u0110\u018B\u018A\u0189\uA779'},
|
||||
{base: 'DZ',letters: '\u01F1\u01C4'},
|
||||
{base: 'Dz',letters: '\u01F2\u01C5'},
|
||||
{base: 'E', letters: '\u0045\u24BA\uFF25\u00C8\u00C9\u00CA\u1EC0\u1EBE\u1EC4\u1EC2\u1EBC\u0112\u1E14\u1E16\u0114\u0116\u00CB\u1EBA\u011A\u0204\u0206\u1EB8\u1EC6\u0228\u1E1C\u0118\u1E18\u1E1A\u0190\u018E'},
|
||||
{base: 'F', letters: '\u0046\u24BB\uFF26\u1E1E\u0191\uA77B'},
|
||||
{base: 'G', letters: '\u0047\u24BC\uFF27\u01F4\u011C\u1E20\u011E\u0120\u01E6\u0122\u01E4\u0193\uA7A0\uA77D\uA77E'},
|
||||
{base: 'H', letters: '\u0048\u24BD\uFF28\u0124\u1E22\u1E26\u021E\u1E24\u1E28\u1E2A\u0126\u2C67\u2C75\uA78D'},
|
||||
{base: 'I', letters: '\u0049\u24BE\uFF29\u00CC\u00CD\u00CE\u0128\u012A\u012C\u0130\u00CF\u1E2E\u1EC8\u01CF\u0208\u020A\u1ECA\u012E\u1E2C\u0197'},
|
||||
{base: 'J', letters: '\u004A\u24BF\uFF2A\u0134\u0248'},
|
||||
{base: 'K', letters: '\u004B\u24C0\uFF2B\u1E30\u01E8\u1E32\u0136\u1E34\u0198\u2C69\uA740\uA742\uA744\uA7A2'},
|
||||
{base: 'L', letters: '\u004C\u24C1\uFF2C\u013F\u0139\u013D\u1E36\u1E38\u013B\u1E3C\u1E3A\u0141\u023D\u2C62\u2C60\uA748\uA746\uA780'},
|
||||
{base: 'LJ',letters: '\u01C7'},
|
||||
{base: 'Lj',letters: '\u01C8'},
|
||||
{base: 'M', letters: '\u004D\u24C2\uFF2D\u1E3E\u1E40\u1E42\u2C6E\u019C'},
|
||||
{base: 'N', letters: '\u004E\u24C3\uFF2E\u01F8\u0143\u00D1\u1E44\u0147\u1E46\u0145\u1E4A\u1E48\u0220\u019D\uA790\uA7A4'},
|
||||
{base: 'NJ',letters: '\u01CA'},
|
||||
{base: 'Nj',letters: '\u01CB'},
|
||||
{base: 'O', letters: '\u004F\u24C4\uFF2F\u00D2\u00D3\u00D4\u1ED2\u1ED0\u1ED6\u1ED4\u00D5\u1E4C\u022C\u1E4E\u014C\u1E50\u1E52\u014E\u022E\u0230\u00D6\u022A\u1ECE\u0150\u01D1\u020C\u020E\u01A0\u1EDC\u1EDA\u1EE0\u1EDE\u1EE2\u1ECC\u1ED8\u01EA\u01EC\u00D8\u01FE\u0186\u019F\uA74A\uA74C'},
|
||||
{base: 'OI',letters: '\u01A2'},
|
||||
{base: 'OO',letters: '\uA74E'},
|
||||
{base: 'OU',letters: '\u0222'},
|
||||
{base: 'OE',letters: '\u008C\u0152'},
|
||||
{base: 'oe',letters: '\u009C\u0153'},
|
||||
{base: 'P', letters: '\u0050\u24C5\uFF30\u1E54\u1E56\u01A4\u2C63\uA750\uA752\uA754'},
|
||||
{base: 'Q', letters: '\u0051\u24C6\uFF31\uA756\uA758\u024A'},
|
||||
{base: 'R', letters: '\u0052\u24C7\uFF32\u0154\u1E58\u0158\u0210\u0212\u1E5A\u1E5C\u0156\u1E5E\u024C\u2C64\uA75A\uA7A6\uA782'},
|
||||
{base: 'S', letters: '\u0053\u24C8\uFF33\u1E9E\u015A\u1E64\u015C\u1E60\u0160\u1E66\u1E62\u1E68\u0218\u015E\u2C7E\uA7A8\uA784'},
|
||||
{base: 'T', letters: '\u0054\u24C9\uFF34\u1E6A\u0164\u1E6C\u021A\u0162\u1E70\u1E6E\u0166\u01AC\u01AE\u023E\uA786'},
|
||||
{base: 'TZ',letters: '\uA728'},
|
||||
{base: 'U', letters: '\u0055\u24CA\uFF35\u00D9\u00DA\u00DB\u0168\u1E78\u016A\u1E7A\u016C\u00DC\u01DB\u01D7\u01D5\u01D9\u1EE6\u016E\u0170\u01D3\u0214\u0216\u01AF\u1EEA\u1EE8\u1EEE\u1EEC\u1EF0\u1EE4\u1E72\u0172\u1E76\u1E74\u0244'},
|
||||
{base: 'V', letters: '\u0056\u24CB\uFF36\u1E7C\u1E7E\u01B2\uA75E\u0245'},
|
||||
{base: 'VY',letters: '\uA760'},
|
||||
{base: 'W', letters: '\u0057\u24CC\uFF37\u1E80\u1E82\u0174\u1E86\u1E84\u1E88\u2C72'},
|
||||
{base: 'X', letters: '\u0058\u24CD\uFF38\u1E8A\u1E8C'},
|
||||
{base: 'Y', letters: '\u0059\u24CE\uFF39\u1EF2\u00DD\u0176\u1EF8\u0232\u1E8E\u0178\u1EF6\u1EF4\u01B3\u024E\u1EFE'},
|
||||
{base: 'Z', letters: '\u005A\u24CF\uFF3A\u0179\u1E90\u017B\u017D\u1E92\u1E94\u01B5\u0224\u2C7F\u2C6B\uA762'},
|
||||
{base: 'a', letters: '\u0061\u24D0\uFF41\u1E9A\u00E0\u00E1\u00E2\u1EA7\u1EA5\u1EAB\u1EA9\u00E3\u0101\u0103\u1EB1\u1EAF\u1EB5\u1EB3\u0227\u01E1\u00E4\u01DF\u1EA3\u00E5\u01FB\u01CE\u0201\u0203\u1EA1\u1EAD\u1EB7\u1E01\u0105\u2C65\u0250'},
|
||||
{base: 'aa',letters: '\uA733'},
|
||||
{base: 'ae',letters: '\u00E6\u01FD\u01E3'},
|
||||
{base: 'ao',letters: '\uA735'},
|
||||
{base: 'au',letters: '\uA737'},
|
||||
{base: 'av',letters: '\uA739\uA73B'},
|
||||
{base: 'ay',letters: '\uA73D'},
|
||||
{base: 'b', letters: '\u0062\u24D1\uFF42\u1E03\u1E05\u1E07\u0180\u0183\u0253'},
|
||||
{base: 'c', letters: '\u0063\u24D2\uFF43\u0107\u0109\u010B\u010D\u00E7\u1E09\u0188\u023C\uA73F\u2184'},
|
||||
{base: 'd', letters: '\u0064\u24D3\uFF44\u1E0B\u010F\u1E0D\u1E11\u1E13\u1E0F\u0111\u018C\u0256\u0257\uA77A'},
|
||||
{base: 'dz',letters: '\u01F3\u01C6'},
|
||||
{base: 'e', letters: '\u0065\u24D4\uFF45\u00E8\u00E9\u00EA\u1EC1\u1EBF\u1EC5\u1EC3\u1EBD\u0113\u1E15\u1E17\u0115\u0117\u00EB\u1EBB\u011B\u0205\u0207\u1EB9\u1EC7\u0229\u1E1D\u0119\u1E19\u1E1B\u0247\u025B\u01DD'},
|
||||
{base: 'f', letters: '\u0066\u24D5\uFF46\u1E1F\u0192\uA77C'},
|
||||
{base: 'g', letters: '\u0067\u24D6\uFF47\u01F5\u011D\u1E21\u011F\u0121\u01E7\u0123\u01E5\u0260\uA7A1\u1D79\uA77F'},
|
||||
{base: 'h', letters: '\u0068\u24D7\uFF48\u0125\u1E23\u1E27\u021F\u1E25\u1E29\u1E2B\u1E96\u0127\u2C68\u2C76\u0265'},
|
||||
{base: 'hv',letters: '\u0195'},
|
||||
{base: 'i', letters: '\u0069\u24D8\uFF49\u00EC\u00ED\u00EE\u0129\u012B\u012D\u00EF\u1E2F\u1EC9\u01D0\u0209\u020B\u1ECB\u012F\u1E2D\u0268\u0131'},
|
||||
{base: 'j', letters: '\u006A\u24D9\uFF4A\u0135\u01F0\u0249'},
|
||||
{base: 'k', letters: '\u006B\u24DA\uFF4B\u1E31\u01E9\u1E33\u0137\u1E35\u0199\u2C6A\uA741\uA743\uA745\uA7A3'},
|
||||
{base: 'l', letters: '\u006C\u24DB\uFF4C\u0140\u013A\u013E\u1E37\u1E39\u013C\u1E3D\u1E3B\u017F\u0142\u019A\u026B\u2C61\uA749\uA781\uA747'},
|
||||
{base: 'lj',letters: '\u01C9'},
|
||||
{base: 'm', letters: '\u006D\u24DC\uFF4D\u1E3F\u1E41\u1E43\u0271\u026F'},
|
||||
{base: 'n', letters: '\u006E\u24DD\uFF4E\u01F9\u0144\u00F1\u1E45\u0148\u1E47\u0146\u1E4B\u1E49\u019E\u0272\u0149\uA791\uA7A5'},
|
||||
{base: 'nj',letters: '\u01CC'},
|
||||
{base: 'o', letters: '\u006F\u24DE\uFF4F\u00F2\u00F3\u00F4\u1ED3\u1ED1\u1ED7\u1ED5\u00F5\u1E4D\u022D\u1E4F\u014D\u1E51\u1E53\u014F\u022F\u0231\u00F6\u022B\u1ECF\u0151\u01D2\u020D\u020F\u01A1\u1EDD\u1EDB\u1EE1\u1EDF\u1EE3\u1ECD\u1ED9\u01EB\u01ED\u00F8\u01FF\u0254\uA74B\uA74D\u0275'},
|
||||
{base: 'oi',letters: '\u01A3'},
|
||||
{base: 'ou',letters: '\u0223'},
|
||||
{base: 'oo',letters: '\uA74F'},
|
||||
{base: 'p',letters: '\u0070\u24DF\uFF50\u1E55\u1E57\u01A5\u1D7D\uA751\uA753\uA755'},
|
||||
{base: 'q',letters: '\u0071\u24E0\uFF51\u024B\uA757\uA759'},
|
||||
{base: 'r',letters: '\u0072\u24E1\uFF52\u0155\u1E59\u0159\u0211\u0213\u1E5B\u1E5D\u0157\u1E5F\u024D\u027D\uA75B\uA7A7\uA783'},
|
||||
{base: 's',letters: '\u0073\u24E2\uFF53\u00DF\u015B\u1E65\u015D\u1E61\u0161\u1E67\u1E63\u1E69\u0219\u015F\u023F\uA7A9\uA785\u1E9B'},
|
||||
{base: 't',letters: '\u0074\u24E3\uFF54\u1E6B\u1E97\u0165\u1E6D\u021B\u0163\u1E71\u1E6F\u0167\u01AD\u0288\u2C66\uA787'},
|
||||
{base: 'tz',letters: '\uA729'},
|
||||
{base: 'u',letters: '\u0075\u24E4\uFF55\u00F9\u00FA\u00FB\u0169\u1E79\u016B\u1E7B\u016D\u00FC\u01DC\u01D8\u01D6\u01DA\u1EE7\u016F\u0171\u01D4\u0215\u0217\u01B0\u1EEB\u1EE9\u1EEF\u1EED\u1EF1\u1EE5\u1E73\u0173\u1E77\u1E75\u0289'},
|
||||
{base: 'v',letters: '\u0076\u24E5\uFF56\u1E7D\u1E7F\u028B\uA75F\u028C'},
|
||||
{base: 'vy',letters: '\uA761'},
|
||||
{base: 'w',letters: '\u0077\u24E6\uFF57\u1E81\u1E83\u0175\u1E87\u1E85\u1E98\u1E89\u2C73'},
|
||||
{base: 'x',letters: '\u0078\u24E7\uFF58\u1E8B\u1E8D'},
|
||||
{base: 'y',letters: '\u0079\u24E8\uFF59\u1EF3\u00FD\u0177\u1EF9\u0233\u1E8F\u00FF\u1EF7\u1E99\u1EF5\u01B4\u024F\u1EFF'},
|
||||
{base: 'z',letters: '\u007A\u24E9\uFF5A\u017A\u1E91\u017C\u017E\u1E93\u1E95\u01B6\u0225\u0240\u2C6C\uA763'}
|
||||
]
|
||||
|
||||
const initNeutraliser = () => {
|
||||
for (const diacritic of defaultAccentsDiacritics) {
|
||||
const letters = diacritic.letters
|
||||
for (let i = 0; i < letters.length; i++) {
|
||||
diacriticsMap[letters[i]] = diacritic.base
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* eslint-disable no-control-regex */
|
||||
const removeDiacritics = str => str.replace(/[^\u0000-\u007E]/g, a => diacriticsMap[a] || a)
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
searchAccentNeutralise: false
|
||||
})
|
||||
|
||||
$.BootstrapTable = class extends $.BootstrapTable {
|
||||
init () {
|
||||
if (this.options.searchAccentNeutralise) {
|
||||
initNeutraliser()
|
||||
}
|
||||
super.init()
|
||||
}
|
||||
|
||||
initSearch () {
|
||||
if (this.options.sidePagination !== 'server') {
|
||||
let s = this.searchText && this.searchText.toLowerCase()
|
||||
const f = $.isEmptyObject(this.filterColumns) ? null : this.filterColumns
|
||||
|
||||
// Check filter
|
||||
this.data = f ? this.options.data.filter((item, i) => {
|
||||
for (const key in f) {
|
||||
if (item[key] !== f[key]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}) : this.options.data
|
||||
|
||||
this.data = s ? this.options.data.filter((item, i) => {
|
||||
for (let [key, value] of Object.entries(item)) {
|
||||
key = $.isNumeric(key) ? parseInt(key, 10) : key
|
||||
const column = this.columns[this.fieldsColumnsIndex[key]]
|
||||
const j = this.header.fields.indexOf(key)
|
||||
|
||||
if (column && column.searchFormatter) {
|
||||
value = $.fn.bootstrapTable.utils.calculateObjectValue(column,
|
||||
this.header.formatters[j], [value, item, i], value)
|
||||
}
|
||||
|
||||
const index = this.header.fields.indexOf(key)
|
||||
if (index !== -1 && this.header.searchables[index] && typeof value === 'string') {
|
||||
if (this.options.searchAccentNeutralise) {
|
||||
value = removeDiacritics(value)
|
||||
s = removeDiacritics(s)
|
||||
}
|
||||
if (this.options.strictSearch) {
|
||||
if ((`${value}`).toLowerCase() === s) {
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
if ((`${value}`).toLowerCase().includes(s)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}) : this.data
|
||||
}
|
||||
}
|
||||
}
|
||||
})(jQuery)
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Accent Neutralise",
|
||||
"version": "1.0.0",
|
||||
"description": "Plugin to neutralise the words.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/accent-neutralise",
|
||||
"example": "#",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-accent-neutralise",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/accent-neutralise"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "djhvscf",
|
||||
"image": "https://avatars1.githubusercontent.com/u/4496763"
|
||||
}
|
||||
}
|
||||
116
InvenTree/InvenTree/static/bootstrap-table/extensions/addrbar/bootstrap-table-addrbar.js
vendored
Normal file
116
InvenTree/InvenTree/static/bootstrap-table/extensions/addrbar/bootstrap-table-addrbar.js
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
/**
|
||||
* @author: general
|
||||
* @website: note.generals.space
|
||||
* @email: generals.space@gmail.com
|
||||
* @github: https://github.com/generals-space/bootstrap-table-addrbar
|
||||
* @update: zhixin wen <wenzhixin2010@gmail.com>
|
||||
*/
|
||||
|
||||
($ => {
|
||||
/*
|
||||
* function: 获取浏览器地址栏中的指定参数.
|
||||
* key: 参数名
|
||||
* url: 默认为当前地址栏
|
||||
*/
|
||||
function _GET (key, url = window.location.search) {
|
||||
/*
|
||||
* 注意这里正则表达式的书写方法
|
||||
* (^|&)key匹配: 直接以key开始或以&key开始的字符串
|
||||
* 同理(&|$)表示以&结束或是直接结束的字符串
|
||||
* ...当然, 我并不知道这种用法.
|
||||
*/
|
||||
const reg = new RegExp(`(^|&)${key}=([^&]*)(&|$)`)
|
||||
const result = url.substr(1).match(reg)
|
||||
|
||||
if (result) {
|
||||
return decodeURIComponent(result[2])
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/*
|
||||
* function: 根据给定参数生成url地址
|
||||
* var dic = {name: 'genreal', age: 24}
|
||||
* var url = 'https://www.baidu.com?age=22';
|
||||
* _buildUrl(dic, url);
|
||||
* 将得到"https://www.baidu.com?age=24&name=genreal"
|
||||
* 哦, 忽略先后顺序吧...
|
||||
*
|
||||
* 补充: 可以参考浏览器URLSearchParams对象, 更加方便和强大.
|
||||
* 考虑到兼容性, 暂时不使用这个工具.
|
||||
*/
|
||||
|
||||
function _buildUrl (dict, url = window.location.search) {
|
||||
for (const [key, val] of Object.entries(dict)) {
|
||||
// 搜索name=general这种形式的字符串(&是分隔符)
|
||||
const pattern = `${key}=([^&]*)`
|
||||
const targetStr = `${key}=${val}`
|
||||
|
||||
/*
|
||||
* 如果目标url中包含了key键, 我们需要将它替换成我们自己的val
|
||||
* 不然就直接添加好了.
|
||||
*/
|
||||
if (url.match(pattern)) {
|
||||
const tmp = new RegExp(`(${key}=)([^&]*)`, 'gi')
|
||||
url = url.replace(tmp, targetStr)
|
||||
} else {
|
||||
const seperator = url.match('[?]') ? '&' : '?'
|
||||
url = url + seperator + targetStr
|
||||
}
|
||||
}
|
||||
if (location.hash) {
|
||||
url += location.hash
|
||||
}
|
||||
return url
|
||||
}
|
||||
|
||||
$.BootstrapTable = class extends $.BootstrapTable {
|
||||
init () {
|
||||
// 拥有addrbar选项并且其值为true的才会继续执行
|
||||
if (this.options.addrbar) {
|
||||
// 标志位, 初始加载后关闭
|
||||
this.addrbarInit = true
|
||||
const _prefix = this.options.addrPrefix || ''
|
||||
|
||||
// 优先级排序: 用户指定值最优先, 未指定时从地址栏获取, 未获取到时采用默认值
|
||||
this.options.pageSize = this.options.pageSize || (
|
||||
_GET(`${_prefix}limit`) ? parseInt(_GET(`${_prefix}limit`)) : $.BootstrapTable.DEFAULTS.pageSize
|
||||
)
|
||||
this.options.pageNumber = this.options.pageNumber || (
|
||||
_GET(`${_prefix}page`) ? parseInt(_GET(`${_prefix}page`)) : $.BootstrapTable.DEFAULTS.pageNumber
|
||||
)
|
||||
this.options.sortOrder = this.options.sortOrder || (
|
||||
_GET(`${_prefix}order`) ? _GET(`${_prefix}order`) : $.BootstrapTable.DEFAULTS.sortOrder
|
||||
)
|
||||
this.options.sortName = this.options.sortName || (
|
||||
_GET(`${_prefix}sort`) ? _GET(`${_prefix}sort`) : 'id'
|
||||
)
|
||||
this.options.searchText = this.options.searchText || (
|
||||
_GET(`${_prefix}search`) ? _GET(`${_prefix}search`) : $.BootstrapTable.DEFAULTS.searchText
|
||||
)
|
||||
|
||||
const _onLoadSuccess = this.options.onLoadSuccess
|
||||
|
||||
this.options.onLoadSuccess = data => {
|
||||
if (this.addrbarInit) {
|
||||
this.addrbarInit = false
|
||||
} else {
|
||||
const params = {}
|
||||
params[`${_prefix}page`] = this.options.pageNumber,
|
||||
params[`${_prefix}limit`] = this.options.pageSize,
|
||||
params[`${_prefix}order`] = this.options.sortOrder,
|
||||
params[`${_prefix}sort`] = this.options.sortName,
|
||||
params[`${_prefix}search`] = this.options.searchText
|
||||
// h5提供的修改浏览器地址栏的方法
|
||||
window.history.pushState({}, '', _buildUrl(params))
|
||||
}
|
||||
|
||||
if (_onLoadSuccess) {
|
||||
_onLoadSuccess.call(this, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
super.init()
|
||||
}
|
||||
}
|
||||
})(jQuery)
|
||||
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* @author: Alec Fenichel
|
||||
* @webSite: https://fenichelar.com
|
||||
* @update: zhixin wen <wenzhixin2010@gmail.com>
|
||||
*/
|
||||
|
||||
($ => {
|
||||
const Utils = $.fn.bootstrapTable.utils
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
autoRefresh: false,
|
||||
autoRefreshInterval: 60,
|
||||
autoRefreshSilent: true,
|
||||
autoRefreshStatus: true,
|
||||
autoRefreshFunction: null
|
||||
})
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults.icons, {
|
||||
autoRefresh: Utils.bootstrapVersion === 4 ? 'fa-clock' : 'glyphicon-time icon-time'
|
||||
})
|
||||
|
||||
$.extend($.fn.bootstrapTable.locales, {
|
||||
formatAutoRefresh () {
|
||||
return 'Auto Refresh'
|
||||
}
|
||||
})
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, $.fn.bootstrapTable.locales)
|
||||
|
||||
$.BootstrapTable = class extends $.BootstrapTable {
|
||||
init (...args) {
|
||||
super.init(...args)
|
||||
|
||||
if (this.options.autoRefresh && this.options.autoRefreshStatus) {
|
||||
this.options.autoRefreshFunction = setInterval(() => {
|
||||
this.refresh({silent: this.options.autoRefreshSilent})
|
||||
}, this.options.autoRefreshInterval * 1000)
|
||||
}
|
||||
}
|
||||
|
||||
initToolbar (...args) {
|
||||
super.initToolbar(...args)
|
||||
|
||||
if (this.options.autoRefresh) {
|
||||
const $btnGroup = this.$toolbar.find('>.btn-group')
|
||||
let $btnAutoRefresh = $btnGroup.find('.auto-refresh')
|
||||
|
||||
if (!$btnAutoRefresh.length) {
|
||||
$btnAutoRefresh = $(`
|
||||
<button class="auto-refresh btn${Utils.sprintf(' btn-%s', this.options.buttonsClass)}
|
||||
${Utils.sprintf(' btn-%s', this.options.iconSize)}
|
||||
${this.options.autoRefreshStatus ? 'active' : ''}"
|
||||
type="button" title="${this.options.formatAutoRefresh()}">
|
||||
<i class="${this.options.iconsPrefix} ${this.options.icons.autoRefresh}"></i>
|
||||
</button>
|
||||
`).appendTo($btnGroup)
|
||||
|
||||
$btnAutoRefresh.on('click', $.proxy(this.toggleAutoRefresh, this))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
toggleAutoRefresh () {
|
||||
if (this.options.autoRefresh) {
|
||||
if (this.options.autoRefreshStatus) {
|
||||
clearInterval(this.options.autoRefreshFunction)
|
||||
this.$toolbar.find('>.btn-group').find('.auto-refresh').removeClass('active')
|
||||
} else {
|
||||
this.options.autoRefreshFunction = setInterval(() => {
|
||||
this.refresh({silent: this.options.autoRefreshSilent})
|
||||
}, this.options.autoRefreshInterval * 1000)
|
||||
this.$toolbar.find('>.btn-group').find('.auto-refresh').addClass('active')
|
||||
}
|
||||
this.options.autoRefreshStatus = !this.options.autoRefreshStatus
|
||||
}
|
||||
}
|
||||
}
|
||||
})(jQuery)
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Auto Refresh",
|
||||
"version": "1.0.0",
|
||||
"description": "Plugin to automatically refresh the table on an interval.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/auto-refresh",
|
||||
"example": "#",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-auto-refresh",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/auto-refresh"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "fenichaler",
|
||||
"image": "https://avatars.githubusercontent.com/u/3437075"
|
||||
}
|
||||
}
|
||||
401
InvenTree/InvenTree/static/bootstrap-table/extensions/cookie/bootstrap-table-cookie.js
vendored
Normal file
401
InvenTree/InvenTree/static/bootstrap-table/extensions/cookie/bootstrap-table-cookie.js
vendored
Normal file
@@ -0,0 +1,401 @@
|
||||
/**
|
||||
* @author: Dennis Hernández
|
||||
* @webSite: http://djhvscf.github.io/Blog
|
||||
* @update zhixin wen <wenzhixin2010@gmail.com>
|
||||
*/
|
||||
|
||||
($ => {
|
||||
const UtilsCookie = {
|
||||
cookieIds: {
|
||||
sortOrder: 'bs.table.sortOrder',
|
||||
sortName: 'bs.table.sortName',
|
||||
pageNumber: 'bs.table.pageNumber',
|
||||
pageList: 'bs.table.pageList',
|
||||
columns: 'bs.table.columns',
|
||||
searchText: 'bs.table.searchText',
|
||||
filterControl: 'bs.table.filterControl',
|
||||
filterBy: 'bs.table.filterBy'
|
||||
},
|
||||
getCurrentHeader (that) {
|
||||
let header = that.$header
|
||||
if (that.options.height) {
|
||||
header = that.$tableHeader
|
||||
}
|
||||
|
||||
return header
|
||||
},
|
||||
getCurrentSearchControls (that) {
|
||||
let searchControls = 'select, input'
|
||||
if (that.options.height) {
|
||||
searchControls = 'table select, table input'
|
||||
}
|
||||
|
||||
return searchControls
|
||||
},
|
||||
cookieEnabled () {
|
||||
return !!(navigator.cookieEnabled)
|
||||
},
|
||||
inArrayCookiesEnabled (cookieName, cookiesEnabled) {
|
||||
let index = -1
|
||||
|
||||
for (let i = 0; i < cookiesEnabled.length; i++) {
|
||||
if (cookieName.toLowerCase() === cookiesEnabled[i].toLowerCase()) {
|
||||
index = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return index
|
||||
},
|
||||
setCookie (that, cookieName, cookieValue) {
|
||||
if ((!that.options.cookie) || (!UtilsCookie.cookieEnabled()) || (that.options.cookieIdTable === '')) {
|
||||
return
|
||||
}
|
||||
|
||||
if (UtilsCookie.inArrayCookiesEnabled(cookieName, that.options.cookiesEnabled) === -1) {
|
||||
return
|
||||
}
|
||||
|
||||
cookieName = `${that.options.cookieIdTable}.${cookieName}`
|
||||
|
||||
switch (that.options.cookieStorage) {
|
||||
case 'cookieStorage':
|
||||
document.cookie = [
|
||||
cookieName, '=', encodeURIComponent(cookieValue),
|
||||
`; expires=${UtilsCookie.calculateExpiration(that.options.cookieExpire)}`,
|
||||
that.options.cookiePath ? `; path=${that.options.cookiePath}` : '',
|
||||
that.options.cookieDomain ? `; domain=${that.options.cookieDomain}` : '',
|
||||
that.options.cookieSecure ? '; secure' : ''
|
||||
].join('')
|
||||
break
|
||||
case 'localStorage':
|
||||
localStorage.setItem(cookieName, cookieValue)
|
||||
break
|
||||
case 'sessionStorage':
|
||||
sessionStorage.setItem(cookieName, cookieValue)
|
||||
break
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
},
|
||||
getCookie (that, tableName, cookieName) {
|
||||
if (!cookieName) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (UtilsCookie.inArrayCookiesEnabled(cookieName, that.options.cookiesEnabled) === -1) {
|
||||
return null
|
||||
}
|
||||
|
||||
cookieName = `${tableName}.${cookieName}`
|
||||
|
||||
switch (that.options.cookieStorage) {
|
||||
case 'cookieStorage':
|
||||
const value = `; ${document.cookie}`
|
||||
const parts = value.split(`; ${cookieName}=`)
|
||||
return parts.length === 2 ? decodeURIComponent(parts.pop().split(';').shift()) : null
|
||||
case 'localStorage':
|
||||
return localStorage.getItem(cookieName)
|
||||
case 'sessionStorage':
|
||||
return sessionStorage.getItem(cookieName)
|
||||
default:
|
||||
return null
|
||||
}
|
||||
},
|
||||
deleteCookie (that, tableName, cookieName) {
|
||||
cookieName = `${tableName}.${cookieName}`
|
||||
|
||||
switch (that.options.cookieStorage) {
|
||||
case 'cookieStorage':
|
||||
document.cookie = [
|
||||
encodeURIComponent(cookieName), '=',
|
||||
'; expires=Thu, 01 Jan 1970 00:00:00 GMT',
|
||||
that.options.cookiePath ? `; path=${that.options.cookiePath}` : '',
|
||||
that.options.cookieDomain ? `; domain=${that.options.cookieDomain}` : ''
|
||||
].join('')
|
||||
break
|
||||
case 'localStorage':
|
||||
localStorage.removeItem(cookieName)
|
||||
break
|
||||
case 'sessionStorage':
|
||||
sessionStorage.removeItem(cookieName)
|
||||
break
|
||||
default:
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
calculateExpiration (cookieExpire) {
|
||||
const time = cookieExpire.replace(/[0-9]*/, '') // s,mi,h,d,m,y
|
||||
cookieExpire = cookieExpire.replace(/[A-Za-z]{1,2}/, '') // number
|
||||
|
||||
switch (time.toLowerCase()) {
|
||||
case 's':
|
||||
cookieExpire = +cookieExpire
|
||||
break
|
||||
case 'mi':
|
||||
cookieExpire *= 60
|
||||
break
|
||||
case 'h':
|
||||
cookieExpire = cookieExpire * 60 * 60
|
||||
break
|
||||
case 'd':
|
||||
cookieExpire = cookieExpire * 24 * 60 * 60
|
||||
break
|
||||
case 'm':
|
||||
cookieExpire = cookieExpire * 30 * 24 * 60 * 60
|
||||
break
|
||||
case 'y':
|
||||
cookieExpire = cookieExpire * 365 * 24 * 60 * 60
|
||||
break
|
||||
default:
|
||||
cookieExpire = undefined
|
||||
break
|
||||
}
|
||||
if (!cookieExpire) {
|
||||
return ''
|
||||
}
|
||||
const d = new Date()
|
||||
d.setTime(d.getTime() + cookieExpire * 1000)
|
||||
return d.toGMTString()
|
||||
},
|
||||
initCookieFilters (bootstrapTable) {
|
||||
setTimeout(() => {
|
||||
const parsedCookieFilters = JSON.parse(UtilsCookie.getCookie(bootstrapTable, bootstrapTable.options.cookieIdTable, UtilsCookie.cookieIds.filterControl))
|
||||
|
||||
if (!bootstrapTable.options.filterControlValuesLoaded && parsedCookieFilters) {
|
||||
|
||||
const cachedFilters = {}
|
||||
const header = UtilsCookie.getCurrentHeader(bootstrapTable)
|
||||
const searchControls = UtilsCookie.getCurrentSearchControls(bootstrapTable)
|
||||
|
||||
const applyCookieFilters = (element, filteredCookies) => {
|
||||
$(filteredCookies).each((i, cookie) => {
|
||||
if (cookie.text !== '') {
|
||||
$(element).val(cookie.text)
|
||||
cachedFilters[cookie.field] = cookie.text
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
header.find(searchControls).each(function () {
|
||||
const field = $(this).closest('[data-field]').data('field')
|
||||
const filteredCookies = parsedCookieFilters.filter(cookie => cookie.field === field)
|
||||
|
||||
applyCookieFilters(this, filteredCookies)
|
||||
})
|
||||
|
||||
bootstrapTable.initColumnSearch(cachedFilters)
|
||||
bootstrapTable.options.filterControlValuesLoaded = true
|
||||
bootstrapTable.initServer()
|
||||
}
|
||||
}, 250)
|
||||
}
|
||||
}
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
cookie: false,
|
||||
cookieExpire: '2h',
|
||||
cookiePath: null,
|
||||
cookieDomain: null,
|
||||
cookieSecure: null,
|
||||
cookieIdTable: '',
|
||||
cookiesEnabled: [
|
||||
'bs.table.sortOrder', 'bs.table.sortName',
|
||||
'bs.table.pageNumber', 'bs.table.pageList',
|
||||
'bs.table.columns', 'bs.table.searchText',
|
||||
'bs.table.filterControl', 'bs.table.filterBy'
|
||||
],
|
||||
cookieStorage: 'cookieStorage', // localStorage, sessionStorage
|
||||
// internal variable
|
||||
filterControls: [],
|
||||
filterControlValuesLoaded: false
|
||||
})
|
||||
|
||||
$.fn.bootstrapTable.methods.push('getCookies')
|
||||
$.fn.bootstrapTable.methods.push('deleteCookie')
|
||||
|
||||
$.extend($.fn.bootstrapTable.utils, {
|
||||
setCookie: UtilsCookie.setCookie,
|
||||
getCookie: UtilsCookie.getCookie
|
||||
})
|
||||
|
||||
$.BootstrapTable = class extends $.BootstrapTable {
|
||||
init () {
|
||||
// FilterBy logic
|
||||
const filterByCookie = JSON.parse(UtilsCookie.getCookie(this, this.options.cookieIdTable, UtilsCookie.cookieIds.filterBy))
|
||||
this.filterColumns = filterByCookie ? filterByCookie : {}
|
||||
|
||||
// FilterControl logic
|
||||
this.options.filterControls = []
|
||||
this.options.filterControlValuesLoaded = false
|
||||
|
||||
this.options.cookiesEnabled = typeof this.options.cookiesEnabled === 'string' ?
|
||||
this.options.cookiesEnabled.replace('[', '').replace(']', '')
|
||||
.replace(/ /g, '').toLowerCase().split(',') :
|
||||
this.options.cookiesEnabled
|
||||
|
||||
if (this.options.filterControl) {
|
||||
const that = this
|
||||
this.$el.on('column-search.bs.table', (e, field, text) => {
|
||||
let isNewField = true
|
||||
|
||||
for (let i = 0; i < that.options.filterControls.length; i++) {
|
||||
if (that.options.filterControls[i].field === field) {
|
||||
that.options.filterControls[i].text = text
|
||||
isNewField = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if (isNewField) {
|
||||
that.options.filterControls.push({
|
||||
field,
|
||||
text
|
||||
})
|
||||
}
|
||||
|
||||
UtilsCookie.setCookie(that, UtilsCookie.cookieIds.filterControl, JSON.stringify(that.options.filterControls))
|
||||
}).on('created-controls.bs.table', UtilsCookie.initCookieFilters(that))
|
||||
}
|
||||
super.init()
|
||||
}
|
||||
|
||||
initServer (...args) {
|
||||
if (
|
||||
this.options.cookie &&
|
||||
this.options.filterControl &&
|
||||
!this.options.filterControlValuesLoaded
|
||||
) {
|
||||
const cookie = JSON.parse(UtilsCookie.getCookie(this, this.options.cookieIdTable, UtilsCookie.cookieIds.filterControl))
|
||||
if (cookie) {
|
||||
return
|
||||
}
|
||||
}
|
||||
super.initServer(...args)
|
||||
}
|
||||
|
||||
initTable (...args) {
|
||||
super.initTable(...args)
|
||||
this.initCookie()
|
||||
}
|
||||
|
||||
onSort (...args) {
|
||||
super.onSort(...args)
|
||||
UtilsCookie.setCookie(this, UtilsCookie.cookieIds.sortOrder, this.options.sortOrder)
|
||||
UtilsCookie.setCookie(this, UtilsCookie.cookieIds.sortName, this.options.sortName)
|
||||
}
|
||||
|
||||
onPageNumber (...args) {
|
||||
super.onPageNumber(...args)
|
||||
UtilsCookie.setCookie(this, UtilsCookie.cookieIds.pageNumber, this.options.pageNumber)
|
||||
}
|
||||
|
||||
onPageListChange (...args) {
|
||||
super.onPageListChange(...args)
|
||||
UtilsCookie.setCookie(this, UtilsCookie.cookieIds.pageList, this.options.pageSize)
|
||||
UtilsCookie.setCookie(this, UtilsCookie.cookieIds.pageNumber, this.options.pageNumber)
|
||||
}
|
||||
|
||||
onPagePre (...args) {
|
||||
super.onPagePre(...args)
|
||||
UtilsCookie.setCookie(this, UtilsCookie.cookieIds.pageNumber, this.options.pageNumber)
|
||||
}
|
||||
|
||||
onPageNext (...args) {
|
||||
super.onPageNext(...args)
|
||||
UtilsCookie.setCookie(this, UtilsCookie.cookieIds.pageNumber, this.options.pageNumber)
|
||||
}
|
||||
|
||||
toggleColumn (...args) {
|
||||
super.toggleColumn(...args)
|
||||
|
||||
const visibleColumns = []
|
||||
|
||||
$.each(this.columns, (i, column) => {
|
||||
if (column.visible) {
|
||||
visibleColumns.push(column.field)
|
||||
}
|
||||
})
|
||||
|
||||
UtilsCookie.setCookie(this, UtilsCookie.cookieIds.columns, JSON.stringify(visibleColumns))
|
||||
}
|
||||
|
||||
selectPage (page) {
|
||||
super.selectPage(page)
|
||||
UtilsCookie.setCookie(this, UtilsCookie.cookieIds.pageNumber, page)
|
||||
}
|
||||
|
||||
onSearch (event) {
|
||||
super.onSearch(event)
|
||||
|
||||
if ($(event.currentTarget).parent().hasClass('search')) {
|
||||
UtilsCookie.setCookie(this, UtilsCookie.cookieIds.searchText, this.searchText)
|
||||
}
|
||||
UtilsCookie.setCookie(this, UtilsCookie.cookieIds.pageNumber, this.options.pageNumber)
|
||||
}
|
||||
|
||||
filterBy (...args) {
|
||||
super.filterBy(...args)
|
||||
UtilsCookie.setCookie(this, UtilsCookie.cookieIds.filterBy, JSON.stringify(this.filterColumns))
|
||||
}
|
||||
|
||||
initCookie () {
|
||||
if (!this.options.cookie) {
|
||||
return
|
||||
}
|
||||
|
||||
if ((this.options.cookieIdTable === '') || (this.options.cookieExpire === '') || (!UtilsCookie.cookieEnabled())) {
|
||||
console.error('Configuration error. Please review the cookieIdTable and the cookieExpire property. If the properties are correct, then this browser does not support cookies.')
|
||||
this.options.cookie = false // Make sure that the cookie extension is disabled
|
||||
return
|
||||
}
|
||||
|
||||
const sortOrderCookie = UtilsCookie.getCookie(this, this.options.cookieIdTable, UtilsCookie.cookieIds.sortOrder)
|
||||
const sortOrderNameCookie = UtilsCookie.getCookie(this, this.options.cookieIdTable, UtilsCookie.cookieIds.sortName)
|
||||
const pageNumberCookie = UtilsCookie.getCookie(this, this.options.cookieIdTable, UtilsCookie.cookieIds.pageNumber)
|
||||
const pageListCookie = UtilsCookie.getCookie(this, this.options.cookieIdTable, UtilsCookie.cookieIds.pageList)
|
||||
const columnsCookie = JSON.parse(UtilsCookie.getCookie(this, this.options.cookieIdTable, UtilsCookie.cookieIds.columns))
|
||||
const searchTextCookie = UtilsCookie.getCookie(this, this.options.cookieIdTable, UtilsCookie.cookieIds.searchText)
|
||||
|
||||
// sortOrder
|
||||
this.options.sortOrder = sortOrderCookie ? sortOrderCookie : this.options.sortOrder
|
||||
// sortName
|
||||
this.options.sortName = sortOrderNameCookie ? sortOrderNameCookie : this.options.sortName
|
||||
// pageNumber
|
||||
this.options.pageNumber = pageNumberCookie ? +pageNumberCookie : this.options.pageNumber
|
||||
// pageSize
|
||||
this.options.pageSize = pageListCookie ? pageListCookie === this.options.formatAllRows() ? pageListCookie : +pageListCookie : this.options.pageSize
|
||||
// searchText
|
||||
this.options.searchText = searchTextCookie ? searchTextCookie : ''
|
||||
|
||||
if (columnsCookie) {
|
||||
$.each(this.columns, (i, column) => {
|
||||
column.visible = $.inArray(column.field, columnsCookie) !== -1
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
getCookies () {
|
||||
const bootstrapTable = this
|
||||
const cookies = {}
|
||||
$.each(UtilsCookie.cookieIds, (key, value) => {
|
||||
cookies[key] = UtilsCookie.getCookie(bootstrapTable, bootstrapTable.options.cookieIdTable, value)
|
||||
if (key === 'columns') {
|
||||
cookies[key] = JSON.parse(cookies[key])
|
||||
}
|
||||
})
|
||||
return cookies
|
||||
}
|
||||
|
||||
deleteCookie (cookieName) {
|
||||
if ((cookieName === '') || (!UtilsCookie.cookieEnabled())) {
|
||||
return
|
||||
}
|
||||
|
||||
UtilsCookie.deleteCookie(this, this.options.cookieIdTable, UtilsCookie.cookieIds[cookieName])
|
||||
}
|
||||
}
|
||||
|
||||
})(jQuery)
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Cookie",
|
||||
"version": "1.2.1",
|
||||
"description": "Plugin to use the cookie of the browser.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/cookie",
|
||||
"example": "http://issues.wenzhixin.net.cn/bootstrap-table/#extensions/cookie.html",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-cookie",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/cookie"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "djhvscf",
|
||||
"image": "https://avatars1.githubusercontent.com/u/4496763"
|
||||
}
|
||||
}
|
||||
102
InvenTree/InvenTree/static/bootstrap-table/extensions/copy-rows/bootstrap-table-copy-rows.js
vendored
Normal file
102
InvenTree/InvenTree/static/bootstrap-table/extensions/copy-rows/bootstrap-table-copy-rows.js
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* @author Homer Glascock <HopGlascock@gmail.com>
|
||||
* @version: v1.0.0
|
||||
*/
|
||||
|
||||
!function ($) {
|
||||
"use strict";
|
||||
|
||||
var calculateObjectValue = $.fn.bootstrapTable.utils.calculateObjectValue,
|
||||
sprintf = $.fn.bootstrapTable.utils.sprintf;
|
||||
|
||||
var copytext = function (text) {
|
||||
var textField = document.createElement('textarea');
|
||||
$(textField).html(text);
|
||||
document.body.appendChild(textField);
|
||||
textField.select();
|
||||
|
||||
try {
|
||||
document.execCommand('copy');
|
||||
}
|
||||
catch (e) {
|
||||
console.log("Oops, unable to copy");
|
||||
}
|
||||
$(textField).remove();
|
||||
};
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
copyBtn: false,
|
||||
copyWHiddenBtn: false,
|
||||
copyDelemeter: ", "
|
||||
});
|
||||
|
||||
$.fn.bootstrapTable.methods.push('copyColumnsToClipboard', 'copyColumnsToClipboardWithHidden');
|
||||
|
||||
var BootstrapTable = $.fn.bootstrapTable.Constructor,
|
||||
_initToolbar = BootstrapTable.prototype.initToolbar;
|
||||
|
||||
BootstrapTable.prototype.initToolbar = function () {
|
||||
|
||||
_initToolbar.apply(this, Array.prototype.slice.apply(arguments));
|
||||
|
||||
var that = this,
|
||||
$btnGroup = this.$toolbar.find('>.btn-group');
|
||||
|
||||
if (this.options.clickToSelect || this.options.singleSelect) {
|
||||
|
||||
if (this.options.copyBtn) {
|
||||
var copybtn = "<button class='btn btn-default' id='copyBtn'><span class='glyphicon glyphicon-copy icon-pencil'></span></button>";
|
||||
$btnGroup.append(copybtn);
|
||||
$btnGroup.find('#copyBtn').click(function () { that.copyColumnsToClipboard(); });
|
||||
}
|
||||
|
||||
if (this.options.copyWHiddenBtn) {
|
||||
var copyhiddenbtn = "<button class='btn btn-default' id='copyWHiddenBtn'><span class='badge'><span class='glyphicon glyphicon-copy icon-pencil'></span></span></button>";
|
||||
$btnGroup.append(copyhiddenbtn);
|
||||
$btnGroup.find('#copyWHiddenBtn').click(function () { that.copyColumnsToClipboardWithHidden(); });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.copyColumnsToClipboard = function () {
|
||||
var that = this,
|
||||
ret = "",
|
||||
delimet = this.options.copyDelemeter;
|
||||
|
||||
$.each(that.getSelections(), function (index, row) {
|
||||
$.each(that.options.columns[0], function (indy, column) {
|
||||
if (column.field !== "state" && column.field !== "RowNumber" && column.visible) {
|
||||
if (row[column.field] !== null) {
|
||||
ret += calculateObjectValue(column, that.header.formatters[indy], [row[column.field], row, index], row[column.field]);
|
||||
}
|
||||
ret += delimet;
|
||||
}
|
||||
});
|
||||
|
||||
ret += "\r\n";
|
||||
});
|
||||
|
||||
copytext(ret);
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.copyColumnsToClipboardWithHidden = function () {
|
||||
var that = this,
|
||||
ret = "",
|
||||
delimet = this.options.copyDelemeter;
|
||||
|
||||
$.each(that.getSelections(), function (index, row) {
|
||||
$.each(that.options.columns[0], function (indy, column) {
|
||||
if (column.field != "state" && column.field !== "RowNumber") {
|
||||
if (row[column.field] !== null) {
|
||||
ret += calculateObjectValue(column, that.header.formatters[indy], [row[column.field], row, index], row[column.field]);
|
||||
}
|
||||
ret += delimet;
|
||||
}
|
||||
});
|
||||
|
||||
ret += "\r\n";
|
||||
});
|
||||
|
||||
copytext(ret);
|
||||
};
|
||||
}(jQuery);
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Copy Rows",
|
||||
"version": "1.0.0",
|
||||
"description": "Allows pushing of selected column data to the clipboard.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/copy-rows",
|
||||
"example": "http://issues.wenzhixin.net.cn/bootstrap-table/#extensions/copy-rows.html",
|
||||
|
||||
"plugins": [{
|
||||
"name": "copy-rows",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/copy-rows"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "Homer Glascock",
|
||||
"image": "https://avatars1.githubusercontent.com/u/5546710"
|
||||
}
|
||||
}
|
||||
32
InvenTree/InvenTree/static/bootstrap-table/extensions/defer-url/bootstrap-table-defer-url.js
vendored
Normal file
32
InvenTree/InvenTree/static/bootstrap-table/extensions/defer-url/bootstrap-table-defer-url.js
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* When using server-side processing, the default mode of operation for
|
||||
* bootstrap-table is to simply throw away any data that currently exists in the
|
||||
* table and make a request to the server to get the first page of data to
|
||||
* display. This is fine for an empty table, but if you already have the first
|
||||
* page of data displayed in the plain HTML, it is a waste of resources. As
|
||||
* such, you can use data-defer-url instead of data-url to allow you to instruct
|
||||
* bootstrap-table to not make that initial request, rather it will use the data
|
||||
* already on the page.
|
||||
*
|
||||
* @author: Ruben Suarez
|
||||
* @webSite: http://rubensa.eu.org
|
||||
* @version: v1.0.0
|
||||
*/
|
||||
|
||||
(function($) {
|
||||
'use strict';
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
deferUrl : undefined
|
||||
});
|
||||
|
||||
var BootstrapTable = $.fn.bootstrapTable.Constructor, _init = BootstrapTable.prototype.init;
|
||||
|
||||
BootstrapTable.prototype.init = function() {
|
||||
_init.apply(this, Array.prototype.slice.apply(arguments));
|
||||
|
||||
if (this.options.deferUrl) {
|
||||
this.options.url = this.options.deferUrl;
|
||||
}
|
||||
}
|
||||
})(jQuery);
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "DeferURL",
|
||||
"version": "1.0.0",
|
||||
"description": "Plugin to defer server side processing.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/defer-url",
|
||||
"example": "http://issues.wenzhixin.net.cn/bootstrap-table/#extensions/defer-url.html",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-defer-url",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/defer-url"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "rubensa",
|
||||
"image": "https://avatars1.githubusercontent.com/u/1469340"
|
||||
}
|
||||
}
|
||||
149
InvenTree/InvenTree/static/bootstrap-table/extensions/editable/bootstrap-table-editable.js
vendored
Normal file
149
InvenTree/InvenTree/static/bootstrap-table/extensions/editable/bootstrap-table-editable.js
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
/**
|
||||
* @author zhixin wen <wenzhixin2010@gmail.com>
|
||||
* extensions: https://github.com/vitalets/x-editable
|
||||
*/
|
||||
|
||||
($ => {
|
||||
const Utils = $.fn.bootstrapTable.utils
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
editable: true,
|
||||
onEditableInit () {
|
||||
return false
|
||||
},
|
||||
onEditableSave (field, row, oldValue, $el) {
|
||||
return false
|
||||
},
|
||||
onEditableShown (field, row, $el, editable) {
|
||||
return false
|
||||
},
|
||||
onEditableHidden (field, row, $el, reason) {
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
$.extend($.fn.bootstrapTable.Constructor.EVENTS, {
|
||||
'editable-init.bs.table': 'onEditableInit',
|
||||
'editable-save.bs.table': 'onEditableSave',
|
||||
'editable-shown.bs.table': 'onEditableShown',
|
||||
'editable-hidden.bs.table': 'onEditableHidden'
|
||||
})
|
||||
|
||||
$.BootstrapTable = class extends $.BootstrapTable {
|
||||
initTable () {
|
||||
super.initTable()
|
||||
|
||||
if (!this.options.editable) {
|
||||
return
|
||||
}
|
||||
|
||||
$.each(this.columns, (i, column) => {
|
||||
if (!column.editable) {
|
||||
return
|
||||
}
|
||||
|
||||
const editableOptions = {}
|
||||
const editableDataMarkup = []
|
||||
const editableDataPrefix = 'editable-'
|
||||
const processDataOptions = (key, value) => {
|
||||
// Replace camel case with dashes.
|
||||
const dashKey = key.replace(/([A-Z])/g, $1 => `-${$1.toLowerCase()}`)
|
||||
if (dashKey.indexOf(editableDataPrefix) === 0) {
|
||||
editableOptions[dashKey.replace(editableDataPrefix, 'data-')] = value
|
||||
}
|
||||
}
|
||||
|
||||
$.each(this.options, processDataOptions)
|
||||
|
||||
column.formatter = column.formatter || (value => value)
|
||||
column._formatter = column._formatter ? column._formatter : column.formatter
|
||||
column.formatter = (value, row, index) => {
|
||||
const result = Utils.calculateObjectValue(column,
|
||||
column._formatter, [value, row, index], value)
|
||||
|
||||
$.each(column, processDataOptions)
|
||||
|
||||
$.each(editableOptions, (key, value) => {
|
||||
editableDataMarkup.push(` ${key}="${value}"`)
|
||||
})
|
||||
|
||||
let _dont_edit_formatter = false
|
||||
if (column.editable.hasOwnProperty('noeditFormatter')) {
|
||||
_dont_edit_formatter = column.editable.noeditFormatter(value, row, index)
|
||||
}
|
||||
|
||||
if (_dont_edit_formatter === false) {
|
||||
return `<a href="javascript:void(0)"
|
||||
data-name="${column.field}"
|
||||
data-pk="${row[this.options.idField]}"
|
||||
data-value="${result}"
|
||||
${editableDataMarkup.join('')}></a>`
|
||||
}
|
||||
return _dont_edit_formatter
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
initBody (fixedScroll) {
|
||||
super.initBody(fixedScroll)
|
||||
|
||||
if (!this.options.editable) {
|
||||
return
|
||||
}
|
||||
|
||||
$.each(this.columns, (i, column) => {
|
||||
if (!column.editable) {
|
||||
return
|
||||
}
|
||||
|
||||
const data = this.getData()
|
||||
const $field = this.$body.find(`a[data-name="${column.field}"]`)
|
||||
|
||||
$field.each((i, element) => {
|
||||
const $element = $(element)
|
||||
const $tr = $element.closest('tr')
|
||||
const index = $tr.data('index')
|
||||
const row = data[index]
|
||||
|
||||
const editableOpts = Utils.calculateObjectValue(column,
|
||||
column.editable, [index, row, $element], {})
|
||||
|
||||
$element.editable(editableOpts)
|
||||
})
|
||||
|
||||
$field.off('save').on('save', ({currentTarget}, {submitValue}) => {
|
||||
const $this = $(currentTarget)
|
||||
const data = this.getData()
|
||||
const index = $this.parents('tr[data-index]').data('index')
|
||||
const row = data[index]
|
||||
const oldValue = row[column.field]
|
||||
|
||||
$this.data('value', submitValue)
|
||||
row[column.field] = submitValue
|
||||
this.trigger('editable-save', column.field, row, oldValue, $this)
|
||||
this.resetFooter()
|
||||
})
|
||||
|
||||
$field.off('shown').on('shown', ({currentTarget}, editable) => {
|
||||
const $this = $(currentTarget)
|
||||
const data = this.getData()
|
||||
const index = $this.parents('tr[data-index]').data('index')
|
||||
const row = data[index]
|
||||
|
||||
this.trigger('editable-shown', column.field, row, $this, editable)
|
||||
})
|
||||
|
||||
$field.off('hidden').on('hidden', ({currentTarget}, reason) => {
|
||||
const $this = $(currentTarget)
|
||||
const data = this.getData()
|
||||
const index = $this.parents('tr[data-index]').data('index')
|
||||
const row = data[index]
|
||||
|
||||
this.trigger('editable-hidden', column.field, row, $this, reason)
|
||||
})
|
||||
})
|
||||
this.trigger('editable-init')
|
||||
}
|
||||
}
|
||||
|
||||
})(jQuery)
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Table Editable",
|
||||
"version": "1.1.0",
|
||||
"description": "Use the x-editable to in-place editing your table.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/editable",
|
||||
"example": "http://issues.wenzhixin.net.cn/bootstrap-table/#extensions/editable.html",
|
||||
|
||||
"plugins": [{
|
||||
"name": "x-editable",
|
||||
"url": "https://github.com/vitalets/x-editable"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "wenzhixin",
|
||||
"image": "https://avatars1.githubusercontent.com/u/2117018"
|
||||
}
|
||||
}
|
||||
225
InvenTree/InvenTree/static/bootstrap-table/extensions/export/bootstrap-table-export.js
vendored
Normal file
225
InvenTree/InvenTree/static/bootstrap-table/extensions/export/bootstrap-table-export.js
vendored
Normal file
@@ -0,0 +1,225 @@
|
||||
/**
|
||||
* @author zhixin wen <wenzhixin2010@gmail.com>
|
||||
* extensions: https://github.com/hhurz/tableExport.jquery.plugin
|
||||
*/
|
||||
|
||||
($ => {
|
||||
const Utils = $.fn.bootstrapTable.utils
|
||||
|
||||
const bootstrap = {
|
||||
3: {
|
||||
icons: {
|
||||
export: 'glyphicon-export icon-share'
|
||||
},
|
||||
html: {
|
||||
dropmenu: '<ul class="dropdown-menu" role="menu"></ul>',
|
||||
dropitem: '<li role="menuitem" data-type="%s"><a href="javascript:">%s</a></li>'
|
||||
}
|
||||
},
|
||||
4: {
|
||||
icons: {
|
||||
export: 'fa-download'
|
||||
},
|
||||
html: {
|
||||
dropmenu: '<div class="dropdown-menu dropdown-menu-right"></div>',
|
||||
dropitem: '<a class="dropdown-item" data-type="%s" href="javascript:">%s</a>'
|
||||
}
|
||||
}
|
||||
}[Utils.bootstrapVersion]
|
||||
|
||||
const TYPE_NAME = {
|
||||
json: 'JSON',
|
||||
xml: 'XML',
|
||||
png: 'PNG',
|
||||
csv: 'CSV',
|
||||
txt: 'TXT',
|
||||
sql: 'SQL',
|
||||
doc: 'MS-Word',
|
||||
excel: 'MS-Excel',
|
||||
xlsx: 'MS-Excel (OpenXML)',
|
||||
powerpoint: 'MS-Powerpoint',
|
||||
pdf: 'PDF'
|
||||
}
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
showExport: false,
|
||||
exportDataType: 'basic', // basic, all, selected
|
||||
exportTypes: ['json', 'xml', 'csv', 'txt', 'sql', 'excel'],
|
||||
exportOptions: {},
|
||||
exportFooter: false
|
||||
})
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults.icons, {
|
||||
export: bootstrap.icons.export
|
||||
})
|
||||
|
||||
$.extend($.fn.bootstrapTable.locales, {
|
||||
formatExport () {
|
||||
return 'Export data'
|
||||
}
|
||||
})
|
||||
$.extend($.fn.bootstrapTable.defaults, $.fn.bootstrapTable.locales)
|
||||
|
||||
$.fn.bootstrapTable.methods.push('exportTable')
|
||||
|
||||
$.BootstrapTable = class extends $.BootstrapTable {
|
||||
initToolbar () {
|
||||
const o = this.options
|
||||
|
||||
this.showToolbar = this.showToolbar || o.showExport
|
||||
|
||||
super.initToolbar()
|
||||
|
||||
if (!this.options.showExport) {
|
||||
return
|
||||
}
|
||||
const $btnGroup = this.$toolbar.find('>.btn-group')
|
||||
this.$export = $btnGroup.find('div.export')
|
||||
|
||||
if (this.$export.length) {
|
||||
this.updateExportButton()
|
||||
return
|
||||
}
|
||||
this.$export = $(`
|
||||
<div class="export btn-group">
|
||||
<button class="btn btn-${o.buttonsClass} btn-${o.iconSize} dropdown-toggle"
|
||||
aria-label="export type"
|
||||
title="${o.formatExport()}"
|
||||
data-toggle="dropdown"
|
||||
type="button">
|
||||
<i class="${o.iconsPrefix} ${o.icons.export}"></i>
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
${bootstrap.html.dropmenu}
|
||||
</div>
|
||||
`).appendTo($btnGroup)
|
||||
|
||||
this.updateExportButton()
|
||||
|
||||
const $menu = this.$export.find('.dropdown-menu')
|
||||
let exportTypes = o.exportTypes
|
||||
|
||||
if (typeof exportTypes === 'string') {
|
||||
const types = exportTypes.slice(1, -1).replace(/ /g, '').split(',')
|
||||
exportTypes = types.map(t => t.slice(1, -1))
|
||||
}
|
||||
for (const type of exportTypes) {
|
||||
if (TYPE_NAME.hasOwnProperty(type)) {
|
||||
$menu.append(Utils.sprintf(bootstrap.html.dropitem, type, TYPE_NAME[type]))
|
||||
}
|
||||
}
|
||||
|
||||
$menu.find('>li, >a').click(({currentTarget}) => {
|
||||
const type = $(currentTarget).data('type')
|
||||
const exportOptions = {
|
||||
type,
|
||||
escape: false
|
||||
}
|
||||
|
||||
this.exportTable(exportOptions)
|
||||
})
|
||||
}
|
||||
|
||||
exportTable (options) {
|
||||
const o = this.options
|
||||
const stateField = this.header.stateField
|
||||
const isCardView = o.cardView
|
||||
|
||||
const doExport = callback => {
|
||||
if (stateField) {
|
||||
this.hideColumn(stateField)
|
||||
}
|
||||
if (isCardView) {
|
||||
this.toggleView()
|
||||
}
|
||||
|
||||
const data = this.getData()
|
||||
if (o.exportFooter) {
|
||||
const $footerRow = this.$tableFooter.find('tr').first()
|
||||
const footerData = {}
|
||||
const footerHtml = []
|
||||
|
||||
$.each($footerRow.children(), (index, footerCell) => {
|
||||
const footerCellHtml = $(footerCell).children('.th-inner').first().html()
|
||||
footerData[this.columns[index].field] = footerCellHtml === ' ' ? null : footerCellHtml
|
||||
|
||||
// grab footer cell text into cell index-based array
|
||||
footerHtml.push(footerCellHtml)
|
||||
})
|
||||
|
||||
this.append(footerData)
|
||||
|
||||
const $lastTableRow = this.$body.children().last()
|
||||
|
||||
$.each($lastTableRow.children(), (index, lastTableRowCell) => {
|
||||
$(lastTableRowCell).html(footerHtml[index])
|
||||
})
|
||||
}
|
||||
|
||||
this.$el.tableExport($.extend({
|
||||
onAfterSaveToFile: () => {
|
||||
if (o.exportFooter) {
|
||||
this.load(data)
|
||||
}
|
||||
|
||||
if (stateField) {
|
||||
this.showColumn(stateField)
|
||||
}
|
||||
if (isCardView) {
|
||||
this.toggleView()
|
||||
}
|
||||
|
||||
callback()
|
||||
}
|
||||
}, o.exportOptions, options))
|
||||
}
|
||||
|
||||
if (o.exportDataType === 'all' && o.pagination) {
|
||||
const eventName = o.sidePagination === 'server'
|
||||
? 'post-body.bs.table' : 'page-change.bs.table'
|
||||
this.$el.one(eventName, () => {
|
||||
doExport(() => {
|
||||
this.togglePagination()
|
||||
})
|
||||
})
|
||||
this.togglePagination()
|
||||
} else if (o.exportDataType === 'selected') {
|
||||
let data = this.getData()
|
||||
let selectedData = this.getSelections()
|
||||
if (!selectedData.length) {
|
||||
return
|
||||
}
|
||||
|
||||
if (o.sidePagination === 'server') {
|
||||
data = {
|
||||
total: o.totalRows,
|
||||
[this.options.dataField]: data
|
||||
}
|
||||
selectedData = {
|
||||
total: selectedData.length,
|
||||
[this.options.dataField]: selectedData
|
||||
}
|
||||
}
|
||||
|
||||
this.load(selectedData)
|
||||
doExport(() => {
|
||||
this.load(data)
|
||||
})
|
||||
} else {
|
||||
doExport()
|
||||
}
|
||||
}
|
||||
|
||||
updateSelected () {
|
||||
super.updateSelected()
|
||||
this.updateExportButton()
|
||||
}
|
||||
|
||||
updateExportButton () {
|
||||
if (this.options.exportDataType === 'selected') {
|
||||
this.$export.find('> button')
|
||||
.prop('disabled', !this.getSelections().length)
|
||||
}
|
||||
}
|
||||
}
|
||||
})(jQuery)
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Table Export",
|
||||
"version": "1.1.0",
|
||||
"description": "Export your table data to JSON, XML, CSV, TXT, SQL, Word, Excel, PNG, PDF.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/export",
|
||||
"example": "http://issues.wenzhixin.net.cn/bootstrap-table/#extensions/export.html",
|
||||
|
||||
"plugins": [{
|
||||
"name": "tableExport.jquery.plugin",
|
||||
"url": "https://github.com/hhurz/tableExport.jquery.plugin"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "wenzhixin",
|
||||
"image": "https://avatars1.githubusercontent.com/u/2117018"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* @author: Dennis Hernández
|
||||
* @webSite: http://djhvscf.github.io/Blog
|
||||
* @version: v2.1.1
|
||||
*/
|
||||
|
||||
.no-filter-control {
|
||||
height: 34px;
|
||||
}
|
||||
|
||||
.filter-control {
|
||||
margin: 0 2px 2px 2px;
|
||||
}
|
||||
@@ -0,0 +1,917 @@
|
||||
/**
|
||||
* @author: Dennis Hernández
|
||||
* @webSite: http://djhvscf.github.io/Blog
|
||||
* @version: v2.2.0
|
||||
*/
|
||||
|
||||
($ => {
|
||||
const Utils = $.fn.bootstrapTable.utils
|
||||
const UtilsFilterControl = {
|
||||
getOptionsFromSelectControl (selectControl) {
|
||||
return selectControl.get(selectControl.length - 1).options
|
||||
},
|
||||
|
||||
hideUnusedSelectOptions (selectControl, uniqueValues) {
|
||||
const options = UtilsFilterControl.getOptionsFromSelectControl(
|
||||
selectControl
|
||||
)
|
||||
|
||||
for (let i = 0; i < options.length; i++) {
|
||||
if (options[i].value !== '') {
|
||||
if (!uniqueValues.hasOwnProperty(options[i].value)) {
|
||||
selectControl
|
||||
.find(Utils.sprintf('option[value=\'%s\']', options[i].value))
|
||||
.hide()
|
||||
} else {
|
||||
selectControl
|
||||
.find(Utils.sprintf('option[value=\'%s\']', options[i].value))
|
||||
.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
addOptionToSelectControl (selectControl, _value, text) {
|
||||
const value = $.trim(_value)
|
||||
const $selectControl = $(selectControl.get(selectControl.length - 1))
|
||||
if (
|
||||
!UtilsFilterControl.existOptionInSelectControl(selectControl, value)
|
||||
) {
|
||||
$selectControl.append(
|
||||
$('<option></option>')
|
||||
.attr('value', value)
|
||||
.text(
|
||||
$('<div />')
|
||||
.html(text)
|
||||
.text()
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
sortSelectControl (selectControl) {
|
||||
const $selectControl = $(selectControl.get(selectControl.length - 1))
|
||||
const $opts = $selectControl.find('option:gt(0)')
|
||||
|
||||
$opts.sort((a, b) => {
|
||||
let aa = $(a).text().toLowerCase()
|
||||
let bb = $(b).text().toLowerCase()
|
||||
if ($.isNumeric(a) && $.isNumeric(b)) {
|
||||
// Convert numerical values from string to float.
|
||||
aa = parseFloat(aa)
|
||||
bb = parseFloat(bb)
|
||||
}
|
||||
return aa > bb ? 1 : aa < bb ? -1 : 0
|
||||
})
|
||||
|
||||
$selectControl.find('option:gt(0)').remove()
|
||||
$selectControl.append($opts)
|
||||
},
|
||||
existOptionInSelectControl (selectControl, value) {
|
||||
const options = UtilsFilterControl.getOptionsFromSelectControl(
|
||||
selectControl
|
||||
)
|
||||
for (let i = 0; i < options.length; i++) {
|
||||
if (options[i].value === value.toString()) {
|
||||
// The value is not valid to add
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// If we get here, the value is valid to add
|
||||
return false
|
||||
},
|
||||
fixHeaderCSS ({ $tableHeader }) {
|
||||
$tableHeader.css('height', '77px')
|
||||
},
|
||||
getCurrentHeader ({ $header, options, $tableHeader }) {
|
||||
let header = $header
|
||||
if (options.height) {
|
||||
header = $tableHeader
|
||||
}
|
||||
|
||||
return header
|
||||
},
|
||||
getCurrentSearchControls ({ options }) {
|
||||
let searchControls = 'select, input'
|
||||
if (options.height) {
|
||||
searchControls = 'table select, table input'
|
||||
}
|
||||
|
||||
return searchControls
|
||||
},
|
||||
getCursorPosition (el) {
|
||||
if (Utils.isIEBrowser()) {
|
||||
if ($(el).is('input[type=text]')) {
|
||||
let pos = 0
|
||||
if ('selectionStart' in el) {
|
||||
pos = el.selectionStart
|
||||
} else if ('selection' in document) {
|
||||
el.focus()
|
||||
const Sel = document.selection.createRange()
|
||||
const SelLength = document.selection.createRange().text.length
|
||||
Sel.moveStart('character', -el.value.length)
|
||||
pos = Sel.text.length - SelLength
|
||||
}
|
||||
return pos
|
||||
}
|
||||
return -1
|
||||
|
||||
}
|
||||
return -1
|
||||
},
|
||||
setCursorPosition (el) {
|
||||
$(el).val(el.value)
|
||||
},
|
||||
copyValues (that) {
|
||||
const header = UtilsFilterControl.getCurrentHeader(that)
|
||||
const searchControls = UtilsFilterControl.getCurrentSearchControls(that)
|
||||
|
||||
that.options.valuesFilterControl = []
|
||||
|
||||
header.find(searchControls).each(function () {
|
||||
that.options.valuesFilterControl.push({
|
||||
field: $(this)
|
||||
.closest('[data-field]')
|
||||
.data('field'),
|
||||
value: $(this).val(),
|
||||
position: UtilsFilterControl.getCursorPosition($(this).get(0)),
|
||||
hasFocus: $(this).is(':focus')
|
||||
})
|
||||
})
|
||||
},
|
||||
setValues (that) {
|
||||
let field = null
|
||||
let result = []
|
||||
const header = UtilsFilterControl.getCurrentHeader(that)
|
||||
const searchControls = UtilsFilterControl.getCurrentSearchControls(that)
|
||||
|
||||
if (that.options.valuesFilterControl.length > 0) {
|
||||
// Callback to apply after settings fields values
|
||||
let fieldToFocusCallback = null
|
||||
header.find(searchControls).each(function (index, ele) {
|
||||
field = $(this)
|
||||
.closest('[data-field]')
|
||||
.data('field')
|
||||
result = that.options.valuesFilterControl.filter(valueObj => valueObj.field === field)
|
||||
|
||||
if (result.length > 0) {
|
||||
$(this).val(result[0].value)
|
||||
if (result[0].hasFocus) {
|
||||
// set callback if the field had the focus.
|
||||
fieldToFocusCallback = ((fieldToFocus, carretPosition) => {
|
||||
// Closure here to capture the field and cursor position
|
||||
const closedCallback = () => {
|
||||
fieldToFocus.focus()
|
||||
UtilsFilterControl.setCursorPosition(fieldToFocus, carretPosition)
|
||||
}
|
||||
return closedCallback
|
||||
})($(this).get(0), result[0].position)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Callback call.
|
||||
if (fieldToFocusCallback !== null) {
|
||||
fieldToFocusCallback()
|
||||
}
|
||||
}
|
||||
},
|
||||
collectBootstrapCookies () {
|
||||
const cookies = []
|
||||
const foundCookies = document.cookie.match(/(?:bs.table.)(\w*)/g)
|
||||
|
||||
if (foundCookies) {
|
||||
$.each(foundCookies, (i, _cookie) => {
|
||||
let cookie = _cookie
|
||||
if (/./.test(cookie)) {
|
||||
cookie = cookie.split('.').pop()
|
||||
}
|
||||
|
||||
if ($.inArray(cookie, cookies) === -1) {
|
||||
cookies.push(cookie)
|
||||
}
|
||||
})
|
||||
return cookies
|
||||
}
|
||||
},
|
||||
escapeID (id) {
|
||||
return String(id).replace(/(:|\.|\[|\]|,)/g, '\\$1')
|
||||
},
|
||||
isColumnSearchableViaSelect ({ filterControl, searchable }) {
|
||||
return filterControl &&
|
||||
filterControl.toLowerCase() === 'select' &&
|
||||
searchable
|
||||
},
|
||||
isFilterDataNotGiven ({ filterData }) {
|
||||
return filterData === undefined ||
|
||||
filterData.toLowerCase() === 'column'
|
||||
},
|
||||
hasSelectControlElement (selectControl) {
|
||||
return selectControl && selectControl.length > 0
|
||||
},
|
||||
initFilterSelectControls (that) {
|
||||
const data = that.data
|
||||
const itemsPerPage = that.pageTo < that.options.data.length ? that.options.data.length : that.pageTo
|
||||
const z = that.options.pagination
|
||||
? that.options.sidePagination === 'server'
|
||||
? that.pageTo
|
||||
: that.options.totalRows
|
||||
: that.pageTo
|
||||
|
||||
$.each(that.header.fields, (j, field) => {
|
||||
const column = that.columns[that.fieldsColumnsIndex[field]]
|
||||
const selectControl = $(`.bootstrap-table-filter-control-${UtilsFilterControl.escapeID(column.field)}`)
|
||||
|
||||
if (
|
||||
UtilsFilterControl.isColumnSearchableViaSelect(column) &&
|
||||
UtilsFilterControl.isFilterDataNotGiven(column) &&
|
||||
UtilsFilterControl.hasSelectControlElement(selectControl)
|
||||
) {
|
||||
if (selectControl.get(selectControl.length - 1).options.length === 0) {
|
||||
// Added the default option
|
||||
UtilsFilterControl.addOptionToSelectControl(selectControl, '', column.filterControlPlaceholder)
|
||||
}
|
||||
|
||||
const uniqueValues = {}
|
||||
for (let i = 0; i < z; i++) {
|
||||
// Added a new value
|
||||
const fieldValue = data[i][field]
|
||||
const formattedValue = Utils.calculateObjectValue(that.header, that.header.formatters[j], [fieldValue, data[i], i], fieldValue)
|
||||
|
||||
uniqueValues[formattedValue] = fieldValue
|
||||
}
|
||||
|
||||
// eslint-disable-next-line guard-for-in
|
||||
for (const key in uniqueValues) {
|
||||
UtilsFilterControl.addOptionToSelectControl(selectControl, uniqueValues[key], key)
|
||||
}
|
||||
|
||||
UtilsFilterControl.sortSelectControl(selectControl)
|
||||
|
||||
if (that.options.hideUnusedSelectOptions) {
|
||||
UtilsFilterControl.hideUnusedSelectOptions(selectControl, uniqueValues)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
that.trigger('created-controls')
|
||||
},
|
||||
getFilterDataMethod (objFilterDataMethod, searchTerm) {
|
||||
const keys = Object.keys(objFilterDataMethod)
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
if (keys[i] === searchTerm) {
|
||||
return objFilterDataMethod[searchTerm]
|
||||
}
|
||||
}
|
||||
return null
|
||||
},
|
||||
createControls (that, header) {
|
||||
let addedFilterControl = false
|
||||
let isVisible
|
||||
let html
|
||||
|
||||
$.each(that.columns, (i, column) => {
|
||||
isVisible = 'hidden'
|
||||
html = []
|
||||
|
||||
if (!column.visible) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!column.filterControl) {
|
||||
html.push('<div class="no-filter-control"></div>')
|
||||
} else {
|
||||
html.push('<div class="filter-control">')
|
||||
|
||||
const nameControl = column.filterControl.toLowerCase()
|
||||
if (column.searchable && that.options.filterTemplate[nameControl]) {
|
||||
addedFilterControl = true
|
||||
isVisible = 'visible'
|
||||
html.push(
|
||||
that.options.filterTemplate[nameControl](
|
||||
that,
|
||||
column.field,
|
||||
isVisible,
|
||||
column.filterControlPlaceholder
|
||||
? column.filterControlPlaceholder
|
||||
: '',
|
||||
`filter-control-${i}`
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
$.each(header.children().children(), (i, tr) => {
|
||||
const $tr = $(tr)
|
||||
if ($tr.data('field') === column.field) {
|
||||
$tr.find('.fht-cell').append(html.join(''))
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
if (
|
||||
column.filterData !== undefined &&
|
||||
column.filterData.toLowerCase() !== 'column'
|
||||
) {
|
||||
const filterDataType = UtilsFilterControl.getFilterDataMethod(
|
||||
/* eslint-disable no-use-before-define */
|
||||
filterDataMethods,
|
||||
column.filterData.substring(0, column.filterData.indexOf(':'))
|
||||
)
|
||||
let filterDataSource
|
||||
let selectControl
|
||||
|
||||
if (filterDataType !== null) {
|
||||
filterDataSource = column.filterData.substring(
|
||||
column.filterData.indexOf(':') + 1,
|
||||
column.filterData.length
|
||||
)
|
||||
selectControl = $(
|
||||
`.bootstrap-table-filter-control-${UtilsFilterControl.escapeID(column.field)}`
|
||||
)
|
||||
|
||||
UtilsFilterControl.addOptionToSelectControl(selectControl, '', column.filterControlPlaceholder)
|
||||
filterDataType(filterDataSource, selectControl)
|
||||
} else {
|
||||
throw new SyntaxError(
|
||||
'Error. You should use any of these allowed filter data methods: var, json, url.' +
|
||||
' Use like this: var: {key: "value"}'
|
||||
)
|
||||
}
|
||||
|
||||
let variableValues
|
||||
let key
|
||||
// eslint-disable-next-line default-case
|
||||
switch (filterDataType) {
|
||||
case 'url':
|
||||
$.ajax({
|
||||
url: filterDataSource,
|
||||
dataType: 'json',
|
||||
success (data) {
|
||||
// eslint-disable-next-line guard-for-in
|
||||
for (const key in data) {
|
||||
UtilsFilterControl.addOptionToSelectControl(selectControl, key, data[key])
|
||||
}
|
||||
UtilsFilterControl.sortSelectControl(selectControl)
|
||||
}
|
||||
})
|
||||
break
|
||||
case 'var':
|
||||
variableValues = window[filterDataSource]
|
||||
// eslint-disable-next-line guard-for-in
|
||||
for (key in variableValues) {
|
||||
UtilsFilterControl.addOptionToSelectControl(selectControl, key, variableValues[key])
|
||||
}
|
||||
UtilsFilterControl.sortSelectControl(selectControl)
|
||||
break
|
||||
case 'jso':
|
||||
variableValues = JSON.parse(filterDataSource)
|
||||
// eslint-disable-next-line guard-for-in
|
||||
for (key in variableValues) {
|
||||
UtilsFilterControl.addOptionToSelectControl(selectControl, key, variableValues[key])
|
||||
}
|
||||
UtilsFilterControl.sortSelectControl(selectControl)
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (addedFilterControl) {
|
||||
header.off('keyup', 'input').on('keyup', 'input', (event, obj) => {
|
||||
// Simulate enter key action from clear button
|
||||
event.keyCode = obj ? obj.keyCode : event.keyCode
|
||||
|
||||
if (that.options.searchOnEnterKey && event.keyCode !== 13) {
|
||||
return
|
||||
}
|
||||
|
||||
if ($.inArray(event.keyCode, [37, 38, 39, 40]) > -1) {
|
||||
return
|
||||
}
|
||||
|
||||
const $currentTarget = $(event.currentTarget)
|
||||
|
||||
if ($currentTarget.is(':checkbox') || $currentTarget.is(':radio')) {
|
||||
return
|
||||
}
|
||||
|
||||
clearTimeout(event.currentTarget.timeoutId || 0)
|
||||
event.currentTarget.timeoutId = setTimeout(() => {
|
||||
that.onColumnSearch(event)
|
||||
}, that.options.searchTimeOut)
|
||||
})
|
||||
|
||||
header.off('change', 'select').on('change', 'select', event => {
|
||||
if (that.options.searchOnEnterKey && event.keyCode !== 13) {
|
||||
return
|
||||
}
|
||||
|
||||
if ($.inArray(event.keyCode, [37, 38, 39, 40]) > -1) {
|
||||
return
|
||||
}
|
||||
|
||||
clearTimeout(event.currentTarget.timeoutId || 0)
|
||||
event.currentTarget.timeoutId = setTimeout(() => {
|
||||
that.onColumnSearch(event)
|
||||
}, that.options.searchTimeOut)
|
||||
})
|
||||
|
||||
header.off('mouseup', 'input').on('mouseup', 'input', function (event) {
|
||||
const $input = $(this)
|
||||
const oldValue = $input.val()
|
||||
|
||||
if (oldValue === '') {
|
||||
return
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
const newValue = $input.val()
|
||||
|
||||
if (newValue === '') {
|
||||
clearTimeout(event.currentTarget.timeoutId || 0)
|
||||
event.currentTarget.timeoutId = setTimeout(() => {
|
||||
that.onColumnSearch(event)
|
||||
}, that.options.searchTimeOut)
|
||||
}
|
||||
}, 1)
|
||||
})
|
||||
|
||||
if (header.find('.date-filter-control').length > 0) {
|
||||
$.each(that.columns, (i, { filterControl, field, filterDatepickerOptions }) => {
|
||||
if (
|
||||
filterControl !== undefined &&
|
||||
filterControl.toLowerCase() === 'datepicker'
|
||||
) {
|
||||
header
|
||||
.find(
|
||||
`.date-filter-control.bootstrap-table-filter-control-${field}`
|
||||
)
|
||||
.datepicker(filterDatepickerOptions)
|
||||
.on('changeDate', ({ currentTarget }) => {
|
||||
$(currentTarget).val(
|
||||
currentTarget.value
|
||||
)
|
||||
// Fired the keyup event
|
||||
$(currentTarget).keyup()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
header.find('.filterControl').hide()
|
||||
}
|
||||
},
|
||||
getDirectionOfSelectOptions (_alignment) {
|
||||
const alignment = _alignment === undefined ? 'left' : _alignment.toLowerCase()
|
||||
|
||||
switch (alignment) {
|
||||
case 'left':
|
||||
return 'ltr'
|
||||
case 'right':
|
||||
return 'rtl'
|
||||
case 'auto':
|
||||
return 'auto'
|
||||
default:
|
||||
return 'ltr'
|
||||
}
|
||||
}
|
||||
}
|
||||
const filterDataMethods = {
|
||||
var (filterDataSource, selectControl) {
|
||||
const variableValues = window[filterDataSource]
|
||||
// eslint-disable-next-line guard-for-in
|
||||
for (const key in variableValues) {
|
||||
UtilsFilterControl.addOptionToSelectControl(selectControl, key, variableValues[key])
|
||||
}
|
||||
UtilsFilterControl.sortSelectControl(selectControl)
|
||||
},
|
||||
url (filterDataSource, selectControl) {
|
||||
$.ajax({
|
||||
url: filterDataSource,
|
||||
dataType: 'json',
|
||||
success (data) {
|
||||
// eslint-disable-next-line guard-for-in
|
||||
for (const key in data) {
|
||||
UtilsFilterControl.addOptionToSelectControl(selectControl, key, data[key])
|
||||
}
|
||||
UtilsFilterControl.sortSelectControl(selectControl)
|
||||
}
|
||||
})
|
||||
},
|
||||
json (filterDataSource, selectControl) {
|
||||
const variableValues = JSON.parse(filterDataSource)
|
||||
// eslint-disable-next-line guard-for-in
|
||||
for (const key in variableValues) {
|
||||
UtilsFilterControl.addOptionToSelectControl(selectControl, key, variableValues[key])
|
||||
}
|
||||
UtilsFilterControl.sortSelectControl(selectControl)
|
||||
}
|
||||
}
|
||||
|
||||
const bootstrap = {
|
||||
3: {
|
||||
icons: {
|
||||
clear: 'glyphicon-trash icon-clear'
|
||||
}
|
||||
},
|
||||
4: {
|
||||
icons: {
|
||||
clear: 'fa-trash icon-clear'
|
||||
}
|
||||
}
|
||||
}[Utils.bootstrapVersion]
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
filterControl: false,
|
||||
onColumnSearch (field, text) {
|
||||
return false
|
||||
},
|
||||
onCreatedControls () {
|
||||
return true
|
||||
},
|
||||
filterShowClear: false,
|
||||
alignmentSelectControlOptions: undefined,
|
||||
filterTemplate: {
|
||||
input (that, field, isVisible, placeholder) {
|
||||
return Utils.sprintf(
|
||||
'<input type="text" class="form-control bootstrap-table-filter-control-%s" style="width: 100%; visibility: %s" placeholder="%s">',
|
||||
field,
|
||||
isVisible,
|
||||
placeholder
|
||||
)
|
||||
},
|
||||
select ({ options }, field, isVisible) {
|
||||
return Utils.sprintf(
|
||||
'<select class="form-control bootstrap-table-filter-control-%s" style="width: 100%; visibility: %s" dir="%s"></select>',
|
||||
field,
|
||||
isVisible,
|
||||
UtilsFilterControl.getDirectionOfSelectOptions(
|
||||
options.alignmentSelectControlOptions
|
||||
)
|
||||
)
|
||||
},
|
||||
datepicker (that, field, isVisible) {
|
||||
return Utils.sprintf(
|
||||
'<input type="text" class="form-control date-filter-control bootstrap-table-filter-control-%s" style="width: 100%; visibility: %s">',
|
||||
field,
|
||||
isVisible
|
||||
)
|
||||
}
|
||||
},
|
||||
disableControlWhenSearch: false,
|
||||
searchOnEnterKey: false,
|
||||
// internal variables
|
||||
valuesFilterControl: []
|
||||
})
|
||||
|
||||
$.extend($.fn.bootstrapTable.columnDefaults, {
|
||||
filterControl: undefined,
|
||||
filterData: undefined,
|
||||
filterDatepickerOptions: undefined,
|
||||
filterStrictSearch: false,
|
||||
filterStartsWithSearch: false,
|
||||
filterControlPlaceholder: ''
|
||||
})
|
||||
|
||||
$.extend($.fn.bootstrapTable.Constructor.EVENTS, {
|
||||
'column-search.bs.table': 'onColumnSearch',
|
||||
'created-controls.bs.table': 'onCreatedControls'
|
||||
})
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults.icons, {
|
||||
clear: bootstrap.icons.clear
|
||||
})
|
||||
|
||||
$.extend($.fn.bootstrapTable.locales, {
|
||||
formatClearFilters () {
|
||||
return 'Clear Filters'
|
||||
}
|
||||
})
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, $.fn.bootstrapTable.locales)
|
||||
|
||||
$.fn.bootstrapTable.methods.push('triggerSearch')
|
||||
$.fn.bootstrapTable.methods.push('clearFilterControl')
|
||||
|
||||
$.BootstrapTable = class extends $.BootstrapTable {
|
||||
init () {
|
||||
// Make sure that the filterControl option is set
|
||||
if (this.options.filterControl) {
|
||||
const that = this
|
||||
|
||||
// Make sure that the internal variables are set correctly
|
||||
this.options.valuesFilterControl = []
|
||||
|
||||
this.$el
|
||||
.on('reset-view.bs.table', () => {
|
||||
// Create controls on $tableHeader if the height is set
|
||||
if (!that.options.height) {
|
||||
return
|
||||
}
|
||||
|
||||
// Avoid recreate the controls
|
||||
if (
|
||||
that.$tableHeader.find('select').length > 0 ||
|
||||
that.$tableHeader.find('input').length > 0
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
UtilsFilterControl.createControls(that, that.$tableHeader)
|
||||
})
|
||||
.on('post-header.bs.table', () => {
|
||||
UtilsFilterControl.setValues(that)
|
||||
})
|
||||
.on('post-body.bs.table', () => {
|
||||
if (that.options.height) {
|
||||
UtilsFilterControl.fixHeaderCSS(that)
|
||||
}
|
||||
})
|
||||
.on('column-switch.bs.table', () => {
|
||||
UtilsFilterControl.setValues(that)
|
||||
})
|
||||
.on('load-success.bs.table', () => {
|
||||
that.EnableControls(true)
|
||||
})
|
||||
.on('load-error.bs.table', () => {
|
||||
that.EnableControls(true)
|
||||
})
|
||||
}
|
||||
|
||||
super.init()
|
||||
}
|
||||
|
||||
initToolbar () {
|
||||
this.showToolbar =
|
||||
this.showToolbar ||
|
||||
(this.options.filterControl && this.options.filterShowClear)
|
||||
|
||||
super.initToolbar()
|
||||
|
||||
if (this.options.filterControl && this.options.filterShowClear) {
|
||||
const $btnGroup = this.$toolbar.find('>.btn-group')
|
||||
let $btnClear = $btnGroup.find('.filter-show-clear')
|
||||
|
||||
if (!$btnClear.length) {
|
||||
$btnClear = $(
|
||||
[
|
||||
Utils.sprintf(
|
||||
'<button class="btn btn-%s filter-show-clear" ',
|
||||
this.options.buttonsClass
|
||||
),
|
||||
Utils.sprintf(
|
||||
'type="button" title="%s">',
|
||||
this.options.formatClearFilters()
|
||||
),
|
||||
Utils.sprintf(
|
||||
'<i class="%s %s"></i> ',
|
||||
this.options.iconsPrefix,
|
||||
this.options.icons.clear
|
||||
),
|
||||
'</button>'
|
||||
].join('')
|
||||
).appendTo($btnGroup)
|
||||
|
||||
$btnClear
|
||||
.off('click')
|
||||
.on('click', $.proxy(this.clearFilterControl, this))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
initHeader () {
|
||||
super.initHeader()
|
||||
|
||||
if (!this.options.filterControl) {
|
||||
return
|
||||
}
|
||||
UtilsFilterControl.createControls(this, this.$header)
|
||||
}
|
||||
initBody () {
|
||||
super.initBody()
|
||||
|
||||
UtilsFilterControl.initFilterSelectControls(this)
|
||||
}
|
||||
|
||||
initSearch () {
|
||||
const that = this
|
||||
const fp = $.isEmptyObject(that.filterColumnsPartial)
|
||||
? null
|
||||
: that.filterColumnsPartial
|
||||
|
||||
if (fp === null || Object.keys(fp).length <= 1) {
|
||||
super.initSearch()
|
||||
}
|
||||
|
||||
if (this.options.sidePagination === 'server') {
|
||||
return
|
||||
}
|
||||
|
||||
if (fp === null) {
|
||||
return
|
||||
}
|
||||
|
||||
// Check partial column filter
|
||||
that.data = fp
|
||||
? that.options.data.filter((item, i) => {
|
||||
const itemIsExpected = []
|
||||
Object.keys(item).forEach((key, index) => {
|
||||
const thisColumn = that.columns[that.fieldsColumnsIndex[key]]
|
||||
const fval = (fp[key] || '').toLowerCase()
|
||||
let value = item[key]
|
||||
|
||||
if (fval === '') {
|
||||
itemIsExpected.push(true)
|
||||
} else {
|
||||
// Fix #142: search use formated data
|
||||
if (thisColumn && thisColumn.searchFormatter) {
|
||||
value = $.fn.bootstrapTable.utils.calculateObjectValue(
|
||||
that.header,
|
||||
that.header.formatters[$.inArray(key, that.header.fields)],
|
||||
[value, item, i],
|
||||
value
|
||||
)
|
||||
}
|
||||
|
||||
if ($.inArray(key, that.header.fields) !== -1) {
|
||||
if (typeof value === 'string' || typeof value === 'number') {
|
||||
if (thisColumn.filterStrictSearch) {
|
||||
if (value.toString().toLowerCase() === fval.toString().toLowerCase()) {
|
||||
itemIsExpected.push(true)
|
||||
} else {
|
||||
itemIsExpected.push(false)
|
||||
}
|
||||
} else if (thisColumn.filterStartsWithSearch) {
|
||||
if ((`${value}`).toLowerCase().indexOf(fval) === 0) {
|
||||
itemIsExpected.push(true)
|
||||
} else {
|
||||
itemIsExpected.push(false)
|
||||
}
|
||||
} else {
|
||||
if ((`${value}`).toLowerCase().includes(fval)) {
|
||||
itemIsExpected.push(true)
|
||||
} else {
|
||||
itemIsExpected.push(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return !itemIsExpected.includes(false)
|
||||
})
|
||||
: that.data
|
||||
}
|
||||
|
||||
initColumnSearch (filterColumnsDefaults) {
|
||||
UtilsFilterControl.copyValues(this)
|
||||
|
||||
if (filterColumnsDefaults) {
|
||||
this.filterColumnsPartial = filterColumnsDefaults
|
||||
this.updatePagination()
|
||||
|
||||
// eslint-disable-next-line guard-for-in
|
||||
for (const filter in filterColumnsDefaults) {
|
||||
this.trigger('column-search', filter, filterColumnsDefaults[filter])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onColumnSearch (event) {
|
||||
if ($.inArray(event.keyCode, [37, 38, 39, 40]) > -1) {
|
||||
return
|
||||
}
|
||||
|
||||
UtilsFilterControl.copyValues(this)
|
||||
const text = $.trim($(event.currentTarget).val())
|
||||
const $field = $(event.currentTarget)
|
||||
.closest('[data-field]')
|
||||
.data('field')
|
||||
|
||||
if ($.isEmptyObject(this.filterColumnsPartial)) {
|
||||
this.filterColumnsPartial = {}
|
||||
}
|
||||
if (text) {
|
||||
this.filterColumnsPartial[$field] = text
|
||||
} else {
|
||||
delete this.filterColumnsPartial[$field]
|
||||
}
|
||||
|
||||
// if the searchText is the same as the previously selected column value,
|
||||
// bootstrapTable will not try searching again (even though the selected column
|
||||
// may be different from the previous search). As a work around
|
||||
// we're manually appending some text to bootrap's searchText field
|
||||
// to guarantee that it will perform a search again when we call this.onSearch(event)
|
||||
this.searchText += 'randomText'
|
||||
|
||||
this.options.pageNumber = 1
|
||||
this.EnableControls(false)
|
||||
this.onSearch(event)
|
||||
this.trigger('column-search', $field, text)
|
||||
}
|
||||
|
||||
clearFilterControl () {
|
||||
if (this.options.filterControl && this.options.filterShowClear) {
|
||||
const that = this
|
||||
const cookies = UtilsFilterControl.collectBootstrapCookies()
|
||||
const header = UtilsFilterControl.getCurrentHeader(that)
|
||||
const table = header.closest('table')
|
||||
const controls = header.find(UtilsFilterControl.getCurrentSearchControls(that))
|
||||
const search = that.$toolbar.find('.search input')
|
||||
let hasValues = false
|
||||
let timeoutId = 0
|
||||
|
||||
$.each(that.options.valuesFilterControl, (i, item) => {
|
||||
hasValues = hasValues ? true : item.value !== ''
|
||||
item.value = ''
|
||||
})
|
||||
|
||||
UtilsFilterControl.setValues(that)
|
||||
|
||||
// clear cookies once the filters are clean
|
||||
clearTimeout(timeoutId)
|
||||
timeoutId = setTimeout(() => {
|
||||
if (cookies && cookies.length > 0) {
|
||||
$.each(cookies, (i, item) => {
|
||||
if (that.deleteCookie !== undefined) {
|
||||
that.deleteCookie(item)
|
||||
}
|
||||
})
|
||||
}
|
||||
}, that.options.searchTimeOut)
|
||||
|
||||
// If there is not any value in the controls exit this method
|
||||
if (!hasValues) {
|
||||
return
|
||||
}
|
||||
|
||||
// Clear each type of filter if it exists.
|
||||
// Requires the body to reload each time a type of filter is found because we never know
|
||||
// which ones are going to be present.
|
||||
if (controls.length > 0) {
|
||||
this.filterColumnsPartial = {}
|
||||
$(controls[0]).trigger(
|
||||
controls[0].tagName === 'INPUT' ? 'keyup' : 'change', { keyCode: 13 }
|
||||
)
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
if (search.length > 0) {
|
||||
that.resetSearch()
|
||||
}
|
||||
|
||||
// use the default sort order if it exists. do nothing if it does not
|
||||
if (
|
||||
that.options.sortName !== table.data('sortName') ||
|
||||
that.options.sortOrder !== table.data('sortOrder')
|
||||
) {
|
||||
const sorter = header.find(
|
||||
Utils.sprintf(
|
||||
'[data-field="%s"]',
|
||||
$(controls[0])
|
||||
.closest('table')
|
||||
.data('sortName')
|
||||
)
|
||||
)
|
||||
if (sorter.length > 0) {
|
||||
that.onSort({ type: 'keypress', currentTarget: sorter })
|
||||
$(sorter)
|
||||
.find('.sortable')
|
||||
.trigger('click')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
triggerSearch () {
|
||||
const header = UtilsFilterControl.getCurrentHeader(this)
|
||||
const searchControls = UtilsFilterControl.getCurrentSearchControls(this)
|
||||
|
||||
header.find(searchControls).each(function () {
|
||||
const el = $(this)
|
||||
if (el.is('select')) {
|
||||
el.change()
|
||||
} else {
|
||||
el.keyup()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
EnableControls (enable) {
|
||||
if (
|
||||
this.options.disableControlWhenSearch &&
|
||||
this.options.sidePagination === 'server'
|
||||
) {
|
||||
const header = UtilsFilterControl.getCurrentHeader(this)
|
||||
const searchControls = UtilsFilterControl.getCurrentSearchControls(this)
|
||||
|
||||
if (!enable) {
|
||||
header.find(searchControls).prop('disabled', 'disabled')
|
||||
} else {
|
||||
header.find(searchControls).removeProp('disabled')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})(jQuery)
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Filter Control",
|
||||
"version": "2.1.0",
|
||||
"description": "Plugin to add input/select element on the top of the columns in order to filter the data.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/filter-control",
|
||||
"example": "http://issues.wenzhixin.net.cn/bootstrap-table/#extensions/filter-control.html",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-filter-control",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/filter-control"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "djhvscf",
|
||||
"image": "https://avatars1.githubusercontent.com/u/4496763"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
.fixed-table-header-columns,
|
||||
.fixed-table-body-columns {
|
||||
position: absolute;
|
||||
background-color: #fff;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.fixed-table-header-columns {
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.fixed-table-header-columns .table,
|
||||
.fixed-table-body-columns .table {
|
||||
border-right: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.fixed-table-header-columns .table.table-no-bordered,
|
||||
.fixed-table-body-columns .table.table-no-bordered {
|
||||
border-right: 1px solid transparent;
|
||||
}
|
||||
|
||||
.fixed-table-body-columns table {
|
||||
position: absolute;
|
||||
animation: none;
|
||||
}
|
||||
115
InvenTree/InvenTree/static/bootstrap-table/extensions/fixed-columns/bootstrap-table-fixed-columns.js
vendored
Normal file
115
InvenTree/InvenTree/static/bootstrap-table/extensions/fixed-columns/bootstrap-table-fixed-columns.js
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
/**
|
||||
* @author zhixin wen <wenzhixin2010@gmail.com>
|
||||
*/
|
||||
|
||||
($ => {
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
fixedColumns: false,
|
||||
fixedNumber: 1
|
||||
})
|
||||
|
||||
$.BootstrapTable = class extends $.BootstrapTable {
|
||||
|
||||
fitHeader (...args) {
|
||||
super.fitHeader(...args)
|
||||
|
||||
if (!this.options.fixedColumns) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this.$el.is(':hidden')) {
|
||||
return
|
||||
}
|
||||
|
||||
this.$container.find('.fixed-table-header-columns').remove()
|
||||
this.$fixedHeader = $('<div class="fixed-table-header-columns"></div>')
|
||||
this.$fixedHeader.append(this.$tableHeader.find('>table').clone(true))
|
||||
this.$tableHeader.after(this.$fixedHeader)
|
||||
|
||||
const width = this.getFixedColumnsWidth()
|
||||
|
||||
this.$fixedHeader.css({
|
||||
top: 0,
|
||||
width,
|
||||
height: this.$tableHeader.outerHeight(true)
|
||||
})
|
||||
|
||||
this.initFixedColumnsBody()
|
||||
|
||||
this.$fixedBody.css({
|
||||
top: this.$tableHeader.outerHeight(true),
|
||||
width,
|
||||
height: this.$tableBody.outerHeight(true) - 1
|
||||
})
|
||||
|
||||
this.initFixedColumnsEvents()
|
||||
}
|
||||
|
||||
initBody (...args) {
|
||||
super.initBody(...args)
|
||||
|
||||
if (!this.options.fixedColumns) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this.options.showHeader && this.options.height) {
|
||||
return
|
||||
}
|
||||
|
||||
this.initFixedColumnsBody()
|
||||
|
||||
this.$fixedBody.css({
|
||||
top: 0,
|
||||
width: this.getFixedColumnsWidth(),
|
||||
height: this.$tableHeader.outerHeight(true) + this.$tableBody.outerHeight(true)
|
||||
})
|
||||
|
||||
this.initFixedColumnsEvents()
|
||||
}
|
||||
|
||||
initFixedColumnsBody () {
|
||||
this.$container.find('.fixed-table-body-columns').remove()
|
||||
this.$fixedBody = $('<div class="fixed-table-body-columns"></div>')
|
||||
this.$fixedBody.append(this.$tableBody.find('>table').clone(true))
|
||||
this.$tableBody.after(this.$fixedBody)
|
||||
}
|
||||
|
||||
getFixedColumnsWidth () {
|
||||
const visibleFields = this.getVisibleFields()
|
||||
let width = 0
|
||||
|
||||
for (let i = 0; i < this.options.fixedNumber; i++) {
|
||||
width += this.$header.find(`th[data-field="${visibleFields[i]}"]`).outerWidth(true)
|
||||
}
|
||||
|
||||
return width + 1
|
||||
}
|
||||
|
||||
initFixedColumnsEvents () {
|
||||
// events
|
||||
this.$tableBody.off('scroll.fixed-columns').on('scroll.fixed-columns', e => {
|
||||
this.$fixedBody.find('table').css('top', -$(e.currentTarget).scrollTop())
|
||||
})
|
||||
|
||||
this.$body.find('> tr[data-index]').off('hover').hover(e => {
|
||||
const index = $(e.currentTarget).data('index')
|
||||
this.$fixedBody.find(`tr[data-index="${index}"]`)
|
||||
.css('background-color', $(e.currentTarget).css('background-color'))
|
||||
}, e => {
|
||||
const index = $(e.currentTarget).data('index')
|
||||
const $tr = this.$fixedBody.find(`tr[data-index="${index}"]`)
|
||||
$tr.attr('style', $tr.attr('style').replace(/background-color:.*;/, ''))
|
||||
})
|
||||
|
||||
this.$fixedBody.find('tr[data-index]').off('hover').hover(e => {
|
||||
const index = $(e.currentTarget).data('index')
|
||||
this.$body.find(`tr[data-index="${index}"]`)
|
||||
.css('background-color', $(e.currentTarget).css('background-color'))
|
||||
}, e => {
|
||||
const index = $(e.currentTarget).data('index')
|
||||
const $tr = this.$body.find(`> tr[data-index="${index}"]`)
|
||||
$tr.attr('style', $tr.attr('style').replace(/background-color:.*;/, ''))
|
||||
})
|
||||
}
|
||||
}
|
||||
})(jQuery)
|
||||
11
InvenTree/InvenTree/static/bootstrap-table/extensions/group-by-v2/bootstrap-table-group-by.css
vendored
Normal file
11
InvenTree/InvenTree/static/bootstrap-table/extensions/group-by-v2/bootstrap-table-group-by.css
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
.bootstrap-table .table > tbody > tr.groupBy {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.bootstrap-table .table > tbody > tr.groupBy.expanded {
|
||||
|
||||
}
|
||||
|
||||
.bootstrap-table .table > tbody > tr.hidden + tr.detail-view {
|
||||
display: none;
|
||||
}
|
||||
230
InvenTree/InvenTree/static/bootstrap-table/extensions/group-by-v2/bootstrap-table-group-by.js
vendored
Normal file
230
InvenTree/InvenTree/static/bootstrap-table/extensions/group-by-v2/bootstrap-table-group-by.js
vendored
Normal file
@@ -0,0 +1,230 @@
|
||||
/**
|
||||
* @author: Yura Knoxville
|
||||
* @version: v1.1.0
|
||||
*/
|
||||
|
||||
(function ($) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var initBodyCaller,
|
||||
tableGroups;
|
||||
|
||||
// it only does '%s', and return '' when arguments are undefined
|
||||
var sprintf = function (str) {
|
||||
var args = arguments,
|
||||
flag = true,
|
||||
i = 1;
|
||||
|
||||
str = str.replace(/%s/g, function () {
|
||||
var arg = args[i++];
|
||||
|
||||
if (typeof arg === 'undefined') {
|
||||
flag = false;
|
||||
return '';
|
||||
}
|
||||
return arg;
|
||||
});
|
||||
return flag ? str : '';
|
||||
};
|
||||
|
||||
var groupBy = function (array , f) {
|
||||
var groups = {};
|
||||
array.forEach(function(o) {
|
||||
var group = f(o);
|
||||
groups[group] = groups[group] || [];
|
||||
groups[group].push(o);
|
||||
});
|
||||
|
||||
return groups;
|
||||
};
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
groupBy: false,
|
||||
groupByField: '',
|
||||
groupByFormatter: undefined
|
||||
});
|
||||
|
||||
var BootstrapTable = $.fn.bootstrapTable.Constructor,
|
||||
_initSort = BootstrapTable.prototype.initSort,
|
||||
_initBody = BootstrapTable.prototype.initBody,
|
||||
_updateSelected = BootstrapTable.prototype.updateSelected;
|
||||
|
||||
BootstrapTable.prototype.initSort = function () {
|
||||
_initSort.apply(this, Array.prototype.slice.apply(arguments));
|
||||
|
||||
var that = this;
|
||||
tableGroups = [];
|
||||
|
||||
if ((this.options.groupBy) && (this.options.groupByField !== '')) {
|
||||
|
||||
if ((this.options.sortName != this.options.groupByField)) {
|
||||
this.data.sort(function(a, b) {
|
||||
return a[that.options.groupByField].localeCompare(b[that.options.groupByField]);
|
||||
});
|
||||
}
|
||||
|
||||
var that = this;
|
||||
var groups = groupBy(that.data, function (item) {
|
||||
return [item[that.options.groupByField]];
|
||||
});
|
||||
|
||||
var index = 0;
|
||||
$.each(groups, function(key, value) {
|
||||
tableGroups.push({
|
||||
id: index,
|
||||
name: key,
|
||||
data: value
|
||||
});
|
||||
|
||||
value.forEach(function(item) {
|
||||
if (!item._data) {
|
||||
item._data = {};
|
||||
}
|
||||
|
||||
item._data['parent-index'] = index;
|
||||
});
|
||||
|
||||
index++;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
BootstrapTable.prototype.initBody = function () {
|
||||
initBodyCaller = true;
|
||||
|
||||
_initBody.apply(this, Array.prototype.slice.apply(arguments));
|
||||
|
||||
if ((this.options.groupBy) && (this.options.groupByField !== '')) {
|
||||
var that = this,
|
||||
checkBox = false,
|
||||
visibleColumns = 0;
|
||||
|
||||
this.columns.forEach(function(column) {
|
||||
if (column.checkbox) {
|
||||
checkBox = true;
|
||||
} else {
|
||||
if (column.visible) {
|
||||
visibleColumns += 1;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (this.options.detailView && !this.options.cardView) {
|
||||
visibleColumns += 1;
|
||||
}
|
||||
|
||||
tableGroups.forEach(function(item){
|
||||
var html = [];
|
||||
|
||||
html.push(sprintf('<tr class="info groupBy expanded" data-group-index="%s">', item.id));
|
||||
|
||||
if (that.options.detailView && !that.options.cardView) {
|
||||
html.push('<td class="detail"></td>');
|
||||
}
|
||||
|
||||
if (checkBox) {
|
||||
html.push('<td class="bs-checkbox">',
|
||||
'<input name="btSelectGroup" type="checkbox" />',
|
||||
'</td>'
|
||||
);
|
||||
}
|
||||
var formattedValue = item.name;
|
||||
if (typeof(that.options.groupByFormatter) == "function") {
|
||||
formattedValue = that.options.groupByFormatter(item.name, item.id, item.data);
|
||||
}
|
||||
html.push('<td',
|
||||
sprintf(' colspan="%s"', visibleColumns),
|
||||
'>', formattedValue, '</td>'
|
||||
);
|
||||
|
||||
html.push('</tr>');
|
||||
|
||||
that.$body.find('tr[data-parent-index='+item.id+']:first').before($(html.join('')));
|
||||
});
|
||||
|
||||
this.$selectGroup = [];
|
||||
this.$body.find('[name="btSelectGroup"]').each(function() {
|
||||
var self = $(this);
|
||||
|
||||
that.$selectGroup.push({
|
||||
group: self,
|
||||
item: that.$selectItem.filter(function () {
|
||||
return ($(this).closest('tr').data('parent-index') ===
|
||||
self.closest('tr').data('group-index'));
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
this.$container.off('click', '.groupBy')
|
||||
.on('click', '.groupBy', function() {
|
||||
$(this).toggleClass('expanded');
|
||||
that.$body.find('tr[data-parent-index='+$(this).closest('tr').data('group-index')+']').toggleClass('hidden');
|
||||
});
|
||||
|
||||
this.$container.off('click', '[name="btSelectGroup"]')
|
||||
.on('click', '[name="btSelectGroup"]', function (event) {
|
||||
event.stopImmediatePropagation();
|
||||
|
||||
var self = $(this);
|
||||
var checked = self.prop('checked');
|
||||
that[checked ? 'checkGroup' : 'uncheckGroup']($(this).closest('tr').data('group-index'));
|
||||
});
|
||||
}
|
||||
|
||||
initBodyCaller = false;
|
||||
this.updateSelected();
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.updateSelected = function () {
|
||||
if (!initBodyCaller) {
|
||||
_updateSelected.apply(this, Array.prototype.slice.apply(arguments));
|
||||
|
||||
if ((this.options.groupBy) && (this.options.groupByField !== '')) {
|
||||
this.$selectGroup.forEach(function (item) {
|
||||
var checkGroup = item.item.filter(':enabled').length ===
|
||||
item.item.filter(':enabled').filter(':checked').length;
|
||||
|
||||
item.group.prop('checked', checkGroup);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.getGroupSelections = function (index) {
|
||||
var that = this;
|
||||
|
||||
return $.grep(this.data, function (row) {
|
||||
return (row[that.header.stateField] && (row._data['parent-index'] === index));
|
||||
});
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.checkGroup = function (index) {
|
||||
this.checkGroup_(index, true);
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.uncheckGroup = function (index) {
|
||||
this.checkGroup_(index, false);
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.checkGroup_ = function (index, checked) {
|
||||
var rows;
|
||||
var filter = function() {
|
||||
return ($(this).closest('tr').data('parent-index') === index);
|
||||
};
|
||||
|
||||
if (!checked) {
|
||||
rows = this.getGroupSelections(index);
|
||||
}
|
||||
|
||||
this.$selectItem.filter(filter).prop('checked', checked);
|
||||
|
||||
this.updateRows();
|
||||
this.updateSelected();
|
||||
if (checked) {
|
||||
rows = this.getGroupSelections(index);
|
||||
}
|
||||
this.trigger(checked ? 'check-all' : 'uncheck-all', rows);
|
||||
};
|
||||
|
||||
})(jQuery);
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "Group By V2",
|
||||
"version": "1.0.0",
|
||||
"description": "Group the data by field",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/group-by-v2",
|
||||
"example": "",
|
||||
"plugins": [],
|
||||
"author": {
|
||||
"name": "Knoxvillekm",
|
||||
"image": "https://avatars3.githubusercontent.com/u/11072464"
|
||||
}
|
||||
}
|
||||
53
InvenTree/InvenTree/static/bootstrap-table/extensions/group-by/bootstrap-table-group-by.css
vendored
Normal file
53
InvenTree/InvenTree/static/bootstrap-table/extensions/group-by/bootstrap-table-group-by.css
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
table.treetable tbody tr td {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
table.treetable span {
|
||||
background-position: center left;
|
||||
background-repeat: no-repeat;
|
||||
padding: .2em 0 .2em 1.5em;
|
||||
}
|
||||
|
||||
table.treetable tr.collapsed span.indenter a {
|
||||
background-image: url(data:image/png;base64,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);
|
||||
padding-right: 12px;
|
||||
}
|
||||
|
||||
table.treetable tr.expanded span.indenter a {
|
||||
background-image: url(data:image/png;base64,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);
|
||||
padding-right: 12px;
|
||||
}
|
||||
|
||||
table.treetable tr.branch {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
table.treetable tr.selected {
|
||||
background-color: #3875d7;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
table.treetable tr span.indenter a {
|
||||
outline: none; /* Expander shows outline after upgrading to 3.0 (#141) */
|
||||
}
|
||||
|
||||
table.treetable tr.collapsed.selected span.indenter a {
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAAFpJREFUeNpi/P//PwMlgHHADWD4//8/NtyAQxwD45KAAQdKDfj//////fgMIsYAZIMw1DKREFwODAwM/4kNRKq64AADA4MjFDOQ6gKyY4HodMA49PMCxQYABgAVYHsjyZ1x7QAAAABJRU5ErkJggg==);
|
||||
}
|
||||
|
||||
table.treetable tr.expanded.selected span.indenter a {
|
||||
background-image: url(data:image/png;base64,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);
|
||||
}
|
||||
|
||||
table.treetable tr.accept {
|
||||
background-color: #a3bce4;
|
||||
color: #fff
|
||||
}
|
||||
|
||||
table.treetable tr.collapsed.accept td span.indenter a {
|
||||
background-image: url(data:image/x-png;base64,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);
|
||||
}
|
||||
|
||||
table.treetable tr.expanded.accept td span.indenter a {
|
||||
background-image: url(data:image/x-png;base64,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);
|
||||
}
|
||||
243
InvenTree/InvenTree/static/bootstrap-table/extensions/group-by/bootstrap-table-group-by.js
vendored
Normal file
243
InvenTree/InvenTree/static/bootstrap-table/extensions/group-by/bootstrap-table-group-by.js
vendored
Normal file
@@ -0,0 +1,243 @@
|
||||
/**
|
||||
* @author: Dennis Hernández
|
||||
* @webSite: http://djhvscf.github.io/Blog
|
||||
* @version: v1.1.0
|
||||
*/
|
||||
|
||||
!function ($) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var originalRowAttr,
|
||||
dataTTId = 'data-tt-id',
|
||||
dataTTParentId = 'data-tt-parent-id',
|
||||
obj = {},
|
||||
parentId = undefined;
|
||||
|
||||
var getParentRowId = function (that, id) {
|
||||
var parentRows = that.$body.find('tr').not('[' + 'data-tt-parent-id]');
|
||||
|
||||
for (var i = 0; i < parentRows.length; i++) {
|
||||
if (i === id) {
|
||||
return $(parentRows[i]).attr('data-tt-id');
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
var sumData = function (that, data) {
|
||||
var sumRow = {};
|
||||
$.each(data, function (i, row) {
|
||||
if (!row.IsParent) {
|
||||
for (var prop in row) {
|
||||
if (!isNaN(parseFloat(row[prop]))) {
|
||||
if (that.columns[that.fieldsColumnsIndex[prop]].groupBySumGroup) {
|
||||
if (sumRow[prop] === undefined) {
|
||||
sumRow[prop] = 0;
|
||||
}
|
||||
sumRow[prop] += +row[prop];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return sumRow;
|
||||
};
|
||||
|
||||
var rowAttr = function (row, index) {
|
||||
//Call the User Defined Function
|
||||
originalRowAttr.apply([row, index]);
|
||||
|
||||
obj[dataTTId.toString()] = index;
|
||||
|
||||
if (!row.IsParent) {
|
||||
obj[dataTTParentId.toString()] = parentId === undefined ? index : parentId;
|
||||
} else {
|
||||
parentId = index;
|
||||
delete obj[dataTTParentId.toString()];
|
||||
}
|
||||
|
||||
return obj;
|
||||
};
|
||||
|
||||
var setObjectKeys = function () {
|
||||
// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
|
||||
Object.keys = function (o) {
|
||||
if (o !== Object(o)) {
|
||||
throw new TypeError('Object.keys called on a non-object');
|
||||
}
|
||||
var k = [],
|
||||
p;
|
||||
for (p in o) {
|
||||
if (Object.prototype.hasOwnProperty.call(o, p)) {
|
||||
k.push(p);
|
||||
}
|
||||
}
|
||||
return k;
|
||||
}
|
||||
};
|
||||
|
||||
var getDataArrayFromItem = function (that, item) {
|
||||
var itemDataArray = [];
|
||||
for (var i = 0; i < that.options.groupByField.length; i++) {
|
||||
itemDataArray.push(item[that.options.groupByField[i]]);
|
||||
}
|
||||
|
||||
return itemDataArray;
|
||||
};
|
||||
|
||||
var getNewRow = function (that, result, index) {
|
||||
var newRow = {};
|
||||
for (var i = 0; i < that.options.groupByField.length; i++) {
|
||||
newRow[that.options.groupByField[i].toString()] = result[index][0][that.options.groupByField[i]];
|
||||
}
|
||||
|
||||
newRow.IsParent = true;
|
||||
|
||||
return newRow;
|
||||
};
|
||||
|
||||
var groupBy = function (array, f) {
|
||||
var groups = {};
|
||||
$.each(array, function (i, o) {
|
||||
var group = JSON.stringify(f(o));
|
||||
groups[group] = groups[group] || [];
|
||||
groups[group].push(o);
|
||||
});
|
||||
return Object.keys(groups).map(function (group) {
|
||||
return groups[group];
|
||||
});
|
||||
};
|
||||
|
||||
var makeGrouped = function (that, data) {
|
||||
var newData = [],
|
||||
sumRow = {};
|
||||
|
||||
var result = groupBy(data, function (item) {
|
||||
return getDataArrayFromItem(that, item);
|
||||
});
|
||||
|
||||
for (var i = 0; i < result.length; i++) {
|
||||
result[i].unshift(getNewRow(that, result, i));
|
||||
if (that.options.groupBySumGroup) {
|
||||
sumRow = sumData(that, result[i]);
|
||||
if (!$.isEmptyObject(sumRow)) {
|
||||
result[i].push(sumRow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newData = newData.concat.apply(newData, result);
|
||||
|
||||
if (!that.options.loaded && newData.length > 0) {
|
||||
that.options.loaded = true;
|
||||
that.options.originalData = that.options.data;
|
||||
that.options.data = newData;
|
||||
}
|
||||
|
||||
return newData;
|
||||
};
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
groupBy: false,
|
||||
groupByField: [],
|
||||
groupBySumGroup: false,
|
||||
groupByInitExpanded: undefined, //node, 'all'
|
||||
//internal variables
|
||||
loaded: false,
|
||||
originalData: undefined
|
||||
});
|
||||
|
||||
$.fn.bootstrapTable.methods.push('collapseAll', 'expandAll', 'refreshGroupByField');
|
||||
|
||||
$.extend($.fn.bootstrapTable.COLUMN_DEFAULTS, {
|
||||
groupBySumGroup: false
|
||||
});
|
||||
|
||||
var BootstrapTable = $.fn.bootstrapTable.Constructor,
|
||||
_init = BootstrapTable.prototype.init,
|
||||
_initData = BootstrapTable.prototype.initData;
|
||||
|
||||
BootstrapTable.prototype.init = function () {
|
||||
//Temporal validation
|
||||
if (!this.options.sortName) {
|
||||
if ((this.options.groupBy) && (this.options.groupByField.length > 0)) {
|
||||
var that = this;
|
||||
|
||||
// Compatibility: IE < 9 and old browsers
|
||||
if (!Object.keys) {
|
||||
$.fn.bootstrapTable.utils.objectKeys();
|
||||
}
|
||||
|
||||
//Make sure that the internal variables are set correctly
|
||||
this.options.loaded = false;
|
||||
this.options.originalData = undefined;
|
||||
|
||||
originalRowAttr = this.options.rowAttributes;
|
||||
this.options.rowAttributes = rowAttr;
|
||||
this.$el.off('post-body.bs.table').on('post-body.bs.table', function () {
|
||||
that.$el.treetable({
|
||||
expandable: true,
|
||||
onNodeExpand: function () {
|
||||
if (that.options.height) {
|
||||
that.resetHeader();
|
||||
}
|
||||
},
|
||||
onNodeCollapse: function () {
|
||||
if (that.options.height) {
|
||||
that.resetHeader();
|
||||
}
|
||||
}
|
||||
}, true);
|
||||
|
||||
if (that.options.groupByInitExpanded !== undefined) {
|
||||
if (typeof that.options.groupByInitExpanded === 'number') {
|
||||
that.expandNode(that.options.groupByInitExpanded);
|
||||
} else if (that.options.groupByInitExpanded.toLowerCase() === 'all') {
|
||||
that.expandAll();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
_init.apply(this, Array.prototype.slice.apply(arguments));
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.initData = function (data, type) {
|
||||
//Temporal validation
|
||||
if (!this.options.sortName) {
|
||||
if ((this.options.groupBy) && (this.options.groupByField.length > 0)) {
|
||||
|
||||
this.options.groupByField = typeof this.options.groupByField === 'string' ?
|
||||
this.options.groupByField.replace('[', '').replace(']', '')
|
||||
.replace(/ /g, '').toLowerCase().split(',') : this.options.groupByField;
|
||||
|
||||
data = makeGrouped(this, data ? data : this.options.data);
|
||||
}
|
||||
}
|
||||
_initData.apply(this, [data, type]);
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.expandAll = function () {
|
||||
this.$el.treetable('expandAll');
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.collapseAll = function () {
|
||||
this.$el.treetable('collapseAll');
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.expandNode = function (id) {
|
||||
id = getParentRowId(this, id);
|
||||
if (id !== undefined) {
|
||||
this.$el.treetable('expandNode', id);
|
||||
}
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.refreshGroupByField = function (groupByFields) {
|
||||
if (!$.fn.bootstrapTable.utils.compareObjects(this.options.groupByField, groupByFields)) {
|
||||
this.options.groupByField = groupByFields;
|
||||
this.load(this.options.originalData);
|
||||
}
|
||||
};
|
||||
}(jQuery);
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Group By",
|
||||
"version": "1.1.0",
|
||||
"description": "Plugin to group the data by fields.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/group-by",
|
||||
"example": "#",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-group-by",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/group-by"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "djhvscf",
|
||||
"image": "https://avatars1.githubusercontent.com/u/4496763"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* @author: Jewway
|
||||
* @version: v1.0.0
|
||||
*/
|
||||
|
||||
!function ($) {
|
||||
'use strict';
|
||||
|
||||
var BootstrapTable = $.fn.bootstrapTable.Constructor;
|
||||
|
||||
BootstrapTable.prototype.changeTitle = function (locale) {
|
||||
$.each(this.options.columns, function (idx, columnList) {
|
||||
$.each(columnList, function (idx, column) {
|
||||
if (column.field) {
|
||||
column.title = locale[column.field];
|
||||
}
|
||||
});
|
||||
});
|
||||
this.initHeader();
|
||||
this.initBody();
|
||||
this.initToolbar();
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.changeLocale = function (localeId) {
|
||||
this.options.locale = localeId;
|
||||
this.initLocale();
|
||||
this.initPagination();
|
||||
this.initBody();
|
||||
this.initToolbar();
|
||||
};
|
||||
|
||||
$.fn.bootstrapTable.methods.push('changeTitle');
|
||||
$.fn.bootstrapTable.methods.push('changeLocale');
|
||||
|
||||
}(jQuery);
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "i18n Enhance",
|
||||
"version": "1.0.0",
|
||||
"description": "Plugin to add i18n API in order to change column's title and table locale.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/i18n-enhance",
|
||||
"example": "http://issues.wenzhixin.net.cn/bootstrap-table/#extensions/i18n-enhance.html",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-i18n-enhance",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/i18n-enhance"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "Jewway",
|
||||
"image": "https://avatars0.githubusercontent.com/u/3501899"
|
||||
}
|
||||
}
|
||||
80
InvenTree/InvenTree/static/bootstrap-table/extensions/key-events/bootstrap-table-key-events.js
vendored
Normal file
80
InvenTree/InvenTree/static/bootstrap-table/extensions/key-events/bootstrap-table-key-events.js
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* @author: Dennis Hernández
|
||||
* @webSite: http://djhvscf.github.io/Blog
|
||||
* @version: v1.0.0
|
||||
*
|
||||
* @update zhixin wen <wenzhixin2010@gmail.com>
|
||||
*/
|
||||
|
||||
!function ($) {
|
||||
|
||||
'use strict';
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
keyEvents: false
|
||||
});
|
||||
|
||||
var BootstrapTable = $.fn.bootstrapTable.Constructor,
|
||||
_init = BootstrapTable.prototype.init;
|
||||
|
||||
BootstrapTable.prototype.init = function () {
|
||||
_init.apply(this, Array.prototype.slice.apply(arguments));
|
||||
this.initKeyEvents();
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.initKeyEvents = function () {
|
||||
if (this.options.keyEvents) {
|
||||
var that = this;
|
||||
|
||||
$(document).off('keydown').on('keydown', function (e) {
|
||||
var $search = that.$toolbar.find('.search input'),
|
||||
$refresh = that.$toolbar.find('button[name="refresh"]'),
|
||||
$toggle = that.$toolbar.find('button[name="toggle"]'),
|
||||
$paginationSwitch = that.$toolbar.find('button[name="paginationSwitch"]');
|
||||
|
||||
if (document.activeElement === $search.get(0) || !$.contains(document.activeElement ,that.$toolbar.get(0))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (e.keyCode) {
|
||||
case 83: //s
|
||||
if (!that.options.search) {
|
||||
return;
|
||||
}
|
||||
$search.focus();
|
||||
return false;
|
||||
case 82: //r
|
||||
if (!that.options.showRefresh) {
|
||||
return;
|
||||
}
|
||||
$refresh.click();
|
||||
return false;
|
||||
case 84: //t
|
||||
if (!that.options.showToggle) {
|
||||
return;
|
||||
}
|
||||
$toggle.click();
|
||||
return false;
|
||||
case 80: //p
|
||||
if (!that.options.showPaginationSwitch) {
|
||||
return;
|
||||
}
|
||||
$paginationSwitch.click();
|
||||
return false;
|
||||
case 37: // left
|
||||
if (!that.options.pagination) {
|
||||
return;
|
||||
}
|
||||
that.prevPage();
|
||||
return false;
|
||||
case 39: // right
|
||||
if (!that.options.pagination) {
|
||||
return;
|
||||
}
|
||||
that.nextPage();
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}(jQuery);
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Key Events",
|
||||
"version": "1.0.0",
|
||||
"description": "Plugin to support the key events in the bootstrap table.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/key-events",
|
||||
"example": "http://issues.wenzhixin.net.cn/bootstrap-table/#extensions/key-events.html",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-key-events",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/key-events"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "djhvscf",
|
||||
"image": "https://avatars1.githubusercontent.com/u/4496763"
|
||||
}
|
||||
}
|
||||
136
InvenTree/InvenTree/static/bootstrap-table/extensions/mobile/bootstrap-table-mobile.js
vendored
Normal file
136
InvenTree/InvenTree/static/bootstrap-table/extensions/mobile/bootstrap-table-mobile.js
vendored
Normal file
@@ -0,0 +1,136 @@
|
||||
/**
|
||||
* @author: Dennis Hernández
|
||||
* @webSite: http://djhvscf.github.io/Blog
|
||||
* @version: v1.1.0
|
||||
*/
|
||||
|
||||
!function ($) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var showHideColumns = function (that, checked) {
|
||||
if (that.options.columnsHidden.length > 0 ) {
|
||||
$.each(that.columns, function (i, column) {
|
||||
if (that.options.columnsHidden.indexOf(column.field) !== -1) {
|
||||
if (column.visible !== checked) {
|
||||
that.toggleColumn(that.fieldsColumnsIndex[column.field], checked, true);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var resetView = function (that) {
|
||||
if (that.options.height || that.options.showFooter) {
|
||||
setTimeout(function(){
|
||||
that.resetView.call(that);
|
||||
}, 1);
|
||||
}
|
||||
};
|
||||
|
||||
var changeView = function (that, width, height) {
|
||||
if (that.options.minHeight) {
|
||||
if ((width <= that.options.minWidth) && (height <= that.options.minHeight)) {
|
||||
conditionCardView(that);
|
||||
} else if ((width > that.options.minWidth) && (height > that.options.minHeight)) {
|
||||
conditionFullView(that);
|
||||
}
|
||||
} else {
|
||||
if (width <= that.options.minWidth) {
|
||||
conditionCardView(that);
|
||||
} else if (width > that.options.minWidth) {
|
||||
conditionFullView(that);
|
||||
}
|
||||
}
|
||||
|
||||
resetView(that);
|
||||
};
|
||||
|
||||
var conditionCardView = function (that) {
|
||||
changeTableView(that, false);
|
||||
showHideColumns(that, false);
|
||||
};
|
||||
|
||||
var conditionFullView = function (that) {
|
||||
changeTableView(that, true);
|
||||
showHideColumns(that, true);
|
||||
};
|
||||
|
||||
var changeTableView = function (that, cardViewState) {
|
||||
that.options.cardView = cardViewState;
|
||||
that.toggleView();
|
||||
};
|
||||
|
||||
var debounce = function(func,wait) {
|
||||
var timeout;
|
||||
return function() {
|
||||
var context = this,
|
||||
args = arguments;
|
||||
var later = function() {
|
||||
timeout = null;
|
||||
func.apply(context,args);
|
||||
};
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(later, wait);
|
||||
};
|
||||
};
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
mobileResponsive: false,
|
||||
minWidth: 562,
|
||||
minHeight: undefined,
|
||||
heightThreshold: 100, // just slightly larger than mobile chrome's auto-hiding toolbar
|
||||
checkOnInit: true,
|
||||
columnsHidden: []
|
||||
});
|
||||
|
||||
var BootstrapTable = $.fn.bootstrapTable.Constructor,
|
||||
_init = BootstrapTable.prototype.init;
|
||||
|
||||
BootstrapTable.prototype.init = function () {
|
||||
_init.apply(this, Array.prototype.slice.apply(arguments));
|
||||
|
||||
if (!this.options.mobileResponsive) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.options.minWidth) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.options.minWidth < 100 && this.options.resizable) {
|
||||
console.log("The minWidth when the resizable extension is active should be greater or equal than 100");
|
||||
this.options.minWidth = 100;
|
||||
}
|
||||
|
||||
var that = this,
|
||||
old = {
|
||||
width: $(window).width(),
|
||||
height: $(window).height()
|
||||
};
|
||||
|
||||
$(window).on('resize orientationchange',debounce(function (evt) {
|
||||
// reset view if height has only changed by at least the threshold.
|
||||
var height = $(this).height(),
|
||||
width = $(this).width();
|
||||
|
||||
if (Math.abs(old.height - height) > that.options.heightThreshold || old.width != width) {
|
||||
changeView(that, width, height);
|
||||
old = {
|
||||
width: width,
|
||||
height: height
|
||||
};
|
||||
}
|
||||
},200));
|
||||
|
||||
if (this.options.checkOnInit) {
|
||||
var height = $(window).height(),
|
||||
width = $(window).width();
|
||||
changeView(this, width, height);
|
||||
old = {
|
||||
width: width,
|
||||
height: height
|
||||
};
|
||||
}
|
||||
};
|
||||
}(jQuery);
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Mobile",
|
||||
"version": "1.1.0",
|
||||
"description": "Plugin to support the responsive feature.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/mobile",
|
||||
"example": "http://issues.wenzhixin.net.cn/bootstrap-table/#extensions/mobile.html",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-mobile",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/mobile"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "djhvscf",
|
||||
"image": "https://avatars1.githubusercontent.com/u/4496763"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* @author Homer Glascock <HopGlascock@gmail.com>
|
||||
* @version: v1.0.0
|
||||
*/
|
||||
|
||||
!function ($) {
|
||||
"use strict";
|
||||
|
||||
var sprintf = $.fn.bootstrapTable.utils.sprintf;
|
||||
|
||||
var reInit = function (self) {
|
||||
self.initHeader();
|
||||
self.initSearch();
|
||||
self.initPagination();
|
||||
self.initBody();
|
||||
};
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
showToggleBtn: false,
|
||||
multiToggleDefaults: [], //column names go here
|
||||
});
|
||||
|
||||
$.fn.bootstrapTable.methods.push('hideAllColumns', 'showAllColumns');
|
||||
|
||||
var BootstrapTable = $.fn.bootstrapTable.Constructor,
|
||||
_initToolbar = BootstrapTable.prototype.initToolbar;
|
||||
|
||||
BootstrapTable.prototype.initToolbar = function () {
|
||||
|
||||
_initToolbar.apply(this, Array.prototype.slice.apply(arguments));
|
||||
|
||||
var that = this,
|
||||
$btnGroup = this.$toolbar.find('>.btn-group');
|
||||
|
||||
if (typeof this.options.multiToggleDefaults === 'string') {
|
||||
this.options.multiToggleDefaults = JSON.parse(this.options.multiToggleDefaults);
|
||||
}
|
||||
|
||||
if (this.options.showToggleBtn && this.options.showColumns) {
|
||||
var showbtn = "<button class='btn btn-default hidden' id='showAllBtn'><span class='glyphicon glyphicon-resize-full icon-zoom-in'></span></button>",
|
||||
hidebtn = "<button class='btn btn-default' id='hideAllBtn'><span class='glyphicon glyphicon-resize-small icon-zoom-out'></span></button>";
|
||||
|
||||
$btnGroup.append(showbtn + hidebtn);
|
||||
|
||||
$btnGroup.find('#showAllBtn').click(function () { that.showAllColumns();
|
||||
$btnGroup.find('#hideAllBtn').toggleClass('hidden');
|
||||
$btnGroup.find('#showAllBtn').toggleClass('hidden');
|
||||
});
|
||||
$btnGroup.find('#hideAllBtn').click(function () { that.hideAllColumns();
|
||||
$btnGroup.find('#hideAllBtn').toggleClass('hidden');
|
||||
$btnGroup.find('#showAllBtn').toggleClass('hidden');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.hideAllColumns = function () {
|
||||
var that = this,
|
||||
defaults = that.options.multiToggleDefaults;
|
||||
|
||||
$.each(this.columns, function (index, column) {
|
||||
//if its one of the defaults dont touch it
|
||||
if (defaults.indexOf(column.field) == -1 && column.switchable) {
|
||||
column.visible = false;
|
||||
var $items = that.$toolbar.find('.keep-open input').prop('disabled', false);
|
||||
$items.filter(sprintf('[value="%s"]', index)).prop('checked', false);
|
||||
}
|
||||
});
|
||||
|
||||
reInit(that);
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.showAllColumns = function () {
|
||||
var that = this;
|
||||
$.each(this.columns, function (index, column) {
|
||||
if (column.switchable) {
|
||||
column.visible = true;
|
||||
}
|
||||
|
||||
var $items = that.$toolbar.find('.keep-open input').prop('disabled', false);
|
||||
$items.filter(sprintf('[value="%s"]', index)).prop('checked', true);
|
||||
});
|
||||
|
||||
reInit(that);
|
||||
|
||||
that.toggleColumn(0, that.columns[0].visible, false);
|
||||
};
|
||||
|
||||
}(jQuery);
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Multi Column Toggle",
|
||||
"version": "1.0.0",
|
||||
"description": "Allows hiding and showing of multiple columns at once.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/multi-column-toggle",
|
||||
"example": "http://issues.wenzhixin.net.cn/bootstrap-table/#extensions/multi-column-toggle.html",
|
||||
|
||||
"plugins": [{
|
||||
"name": "multi-column-toggle",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/multi-column-toggle"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "Homer Glascock",
|
||||
"image": "https://avatars1.githubusercontent.com/u/5546710"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* @author: Dennis Hernández
|
||||
* @webSite: http://djhvscf.github.io/Blog
|
||||
* @version: v1.0.0
|
||||
*/
|
||||
|
||||
!function ($) {
|
||||
|
||||
'use strict';
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
multipleSearch: false,
|
||||
delimeter: " "
|
||||
});
|
||||
|
||||
var BootstrapTable = $.fn.bootstrapTable.Constructor,
|
||||
_initSearch = BootstrapTable.prototype.initSearch;
|
||||
|
||||
BootstrapTable.prototype.initSearch = function () {
|
||||
if (this.options.multipleSearch) {
|
||||
if (this.searchText === undefined) {
|
||||
return;
|
||||
}
|
||||
var strArray = this.searchText.split(this.options.delimeter),
|
||||
that = this,
|
||||
f = $.isEmptyObject(this.filterColumns) ? null : this.filterColumns,
|
||||
dataFiltered = [];
|
||||
|
||||
if (strArray.length === 1) {
|
||||
_initSearch.apply(this, Array.prototype.slice.apply(arguments));
|
||||
} else {
|
||||
for (var i = 0; i < strArray.length; i++) {
|
||||
var str = strArray[i].trim();
|
||||
dataFiltered = str ? $.grep(dataFiltered.length === 0 ? this.options.data : dataFiltered, function (item, i) {
|
||||
for (var key in item) {
|
||||
key = $.isNumeric(key) ? parseInt(key, 10) : key;
|
||||
var value = item[key],
|
||||
column = that.columns[that.fieldsColumnsIndex[key]],
|
||||
j = $.inArray(key, that.header.fields);
|
||||
|
||||
// Fix #142: search use formated data
|
||||
if (column && column.searchFormatter) {
|
||||
value = $.fn.bootstrapTable.utils.calculateObjectValue(column,
|
||||
that.header.formatters[j], [value, item, i], value);
|
||||
}
|
||||
|
||||
var index = $.inArray(key, that.header.fields);
|
||||
if (index !== -1 && that.header.searchables[index] && (typeof value === 'string' || typeof value === 'number')) {
|
||||
if (that.options.strictSearch) {
|
||||
if ((value + '').toLowerCase() === str) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if ((value + '').toLowerCase().indexOf(str) !== -1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}) : this.data;
|
||||
}
|
||||
|
||||
this.data = dataFiltered;
|
||||
}
|
||||
} else {
|
||||
_initSearch.apply(this, Array.prototype.slice.apply(arguments));
|
||||
}
|
||||
};
|
||||
|
||||
}(jQuery);
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Multiple Search",
|
||||
"version": "1.0.0",
|
||||
"description": "Plugin to support the multiple search.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/multiple-search",
|
||||
"example": "#",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-multiple-search",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/multiple-search"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "djhvscf",
|
||||
"image": "https://avatars1.githubusercontent.com/u/4496763"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
.multiple-select-row-selected {
|
||||
background: lightBlue
|
||||
}
|
||||
|
||||
.table tbody tr:hover td,
|
||||
.table tbody tr:hover th {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
|
||||
.table-striped tbody tr:nth-child(odd):hover td {
|
||||
background-color: #F9F9F9;
|
||||
}
|
||||
|
||||
.fixed-table-container tbody .selected td {
|
||||
background: lightBlue;
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/**
|
||||
* @author: Dennis Hernández
|
||||
* @webSite: http://djhvscf.github.io/Blog
|
||||
* @version: v1.0.0
|
||||
*/
|
||||
|
||||
!function ($) {
|
||||
|
||||
'use strict';
|
||||
|
||||
document.onselectstart = function() {
|
||||
return false;
|
||||
};
|
||||
|
||||
var getTableObjectFromCurrentTarget = function (currentTarget) {
|
||||
currentTarget = $(currentTarget);
|
||||
return currentTarget.is("table") ? currentTarget : currentTarget.parents().find(".table");
|
||||
};
|
||||
|
||||
var getRow = function (target) {
|
||||
target = $(target);
|
||||
return target.parent().parent();
|
||||
};
|
||||
|
||||
var onRowClick = function (e) {
|
||||
var that = getTableObjectFromCurrentTarget(e.currentTarget);
|
||||
|
||||
if (window.event.ctrlKey) {
|
||||
toggleRow(e.currentTarget, that, false, false);
|
||||
}
|
||||
|
||||
if (window.event.button === 0) {
|
||||
if (!window.event.ctrlKey && !window.event.shiftKey) {
|
||||
clearAll(that);
|
||||
toggleRow(e.currentTarget, that, false, false);
|
||||
}
|
||||
|
||||
if (window.event.shiftKey) {
|
||||
selectRowsBetweenIndexes([that.bootstrapTable("getOptions").multipleSelectRowLastSelectedRow.rowIndex, e.currentTarget.rowIndex], that)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var onCheckboxChange = function (e) {
|
||||
var that = getTableObjectFromCurrentTarget(e.currentTarget);
|
||||
clearAll(that);
|
||||
toggleRow(getRow(e.currentTarget), that, false, false);
|
||||
};
|
||||
|
||||
var toggleRow = function (row, that, clearAll, useShift) {
|
||||
if (clearAll) {
|
||||
row = $(row);
|
||||
that.bootstrapTable("getOptions").multipleSelectRowLastSelectedRow = undefined;
|
||||
row.removeClass(that.bootstrapTable("getOptions").multipleSelectRowCssClass);
|
||||
that.bootstrapTable("uncheck", row.data("index"));
|
||||
} else {
|
||||
that.bootstrapTable("getOptions").multipleSelectRowLastSelectedRow = row;
|
||||
row = $(row);
|
||||
if (useShift) {
|
||||
row.addClass(that.bootstrapTable("getOptions").multipleSelectRowCssClass);
|
||||
that.bootstrapTable("check", row.data("index"));
|
||||
} else {
|
||||
if(row.hasClass(that.bootstrapTable("getOptions").multipleSelectRowCssClass)) {
|
||||
row.removeClass(that.bootstrapTable("getOptions").multipleSelectRowCssClass)
|
||||
that.bootstrapTable("uncheck", row.data("index"));
|
||||
} else {
|
||||
row.addClass(that.bootstrapTable("getOptions").multipleSelectRowCssClass);
|
||||
that.bootstrapTable("check", row.data("index"));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var selectRowsBetweenIndexes = function (indexes, that) {
|
||||
indexes.sort(function(a, b) {
|
||||
return a - b;
|
||||
});
|
||||
|
||||
for (var i = indexes[0]; i <= indexes[1]; i++) {
|
||||
toggleRow(that.bootstrapTable("getOptions").multipleSelectRowRows[i-1], that, false, true);
|
||||
}
|
||||
};
|
||||
|
||||
var clearAll = function (that) {
|
||||
for (var i = 0; i < that.bootstrapTable("getOptions").multipleSelectRowRows.length; i++) {
|
||||
toggleRow(that.bootstrapTable("getOptions").multipleSelectRowRows[i], that, true, false);
|
||||
}
|
||||
};
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
multipleSelectRow: false,
|
||||
multipleSelectRowCssClass: 'multiple-select-row-selected',
|
||||
//internal variables used by the extension
|
||||
multipleSelectRowLastSelectedRow: undefined,
|
||||
multipleSelectRowRows: []
|
||||
});
|
||||
|
||||
var BootstrapTable = $.fn.bootstrapTable.Constructor,
|
||||
_init = BootstrapTable.prototype.init,
|
||||
_initBody = BootstrapTable.prototype.initBody;
|
||||
|
||||
BootstrapTable.prototype.init = function () {
|
||||
if (this.options.multipleSelectRow) {
|
||||
var that = this;
|
||||
|
||||
//Make sure that the internal variables have the correct value
|
||||
this.options.multipleSelectRowLastSelectedRow = undefined;
|
||||
this.options.multipleSelectRowRows = [];
|
||||
|
||||
this.$el.on("post-body.bs.table", function (e) {
|
||||
setTimeout(function () {
|
||||
that.options.multipleSelectRowRows = that.$body.children();
|
||||
that.options.multipleSelectRowRows.click(onRowClick);
|
||||
that.options.multipleSelectRowRows.find("input[type=checkbox]").change(onCheckboxChange);
|
||||
}, 1);
|
||||
});
|
||||
}
|
||||
|
||||
_init.apply(this, Array.prototype.slice.apply(arguments));
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.clearAllMultipleSelectionRow = function () {
|
||||
clearAll(this);
|
||||
};
|
||||
|
||||
$.fn.bootstrapTable.methods.push('clearAllMultipleSelectionRow');
|
||||
}(jQuery);
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Multiple Selection Row",
|
||||
"version": "1.0.0",
|
||||
"description": "Plugin to enable the multiple selection row. You can use the ctrl+click to select one row or use ctrl+shift+click to select a range of rows.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/multiple-selection-row",
|
||||
"example": "",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-multiple-selection-row",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/multiple-selection-row"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "djhvscf",
|
||||
"image": "https://avatars1.githubusercontent.com/u/4496763"
|
||||
}
|
||||
}
|
||||
412
InvenTree/InvenTree/static/bootstrap-table/extensions/multiple-sort/bootstrap-table-multiple-sort.js
vendored
Normal file
412
InvenTree/InvenTree/static/bootstrap-table/extensions/multiple-sort/bootstrap-table-multiple-sort.js
vendored
Normal file
@@ -0,0 +1,412 @@
|
||||
/**
|
||||
* @author Nadim Basalamah <dimbslmh@gmail.com>
|
||||
* @version: v1.1.0
|
||||
* https://github.com/dimbslmh/bootstrap-table/tree/master/src/extensions/multiple-sort/bootstrap-table-multiple-sort.js
|
||||
* Modification: ErwannNevou <https://github.com/ErwannNevou>
|
||||
*/
|
||||
|
||||
(function($) {
|
||||
'use strict';
|
||||
|
||||
var isSingleSort = false;
|
||||
|
||||
var showSortModal = function(that) {
|
||||
var _selector = that.sortModalSelector,
|
||||
_id = '#' + _selector;
|
||||
|
||||
if (!$(_id).hasClass("modal")) {
|
||||
var sModal = ' <div class="modal fade" id="' + _selector + '" tabindex="-1" role="dialog" aria-labelledby="' + _selector + 'Label" aria-hidden="true">';
|
||||
sModal += ' <div class="modal-dialog">';
|
||||
sModal += ' <div class="modal-content">';
|
||||
sModal += ' <div class="modal-header">';
|
||||
sModal += ' <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>';
|
||||
sModal += ' <h4 class="modal-title" id="' + _selector + 'Label">' + that.options.formatMultipleSort() + '</h4>';
|
||||
sModal += ' </div>';
|
||||
sModal += ' <div class="modal-body">';
|
||||
sModal += ' <div class="bootstrap-table">';
|
||||
sModal += ' <div class="fixed-table-toolbar">';
|
||||
sModal += ' <div class="bars">';
|
||||
sModal += ' <div id="toolbar">';
|
||||
sModal += ' <button id="add" type="button" class="btn btn-default"><i class="' + that.options.iconsPrefix + ' ' + that.options.icons.plus + '"></i> ' + that.options.formatAddLevel() + '</button>';
|
||||
sModal += ' <button id="delete" type="button" class="btn btn-default" disabled><i class="' + that.options.iconsPrefix + ' ' + that.options.icons.minus + '"></i> ' + that.options.formatDeleteLevel() + '</button>';
|
||||
sModal += ' </div>';
|
||||
sModal += ' </div>';
|
||||
sModal += ' </div>';
|
||||
sModal += ' <div class="fixed-table-container">';
|
||||
sModal += ' <table id="multi-sort" class="table">';
|
||||
sModal += ' <thead>';
|
||||
sModal += ' <tr>';
|
||||
sModal += ' <th></th>';
|
||||
sModal += ' <th><div class="th-inner">' + that.options.formatColumn() + '</div></th>';
|
||||
sModal += ' <th><div class="th-inner">' + that.options.formatOrder() + '</div></th>';
|
||||
sModal += ' </tr>';
|
||||
sModal += ' </thead>';
|
||||
sModal += ' <tbody></tbody>';
|
||||
sModal += ' </table>';
|
||||
sModal += ' </div>';
|
||||
sModal += ' </div>';
|
||||
sModal += ' </div>';
|
||||
sModal += ' <div class="modal-footer">';
|
||||
sModal += ' <button type="button" class="btn btn-default" data-dismiss="modal">' + that.options.formatCancel() + '</button>';
|
||||
sModal += ' <button type="button" class="btn btn-primary">' + that.options.formatSort() + '</button>';
|
||||
sModal += ' </div>';
|
||||
sModal += ' </div>';
|
||||
sModal += ' </div>';
|
||||
sModal += ' </div>';
|
||||
|
||||
$('body').append($(sModal));
|
||||
|
||||
that.$sortModal = $(_id);
|
||||
var $rows = that.$sortModal.find('tbody > tr');
|
||||
|
||||
that.$sortModal.off('click', '#add').on('click', '#add', function() {
|
||||
var total = that.$sortModal.find('.multi-sort-name:first option').length,
|
||||
current = that.$sortModal.find('tbody tr').length;
|
||||
|
||||
if (current < total) {
|
||||
current++;
|
||||
that.addLevel();
|
||||
that.setButtonStates();
|
||||
}
|
||||
});
|
||||
|
||||
that.$sortModal.off('click', '#delete').on('click', '#delete', function() {
|
||||
var total = that.$sortModal.find('.multi-sort-name:first option').length,
|
||||
current = that.$sortModal.find('tbody tr').length;
|
||||
|
||||
if (current > 1 && current <= total) {
|
||||
current--;
|
||||
that.$sortModal.find('tbody tr:last').remove();
|
||||
that.setButtonStates();
|
||||
}
|
||||
});
|
||||
|
||||
that.$sortModal.off('click', '.btn-primary').on('click', '.btn-primary', function() {
|
||||
var $rows = that.$sortModal.find('tbody > tr'),
|
||||
$alert = that.$sortModal.find('div.alert'),
|
||||
fields = [],
|
||||
results = [];
|
||||
|
||||
|
||||
that.options.sortPriority = $.map($rows, function(row) {
|
||||
var $row = $(row),
|
||||
name = $row.find('.multi-sort-name').val(),
|
||||
order = $row.find('.multi-sort-order').val();
|
||||
|
||||
fields.push(name);
|
||||
|
||||
return {
|
||||
sortName: name,
|
||||
sortOrder: order
|
||||
};
|
||||
});
|
||||
|
||||
var sorted_fields = fields.sort();
|
||||
|
||||
for (var i = 0; i < fields.length - 1; i++) {
|
||||
if (sorted_fields[i + 1] == sorted_fields[i]) {
|
||||
results.push(sorted_fields[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (results.length > 0) {
|
||||
if ($alert.length === 0) {
|
||||
$alert = '<div class="alert alert-danger" role="alert"><strong>' + that.options.formatDuplicateAlertTitle() + '</strong> ' + that.options.formatDuplicateAlertDescription() + '</div>';
|
||||
$($alert).insertBefore(that.$sortModal.find('.bars'));
|
||||
}
|
||||
} else {
|
||||
if ($alert.length === 1) {
|
||||
$($alert).remove();
|
||||
}
|
||||
|
||||
that.$sortModal.modal('hide');
|
||||
that.options.sortName = '';
|
||||
|
||||
if (that.options.sidePagination === 'server') {
|
||||
var t = that.options.queryParams;
|
||||
that.options.queryParams = function(params) {
|
||||
params.multiSort = that.options.sortPriority;
|
||||
return t(params);
|
||||
};
|
||||
isSingleSort=false;
|
||||
that.initServer(that.options.silentSort);
|
||||
return;
|
||||
}
|
||||
that.onMultipleSort();
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
if (that.options.sortPriority === null || that.options.sortPriority.length === 0) {
|
||||
if (that.options.sortName) {
|
||||
that.options.sortPriority = [{
|
||||
sortName: that.options.sortName,
|
||||
sortOrder: that.options.sortOrder
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
if (that.options.sortPriority !== null && that.options.sortPriority.length > 0) {
|
||||
if ($rows.length < that.options.sortPriority.length && typeof that.options.sortPriority === 'object') {
|
||||
for (var i = 0; i < that.options.sortPriority.length; i++) {
|
||||
that.addLevel(i, that.options.sortPriority[i]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
that.addLevel(0);
|
||||
}
|
||||
|
||||
that.setButtonStates();
|
||||
}
|
||||
};
|
||||
|
||||
$.fn.bootstrapTable.methods.push('multipleSort');
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
showMultiSort: false,
|
||||
showMultiSortButton: true,
|
||||
sortPriority: null,
|
||||
onMultipleSort: function() {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults.icons, {
|
||||
sort: 'glyphicon-sort',
|
||||
plus: 'glyphicon-plus',
|
||||
minus: 'glyphicon-minus'
|
||||
});
|
||||
|
||||
$.extend($.fn.bootstrapTable.Constructor.EVENTS, {
|
||||
'multiple-sort.bs.table': 'onMultipleSort'
|
||||
});
|
||||
|
||||
$.extend($.fn.bootstrapTable.locales, {
|
||||
formatMultipleSort: function() {
|
||||
return 'Multiple Sort';
|
||||
},
|
||||
formatAddLevel: function() {
|
||||
return 'Add Level';
|
||||
},
|
||||
formatDeleteLevel: function() {
|
||||
return 'Delete Level';
|
||||
},
|
||||
formatColumn: function() {
|
||||
return 'Column';
|
||||
},
|
||||
formatOrder: function() {
|
||||
return 'Order';
|
||||
},
|
||||
formatSortBy: function() {
|
||||
return 'Sort by';
|
||||
},
|
||||
formatThenBy: function() {
|
||||
return 'Then by';
|
||||
},
|
||||
formatSort: function() {
|
||||
return 'Sort';
|
||||
},
|
||||
formatCancel: function() {
|
||||
return 'Cancel';
|
||||
},
|
||||
formatDuplicateAlertTitle: function() {
|
||||
return 'Duplicate(s) detected!';
|
||||
},
|
||||
formatDuplicateAlertDescription: function() {
|
||||
return 'Please remove or change any duplicate column.';
|
||||
},
|
||||
formatSortOrders: function() {
|
||||
return {
|
||||
asc: 'Ascending',
|
||||
desc: 'Descending'
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, $.fn.bootstrapTable.locales);
|
||||
|
||||
var BootstrapTable = $.fn.bootstrapTable.Constructor,
|
||||
_initToolbar = BootstrapTable.prototype.initToolbar;
|
||||
|
||||
BootstrapTable.prototype.initToolbar = function() {
|
||||
this.showToolbar = this.showToolbar || this.options.showMultiSort;
|
||||
var that = this,
|
||||
sortModalSelector = 'sortModal_' + this.$el.attr('id'),
|
||||
sortModalId = '#' + sortModalSelector;
|
||||
this.$sortModal = $(sortModalId);
|
||||
this.sortModalSelector = sortModalSelector;
|
||||
|
||||
_initToolbar.apply(this, Array.prototype.slice.apply(arguments));
|
||||
|
||||
if (that.options.sidePagination === 'server' && !isSingleSort && that.options.sortPriority !== null){
|
||||
var t = that.options.queryParams;
|
||||
that.options.queryParams = function(params) {
|
||||
params.multiSort = that.options.sortPriority;
|
||||
return t(params);
|
||||
};
|
||||
}
|
||||
|
||||
if (this.options.showMultiSort) {
|
||||
var $btnGroup = this.$toolbar.find('>.btn-group').first(),
|
||||
$multiSortBtn = this.$toolbar.find('div.multi-sort');
|
||||
|
||||
if (!$multiSortBtn.length && this.options.showMultiSortButton) {
|
||||
$multiSortBtn = ' <button class="multi-sort btn btn-default' + (this.options.iconSize === undefined ? '' : ' btn-' + this.options.iconSize) + '" type="button" data-toggle="modal" data-target="' + sortModalId + '" title="' + this.options.formatMultipleSort() + '">';
|
||||
$multiSortBtn += ' <i class="' + this.options.iconsPrefix + ' ' + this.options.icons.sort + '"></i>';
|
||||
$multiSortBtn += '</button>';
|
||||
|
||||
$btnGroup.append($multiSortBtn);
|
||||
|
||||
showSortModal(that);
|
||||
}
|
||||
|
||||
this.$el.on('sort.bs.table', function() {
|
||||
isSingleSort = true;
|
||||
});
|
||||
|
||||
this.$el.on('multiple-sort.bs.table', function() {
|
||||
isSingleSort = false;
|
||||
});
|
||||
|
||||
this.$el.on('load-success.bs.table', function() {
|
||||
if (!isSingleSort && that.options.sortPriority !== null && typeof that.options.sortPriority === 'object' && that.options.sidePagination !== 'server') {
|
||||
that.onMultipleSort();
|
||||
}
|
||||
});
|
||||
|
||||
this.$el.on('column-switch.bs.table', function(field, checked) {
|
||||
for (var i = 0; i < that.options.sortPriority.length; i++) {
|
||||
if (that.options.sortPriority[i].sortName === checked) {
|
||||
that.options.sortPriority.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
that.assignSortableArrows();
|
||||
that.$sortModal.remove();
|
||||
showSortModal(that);
|
||||
});
|
||||
|
||||
this.$el.on('reset-view.bs.table', function() {
|
||||
if (!isSingleSort && that.options.sortPriority !== null && typeof that.options.sortPriority === 'object') {
|
||||
that.assignSortableArrows();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.multipleSort = function() {
|
||||
var that = this;
|
||||
if (!isSingleSort && that.options.sortPriority !== null && typeof that.options.sortPriority === 'object' && that.options.sidePagination !== 'server') {
|
||||
that.onMultipleSort();
|
||||
}
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.onMultipleSort = function() {
|
||||
var that = this;
|
||||
|
||||
var cmp = function(x, y) {
|
||||
return x > y ? 1 : x < y ? -1 : 0;
|
||||
};
|
||||
|
||||
var arrayCmp = function(a, b) {
|
||||
var arr1 = [],
|
||||
arr2 = [];
|
||||
|
||||
for (var i = 0; i < that.options.sortPriority.length; i++) {
|
||||
var order = that.options.sortPriority[i].sortOrder === 'desc' ? -1 : 1,
|
||||
aa = a[that.options.sortPriority[i].sortName],
|
||||
bb = b[that.options.sortPriority[i].sortName];
|
||||
|
||||
if (aa === undefined || aa === null) {
|
||||
aa = '';
|
||||
}
|
||||
if (bb === undefined || bb === null) {
|
||||
bb = '';
|
||||
}
|
||||
if ($.isNumeric(aa) && $.isNumeric(bb)) {
|
||||
aa = parseFloat(aa);
|
||||
bb = parseFloat(bb);
|
||||
}
|
||||
if (typeof aa !== 'string') {
|
||||
aa = aa.toString();
|
||||
}
|
||||
|
||||
arr1.push(
|
||||
order * cmp(aa, bb));
|
||||
arr2.push(
|
||||
order * cmp(bb, aa));
|
||||
}
|
||||
|
||||
return cmp(arr1, arr2);
|
||||
};
|
||||
|
||||
this.data.sort(function(a, b) {
|
||||
return arrayCmp(a, b);
|
||||
});
|
||||
|
||||
this.initBody();
|
||||
this.assignSortableArrows();
|
||||
this.trigger('multiple-sort');
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.addLevel = function(index, sortPriority) {
|
||||
var text = index === 0 ? this.options.formatSortBy() : this.options.formatThenBy();
|
||||
|
||||
this.$sortModal.find('tbody')
|
||||
.append($('<tr>')
|
||||
.append($('<td>').text(text))
|
||||
.append($('<td>').append($('<select class="form-control multi-sort-name">')))
|
||||
.append($('<td>').append($('<select class="form-control multi-sort-order">')))
|
||||
);
|
||||
|
||||
var $multiSortName = this.$sortModal.find('.multi-sort-name').last(),
|
||||
$multiSortOrder = this.$sortModal.find('.multi-sort-order').last();
|
||||
|
||||
$.each(this.columns, function(i, column) {
|
||||
if (column.sortable === false || column.visible === false) {
|
||||
return true;
|
||||
}
|
||||
$multiSortName.append('<option value="' + column.field + '">' + column.title + '</option>');
|
||||
});
|
||||
|
||||
$.each(this.options.formatSortOrders(), function(value, order) {
|
||||
$multiSortOrder.append('<option value="' + value + '">' + order + '</option>');
|
||||
});
|
||||
|
||||
if (sortPriority !== undefined) {
|
||||
$multiSortName.find('option[value="' + sortPriority.sortName + '"]').attr("selected", true);
|
||||
$multiSortOrder.find('option[value="' + sortPriority.sortOrder + '"]').attr("selected", true);
|
||||
}
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.assignSortableArrows = function() {
|
||||
var that = this,
|
||||
headers = that.$header.find('th');
|
||||
|
||||
for (var i = 0; i < headers.length; i++) {
|
||||
for (var c = 0; c < that.options.sortPriority.length; c++) {
|
||||
if ($(headers[i]).data('field') === that.options.sortPriority[c].sortName) {
|
||||
$(headers[i]).find('.sortable').removeClass('desc asc').addClass(that.options.sortPriority[c].sortOrder);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.setButtonStates = function() {
|
||||
var total = this.$sortModal.find('.multi-sort-name:first option').length,
|
||||
current = this.$sortModal.find('tbody tr').length;
|
||||
|
||||
if (current == total) {
|
||||
this.$sortModal.find('#add').attr('disabled', 'disabled');
|
||||
}
|
||||
if (current > 1) {
|
||||
this.$sortModal.find('#delete').removeAttr('disabled');
|
||||
}
|
||||
if (current < total) {
|
||||
this.$sortModal.find('#add').removeAttr('disabled');
|
||||
}
|
||||
if (current == 1) {
|
||||
this.$sortModal.find('#delete').attr('disabled', 'disabled');
|
||||
}
|
||||
};
|
||||
})(jQuery);
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Multiple Sort",
|
||||
"version": "1.1.0",
|
||||
"description": "Plugin to support the multiple sort.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/multiple-sort",
|
||||
"example": "#",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-multiple-sort",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/multiple-sort"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "dimbslmh",
|
||||
"image": "https://avatars1.githubusercontent.com/u/745635"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* @author: Brian Huisman
|
||||
* @webSite: http://www.greywyvern.com
|
||||
* @version: v1.0.0
|
||||
* JS functions to allow natural sorting on bootstrap-table columns
|
||||
* add data-sorter="alphanum" or data-sorter="numericOnly" to any th
|
||||
*
|
||||
* @update Dennis Hernández <http://djhvscf.github.io/Blog>
|
||||
* @update Duane May
|
||||
*/
|
||||
|
||||
function alphanum(a, b) {
|
||||
function chunkify(t) {
|
||||
var tz = [],
|
||||
x = 0,
|
||||
y = -1,
|
||||
n = 0,
|
||||
i,
|
||||
j;
|
||||
|
||||
while (i = (j = t.charAt(x++)).charCodeAt(0)) {
|
||||
var m = (i === 46 || (i >= 48 && i <= 57));
|
||||
if (m !== n) {
|
||||
tz[++y] = "";
|
||||
n = m;
|
||||
}
|
||||
tz[y] += j;
|
||||
}
|
||||
return tz;
|
||||
}
|
||||
|
||||
function stringfy(v) {
|
||||
if (typeof(v) === "number") {
|
||||
v = "" + v;
|
||||
}
|
||||
if (!v) {
|
||||
v = "";
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
var aa = chunkify(stringfy(a));
|
||||
var bb = chunkify(stringfy(b));
|
||||
|
||||
for (x = 0; aa[x] && bb[x]; x++) {
|
||||
if (aa[x] !== bb[x]) {
|
||||
var c = Number(aa[x]),
|
||||
d = Number(bb[x]);
|
||||
|
||||
if (c == aa[x] && d == bb[x]) {
|
||||
return c - d;
|
||||
} else {
|
||||
return (aa[x] > bb[x]) ? 1 : -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return aa.length - bb.length;
|
||||
}
|
||||
|
||||
function numericOnly(a, b) {
|
||||
function stripNonNumber(s) {
|
||||
s = s.replace(new RegExp(/[^0-9]/g), "");
|
||||
return parseInt(s, 10);
|
||||
}
|
||||
|
||||
return stripNonNumber(a) - stripNonNumber(b);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Natural Sorting",
|
||||
"version": "1.0.0",
|
||||
"description": "Plugin to support the natural sorting.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/natural-sorting",
|
||||
"example": "#",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-natural-sorting",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/natural-sorting"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "GreyWyvern",
|
||||
"image": "https://avatars1.githubusercontent.com/u/137631"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
.jumpto input {
|
||||
height: 31px;
|
||||
width: 50px;
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* @author Jay <jwang@dizsoft.com>
|
||||
*/
|
||||
|
||||
(function ($) {
|
||||
'use strict';
|
||||
var sprintf = $.fn.bootstrapTable.utils.sprintf;
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
showJumpto: false,
|
||||
exportOptions: {}
|
||||
});
|
||||
|
||||
$.extend($.fn.bootstrapTable.locales, {
|
||||
formatJumpto: function () {
|
||||
return 'GO';
|
||||
}
|
||||
});
|
||||
$.extend($.fn.bootstrapTable.defaults, $.fn.bootstrapTable.locales);
|
||||
|
||||
var BootstrapTable = $.fn.bootstrapTable.Constructor,
|
||||
_initPagination = BootstrapTable.prototype.initPagination;
|
||||
|
||||
BootstrapTable.prototype.initPagination = function () {
|
||||
_initPagination.apply(this, Array.prototype.slice.apply(arguments));
|
||||
|
||||
if (this.options.showJumpto) {
|
||||
var that = this,
|
||||
$pageGroup = this.$pagination.find('ul.pagination'),
|
||||
$jumpto = $pageGroup.find('li.jumpto');
|
||||
|
||||
if (!$jumpto.length) {
|
||||
$jumpto = $([
|
||||
'<li class="jumpto">',
|
||||
'<input type="text" class="form-control">',
|
||||
'<button class="btn' +
|
||||
sprintf(' btn-%s', this.options.buttonsClass) +
|
||||
sprintf(' btn-%s', this.options.iconSize) +
|
||||
'" title="' + this.options.formatJumpto() + '" ' +
|
||||
' type="button">'+this.options.formatJumpto(),
|
||||
'</button>',
|
||||
'</li>'].join('')).appendTo($pageGroup);
|
||||
|
||||
$jumpto.find('button').click(function () {
|
||||
that.selectPage(parseInt($jumpto.find('input').val()));
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
})(jQuery);
|
||||
@@ -0,0 +1,21 @@
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2019 doug-the-guy <badlydrawnsun@yahoo.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -0,0 +1,92 @@
|
||||
# Bootstrap Table Pipelining
|
||||
|
||||
Use Plugin: [bootstrap-table-pipeline]
|
||||
|
||||
This plugin enables client side data caching for server side requests which will
|
||||
eliminate the need to issue a new request every page change. This will allow
|
||||
for a performance balance for a large data set between returning all data at once
|
||||
(client side paging) and a new server side request (server side paging).
|
||||
|
||||
There are two new options:
|
||||
- usePipeline: enables this feature
|
||||
- pipelineSize: the size of each cache window
|
||||
|
||||
The size of the pipeline must be evenly divisible by the current page size. This is
|
||||
assured by rounding up to the nearest evenly divisible value. For example, if
|
||||
the pipeline size is 4990 and the current page size is 25, then pipeline size will
|
||||
be dynamically set to 5000.
|
||||
|
||||
The cache windows are computed based on the pipeline size and the total number of rows
|
||||
returned by the server side query. For example, with pipeline size 500 and total rows
|
||||
1300, the cache windows will be:
|
||||
|
||||
[{'lower': 0, 'upper': 499}, {'lower': 500, 'upper': 999}, {'lower': 1000, 'upper': 1499}]
|
||||
|
||||
Using the limit (i.e. the pipelineSize) and offset parameters, the server side request
|
||||
**MUST** return only the data in the requested cache window **AND** the total number of rows.
|
||||
To wit, the server side code must use the offset and limit parameters to prepare the response
|
||||
data.
|
||||
|
||||
On a page change, the new offset is checked if it is within the current cache window. If so,
|
||||
the requested page data is returned from the cached data set. Otherwise, a new server side
|
||||
request will be issued for the new cache window.
|
||||
|
||||
The current cached data is only invalidated on these events:
|
||||
- sorting
|
||||
- searching
|
||||
- page size change
|
||||
- page change moves into a new cache window
|
||||
|
||||
There are two new events:
|
||||
- cached-data-hit.bs.table: issued when cached data is used on a page change
|
||||
- cached-data-reset.bs.table: issued when the cached data is invalidated and new server side request is issued
|
||||
|
||||
## Features
|
||||
|
||||
* Created with Bootstrap 4
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
# assumed import of bootstrap and bootstrap-table assets
|
||||
<script src="/path/to/bootstrap-table-pipeline.js"></script>
|
||||
...
|
||||
<table id="pipeline_table"
|
||||
class="table table-striped"
|
||||
data-method='post'
|
||||
data-use-pipeline="true"
|
||||
data-pipeline-size="5000"
|
||||
data-pagination="true"
|
||||
data-side-pagination="server"
|
||||
data-page-size="50">
|
||||
<thead><tr>
|
||||
<th data-field="type" data-sortable="true">Type</th>
|
||||
<th data-field="value" data-sortable="true">Value</th>
|
||||
<th data-field="date" data-sortable="true">Date</th>
|
||||
</tr></thead>
|
||||
</table>
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
### usePipeline
|
||||
|
||||
* type: Boolean
|
||||
* description: Set true to enable pipelining
|
||||
* default: `false`
|
||||
|
||||
## pipelineSize
|
||||
|
||||
* type: Integer
|
||||
* description: Size of each cache window. Must be greater than 0
|
||||
* default: `1000`
|
||||
|
||||
## Events
|
||||
|
||||
### onCachedDataHit(cached-data-hit.bs.table)
|
||||
|
||||
* Fires when paging was able to use the locally cached data.
|
||||
|
||||
### onCachedDataReset(cached-data-reset.bs.table)
|
||||
|
||||
* Fires when the locally cached data needed to be reset (i.e. on sorting, searching, page size change or paged out of current cache window)
|
||||
330
InvenTree/InvenTree/static/bootstrap-table/extensions/pipeline/bootstrap-table-pipeline.js
vendored
Normal file
330
InvenTree/InvenTree/static/bootstrap-table/extensions/pipeline/bootstrap-table-pipeline.js
vendored
Normal file
@@ -0,0 +1,330 @@
|
||||
/**
|
||||
* @author doug-the-guy
|
||||
* @version v1.0.0
|
||||
*
|
||||
* Boostrap Table Pipeline
|
||||
* -----------------------
|
||||
*
|
||||
* This plugin enables client side data caching for server side requests which will
|
||||
* eliminate the need to issue a new request every page change. This will allow
|
||||
* for a performance balance for a large data set between returning all data at once
|
||||
* (client side paging) and a new server side request (server side paging).
|
||||
*
|
||||
* There are two new options:
|
||||
* - usePipeline: enables this feature
|
||||
* - pipelineSize: the size of each cache window
|
||||
*
|
||||
* The size of the pipeline must be evenly divisible by the current page size. This is
|
||||
* assured by rounding up to the nearest evenly divisible value. For example, if
|
||||
* the pipeline size is 4990 and the current page size is 25, then pipeline size will
|
||||
* be dynamically set to 5000.
|
||||
*
|
||||
* The cache windows are computed based on the pipeline size and the total number of rows
|
||||
* returned by the server side query. For example, with pipeline size 500 and total rows
|
||||
* 1300, the cache windows will be:
|
||||
*
|
||||
* [{'lower': 0, 'upper': 499}, {'lower': 500, 'upper': 999}, {'lower': 1000, 'upper': 1499}]
|
||||
*
|
||||
* Using the limit (i.e. the pipelineSize) and offset parameters, the server side request
|
||||
* **MUST** return only the data in the requested cache window **AND** the total number of rows.
|
||||
* To wit, the server side code must use the offset and limit parameters to prepare the response
|
||||
* data.
|
||||
*
|
||||
* On a page change, the new offset is checked if it is within the current cache window. If so,
|
||||
* the requested page data is returned from the cached data set. Otherwise, a new server side
|
||||
* request will be issued for the new cache window.
|
||||
*
|
||||
* The current cached data is only invalidated on these events:
|
||||
* * sorting
|
||||
* * searching
|
||||
* * page size change
|
||||
* * page change moves into a new cache window
|
||||
*
|
||||
* There are two new events:
|
||||
* - cached-data-hit.bs.table: issued when cached data is used on a page change
|
||||
* - cached-data-reset.bs.table: issued when the cached data is invalidated and a
|
||||
* new server side request is issued
|
||||
*
|
||||
**/
|
||||
|
||||
(function ($) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var Utils = $.fn.bootstrapTable.utils;
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
usePipeline: false,
|
||||
pipelineSize: 1000,
|
||||
onCachedDataHit: function(data) {
|
||||
return false;
|
||||
},
|
||||
onCachedDataReset: function(data){
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
$.extend($.fn.bootstrapTable.Constructor.EVENTS, {
|
||||
'cached-data-hit.bs.table': 'onCachedDataHit',
|
||||
'cached-data-reset.bs.table': 'onCachedDataReset'
|
||||
});
|
||||
|
||||
var BootstrapTable = $.fn.bootstrapTable.Constructor,
|
||||
_init = BootstrapTable.prototype.init,
|
||||
_initServer = BootstrapTable.prototype.initServer,
|
||||
_onSearch = BootstrapTable.prototype.onSearch,
|
||||
_onSort = BootstrapTable.prototype.onSort,
|
||||
_onPageListChange = BootstrapTable.prototype.onPageListChange;
|
||||
|
||||
BootstrapTable.prototype.init = function () {
|
||||
// needs to be called before initServer()
|
||||
this.initPipeline();
|
||||
_init.apply(this, Array.prototype.slice.apply(arguments));
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.initPipeline = function() {
|
||||
this.cacheRequestJSON = {};
|
||||
this.cacheWindows = [];
|
||||
this.currWindow = 0;
|
||||
this.resetCache = true;
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.onSearch = function(event) {
|
||||
/* force a cache reset on search */
|
||||
if (this.options.usePipeline) {
|
||||
this.resetCache = true;
|
||||
}
|
||||
_onSearch.apply(this, Array.prototype.slice.apply(arguments));
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.onSort = function(event) {
|
||||
/* force a cache reset on sort */
|
||||
if (this.options.usePipeline) {
|
||||
this.resetCache = true;
|
||||
}
|
||||
_onSort.apply(this, Array.prototype.slice.apply(arguments));
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.onPageListChange = function (event) {
|
||||
/* rebuild cache window on page size change */
|
||||
var target = $(event.currentTarget);
|
||||
var newPageSize = parseInt(target.text());
|
||||
this.options.pipelineSize = this.calculatePipelineSize(this.options.pipelineSize, newPageSize);
|
||||
this.resetCache = true;
|
||||
_onPageListChange.apply(this, Array.prototype.slice.apply(arguments));
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.calculatePipelineSize = function(pipelineSize, pageSize) {
|
||||
/* calculate pipeline size by rounding up to the nearest value evenly divisible
|
||||
* by the pageSize */
|
||||
if(pageSize == 0) return 0;
|
||||
return Math.ceil(pipelineSize/pageSize) * pageSize;
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.setCacheWindows = function() {
|
||||
/* set cache windows based on the total number of rows returned by server side
|
||||
* request and the pipelineSize */
|
||||
this.cacheWindows = [];
|
||||
var numWindows = this.options.totalRows / this.options.pipelineSize;
|
||||
for(var i = 0; i <= numWindows; i++){
|
||||
var b = i * this.options.pipelineSize;
|
||||
this.cacheWindows[i] = {'lower': b, 'upper': b + this.options.pipelineSize - 1};
|
||||
}
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.setCurrWindow = function(offset) {
|
||||
/* set the current cache window index, based on where the current offset falls */
|
||||
this.currWindow = 0;
|
||||
for(var i = 0; i < this.cacheWindows.length; i++){
|
||||
if(this.cacheWindows[i].lower <= offset && offset <= this.cacheWindows[i].upper){
|
||||
this.currWindow = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.drawFromCache = function(offset, limit) {
|
||||
/* draw rows from the cache using offset and limit */
|
||||
var res = $.extend(true, {}, this.cacheRequestJSON);
|
||||
var drawStart = offset - this.cacheWindows[this.currWindow].lower;
|
||||
var drawEnd = drawStart + limit;
|
||||
res.rows = res.rows.slice(drawStart, drawEnd);
|
||||
return res;
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.initServer = function(silent, query, url){
|
||||
/* determine if requested data is in cache (on paging) or if
|
||||
* a new ajax request needs to be issued (sorting, searching, paging
|
||||
* moving outside of cached data, page size change)
|
||||
* initial version of this extension will entirely override base initServer
|
||||
**/
|
||||
|
||||
var data = {};
|
||||
var index = this.header.fields.indexOf(this.options.sortName);
|
||||
|
||||
var params = {
|
||||
searchText: this.searchText,
|
||||
sortName: this.options.sortName,
|
||||
sortOrder: this.options.sortOrder
|
||||
};
|
||||
|
||||
var request = null;
|
||||
|
||||
if (this.header.sortNames[index]) {
|
||||
params.sortName = this.header.sortNames[index];
|
||||
}
|
||||
|
||||
if (this.options.pagination && this.options.sidePagination === 'server') {
|
||||
params.pageSize = this.options.pageSize === this.options.formatAllRows()
|
||||
? this.options.totalRows : this.options.pageSize
|
||||
params.pageNumber = this.options.pageNumber
|
||||
}
|
||||
|
||||
if (!(url || this.options.url) && !this.options.ajax) {
|
||||
return;
|
||||
}
|
||||
|
||||
var useAjax = true;
|
||||
if (this.options.queryParamsType === 'limit') {
|
||||
params = {
|
||||
searchText: params.searchText,
|
||||
sortName: params.sortName,
|
||||
sortOrder: params.sortOrder
|
||||
}
|
||||
if (this.options.pagination && this.options.sidePagination === 'server') {
|
||||
params.limit = this.options.pageSize === this.options.formatAllRows() ? this.options.totalRows : this.options.pageSize;
|
||||
params.offset = (this.options.pageSize === this.options.formatAllRows() ? this.options.totalRows : this.options.pageSize) * (this.options.pageNumber - 1);
|
||||
if (this.options.usePipeline) {
|
||||
// if cacheWindows is empty, this is the initial request
|
||||
if(!this.cacheWindows.length){
|
||||
useAjax = true;
|
||||
params.drawOffset = params.offset;
|
||||
// cache exists: determine if the page request is entirely within the current cached window
|
||||
} else {
|
||||
var w = this.cacheWindows[this.currWindow];
|
||||
// case 1: reset cache but stay within current window (e.g. column sort)
|
||||
// case 2: move outside of the current window (e.g. search or paging)
|
||||
// since each cache window is aligned with the current page size
|
||||
// checking if params.offset is outside the current window is sufficient.
|
||||
// need to requery for preceding or succeeding cache window
|
||||
// also handle case
|
||||
if(this.resetCache || (params.offset < w.lower || params.offset > w.upper)){
|
||||
useAjax = true;
|
||||
this.setCurrWindow(params.offset);
|
||||
// store the relative offset for drawing the page data afterwards
|
||||
params.drawOffset = params.offset;
|
||||
// now set params.offset to the lower bound of the new cache window
|
||||
// the server will return that whole cache window
|
||||
params.offset = this.cacheWindows[this.currWindow].lower;
|
||||
// within current cache window
|
||||
} else {
|
||||
useAjax = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (params.limit === 0) {
|
||||
delete params.limit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// force an ajax call - this is on search, sort or page size change
|
||||
if (this.resetCache) {
|
||||
useAjax = true;
|
||||
this.resetCache = false;
|
||||
}
|
||||
|
||||
if(this.options.usePipeline && useAjax) {
|
||||
/* in this scenario limit is used on the server to get the cache window
|
||||
* and drawLimit is used to get the page data afterwards */
|
||||
params.drawLimit = params.limit;
|
||||
params.limit = this.options.pipelineSize;
|
||||
}
|
||||
|
||||
// cached results can be used
|
||||
if(!useAjax) {
|
||||
var res = this.drawFromCache(params.offset, params.limit);
|
||||
this.load(res);
|
||||
this.trigger('load-success', res);
|
||||
this.trigger('cached-data-hit', res);
|
||||
return;
|
||||
}
|
||||
// cached results can't be used
|
||||
// continue base initServer code
|
||||
if (!($.isEmptyObject(this.filterColumnsPartial))) {
|
||||
params.filter = JSON.stringify(this.filterColumnsPartial, null);
|
||||
}
|
||||
|
||||
data = Utils.calculateObjectValue(this.options, this.options.queryParams, [params], data);
|
||||
|
||||
$.extend(data, query || {});
|
||||
|
||||
// false to stop request
|
||||
if (data === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!silent) {
|
||||
this.$tableLoading.show();
|
||||
}
|
||||
var self = this;
|
||||
|
||||
request = $.extend({}, Utils.calculateObjectValue(null, this.options.ajaxOptions), {
|
||||
type: this.options.method,
|
||||
url: url || this.options.url,
|
||||
data: this.options.contentType === 'application/json' && this.options.method === 'post'
|
||||
? JSON.stringify(data) : data,
|
||||
cache: this.options.cache,
|
||||
contentType: this.options.contentType,
|
||||
dataType: this.options.dataType,
|
||||
success: function(res){
|
||||
res = Utils.calculateObjectValue(self.options, self.options.responseHandler, [res], res);
|
||||
// cache results if using pipelining
|
||||
if(self.options.usePipeline){
|
||||
// store entire request in cache
|
||||
self.cacheRequestJSON = $.extend(true, {}, res);
|
||||
// this gets set in load() also but needs to be set before
|
||||
// setting cacheWindows
|
||||
self.options.totalRows = res[self.options.totalField];
|
||||
// if this is a search, potentially less results will be returned
|
||||
// so cache windows need to be rebuilt. Otherwise it
|
||||
// will come out the same
|
||||
self.setCacheWindows();
|
||||
self.setCurrWindow(params.drawOffset);
|
||||
// just load data for the page
|
||||
res = self.drawFromCache(params.drawOffset, params.drawLimit);
|
||||
self.trigger('cached-data-reset', res);
|
||||
}
|
||||
self.load(res);
|
||||
self.trigger('load-success', res);
|
||||
if (!silent) self.$tableLoading.hide();
|
||||
},
|
||||
error: function(res){
|
||||
var data = [];
|
||||
if (self.options.sidePagination === 'server') {
|
||||
data = {};
|
||||
data[self.options.totalField] = 0;
|
||||
data[self.options.dataField] = [];
|
||||
}
|
||||
self.load(data);
|
||||
self.trigger('load-error', res.status, res);
|
||||
if (!silent) self.$tableLoading.hide();
|
||||
}
|
||||
});
|
||||
|
||||
if (this.options.ajax) {
|
||||
Utils.calculateObjectValue(this, this.options.ajax, [request], null);
|
||||
} else {
|
||||
if (this._xhr && this._xhr.readyState !== 4) {
|
||||
this._xhr.abort();
|
||||
}
|
||||
this._xhr = $.ajax(request);
|
||||
}
|
||||
}
|
||||
|
||||
$.fn.bootstrapTable.methods.push();
|
||||
|
||||
|
||||
|
||||
})(jQuery);
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "Pipeline",
|
||||
"version": "1.0.0",
|
||||
"description": "Plugin to support a hybrid approach to server/client side paging.",
|
||||
"url": "",
|
||||
"example": "#",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-pipeline",
|
||||
"url": ""
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "doug-the-guy",
|
||||
"image": ""
|
||||
}
|
||||
}
|
||||
|
||||
149
InvenTree/InvenTree/static/bootstrap-table/extensions/print/bootstrap-table-print.js
vendored
Normal file
149
InvenTree/InvenTree/static/bootstrap-table/extensions/print/bootstrap-table-print.js
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
(function ($) {
|
||||
'use strict';
|
||||
|
||||
var sprintf = $.fn.bootstrapTable.utils.sprintf;
|
||||
|
||||
function printPageBuilderDefault(table) {
|
||||
return '<html><head>' +
|
||||
'<style type="text/css" media="print">' +
|
||||
' @page { size: auto; margin: 25px 0 25px 0; }' +
|
||||
'</style>' +
|
||||
'<style type="text/css" media="all">' +
|
||||
'table{border-collapse: collapse; font-size: 12px; }\n' +
|
||||
'table, th, td {border: 1px solid grey}\n' +
|
||||
'th, td {text-align: center; vertical-align: middle;}\n' +
|
||||
'p {font-weight: bold; margin-left:20px }\n' +
|
||||
'table { width:94%; margin-left:3%; margin-right:3%}\n' +
|
||||
'div.bs-table-print { text-align:center;}\n' +
|
||||
'</style></head><title>Print Table</title><body>' +
|
||||
'<p>Printed on: ' + new Date + ' </p>' +
|
||||
'<div class="bs-table-print">' + table + "</div></body></html>";
|
||||
}
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
showPrint: false,
|
||||
printAsFilteredAndSortedOnUI: true, //boolean, when true - print table as sorted and filtered on UI.
|
||||
//Please note that if true is set, along with explicit predefined print options for filtering and sorting (printFilter, printSortOrder, printSortColumn)- then they will be applied on data already filtered and sorted by UI controls.
|
||||
//For printing data as filtered and sorted on UI - do not set these 3 options:printFilter, printSortOrder, printSortColumn
|
||||
|
||||
printSortColumn: undefined , //String, set column field name to be sorted by
|
||||
printSortOrder: 'asc', //String: 'asc' , 'desc' - relevant only if printSortColumn is set
|
||||
printPageBuilder: function(table){return printPageBuilderDefault(table)} // function, receive html <table> element as string, returns html string for printing. by default delegates to function printPageBuilderDefault(table). used for styling and adding header or footer
|
||||
});
|
||||
$.extend($.fn.bootstrapTable.COLUMN_DEFAULTS, {
|
||||
printFilter: undefined, //set value to filter by in print page
|
||||
printIgnore: false, //boolean, set true to ignore this column in the print page
|
||||
printFormatter:undefined //function(value, row, index), formats the cell value for this column in the printed table. Function behaviour is similar to the 'formatter' column option
|
||||
|
||||
});
|
||||
$.extend($.fn.bootstrapTable.defaults.icons, {
|
||||
print: 'glyphicon-print icon-share'
|
||||
});
|
||||
|
||||
var BootstrapTable = $.fn.bootstrapTable.Constructor,
|
||||
_initToolbar = BootstrapTable.prototype.initToolbar;
|
||||
|
||||
BootstrapTable.prototype.initToolbar = function () {
|
||||
this.showToolbar = this.showToolbar || this.options.showPrint;
|
||||
|
||||
_initToolbar.apply(this, Array.prototype.slice.apply(arguments));
|
||||
|
||||
if (this.options.showPrint) {
|
||||
var that = this,
|
||||
$btnGroup = this.$toolbar.find('>.btn-group'),
|
||||
$print = $btnGroup.find('button.bs-print');
|
||||
|
||||
if (!$print.length) {
|
||||
$print = $([
|
||||
'<button class="bs-print btn btn-default' + sprintf(' btn-%s"', this.options.iconSize) + ' name="print" title="print" type="button">',
|
||||
sprintf('<i class="%s %s"></i> ', this.options.iconsPrefix, this.options.icons.print),
|
||||
'</button>'].join('')).appendTo($btnGroup);
|
||||
|
||||
$print.click(function () {
|
||||
function formatValue(row, i, column ) {
|
||||
var value = row[column.field];
|
||||
if (typeof column.printFormatter === 'function') {
|
||||
return column.printFormatter.apply(column, [value, row, i]);
|
||||
}
|
||||
else {
|
||||
return typeof value === 'undefined' ? "-" : value;
|
||||
}
|
||||
}
|
||||
|
||||
function buildTable(data, columnsArray) {
|
||||
var html = ['<table><thead>'];
|
||||
for (var k = 0; k < columnsArray.length; k++) {
|
||||
var columns = columnsArray[k];
|
||||
html.push('<tr>');
|
||||
for (var h = 0; h < columns.length; h++) {
|
||||
if (!columns[h].printIgnore) {
|
||||
html.push(
|
||||
'<th',
|
||||
sprintf(' rowspan="%s"', columns[h].rowspan),
|
||||
sprintf(' colspan="%s"', columns[h].colspan),
|
||||
sprintf('>%s</th>', columns[h].title)
|
||||
);
|
||||
}
|
||||
}
|
||||
html.push('</tr>');
|
||||
}
|
||||
html.push('</thead><tbody>');
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
html.push('<tr>');
|
||||
for(var l = 0; l < columnsArray.length; l++) {
|
||||
var columns = columnsArray[l];
|
||||
for(var j = 0; j < columns.length; j++) {
|
||||
if (!columns[j].printIgnore && columns[j].field) {
|
||||
html.push('<td>', formatValue(data[i], i, columns[j]), '</td>');
|
||||
}
|
||||
}
|
||||
}
|
||||
html.push('</tr>');
|
||||
}
|
||||
html.push('</tbody></table>');
|
||||
return html.join('');
|
||||
}
|
||||
function sortRows(data,colName,sortOrder) {
|
||||
if(!colName){
|
||||
return data;
|
||||
}
|
||||
var reverse = sortOrder != 'asc';
|
||||
reverse = -((+reverse) || -1);
|
||||
return data.sort(function (a, b) {
|
||||
return reverse * (a[colName].localeCompare(b[colName]));
|
||||
});
|
||||
}
|
||||
function filterRow(row,filters) {
|
||||
for (var index = 0; index < filters.length; ++index) {
|
||||
if(row[filters[index].colName]!=filters[index].value) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
function filterRows(data,filters) {
|
||||
return data.filter(function (row) {
|
||||
return filterRow(row,filters)
|
||||
});
|
||||
}
|
||||
function getColumnFilters(columns) {
|
||||
return !columns || !columns[0] ? [] : columns[0].filter(function (col) {
|
||||
return col.printFilter;
|
||||
}).map(function (col) {
|
||||
return {colName:col.field, value:col.printFilter};
|
||||
});
|
||||
}
|
||||
var doPrint = function (data) {
|
||||
data=filterRows(data,getColumnFilters(that.options.columns));
|
||||
data=sortRows(data,that.options.printSortColumn,that.options.printSortOrder);
|
||||
var table=buildTable(data,that.options.columns);
|
||||
var newWin = window.open("");
|
||||
newWin.document.write(that.options.printPageBuilder.call(this, table));
|
||||
newWin.print();
|
||||
newWin.close();
|
||||
};
|
||||
doPrint(that.options.printAsFilteredAndSortedOnUI? that.getData() : that.options.data.slice(0));
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
})(jQuery);
|
||||
@@ -0,0 +1,181 @@
|
||||
/**
|
||||
* @author: Dennis Hernández
|
||||
* @webSite: http://djhvscf.github.io/Blog
|
||||
* @version: v1.1.0
|
||||
*/
|
||||
|
||||
!function ($) {
|
||||
|
||||
'use strict';
|
||||
|
||||
//From MDN site, https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
|
||||
var filterFn = function () {
|
||||
if (!Array.prototype.filter) {
|
||||
Array.prototype.filter = function(fun/*, thisArg*/) {
|
||||
'use strict';
|
||||
|
||||
if (this === void 0 || this === null) {
|
||||
throw new TypeError();
|
||||
}
|
||||
|
||||
var t = Object(this);
|
||||
var len = t.length >>> 0;
|
||||
if (typeof fun !== 'function') {
|
||||
throw new TypeError();
|
||||
}
|
||||
|
||||
var res = [];
|
||||
var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
|
||||
for (var i = 0; i < len; i++) {
|
||||
if (i in t) {
|
||||
var val = t[i];
|
||||
|
||||
// NOTE: Technically this should Object.defineProperty at
|
||||
// the next index, as push can be affected by
|
||||
// properties on Object.prototype and Array.prototype.
|
||||
// But that method's new, and collisions should be
|
||||
// rare, so use the more-compatible alternative.
|
||||
if (fun.call(thisArg, val, i, t)) {
|
||||
res.push(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
reorderableColumns: false,
|
||||
maxMovingRows: 10,
|
||||
onReorderColumn: function (headerFields) {
|
||||
return false;
|
||||
},
|
||||
dragaccept: null
|
||||
});
|
||||
|
||||
$.extend($.fn.bootstrapTable.Constructor.EVENTS, {
|
||||
'reorder-column.bs.table': 'onReorderColumn'
|
||||
});
|
||||
|
||||
var BootstrapTable = $.fn.bootstrapTable.Constructor,
|
||||
_initHeader = BootstrapTable.prototype.initHeader,
|
||||
_toggleColumn = BootstrapTable.prototype.toggleColumn,
|
||||
_toggleView = BootstrapTable.prototype.toggleView,
|
||||
_resetView = BootstrapTable.prototype.resetView;
|
||||
|
||||
BootstrapTable.prototype.initHeader = function () {
|
||||
_initHeader.apply(this, Array.prototype.slice.apply(arguments));
|
||||
|
||||
if (!this.options.reorderableColumns) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.makeRowsReorderable();
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.toggleColumn = function () {
|
||||
_toggleColumn.apply(this, Array.prototype.slice.apply(arguments));
|
||||
|
||||
if (!this.options.reorderableColumns) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.makeRowsReorderable();
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.toggleView = function () {
|
||||
_toggleView.apply(this, Array.prototype.slice.apply(arguments));
|
||||
|
||||
if (!this.options.reorderableColumns) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.options.cardView) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.makeRowsReorderable();
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.resetView = function () {
|
||||
_resetView.apply(this, Array.prototype.slice.apply(arguments));
|
||||
|
||||
if (!this.options.reorderableColumns) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.makeRowsReorderable();
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.makeRowsReorderable = function () {
|
||||
var that = this;
|
||||
try {
|
||||
$(this.$el).dragtable('destroy');
|
||||
} catch (e) {}
|
||||
$(this.$el).dragtable({
|
||||
maxMovingRows: that.options.maxMovingRows,
|
||||
dragaccept: that.options.dragaccept,
|
||||
clickDelay:200,
|
||||
beforeStop: function() {
|
||||
var ths = [],
|
||||
formatters = [],
|
||||
columns = [],
|
||||
columnsHidden = [],
|
||||
columnIndex = -1,
|
||||
optionsColumns = [];
|
||||
that.$header.find('th').each(function (i) {
|
||||
ths.push($(this).data('field'));
|
||||
formatters.push($(this).data('formatter'));
|
||||
});
|
||||
|
||||
//Exist columns not shown
|
||||
if (ths.length < that.columns.length) {
|
||||
columnsHidden = $.grep(that.columns, function (column) {
|
||||
return !column.visible;
|
||||
});
|
||||
for (var i = 0; i < columnsHidden.length; i++) {
|
||||
ths.push(columnsHidden[i].field);
|
||||
formatters.push(columnsHidden[i].formatter);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < this.length; i++ ) {
|
||||
columnIndex = that.fieldsColumnsIndex[ths[i]];
|
||||
if (columnIndex !== -1) {
|
||||
that.columns[columnIndex].fieldIndex = i;
|
||||
columns.push(that.columns[columnIndex]);
|
||||
that.columns.splice(columnIndex, 1);
|
||||
}
|
||||
}
|
||||
|
||||
that.columns = that.columns.concat(columns);
|
||||
|
||||
filterFn(); //Support <IE9
|
||||
$.each(that.columns, function(i, column) {
|
||||
var found = false,
|
||||
field = column.field;
|
||||
that.options.columns[0].filter(function(item) {
|
||||
if(!found && item["field"] == field) {
|
||||
optionsColumns.push(item);
|
||||
found = true;
|
||||
return false;
|
||||
} else
|
||||
return true;
|
||||
})
|
||||
});
|
||||
|
||||
that.options.columns[0] = optionsColumns;
|
||||
|
||||
that.header.fields = ths;
|
||||
that.header.formatters = formatters;
|
||||
that.initHeader();
|
||||
that.initToolbar();
|
||||
that.initBody();
|
||||
that.resetView();
|
||||
that.trigger('reorder-column', ths);
|
||||
}
|
||||
});
|
||||
};
|
||||
}(jQuery);
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Reorder Columns",
|
||||
"version": "1.1.0",
|
||||
"description": "Plugin to support the reordering columns feature.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/reorder-columns",
|
||||
"example": "http://issues.wenzhixin.net.cn/bootstrap-table/#extensions/reorder-columns.html",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-reorder-columns",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/reorder-columns"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "djhvscf",
|
||||
"image": "https://avatars1.githubusercontent.com/u/4496763"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
.reorder_rows_onDragClass td {
|
||||
background-color: #eee;
|
||||
-webkit-box-shadow: 11px 5px 12px 2px #333, 0 1px 0 #ccc inset, 0 -1px 0 #ccc inset;
|
||||
-webkit-box-shadow: 6px 3px 5px #555, 0 1px 0 #ccc inset, 0 -1px 0 #ccc inset;
|
||||
-moz-box-shadow: 6px 4px 5px 1px #555, 0 1px 0 #ccc inset, 0 -1px 0 #ccc inset;
|
||||
-box-shadow: 6px 4px 5px 1px #555, 0 1px 0 #ccc inset, 0 -1px 0 #ccc inset;
|
||||
}
|
||||
|
||||
.reorder_rows_onDragClass td:last-child {
|
||||
-webkit-box-shadow: 8px 7px 12px 0 #333, 0 1px 0 #ccc inset, 0 -1px 0 #ccc inset;
|
||||
-webkit-box-shadow: 1px 8px 6px -4px #555, 0 1px 0 #ccc inset, 0 -1px 0 #ccc inset;
|
||||
-moz-box-shadow: 0 9px 4px -4px #555, 0 1px 0 #ccc inset, 0 -1px 0 #ccc inset, -1px 0 0 #ccc inset;
|
||||
-box-shadow: 0 9px 4px -4px #555, 0 1px 0 #ccc inset, 0 -1px 0 #ccc inset, -1px 0 0 #ccc inset;
|
||||
}
|
||||
118
InvenTree/InvenTree/static/bootstrap-table/extensions/reorder-rows/bootstrap-table-reorder-rows.js
vendored
Normal file
118
InvenTree/InvenTree/static/bootstrap-table/extensions/reorder-rows/bootstrap-table-reorder-rows.js
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
/**
|
||||
* @author: Dennis Hernández
|
||||
* @webSite: http://djhvscf.github.io/Blog
|
||||
* @version: v1.0.1
|
||||
*/
|
||||
|
||||
(function ($) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var isSearch = false;
|
||||
|
||||
var rowAttr = function (row, index) {
|
||||
return {
|
||||
id: 'customId_' + index
|
||||
};
|
||||
};
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
reorderableRows: false,
|
||||
onDragStyle: null,
|
||||
onDropStyle: null,
|
||||
onDragClass: "reorder_rows_onDragClass",
|
||||
dragHandle: null,
|
||||
useRowAttrFunc: false,
|
||||
onReorderRowsDrag: function (table, row) {
|
||||
return false;
|
||||
},
|
||||
onReorderRowsDrop: function (table, row) {
|
||||
return false;
|
||||
},
|
||||
onReorderRow: function (newData) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
$.extend($.fn.bootstrapTable.Constructor.EVENTS, {
|
||||
'reorder-row.bs.table': 'onReorderRow'
|
||||
});
|
||||
|
||||
var BootstrapTable = $.fn.bootstrapTable.Constructor,
|
||||
_init = BootstrapTable.prototype.init,
|
||||
_initSearch = BootstrapTable.prototype.initSearch;
|
||||
|
||||
BootstrapTable.prototype.init = function () {
|
||||
|
||||
if (!this.options.reorderableRows) {
|
||||
_init.apply(this, Array.prototype.slice.apply(arguments));
|
||||
return;
|
||||
}
|
||||
|
||||
var that = this;
|
||||
if (this.options.useRowAttrFunc) {
|
||||
this.options.rowAttributes = rowAttr;
|
||||
}
|
||||
|
||||
var onPostBody = this.options.onPostBody;
|
||||
this.options.onPostBody = function () {
|
||||
setTimeout(function () {
|
||||
that.makeRowsReorderable();
|
||||
onPostBody.apply();
|
||||
}, 1);
|
||||
};
|
||||
|
||||
_init.apply(this, Array.prototype.slice.apply(arguments));
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.initSearch = function () {
|
||||
_initSearch.apply(this, Array.prototype.slice.apply(arguments));
|
||||
|
||||
if (!this.options.reorderableRows) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Known issue after search if you reorder the rows the data is not display properly
|
||||
//isSearch = true;
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.makeRowsReorderable = function () {
|
||||
if (this.options.cardView) {
|
||||
return;
|
||||
}
|
||||
|
||||
var that = this;
|
||||
this.$el.tableDnD({
|
||||
onDragStyle: that.options.onDragStyle,
|
||||
onDropStyle: that.options.onDropStyle,
|
||||
onDragClass: that.options.onDragClass,
|
||||
onDrop: that.onDrop,
|
||||
onDragStart: that.options.onReorderRowsDrag,
|
||||
dragHandle: that.options.dragHandle
|
||||
});
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.onDrop = function (table, droppedRow) {
|
||||
var tableBs = $(table),
|
||||
tableBsData = tableBs.data('bootstrap.table'),
|
||||
tableBsOptions = tableBs.data('bootstrap.table').options,
|
||||
row = null,
|
||||
newData = [];
|
||||
|
||||
for (var i = 0; i < table.tBodies[0].rows.length; i++) {
|
||||
row = $(table.tBodies[0].rows[i]);
|
||||
newData.push(tableBsOptions.data[row.data('index')]);
|
||||
row.data('index', i).attr('data-index', i);
|
||||
}
|
||||
|
||||
tableBsOptions.data = tableBsOptions.data.slice(0, tableBsData.pageFrom - 1)
|
||||
.concat(newData)
|
||||
.concat(tableBsOptions.data.slice(tableBsData.pageTo));
|
||||
|
||||
//Call the user defined function
|
||||
tableBsOptions.onReorderRowsDrop.apply(table, [table, droppedRow]);
|
||||
|
||||
//Call the event reorder-row
|
||||
tableBsData.trigger('reorder-row', newData);
|
||||
};
|
||||
})(jQuery);
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Reorder Rows",
|
||||
"version": "1.0.0",
|
||||
"description": "Plugin to support the reordering rows feature.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/reorder-rows",
|
||||
"example": "http://issues.wenzhixin.net.cn/bootstrap-table/#extensions/reorder-rows.html",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-reorder-rows",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/reorder-rows"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "djhvscf",
|
||||
"image": "https://avatars1.githubusercontent.com/u/4496763"
|
||||
}
|
||||
}
|
||||
72
InvenTree/InvenTree/static/bootstrap-table/extensions/resizable/bootstrap-table-resizable.js
vendored
Normal file
72
InvenTree/InvenTree/static/bootstrap-table/extensions/resizable/bootstrap-table-resizable.js
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* @author: Dennis Hernández
|
||||
* @webSite: http://djhvscf.github.io/Blog
|
||||
* @version: v2.0.0
|
||||
*/
|
||||
|
||||
(function($) {
|
||||
"use strict";
|
||||
|
||||
var initResizable = function(that) {
|
||||
if (that.options.resizable && !that.options.cardView && !isInit(that)) {
|
||||
that.$el.resizableColumns();
|
||||
}
|
||||
};
|
||||
|
||||
var reInitResizable = function(that) {
|
||||
destroy(that);
|
||||
initResizable(that);
|
||||
};
|
||||
|
||||
var destroy = function(that) {
|
||||
if (isInit(that)) {
|
||||
that.$el.data("resizableColumns").destroy();
|
||||
}
|
||||
};
|
||||
|
||||
var isInit = function(that) {
|
||||
return that.$el.data("resizableColumns") !== undefined;
|
||||
};
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
resizable: false
|
||||
});
|
||||
|
||||
var BootstrapTable = $.fn.bootstrapTable.Constructor,
|
||||
_initBody = BootstrapTable.prototype.initBody,
|
||||
_toggleView = BootstrapTable.prototype.toggleView,
|
||||
_resetView = BootstrapTable.prototype.resetView;
|
||||
|
||||
BootstrapTable.prototype.initBody = function() {
|
||||
var that = this;
|
||||
_initBody.apply(this, Array.prototype.slice.apply(arguments));
|
||||
|
||||
that.$el
|
||||
.off("column-switch.bs.table, page-change.bs.table")
|
||||
.on("column-switch.bs.table, page-change.bs.table", function() {
|
||||
reInitResizable(that);
|
||||
});
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.toggleView = function() {
|
||||
_toggleView.apply(this, Array.prototype.slice.apply(arguments));
|
||||
|
||||
if (this.options.resizable && this.options.cardView) {
|
||||
//Destroy the plugin
|
||||
destroy(this);
|
||||
}
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.resetView = function() {
|
||||
var that = this;
|
||||
|
||||
_resetView.apply(this, Array.prototype.slice.apply(arguments));
|
||||
|
||||
if (this.options.resizable) {
|
||||
// because in fitHeader function, we use setTimeout(func, 100);
|
||||
setTimeout(function() {
|
||||
initResizable(that);
|
||||
}, 100);
|
||||
}
|
||||
};
|
||||
})(jQuery);
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Resizable",
|
||||
"version": "1.1.0",
|
||||
"description": "Plugin to support the resizable feature.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/resizable",
|
||||
"example": "http://issues.wenzhixin.net.cn/bootstrap-table/#extensions/resizable.html",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-resizable",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/resizable"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "djhvscf",
|
||||
"image": "https://avatars1.githubusercontent.com/u/4496763"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,332 @@
|
||||
/**
|
||||
* @author: Jewway
|
||||
* @version: v1.1.1
|
||||
*/
|
||||
|
||||
! function ($) {
|
||||
'use strict';
|
||||
|
||||
function getCurrentHeader(that) {
|
||||
var header = that.$header;
|
||||
if (that.options.height) {
|
||||
header = that.$tableHeader;
|
||||
}
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
function initFilterValues(that) {
|
||||
if (!$.isEmptyObject(that.filterColumnsPartial)) {
|
||||
var $header = getCurrentHeader(that);
|
||||
|
||||
$.each(that.columns, function (idx, column) {
|
||||
var value = that.filterColumnsPartial[column.field];
|
||||
|
||||
if (column.filter) {
|
||||
if (column.filter.setFilterValue) {
|
||||
var $filter = $header.find('[data-field=' + column.field + '] .filter');
|
||||
column.filter.setFilterValue($filter, column.field, value);
|
||||
} else {
|
||||
var $ele = $header.find('[data-filter-field=' + column.field + ']');
|
||||
switch (column.filter.type) {
|
||||
case 'input':
|
||||
$ele.val(value);
|
||||
case 'select':
|
||||
$ele.val(value).trigger('change');
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function createFilter(that, header) {
|
||||
var enableFilter = false,
|
||||
isVisible,
|
||||
html,
|
||||
timeoutId = 0;
|
||||
|
||||
$.each(that.columns, function (i, column) {
|
||||
isVisible = 'hidden';
|
||||
html = null;
|
||||
|
||||
if (!column.visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!column.filter) {
|
||||
html = $('<div class="no-filter"></div>');
|
||||
} else {
|
||||
var filterClass = column.filter.class ? ' ' + column.filter.class : '';
|
||||
html = $('<div style="margin: 0px 2px 2px 2px;" class="filter' + filterClass + '">');
|
||||
|
||||
if (column.searchable) {
|
||||
enableFilter = true;
|
||||
isVisible = 'visible'
|
||||
}
|
||||
|
||||
if (column.filter.template) {
|
||||
html.append(column.filter.template(that, column, isVisible));
|
||||
} else {
|
||||
var $filter = $(that.options.filterTemplate[column.filter.type.toLowerCase()](that, column, isVisible));
|
||||
|
||||
switch (column.filter.type) {
|
||||
case 'input':
|
||||
var cpLock = true;
|
||||
$filter.off('compositionstart').on('compositionstart', function (event) {
|
||||
cpLock = false;
|
||||
});
|
||||
|
||||
$filter.off('compositionend').on('compositionend', function (event) {
|
||||
cpLock = true;
|
||||
var $input = $(this);
|
||||
clearTimeout(timeoutId);
|
||||
timeoutId = setTimeout(function () {
|
||||
that.onColumnSearch(event, column.field, $input.val());
|
||||
}, that.options.searchTimeOut);
|
||||
});
|
||||
|
||||
$filter.off('keyup').on('keyup', function (event) {
|
||||
if (cpLock) {
|
||||
var $input = $(this);
|
||||
clearTimeout(timeoutId);
|
||||
timeoutId = setTimeout(function () {
|
||||
that.onColumnSearch(event, column.field, $input.val());
|
||||
}, that.options.searchTimeOut);
|
||||
}
|
||||
});
|
||||
|
||||
$filter.off('mouseup').on('mouseup', function (event) {
|
||||
var $input = $(this),
|
||||
oldValue = $input.val();
|
||||
|
||||
if (oldValue === "") {
|
||||
return;
|
||||
}
|
||||
|
||||
setTimeout(function () {
|
||||
var newValue = $input.val();
|
||||
|
||||
if (newValue === "") {
|
||||
clearTimeout(timeoutId);
|
||||
timeoutId = setTimeout(function () {
|
||||
that.onColumnSearch(event, column.field, newValue);
|
||||
}, that.options.searchTimeOut);
|
||||
}
|
||||
}, 1);
|
||||
});
|
||||
break;
|
||||
case 'select':
|
||||
$filter.on('select2:select', function (event) {
|
||||
that.onColumnSearch(event, column.field, $(this).val());
|
||||
});
|
||||
|
||||
$filter.on("select2:unselecting", function (event) {
|
||||
var $select2 = $(this);
|
||||
event.preventDefault();
|
||||
$select2.val(null).trigger('change');
|
||||
that.searchText = undefined;
|
||||
that.onColumnSearch(event, column.field, $select2.val());
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
html.append($filter);
|
||||
}
|
||||
}
|
||||
|
||||
$.each(header.children().children(), function (i, tr) {
|
||||
tr = $(tr);
|
||||
if (tr.data('field') === column.field) {
|
||||
tr.find('.fht-cell').append(html);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (!enableFilter) {
|
||||
header.find('.filter').hide();
|
||||
}
|
||||
}
|
||||
|
||||
function initSelect2(that) {
|
||||
var $header = getCurrentHeader(that);
|
||||
|
||||
$.each(that.columns, function (idx, column) {
|
||||
if (column.filter && column.filter.type === 'select') {
|
||||
var $selectEle = $header.find('select[data-filter-field="' + column.field + '"]');
|
||||
|
||||
if ($selectEle.length > 0 && !$selectEle.data().select2) {
|
||||
var select2Opts = {
|
||||
placeholder: "",
|
||||
allowClear: true,
|
||||
data: column.filter.data,
|
||||
dropdownParent: that.$el.closest(".bootstrap-table")
|
||||
};
|
||||
|
||||
$selectEle.select2(select2Opts);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
filter: false,
|
||||
filterValues: {},
|
||||
filterTemplate: {
|
||||
input: function (instance, column, isVisible) {
|
||||
return '<input type="text" class="form-control" data-filter-field="' + column.field + '" style="width: 100%; visibility:' + isVisible + '">';
|
||||
},
|
||||
select: function (instance, column, isVisible) {
|
||||
return '<select data-filter-field="' + column.field + '" style="width: 100%; visibility:' + isVisible + '"></select>';
|
||||
}
|
||||
},
|
||||
onColumnSearch: function (field, text) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
$.extend($.fn.bootstrapTable.COLUMN_DEFAULTS, {
|
||||
filter: undefined
|
||||
});
|
||||
|
||||
$.extend($.fn.bootstrapTable.Constructor.EVENTS, {
|
||||
'column-search.bs.table': 'onColumnSearch'
|
||||
});
|
||||
|
||||
var BootstrapTable = $.fn.bootstrapTable.Constructor,
|
||||
_init = BootstrapTable.prototype.init,
|
||||
_initHeader = BootstrapTable.prototype.initHeader,
|
||||
_initSearch = BootstrapTable.prototype.initSearch;
|
||||
|
||||
BootstrapTable.prototype.init = function () {
|
||||
//Make sure that the filtercontrol option is set
|
||||
if (this.options.filter) {
|
||||
var that = this;
|
||||
|
||||
if (that.options.filterTemplate) {
|
||||
that.options.filterTemplate = $.extend({}, $.fn.bootstrapTable.defaults.filterTemplate, that.options.filterTemplate);
|
||||
}
|
||||
|
||||
if (!$.isEmptyObject(that.options.filterValues)) {
|
||||
that.filterColumnsPartial = that.options.filterValues;
|
||||
that.options.filterValues = {};
|
||||
}
|
||||
|
||||
this.$el.on('reset-view.bs.table', function () {
|
||||
//Create controls on $tableHeader if the height is set
|
||||
if (!that.options.height) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Avoid recreate the controls
|
||||
if (that.$tableHeader.find('select').length > 0 || that.$tableHeader.find('input').length > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
createFilter(that, that.$tableHeader);
|
||||
}).on('post-header.bs.table', function () {
|
||||
var timeoutId = 0;
|
||||
|
||||
initSelect2(that);
|
||||
clearTimeout(timeoutId);
|
||||
timeoutId = setTimeout(function () {
|
||||
initFilterValues(that);
|
||||
}, that.options.searchTimeOut - 1000);
|
||||
}).on('column-switch.bs.table', function (field, checked) {
|
||||
initFilterValues(that);
|
||||
});
|
||||
}
|
||||
|
||||
_init.apply(this, Array.prototype.slice.apply(arguments));
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.initHeader = function () {
|
||||
_initHeader.apply(this, Array.prototype.slice.apply(arguments));
|
||||
if (this.options.filter) {
|
||||
createFilter(this, this.$header);
|
||||
}
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.initSearch = function () {
|
||||
var that = this,
|
||||
filterValues = that.filterColumnsPartial;
|
||||
|
||||
// Filter for client
|
||||
if (that.options.sidePagination === 'client') {
|
||||
this.data = $.grep(this.data, function (row, idx) {
|
||||
for (var field in filterValues) {
|
||||
var column = that.columns[that.fieldsColumnsIndex[field]],
|
||||
filterValue = filterValues[field].toLowerCase(),
|
||||
rowValue = row[field];
|
||||
|
||||
rowValue = $.fn.bootstrapTable.utils.calculateObjectValue(
|
||||
that.header,
|
||||
that.header.formatters[$.inArray(field, that.header.fields)], [rowValue, row, idx], rowValue);
|
||||
|
||||
if (column.filterStrictSearch) {
|
||||
if (!($.inArray(field, that.header.fields) !== -1 &&
|
||||
(typeof rowValue === 'string' || typeof rowValue === 'number') &&
|
||||
rowValue.toString().toLowerCase() === filterValue.toString().toLowerCase())) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!($.inArray(field, that.header.fields) !== -1 &&
|
||||
(typeof rowValue === 'string' || typeof rowValue === 'number') &&
|
||||
(rowValue + '').toLowerCase().indexOf(filterValue) !== -1)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
_initSearch.apply(this, Array.prototype.slice.apply(arguments));
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.onColumnSearch = function (event, field, value) {
|
||||
if ($.isEmptyObject(this.filterColumnsPartial)) {
|
||||
this.filterColumnsPartial = {};
|
||||
}
|
||||
|
||||
if (value) {
|
||||
this.filterColumnsPartial[field] = value;
|
||||
} else {
|
||||
delete this.filterColumnsPartial[field];
|
||||
}
|
||||
|
||||
this.options.pageNumber = 1;
|
||||
this.onSearch(event);
|
||||
this.trigger('column-search', field, value);
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.setSelect2Data = function (field, data) {
|
||||
var that = this,
|
||||
$header = getCurrentHeader(that),
|
||||
$selectEle = $header.find('select[data-filter-field=\"' + field + '\"]');
|
||||
$selectEle.empty();
|
||||
$selectEle.select2({
|
||||
data: data,
|
||||
placeholder: "",
|
||||
allowClear: true,
|
||||
dropdownParent: that.$el.closest(".bootstrap-table")
|
||||
});
|
||||
|
||||
$.each(this.columns, function (idx, column) {
|
||||
if (column.field === field) {
|
||||
column.filter.data = data;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.setFilterValues = function (values) {
|
||||
this.filterColumnsPartial = values;
|
||||
};
|
||||
|
||||
$.fn.bootstrapTable.methods.push('setSelect2Data');
|
||||
$.fn.bootstrapTable.methods.push('setFilterValues');
|
||||
|
||||
}(jQuery);
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Select2 Filter",
|
||||
"version": "1.1.0",
|
||||
"description": "Plugin to add select2 filter on the top of the columns in order to filter the data.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/select2-filter",
|
||||
"example": "http://issues.wenzhixin.net.cn/bootstrap-table/#extensions/select2-filter.html",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-select2-filter",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/select2-filter"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "Jewway",
|
||||
"image": "https://avatars0.githubusercontent.com/u/3501899"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* @author vincent loh <vincent.ml@gmail.com>
|
||||
* @update zhixin wen <wenzhixin2010@gmail.com>
|
||||
*/
|
||||
|
||||
.fix-sticky {
|
||||
position: fixed !important;
|
||||
overflow: hidden;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.fix-sticky table thead {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.fix-sticky table thead.thead-light {
|
||||
background: #e9ecef;
|
||||
}
|
||||
|
||||
.fix-sticky table thead.thead-light {
|
||||
background: #212529;
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* @author vincent loh <vincent.ml@gmail.com>
|
||||
* @update J Manuel Corona <jmcg92@gmail.com>
|
||||
* @update zhixin wen <wenzhixin2010@gmail.com>
|
||||
*/
|
||||
|
||||
($ => {
|
||||
const Utils = $.fn.bootstrapTable.utils
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
stickyHeader: false,
|
||||
stickyHeaderOffsetY: 0
|
||||
})
|
||||
|
||||
const hiddenClass = Utils.bootstrapVersion === 4 ? 'd-none' : 'hidden'
|
||||
|
||||
$.BootstrapTable = class extends $.BootstrapTable {
|
||||
initHeader (...args) {
|
||||
super.initHeader(...args)
|
||||
|
||||
if (!this.options.stickyHeader) {
|
||||
return
|
||||
}
|
||||
|
||||
this.$el.before('<div class="sticky-header-container"></div>')
|
||||
this.$el.before('<div class="sticky_anchor_begin"></div>')
|
||||
this.$el.after('<div class="sticky_anchor_end"></div>')
|
||||
this.$header.addClass('sticky-header')
|
||||
|
||||
// clone header just once, to be used as sticky header
|
||||
// deep clone header, using source header affects tbody>td width
|
||||
this.$stickyContainer = this.$tableBody.find('.sticky-header-container')
|
||||
this.$stickyBegin = this.$tableBody.find('.sticky_anchor_begin')
|
||||
this.$stickyEnd = this.$tableBody.find('.sticky_anchor_end')
|
||||
this.$stickyHeader = this.$header.clone(true, true)
|
||||
|
||||
// render sticky on window scroll or resize
|
||||
$(window).on('resize.sticky-header-table', () => this.renderStickyHeader())
|
||||
$(window).on('scroll.sticky-header-table', () => this.renderStickyHeader())
|
||||
this.$tableBody.off('scroll').on('scroll', () => this.matchPositionX())
|
||||
}
|
||||
|
||||
renderStickyHeader () {
|
||||
const top = $(window).scrollTop()
|
||||
// top anchor scroll position, minus header height
|
||||
const start = this.$stickyBegin.offset().top - this.options.stickyHeaderOffsetY
|
||||
// bottom anchor scroll position, minus header height, minus sticky height
|
||||
const end = this.$stickyEnd.offset().top - this.options.stickyHeaderOffsetY - this.$header.height()
|
||||
// show sticky when top anchor touches header, and when bottom anchor not exceeded
|
||||
if (top > start && top <= end) {
|
||||
// ensure clone and source column widths are the same
|
||||
this.$stickyHeader.find('tr:eq(0)').find('th').each((index, el) => {
|
||||
$(el).css('min-width', this.$header.find('tr:eq(0)').find('th').eq(index).css('width'))
|
||||
})
|
||||
// match bootstrap table style
|
||||
this.$stickyContainer.removeClass(hiddenClass).addClass('fix-sticky fixed-table-container')
|
||||
// stick it in position
|
||||
this.$stickyContainer.css('top', `${this.options.stickyHeaderOffsetY}px`)
|
||||
// create scrollable container for header
|
||||
this.$stickyTable = $('<table/>')
|
||||
this.$stickyTable.addClass(this.options.classes)
|
||||
// append cloned header to dom
|
||||
this.$stickyContainer.html(this.$stickyTable.append(this.$stickyHeader))
|
||||
// match clone and source header positions when left-right scroll
|
||||
this.matchPositionX()
|
||||
} else {
|
||||
this.$stickyContainer.removeClass('fix-sticky').addClass(hiddenClass)
|
||||
}
|
||||
}
|
||||
|
||||
matchPositionX () {
|
||||
this.$stickyContainer.scrollLeft(this.$tableBody.scrollLeft())
|
||||
}
|
||||
}
|
||||
|
||||
})(jQuery)
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Sticky Header",
|
||||
"version": "1.0.0",
|
||||
"description": "An extension which provides a sticky header for table columns when scrolling on a long page and / or table. Works for tables with many columns and narrow width with horizontal scrollbars too.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/sticky-header",
|
||||
"example": "http://issues.wenzhixin.net.cn/bootstrap-table/#extensions/sticky-header.html",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-sticky-header",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/sticky-header"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "vinzloh",
|
||||
"image": "https://avatars0.githubusercontent.com/u/5501845"
|
||||
}
|
||||
}
|
||||
228
InvenTree/InvenTree/static/bootstrap-table/extensions/toolbar/bootstrap-table-toolbar.js
vendored
Normal file
228
InvenTree/InvenTree/static/bootstrap-table/extensions/toolbar/bootstrap-table-toolbar.js
vendored
Normal file
@@ -0,0 +1,228 @@
|
||||
/**
|
||||
* @author: aperez <aperez@datadec.es>
|
||||
* @version: v2.0.0
|
||||
*
|
||||
* @update Dennis Hernández <http://djhvscf.github.io/Blog>
|
||||
* @update zhixin wen <wenzhixin2010@gmail.com>
|
||||
*/
|
||||
|
||||
($ => {
|
||||
const Utils = $.fn.bootstrapTable.utils
|
||||
|
||||
const bootstrap = {
|
||||
3: {
|
||||
icons: {
|
||||
advancedSearchIcon: 'glyphicon-chevron-down'
|
||||
},
|
||||
html: {
|
||||
modalHeader: `
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h4 class="modal-title">%s</h4>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
},
|
||||
4: {
|
||||
icons: {
|
||||
advancedSearchIcon: 'fa-chevron-down'
|
||||
},
|
||||
html: {
|
||||
modalHeader: `
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">%s</h4>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
}
|
||||
}[Utils.bootstrapVersion]
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
advancedSearch: false,
|
||||
idForm: 'advancedSearch',
|
||||
actionForm: '',
|
||||
idTable: undefined,
|
||||
onColumnAdvancedSearch (field, text) {
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults.icons, {
|
||||
advancedSearchIcon: bootstrap.icons.advancedSearchIcon
|
||||
})
|
||||
|
||||
$.extend($.fn.bootstrapTable.Constructor.EVENTS, {
|
||||
'column-advanced-search.bs.table': 'onColumnAdvancedSearch'
|
||||
})
|
||||
|
||||
$.extend($.fn.bootstrapTable.locales, {
|
||||
formatAdvancedSearch () {
|
||||
return 'Advanced search'
|
||||
},
|
||||
formatAdvancedCloseButton () {
|
||||
return 'Close'
|
||||
}
|
||||
})
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, $.fn.bootstrapTable.locales)
|
||||
|
||||
$.BootstrapTable = class extends $.BootstrapTable {
|
||||
initToolbar () {
|
||||
const o = this.options
|
||||
|
||||
this.showToolbar = this.showToolbar ||
|
||||
(o.search &&
|
||||
o.advancedSearch &&
|
||||
o.idTable)
|
||||
|
||||
super.initToolbar()
|
||||
|
||||
if (!o.search || !o.advancedSearch || !o.idTable) {
|
||||
return
|
||||
}
|
||||
|
||||
this.$toolbar.find('>.btn-group').append(`
|
||||
<button class="btn btn-default${Utils.sprintf(' btn-%s', o.buttonsClass)}${Utils.sprintf(' btn-%s', o.iconSize)}"
|
||||
type="button"
|
||||
name="advancedSearch"
|
||||
aria-label="advanced search"
|
||||
title="${o.formatAdvancedSearch()}">
|
||||
<i class="${o.iconsPrefix} ${o.icons.advancedSearchIcon}"></i>
|
||||
</button>
|
||||
`)
|
||||
|
||||
this.$toolbar.find('button[name="advancedSearch"]').off('click').on('click', () => this.showAvdSearch())
|
||||
}
|
||||
|
||||
showAvdSearch () {
|
||||
const o = this.options
|
||||
|
||||
if (!$(`#avdSearchModal_${o.idTable}`).hasClass('modal')) {
|
||||
$('body').append(`
|
||||
<div id="avdSearchModal_${o.idTable}" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-xs">
|
||||
<div class="modal-content">
|
||||
${Utils.sprintf(bootstrap.html.modalHeader, o.formatAdvancedSearch())}
|
||||
<div class="modal-body modal-body-custom">
|
||||
<div class="container-fluid" id="avdSearchModalContent_${o.idTable}"
|
||||
style="padding-right: 0px; padding-left: 0px;" >
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" id="btnCloseAvd_${o.idTable}" class="btn btn-${o.buttonsClass}">
|
||||
${o.formatAdvancedCloseButton()}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`)
|
||||
|
||||
let timeoutId = 0
|
||||
|
||||
$(`#avdSearchModalContent_${o.idTable}`).append(this.createFormAvd().join(''))
|
||||
|
||||
$(`#${o.idForm}`).off('keyup blur', 'input').on('keyup blur', 'input', e => {
|
||||
if (o.sidePagination === 'server') {
|
||||
this.onColumnAdvancedSearch(e)
|
||||
} else {
|
||||
clearTimeout(timeoutId)
|
||||
timeoutId = setTimeout(() => {
|
||||
this.onColumnAdvancedSearch(e)
|
||||
}, o.searchTimeOut)
|
||||
}
|
||||
})
|
||||
|
||||
$(`#btnCloseAvd_${o.idTable}`).click(() => {
|
||||
$(`#avdSearchModal_${o.idTable}`).modal('hide')
|
||||
if (o.sidePagination === 'server') {
|
||||
this.options.pageNumber = 1
|
||||
this.updatePagination()
|
||||
this.trigger('column-advanced-search', this.filterColumnsPartial)
|
||||
}
|
||||
})
|
||||
|
||||
$(`#avdSearchModal_${o.idTable}`).modal()
|
||||
} else {
|
||||
$(`#avdSearchModal_${o.idTable}`).modal()
|
||||
}
|
||||
}
|
||||
|
||||
createFormAvd () {
|
||||
const o = this.options
|
||||
const html = [`<form class="form-horizontal" id="${o.idForm}" action="${o.actionForm}">`]
|
||||
|
||||
for (const column of this.columns) {
|
||||
if (!column.checkbox && column.visible && column.searchable) {
|
||||
html.push(`
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-4 control-label">${column.title}</label>
|
||||
<div class="col-sm-6">
|
||||
<input type="text" class="form-control input-md" name="${column.field}" placeholder="${column.title}" id="${column.field}">
|
||||
</div>
|
||||
</div>
|
||||
`)
|
||||
}
|
||||
}
|
||||
|
||||
html.push('</form>')
|
||||
|
||||
return html
|
||||
}
|
||||
|
||||
initSearch () {
|
||||
super.initSearch()
|
||||
|
||||
if (!this.options.advancedSearch || this.options.sidePagination === 'server') {
|
||||
return
|
||||
}
|
||||
|
||||
const fp = $.isEmptyObject(this.filterColumnsPartial) ? null : this.filterColumnsPartial
|
||||
|
||||
this.data = fp ? $.grep(this.data, (item, i) => {
|
||||
for (const [key, v] of Object.entries(fp)) {
|
||||
const fval = v.toLowerCase()
|
||||
let value = item[key]
|
||||
const index = this.header.fields.indexOf(key)
|
||||
value = Utils.calculateObjectValue(this.header,
|
||||
this.header.formatters[index], [value, item, i], value)
|
||||
|
||||
if (
|
||||
!(index !== -1 &&
|
||||
(typeof value === 'string' || typeof value === 'number') &&
|
||||
(`${value}`).toLowerCase().includes(fval))
|
||||
) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}) : this.data
|
||||
}
|
||||
|
||||
onColumnAdvancedSearch (e) {
|
||||
const text = $.trim($(e.currentTarget).val())
|
||||
const $field = $(e.currentTarget)[0].id
|
||||
|
||||
if ($.isEmptyObject(this.filterColumnsPartial)) {
|
||||
this.filterColumnsPartial = {}
|
||||
}
|
||||
if (text) {
|
||||
this.filterColumnsPartial[$field] = text
|
||||
} else {
|
||||
delete this.filterColumnsPartial[$field]
|
||||
}
|
||||
|
||||
if (this.options.sidePagination !== 'server') {
|
||||
this.options.pageNumber = 1
|
||||
this.onSearch(e)
|
||||
this.updatePagination()
|
||||
this.trigger('column-advanced-search', $field, text)
|
||||
}
|
||||
}
|
||||
}
|
||||
})(jQuery)
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Toolbar",
|
||||
"version": "2.0.0",
|
||||
"description": "Plugin to support the advanced search.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/toolbar",
|
||||
"example": "http://issues.wenzhixin.net.cn/bootstrap-table/#extensions/toolbar.html",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-toolbar",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/toolbar"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "djhvscf",
|
||||
"image": "https://avatars1.githubusercontent.com/u/4496763"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
.table:not(.table-condensed)>tbody>tr>td.treenode{padding-top:0;padding-bottom:0;border-bottom:solid #fff 1px}.table:not(.table-condensed)>tbody>tr:last-child>td.treenode{border-bottom:none}.treenode .text{float:left;display:block;padding-top:6px;padding-bottom:6px}.treenode .vertical,.treenode .vertical.last{float:left;display:block;width:1px;border-left:dashed silver 1px;height:38px;margin-left:8px}.treenode .vertical.last{height:15px}.treenode .space,.treenode .node{float:left;display:block;width:15px;height:5px;margin-top:15px}.treenode .node{border-top:dashed silver 1px}
|
||||
130
InvenTree/InvenTree/static/bootstrap-table/extensions/tree-column/bootstrap-table-tree-column.js
vendored
Normal file
130
InvenTree/InvenTree/static/bootstrap-table/extensions/tree-column/bootstrap-table-tree-column.js
vendored
Normal file
@@ -0,0 +1,130 @@
|
||||
/**
|
||||
* @author: KingYang
|
||||
* @webSite: https://github.com/kingyang
|
||||
* @version: v1.0.0
|
||||
*/
|
||||
|
||||
! function ($) {
|
||||
|
||||
'use strict';
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
treeShowField: null,
|
||||
idField: 'id',
|
||||
parentIdField: 'pid',
|
||||
treeVerticalcls: 'vertical',
|
||||
treeVerticalLastcls: 'vertical last',
|
||||
treeSpacecls: 'space',
|
||||
treeNodecls: 'node',
|
||||
treeCellcls: 'treenode',
|
||||
treeTextcls: 'text',
|
||||
onTreeFormatter: function (row) {
|
||||
var that = this,
|
||||
options = that.options,
|
||||
level = row._level || 0,
|
||||
plevel = row._parent && row._parent._level || 0,
|
||||
paddings = [];
|
||||
for (var i = 0; i < plevel; i++) {
|
||||
paddings.push('<i class="' + options.treeVerticalcls + '"></i>');
|
||||
paddings.push('<i class="' + options.treeSpacecls + '"></i>');
|
||||
}
|
||||
|
||||
for (var i = plevel; i < level; i++) {
|
||||
if (row._last && i === (level - 1)) {
|
||||
paddings.push('<i class="' + options.treeVerticalLastcls + '"></i>');
|
||||
} else {
|
||||
paddings.push('<i class="' + options.treeVerticalcls + '"></i>');
|
||||
}
|
||||
paddings.push('<i class="' + options.treeNodecls + '"></i>');
|
||||
}
|
||||
return paddings.join('');
|
||||
}, onGetNodes: function (row, data) {
|
||||
var that = this;
|
||||
var nodes = [];
|
||||
$.each(data, function (i, item) {
|
||||
if (row[that.options.idField] === item[that.options.parentIdField]) {
|
||||
nodes.push(item);
|
||||
}
|
||||
});
|
||||
return nodes;
|
||||
},
|
||||
onCheckLeaf: function (row, data) {
|
||||
if (row.isLeaf !== undefined) {
|
||||
return row.isLeaf;
|
||||
}
|
||||
return !row._nodes || !row._nodes.length;
|
||||
}, onCheckRoot: function (row, data) {
|
||||
var that = this;
|
||||
return !row[that.options.parentIdField];
|
||||
}
|
||||
});
|
||||
|
||||
var BootstrapTable = $.fn.bootstrapTable.Constructor,
|
||||
_initRow = BootstrapTable.prototype.initRow,
|
||||
_initHeader = BootstrapTable.prototype.initHeader;
|
||||
|
||||
BootstrapTable.prototype.initHeader = function () {
|
||||
var that = this;
|
||||
_initHeader.apply(that, Array.prototype.slice.apply(arguments));
|
||||
var treeShowField = that.options.treeShowField;
|
||||
if (treeShowField) {
|
||||
$.each(this.header.fields, function (i, field) {
|
||||
if (treeShowField === field) {
|
||||
that.treeEnable = true;
|
||||
var _formatter = that.header.formatters[i];
|
||||
var _class = [that.options.treeCellcls];
|
||||
if (that.header.classes[i]) {
|
||||
_class.push(that.header.classes[i].split('"')[1] || '');
|
||||
}
|
||||
that.header.classes[i] = ' class="' + _class.join(' ') + '"';
|
||||
that.header.formatters[i] = function (value, row, index) {
|
||||
var colTree = [that.options.onTreeFormatter.apply(that, [row])];
|
||||
colTree.push('<span class="' + that.options.treeTextcls + '">');
|
||||
if (_formatter) {
|
||||
colTree.push(_formatter.apply(this, Array.prototype.slice.apply(arguments)));
|
||||
} else {
|
||||
colTree.push(value);
|
||||
}
|
||||
colTree.push('</span>');
|
||||
return colTree.join('');
|
||||
};
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var initNode = function (item, idx, data, parentDom) {
|
||||
var that = this;
|
||||
var nodes = that.options.onGetNodes.apply(that, [item, data]);
|
||||
item._nodes = nodes;
|
||||
parentDom.append(_initRow.apply(that, [item, idx, data, parentDom]));
|
||||
var len = nodes.length - 1;
|
||||
for (var i = 0; i <= len; i++) {
|
||||
var node = nodes[i];
|
||||
node._level = item._level + 1;
|
||||
node._parent = item;
|
||||
if (i === len)
|
||||
node._last = 1;
|
||||
initNode.apply(that, [node, $.inArray(node, data), data, parentDom]);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
BootstrapTable.prototype.initRow = function (item, idx, data, parentDom) {
|
||||
var that = this;
|
||||
if (that.treeEnable) {
|
||||
if (that.options.onCheckRoot.apply(that, [item, data])) {
|
||||
if (item._level === undefined) {
|
||||
item._level = 0;
|
||||
}
|
||||
initNode.apply(that, [item, idx, data, parentDom]);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
return _initRow.apply(that, Array.prototype.slice.apply(arguments));
|
||||
};
|
||||
|
||||
} (jQuery);
|
||||
@@ -0,0 +1,43 @@
|
||||
.table:not(.table-condensed) > tbody > tr > td.treenode {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
border-bottom: solid #fff 1px;
|
||||
}
|
||||
|
||||
.table:not(.table-condensed) > tbody > tr:last-child > td.treenode {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.treenode {
|
||||
.text {
|
||||
float: left;
|
||||
display: block;
|
||||
padding-top: 6px;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
.vertical,
|
||||
.vertical.last {
|
||||
float: left;
|
||||
display: block;
|
||||
width: 1px;
|
||||
border-left: dashed silver 1px;
|
||||
height: 38px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
.vertical.last {
|
||||
height: 15px;
|
||||
}
|
||||
|
||||
.space,
|
||||
.node {
|
||||
float: left;
|
||||
display: block;
|
||||
width: 15px;
|
||||
height: 5px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.node {
|
||||
border-top: dashed silver 1px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Tree column",
|
||||
"version": "1.0.0",
|
||||
"description": "Plugin to support display tree data column.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/tree-column",
|
||||
"example": "http://issues.wenzhixin.net.cn/bootstrap-table/#extensions/tree-column.html",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-reorder-rows",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/tree-column"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "KingYang",
|
||||
"image": "https://avatars3.githubusercontent.com/u/1540211"
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 346 B |
111
InvenTree/InvenTree/static/bootstrap-table/extensions/treegrid/bootstrap-table-treegrid.js
vendored
Normal file
111
InvenTree/InvenTree/static/bootstrap-table/extensions/treegrid/bootstrap-table-treegrid.js
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
/**
|
||||
* @author: YL
|
||||
* @version: v1.0.0
|
||||
*/
|
||||
!function ($) {
|
||||
'use strict';
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
treeShowField: null,
|
||||
idField: 'id',
|
||||
parentIdField: 'pid',
|
||||
rootParentId: null,
|
||||
onGetNodes: function (row, data) {
|
||||
var that = this;
|
||||
var nodes = [];
|
||||
$.each(data, function (i, item) {
|
||||
if (row[that.options.idField] === item[that.options.parentIdField]) {
|
||||
nodes.push(item);
|
||||
}
|
||||
});
|
||||
return nodes;
|
||||
},
|
||||
onCheckRoot: function (row, data) {
|
||||
var that = this;
|
||||
return that.options.rootParentId === row[that.options.parentIdField] ||
|
||||
!row[that.options.parentIdField];
|
||||
}
|
||||
});
|
||||
|
||||
var BootstrapTable = $.fn.bootstrapTable.Constructor,
|
||||
_init = BootstrapTable.prototype.init,
|
||||
_initRow = BootstrapTable.prototype.initRow,
|
||||
_initHeader = BootstrapTable.prototype.initHeader,
|
||||
_rowStyle = null;
|
||||
|
||||
BootstrapTable.prototype.init = function () {
|
||||
_rowStyle = this.options.rowStyle;
|
||||
_init.apply(this, Array.prototype.slice.apply(arguments));
|
||||
};
|
||||
|
||||
// td
|
||||
BootstrapTable.prototype.initHeader = function () {
|
||||
var that = this;
|
||||
_initHeader.apply(that, Array.prototype.slice.apply(arguments));
|
||||
var treeShowField = that.options.treeShowField;
|
||||
if (treeShowField) {
|
||||
$.each(this.header.fields, function (i, field) {
|
||||
if (treeShowField === field) {
|
||||
that.treeEnable = true;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var initTr = function (item, idx, data, parentDom) {
|
||||
var that = this;
|
||||
var nodes = that.options.onGetNodes.apply(that, [item, data]);
|
||||
item._nodes = nodes;
|
||||
parentDom.append(_initRow.apply(that, [item, idx, data, parentDom]));
|
||||
|
||||
// init sub node
|
||||
var len = nodes.length - 1;
|
||||
for (var i = 0; i <= len; i++) {
|
||||
var node = nodes[i];
|
||||
node._level = item._level + 1;
|
||||
node._parent = item;
|
||||
if (i === len)
|
||||
node._last = 1;
|
||||
// jquery.treegrid.js
|
||||
that.options.rowStyle = function (item, idx) {
|
||||
var res = _rowStyle.apply(that, Array.prototype.slice.apply(arguments));
|
||||
var id = item[that.options.idField] ? item[that.options.idField] : 0;
|
||||
var pid = item[that.options.parentIdField] ? item[that.options.parentIdField] : 0;
|
||||
res.classes = [
|
||||
res.classes || '',
|
||||
'treegrid-' + id,
|
||||
'treegrid-parent-' + pid
|
||||
].join(' ');
|
||||
return res;
|
||||
};
|
||||
initTr.apply(that, [node, $.inArray(node, data), data, parentDom]);
|
||||
}
|
||||
};
|
||||
|
||||
// tr
|
||||
BootstrapTable.prototype.initRow = function (item, idx, data, parentDom) {
|
||||
var that = this;
|
||||
if (that.treeEnable) {
|
||||
// init root node
|
||||
if (that.options.onCheckRoot.apply(that, [item, data])) {
|
||||
if (item._level === undefined) {
|
||||
item._level = 0;
|
||||
}
|
||||
// jquery.treegrid.js
|
||||
that.options.rowStyle = function (item, idx) {
|
||||
var res = _rowStyle.apply(that, Array.prototype.slice.apply(arguments));
|
||||
var x = item[that.options.idField] ? item[that.options.idField] : 0;
|
||||
res.classes = [
|
||||
res.classes || '',
|
||||
'treegrid-' + x
|
||||
].join(' ');
|
||||
return res;
|
||||
};
|
||||
initTr.apply(that, [item, idx, data, parentDom]);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return _initRow.apply(that, Array.prototype.slice.apply(arguments));
|
||||
};
|
||||
}(jQuery);
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "treegrid",
|
||||
"version": "1.0.0",
|
||||
"description": "Plugin to support the jquery treegrid.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/treegrid",
|
||||
"example": "https://github.com/wenzhixin/bootstrap-table-examples/blob/master/extensions/treegrid.html",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-treegrid",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/treegrid"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "foreveryang321",
|
||||
"image": "https://avatars0.githubusercontent.com/u/5868190"
|
||||
}
|
||||
}
|
||||
2793
InvenTree/InvenTree/static/css/color-themes/dark-reader.css
Normal file
2793
InvenTree/InvenTree/static/css/color-themes/dark-reader.css
Normal file
File diff suppressed because it is too large
Load Diff
42
InvenTree/InvenTree/static/css/color-themes/darker.css
Normal file
42
InvenTree/InvenTree/static/css/color-themes/darker.css
Normal file
@@ -0,0 +1,42 @@
|
||||
/* Color Theme: "Darker" by Radek Hladik */
|
||||
|
||||
.navbar-nav > li {
|
||||
border-color: rgb(179, 179, 179);
|
||||
}
|
||||
|
||||
.navbar-nav > li > a {
|
||||
color:#0b2a62 !important;
|
||||
}
|
||||
|
||||
.navbar-nav > li > a:hover {
|
||||
color:#202020 !important;
|
||||
}
|
||||
|
||||
.navbar-nav > .open > a {
|
||||
color:#202020 !important;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
background-color: rgb(189, 189, 189);
|
||||
}
|
||||
|
||||
.table-condensed > tbody > tr > td {
|
||||
border-top: 1px solid #062152 !important ;
|
||||
}
|
||||
|
||||
.table-striped > tbody > tr > td {
|
||||
border-top: 1px solid #92b3f1 ;
|
||||
}
|
||||
|
||||
.table-bordered, .table-bordered > tbody > tr > td {
|
||||
border: 1px solid rgb(182,182,182);
|
||||
}
|
||||
|
||||
.table-bordered > thead > tr > th {
|
||||
border: 1px solid rgb(182, 182, 182);
|
||||
background-color: rgb(235, 235, 235);
|
||||
}
|
||||
|
||||
h3 {
|
||||
color:#06255d;
|
||||
}
|
||||
1
InvenTree/InvenTree/static/css/color-themes/default.css
Normal file
1
InvenTree/InvenTree/static/css/color-themes/default.css
Normal file
@@ -0,0 +1 @@
|
||||
/* Color Theme: "Default" */
|
||||
@@ -1,3 +1,72 @@
|
||||
:root {
|
||||
--primary-color: #335d88;
|
||||
--secondary-color: #b69c80;
|
||||
--highlight-color: #f5efe8;
|
||||
--basic-color: #333;
|
||||
|
||||
--label-red: #e35a57;
|
||||
--label-blue: #4194bd;
|
||||
--label-green: #50aa51;
|
||||
--label-grey: #aaa;
|
||||
--label-yellow: #fdc82a;
|
||||
}
|
||||
|
||||
.markdownx .row {
|
||||
margin: 5px;
|
||||
padding: 5px;
|
||||
border: 1px solid #cce;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.markdownx-editor {
|
||||
width: 100%;
|
||||
border: 1px solid #cce;
|
||||
border-radius: 3px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.panel-content {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.markdownx-preview {
|
||||
border: 1px solid #cce;
|
||||
border-radius: 3px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
/* Progress bars */
|
||||
|
||||
.progress {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
margin-bottom: 0px;
|
||||
background: #eeeef5;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
opacity: 60%;
|
||||
background: #2aa02a;
|
||||
}
|
||||
|
||||
.progress-bar-under {
|
||||
background: #eeaa33;
|
||||
}
|
||||
|
||||
.progress-bar-over {
|
||||
background: #337ab7;
|
||||
}
|
||||
|
||||
.progress-value {
|
||||
width: 100%;
|
||||
color: #333;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
font-size: 110%;
|
||||
}
|
||||
|
||||
.qr-code {
|
||||
max-width: 400px;
|
||||
max-height: 400px;
|
||||
@@ -13,14 +82,43 @@
|
||||
float: left;
|
||||
}
|
||||
|
||||
#navbar-barcode-li {
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.navbar-nav > li {
|
||||
border-left: 1px solid;
|
||||
border-right: 1px solid;
|
||||
border-color: #eee;
|
||||
}
|
||||
|
||||
.navbar-form {
|
||||
padding-right: 3px;
|
||||
}
|
||||
|
||||
#barcode-scan {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.icon-header {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.glyphicon {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
|
||||
.glyphicon-small {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.glyphicon-right {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.starred-part {
|
||||
color: #ffbb00;
|
||||
}
|
||||
@@ -33,24 +131,20 @@
|
||||
color: rgb(13, 245, 25);
|
||||
}
|
||||
|
||||
.glyphicon-ok {
|
||||
color: #5C5;
|
||||
.icon-red {
|
||||
color: #c55;
|
||||
}
|
||||
|
||||
.glyphicon-ok-circle {
|
||||
.icon-green {
|
||||
color: #43bb43;
|
||||
}
|
||||
|
||||
.icon-blue {
|
||||
color: #55c;
|
||||
}
|
||||
|
||||
.glyphicon-remove {
|
||||
color: #C55;
|
||||
}
|
||||
|
||||
.glyphicon-trash {
|
||||
color: #C55;
|
||||
}
|
||||
|
||||
.glyphicon-plus {
|
||||
color: #5C5;
|
||||
.icon-yellow {
|
||||
color: #CC2;
|
||||
}
|
||||
|
||||
/* CSS overrides for treeview */
|
||||
@@ -75,6 +169,64 @@
|
||||
.label-large {
|
||||
margin: 3px;
|
||||
font-size: 100%;
|
||||
border: 3px solid;
|
||||
border-radius: 15px;
|
||||
background: none;
|
||||
padding-right: 10px;
|
||||
padding-left: 10px;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.label-large-red {
|
||||
color: var(--label-red);
|
||||
border-color: var(--label-red);
|
||||
}
|
||||
|
||||
.label-red {
|
||||
background: var(--label-red);
|
||||
}
|
||||
|
||||
.label-large-blue {
|
||||
color: var(--label-blue);
|
||||
border-color: var(--label-blue);
|
||||
}
|
||||
|
||||
.label-blue {
|
||||
background: var(--label-blue);
|
||||
}
|
||||
|
||||
.label-large-green {
|
||||
color: var(--label-green);
|
||||
border-color: var(--label-green);
|
||||
}
|
||||
|
||||
.label-green {
|
||||
background: var(--label-green);
|
||||
}
|
||||
|
||||
.label-large-grey {
|
||||
color: var(--label-grey);
|
||||
border-color: var(--label-grey);
|
||||
}
|
||||
|
||||
.label-grey {
|
||||
background: var(--label-grey);
|
||||
}
|
||||
|
||||
.label-large-yellow {
|
||||
color: var(--label-yellow);
|
||||
border-color: var(--label-yellow);
|
||||
}
|
||||
|
||||
.label-yellow {
|
||||
background: var(--label-yellow);
|
||||
}
|
||||
|
||||
.label-right {
|
||||
float: right;
|
||||
margin-left: 3px;
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
/* Bootstrap table overrides */
|
||||
@@ -83,6 +235,15 @@
|
||||
background-color: #ebf4f4;
|
||||
}
|
||||
|
||||
.sub-table {
|
||||
margin-left: 45px;
|
||||
margin-right: 45px;
|
||||
}
|
||||
|
||||
.detail-icon .glyphicon {
|
||||
color: #98d296;
|
||||
}
|
||||
|
||||
/* Force select2 elements in modal forms to be full width */
|
||||
.select-full-width {
|
||||
width: 100%;
|
||||
@@ -98,15 +259,85 @@
|
||||
max-width: 250px;
|
||||
}
|
||||
|
||||
.bomrowvalid {
|
||||
.rowvalid {
|
||||
color: #050;
|
||||
}
|
||||
|
||||
.bomrowinvalid {
|
||||
.rowinvalid {
|
||||
color: #A00;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
padding-left: 1px;
|
||||
margin-left: 1px;
|
||||
}
|
||||
|
||||
.dropdown-buttons {
|
||||
display: inline-block
|
||||
}
|
||||
|
||||
.dropdown-menu .open{
|
||||
z-index: 1000;
|
||||
position: relative;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
/* Styles for table buttons and filtering */
|
||||
.button-toolbar .btn {
|
||||
margin-left: 1px;
|
||||
margin-right: 1px;
|
||||
}
|
||||
|
||||
.filter-list {
|
||||
display: inline-block;
|
||||
*display: inline;
|
||||
margin-bottom: 1px;
|
||||
margin-top: 1px;
|
||||
vertical-align: middle;
|
||||
margin: 1px;
|
||||
padding: 2px;
|
||||
background: #eee;
|
||||
border: 1px solid #eee;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.filter-list .close {
|
||||
cursor: pointer;
|
||||
right: 0%;
|
||||
padding-right: 2px;
|
||||
padding-left: 2px;
|
||||
transform: translate(0%, -25%);
|
||||
}
|
||||
|
||||
.filter-list .close:hover {background: #bbb;}
|
||||
|
||||
.filter-tag {
|
||||
display: inline-block;
|
||||
*display: inline;
|
||||
zoom: 1;
|
||||
padding-left: 3px;
|
||||
padding-right: 3px;
|
||||
padding-top: 2px;
|
||||
padding-bottom: 2px;
|
||||
border: 1px solid #aaa;
|
||||
border-radius: 3px;
|
||||
background: #eee;
|
||||
margin: 1px;
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.filter-input {
|
||||
display: inline-block;
|
||||
*display: inline;
|
||||
zoom: 1;
|
||||
}
|
||||
|
||||
.filter-tag:hover {
|
||||
background: #ddd;
|
||||
}
|
||||
|
||||
/* Part image icons with full-display on mouse hover */
|
||||
|
||||
.hover-img-thumb {
|
||||
@@ -136,13 +367,14 @@
|
||||
|
||||
/* dropzone class - for Drag-n-Drop file uploads */
|
||||
.dropzone {
|
||||
border: 1px solid #555;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
/*
|
||||
.dropzone * {
|
||||
pointer-events: none;
|
||||
}
|
||||
*/
|
||||
|
||||
.dragover {
|
||||
background-color: #55A;
|
||||
@@ -152,6 +384,24 @@
|
||||
-webkit-opacity: 10%;
|
||||
}
|
||||
|
||||
/* grid display for part images */
|
||||
|
||||
.table-img-grid tr {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.table-img-grid td {
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.table-img-grid .grid-image {
|
||||
|
||||
height: 128px;
|
||||
width: 128px;
|
||||
object-fit: contain;
|
||||
background: #eee;
|
||||
}
|
||||
|
||||
.btn-glyph {
|
||||
padding-left: 6px;
|
||||
@@ -160,6 +410,28 @@
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
|
||||
.action-button {
|
||||
font-size: 125%;
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.action-buttons .btn {
|
||||
font-size: 175%;
|
||||
align-content: center;
|
||||
vertical-align: middle;
|
||||
padding-left: 6px;
|
||||
padding-right: 6px;
|
||||
padding-top: 3px;
|
||||
padding-bottom: 2px;
|
||||
};
|
||||
|
||||
.panel-heading .badge {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.badge {
|
||||
float: right;
|
||||
background-color: #777;
|
||||
@@ -178,6 +450,22 @@
|
||||
margin: 2px;
|
||||
padding: 3px;
|
||||
object-fit: contain;
|
||||
border: 1px solid #aaa;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.part-thumb-container:hover .part-thumb-overlay {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.part-thumb-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
opacity: 0;
|
||||
transition: .25s ease;
|
||||
padding: 15px;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
@@ -191,10 +479,12 @@
|
||||
|
||||
.media {
|
||||
padding-top: 15px;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.media-body {
|
||||
padding-top: 10px;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.navigation {
|
||||
@@ -297,6 +587,12 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
input[type="submit"] {
|
||||
color: #333;
|
||||
background-color: #e6e6e6;
|
||||
border-color: #adadad;
|
||||
}
|
||||
|
||||
.modal textarea {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
34
InvenTree/InvenTree/static/fontawesome/LICENSE.txt
Normal file
34
InvenTree/InvenTree/static/fontawesome/LICENSE.txt
Normal file
@@ -0,0 +1,34 @@
|
||||
Font Awesome Free License
|
||||
-------------------------
|
||||
|
||||
Font Awesome Free is free, open source, and GPL friendly. You can use it for
|
||||
commercial projects, open source projects, or really almost whatever you want.
|
||||
Full Font Awesome Free license: https://fontawesome.com/license/free.
|
||||
|
||||
# Icons: CC BY 4.0 License (https://creativecommons.org/licenses/by/4.0/)
|
||||
In the Font Awesome Free download, the CC BY 4.0 license applies to all icons
|
||||
packaged as SVG and JS file types.
|
||||
|
||||
# Fonts: SIL OFL 1.1 License (https://scripts.sil.org/OFL)
|
||||
In the Font Awesome Free download, the SIL OFL license applies to all icons
|
||||
packaged as web and desktop font files.
|
||||
|
||||
# Code: MIT License (https://opensource.org/licenses/MIT)
|
||||
In the Font Awesome Free download, the MIT license applies to all non-font and
|
||||
non-icon files.
|
||||
|
||||
# Attribution
|
||||
Attribution is required by MIT, SIL OFL, and CC BY licenses. Downloaded Font
|
||||
Awesome Free files already contain embedded comments with sufficient
|
||||
attribution, so you shouldn't need to do anything additional when using these
|
||||
files normally.
|
||||
|
||||
We've kept attribution comments terse, so we ask that you do not actively work
|
||||
to remove them from files, especially code. They're a great way for folks to
|
||||
learn about Font Awesome.
|
||||
|
||||
# Brand Icons
|
||||
All brand icons are trademarks of their respective owners. The use of these
|
||||
trademarks does not indicate endorsement of the trademark holder by Font
|
||||
Awesome, nor vice versa. **Please do not use brand logos for any purpose except
|
||||
to represent the company, product, or service to which they refer.**
|
||||
4556
InvenTree/InvenTree/static/fontawesome/css/all.css
vendored
Normal file
4556
InvenTree/InvenTree/static/fontawesome/css/all.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
5
InvenTree/InvenTree/static/fontawesome/css/all.min.css
vendored
Normal file
5
InvenTree/InvenTree/static/fontawesome/css/all.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
15
InvenTree/InvenTree/static/fontawesome/css/brands.css
vendored
Normal file
15
InvenTree/InvenTree/static/fontawesome/css/brands.css
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
/*!
|
||||
* Font Awesome Free 5.13.0 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
*/
|
||||
@font-face {
|
||||
font-family: 'Font Awesome 5 Brands';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: block;
|
||||
src: url("../webfonts/fa-brands-400.eot");
|
||||
src: url("../webfonts/fa-brands-400.eot?#iefix") format("embedded-opentype"), url("../webfonts/fa-brands-400.woff2") format("woff2"), url("../webfonts/fa-brands-400.woff") format("woff"), url("../webfonts/fa-brands-400.ttf") format("truetype"), url("../webfonts/fa-brands-400.svg#fontawesome") format("svg"); }
|
||||
|
||||
.fab {
|
||||
font-family: 'Font Awesome 5 Brands';
|
||||
font-weight: 400; }
|
||||
5
InvenTree/InvenTree/static/fontawesome/css/brands.min.css
vendored
Normal file
5
InvenTree/InvenTree/static/fontawesome/css/brands.min.css
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
/*!
|
||||
* Font Awesome Free 5.13.0 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
*/
|
||||
@font-face{font-family:"Font Awesome 5 Brands";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-brands-400.eot);src:url(../webfonts/fa-brands-400.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.woff) format("woff"),url(../webfonts/fa-brands-400.ttf) format("truetype"),url(../webfonts/fa-brands-400.svg#fontawesome) format("svg")}.fab{font-family:"Font Awesome 5 Brands";font-weight:400}
|
||||
4522
InvenTree/InvenTree/static/fontawesome/css/fontawesome.css
vendored
Normal file
4522
InvenTree/InvenTree/static/fontawesome/css/fontawesome.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
5
InvenTree/InvenTree/static/fontawesome/css/fontawesome.min.css
vendored
Normal file
5
InvenTree/InvenTree/static/fontawesome/css/fontawesome.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
15
InvenTree/InvenTree/static/fontawesome/css/regular.css
vendored
Normal file
15
InvenTree/InvenTree/static/fontawesome/css/regular.css
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
/*!
|
||||
* Font Awesome Free 5.13.0 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
*/
|
||||
@font-face {
|
||||
font-family: 'Font Awesome 5 Free';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: block;
|
||||
src: url("../webfonts/fa-regular-400.eot");
|
||||
src: url("../webfonts/fa-regular-400.eot?#iefix") format("embedded-opentype"), url("../webfonts/fa-regular-400.woff2") format("woff2"), url("../webfonts/fa-regular-400.woff") format("woff"), url("../webfonts/fa-regular-400.ttf") format("truetype"), url("../webfonts/fa-regular-400.svg#fontawesome") format("svg"); }
|
||||
|
||||
.far {
|
||||
font-family: 'Font Awesome 5 Free';
|
||||
font-weight: 400; }
|
||||
5
InvenTree/InvenTree/static/fontawesome/css/regular.min.css
vendored
Normal file
5
InvenTree/InvenTree/static/fontawesome/css/regular.min.css
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
/*!
|
||||
* Font Awesome Free 5.13.0 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
*/
|
||||
@font-face{font-family:"Font Awesome 5 Free";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-regular-400.eot);src:url(../webfonts/fa-regular-400.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.woff) format("woff"),url(../webfonts/fa-regular-400.ttf) format("truetype"),url(../webfonts/fa-regular-400.svg#fontawesome) format("svg")}.far{font-family:"Font Awesome 5 Free";font-weight:400}
|
||||
16
InvenTree/InvenTree/static/fontawesome/css/solid.css
vendored
Normal file
16
InvenTree/InvenTree/static/fontawesome/css/solid.css
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
/*!
|
||||
* Font Awesome Free 5.13.0 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
*/
|
||||
@font-face {
|
||||
font-family: 'Font Awesome 5 Free';
|
||||
font-style: normal;
|
||||
font-weight: 900;
|
||||
font-display: block;
|
||||
src: url("../webfonts/fa-solid-900.eot");
|
||||
src: url("../webfonts/fa-solid-900.eot?#iefix") format("embedded-opentype"), url("../webfonts/fa-solid-900.woff2") format("woff2"), url("../webfonts/fa-solid-900.woff") format("woff"), url("../webfonts/fa-solid-900.ttf") format("truetype"), url("../webfonts/fa-solid-900.svg#fontawesome") format("svg"); }
|
||||
|
||||
.fa,
|
||||
.fas {
|
||||
font-family: 'Font Awesome 5 Free';
|
||||
font-weight: 900; }
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user