mirror of
https://github.com/inventree/InvenTree.git
synced 2025-12-22 06:40:41 -06:00
Compare commits
2430 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ac9eadd13 | ||
|
|
d426e376c4 | ||
|
|
2b1101e165 | ||
|
|
afd7199a69 | ||
|
|
745188082b | ||
|
|
e53c6e9975 | ||
|
|
cc2b672117 | ||
|
|
d11adf3b34 | ||
|
|
dffff89e9d | ||
|
|
9d12d43574 | ||
|
|
116ea65160 | ||
|
|
6df6a460e4 | ||
|
|
b95d6a4ab2 | ||
|
|
6037f1452a | ||
|
|
beeb94785d | ||
|
|
e02fb8c8fa | ||
|
|
ea4b713eed | ||
|
|
ca16e4f35d | ||
|
|
ba71ce941f | ||
|
|
5b549b8dea | ||
|
|
098b494047 | ||
|
|
e8bacbe45f | ||
|
|
644583f636 | ||
|
|
556d6455e8 | ||
|
|
a9f255be85 | ||
|
|
ef23ab1abc | ||
|
|
cdb3dd2aa5 | ||
|
|
0c053c6339 | ||
|
|
bfbcbe252b | ||
|
|
8cb3d6ab0a | ||
|
|
43a7359501 | ||
|
|
d2d842163d | ||
|
|
0f60482e09 | ||
|
|
1b73f56937 | ||
|
|
e75b9d04fe | ||
|
|
7a51e6cf78 | ||
|
|
25ada20a19 | ||
|
|
07ee27ad9b | ||
|
|
3822b60bb0 | ||
|
|
abe1018abe | ||
|
|
8caf6bad10 | ||
|
|
bf63005731 | ||
|
|
3f30421ba9 | ||
|
|
ef902fc313 | ||
|
|
bb3440a8a4 | ||
|
|
1eb2456e3d | ||
|
|
5b402b6bc0 | ||
|
|
43eba3f7ec | ||
|
|
40d75090a7 | ||
|
|
d692c18274 | ||
|
|
69708b842c | ||
|
|
afc33c59ea | ||
|
|
08cc866e74 | ||
|
|
98bd7dfa9a | ||
|
|
fcc35f2260 | ||
|
|
ba542dcbdb | ||
|
|
c8650ce34c | ||
|
|
8780b8435a | ||
|
|
34df19242c | ||
|
|
28c9c80f54 | ||
|
|
cda97829ab | ||
|
|
d2c9f759b1 | ||
|
|
baa352ca98 | ||
|
|
a444f21e64 | ||
|
|
6b48f0db43 | ||
|
|
7071ef5a5c | ||
|
|
7d30e75bc6 | ||
|
|
46f20593c5 | ||
|
|
f87b15e4ea | ||
|
|
b09e9c0781 | ||
|
|
fdca3d842d | ||
|
|
81cac0927d | ||
|
|
a416c56e5a | ||
|
|
a722057dab | ||
|
|
31a8c94d2f | ||
|
|
6cc0880b4a | ||
|
|
247c4bdb4b | ||
|
|
b222119653 | ||
|
|
e72aaf2e07 | ||
|
|
a349e77866 | ||
|
|
4431082440 | ||
|
|
e0db833038 | ||
|
|
39d9ecffec | ||
|
|
99578e6986 | ||
|
|
10e50cf5e4 | ||
|
|
998620b5f3 | ||
|
|
16692a2905 | ||
|
|
67b6123b70 | ||
|
|
e8fd336612 | ||
|
|
11099676ef | ||
|
|
4e9b9ee6fd | ||
|
|
9be2989971 | ||
|
|
a1cf893eb2 | ||
|
|
ba85ff63bf | ||
|
|
12821b80fb | ||
|
|
19143ed082 | ||
|
|
b22b82877d | ||
|
|
efc2290613 | ||
|
|
b43d6a2ad4 | ||
|
|
d6eae83809 | ||
|
|
89adac49f9 | ||
|
|
2fa7c8706b | ||
|
|
9385447761 | ||
|
|
a5c36e2ca8 | ||
|
|
ef31727ec9 | ||
|
|
30d95e1511 | ||
|
|
dbaa0fc300 | ||
|
|
2aa2f08658 | ||
|
|
b5e993872f | ||
|
|
d686fb5057 | ||
|
|
f458342e5f | ||
|
|
33d66182df | ||
|
|
af7a627230 | ||
|
|
978ea7cc0b | ||
|
|
3c5169c793 | ||
|
|
74704a7c1e | ||
|
|
4d91a34136 | ||
|
|
140c8b5395 | ||
|
|
62a7964dda | ||
|
|
a0e7d37a19 | ||
|
|
5aec487c12 | ||
|
|
bc36775270 | ||
|
|
98d291c2f8 | ||
|
|
59904ace1d | ||
|
|
264bd625d3 | ||
|
|
0de284f1aa | ||
|
|
6ebebe03d4 | ||
|
|
a025b7239d | ||
|
|
ddbf2a6313 | ||
|
|
cf0c43d899 | ||
|
|
6230fb3614 | ||
|
|
801b945438 | ||
|
|
3ccc500e8e | ||
|
|
b107c54eb2 | ||
|
|
cabac6614c | ||
|
|
e407b99d0d | ||
|
|
d811f3c48a | ||
|
|
93f0dbd4ee | ||
|
|
ad0b59bf11 | ||
|
|
b284fe7f2b | ||
|
|
75431f0ee4 | ||
|
|
e417ff2b4d | ||
|
|
29bb735dc4 | ||
|
|
5c8e65c285 | ||
|
|
f135f11564 | ||
|
|
c2b5d96186 | ||
|
|
bd9447d9aa | ||
|
|
34dbfe6d28 | ||
|
|
1d317b1ecb | ||
|
|
cbadb2a888 | ||
|
|
90bef69a59 | ||
|
|
79ddea50f5 | ||
|
|
5e9097b5e0 | ||
|
|
0e246a7fdf | ||
|
|
793e5b820e | ||
|
|
bc43d14ebf | ||
|
|
f798537c73 | ||
|
|
65791a2b9b | ||
|
|
43e03ed023 | ||
|
|
7d38507785 | ||
|
|
3449848682 | ||
|
|
97140b19ba | ||
|
|
27b8928a10 | ||
|
|
cb81d8b66a | ||
|
|
8fda2cf745 | ||
|
|
4ef6a6dc62 | ||
|
|
282ed0c637 | ||
|
|
c1a54ddffd | ||
|
|
62501ecb93 | ||
|
|
8c7621d4bc | ||
|
|
fc193c26d0 | ||
|
|
489a15704c | ||
|
|
ae15ce9d0a | ||
|
|
e8d73c78eb | ||
|
|
c61631a380 | ||
|
|
5e5bced0c7 | ||
|
|
4641123cd8 | ||
|
|
d61ae8532a | ||
|
|
7e8def15ed | ||
|
|
51a33e5dca | ||
|
|
14d24ebe07 | ||
|
|
220777611a | ||
|
|
5c9dd93ff1 | ||
|
|
03f7baf87f | ||
|
|
2d412e2be1 | ||
|
|
b90311acea | ||
|
|
69362ab960 | ||
|
|
71522fa608 | ||
|
|
82e6b87e1c | ||
|
|
914fe15921 | ||
|
|
0d462389b9 | ||
|
|
d17cb47e3c | ||
|
|
5c2012f873 | ||
|
|
0517fe5073 | ||
|
|
59e37b2526 | ||
|
|
ce28b84f34 | ||
|
|
f81c154578 | ||
|
|
436207b315 | ||
|
|
a5aa90cdf2 | ||
|
|
833ba8c472 | ||
|
|
526d81481b | ||
|
|
697a338700 | ||
|
|
6a7c722efc | ||
|
|
952da19600 | ||
|
|
0a566c062d | ||
|
|
ef7cc3f78d | ||
|
|
1b835a71df | ||
|
|
cbb286e46d | ||
|
|
873ac9accb | ||
|
|
f7079c3bc2 | ||
|
|
3ae4125df3 | ||
|
|
1d6bd2c6ca | ||
|
|
c392bba196 | ||
|
|
c517801c5f | ||
|
|
2277d225eb | ||
|
|
2b30df1a70 | ||
|
|
a7baad33ba | ||
|
|
72c7ceb553 | ||
|
|
2991ce9317 | ||
|
|
0dee4df8fb | ||
|
|
69e6006436 | ||
|
|
4952c95c33 | ||
|
|
fc32d99327 | ||
|
|
ef3ac43c4a | ||
|
|
8f4ae14f2d | ||
|
|
85bce24e30 | ||
|
|
aada0ca5af | ||
|
|
7d5571ba5b | ||
|
|
a70416abac | ||
|
|
0134597747 | ||
|
|
449b462bf2 | ||
|
|
4fd0d7d8b5 | ||
|
|
959914c78c | ||
|
|
fef5b7548e | ||
|
|
af7af395f8 | ||
|
|
3cfe358102 | ||
|
|
02132fa495 | ||
|
|
75e1442fce | ||
|
|
e8fd597f29 | ||
|
|
21e8ddd1e6 | ||
|
|
a8e6d0a89f | ||
|
|
4d73aab090 | ||
|
|
834d9ec9a1 | ||
|
|
9efde9de29 | ||
|
|
1dc2636e45 | ||
|
|
8d6b4a2fd3 | ||
|
|
be41741b1e | ||
|
|
76c938762c | ||
|
|
3ec57a7259 | ||
|
|
bb9fe98a7e | ||
|
|
aac835f634 | ||
|
|
d459947949 | ||
|
|
a2f9d721f3 | ||
|
|
1cb951bd0b | ||
|
|
df327d4e64 | ||
|
|
1316e6bf5b | ||
|
|
890ce9ef95 | ||
|
|
df335b683c | ||
|
|
c5778b6fb6 | ||
|
|
e92e5dfe8f | ||
|
|
e4b202edd8 | ||
|
|
b7bbc97218 | ||
|
|
9884fe5c5e | ||
|
|
88a7b3251d | ||
|
|
af1abb7129 | ||
|
|
59c0a50289 | ||
|
|
28fb1b5fab | ||
|
|
6e4cf7c092 | ||
|
|
80c88b4fcc | ||
|
|
4e2d3b7da2 | ||
|
|
e561b3a4fc | ||
|
|
eedd8059d8 | ||
|
|
a1b2347784 | ||
|
|
ba1862478c | ||
|
|
0a0a47a5e4 | ||
|
|
3054c637dd | ||
|
|
5ca03af3e2 | ||
|
|
49c954aa68 | ||
|
|
452d22579a | ||
|
|
b6135fda74 | ||
|
|
9eace09e0e | ||
|
|
6f3cbb4e14 | ||
|
|
6a88bdb37d | ||
|
|
01e27a0d59 | ||
|
|
663a0a6165 | ||
|
|
d1d243fb14 | ||
|
|
b6cd2c215a | ||
|
|
1368b06afa | ||
|
|
d91700fd39 | ||
|
|
af47b211fd | ||
|
|
79b63e6d30 | ||
|
|
a3c5ea1f2b | ||
|
|
21d5440f98 | ||
|
|
e133fff03e | ||
|
|
bdc7367e29 | ||
|
|
44e60a705e | ||
|
|
a0d1f95171 | ||
|
|
587bf26d94 | ||
|
|
2f78c7c036 | ||
|
|
d25a719724 | ||
|
|
7c7a67fcc6 | ||
|
|
f0fa092c66 | ||
|
|
80c7ee6dab | ||
|
|
446c744462 | ||
|
|
ab9a6bd3c4 | ||
|
|
59bb5d15c8 | ||
|
|
67bc86c159 | ||
|
|
f26f1c38a2 | ||
|
|
5666db6b7a | ||
|
|
75f31ecc63 | ||
|
|
47b0f40e97 | ||
|
|
76c86e7b2f | ||
|
|
38b6367453 | ||
|
|
b7203f0ebb | ||
|
|
5f6442ba6b | ||
|
|
b4277e09e8 | ||
|
|
c377fb7b0c | ||
|
|
bb72658e76 | ||
|
|
735a3d2eb2 | ||
|
|
7ac7e8f969 | ||
|
|
a8e3e6c8db | ||
|
|
e5b346e7fa | ||
|
|
1d6a049c5a | ||
|
|
a5b18640af | ||
|
|
e62873a650 | ||
|
|
ba915da22b | ||
|
|
33d6396a4e | ||
|
|
580e7599a0 | ||
|
|
9b086560cb | ||
|
|
d0fb69e67d | ||
|
|
1335c85de1 | ||
|
|
855098e30b | ||
|
|
e715ea5d79 | ||
|
|
213d6550d3 | ||
|
|
9a30108b75 | ||
|
|
9dc9c0fcb7 | ||
|
|
da02ab3eac | ||
|
|
7d7d5d24cc | ||
|
|
37dcf1c1cf | ||
|
|
692cee113c | ||
|
|
4d7c60a130 | ||
|
|
d1ce0f062e | ||
|
|
a0c95579b4 | ||
|
|
39b9dcfec9 | ||
|
|
1a930f7f80 | ||
|
|
6d4c81e68b | ||
|
|
07cda765f0 | ||
|
|
bc72cd612e | ||
|
|
0f9c4703cf | ||
|
|
b05504e1c4 | ||
|
|
fe3d4a9867 | ||
|
|
0aeeba808c | ||
|
|
6c7b648133 | ||
|
|
9fa37a9156 | ||
|
|
8e9c976e99 | ||
|
|
2a91bb0c87 | ||
|
|
bdc3a9ef02 | ||
|
|
2ad090c224 | ||
|
|
fe0d356675 | ||
|
|
11c6248b06 | ||
|
|
298e9cae65 | ||
|
|
2dfa4d1acd | ||
|
|
725a70327a | ||
|
|
b2a732197b | ||
|
|
2b57ffeb08 | ||
|
|
03276629c2 | ||
|
|
3ff76fbdab | ||
|
|
7560b7e167 | ||
|
|
08a8556fe7 | ||
|
|
13e924cc05 | ||
|
|
8e13a7b470 | ||
|
|
c34196538b | ||
|
|
b21c6f0b99 | ||
|
|
c6134b54ab | ||
|
|
000348f70f | ||
|
|
8bb4683bbe | ||
|
|
d38d9b21a9 | ||
|
|
1a48405491 | ||
|
|
bcae1d09a8 | ||
|
|
a8c6e79bc0 | ||
|
|
06d9f4f982 | ||
|
|
7df25df2af | ||
|
|
464d76a819 | ||
|
|
447f0b0ed7 | ||
|
|
0500036d0a | ||
|
|
261a9af4f5 | ||
|
|
04336dd039 | ||
|
|
a9310d4a39 | ||
|
|
a8704a05d6 | ||
|
|
597bf8be73 | ||
|
|
6ef4325eac | ||
|
|
1a07ae0936 | ||
|
|
bf3b5bdd7f | ||
|
|
17d23fa47c | ||
|
|
c8c50af54b | ||
|
|
51da26d21d | ||
|
|
802dd5174c | ||
|
|
f11348c965 | ||
|
|
a7d825158c | ||
|
|
297bfd776c | ||
|
|
ef032d406f | ||
|
|
980cb9522b | ||
|
|
55b7cd3d6a | ||
|
|
70cac17138 | ||
|
|
648595cf18 | ||
|
|
d609e881c2 | ||
|
|
1c168452a4 | ||
|
|
1ef5a2b481 | ||
|
|
33dfecfdef | ||
|
|
3aad5111b5 | ||
|
|
f99c83f69d | ||
|
|
5c6939429a | ||
|
|
1a7a460ba8 | ||
|
|
de1dfdcc38 | ||
|
|
2bdd1305ed | ||
|
|
c66ac2579e | ||
|
|
2d7461f609 | ||
|
|
c9b3c16c6f | ||
|
|
8dac6bb982 | ||
|
|
4104e7df8e | ||
|
|
2c38be2d13 | ||
|
|
e1fb7e5d98 | ||
|
|
9dae7c1566 | ||
|
|
47d38e1cca | ||
|
|
28a5ee32c4 | ||
|
|
95f62d529f | ||
|
|
a8f605c2e6 | ||
|
|
ea2f5009c8 | ||
|
|
a5e6ac2300 | ||
|
|
b150c9c6bc | ||
|
|
50a88e4826 | ||
|
|
6c68197e61 | ||
|
|
7068f70811 | ||
|
|
607cc90ce0 | ||
|
|
5f8f0232a9 | ||
|
|
56f05e2604 | ||
|
|
aacc7119bd | ||
|
|
083d7671d0 | ||
|
|
28333c1a21 | ||
|
|
e3231bbedb | ||
|
|
3ff0759bb9 | ||
|
|
b5d75d6e6a | ||
|
|
ce82579930 | ||
|
|
371ec582e1 | ||
|
|
3391db506a | ||
|
|
af9b88de11 | ||
|
|
73259c0bcb | ||
|
|
a0168515c3 | ||
|
|
20e8161038 | ||
|
|
11745ebd6c | ||
|
|
4768c9cbb3 | ||
|
|
6b104fbb8b | ||
|
|
4049c8e915 | ||
|
|
e55f4a2798 | ||
|
|
599220a931 | ||
|
|
0594ebaef7 | ||
|
|
24fd520ec3 | ||
|
|
4daf291619 | ||
|
|
6144d7e209 | ||
|
|
9fa718e58d | ||
|
|
ac2797c7a1 | ||
|
|
d8e7c2a932 | ||
|
|
20f6964b1f | ||
|
|
771efecaa2 | ||
|
|
2e842503e6 | ||
|
|
b738f8b143 | ||
|
|
aae1400929 | ||
|
|
5f9758e85f | ||
|
|
03e852f957 | ||
|
|
0bb8c0a1e3 | ||
|
|
f0777ead92 | ||
|
|
0f42916521 | ||
|
|
ee70e27f7d | ||
|
|
01ff562dcd | ||
|
|
1d4b826d03 | ||
|
|
d777549a1a | ||
|
|
3130b672b4 | ||
|
|
d4ac35b9aa | ||
|
|
80b70fd2df | ||
|
|
362437e75e | ||
|
|
f239c8f8c8 | ||
|
|
ae7fbd6112 | ||
|
|
47cbf3071d | ||
|
|
534f43872f | ||
|
|
fd79f1ea0e | ||
|
|
7879c7565e | ||
|
|
6e7224ee7c | ||
|
|
0988040172 | ||
|
|
cb3c86f87a | ||
|
|
265a29fa1a | ||
|
|
4765065eb0 | ||
|
|
563bfe9bf5 | ||
|
|
96ef5e1bde | ||
|
|
fe9749ba4f | ||
|
|
21315096d4 | ||
|
|
1738df9042 | ||
|
|
ec8d8e5a64 | ||
|
|
4a8170079e | ||
|
|
b7187c5e06 | ||
|
|
391eeb0e46 | ||
|
|
51d2d85c26 | ||
|
|
1532be9c1e | ||
|
|
643589b4a9 | ||
|
|
b4fa56fd96 | ||
|
|
bfdda847c4 | ||
|
|
a7444a9926 | ||
|
|
4430098e98 | ||
|
|
039a7badd1 | ||
|
|
6d5bdaadbd | ||
|
|
56765d3f5a | ||
|
|
6c667937c5 | ||
|
|
ebac06ebee | ||
|
|
fc89501a62 | ||
|
|
5567ad07fd | ||
|
|
a19cf1f27a | ||
|
|
4dff18e4a6 | ||
|
|
1fc2ef5f18 | ||
|
|
83582ae87f | ||
|
|
e4f2eecb3b | ||
|
|
978fd7c683 | ||
|
|
48c20c600a | ||
|
|
734436b02e | ||
|
|
9d9ef5fc9c | ||
|
|
9ac334ddd2 | ||
|
|
a6028f027a | ||
|
|
b17b8db25c | ||
|
|
eead52a5dd | ||
|
|
7286281a06 | ||
|
|
c4296ad4f1 | ||
|
|
c95f124578 | ||
|
|
75ab7b247b | ||
|
|
e1b70ff68f | ||
|
|
8149759852 | ||
|
|
826c471179 | ||
|
|
dadf4d4c13 | ||
|
|
d23444a86a | ||
|
|
ba2da17f1e | ||
|
|
5b3dd63b89 | ||
|
|
684db67733 | ||
|
|
0b76d1d036 | ||
|
|
15ea73a448 | ||
|
|
a35c4a5a95 | ||
|
|
7e4b84f016 | ||
|
|
2591d34260 | ||
|
|
d44092b209 | ||
|
|
8b7789cdb3 | ||
|
|
6c89a2aaeb | ||
|
|
324645b67c | ||
|
|
e401bb8e3c | ||
|
|
1c14c2237a | ||
|
|
9eba564ff6 | ||
|
|
142cea0cbb | ||
|
|
853a821497 | ||
|
|
dc626ed68b | ||
|
|
f560be1a9a | ||
|
|
ede7d6dbde | ||
|
|
5f6f722d6c | ||
|
|
f8da15287c | ||
|
|
fc99086c8f | ||
|
|
4e157fe956 | ||
|
|
279d5a00ce | ||
|
|
5a5a36083e | ||
|
|
56b287b2c8 | ||
|
|
72b5a105f8 | ||
|
|
0882528b82 | ||
|
|
32b46cdc2a | ||
|
|
de2b7e5daf | ||
|
|
6b702ef676 | ||
|
|
b1885138de | ||
|
|
1e844c642f | ||
|
|
2e1a5a85a3 | ||
|
|
3ea671986c | ||
|
|
083dac1300 | ||
|
|
1c503adced | ||
|
|
ef2c04baa8 | ||
|
|
897d9b4831 | ||
|
|
5988e847ce | ||
|
|
8d0845d92b | ||
|
|
ac03dab8de | ||
|
|
2b91f69c7d | ||
|
|
b936f67d87 | ||
|
|
152801f06f | ||
|
|
3f03adba72 | ||
|
|
13a07be728 | ||
|
|
6320384ecb | ||
|
|
43fab8a8b3 | ||
|
|
34b784d1e4 | ||
|
|
978b5f869d | ||
|
|
05613b9642 | ||
|
|
f5d0d54ded | ||
|
|
500da8099b | ||
|
|
b02c87ea50 | ||
|
|
f1b83f1c17 | ||
|
|
3a347fba21 | ||
|
|
2a563d7370 | ||
|
|
3e5d8d2b2d | ||
|
|
5310ce8465 | ||
|
|
34ff05d66e | ||
|
|
95fadf1300 | ||
|
|
3a702266e6 | ||
|
|
ad90adbc04 | ||
|
|
1caa341f8e | ||
|
|
e049ca1a85 | ||
|
|
87903f27ed | ||
|
|
c533f59405 | ||
|
|
cabbdbb5cf | ||
|
|
fb28204dfd | ||
|
|
2428e77969 | ||
|
|
1311e5fe58 | ||
|
|
5c5641d884 | ||
|
|
2d583d19c2 | ||
|
|
90cfb3496a | ||
|
|
a148cfe593 | ||
|
|
1d6d1121a7 | ||
|
|
f1a7ac3187 | ||
|
|
fda0bff14c | ||
|
|
8246e9c802 | ||
|
|
b3ac261746 | ||
|
|
60a8ef723b | ||
|
|
05ce17f8df | ||
|
|
a263d2fdcd | ||
|
|
551064b3a4 | ||
|
|
544b63cac5 | ||
|
|
4a7e9a2278 | ||
|
|
ed8be5225d | ||
|
|
091a9d9803 | ||
|
|
6dc5ef0cdc | ||
|
|
d8ada930c8 | ||
|
|
d06b4d7c9f | ||
|
|
c8ec55a5fe | ||
|
|
44010fe05b | ||
|
|
d0b7d91ca4 | ||
|
|
d272eec384 | ||
|
|
5e70d8df7e | ||
|
|
c63480c65b | ||
|
|
3ec2396ec1 | ||
|
|
22a5f921b8 | ||
|
|
170d55d64e | ||
|
|
4055a36db2 | ||
|
|
646fe40950 | ||
|
|
54d5d2899e | ||
|
|
43907fb129 | ||
|
|
c61eeca3e4 | ||
|
|
8bf281e153 | ||
|
|
06a3899325 | ||
|
|
b5e1d3f87a | ||
|
|
9ed787b4da | ||
|
|
4167e1fdb3 | ||
|
|
210d39c8b7 | ||
|
|
d8a0ab8879 | ||
|
|
9203f541be | ||
|
|
a4f6efc05d | ||
|
|
b38fde85f2 | ||
|
|
5e0d1fe25a | ||
|
|
7525bc2ead | ||
|
|
664dd0000c | ||
|
|
8f108d42d2 | ||
|
|
24ab48ef4c | ||
|
|
1a4eb3f870 | ||
|
|
815d4bf7eb | ||
|
|
ffe15763a7 | ||
|
|
6aaf178f0b | ||
|
|
720579dcd7 | ||
|
|
e978e1df52 | ||
|
|
5908890726 | ||
|
|
549f50ac3f | ||
|
|
9284837da8 | ||
|
|
7d95faa4f5 | ||
|
|
432ecc1d96 | ||
|
|
3a325399c6 | ||
|
|
f1e8afa314 | ||
|
|
73296eafcb | ||
|
|
4d96b385b1 | ||
|
|
b57a78dea4 | ||
|
|
e3f5e8fbb1 | ||
|
|
3e17bf3316 | ||
|
|
10758a9626 | ||
|
|
767ceed698 | ||
|
|
c63a09330f | ||
|
|
42c1210fba | ||
|
|
a3265ef9fd | ||
|
|
b7e7543be6 | ||
|
|
b45a11aa3d | ||
|
|
ea7b1b65d6 | ||
|
|
a71a51b848 | ||
|
|
fb7d9a7edf | ||
|
|
0752df26dc | ||
|
|
076d5c4f7f | ||
|
|
5e63ccc9f6 | ||
|
|
cd4cb12937 | ||
|
|
647182237e | ||
|
|
5db043ab4b | ||
|
|
f4f6253178 | ||
|
|
33c454ed5a | ||
|
|
6245d65ebc | ||
|
|
23ac83d2a8 | ||
|
|
ae20db0ec6 | ||
|
|
d37cdd8e50 | ||
|
|
fae516b38e | ||
|
|
1ca08f8bff | ||
|
|
5877b9616d | ||
|
|
9131edc43d | ||
|
|
1ef21700c0 | ||
|
|
f989d3b3ec | ||
|
|
46b889c572 | ||
|
|
de65e1631d | ||
|
|
39eddc7203 | ||
|
|
96277edcf1 | ||
|
|
8ae16a125e | ||
|
|
e02536071d | ||
|
|
fd6d630037 | ||
|
|
5793839a01 | ||
|
|
3bb247a135 | ||
|
|
7bc925d016 | ||
|
|
652c2dbcbe | ||
|
|
2e4613e702 | ||
|
|
2df0f03a9a | ||
|
|
ac79e131bc | ||
|
|
28460b3023 | ||
|
|
fdcef7b699 | ||
|
|
bc6f58cf26 | ||
|
|
4193d2e7fe | ||
|
|
85ac1bfb95 | ||
|
|
73a1765a11 | ||
|
|
d242e04e64 | ||
|
|
a2ee172058 | ||
|
|
5a6697866f | ||
|
|
406d7bcf80 | ||
|
|
7aa473712f | ||
|
|
226a91718b | ||
|
|
c6e61c20fe | ||
|
|
98d20bceeb | ||
|
|
e8a0095e50 | ||
|
|
a5639c380f | ||
|
|
06040f94ee | ||
|
|
92c1e3c1a5 | ||
|
|
c13cee2407 | ||
|
|
9b7a9a3ee0 | ||
|
|
b5d15aab08 | ||
|
|
5405ad566e | ||
|
|
0b7cf9e7f3 | ||
|
|
a8d47c54f9 | ||
|
|
934078a42c | ||
|
|
416cfb99da | ||
|
|
6b99d923d7 | ||
|
|
396b7dba73 | ||
|
|
957c538136 | ||
|
|
647e2cf64d | ||
|
|
ce1cce99bb | ||
|
|
634410294b | ||
|
|
2dc9109b11 | ||
|
|
dfa4d3f8ed | ||
|
|
d1df647dde | ||
|
|
529c4052ff | ||
|
|
7edd08035f | ||
|
|
3c175a6c8d | ||
|
|
83077514cb | ||
|
|
4d4f0e5cfc | ||
|
|
5a6cac43f5 | ||
|
|
0b26d68d0f | ||
|
|
34e4409e7f | ||
|
|
8579abb9c2 | ||
|
|
3d9223c2ee | ||
|
|
af6b30eaca | ||
|
|
dbee26aaad | ||
|
|
70a3b7f891 | ||
|
|
ac82640c6c | ||
|
|
3143242d13 | ||
|
|
eb98496a79 | ||
|
|
f1f31a1338 | ||
|
|
ca8472ac23 | ||
|
|
3d60bccae0 | ||
|
|
26ee3032f6 | ||
|
|
8b16304e84 | ||
|
|
b595f3b732 | ||
|
|
305920d1ac | ||
|
|
ec4bc357df | ||
|
|
01eee4d208 | ||
|
|
8499ced636 | ||
|
|
19a2326638 | ||
|
|
1ce2166843 | ||
|
|
5bcf8529ff | ||
|
|
e4ce19d22a | ||
|
|
095cfe9845 | ||
|
|
279b50d977 | ||
|
|
8b37229e4b | ||
|
|
b80e4302ba | ||
|
|
2325b1e4ba | ||
|
|
9abb211cdf | ||
|
|
1c97aaf87a | ||
|
|
606c62078f | ||
|
|
ab454e5ba4 | ||
|
|
88f73443ee | ||
|
|
77a2b6de8b | ||
|
|
b3e4efd96e | ||
|
|
e076470305 | ||
|
|
d691b15f4b | ||
|
|
11d31960c7 | ||
|
|
c740cce5e4 | ||
|
|
dc2c9aa662 | ||
|
|
d2e2e7511f | ||
|
|
fa21d66c41 | ||
|
|
23aee234f0 | ||
|
|
556ffa1099 | ||
|
|
8b2189daca | ||
|
|
e74bfb90a5 | ||
|
|
911b23ca24 | ||
|
|
d980da7247 | ||
|
|
c910307ce5 | ||
|
|
3f59ce3f93 | ||
|
|
16d720b62c | ||
|
|
8ee16d6f98 | ||
|
|
ba4c829b10 | ||
|
|
afadd51a14 | ||
|
|
66bdce3d04 | ||
|
|
4d49cb029f | ||
|
|
796e89c921 | ||
|
|
3e9c7cda21 | ||
|
|
e5960f6ce4 | ||
|
|
806a7f961d | ||
|
|
731c796254 | ||
|
|
dc41231fcc | ||
|
|
cc05220263 | ||
|
|
898c604b3b | ||
|
|
095ef51991 | ||
|
|
bce7eb1aad | ||
|
|
13cd8624b2 | ||
|
|
48e050d317 | ||
|
|
3ee7be1d58 | ||
|
|
c1595396c4 | ||
|
|
fe3a72c6cc | ||
|
|
26d113e8ad | ||
|
|
ee28b4eea5 | ||
|
|
7f3ce9b0b1 | ||
|
|
62734c4b72 | ||
|
|
3fe0886207 | ||
|
|
852da6d696 | ||
|
|
42a75a8238 | ||
|
|
46f459b4c7 | ||
|
|
b9291c6705 | ||
|
|
824ce6778f | ||
|
|
3c5968ef1a | ||
|
|
9c27680202 | ||
|
|
45c888e13d | ||
|
|
a686500df1 | ||
|
|
fd22e713ff | ||
|
|
f04977e7e1 | ||
|
|
b467c8a1ef | ||
|
|
b27f926310 | ||
|
|
fb09f53dc9 | ||
|
|
929411e49a | ||
|
|
31b699d521 | ||
|
|
cda52a58e3 | ||
|
|
c19c014f55 | ||
|
|
d5c0c12d78 | ||
|
|
c09b4980ad | ||
|
|
1ded9e1fc0 | ||
|
|
6c2eb959a6 | ||
|
|
2039100d3e | ||
|
|
6bc5fe2497 | ||
|
|
9e4cc73b1c | ||
|
|
16f1b4c784 | ||
|
|
bedda66949 | ||
|
|
d81aa5c051 | ||
|
|
c7403fd512 | ||
|
|
e6526288cd | ||
|
|
f12f8156bd | ||
|
|
7f3018ebf8 | ||
|
|
496232ed6d | ||
|
|
9d3d9a190b | ||
|
|
15e1c05791 | ||
|
|
b7d25a75c4 | ||
|
|
a71b5ef0a0 | ||
|
|
7356fc3dfc | ||
|
|
4763f3ea46 | ||
|
|
87d836617e | ||
|
|
4f648f8787 | ||
|
|
756f3ddb0f | ||
|
|
626d0266c8 | ||
|
|
56660d52f2 | ||
|
|
81864a6ab8 | ||
|
|
27656633df | ||
|
|
400f183597 | ||
|
|
a952dc38a3 | ||
|
|
c4cae02170 | ||
|
|
8ea2c627c4 | ||
|
|
064a142d76 | ||
|
|
4f1d087654 | ||
|
|
40d8a07acc | ||
|
|
d05a5978a0 | ||
|
|
6b48977e7b | ||
|
|
d1cce7df94 | ||
|
|
0e5f10c020 | ||
|
|
1f6cbd7408 | ||
|
|
18e19d7920 | ||
|
|
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 | ||
|
|
14217ff648 | ||
|
|
d6749dfb24 | ||
|
|
41d91a8f9b | ||
|
|
119cccc318 | ||
|
|
0a7ea27e9f | ||
|
|
420b16104c | ||
|
|
f64758eb03 | ||
|
|
d0ad3f0e37 | ||
|
|
5fde9777fb | ||
|
|
4b33b15dd2 | ||
|
|
2be99be4de | ||
|
|
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 |
@@ -1,8 +1,6 @@
|
|||||||
[run]
|
[run]
|
||||||
source = ./InvenTree
|
source = ./InvenTree
|
||||||
omit =
|
omit =
|
||||||
# Do not run coverage on migration files
|
|
||||||
*/migrations/*
|
|
||||||
InvenTree/manage.py
|
InvenTree/manage.py
|
||||||
InvenTree/setup.py
|
InvenTree/setup.py
|
||||||
InvenTree/InvenTree/middleware.py
|
InvenTree/InvenTree/middleware.py
|
||||||
|
|||||||
10
.gitignore
vendored
10
.gitignore
vendored
@@ -6,9 +6,14 @@ __pycache__/
|
|||||||
# Distribution / packaging
|
# Distribution / packaging
|
||||||
.Python
|
.Python
|
||||||
env/
|
env/
|
||||||
|
inventree-env/
|
||||||
./build/
|
./build/
|
||||||
develop-eggs/
|
develop-eggs/
|
||||||
dist/
|
dist/
|
||||||
|
bin/
|
||||||
|
lib64
|
||||||
|
pyvenv.cfg
|
||||||
|
share/
|
||||||
downloads/
|
downloads/
|
||||||
eggs/
|
eggs/
|
||||||
.eggs/
|
.eggs/
|
||||||
@@ -27,15 +32,12 @@ var/
|
|||||||
local_settings.py
|
local_settings.py
|
||||||
*.sqlite3
|
*.sqlite3
|
||||||
*.backup
|
*.backup
|
||||||
|
*.old
|
||||||
|
|
||||||
# Sphinx files
|
# Sphinx files
|
||||||
docs/_build
|
docs/_build
|
||||||
|
|
||||||
# Local static and media file storage (only when running in development mode)
|
# Local static and media file storage (only when running in development mode)
|
||||||
InvenTree/media
|
|
||||||
InvenTree/static
|
|
||||||
media
|
|
||||||
static
|
|
||||||
inventree_media
|
inventree_media
|
||||||
inventree_static
|
inventree_static
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
# .readthedocs.yml
|
|
||||||
# Read the Docs configuration file
|
|
||||||
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
|
||||||
|
|
||||||
version: 2
|
|
||||||
|
|
||||||
# Build documentation in the docs/ directory with Sphinx
|
|
||||||
sphinx:
|
|
||||||
configuration: docs/conf.py
|
|
||||||
|
|
||||||
formats: all
|
|
||||||
|
|
||||||
# Optionally set the version of Python and requirements required to build your docs
|
|
||||||
python:
|
|
||||||
version: 3.5
|
|
||||||
install:
|
|
||||||
- requirements: docs/requirements.txt
|
|
||||||
43
.travis.yml
43
.travis.yml
@@ -1,5 +1,9 @@
|
|||||||
dist: xenial
|
dist: xenial
|
||||||
|
|
||||||
|
services:
|
||||||
|
- mysql
|
||||||
|
- postgresql
|
||||||
|
|
||||||
language: python
|
language: python
|
||||||
python:
|
python:
|
||||||
- 3.6
|
- 3.6
|
||||||
@@ -7,18 +11,47 @@ python:
|
|||||||
|
|
||||||
addons:
|
addons:
|
||||||
apt-packages:
|
apt-packages:
|
||||||
-sqlite3
|
- sqlite3
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- make install
|
- sudo apt-get update
|
||||||
- make migrate
|
- 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 ..
|
- 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:
|
script:
|
||||||
- cd InvenTree && python3 manage.py makemigrations && cd ..
|
- cd InvenTree && python3 manage.py makemigrations && cd ..
|
||||||
- python3 ci/check_migration_files.py
|
- python3 ci/check_migration_files.py
|
||||||
- make coverage
|
# Run unit testing / code coverage tests
|
||||||
- make style
|
- invoke coverage
|
||||||
|
# Run unit test for SQL database backend
|
||||||
|
- cd InvenTree && python3 manage.py test --settings=InvenTree.ci_mysql && cd ..
|
||||||
|
# Run unit test for PostgreSQL database backend
|
||||||
|
- cd InvenTree && python3 manage.py test --settings=InvenTree.ci_postgresql && cd ..
|
||||||
|
- invoke translate
|
||||||
|
- invoke style
|
||||||
|
# Create an empty database and fill it with test data
|
||||||
|
- rm inventree_default_db.sqlite3
|
||||||
|
- invoke migrate
|
||||||
|
- invoke import-fixtures
|
||||||
|
# Export database records
|
||||||
|
- invoke export-records -f data.json
|
||||||
|
# Create a new empty database and import the saved data
|
||||||
|
- rm inventree_default_db.sqlite3
|
||||||
|
- invoke migrate
|
||||||
|
- invoke import-records -f data.json
|
||||||
|
# Run linting checks on migration files (django-migration-linter)
|
||||||
|
# Run subset of linting checks on *ALL* migration files
|
||||||
|
# Run strict migration file checks on *NEW* migrations (old ones are what they are)
|
||||||
|
- cd InvenTree && python manage.py lintmigrations 79ddea50f507e34195bad635008419daac0d7a5f -q ok ignore --no-cache && cd ..
|
||||||
|
|
||||||
after_success:
|
after_success:
|
||||||
- coveralls
|
- coveralls
|
||||||
@@ -6,7 +6,15 @@ No pushing to master! New featues must be submitted in a separate branch (one br
|
|||||||
|
|
||||||
## Include Migration Files
|
## 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 `make migrate` and commit the migration files before submitting the PR.
|
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
|
## Testing
|
||||||
|
|
||||||
@@ -14,9 +22,8 @@ Any new code should be covered by unit tests - a submitted PR may not be accepte
|
|||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
New features or updates to existing features should be accompanied by user 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-docs/
|
||||||
A PR with associated documentation should link to the matching PR at https://github.com/inventree/InvenTree.github.io
|
|
||||||
|
|
||||||
## Code Style
|
## 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.
|
||||||
|
|||||||
106
InvenTree/InvenTree/api.py
Normal file
106
InvenTree/InvenTree/api.py
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
"""
|
||||||
|
Main JSON interface views
|
||||||
|
"""
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
logger.info("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.
|
||||||
|
"""
|
||||||
|
|
||||||
|
permission_classes = [permissions.AllowAny]
|
||||||
|
|
||||||
|
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:
|
||||||
|
print('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:
|
||||||
|
print('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': '',
|
||||||
|
}
|
||||||
104
InvenTree/InvenTree/context.py
Normal file
104
InvenTree/InvenTree/context.py
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
"""
|
||||||
|
Provides extra global data to all templates.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from InvenTree.status_codes import SalesOrderStatus, PurchaseOrderStatus
|
||||||
|
from InvenTree.status_codes import BuildStatus, StockStatus
|
||||||
|
|
||||||
|
import InvenTree.status
|
||||||
|
|
||||||
|
from users.models import RuleSet
|
||||||
|
|
||||||
|
|
||||||
|
def health_status(request):
|
||||||
|
"""
|
||||||
|
Provide system health status information to the global context.
|
||||||
|
|
||||||
|
- Not required for AJAX requests
|
||||||
|
- Do not provide if it is already provided to the context
|
||||||
|
"""
|
||||||
|
|
||||||
|
if request.path.endswith('.js'):
|
||||||
|
# Do not provide to script requests
|
||||||
|
return {}
|
||||||
|
|
||||||
|
if hasattr(request, '_inventree_health_status'):
|
||||||
|
# Do not duplicate efforts
|
||||||
|
return {}
|
||||||
|
|
||||||
|
request._inventree_health_status = True
|
||||||
|
|
||||||
|
return {
|
||||||
|
"system_healthy": InvenTree.status.check_system_health(),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def status_codes(request):
|
||||||
|
"""
|
||||||
|
Provide status code enumerations.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if hasattr(request, '_inventree_status_codes'):
|
||||||
|
# Do not duplicate efforts
|
||||||
|
return {}
|
||||||
|
|
||||||
|
request._inventree_status_codes = True
|
||||||
|
|
||||||
|
return {
|
||||||
|
# Expose the StatusCode classes to the templates
|
||||||
|
'SalesOrderStatus': SalesOrderStatus,
|
||||||
|
'PurchaseOrderStatus': PurchaseOrderStatus,
|
||||||
|
'BuildStatus': BuildStatus,
|
||||||
|
'StockStatus': StockStatus,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def user_roles(request):
|
||||||
|
"""
|
||||||
|
Return a map of the current roles assigned to the user.
|
||||||
|
|
||||||
|
Roles are denoted by their simple names, and then the permission type.
|
||||||
|
|
||||||
|
Permissions can be access as follows:
|
||||||
|
|
||||||
|
- roles.part.view
|
||||||
|
- roles.build.delete
|
||||||
|
|
||||||
|
Each value will return a boolean True / False
|
||||||
|
"""
|
||||||
|
|
||||||
|
user = request.user
|
||||||
|
|
||||||
|
roles = {
|
||||||
|
}
|
||||||
|
|
||||||
|
if user.is_superuser:
|
||||||
|
for ruleset in RuleSet.RULESET_MODELS.keys():
|
||||||
|
roles[ruleset] = {
|
||||||
|
'view': True,
|
||||||
|
'add': True,
|
||||||
|
'change': True,
|
||||||
|
'delete': True,
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
for group in user.groups.all():
|
||||||
|
for rule in group.rule_sets.all():
|
||||||
|
|
||||||
|
# Ensure the role name is in the dict
|
||||||
|
if rule.name not in roles:
|
||||||
|
roles[rule.name] = {
|
||||||
|
'view': user.is_superuser,
|
||||||
|
'add': user.is_superuser,
|
||||||
|
'change': user.is_superuser,
|
||||||
|
'delete': user.is_superuser
|
||||||
|
}
|
||||||
|
|
||||||
|
# Roles are additive across groups
|
||||||
|
roles[rule.name]['view'] |= rule.can_view
|
||||||
|
roles[rule.name]['add'] |= rule.can_add
|
||||||
|
roles[rule.name]['change'] |= rule.can_change
|
||||||
|
roles[rule.name]['delete'] |= rule.can_delete
|
||||||
|
|
||||||
|
return {'roles': roles}
|
||||||
21
InvenTree/InvenTree/exchange.py
Normal file
21
InvenTree/InvenTree/exchange.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
from djmoney.contrib.exchange.backends.base import BaseExchangeBackend
|
||||||
|
|
||||||
|
|
||||||
|
class InvenTreeManualExchangeBackend(BaseExchangeBackend):
|
||||||
|
"""
|
||||||
|
Backend for manually updating currency exchange rates
|
||||||
|
|
||||||
|
See the documentation for django-money: https://github.com/django-money/django-money
|
||||||
|
|
||||||
|
Specifically: https://github.com/django-money/django-money/tree/master/djmoney/contrib/exchange/backends
|
||||||
|
"""
|
||||||
|
|
||||||
|
name = "inventree"
|
||||||
|
url = None
|
||||||
|
|
||||||
|
def get_rates(self, **kwargs):
|
||||||
|
"""
|
||||||
|
Do not get any rates...
|
||||||
|
"""
|
||||||
|
|
||||||
|
return {}
|
||||||
@@ -5,9 +5,16 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
from .validators import allowable_url_schemes
|
from .validators import allowable_url_schemes
|
||||||
|
|
||||||
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
from django.forms.fields import URLField as FormURLField
|
from django.forms.fields import URLField as FormURLField
|
||||||
from django.db import models as models
|
from django.db import models as models
|
||||||
from django.core import validators
|
from django.core import validators
|
||||||
|
from django import forms
|
||||||
|
|
||||||
|
from decimal import Decimal
|
||||||
|
|
||||||
|
import InvenTree.helpers
|
||||||
|
|
||||||
|
|
||||||
class InvenTreeURLFormField(FormURLField):
|
class InvenTreeURLFormField(FormURLField):
|
||||||
@@ -25,3 +32,73 @@ class InvenTreeURLField(models.URLField):
|
|||||||
return super().formfield(**{
|
return super().formfield(**{
|
||||||
'form_class': InvenTreeURLFormField
|
'form_class': InvenTreeURLFormField
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
class DatePickerFormField(forms.DateField):
|
||||||
|
"""
|
||||||
|
Custom date-picker field
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
|
||||||
|
help_text = kwargs.get('help_text', _('Enter date'))
|
||||||
|
required = kwargs.get('required', False)
|
||||||
|
initial = kwargs.get('initial', None)
|
||||||
|
|
||||||
|
widget = forms.DateInput(
|
||||||
|
attrs={
|
||||||
|
'type': 'date',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
forms.DateField.__init__(
|
||||||
|
self,
|
||||||
|
required=required,
|
||||||
|
initial=initial,
|
||||||
|
help_text=help_text,
|
||||||
|
widget=widget
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
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,19 +5,115 @@ Helper forms which subclass Django forms to provide additional functionality
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.utils.translation import ugettext as _
|
||||||
from django import forms
|
from django import forms
|
||||||
from crispy_forms.helper import FormHelper
|
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 django.contrib.auth.models import User
|
||||||
|
from common.models import ColorTheme
|
||||||
|
from part.models import PartCategory
|
||||||
|
|
||||||
|
|
||||||
class HelperForm(forms.ModelForm):
|
class HelperForm(forms.ModelForm):
|
||||||
""" Provides simple integration of crispy_forms extension. """
|
""" 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):
|
def __init__(self, *args, **kwargs):
|
||||||
super(forms.ModelForm, self).__init__(*args, **kwargs)
|
super(forms.ModelForm, self).__init__(*args, **kwargs)
|
||||||
self.helper = FormHelper()
|
self.helper = FormHelper()
|
||||||
|
|
||||||
self.helper.form_tag = False
|
self.helper.form_tag = False
|
||||||
|
self.helper.form_show_errors = True
|
||||||
|
|
||||||
|
"""
|
||||||
|
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 is_valid(self):
|
||||||
|
|
||||||
|
valid = super(HelperForm, self).is_valid()
|
||||||
|
|
||||||
|
return valid
|
||||||
|
|
||||||
|
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):
|
class DeleteForm(forms.Form):
|
||||||
@@ -27,7 +123,7 @@ class DeleteForm(forms.Form):
|
|||||||
confirm_delete = forms.BooleanField(
|
confirm_delete = forms.BooleanField(
|
||||||
required=False,
|
required=False,
|
||||||
initial=False,
|
initial=False,
|
||||||
help_text='Confirm item deletion'
|
help_text=_('Confirm item deletion')
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@@ -59,14 +155,14 @@ class SetPasswordForm(HelperForm):
|
|||||||
required=True,
|
required=True,
|
||||||
initial='',
|
initial='',
|
||||||
widget=forms.PasswordInput(attrs={'autocomplete': 'off'}),
|
widget=forms.PasswordInput(attrs={'autocomplete': 'off'}),
|
||||||
help_text='Enter new password')
|
help_text=_('Enter new password'))
|
||||||
|
|
||||||
confirm_password = forms.CharField(max_length=100,
|
confirm_password = forms.CharField(max_length=100,
|
||||||
min_length=8,
|
min_length=8,
|
||||||
required=True,
|
required=True,
|
||||||
initial='',
|
initial='',
|
||||||
widget=forms.PasswordInput(attrs={'autocomplete': 'off'}),
|
widget=forms.PasswordInput(attrs={'autocomplete': 'off'}),
|
||||||
help_text='Confirm new password')
|
help_text=_('Confirm new password'))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
@@ -74,3 +170,66 @@ class SetPasswordForm(HelperForm):
|
|||||||
'enter_password',
|
'enter_password',
|
||||||
'confirm_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',
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class SettingCategorySelectForm(forms.ModelForm):
|
||||||
|
""" Form for setting category settings """
|
||||||
|
|
||||||
|
category = forms.ModelChoiceField(queryset=PartCategory.objects.all())
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = PartCategory
|
||||||
|
fields = [
|
||||||
|
'category'
|
||||||
|
]
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(SettingCategorySelectForm, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
self.helper = FormHelper()
|
||||||
|
# Form rendering
|
||||||
|
self.helper.form_show_labels = False
|
||||||
|
self.helper.layout = Layout(
|
||||||
|
Div(
|
||||||
|
Div(Field('category'),
|
||||||
|
css_class='col-sm-6',
|
||||||
|
style='width: 70%;'),
|
||||||
|
Div(StrictButton(_('Select Category'), css_class='btn btn-primary', type='submit'),
|
||||||
|
css_class='col-sm-6',
|
||||||
|
style='width: 30%; padding-left: 0;'),
|
||||||
|
css_class='row',
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|||||||
@@ -8,11 +8,79 @@ import json
|
|||||||
import os.path
|
import os.path
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
|
from decimal import Decimal
|
||||||
|
|
||||||
from wsgiref.util import FileWrapper
|
from wsgiref.util import FileWrapper
|
||||||
from django.http import StreamingHttpResponse
|
from django.http import StreamingHttpResponse
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError, FieldError
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
|
from django.contrib.auth.models import Permission
|
||||||
|
|
||||||
|
import InvenTree.version
|
||||||
|
|
||||||
|
from common.models import InvenTreeSetting
|
||||||
|
from .settings import MEDIA_URL, STATIC_URL
|
||||||
|
|
||||||
|
|
||||||
|
def getSetting(key, backup_value=None):
|
||||||
|
"""
|
||||||
|
Shortcut for reading a setting value from the database
|
||||||
|
"""
|
||||||
|
|
||||||
|
return InvenTreeSetting.get_setting(key, backup_value=backup_value)
|
||||||
|
|
||||||
|
|
||||||
|
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):
|
def TestIfImage(img):
|
||||||
""" Test if an image file is indeed an image """
|
""" Test if an image file is indeed an image """
|
||||||
@@ -52,6 +120,133 @@ def str2bool(text, test=True):
|
|||||||
return str(text).lower() in ['0', 'n', 'no', 'none', 'f', 'false', 'off', ]
|
return str(text).lower() in ['0', 'n', 'no', 'none', 'f', 'false', 'off', ]
|
||||||
|
|
||||||
|
|
||||||
|
def is_bool(text):
|
||||||
|
"""
|
||||||
|
Determine if a string value 'looks' like a boolean.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if str2bool(text, True):
|
||||||
|
return True
|
||||||
|
elif str2bool(text, False):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
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='"'):
|
def WrapWithQuotes(text, quote='"'):
|
||||||
""" Wrap the supplied text with quotes
|
""" Wrap the supplied text with quotes
|
||||||
|
|
||||||
@@ -72,7 +267,7 @@ def WrapWithQuotes(text, quote='"'):
|
|||||||
return text
|
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.
|
""" Generate a string for a barcode. Adds some global InvenTree parameters.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -85,11 +280,20 @@ def MakeBarcode(object_type, object_id, object_url, data={}):
|
|||||||
json string of the supplied data plus some other data
|
json string of the supplied data plus some other data
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Add in some generic InvenTree data
|
brief = kwargs.get('brief', True)
|
||||||
data['type'] = object_type
|
|
||||||
data['id'] = object_id
|
data = {}
|
||||||
data['url'] = object_url
|
|
||||||
data['tool'] = 'InvenTree'
|
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)
|
return json.dumps(data, sort_keys=True)
|
||||||
|
|
||||||
@@ -133,7 +337,7 @@ def DownloadFile(data, filename, content_type='application/text'):
|
|||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
def ExtractSerialNumbers(serials, expected_quantity):
|
def extract_serial_numbers(serials, expected_quantity):
|
||||||
""" Attempt to extract serial numbers from an input string.
|
""" Attempt to extract serial numbers from an input string.
|
||||||
- Serial numbers must be integer values
|
- Serial numbers must be integer values
|
||||||
- Serial numbers must be positive
|
- Serial numbers must be positive
|
||||||
@@ -192,14 +396,10 @@ def ExtractSerialNumbers(serials, expected_quantity):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
else:
|
else:
|
||||||
try:
|
if group in numbers:
|
||||||
n = int(group)
|
errors.append(_("Duplicate serial: {g}".format(g=group)))
|
||||||
if n in numbers:
|
else:
|
||||||
errors.append(_("Duplicate serial: {n}".format(n=n)))
|
numbers.append(group)
|
||||||
else:
|
|
||||||
numbers.append(n)
|
|
||||||
except ValueError:
|
|
||||||
errors.append(_("Invalid group: {g}".format(g=group)))
|
|
||||||
|
|
||||||
if len(errors) > 0:
|
if len(errors) > 0:
|
||||||
raise ValidationError(errors)
|
raise ValidationError(errors)
|
||||||
@@ -212,3 +412,152 @@ def ExtractSerialNumbers(serials, expected_quantity):
|
|||||||
raise ValidationError([_("Number of unique serial number ({s}) must match quantity ({q})".format(s=len(numbers), q=expected_quantity))])
|
raise ValidationError([_("Number of unique serial number ({s}) must match quantity ({q})".format(s=len(numbers), q=expected_quantity))])
|
||||||
|
|
||||||
return numbers
|
return numbers
|
||||||
|
|
||||||
|
|
||||||
|
def validateFilterString(value, model=None):
|
||||||
|
"""
|
||||||
|
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
|
||||||
|
|
||||||
|
# If a model is provided, verify that the provided filters can be used against it
|
||||||
|
if model is not None:
|
||||||
|
try:
|
||||||
|
model.objects.filter(**results)
|
||||||
|
except FieldError as e:
|
||||||
|
raise ValidationError(
|
||||||
|
str(e),
|
||||||
|
)
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
def addUserPermission(user, permission):
|
||||||
|
"""
|
||||||
|
Shortcut function for adding a certain permission to a user.
|
||||||
|
"""
|
||||||
|
|
||||||
|
perm = Permission.objects.get(codename=permission)
|
||||||
|
user.user_permissions.add(perm)
|
||||||
|
|
||||||
|
|
||||||
|
def addUserPermissions(user, permissions):
|
||||||
|
"""
|
||||||
|
Shortcut function for adding multiple permissions to a user.
|
||||||
|
"""
|
||||||
|
|
||||||
|
for permission in permissions:
|
||||||
|
addUserPermission(user, permission)
|
||||||
|
|
||||||
|
|
||||||
|
def getMigrationFileNames(app):
|
||||||
|
"""
|
||||||
|
Return a list of all migration filenames for provided app
|
||||||
|
"""
|
||||||
|
|
||||||
|
local_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
migration_dir = os.path.join(local_dir, '..', app, 'migrations')
|
||||||
|
|
||||||
|
files = os.listdir(migration_dir)
|
||||||
|
|
||||||
|
# Regex pattern for migration files
|
||||||
|
pattern = r"^[\d]+_.*\.py$"
|
||||||
|
|
||||||
|
migration_files = []
|
||||||
|
|
||||||
|
for f in files:
|
||||||
|
if re.match(pattern, f):
|
||||||
|
migration_files.append(f)
|
||||||
|
|
||||||
|
return migration_files
|
||||||
|
|
||||||
|
|
||||||
|
def getOldestMigrationFile(app, exclude_extension=True, ignore_initial=True):
|
||||||
|
"""
|
||||||
|
Return the filename associated with the oldest migration
|
||||||
|
"""
|
||||||
|
|
||||||
|
oldest_num = -1
|
||||||
|
oldest_file = None
|
||||||
|
|
||||||
|
for f in getMigrationFileNames(app):
|
||||||
|
|
||||||
|
if ignore_initial and f.startswith('0001_initial'):
|
||||||
|
continue
|
||||||
|
|
||||||
|
num = int(f.split('_')[0])
|
||||||
|
|
||||||
|
if oldest_file is None or num < oldest_num:
|
||||||
|
oldest_num = num
|
||||||
|
oldest_file = f
|
||||||
|
|
||||||
|
if exclude_extension:
|
||||||
|
oldest_file = oldest_file.replace('.py', '')
|
||||||
|
|
||||||
|
return oldest_file
|
||||||
|
|
||||||
|
|
||||||
|
def getNewestMigrationFile(app, exclude_extension=True):
|
||||||
|
"""
|
||||||
|
Return the filename associated with the newest migration
|
||||||
|
"""
|
||||||
|
|
||||||
|
newest_file = None
|
||||||
|
newest_num = -1
|
||||||
|
|
||||||
|
for f in getMigrationFileNames(app):
|
||||||
|
num = int(f.split('_')[0])
|
||||||
|
|
||||||
|
if newest_file is None or num > newest_num:
|
||||||
|
newest_num = num
|
||||||
|
newest_file = f
|
||||||
|
|
||||||
|
if exclude_extension:
|
||||||
|
newest_file = newest_file.replace('.py', '')
|
||||||
|
|
||||||
|
return newest_file
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
from django.shortcuts import HttpResponseRedirect
|
from django.shortcuts import HttpResponseRedirect
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
from django.db import connection
|
from django.db import connection
|
||||||
|
from django.shortcuts import redirect
|
||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
import operator
|
import operator
|
||||||
|
|
||||||
|
from rest_framework.authtoken.models import Token
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@@ -20,10 +23,62 @@ class AuthRequiredMiddleware(object):
|
|||||||
|
|
||||||
response = self.get_response(request)
|
response = self.get_response(request)
|
||||||
|
|
||||||
# Redirect any unauthorized HTTP requests to the login page
|
|
||||||
if not request.user.is_authenticated:
|
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
|
||||||
|
|
||||||
|
# Allow static files to be accessed without auth
|
||||||
|
# Important for e.g. login page
|
||||||
|
if request.path_info.startswith('/static/'):
|
||||||
|
authorized = True
|
||||||
|
|
||||||
|
elif '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/']
|
||||||
|
|
||||||
|
# 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
|
# Code to be executed for each request/response after
|
||||||
# the view is called.
|
# the view is called.
|
||||||
@@ -38,7 +93,11 @@ class QueryCountMiddleware(object):
|
|||||||
status code of 200). It does not currently support
|
status code of 200). It does not currently support
|
||||||
multi-db setups.
|
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/
|
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):
|
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
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.contrib.auth.models import User
|
||||||
from django.contrib.contenttypes.models import ContentType
|
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.db.models.signals import pre_delete
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
@@ -15,6 +19,65 @@ from mptt.models import MPTTModel, TreeForeignKey
|
|||||||
from .validators import validate_tree_name
|
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):
|
class InvenTreeTree(MPTTModel):
|
||||||
""" Provides an abstracted self-referencing tree model for data categories.
|
""" Provides an abstracted self-referencing tree model for data categories.
|
||||||
|
|
||||||
@@ -29,6 +92,8 @@ class InvenTreeTree(MPTTModel):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
abstract = True
|
abstract = True
|
||||||
|
|
||||||
|
# Names must be unique at any given level in the tree
|
||||||
unique_together = ('name', 'parent')
|
unique_together = ('name', 'parent')
|
||||||
|
|
||||||
class MPTTMeta:
|
class MPTTMeta:
|
||||||
@@ -37,13 +102,14 @@ class InvenTreeTree(MPTTModel):
|
|||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
blank=False,
|
blank=False,
|
||||||
max_length=100,
|
max_length=100,
|
||||||
unique=True,
|
validators=[validate_tree_name],
|
||||||
validators=[validate_tree_name]
|
help_text=_("Name"),
|
||||||
)
|
)
|
||||||
|
|
||||||
description = models.CharField(
|
description = models.CharField(
|
||||||
blank=False,
|
blank=True,
|
||||||
max_length=250
|
max_length=250,
|
||||||
|
help_text=_("Description (optional)")
|
||||||
)
|
)
|
||||||
|
|
||||||
# When a category is deleted, graft the children onto its parent
|
# 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
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
|
|
||||||
@@ -50,3 +53,32 @@ class InvenTreeModelSerializer(serializers.ModelSerializer):
|
|||||||
instance.clean()
|
instance.clean()
|
||||||
|
|
||||||
return data
|
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))
|
||||||
|
|||||||
@@ -11,16 +11,39 @@ database setup in this file.
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
def eprint(*args, **kwargs):
|
def _is_true(x):
|
||||||
""" Print a warning message to stderr """
|
# Shortcut function to determine if a value "looks" like a boolean
|
||||||
print(*args, file=sys.stderr, **kwargs)
|
return str(x).lower() in ['1', 'y', 'yes', 't', 'true']
|
||||||
|
|
||||||
|
|
||||||
|
def get_setting(environment_var, backup_val, default_value=None):
|
||||||
|
"""
|
||||||
|
Helper function for retrieving a configuration setting value
|
||||||
|
|
||||||
|
- First preference is to look for the environment variable
|
||||||
|
- Second preference is to look for the value of the settings file
|
||||||
|
- Third preference is the default value
|
||||||
|
"""
|
||||||
|
|
||||||
|
val = os.getenv(environment_var)
|
||||||
|
|
||||||
|
if val is not None:
|
||||||
|
return val
|
||||||
|
|
||||||
|
if backup_val is not None:
|
||||||
|
return backup_val
|
||||||
|
|
||||||
|
return default_value
|
||||||
|
|
||||||
|
|
||||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||||
@@ -29,20 +52,73 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|||||||
cfg_filename = os.path.join(BASE_DIR, 'config.yaml')
|
cfg_filename = os.path.join(BASE_DIR, 'config.yaml')
|
||||||
|
|
||||||
if not os.path.exists(cfg_filename):
|
if not os.path.exists(cfg_filename):
|
||||||
CONFIG = {}
|
print("Error: config.yaml not found")
|
||||||
eprint("Warning: config.yaml not found - using default settings")
|
sys.exit(-1)
|
||||||
else:
|
|
||||||
with open(cfg_filename, 'r') as cfg:
|
|
||||||
CONFIG = yaml.safe_load(cfg)
|
|
||||||
|
|
||||||
# Read the autogenerated key-file
|
with open(cfg_filename, 'r') as cfg:
|
||||||
key_file = open(os.path.join(BASE_DIR, 'secret_key.txt'), 'r')
|
CONFIG = yaml.safe_load(cfg)
|
||||||
|
|
||||||
SECRET_KEY = key_file.read().strip()
|
|
||||||
|
|
||||||
# Default action is to run the system in Debug mode
|
# Default action is to run the system in Debug mode
|
||||||
# SECURITY WARNING: don't run with debug turned on in production!
|
# SECURITY WARNING: don't run with debug turned on in production!
|
||||||
DEBUG = CONFIG.get('debug', True)
|
DEBUG = _is_true(get_setting(
|
||||||
|
'INVENTREE_DEBUG',
|
||||||
|
CONFIG.get('debug', True)
|
||||||
|
))
|
||||||
|
|
||||||
|
# Configure logging settings
|
||||||
|
log_level = get_setting(
|
||||||
|
'INVENTREE_LOG_LEVEL',
|
||||||
|
CONFIG.get('log_level', 'DEBUG')
|
||||||
|
)
|
||||||
|
|
||||||
|
logging.basicConfig(
|
||||||
|
level=log_level,
|
||||||
|
format="%(asctime)s %(levelname)s %(message)s",
|
||||||
|
)
|
||||||
|
|
||||||
|
if log_level not in ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL']:
|
||||||
|
log_level = 'WARNING'
|
||||||
|
|
||||||
|
LOGGING = {
|
||||||
|
'version': 1,
|
||||||
|
'disable_existing_loggers': False,
|
||||||
|
'handlers': {
|
||||||
|
'console': {
|
||||||
|
'class': 'logging.StreamHandler',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'root': {
|
||||||
|
'handlers': ['console'],
|
||||||
|
'level': log_level,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get a logger instance for this setup file
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
if os.getenv("INVENTREE_SECRET_KEY"):
|
||||||
|
# Secret key passed in directly
|
||||||
|
SECRET_KEY = os.getenv("INVENTREE_SECRET_KEY").strip()
|
||||||
|
logger.info("SECRET_KEY loaded by INVENTREE_SECRET_KEY")
|
||||||
|
else:
|
||||||
|
# Secret key passed in by file location
|
||||||
|
key_file = os.getenv("INVENTREE_SECRET_KEY_FILE")
|
||||||
|
|
||||||
|
if key_file:
|
||||||
|
if os.path.isfile(key_file):
|
||||||
|
logger.info("SECRET_KEY loaded by INVENTREE_SECRET_KEY_FILE")
|
||||||
|
else:
|
||||||
|
logger.error(f"Secret key file {key_file} not found")
|
||||||
|
exit(-1)
|
||||||
|
else:
|
||||||
|
# default secret key location
|
||||||
|
key_file = os.path.join(BASE_DIR, "secret_key.txt")
|
||||||
|
logger.info(f"SECRET_KEY loaded from {key_file}")
|
||||||
|
try:
|
||||||
|
SECRET_KEY = open(key_file, "r").read().strip()
|
||||||
|
except Exception:
|
||||||
|
logger.exception(f"Couldn't load keyfile {key_file}")
|
||||||
|
sys.exit(-1)
|
||||||
|
|
||||||
# List of allowed hosts (default = allow all)
|
# List of allowed hosts (default = allow all)
|
||||||
ALLOWED_HOSTS = CONFIG.get('allowed_hosts', ['*'])
|
ALLOWED_HOSTS = CONFIG.get('allowed_hosts', ['*'])
|
||||||
@@ -61,12 +137,40 @@ if cors_opt:
|
|||||||
if not CORS_ORIGIN_ALLOW_ALL:
|
if not CORS_ORIGIN_ALLOW_ALL:
|
||||||
CORS_ORIGIN_WHITELIST = cors_opt.get('whitelist', [])
|
CORS_ORIGIN_WHITELIST = cors_opt.get('whitelist', [])
|
||||||
|
|
||||||
if DEBUG:
|
# Web URL endpoint for served static files
|
||||||
# will output to your console
|
STATIC_URL = '/static/'
|
||||||
logging.basicConfig(
|
|
||||||
level=logging.DEBUG,
|
# The filesystem location for served static files
|
||||||
format='%(asctime)s %(levelname)s %(message)s',
|
STATIC_ROOT = os.path.abspath(
|
||||||
|
get_setting(
|
||||||
|
'INVENTREE_STATIC_ROOT',
|
||||||
|
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(
|
||||||
|
get_setting(
|
||||||
|
'INVENTREE_MEDIA_ROOT',
|
||||||
|
CONFIG.get('media_root', os.path.join(BASE_DIR, 'media'))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if DEBUG:
|
||||||
|
logger.info("InvenTree running in DEBUG mode")
|
||||||
|
|
||||||
|
logger.info(f"MEDIA_ROOT: '{MEDIA_ROOT}'")
|
||||||
|
logger.info(f"STATIC_ROOT: '{STATIC_ROOT}'")
|
||||||
|
|
||||||
# Application definition
|
# Application definition
|
||||||
|
|
||||||
@@ -81,65 +185,83 @@ INSTALLED_APPS = [
|
|||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
|
|
||||||
# InvenTree apps
|
# InvenTree apps
|
||||||
'common.apps.CommonConfig',
|
|
||||||
'part.apps.PartConfig',
|
|
||||||
'stock.apps.StockConfig',
|
|
||||||
'company.apps.CompanyConfig',
|
|
||||||
'build.apps.BuildConfig',
|
'build.apps.BuildConfig',
|
||||||
|
'common.apps.CommonConfig',
|
||||||
|
'company.apps.CompanyConfig',
|
||||||
|
'label.apps.LabelConfig',
|
||||||
'order.apps.OrderConfig',
|
'order.apps.OrderConfig',
|
||||||
|
'part.apps.PartConfig',
|
||||||
|
'report.apps.ReportConfig',
|
||||||
|
'stock.apps.StockConfig',
|
||||||
|
'users.apps.UsersConfig',
|
||||||
|
|
||||||
# Third part add-ons
|
# Third part add-ons
|
||||||
'django_filters', # Extended filter functionality
|
'django_filters', # Extended filter functionality
|
||||||
'dbbackup', # Database backup / restore
|
'dbbackup', # Database backup / restore
|
||||||
'rest_framework', # DRF (Django Rest Framework)
|
'rest_framework', # DRF (Django Rest Framework)
|
||||||
'rest_framework.authtoken', # Token authentication for API
|
'rest_framework.authtoken', # Token authentication for API
|
||||||
'corsheaders', # Cross-origin Resource Sharing for DRF
|
'corsheaders', # Cross-origin Resource Sharing for DRF
|
||||||
'crispy_forms', # Improved form rendering
|
'crispy_forms', # Improved form rendering
|
||||||
'import_export', # Import / export tables to file
|
'import_export', # Import / export tables to file
|
||||||
'django_cleanup', # Automatically delete orphaned MEDIA files
|
'django_cleanup.apps.CleanupConfig', # Automatically delete orphaned MEDIA files
|
||||||
'qr_code', # Generate QR codes
|
'qr_code', # Generate QR codes
|
||||||
'mptt', # Modified Preorder Tree Traversal
|
'mptt', # Modified Preorder Tree Traversal
|
||||||
|
'markdownx', # Markdown editing
|
||||||
|
'markdownify', # Markdown template rendering
|
||||||
|
'django_admin_shell', # Python shell for the admin interface
|
||||||
|
'djmoney', # django-money integration
|
||||||
|
'djmoney.contrib.exchange', # django-money exchange rates
|
||||||
|
'error_report', # Error reporting in the admin interface
|
||||||
|
'django_migration_linter', # Linting checking for migration files
|
||||||
]
|
]
|
||||||
|
|
||||||
LOGGING = {
|
MIDDLEWARE = CONFIG.get('middleware', [
|
||||||
'version': 1,
|
|
||||||
'disable_existing_loggers': False,
|
|
||||||
'handlers': {
|
|
||||||
'console': {
|
|
||||||
'level': 'DEBUG',
|
|
||||||
'class': 'logging.StreamHandler',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
MIDDLEWARE = [
|
|
||||||
'django.middleware.security.SecurityMiddleware',
|
'django.middleware.security.SecurityMiddleware',
|
||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
|
'django.middleware.locale.LocaleMiddleware',
|
||||||
'django.middleware.common.CommonMiddleware',
|
'django.middleware.common.CommonMiddleware',
|
||||||
'django.middleware.csrf.CsrfViewMiddleware',
|
'django.middleware.csrf.CsrfViewMiddleware',
|
||||||
'corsheaders.middleware.CorsMiddleware',
|
'corsheaders.middleware.CorsMiddleware',
|
||||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||||
'InvenTree.middleware.AuthRequiredMiddleware',
|
'InvenTree.middleware.AuthRequiredMiddleware'
|
||||||
]
|
])
|
||||||
|
|
||||||
if CONFIG.get('log_queries', False):
|
# Error reporting middleware
|
||||||
MIDDLEWARE.append('InvenTree.middleware.QueryCountMiddleware')
|
MIDDLEWARE.append('error_report.middleware.ExceptionProcessor')
|
||||||
|
|
||||||
|
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):
|
||||||
|
logger.info("Running with DEBUG_TOOLBAR enabled")
|
||||||
|
INSTALLED_APPS.append('debug_toolbar')
|
||||||
|
MIDDLEWARE.append('debug_toolbar.middleware.DebugToolbarMiddleware')
|
||||||
|
|
||||||
ROOT_URLCONF = 'InvenTree.urls'
|
ROOT_URLCONF = 'InvenTree.urls'
|
||||||
|
|
||||||
TEMPLATES = [
|
TEMPLATES = [
|
||||||
{
|
{
|
||||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
'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,
|
'APP_DIRS': True,
|
||||||
'OPTIONS': {
|
'OPTIONS': {
|
||||||
'context_processors': [
|
'context_processors': [
|
||||||
'django.template.context_processors.debug',
|
'django.template.context_processors.debug',
|
||||||
'django.template.context_processors.request',
|
'django.template.context_processors.request',
|
||||||
|
'django.template.context_processors.i18n',
|
||||||
'django.contrib.auth.context_processors.auth',
|
'django.contrib.auth.context_processors.auth',
|
||||||
'django.contrib.messages.context_processors.messages',
|
'django.contrib.messages.context_processors.messages',
|
||||||
|
'InvenTree.context.health_status',
|
||||||
|
'InvenTree.context.status_codes',
|
||||||
|
'InvenTree.context.user_roles',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -153,11 +275,46 @@ REST_FRAMEWORK = {
|
|||||||
'rest_framework.authentication.SessionAuthentication',
|
'rest_framework.authentication.SessionAuthentication',
|
||||||
'rest_framework.authentication.TokenAuthentication',
|
'rest_framework.authentication.TokenAuthentication',
|
||||||
),
|
),
|
||||||
|
'DEFAULT_PERMISSION_CLASSES': (
|
||||||
|
'rest_framework.permissions.IsAuthenticated',
|
||||||
|
'rest_framework.permissions.DjangoModelPermissions',
|
||||||
|
),
|
||||||
'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema'
|
'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema'
|
||||||
}
|
}
|
||||||
|
|
||||||
WSGI_APPLICATION = 'InvenTree.wsgi.application'
|
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 = {}
|
DATABASES = {}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -165,22 +322,79 @@ When running unit tests, enforce usage of sqlite3 database,
|
|||||||
so that the tests can be run in RAM without any setup requirements
|
so that the tests can be run in RAM without any setup requirements
|
||||||
"""
|
"""
|
||||||
if 'test' in sys.argv:
|
if 'test' in sys.argv:
|
||||||
eprint('Running tests - Using sqlite3 memory database')
|
logger.info('InvenTree: Running tests - Using sqlite3 memory database')
|
||||||
DATABASES['default'] = {
|
DATABASES['default'] = {
|
||||||
|
# Ensure sqlite3 backend is being used
|
||||||
'ENGINE': 'django.db.backends.sqlite3',
|
'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
|
# Database backend selection
|
||||||
else:
|
else:
|
||||||
if 'database' in CONFIG:
|
"""
|
||||||
DATABASES['default'] = CONFIG['database']
|
Configure the database backend based on the user-specified values.
|
||||||
else:
|
|
||||||
eprint("Warning: Database backend not specified - using default (sqlite)")
|
- Primarily this configuration happens in the config.yaml file
|
||||||
DATABASES['default'] = {
|
- However there may be reason to configure the DB via environmental variables
|
||||||
'ENGINE': 'django.db.backends.sqlite3',
|
- The following code lets the user "mix and match" database configuration
|
||||||
'NAME': os.path.join(BASE_DIR, 'inventree_db.sqlite3'),
|
"""
|
||||||
}
|
|
||||||
|
logger.info("Configuring database backend:")
|
||||||
|
|
||||||
|
# Extract database configuration from the config.yaml file
|
||||||
|
db_config = CONFIG.get('database', {})
|
||||||
|
|
||||||
|
# If a particular database option is not specified in the config file,
|
||||||
|
# look for it in the environmental variables
|
||||||
|
# e.g. INVENTREE_DB_NAME / INVENTREE_DB_USER / etc
|
||||||
|
|
||||||
|
db_keys = ['ENGINE', 'NAME', 'USER', 'PASSWORD', 'HOST', 'PORT']
|
||||||
|
|
||||||
|
for key in db_keys:
|
||||||
|
if key not in db_config:
|
||||||
|
logger.debug(f" - Missing {key} value: Looking for environment variable INVENTREE_DB_{key}")
|
||||||
|
env_key = f'INVENTREE_DB_{key}'
|
||||||
|
env_var = os.environ.get(env_key, None)
|
||||||
|
|
||||||
|
if env_var is not None:
|
||||||
|
logger.info(f'Using environment variable INVENTREE_DB_{key}')
|
||||||
|
db_config[key] = env_var
|
||||||
|
else:
|
||||||
|
logger.debug(f' INVENTREE_DB_{key} not found in environment variables')
|
||||||
|
|
||||||
|
# Check that required database configuration options are specified
|
||||||
|
reqiured_keys = ['ENGINE', 'NAME']
|
||||||
|
|
||||||
|
for key in reqiured_keys:
|
||||||
|
if key not in db_config:
|
||||||
|
error_msg = f'Missing required database configuration value {key} in config.yaml'
|
||||||
|
logger.error(error_msg)
|
||||||
|
|
||||||
|
print('Error: ' + error_msg)
|
||||||
|
sys.exit(-1)
|
||||||
|
|
||||||
|
"""
|
||||||
|
Special considerations for the database 'ENGINE' setting.
|
||||||
|
It can be specified in config.yaml (or envvar) as either (for example):
|
||||||
|
- sqlite3
|
||||||
|
- django.db.backends.sqlite3
|
||||||
|
- django.db.backends.postgresql
|
||||||
|
"""
|
||||||
|
|
||||||
|
db_engine = db_config['ENGINE']
|
||||||
|
|
||||||
|
if db_engine.lower() in ['sqlite3', 'postgresql', 'mysql']:
|
||||||
|
# Prepend the required python module string
|
||||||
|
db_engine = f'django.db.backends.{db_engine.lower()}'
|
||||||
|
db_config['ENGINE'] = db_engine
|
||||||
|
|
||||||
|
db_name = db_config['NAME']
|
||||||
|
|
||||||
|
logger.info(f"Database ENGINE: '{db_engine}'")
|
||||||
|
logger.info(f"Database NAME: '{db_name}'")
|
||||||
|
|
||||||
|
DATABASES['default'] = db_config
|
||||||
|
|
||||||
CACHES = {
|
CACHES = {
|
||||||
'default': {
|
'default': {
|
||||||
@@ -219,15 +433,39 @@ AUTH_PASSWORD_VALIDATORS = [
|
|||||||
EXTRA_URL_SCHEMES = CONFIG.get('extra_url_schemes', [])
|
EXTRA_URL_SCHEMES = CONFIG.get('extra_url_schemes', [])
|
||||||
|
|
||||||
if not type(EXTRA_URL_SCHEMES) in [list]:
|
if not type(EXTRA_URL_SCHEMES) in [list]:
|
||||||
eprint("Warning: extra_url_schemes not correctly formatted")
|
logger.warning("extra_url_schemes not correctly formatted")
|
||||||
EXTRA_URL_SCHEMES = []
|
EXTRA_URL_SCHEMES = []
|
||||||
|
|
||||||
# Internationalization
|
# Internationalization
|
||||||
# https://docs.djangoproject.com/en/1.10/topics/i18n/
|
# https://docs.djangoproject.com/en/dev/topics/i18n/
|
||||||
|
|
||||||
LANGUAGE_CODE = 'en-us'
|
LANGUAGE_CODE = CONFIG.get('language', 'en-us')
|
||||||
|
|
||||||
TIME_ZONE = 'UTC'
|
# If a new language translation is supported, it must be added here
|
||||||
|
LANGUAGES = [
|
||||||
|
('en', _('English')),
|
||||||
|
('fr', _('French')),
|
||||||
|
('de', _('German')),
|
||||||
|
('pk', _('Polish')),
|
||||||
|
('tr', _('Turkish')),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Currencies available for use
|
||||||
|
CURRENCIES = CONFIG.get(
|
||||||
|
'currencies',
|
||||||
|
[
|
||||||
|
'AUD', 'CAD', 'EUR', 'GBP', 'JPY', 'NZD', 'USD',
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
# TODO - Allow live web-based backends in the future
|
||||||
|
EXCHANGE_BACKEND = 'InvenTree.exchange.InvenTreeManualExchangeBackend'
|
||||||
|
|
||||||
|
LOCALE_PATHS = (
|
||||||
|
os.path.join(BASE_DIR, 'locale/'),
|
||||||
|
)
|
||||||
|
|
||||||
|
TIME_ZONE = CONFIG.get('timezone', 'UTC')
|
||||||
|
|
||||||
USE_I18N = True
|
USE_I18N = True
|
||||||
|
|
||||||
@@ -239,34 +477,24 @@ DATE_INPUT_FORMATS = [
|
|||||||
"%Y-%m-%d",
|
"%Y-%m-%d",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
# 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'),
|
|
||||||
]
|
|
||||||
|
|
||||||
# Web URL endpoint for served media files
|
|
||||||
MEDIA_URL = '/media/'
|
|
||||||
|
|
||||||
# The filesystem location for served static files
|
|
||||||
MEDIA_ROOT = CONFIG.get('media_root', os.path.join(BASE_DIR, 'media'))
|
|
||||||
|
|
||||||
# crispy forms use the bootstrap templates
|
# crispy forms use the bootstrap templates
|
||||||
CRISPY_TEMPLATE_PACK = 'bootstrap'
|
CRISPY_TEMPLATE_PACK = 'bootstrap3'
|
||||||
|
|
||||||
# Use database transactions when importing / exporting data
|
# Use database transactions when importing / exporting data
|
||||||
IMPORT_EXPORT_USE_TRANSACTIONS = True
|
IMPORT_EXPORT_USE_TRANSACTIONS = True
|
||||||
|
|
||||||
|
BACKUP_DIR = get_setting(
|
||||||
|
'INVENTREE_BACKUP_DIR',
|
||||||
|
CONFIG.get('backup_dir', tempfile.gettempdir()),
|
||||||
|
)
|
||||||
|
|
||||||
# Settings for dbbsettings app
|
# Settings for dbbsettings app
|
||||||
DBBACKUP_STORAGE = 'django.core.files.storage.FileSystemStorage'
|
DBBACKUP_STORAGE = 'django.core.files.storage.FileSystemStorage'
|
||||||
DBBACKUP_STORAGE_OPTIONS = {
|
DBBACKUP_STORAGE_OPTIONS = {
|
||||||
'location': CONFIG.get('backup_dir', tempfile.gettempdir()),
|
'location': BACKUP_DIR,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# 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,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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
13
InvenTree/InvenTree/static/css/bootstrap-table-filter-control.css
vendored
Normal file
13
InvenTree/InvenTree/static/css/bootstrap-table-filter-control.css
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
@charset "UTF-8";
|
||||||
|
/**
|
||||||
|
* @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;
|
||||||
|
}
|
||||||
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" */
|
||||||
@@ -3,8 +3,118 @@
|
|||||||
--secondary-color: #b69c80;
|
--secondary-color: #b69c80;
|
||||||
--highlight-color: #f5efe8;
|
--highlight-color: #f5efe8;
|
||||||
--basic-color: #333;
|
--basic-color: #333;
|
||||||
|
|
||||||
|
--label-red: #e35a57;
|
||||||
|
--label-blue: #4194bd;
|
||||||
|
--label-green: #50aa51;
|
||||||
|
--label-grey: #aaa;
|
||||||
|
--label-yellow: #fdc82a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.login-screen {
|
||||||
|
background-image: url("/static/img/paper_splash.jpg");
|
||||||
|
background-size: cover;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
height: 100%;
|
||||||
|
font-family: 'Numans', sans-serif;
|
||||||
|
color: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-container {
|
||||||
|
left: 50%;
|
||||||
|
position: fixed;
|
||||||
|
top: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
width: 30%;
|
||||||
|
align-content: center;
|
||||||
|
border-radius: 15px;
|
||||||
|
padding: 20px;
|
||||||
|
padding-bottom: 35px;
|
||||||
|
background-color: rgba(50, 50, 50, 0.75);
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-header {
|
||||||
|
padding-right: 30px;
|
||||||
|
margin-right: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-container input {
|
||||||
|
background-color: rgba(250, 250, 250, 0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-button {
|
||||||
|
background-color: rgba(250, 250, 250, 0.9);
|
||||||
|
color: #333;
|
||||||
|
border-color: #AAA;
|
||||||
|
width: 100%;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.index-bg {
|
||||||
|
width: 100%;
|
||||||
|
object-fit: fill;
|
||||||
|
opacity: 5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.index-action-selected {
|
||||||
|
background-color: #EEEEF5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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 {
|
.qr-code {
|
||||||
max-width: 400px;
|
max-width: 400px;
|
||||||
@@ -21,14 +131,43 @@
|
|||||||
float: left;
|
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 {
|
.glyphicon {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.glyphicon-small {
|
.glyphicon-small {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.glyphicon-right {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
.starred-part {
|
.starred-part {
|
||||||
color: #ffbb00;
|
color: #ffbb00;
|
||||||
}
|
}
|
||||||
@@ -41,24 +180,20 @@
|
|||||||
color: rgb(13, 245, 25);
|
color: rgb(13, 245, 25);
|
||||||
}
|
}
|
||||||
|
|
||||||
.glyphicon-ok {
|
.icon-red {
|
||||||
color: #5C5;
|
color: #c55;
|
||||||
}
|
}
|
||||||
|
|
||||||
.glyphicon-ok-circle {
|
.icon-green {
|
||||||
|
color: #43bb43;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-blue {
|
||||||
color: #55c;
|
color: #55c;
|
||||||
}
|
}
|
||||||
|
|
||||||
.glyphicon-remove {
|
.icon-yellow {
|
||||||
color: #C55;
|
color: #CC2;
|
||||||
}
|
|
||||||
|
|
||||||
.glyphicon-trash {
|
|
||||||
color: #C55;
|
|
||||||
}
|
|
||||||
|
|
||||||
.glyphicon-plus {
|
|
||||||
color: #5C5;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* CSS overrides for treeview */
|
/* CSS overrides for treeview */
|
||||||
@@ -83,6 +218,64 @@
|
|||||||
.label-large {
|
.label-large {
|
||||||
margin: 3px;
|
margin: 3px;
|
||||||
font-size: 100%;
|
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 */
|
/* Bootstrap table overrides */
|
||||||
@@ -91,6 +284,15 @@
|
|||||||
background-color: #ebf4f4;
|
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 */
|
/* Force select2 elements in modal forms to be full width */
|
||||||
.select-full-width {
|
.select-full-width {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -112,9 +314,83 @@
|
|||||||
|
|
||||||
.rowinvalid {
|
.rowinvalid {
|
||||||
color: #A00;
|
color: #A00;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rowinherited {
|
||||||
|
background-color: #eee;
|
||||||
font-style: italic;
|
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 */
|
/* Part image icons with full-display on mouse hover */
|
||||||
|
|
||||||
.hover-img-thumb {
|
.hover-img-thumb {
|
||||||
@@ -144,13 +420,14 @@
|
|||||||
|
|
||||||
/* dropzone class - for Drag-n-Drop file uploads */
|
/* dropzone class - for Drag-n-Drop file uploads */
|
||||||
.dropzone {
|
.dropzone {
|
||||||
border: 1px solid #555;
|
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
.dropzone * {
|
.dropzone * {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
.dragover {
|
.dragover {
|
||||||
background-color: #55A;
|
background-color: #55A;
|
||||||
@@ -160,6 +437,24 @@
|
|||||||
-webkit-opacity: 10%;
|
-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 {
|
.btn-glyph {
|
||||||
padding-left: 6px;
|
padding-left: 6px;
|
||||||
@@ -168,6 +463,28 @@
|
|||||||
padding-bottom: 2px;
|
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 {
|
.badge {
|
||||||
float: right;
|
float: right;
|
||||||
background-color: #777;
|
background-color: #777;
|
||||||
@@ -180,12 +497,36 @@
|
|||||||
background-color: #f33;
|
background-color: #f33;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.badge-orange {
|
||||||
|
background-color: #fcba03;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-green {
|
||||||
|
background-color: #1A1;
|
||||||
|
}
|
||||||
|
|
||||||
.part-thumb {
|
.part-thumb {
|
||||||
width: 200px;
|
width: 200px;
|
||||||
height: 200px;
|
height: 200px;
|
||||||
margin: 2px;
|
margin: 2px;
|
||||||
padding: 3px;
|
padding: 3px;
|
||||||
object-fit: contain;
|
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 {
|
.checkbox {
|
||||||
@@ -199,10 +540,12 @@
|
|||||||
|
|
||||||
.media {
|
.media {
|
||||||
padding-top: 15px;
|
padding-top: 15px;
|
||||||
|
overflow: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
.media-body {
|
.media-body {
|
||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
|
overflow: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
.navigation {
|
.navigation {
|
||||||
@@ -292,7 +635,7 @@
|
|||||||
margin-bottom: 3px;
|
margin-bottom: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-form-content {
|
.modal-form-content-wrapper {
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
position:relative;
|
position:relative;
|
||||||
height: auto !important;
|
height: auto !important;
|
||||||
@@ -305,6 +648,12 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input[type="submit"] {
|
||||||
|
color: #333;
|
||||||
|
background-color: #e6e6e6;
|
||||||
|
border-color: #adadad;
|
||||||
|
}
|
||||||
|
|
||||||
.modal textarea {
|
.modal textarea {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
@@ -420,13 +769,6 @@
|
|||||||
color: #e00;
|
color: #e00;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login {
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.part-allocation {
|
.part-allocation {
|
||||||
padding: 3px 10px;
|
padding: 3px 10px;
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #ccc;
|
||||||
@@ -460,3 +802,6 @@
|
|||||||
to { transform: scale(1) rotate(360deg);}
|
to { transform: scale(1) rotate(360deg);}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input[type="date"].form-control, input[type="time"].form-control, input[type="datetime-local"].form-control, input[type="month"].form-control {
|
||||||
|
line-height: unset;
|
||||||
|
}
|
||||||
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
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user