Compare commits
1179 Commits
Author | SHA1 | Date | |
---|---|---|---|
7707660978 | |||
7afdab80ac | |||
144718866d | |||
1dd9fdd0c2 | |||
3794ed20a8 | |||
ff8c837fd9 | |||
fe1caffc0f | |||
8e7b2a04fd | |||
0e684736bd | |||
1ad4427584 | |||
b0a16e7730 | |||
7559f79ef6 | |||
c8b642be1c | |||
91c435cdac | |||
57249882ab | |||
c28dfc09b3 | |||
cd5841c4c8 | |||
c83b30f43a | |||
011160a3c2 | |||
067d888f4c | |||
1b5a5080f2 | |||
1a97277ec4 | |||
e426fdd4a3 | |||
5758de7667 | |||
d21d9a86d4 | |||
a435162a2b | |||
4fef2b2942 | |||
77c6419c80 | |||
97fe3b4b40 | |||
5396091b83 | |||
655ad119ce | |||
351446a2ee | |||
f2b7f61030 | |||
b678a3401e | |||
465cd99165 | |||
2e67b49447 | |||
45449b6907 | |||
05d4fb1ad7 | |||
81d81f4393 | |||
60ddcaa08e | |||
0e780f26b9 | |||
bedd504b64 | |||
2f657dc20b | |||
4f8eefe71e | |||
dd63767ddf | |||
e1ff1c6e21 | |||
f673a84cf6 | |||
4a2273c93c | |||
3cb90acb13 | |||
ab38e03af9 | |||
2139f031f4 | |||
ec280cd76d | |||
5e7f57eb0a | |||
26cbe4b592 | |||
e8aa2bd42b | |||
501062ac37 | |||
4dc0bb29d2 | |||
b1c4c8cb5e | |||
e4fe5bbfec | |||
4b35d25849 | |||
3b57d08c0a | |||
29e70a8e3f | |||
a0d30c002a | |||
327065498b | |||
047ee4cb37 | |||
9b945ca0a5 | |||
e70b0fdb40 | |||
c8a38d5785 | |||
10eb7794e9 | |||
0dee5f52fc | |||
7195258d7e | |||
9750d63e55 | |||
e8a14fedf2 | |||
eb897adbd1 | |||
5fb1b87e67 | |||
a9b24f0cf7 | |||
f40ee254c9 | |||
2b047634a0 | |||
7baa5bfe91 | |||
2d6058a6b3 | |||
d34c79b4cd | |||
ed558a6484 | |||
854987bd44 | |||
2977f7385e | |||
760cc71768 | |||
573eac78f1 | |||
02fa04d19d | |||
6e1d190230 | |||
80b2495c11 | |||
3d9d8202d9 | |||
188baa5cc8 | |||
3dbaf7ef32 | |||
545984bb18 | |||
f9a5716002 | |||
818fcf114d | |||
3ca606d6f5 | |||
288e64f357 | |||
6ed1b81451 | |||
823ca8a805 | |||
7301c4852d | |||
602f0fc9c5 | |||
694e7cc01c | |||
7efa34efee | |||
49675ffbee | |||
b82b7d74fc | |||
5102c7c459 | |||
1d022bc8ae | |||
489ec27602 | |||
6e4e065b75 | |||
69db87e305 | |||
2dacca5d06 | |||
1abac0ac0e | |||
ba6ed8967c | |||
3d80f755a1 | |||
7a541b609f | |||
0f4383563f | |||
7b33d40de4 | |||
4c85c22c30 | |||
7839cf1515 | |||
d2a9aaee54 | |||
ff85cb6153 | |||
4c396ba9c6 | |||
d2c397f6b9 | |||
e679322122 | |||
e6ae6c9e9c | |||
d387b793ea | |||
50bb525f60 | |||
8de60b332d | |||
51eb460ce9 | |||
78a37c736f | |||
380c455332 | |||
6c35ade209 | |||
633f30d365 | |||
8c1d882404 | |||
6af73cea55 | |||
d3732d1a54 | |||
3aecaf9161 | |||
b12e94cee4 | |||
3d5d2c05ce | |||
f01cc77f92 | |||
c81271864d | |||
10ea494594 | |||
836b1d17e6 | |||
5004a9f332 | |||
40f134d7bc | |||
391708a49c | |||
566c9fbd1e | |||
56bda5ed71 | |||
d2fbd3b56a | |||
9f680a75e3 | |||
8fadf9d589 | |||
b64d8c6c5b | |||
b393c695a5 | |||
1ab4dedddb | |||
73ee1a41e1 | |||
44a7d36174 | |||
77f314ee55 | |||
8d742e62ed | |||
e1fd2bed59 | |||
61d44146cd | |||
b2761541ec | |||
6412f1108b | |||
c37f50d06e | |||
c900e379c5 | |||
326436c0b5 | |||
66df53bf43 | |||
2cd9f99011 | |||
35ec3c8bfa | |||
52a0ff275d | |||
a26c90ff86 | |||
c173a8c196 | |||
73cf8ccd5a | |||
244372ff8c | |||
74ef13713d | |||
057d4bfae7 | |||
68bc4693cb | |||
ce3260a2f3 | |||
4c5aa1e3de | |||
3c8b564ab9 | |||
7505907976 | |||
05455a518b | |||
c39bc23061 | |||
9ba1bd4c90 | |||
2f789cc4cf | |||
a18c0bd7b5 | |||
2b31cae57b | |||
48dcaa4bcb | |||
4342b2e795 | |||
b2361c57c2 | |||
e6fdea28d4 | |||
15dcdfe39c | |||
0250a50731 | |||
f2aca2b76f | |||
97a7d501d0 | |||
8c3b583f92 | |||
d6e174a014 | |||
df7a9e311b | |||
ebf22643ef | |||
3039f412aa | |||
37ab6034ba | |||
d5931ff53b | |||
fe1250732c | |||
144e3f5229 | |||
5f494aa9bd | |||
7e0cbe8b83 | |||
31ed9d11ab | |||
6e3a70f9f6 | |||
3c47570cce | |||
b4d9e9f21f | |||
1ea5750a76 | |||
36b396be71 | |||
bf4f86a010 | |||
45b0209d8a | |||
f37ec8022e | |||
415778295f | |||
ad0dcb5750 | |||
e0ad68b9d2 | |||
608251452f | |||
8c5547d69d | |||
6c29af4533 | |||
898dfebbde | |||
2d5ae3b1c6 | |||
8256815032 | |||
fe6f737815 | |||
87d15024bb | |||
470d25f269 | |||
70a33e36c0 | |||
94d227b08e | |||
ecb913322f | |||
b001af868f | |||
1fbf0283ba | |||
5bcc046a12 | |||
9009da4b86 | |||
47a4be2cf9 | |||
52aabc2b12 | |||
7b1217bafe | |||
a79ab842ee | |||
810731dfbd | |||
c224c8d977 | |||
ce3e3427dc | |||
7211aa0de7 | |||
02bd0288f2 | |||
a15496e4a5 | |||
a14ac3a122 | |||
1a3f1e9654 | |||
0c936b3fa7 | |||
ccb9c9d331 | |||
738e39ba92 | |||
18e8d47167 | |||
c429ce64d3 | |||
9003c15929 | |||
eb56ab4c4f | |||
29a79577d9 | |||
e890f978db | |||
e944ddc75c | |||
778cda6efc | |||
e912c83079 | |||
78b9146249 | |||
e40a1d028f | |||
edb022be13 | |||
b75a99a145 | |||
7aeb74874b | |||
9749c59549 | |||
5da0ba87a7 | |||
c749c7c923 | |||
9b4c9ad76f | |||
681a70fd92 | |||
67d3b7c1dc | |||
79aee53524 | |||
56f7d5142d | |||
5f7f4b6bdd | |||
0c3ff2ef11 | |||
5997ce8a0f | |||
687831efd5 | |||
5a65c7b645 | |||
9dc6643b78 | |||
c7beb76404 | |||
c132766486 | |||
9a112dc413 | |||
1c3216e26a | |||
78b195f966 | |||
a35bce8a4b | |||
93faf7d88c | |||
a0e7ebd2a0 | |||
2b54ab5497 | |||
7bfdd74f22 | |||
4582e2e917 | |||
b0c75d989f | |||
a3bae9255b | |||
3ab34a64d0 | |||
8110f10c3d | |||
7dae2dd6eb | |||
7c0ea492e1 | |||
9135598649 | |||
0b0e06af00 | |||
1ae99d2189 | |||
7275d81468 | |||
dbfdd1c239 | |||
9eec7123a3 | |||
710d18a43b | |||
6d66ad1ea4 | |||
8d87d2fb2b | |||
61fcffdcbe | |||
057216a21c | |||
abb17844fd | |||
891d8af469 | |||
c701999af1 | |||
af0b392e78 | |||
2cc85049f3 | |||
21a98dc48e | |||
3ecccb6ad8 | |||
e724f885c8 | |||
43f07dac6c | |||
7bcff79d6e | |||
f9f868be9d | |||
4a70422b23 | |||
d8167ed075 | |||
b6c0db7636 | |||
ffd7fb8745 | |||
75bd011a2c | |||
03638991a3 | |||
a7e6b3f198 | |||
d5e3dcaff8 | |||
9108fe066a | |||
f810e2573e | |||
a371a334d0 | |||
ed72eaef84 | |||
bcbd76d4a9 | |||
754cf80c0b | |||
ce0935333b | |||
050ff98d2c | |||
44d2c0e29d | |||
c14bf3a8af | |||
42ffef32fe | |||
1cc5703cd7 | |||
62be0beae6 | |||
e2e23a290f | |||
541b59bb28 | |||
d81fba030c | |||
209655adc3 | |||
8be0fceb69 | |||
8094ff5927 | |||
b614505499 | |||
e17846f4a4 | |||
59f2d21a7f | |||
0a83184db6 | |||
e3e32b4408 | |||
c424e3a65a | |||
2325c8f4ec | |||
5a3d9547ae | |||
bc529e57e8 | |||
f71a1ee1ae | |||
797e66e520 | |||
308a564a2d | |||
174877ec81 | |||
83bb85615a | |||
570c18a069 | |||
462e93292b | |||
82cd204469 | |||
6d55d4136e | |||
546789ce40 | |||
97be2e40f8 | |||
dd708bb1fa | |||
93a6dbe7d6 | |||
fae3314b56 | |||
dbb61372c6 | |||
0b4c2c81c3 | |||
0aee62c174 | |||
7fd881c8e9 | |||
9158d01fdf | |||
ae8df3e6d6 | |||
f6e00d4336 | |||
34b454c175 | |||
5ce34200c9 | |||
1a6a30ef5d | |||
8faf33c438 | |||
6e16632988 | |||
14db3f1ec9 | |||
a4fe28f7b5 | |||
7c796b3d7b | |||
06529d7c92 | |||
42948017a4 | |||
055ec80236 | |||
986ff6de0b | |||
e6a216704c | |||
beaeb13353 | |||
0ea35cb421 | |||
0feb44a836 | |||
9b33a45189 | |||
e0fa618b4e | |||
7672976f47 | |||
8036104731 | |||
76cb8ca00b | |||
cc3b999bc5 | |||
fb327f0c58 | |||
ee914d048b | |||
f57466d42c | |||
3ce928a67d | |||
622b435337 | |||
6acee9ca7a | |||
16ecfe3eb5 | |||
c0fa4cfed2 | |||
00ef5c3706 | |||
618328a4c1 | |||
e5ef054f5b | |||
d9e81a7cf1 | |||
b334a60486 | |||
17b01f6d48 | |||
ef7c1da5f2 | |||
6718fc78e9 | |||
5216b60789 | |||
37c3d6ecf0 | |||
eb12f57e7d | |||
a03b9be329 | |||
07de5ff79b | |||
8aa94f7c14 | |||
8842df76be | |||
6047444077 | |||
95e8b7920a | |||
66b13ef7ba | |||
b32968588a | |||
fafd75b791 | |||
70eb4f11b3 | |||
ae70eab9ea | |||
926179a72d | |||
143229bea4 | |||
3978c4d5cb | |||
705d48abad | |||
6044a676a6 | |||
245e1b4344 | |||
ec9edff2f3 | |||
3fa5b7ab88 | |||
ceb8159dae | |||
8b677ab7a7 | |||
b4b5c63d65 | |||
347674f9e5 | |||
d435a17ec8 | |||
fde9b05bdf | |||
7d9d2e8990 | |||
995690a341 | |||
808b704383 | |||
cd835055ec | |||
117427ced0 | |||
aebb349543 | |||
37f7616ef4 | |||
28d4ce9e86 | |||
0e893f06ba | |||
58bb1ab0ba | |||
46041ccfc6 | |||
625edf5d0c | |||
4edfd9f62c | |||
e5baccbf56 | |||
bfd5ff32c1 | |||
e1fd639ba9 | |||
4e88c27cfb | |||
0153271a62 | |||
7a529619ed | |||
a6020df023 | |||
cda711607a | |||
c5bb555695 | |||
ea5eb21941 | |||
038e61bf37 | |||
bce1afa546 | |||
4502c2f183 | |||
b070282899 | |||
aa6965e98f | |||
b2c8bff967 | |||
ea2a368100 | |||
523fcbd0d9 | |||
003492a964 | |||
e98699acbc | |||
eb31096bff | |||
d1a0147ae2 | |||
6c81ddbfd4 | |||
2fdca56501 | |||
47d6314178 | |||
e73326fd6c | |||
251bbadd96 | |||
6ee198c742 | |||
dfdc6b7356 | |||
94b15f455c | |||
f6278a1304 | |||
04c5b135a7 | |||
8a8ea0b470 | |||
4e2437f2bc | |||
27c7f08d7f | |||
b6f12ff2f6 | |||
317d8fd29b | |||
b66a35843d | |||
ac382a96a8 | |||
87d0960424 | |||
be16ceecb2 | |||
105b0f52ca | |||
63a50f7ed8 | |||
4b9f97f49e | |||
8272cc7240 | |||
538201ef6f | |||
5e9780aad7 | |||
f3265484da | |||
4c3f987b85 | |||
5e3fb46159 | |||
2c73e721d0 | |||
431f6e7d21 | |||
c0e16c6d07 | |||
9c0b1e73ef | |||
05842baccb | |||
7f6b0178a8 | |||
8b938add27 | |||
8672112bdc | |||
033ea46754 | |||
24f80bc18c | |||
4b75987f21 | |||
ca487901c2 | |||
e195987436 | |||
350dae0109 | |||
d514c319c0 | |||
4ace9b6806 | |||
64478d4507 | |||
f314538972 | |||
155650961b | |||
b3366131b8 | |||
040b7f3fdc | |||
ef1e435c6b | |||
a9c3a44f37 | |||
3842df13db | |||
be2f7708ad | |||
29a4114e8c | |||
1690d8c1e0 | |||
e84cbe1bb2 | |||
d0af6fd9e8 | |||
bcc7815ae6 | |||
5d87bd044c | |||
e2b4fae83b | |||
019fff5d62 | |||
cf1c495f90 | |||
cee5a28816 | |||
83a6629f03 | |||
5810405f12 | |||
7b765c6d0b | |||
a9acf23a15 | |||
b14a827715 | |||
1cd8386559 | |||
1fa11cd50d | |||
8a7223593d | |||
ccc9ad0c14 | |||
509db151e7 | |||
0bbc4fe4fb | |||
86c311dddf | |||
eaf3554611 | |||
99aaf0303b | |||
6d1df75889 | |||
9c8e396c9a | |||
c9ec11c2df | |||
587d5fa14e | |||
229703a6a6 | |||
5b8f0c4a9e | |||
41d71dac0c | |||
6dfa66dbde | |||
324abc0f61 | |||
3c0fd79195 | |||
0d30f57e83 | |||
247a9d0e5b | |||
d6800dbc46 | |||
ae283cd898 | |||
a8f898822a | |||
da936b325e | |||
8b3ce0f079 | |||
c9ed90acb4 | |||
17a9539e94 | |||
6f717e6ab7 | |||
0f31a5e10a | |||
e0376c78d1 | |||
a6c253b969 | |||
5a607db93e | |||
e7dc1e8e53 | |||
38a5b78295 | |||
034213fccc | |||
020f561773 | |||
2fc26bf4c6 | |||
09530efefd | |||
0624c3be67 | |||
a71fb185bd | |||
98ae142757 | |||
cd150d7fef | |||
3bca049cd8 | |||
95b06a7e7e | |||
81f02ad622 | |||
64d3c241f6 | |||
4a91a5cb5d | |||
226f0ced83 | |||
bc2122dd98 | |||
2a2ff87b3b | |||
7c0d02758b | |||
e6761807b8 | |||
bfb5d6323d | |||
b3a3e19146 | |||
b5f8413ceb | |||
b2554378e7 | |||
1fa5cce5ae | |||
ea31131e0f | |||
675f8ed9b2 | |||
0dcf25e16c | |||
dd46e292c4 | |||
e9888c762e | |||
49295148d1 | |||
d1421d2eb2 | |||
69c2482fc1 | |||
571bbf7595 | |||
e01a96b8fb | |||
2c7d866677 | |||
be2b387391 | |||
06c55a2094 | |||
9a7084078f | |||
f71e9dbe4d | |||
cecca5f9f0 | |||
79be0ebb34 | |||
c7a77a2eb5 | |||
c0c72e40e4 | |||
01bf7144c2 | |||
f5f59b8382 | |||
75a5727a2e | |||
4f0c3f7984 | |||
83e0310ca7 | |||
9cac51bd82 | |||
a434c032a2 | |||
ad0154f431 | |||
080b112608 | |||
92ad051f6a | |||
da8f4acb29 | |||
43ab033aec | |||
83f9d14630 | |||
634335187e | |||
f06ba6e3cd | |||
675b1ef2f1 | |||
a46a85bf71 | |||
3a739e3920 | |||
231babe218 | |||
4532bd1865 | |||
7b9adbf52e | |||
f607403111 | |||
34996f009b | |||
39031ccf3e | |||
4451544389 | |||
5aa750cde7 | |||
11068029e8 | |||
e75c52d9dc | |||
09f9aa2069 | |||
bbe2fd0a5d | |||
2ad0a24483 | |||
0c52831ec6 | |||
55fca3f92f | |||
034174418b | |||
e8f53542e8 | |||
ae52f8737a | |||
888dbe7187 | |||
37f1391a07 | |||
8b8ece3dad | |||
015b3d0f34 | |||
ee57f72ca6 | |||
9497f5c3df | |||
63fe8684c4 | |||
a2fbe471e3 | |||
c6f2981d63 | |||
410ff4fcc1 | |||
7e8e9a3141 | |||
98c24d4704 | |||
c9533db6a1 | |||
a4555ca908 | |||
45e14a7503 | |||
5eaa33ba82 | |||
38eee85752 | |||
2b9adb0395 | |||
5fb042a773 | |||
04ec5b2fd6 | |||
b0ee397994 | |||
200ff1339c | |||
a8e2049d08 | |||
07152cc3be | |||
89816dc062 | |||
3c4e34f1ed | |||
372b616101 | |||
d93d22f7df | |||
6493f33faf | |||
e66a9f885a | |||
fa74e59854 | |||
a15a97370b | |||
3fd7c84774 | |||
428a77b93d | |||
8e8ee81397 | |||
cf12dfee76 | |||
d2fc955260 | |||
4d991629b1 | |||
6111dfe6f9 | |||
921d594931 | |||
ac13d57634 | |||
42d36ff4bb | |||
28da32f2ac | |||
08aff2ffe8 | |||
8b43dee20f | |||
0d2cde37a0 | |||
5cca3cf335 | |||
679560e185 | |||
d157f097d1 | |||
ec6f9b1189 | |||
7825e46321 | |||
6436ca2e9c | |||
81a1a927b1 | |||
b210954874 | |||
472be3c4ed | |||
edc6e6227e | |||
32b3617fed | |||
45bf1e1136 | |||
fb3805b789 | |||
b861db5d1f | |||
e49ed606f5 | |||
8172d1a593 | |||
a413d7d9ca | |||
8ceec846a5 | |||
d2d48905d7 | |||
7d7ae73f5e | |||
82c8d36144 | |||
c50b1da53b | |||
ddd30fc713 | |||
1e28a1795d | |||
b2300f4cfb | |||
92fe6b7146 | |||
fa4ee22100 | |||
5ae6864a3f | |||
fb567e85e9 | |||
2d33663318 | |||
528d3584b8 | |||
69b4d0c88b | |||
e79021da29 | |||
79980683d1 | |||
ec7e0cc93b | |||
15b26d8e39 | |||
377e102650 | |||
4a9e0f0293 | |||
0b18c06058 | |||
8d289f9eb5 | |||
394c1241fb | |||
0c4c0a436c | |||
b8b5beeae1 | |||
be0baac962 | |||
cf0db8b9fa | |||
dd90dd541c | |||
cdd3878e55 | |||
51c26e6b30 | |||
0c2cc0e32b | |||
828b7a2154 | |||
be4f54b99d | |||
25e57eb493 | |||
707a36fe53 | |||
cd58e083bf | |||
a259ea7b18 | |||
74602f60fa | |||
53b8ce44bf | |||
e99205be54 | |||
b52d301b2a | |||
52bd2773c0 | |||
c9768855a5 | |||
1a45e57b7c | |||
cd242596bc | |||
bc6854a8e5 | |||
0d4b26e493 | |||
0e6a1b6591 | |||
84f0a27d86 | |||
8645926006 | |||
960537f8e0 | |||
5d9b7e9e63 | |||
6bf107e1ad | |||
fa7651faf9 | |||
8b2cd7dd1e | |||
5d2dac5b99 | |||
df7102d13d | |||
82b596ec3c | |||
497216cffa | |||
969ff75078 | |||
f932a80e58 | |||
ae6b1cb209 | |||
d0a236e7ee | |||
ed01b28c0d | |||
52e1d1822a | |||
5e9b3db1f2 | |||
e0e1b59777 | |||
443ffaa132 | |||
4b0226838c | |||
79ce8c7790 | |||
0fa4a3c963 | |||
14967e9ad0 | |||
b4573296d8 | |||
0127d65dfc | |||
14e8fa9f03 | |||
adc331d60d | |||
65227e82ac | |||
56ae9ed80e | |||
f3d9af311e | |||
136b7dab66 | |||
cd7a836db0 | |||
e87e5cb47c | |||
7122085590 | |||
9f0484a93b | |||
93038e61e5 | |||
30b43fd27c | |||
6efe1ffbc8 | |||
8898655a49 | |||
07b1422fad | |||
0232d18f1f | |||
0cef44c986 | |||
6ed755e252 | |||
85cd77267b | |||
488a01f8a5 | |||
ee2760b2b5 | |||
84cb1cb520 | |||
db07976403 | |||
c96a3bbd50 | |||
8cbfaf3646 | |||
29c04e62be | |||
1224882092 | |||
df8b64cff9 | |||
ae42fafe84 | |||
da9ebe8867 | |||
0a72e60f68 | |||
36874cbe55 | |||
d94a280437 | |||
470c39cf79 | |||
941a15c4f4 | |||
3caad13577 | |||
acbae86a6b | |||
34d0aaa8e5 | |||
7941303987 | |||
da8b34a867 | |||
9bf362e08e | |||
6ee319b78c | |||
90f1b39db5 | |||
85a6fafd4f | |||
2bdf4be682 | |||
455adf4b11 | |||
9e49566641 | |||
d1f0ab0c73 | |||
79153ae433 | |||
9c44b5189f | |||
312125f182 | |||
652eac5be0 | |||
dba0d47789 | |||
4a3be8b4bf | |||
7f5966a147 | |||
8f8f528823 | |||
1ec7322b18 | |||
8f8c413927 | |||
50c543218b | |||
18af49f1f4 | |||
906a1f1efa | |||
deecb5a912 | |||
02838c5024 | |||
bf7f6973a4 | |||
c0b54e11e1 | |||
0fe01e14e1 | |||
2f71a97327 | |||
e73ea09ffd | |||
bc0c3774eb | |||
b10487ac13 | |||
64ed6cc0ef | |||
b6c2aee17a | |||
5eea985828 | |||
1835e34690 | |||
2d0fa51c40 | |||
108c649a91 | |||
c26a4ca8e8 | |||
9d9c1e2cce | |||
2cacff6b9b | |||
47a43517cb | |||
1b5590bda7 | |||
91a6d76c4b | |||
03964c446a | |||
74897faa78 | |||
2b2ec71edd | |||
a80a860f8d | |||
3d54a0d62c | |||
aabdf4de30 | |||
20c3d69717 | |||
f57c24abe4 | |||
0ae52a13f3 | |||
ece4f343e2 | |||
b2fb562de6 | |||
19c2d0fddc | |||
b2544fab16 | |||
96cbf78e28 | |||
e30a0b867d | |||
11f9e41254 | |||
a45eba3c56 | |||
9bc22baa80 | |||
7bad51d867 | |||
f441b7680a | |||
10368fabe4 | |||
a42a9bc785 | |||
42f152b366 | |||
0ec8d6d6b3 | |||
1eecff0178 | |||
3e72a66297 | |||
79c0c6cf90 | |||
c1724397d3 | |||
9e30783ecb | |||
496eb68078 | |||
2fe45a6b57 | |||
aca66ae86d | |||
f619c78232 | |||
d91432d960 | |||
62c06b7731 | |||
e060e1b107 | |||
08c40de381 | |||
c76bb4d32a | |||
93e58874de | |||
712956d564 | |||
39118d63e5 | |||
9f048b739e | |||
6e818b7f45 | |||
e085754955 | |||
98bf1e455f | |||
9d84398ee7 | |||
09338e9132 | |||
239b0c055c | |||
444e18c1d9 | |||
f1893d9708 | |||
3030b6b908 | |||
cdf9ad0105 | |||
05c6f22f67 | |||
e74fe9d2f5 | |||
c9f5964d5e | |||
e097b02457 | |||
f2fcc8ee93 | |||
513ba4c70f | |||
66e51af8d7 | |||
ba9c41f495 | |||
07ebb3e988 | |||
c915ea871d | |||
656688a5f3 | |||
60c9c86580 | |||
b0c49ca19c | |||
dd2d25d54c | |||
230c80459e | |||
f7915ba6f2 | |||
ac971c5248 | |||
eaa0e517c1 | |||
4e4ac58263 | |||
88e06a0052 | |||
e0e63cf094 | |||
410d45029f | |||
8f3ce089a4 | |||
7e05a4dcc2 | |||
7b9b62b200 | |||
6bdd33da2a | |||
3894ba8312 | |||
473a046ed8 | |||
47e67d5d1a | |||
e62558d607 | |||
6b9770698b | |||
893584696b | |||
a108adfa23 | |||
f00acbe2d6 | |||
563adace2f | |||
cc7046f0ec | |||
b415938583 | |||
2a41106638 | |||
cb046dbde2 | |||
96d3dfdbb4 | |||
fa22f7b493 | |||
e2c6c14af8 | |||
ca62a411e1 | |||
10bb167ff4 | |||
ead6f8b487 | |||
672552a1e8 | |||
f940606f0c | |||
b271fd48a6 | |||
8e00f3a59d | |||
ebc8006e04 | |||
c84e76a869 | |||
a178ac86c6 | |||
1e18344990 | |||
6b31e759c9 | |||
1842e1a30b | |||
dd20c774ed | |||
b2990e8457 | |||
4e48c8c4fa | |||
3605ec6d0b | |||
2151d64f25 | |||
89080171c9 | |||
733d3f3871 | |||
a14d268652 | |||
b5949466e4 | |||
1f9afd07f3 | |||
717c296e52 | |||
b528b8dc17 | |||
98f709c36e | |||
330ac65e18 | |||
e26e9f10b6 | |||
7d6c6fe2a0 | |||
5d0b879623 | |||
1a182a62a7 | |||
429c9861e3 | |||
99a5907e1b | |||
1e42d431c8 | |||
05a3d0e729 | |||
4b4a259f8a | |||
454324aba6 | |||
470795f21e | |||
95abe28322 | |||
e35373a114 | |||
ea1d342db5 | |||
83a200871d | |||
0f6c998109 | |||
440a999c2e | |||
7711765563 | |||
7efa180bbf | |||
84e4e6ce1b | |||
972d96c0e0 | |||
3bcb0442ca | |||
2afbe84afd | |||
06f07978dc | |||
2dde6f8a7d | |||
c8789dc267 | |||
ed16fd8d45 | |||
dd74e85626 | |||
ab19e9db08 | |||
98f3026a74 | |||
1ba302999d | |||
197a1f8326 | |||
9cc491a05e | |||
bcd28acfc5 | |||
d99f1e7595 | |||
1fb95adfbf | |||
e8cc479a1e | |||
fb29c90691 | |||
3027c8a4a8 | |||
0b6269edd6 | |||
12d05dd71a | |||
ecf3bce14b | |||
5563902b69 | |||
874ad521a7 | |||
a9f5e48bb2 | |||
cbc7555cf2 | |||
e08161aec2 | |||
7754377ecb | |||
0b2a0d4ea9 | |||
94f54366a7 | |||
f5dc15659f | |||
1a21a23e73 | |||
4533ea0b26 | |||
540a82fe22 | |||
467763f8a6 | |||
99f2adf491 | |||
3fa8e7d8e8 | |||
9080f45601 | |||
08348fd349 | |||
e109c6a06c | |||
457c2680b6 | |||
a85f0e28c6 | |||
b611d02a2c | |||
e51159e7f3 | |||
2391ca14ca | |||
212c779552 | |||
0c2243a4f3 | |||
3981b8471a | |||
276a14a80c | |||
b4949eaea2 | |||
993b625042 | |||
557b27ef77 | |||
45b04105f1 | |||
ae4530b5d2 | |||
00fd23895d | |||
aa67b941d4 | |||
b94bf01dee | |||
ba94f439bb | |||
e3af767246 | |||
b0f5bdf668 | |||
|
2b5d650ef8 | ||
e9e16dd2b1 | |||
6b9be7e4d0 | |||
|
8b528d8659 | ||
9c81836648 | |||
c04c85b999 | |||
6953cd08e6 | |||
c82576e4dd | |||
|
b70ff95f03 | ||
30b6afb601 | |||
6ca086d85b | |||
bfb8500386 | |||
70bfe2bab7 | |||
7d1c8c383c | |||
3b876f2c42 | |||
56f9fa28aa | |||
2577bad0af | |||
ed1e888c58 | |||
254afc990e | |||
8d1986d13b | |||
5d2cad4690 | |||
49b3f507a6 | |||
912f1b2ff2 | |||
f75016ca69 | |||
|
4e1bcd962a | ||
69f35d222c | |||
898905c21c | |||
f26adc1c28 | |||
dfe91d0f1c | |||
05912cd540 | |||
ab955f5154 | |||
8343fa9182 | |||
406c7c13cb | |||
|
cd8185960b | ||
87f5324761 | |||
a18f1926d9 | |||
17a335275c | |||
ff13f2ce05 | |||
4937d4c099 | |||
1de8b46657 | |||
6aa51e5915 | |||
8936944743 | |||
76672d5a60 | |||
|
025bc4ef64 | ||
a741526da1 | |||
799a3652d4 | |||
2f8886c28f | |||
c3643565e3 | |||
28e03f6fb2 | |||
4ef2d6df57 | |||
779f4a00eb | |||
d281c26a1c | |||
77399e3fa4 | |||
5f5bf66c75 | |||
385037b669 | |||
fee09c50ae | |||
285a132d35 | |||
0b79ac3596 | |||
de444857dd | |||
d231b39fdc | |||
d5c76a0f01 | |||
485ba46838 | |||
7f2000f180 | |||
34acf00b5e | |||
bc6aed51c8 | |||
011bdda777 | |||
|
80f83e5f53 | ||
b687dcf2a8 | |||
bb3e5a643f | |||
|
4dddd3238c | ||
a8e3c594e3 | |||
728850da08 | |||
443802332b | |||
|
bf6550d0d9 | ||
37ef0f5621 | |||
38cfaa023d | |||
f3772528c5 | |||
dbbea163d1 | |||
7269c5c393 | |||
|
7e695edd29 | ||
6b322d18da | |||
be96e2c6af | |||
5de88986ed | |||
b68cbe5a26 | |||
2462e09205 | |||
acb0a1e8a2 | |||
a73e150ee3 | |||
b8f753a424 | |||
23c16fc9c3 | |||
082b5296b9 | |||
f87dd2636d | |||
60109b3531 | |||
e2731db7ea | |||
156461a0b9 | |||
8a68559f0e | |||
97a5abe665 | |||
d93f5a82a0 | |||
9d139a7d1c | |||
e53f9abf3f | |||
c5f3093a78 | |||
4622b6efa9 | |||
3e39aa0277 | |||
3e53ec1526 | |||
2abb5f3c3a |
158
.gitignore
vendored
158
.gitignore
vendored
@ -1,21 +1,157 @@
|
|||||||
|
|
||||||
|
# Created by https://www.gitignore.io/api/macos,jetbrains+all
|
||||||
|
|
||||||
|
### JetBrains+all ###
|
||||||
|
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
|
||||||
|
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||||
|
|
||||||
|
# User-specific stuff
|
||||||
|
.idea/**/workspace.xml
|
||||||
|
.idea/**/tasks.xml
|
||||||
|
.idea/**/usage.statistics.xml
|
||||||
|
.idea/**/dictionaries
|
||||||
|
.idea/**/shelf
|
||||||
|
|
||||||
|
# Generated files
|
||||||
|
.idea/**/contentModel.xml
|
||||||
|
|
||||||
|
# Sensitive or high-churn files
|
||||||
|
.idea/**/dataSources/
|
||||||
|
.idea/**/dataSources.ids
|
||||||
|
.idea/**/dataSources.local.xml
|
||||||
|
.idea/**/sqlDataSources.xml
|
||||||
|
.idea/**/dynamic.xml
|
||||||
|
.idea/**/uiDesigner.xml
|
||||||
|
.idea/**/dbnavigator.xml
|
||||||
|
|
||||||
|
# Gradle
|
||||||
|
.idea/**/gradle.xml
|
||||||
|
.idea/**/libraries
|
||||||
|
|
||||||
|
# Gradle and Maven with auto-import
|
||||||
|
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||||
|
# since they will be recreated, and may cause churn. Uncomment if using
|
||||||
|
# auto-import.
|
||||||
|
# .idea/modules.xml
|
||||||
|
# .idea/*.iml
|
||||||
|
# .idea/modules
|
||||||
|
|
||||||
|
# CMake
|
||||||
|
cmake-build-*/
|
||||||
|
|
||||||
|
# Mongo Explorer plugin
|
||||||
|
.idea/**/mongoSettings.xml
|
||||||
|
|
||||||
|
# File-based project format
|
||||||
|
*.iws
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
out/
|
||||||
|
|
||||||
|
# mpeltonen/sbt-idea plugin
|
||||||
|
.idea_modules/
|
||||||
|
|
||||||
|
# JIRA plugin
|
||||||
|
atlassian-ide-plugin.xml
|
||||||
|
|
||||||
|
# Cursive Clojure plugin
|
||||||
|
.idea/replstate.xml
|
||||||
|
|
||||||
|
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||||
|
com_crashlytics_export_strings.xml
|
||||||
|
crashlytics.properties
|
||||||
|
crashlytics-build.properties
|
||||||
|
fabric.properties
|
||||||
|
|
||||||
|
# Editor-based Rest Client
|
||||||
|
.idea/httpRequests
|
||||||
|
|
||||||
|
# Android studio 3.1+ serialized cache file
|
||||||
|
.idea/caches/build_file_checksums.ser
|
||||||
|
|
||||||
|
### JetBrains+all Patch ###
|
||||||
|
# Ignores the whole .idea folder and all .iml files
|
||||||
|
# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360
|
||||||
|
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
|
||||||
|
|
||||||
|
*.iml
|
||||||
|
modules.xml
|
||||||
|
.idea/misc.xml
|
||||||
|
*.ipr
|
||||||
|
|
||||||
|
### macOS ###
|
||||||
|
# General
|
||||||
|
.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
|
||||||
|
# Icon must end with two \r
|
||||||
|
Icon
|
||||||
|
|
||||||
|
# Thumbnails
|
||||||
|
._*
|
||||||
|
|
||||||
|
# Files that might appear in the root of a volume
|
||||||
|
.DocumentRevisions-V100
|
||||||
|
.fseventsd
|
||||||
|
.Spotlight-V100
|
||||||
|
.TemporaryItems
|
||||||
|
.Trashes
|
||||||
|
.VolumeIcon.icns
|
||||||
|
.com.apple.timemachine.donotpresent
|
||||||
|
|
||||||
|
# Directories potentially created on remote AFP share
|
||||||
|
.AppleDB
|
||||||
|
.AppleDesktop
|
||||||
|
Network Trash Folder
|
||||||
|
Temporary Items
|
||||||
|
.apdisk
|
||||||
|
|
||||||
|
|
||||||
|
# End of https://www.gitignore.io/api/macos,jetbrains+all
|
||||||
|
|
||||||
.codelite
|
.codelite
|
||||||
|
.phing_targets
|
||||||
|
.sonar/
|
||||||
*.phprj
|
*.phprj
|
||||||
*.workspace
|
*.workspace
|
||||||
vendor
|
vendor
|
||||||
app/cache/*
|
**/cache/**
|
||||||
app/logs/*
|
**/logs/**
|
||||||
public/images/*
|
**/coverage/**
|
||||||
public/js/cache/*
|
**/docs/**
|
||||||
|
**/node_modules/**
|
||||||
composer.lock
|
composer.lock
|
||||||
*.sqlite
|
*.sqlite
|
||||||
*.db
|
*.db
|
||||||
*.sqlite3
|
*.sqlite3
|
||||||
docs/*
|
apidocs/**
|
||||||
coverage/*
|
|
||||||
tests/test_data/sessions/*
|
tests/test_data/sessions/*
|
||||||
build/coverage/*
|
|
||||||
build/logs/*
|
|
||||||
build/pdepend/*
|
|
||||||
build/phpdox/*
|
|
||||||
cache.properties
|
cache.properties
|
||||||
tests/test_data/cache/*
|
build/**
|
||||||
|
!build/*.txt
|
||||||
|
!build/*.xml
|
||||||
|
!build/*.php
|
||||||
|
app/config/*.toml
|
||||||
|
!app/config/*.toml.example
|
||||||
|
phinx.yml
|
||||||
|
Caddyfile
|
||||||
|
build/humbuglog.txt
|
||||||
|
public/images/anime/**
|
||||||
|
public/images/avatars/**
|
||||||
|
public/images/manga/**
|
||||||
|
public/images/characters/**
|
||||||
|
public/images/people/**
|
||||||
|
public/mal_mappings.json
|
||||||
|
.phpunit.result.cache
|
||||||
|
|
||||||
|
.is-dev
|
||||||
|
|
||||||
|
tmp
|
||||||
|
tools/vendor/
|
||||||
|
tools/phinx/vendor/
|
||||||
|
/.php-cs-fixer.php
|
||||||
|
/.php-cs-fixer.cache
|
6
.htaccess
Normal file
6
.htaccess
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#Rewrite index.php out of the app urls
|
||||||
|
RewriteEngine On
|
||||||
|
RewriteBase /
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-f
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-d
|
||||||
|
RewriteRule ^(.*)$ index.php/$1 [L]
|
534
.php-cs-fixer.dist.php
Normal file
534
.php-cs-fixer.dist.php
Normal file
@ -0,0 +1,534 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
use Nexus\CsConfig\Factory;
|
||||||
|
use PhpCsFixer\{Config, Finder};
|
||||||
|
|
||||||
|
$finder = Finder::create()
|
||||||
|
->in([
|
||||||
|
__DIR__ . '/src',
|
||||||
|
__DIR__ . '/tests',
|
||||||
|
__DIR__ . '/tools',
|
||||||
|
])
|
||||||
|
->exclude([
|
||||||
|
'vendor',
|
||||||
|
]);
|
||||||
|
|
||||||
|
return (new Config())
|
||||||
|
->setRiskyAllowed(TRUE)
|
||||||
|
->setFinder($finder)
|
||||||
|
->setIndent(' ')
|
||||||
|
->setRules([
|
||||||
|
'align_multiline_comment' => false,
|
||||||
|
'array_indentation' => true,
|
||||||
|
'array_push' => true,
|
||||||
|
'array_syntax' => ['syntax' => 'short'],
|
||||||
|
'assign_null_coalescing_to_coalesce_equal' => true,
|
||||||
|
'backtick_to_shell_exec' => true,
|
||||||
|
'binary_operator_spaces' => [
|
||||||
|
'default' => 'single_space',
|
||||||
|
'operators' => [
|
||||||
|
'=' => NULL,
|
||||||
|
'&' => NULL,
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'blank_line_after_namespace' => true,
|
||||||
|
'blank_line_after_opening_tag' => false,
|
||||||
|
'blank_line_before_statement' => [
|
||||||
|
'statements' => [
|
||||||
|
// 'case',
|
||||||
|
'continue',
|
||||||
|
'declare',
|
||||||
|
'default',
|
||||||
|
'do',
|
||||||
|
'exit',
|
||||||
|
'for',
|
||||||
|
'foreach',
|
||||||
|
'goto',
|
||||||
|
'return',
|
||||||
|
'switch',
|
||||||
|
'throw',
|
||||||
|
'try',
|
||||||
|
'while',
|
||||||
|
'yield',
|
||||||
|
'yield_from',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
// 'braces' => [
|
||||||
|
// 'allow_single_line_anonymous_class_with_empty_body' => true,
|
||||||
|
// 'allow_single_line_closure' => true,
|
||||||
|
// 'position_after_anonymous_constructs' => 'same',
|
||||||
|
// 'position_after_control_structures' => 'next',
|
||||||
|
// 'position_after_functions_and_oop_constructs' => 'next',
|
||||||
|
// ],
|
||||||
|
'cast_spaces' => ['space' => 'single'],
|
||||||
|
'class_attributes_separation' => [
|
||||||
|
'elements' => [
|
||||||
|
'const' => 'none',
|
||||||
|
'property' => 'none',
|
||||||
|
'method' => 'one',
|
||||||
|
'trait_import' => 'none',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'class_definition' => [
|
||||||
|
'multi_line_extends_each_single_line' => true,
|
||||||
|
'single_item_single_line' => true,
|
||||||
|
'single_line' => true,
|
||||||
|
'space_before_parenthesis' => true,
|
||||||
|
],
|
||||||
|
'class_reference_name_casing' => true,
|
||||||
|
'clean_namespace' => true,
|
||||||
|
'combine_consecutive_issets' => true,
|
||||||
|
'combine_consecutive_unsets' => true,
|
||||||
|
'combine_nested_dirname' => true,
|
||||||
|
'comment_to_phpdoc' => [
|
||||||
|
'ignored_tags' => [
|
||||||
|
'todo',
|
||||||
|
'codeCoverageIgnore',
|
||||||
|
'codeCoverageIgnoreStart',
|
||||||
|
'codeCoverageIgnoreEnd',
|
||||||
|
'phpstan-ignore-line',
|
||||||
|
'phpstan-ignore-next-line',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'compact_nullable_typehint' => true,
|
||||||
|
'concat_space' => ['spacing' => 'one'],
|
||||||
|
'constant_case' => ['case' => 'upper'],
|
||||||
|
'control_structure_braces' => true,
|
||||||
|
'control_structure_continuation_position' => ['position' => 'next_line'],
|
||||||
|
'curly_braces_position' => [
|
||||||
|
'allow_single_line_anonymous_functions' => true,
|
||||||
|
'allow_single_line_empty_anonymous_classes' => true,
|
||||||
|
'anonymous_functions_opening_brace' => 'same_line',
|
||||||
|
'classes_opening_brace' => 'next_line_unless_newline_at_signature_end',
|
||||||
|
'control_structures_opening_brace' => 'next_line_unless_newline_at_signature_end',
|
||||||
|
'functions_opening_brace' => 'next_line_unless_newline_at_signature_end',
|
||||||
|
],
|
||||||
|
'date_time_immutable' => false,
|
||||||
|
'declare_equal_normalize' => ['space' => 'none'],
|
||||||
|
'declare_parentheses' => true,
|
||||||
|
'declare_strict_types' => true,
|
||||||
|
'dir_constant' => true,
|
||||||
|
'doctrine_annotation_array_assignment' => false,
|
||||||
|
'doctrine_annotation_braces' => false,
|
||||||
|
'doctrine_annotation_indentation' => false,
|
||||||
|
'doctrine_annotation_spaces' => false,
|
||||||
|
'echo_tag_syntax' => [
|
||||||
|
'format' => 'short',
|
||||||
|
'long_function' => 'echo',
|
||||||
|
'shorten_simple_statements_only' => false,
|
||||||
|
],
|
||||||
|
'elseif' => false,
|
||||||
|
'empty_loop_body' => ['style' => 'braces'],
|
||||||
|
'empty_loop_condition' => ['style' => 'while'],
|
||||||
|
'encoding' => true,
|
||||||
|
'error_suppression' => [
|
||||||
|
'mute_deprecation_error' => true,
|
||||||
|
'noise_remaining_usages' => false,
|
||||||
|
'noise_remaining_usages_exclude' => [],
|
||||||
|
],
|
||||||
|
'escape_implicit_backslashes' => [
|
||||||
|
'double_quoted' => false,
|
||||||
|
'heredoc_syntax' => false,
|
||||||
|
'single_quoted' => false,
|
||||||
|
],
|
||||||
|
'explicit_indirect_variable' => false,
|
||||||
|
'explicit_string_variable' => false,
|
||||||
|
'final_class' => false,
|
||||||
|
'final_internal_class' => [
|
||||||
|
'annotation_exclude' => ['@no-final'],
|
||||||
|
'annotation_include' => ['@internal'],
|
||||||
|
'consider_absent_docblock_as_internal_class' => false,
|
||||||
|
],
|
||||||
|
'final_public_method_for_abstract_class' => false,
|
||||||
|
'fopen_flag_order' => true,
|
||||||
|
'fopen_flags' => ['b_mode' => true],
|
||||||
|
'full_opening_tag' => true,
|
||||||
|
'fully_qualified_strict_types' => true,
|
||||||
|
'function_declaration' => ['closure_function_spacing' => 'one'],
|
||||||
|
'function_to_constant' => [
|
||||||
|
'functions' => [
|
||||||
|
'get_called_class',
|
||||||
|
'get_class',
|
||||||
|
'get_class_this',
|
||||||
|
'php_sapi_name',
|
||||||
|
'phpversion',
|
||||||
|
'pi',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'function_typehint_space' => true,
|
||||||
|
'general_phpdoc_annotation_remove' => false,
|
||||||
|
'general_phpdoc_tag_rename' => false,
|
||||||
|
'get_class_to_class_keyword' => false,
|
||||||
|
'global_namespace_import' => [
|
||||||
|
'import_constants' => true,
|
||||||
|
'import_functions' => true,
|
||||||
|
'import_classes' => true,
|
||||||
|
],
|
||||||
|
'group_import' => true,
|
||||||
|
'header_comment' => false, // false by default
|
||||||
|
// 'heredoc_indentation' => ['indentation' => 'start_plus_one'],
|
||||||
|
'heredoc_to_nowdoc' => true,
|
||||||
|
'implode_call' => true,
|
||||||
|
'include' => true,
|
||||||
|
'increment_style' => ['style' => 'post'],
|
||||||
|
'indentation_type' => true,
|
||||||
|
'integer_literal_case' => true,
|
||||||
|
'is_null' => true,
|
||||||
|
'lambda_not_used_import' => true,
|
||||||
|
'line_ending' => true,
|
||||||
|
'linebreak_after_opening_tag' => false,
|
||||||
|
'list_syntax' => ['syntax' => 'short'],
|
||||||
|
'logical_operators' => true,
|
||||||
|
'lowercase_cast' => true,
|
||||||
|
'lowercase_keywords' => true,
|
||||||
|
'lowercase_static_reference' => true,
|
||||||
|
'magic_constant_casing' => true,
|
||||||
|
'magic_method_casing' => true,
|
||||||
|
'mb_str_functions' => false,
|
||||||
|
'method_argument_space' => [
|
||||||
|
'after_heredoc' => false,
|
||||||
|
'keep_multiple_spaces_after_comma' => false,
|
||||||
|
'on_multiline' => 'ensure_fully_multiline',
|
||||||
|
],
|
||||||
|
'method_chaining_indentation' => true,
|
||||||
|
'modernize_strpos' => false, // requires 8.0+
|
||||||
|
'modernize_types_casting' => true,
|
||||||
|
'multiline_comment_opening_closing' => true,
|
||||||
|
'multiline_whitespace_before_semicolons' => ['strategy' => 'no_multi_line'],
|
||||||
|
'native_constant_invocation' => false,
|
||||||
|
'native_function_casing' => true,
|
||||||
|
'native_function_invocation' => false,
|
||||||
|
'native_function_type_declaration_casing' => true,
|
||||||
|
'new_with_braces' => true,
|
||||||
|
'no_alias_functions' => ['sets' => ['@all']],
|
||||||
|
'no_alias_language_construct_call' => true,
|
||||||
|
'no_alternative_syntax' => ['fix_non_monolithic_code' => false],
|
||||||
|
'no_binary_string' => true,
|
||||||
|
'no_blank_lines_after_class_opening' => true,
|
||||||
|
'no_blank_lines_after_phpdoc' => true,
|
||||||
|
'no_blank_lines_before_namespace' => false, // conflicts with `single_blank_line_before_namespace`
|
||||||
|
'no_break_comment' => ['comment_text' => 'no break'],
|
||||||
|
'no_closing_tag' => true,
|
||||||
|
'no_empty_comment' => true,
|
||||||
|
'no_empty_phpdoc' => true,
|
||||||
|
'no_empty_statement' => true,
|
||||||
|
'no_extra_blank_lines' => ['tokens' => ['extra']],
|
||||||
|
'no_homoglyph_names' => true,
|
||||||
|
'no_leading_import_slash' => true,
|
||||||
|
'no_leading_namespace_whitespace' => true,
|
||||||
|
'no_mixed_echo_print' => ['use' => 'echo'],
|
||||||
|
'no_multiline_whitespace_around_double_arrow' => true,
|
||||||
|
'no_null_property_initialization' => true,
|
||||||
|
'no_short_bool_cast' => true,
|
||||||
|
'no_singleline_whitespace_before_semicolons' => true,
|
||||||
|
'no_space_around_double_colon' => true,
|
||||||
|
'no_spaces_after_function_name' => true,
|
||||||
|
'no_spaces_around_offset' => ['positions' => ['inside', 'outside']],
|
||||||
|
'no_spaces_inside_parenthesis' => true,
|
||||||
|
'no_superfluous_elseif' => true,
|
||||||
|
'no_superfluous_phpdoc_tags' => [
|
||||||
|
'allow_mixed' => true,
|
||||||
|
'allow_unused_params' => true,
|
||||||
|
'remove_inheritdoc' => false,
|
||||||
|
],
|
||||||
|
'no_trailing_comma_in_singleline' => true,
|
||||||
|
'no_trailing_whitespace' => true,
|
||||||
|
'no_trailing_whitespace_in_comment' => true,
|
||||||
|
'no_trailing_whitespace_in_string' => true,
|
||||||
|
'no_unneeded_control_parentheses' => [
|
||||||
|
'statements' => [
|
||||||
|
'break',
|
||||||
|
'clone',
|
||||||
|
'continue',
|
||||||
|
'echo_print',
|
||||||
|
'return',
|
||||||
|
'switch_case',
|
||||||
|
'yield',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'no_unneeded_curly_braces' => ['namespaces' => true],
|
||||||
|
'no_unneeded_final_method' => ['private_methods' => true],
|
||||||
|
'no_unneeded_import_alias' => true,
|
||||||
|
'no_unreachable_default_argument_value' => true,
|
||||||
|
'no_unset_cast' => true,
|
||||||
|
'no_unset_on_property' => false,
|
||||||
|
'no_unused_imports' => true,
|
||||||
|
'no_useless_else' => true,
|
||||||
|
'no_useless_return' => true,
|
||||||
|
'no_useless_sprintf' => true,
|
||||||
|
'no_whitespace_before_comma_in_array' => ['after_heredoc' => true],
|
||||||
|
'no_whitespace_in_blank_line' => true,
|
||||||
|
'non_printable_character' => ['use_escape_sequences_in_strings' => true],
|
||||||
|
'normalize_index_brace' => true,
|
||||||
|
'not_operator_with_space' => true,
|
||||||
|
'not_operator_with_successor_space' => true,
|
||||||
|
'nullable_type_declaration_for_default_null_value' => ['use_nullable_type_declaration' => true],
|
||||||
|
'object_operator_without_whitespace' => true,
|
||||||
|
'operator_linebreak' => ['only_booleans' => true, 'position' => 'beginning'],
|
||||||
|
'ordered_class_elements' => [
|
||||||
|
'order' => [
|
||||||
|
'use_trait',
|
||||||
|
'case',
|
||||||
|
'constant_public',
|
||||||
|
'constant_protected',
|
||||||
|
'constant_private',
|
||||||
|
'property_public',
|
||||||
|
'property_protected',
|
||||||
|
'property_private',
|
||||||
|
'construct',
|
||||||
|
'destruct',
|
||||||
|
'magic',
|
||||||
|
],
|
||||||
|
'sort_algorithm' => 'none',
|
||||||
|
],
|
||||||
|
'ordered_imports' => [
|
||||||
|
'sort_algorithm' => 'alpha',
|
||||||
|
'imports_order' => ['class', 'function', 'const'],
|
||||||
|
],
|
||||||
|
'ordered_interfaces' => false,
|
||||||
|
'ordered_traits' => false,
|
||||||
|
'php_unit_construct' => [
|
||||||
|
'assertions' => [
|
||||||
|
'assertSame',
|
||||||
|
'assertEquals',
|
||||||
|
'assertNotEquals',
|
||||||
|
'assertNotSame',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'php_unit_dedicate_assert' => ['target' => 'newest'],
|
||||||
|
'php_unit_dedicate_assert_internal_type' => ['target' => 'newest'],
|
||||||
|
'php_unit_expectation' => ['target' => 'newest'],
|
||||||
|
'php_unit_fqcn_annotation' => true,
|
||||||
|
'php_unit_internal_class' => ['types' => ['final']],
|
||||||
|
'php_unit_method_casing' => ['case' => 'camel_case'],
|
||||||
|
'php_unit_mock' => ['target' => 'newest'],
|
||||||
|
'php_unit_mock_short_will_return' => true,
|
||||||
|
'php_unit_namespaced' => ['target' => 'newest'],
|
||||||
|
'php_unit_no_expectation_annotation' => [
|
||||||
|
'target' => 'newest',
|
||||||
|
'use_class_const' => true,
|
||||||
|
],
|
||||||
|
'php_unit_set_up_tear_down_visibility' => true,
|
||||||
|
'php_unit_size_class' => false,
|
||||||
|
// 'php_unit_strict' => [
|
||||||
|
// 'assertions' => [
|
||||||
|
// 'assertAttributeEquals',
|
||||||
|
// 'assertAttributeNotEquals',
|
||||||
|
// 'assertEquals',
|
||||||
|
// 'assertNotEquals',
|
||||||
|
// ],
|
||||||
|
// ],
|
||||||
|
'php_unit_test_annotation' => ['style' => 'prefix'],
|
||||||
|
'php_unit_test_case_static_method_calls' => [
|
||||||
|
'call_type' => 'this',
|
||||||
|
'methods' => [],
|
||||||
|
],
|
||||||
|
'php_unit_test_class_requires_covers' => false,
|
||||||
|
'phpdoc_add_missing_param_annotation' => ['only_untyped' => true],
|
||||||
|
'phpdoc_align' => [
|
||||||
|
'align' => 'left'
|
||||||
|
],
|
||||||
|
'phpdoc_annotation_without_dot' => false,
|
||||||
|
'phpdoc_indent' => true,
|
||||||
|
'phpdoc_inline_tag_normalizer' => [
|
||||||
|
'tags' => [
|
||||||
|
'example',
|
||||||
|
'id',
|
||||||
|
'internal',
|
||||||
|
'inheritdoc',
|
||||||
|
'inheritdocs',
|
||||||
|
'link',
|
||||||
|
'source',
|
||||||
|
'toc',
|
||||||
|
'tutorial',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'phpdoc_line_span' => [
|
||||||
|
'const' => 'multi',
|
||||||
|
'method' => 'multi',
|
||||||
|
'property' => 'multi',
|
||||||
|
],
|
||||||
|
'phpdoc_no_access' => true,
|
||||||
|
'phpdoc_no_empty_return' => false,
|
||||||
|
'phpdoc_no_package' => false,
|
||||||
|
'phpdoc_no_useless_inheritdoc' => true,
|
||||||
|
'phpdoc_order' => true,
|
||||||
|
'phpdoc_order_by_value' => [
|
||||||
|
'annotations' => [
|
||||||
|
'author',
|
||||||
|
'covers',
|
||||||
|
'coversNothing',
|
||||||
|
'dataProvider',
|
||||||
|
'depends',
|
||||||
|
'group',
|
||||||
|
'internal',
|
||||||
|
'method',
|
||||||
|
'property',
|
||||||
|
'property-read',
|
||||||
|
'property-write',
|
||||||
|
'requires',
|
||||||
|
'throws',
|
||||||
|
'uses',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'phpdoc_return_self_reference' => [
|
||||||
|
'replacements' => [
|
||||||
|
'this' => '$this',
|
||||||
|
'@this' => '$this',
|
||||||
|
'$self' => 'self',
|
||||||
|
'@self' => 'self',
|
||||||
|
'$static' => 'static',
|
||||||
|
'@static' => 'static',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'phpdoc_scalar' => [
|
||||||
|
'types' => [
|
||||||
|
'boolean',
|
||||||
|
'callback',
|
||||||
|
'double',
|
||||||
|
'integer',
|
||||||
|
'real',
|
||||||
|
'str',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'phpdoc_separation' => false,
|
||||||
|
'phpdoc_single_line_var_spacing' => true,
|
||||||
|
'phpdoc_summary' => false,
|
||||||
|
'phpdoc_tag_casing' => ['tags' => ['inheritDoc']],
|
||||||
|
'phpdoc_tag_type' => ['tags' => ['inheritDoc' => 'inline']],
|
||||||
|
'phpdoc_to_comment' => false,
|
||||||
|
'phpdoc_to_param_type' => false,
|
||||||
|
'phpdoc_to_property_type' => false,
|
||||||
|
'phpdoc_to_return_type' => false,
|
||||||
|
'phpdoc_trim' => true,
|
||||||
|
'phpdoc_trim_consecutive_blank_line_separation' => true,
|
||||||
|
'phpdoc_types' => ['groups' => ['simple', 'alias', 'meta']],
|
||||||
|
'phpdoc_types_order' => [
|
||||||
|
'null_adjustment' => 'always_last',
|
||||||
|
'sort_algorithm' => 'alpha',
|
||||||
|
],
|
||||||
|
'phpdoc_var_annotation_correct_order' => true,
|
||||||
|
'phpdoc_var_without_name' => true,
|
||||||
|
'pow_to_exponentiation' => true,
|
||||||
|
'protected_to_private' => true,
|
||||||
|
'psr_autoloading' => ['dir' => null],
|
||||||
|
'random_api_migration' => [
|
||||||
|
'replacements' => [
|
||||||
|
'getrandmax' => 'mt_getrandmax',
|
||||||
|
'rand' => 'mt_rand',
|
||||||
|
'srand' => 'mt_srand',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'regular_callable_call' => true,
|
||||||
|
'return_assignment' => true,
|
||||||
|
'return_type_declaration' => ['space_before' => 'none'],
|
||||||
|
'self_accessor' => false,
|
||||||
|
'self_static_accessor' => true,
|
||||||
|
'semicolon_after_instruction' => false,
|
||||||
|
'set_type_to_cast' => true,
|
||||||
|
'short_scalar_cast' => true,
|
||||||
|
'simple_to_complex_string_variable' => true,
|
||||||
|
'simplified_if_return' => true,
|
||||||
|
'simplified_null_return' => false,
|
||||||
|
'single_blank_line_at_eof' => true,
|
||||||
|
'single_blank_line_before_namespace' => true,
|
||||||
|
'single_class_element_per_statement' => ['elements' => ['const', 'property']],
|
||||||
|
'single_import_per_statement' => false,
|
||||||
|
'single_line_after_imports' => true,
|
||||||
|
'single_line_comment_style' => ['comment_types' => ['asterisk', 'hash']],
|
||||||
|
'single_line_throw' => false,
|
||||||
|
'single_quote' => ['strings_containing_single_quote_chars' => false],
|
||||||
|
'single_space_around_construct' => [
|
||||||
|
'constructs_followed_by_a_single_space' => [
|
||||||
|
'abstract',
|
||||||
|
'as',
|
||||||
|
'attribute',
|
||||||
|
'break',
|
||||||
|
'case',
|
||||||
|
'catch',
|
||||||
|
'class',
|
||||||
|
'clone',
|
||||||
|
'comment',
|
||||||
|
'const',
|
||||||
|
'const_import',
|
||||||
|
'continue',
|
||||||
|
'do',
|
||||||
|
'echo',
|
||||||
|
'else',
|
||||||
|
'elseif',
|
||||||
|
'extends',
|
||||||
|
'final',
|
||||||
|
'finally',
|
||||||
|
'for',
|
||||||
|
'foreach',
|
||||||
|
'function',
|
||||||
|
'function_import',
|
||||||
|
'global',
|
||||||
|
'goto',
|
||||||
|
'if',
|
||||||
|
'implements',
|
||||||
|
'include',
|
||||||
|
'include_once',
|
||||||
|
'instanceof',
|
||||||
|
'insteadof',
|
||||||
|
'interface',
|
||||||
|
'match',
|
||||||
|
'named_argument',
|
||||||
|
'new',
|
||||||
|
'open_tag_with_echo',
|
||||||
|
'php_doc',
|
||||||
|
'php_open',
|
||||||
|
'print',
|
||||||
|
'private',
|
||||||
|
'protected',
|
||||||
|
'public',
|
||||||
|
'require',
|
||||||
|
'require_once',
|
||||||
|
'return',
|
||||||
|
'static',
|
||||||
|
'throw',
|
||||||
|
'trait',
|
||||||
|
'try',
|
||||||
|
'use',
|
||||||
|
'use_lambda',
|
||||||
|
'use_trait',
|
||||||
|
'var',
|
||||||
|
'while',
|
||||||
|
'yield',
|
||||||
|
'yield_from',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'single_trait_insert_per_statement' => true,
|
||||||
|
'space_after_semicolon' => ['remove_in_empty_for_expressions' => true],
|
||||||
|
'standardize_increment' => true,
|
||||||
|
'standardize_not_equals' => true,
|
||||||
|
'statement_indentation' => true,
|
||||||
|
'static_lambda' => true,
|
||||||
|
'strict_comparison' => true,
|
||||||
|
'strict_param' => true,
|
||||||
|
'string_length_to_empty' => true,
|
||||||
|
'string_line_ending' => true,
|
||||||
|
'switch_case_semicolon_to_colon' => true,
|
||||||
|
'switch_case_space' => true,
|
||||||
|
'switch_continue_to_break' => true,
|
||||||
|
'ternary_operator_spaces' => true,
|
||||||
|
'ternary_to_elvis_operator' => true,
|
||||||
|
'ternary_to_null_coalescing' => true,
|
||||||
|
'trailing_comma_in_multiline' => [
|
||||||
|
'after_heredoc' => true,
|
||||||
|
'elements' => ['arrays'],
|
||||||
|
],
|
||||||
|
'trim_array_spaces' => true,
|
||||||
|
'types_spaces' => ['space' => 'none'],
|
||||||
|
'unary_operator_spaces' => false,
|
||||||
|
'use_arrow_functions' => true,
|
||||||
|
'visibility_required' => ['elements' => ['const', 'method', 'property']],
|
||||||
|
'void_return' => false, // changes method signature
|
||||||
|
'whitespace_after_comma_in_array' => true,
|
||||||
|
'yoda_style' => [
|
||||||
|
'equal' => false,
|
||||||
|
'identical' => null,
|
||||||
|
'less_and_greater' => false,
|
||||||
|
'always_move_variable' => false,
|
||||||
|
],
|
||||||
|
]);
|
20
.travis.yml
20
.travis.yml
@ -1,23 +1,17 @@
|
|||||||
language: php
|
language: php
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- composer install
|
- composer install --ignore-platform-reqs
|
||||||
|
|
||||||
php:
|
php:
|
||||||
- 5.5
|
- 8.0
|
||||||
- 5.6
|
- 8.1
|
||||||
- 7
|
|
||||||
- hhvm
|
|
||||||
- nightly
|
- nightly
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- mkdir -p build/logs
|
- mkdir -p build/logs
|
||||||
- phpunit -c build
|
- php vendor/bin/phpunit -c build
|
||||||
|
|
||||||
after_script:
|
#matrix:
|
||||||
- wget https://scrutinizer-ci.com/ocular.phar
|
# allow_failures:
|
||||||
- php ocular.phar code-coverage:upload --format=php-clover build/logs/coverage.clover
|
# - php: nightly
|
||||||
|
|
||||||
matrix:
|
|
||||||
allow_failures:
|
|
||||||
- php: nightly
|
|
||||||
|
51
CHANGELOG.md
Normal file
51
CHANGELOG.md
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
## Version 5.3
|
||||||
|
* Update PHP requirement to 8.2
|
||||||
|
|
||||||
|
## Version 5.2
|
||||||
|
* Updated PHP requirement to 8.1
|
||||||
|
* Updated to support PHP 8.2
|
||||||
|
* Improve Anilist <-> Kitsu mappings to be more reliable
|
||||||
|
|
||||||
|
## Version 5.1
|
||||||
|
* Added session check, so when coming back to a page, if the session is expired, the page will refresh.
|
||||||
|
* Updated logging config so that much fewer, much smaller files are generated.
|
||||||
|
* Updated Kitsu integration to use GraphQL API, reducing a lot of internal complexity.
|
||||||
|
|
||||||
|
## Version 5
|
||||||
|
* Updated PHP requirement to 7.4
|
||||||
|
* Added anime watching history view
|
||||||
|
* Added manga reading history view
|
||||||
|
* Updated anime collection to have more media types
|
||||||
|
|
||||||
|
## Version 4.2
|
||||||
|
* Updated dependencies
|
||||||
|
* Updated PHP requirement to 7.3
|
||||||
|
* Added option to automatically set dark mode based on the OS setting
|
||||||
|
|
||||||
|
## Version 4.1
|
||||||
|
* Added optional dark theme
|
||||||
|
* Removed MAL integration, added Anilist Integration
|
||||||
|
* Now uses WebP cache images when the browser supports it
|
||||||
|
* Replaces JS minifier with pre-minified scripts (Removes the need for one caching folder, too)
|
||||||
|
* Updated console command to sync Kitsu and Anilist data (Kitsu can sync MAL, and MAL's API broke, so MAL sync was removed)
|
||||||
|
* Added page to update settings without having to edit config files
|
||||||
|
* Defaulted to secure (HTTPS) urls
|
||||||
|
* Updated Character pages to show voice actors
|
||||||
|
* Added People pages, showing which works they contributed to, and in what role
|
||||||
|
|
||||||
|
## Version 4
|
||||||
|
* Updated to use Kitsu API after discontinuation of Hummingbird
|
||||||
|
* Added streaming links to list entries from the Kitsu API
|
||||||
|
* Added simple integration with MyAnimeList, so an update can cross-post to both Kitsu and MyAnimeList (anime and manga)
|
||||||
|
* Added console command to sync Kitsu and MyAnimeList data
|
||||||
|
* Added character pages
|
||||||
|
|
||||||
|
## Version 3
|
||||||
|
* Converted user configuration to toml files
|
||||||
|
* Added a caching layer for api calls, which resets upon updates from the
|
||||||
|
app.
|
||||||
|
* Added a bulk thumbnail generator script
|
||||||
|
* Removed json file "cache" from the app folder
|
||||||
|
|
65
Jenkinsfile
vendored
Normal file
65
Jenkinsfile
vendored
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
pipeline {
|
||||||
|
agent none
|
||||||
|
stages {
|
||||||
|
stage('setup') {
|
||||||
|
agent any
|
||||||
|
steps {
|
||||||
|
sh 'curl -sS https://getcomposer.org/installer | php'
|
||||||
|
sh 'rm -rf ./vendor'
|
||||||
|
sh 'rm -f composer.lock'
|
||||||
|
sh 'php composer.phar install --ignore-platform-reqs'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('PHP 8.1') {
|
||||||
|
agent {
|
||||||
|
docker {
|
||||||
|
image 'php:8.1-cli-alpine'
|
||||||
|
args '-u root --privileged'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
sh 'apk add --no-cache git icu-dev'
|
||||||
|
sh 'docker-php-ext-configure intl && docker-php-ext-install intl'
|
||||||
|
sh 'php ./vendor/bin/phpunit --colors=never'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('PHP 8.2') {
|
||||||
|
agent {
|
||||||
|
docker {
|
||||||
|
image 'php:8.2-cli-alpine'
|
||||||
|
args '-u root --privileged'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
sh 'apk add --no-cache git icu-dev'
|
||||||
|
sh 'docker-php-ext-configure intl && docker-php-ext-install intl'
|
||||||
|
sh 'php ./vendor/bin/phpunit --colors=never'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Latest PHP') {
|
||||||
|
agent {
|
||||||
|
docker {
|
||||||
|
image 'php:cli-alpine'
|
||||||
|
args '-u root --privileged'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
sh 'apk add --no-cache git icu-dev'
|
||||||
|
sh 'docker-php-ext-configure intl && docker-php-ext-install intl'
|
||||||
|
sh 'php ./vendor/bin/phpunit --colors=never'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Coverage') {
|
||||||
|
agent any
|
||||||
|
steps {
|
||||||
|
sh 'php composer.phar run-script coverage'
|
||||||
|
step([
|
||||||
|
$class: 'CloverPublisher',
|
||||||
|
cloverReportDir: '',
|
||||||
|
cloverReportFileName: 'build/logs/clover.xml',
|
||||||
|
])
|
||||||
|
junit 'build/logs/junit.xml'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2017 Timothy J Warren
|
||||||
|
|
||||||
|
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.
|
53
README.md
53
README.md
@ -1,10 +1,9 @@
|
|||||||
# Hummingbird Anime Client
|
# Hummingbird Anime Client
|
||||||
|
|
||||||
A self-hosted client that allows custom formatting of data from the hummingbird api
|
Update your anime/manga list on Kitsu.io and Anilist
|
||||||
|
|
||||||
[![Build Status](https://jenkins.timshomepage.net/buildStatus/icon?job=animeclient)](https://jenkins.timshomepage.net/job/animeclient/)
|
[![Build Status](https://travis-ci.com/timw4mail/HummingBirdAnimeClient.svg?branch=master)](https://travis-ci.com/github/timw4mail/HummingBirdAnimeClient)
|
||||||
[![Build Status](https://travis-ci.org/timw4mail/HummingBirdAnimeClient.svg?branch=master)](https://travis-ci.org/timw4mail/HummingBirdAnimeClient)
|
[![Build Status](https://jenkins.timshome.page/buildStatus/icon?job=timw4mail/HummingBirdAnimeClient/develop)](https://jenkins.timshome.page/job/timw4mail/job/HummingBirdAnimeClient/job/develop/)
|
||||||
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/timw4mail/HummingBirdAnimeClient/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/timw4mail/HummingBirdAnimeClient/?branch=master)
|
|
||||||
|
|
||||||
[[Hosted Example](https://list.timshomepage.net)]
|
[[Hosted Example](https://list.timshomepage.net)]
|
||||||
|
|
||||||
@ -32,28 +31,36 @@ A self-hosted client that allows custom formatting of data from the hummingbird
|
|||||||
|
|
||||||
### Requirements
|
### Requirements
|
||||||
|
|
||||||
* PHP 5.5+
|
* PHP 8.2
|
||||||
* PDO SQLite (For collection tab)
|
* ext-dom (For editing the DOM)
|
||||||
* GD
|
* ext-gd (For caching images)
|
||||||
|
* ext-intl (For time localization)
|
||||||
|
* ext-json
|
||||||
|
* ext-mbstring
|
||||||
|
* ext-pdo
|
||||||
|
|
||||||
|
### Highly Recommended
|
||||||
|
|
||||||
|
* Redis or Memcached for caching
|
||||||
|
* PDO SQLite or PDO PostgreSQL (For collection tab)
|
||||||
|
|
||||||
### Installation
|
### Installation
|
||||||
|
|
||||||
1. Install via composer: `composer create-project timw4mail/hummingbird-anime-client`
|
1. Install via git, then install dependencies via composer: `composer install`
|
||||||
2. Configure settings in `app/config/config.php` to your liking
|
2. Duplicate `app/config/config.toml.example` file as `app/config/config.toml`
|
||||||
3. Create the following directories if they don't exist, and make sure they are world writable
|
3. Configure settings in `app/config/config.toml` to your liking
|
||||||
* app/cache
|
4. Create the following directories if they don't exist, and make sure they are world writable
|
||||||
* public/images/manga
|
* app/config
|
||||||
|
* app/logs
|
||||||
|
* public/images/avatars
|
||||||
* public/images/anime
|
* public/images/anime
|
||||||
* public/js/cache
|
* public/images/characters
|
||||||
|
* public/images/manga
|
||||||
|
5. Make sure the `console` script is executable
|
||||||
|
6. Additional settings are on the settings page once you log in.
|
||||||
|
|
||||||
#### Anime Collection Additional Installation
|
### Server Setup
|
||||||
* Run `php /vendor/bin/phinx migrate -e development` to create the database tables
|
|
||||||
* For importing anime:
|
See the [wiki](https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient/wiki)
|
||||||
1. Login
|
for more in-depth information
|
||||||
2. Use the form to select your media
|
|
||||||
3. Save & Repeat as needed
|
|
||||||
* For bulk importing anime:
|
|
||||||
1. Find the anime you are looking for on the hummingbird search api page: `https://hummingbird.me/api/v1/search/anime?query=`
|
|
||||||
2. Create an `import.json` file in the root of the app, with an array of objects from the search page that you want to import
|
|
||||||
3. Go to the anime collection tab, and the import will be run
|
|
||||||
|
|
||||||
|
60
app/appConf/base_config.php
Normal file
60
app/appConf/base_config.php
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* Hummingbird Anime List Client
|
||||||
|
*
|
||||||
|
* An API client for Kitsu to manage anime and manga watch lists
|
||||||
|
*
|
||||||
|
* PHP version 8
|
||||||
|
*
|
||||||
|
* @package HummingbirdAnimeClient
|
||||||
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
|
* @copyright 2015 - 2021 Timothy J. Warren
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||||
|
* @version 5.2
|
||||||
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
|
*/
|
||||||
|
|
||||||
|
use function Aviat\AnimeClient\loadConfig;
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Lower level configuration
|
||||||
|
//
|
||||||
|
// You shouldn't generally need to change anything below this line
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
$APP_DIR = dirname(__DIR__);
|
||||||
|
$ROOT_DIR = dirname($APP_DIR);
|
||||||
|
|
||||||
|
$tomlConfig = loadConfig(__DIR__);
|
||||||
|
|
||||||
|
return array_merge($tomlConfig, [
|
||||||
|
'root' => $ROOT_DIR,
|
||||||
|
'asset_dir' => "{$ROOT_DIR}/public",
|
||||||
|
'base_config_dir' => __DIR__,
|
||||||
|
'config_dir' => "{$APP_DIR}/config",
|
||||||
|
|
||||||
|
// No config defaults
|
||||||
|
'kitsu_username' => 'timw4mail',
|
||||||
|
'whose_list' => 'Someone',
|
||||||
|
'cache' => [
|
||||||
|
'connection' => [],
|
||||||
|
'driver' => 'null',
|
||||||
|
],
|
||||||
|
'secure_urls' => TRUE,
|
||||||
|
|
||||||
|
// Routing defaults
|
||||||
|
'asset_path' => '/public',
|
||||||
|
'default_list' => 'anime', //anime|manga
|
||||||
|
'default_anime_list_path' => 'watching', // watching|plan_to_watch|on_hold|dropped|completed|all
|
||||||
|
'default_manga_list_path' => 'reading', // reading|plan_to_read|on_hold|dropped|completed|all
|
||||||
|
'default_view_type' => 'cover_view', // cover_view|list_view
|
||||||
|
|
||||||
|
// Template file path
|
||||||
|
'view_path' => "{$APP_DIR}/views",
|
||||||
|
|
||||||
|
// Cache paths
|
||||||
|
'data_cache_path' => "{$APP_DIR}/cache",
|
||||||
|
'img_cache_path' => "{$ROOT_DIR}/public/images",
|
||||||
|
|
||||||
|
// Included config files
|
||||||
|
'routes' => require 'routes.php',
|
||||||
|
]);
|
21
app/appConf/menus.toml
Normal file
21
app/appConf/menus.toml
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
[anime_list]
|
||||||
|
route_prefix = ""
|
||||||
|
[anime_list.items]
|
||||||
|
watch_history = '/history/anime'
|
||||||
|
watching = '/anime/watching'
|
||||||
|
plan_to_watch = '/anime/plan_to_watch'
|
||||||
|
on_hold = '/anime/on_hold'
|
||||||
|
dropped = '/anime/dropped'
|
||||||
|
completed = '/anime/completed'
|
||||||
|
all = '/anime/all'
|
||||||
|
|
||||||
|
[manga_list]
|
||||||
|
route_prefix = ""
|
||||||
|
[manga_list.items]
|
||||||
|
reading_history = '/history/manga'
|
||||||
|
reading = '/manga/reading'
|
||||||
|
plan_to_read = '/manga/plan_to_read'
|
||||||
|
on_hold = '/manga/on_hold'
|
||||||
|
dropped = '/manga/dropped'
|
||||||
|
completed = '/manga/completed'
|
||||||
|
all = '/manga/all'
|
323
app/appConf/routes.php
Normal file
323
app/appConf/routes.php
Normal file
@ -0,0 +1,323 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* Hummingbird Anime List Client
|
||||||
|
*
|
||||||
|
* An API client for Kitsu to manage anime and manga watch lists
|
||||||
|
*
|
||||||
|
* PHP version 8
|
||||||
|
*
|
||||||
|
* @package HummingbirdAnimeClient
|
||||||
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||||
|
* @copyright 2015 - 2021 Timothy J. Warren
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||||
|
* @version 5.2
|
||||||
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
|
*/
|
||||||
|
|
||||||
|
use const Aviat\AnimeClient\{
|
||||||
|
ALPHA_SLUG_PATTERN,
|
||||||
|
DEFAULT_CONTROLLER,
|
||||||
|
DEFAULT_CONTROLLER_METHOD,
|
||||||
|
SLUG_PATTERN,
|
||||||
|
NUM_PATTERN,
|
||||||
|
};
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Routing Config
|
||||||
|
//
|
||||||
|
// Maps paths to controllers and methods
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
$base_routes = [
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
// AJAX Routes
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
'cache_purge' => [
|
||||||
|
'path' => '/cache_purge',
|
||||||
|
'action' => 'clearCache',
|
||||||
|
],
|
||||||
|
'heartbeat' => [
|
||||||
|
'path' => '/heartbeat',
|
||||||
|
'action' => 'heartbeat',
|
||||||
|
],
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
// Anime List Routes
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
'anime.add.get' => [
|
||||||
|
'path' => '/anime/add',
|
||||||
|
'action' => 'addForm',
|
||||||
|
],
|
||||||
|
'anime.add.post' => [
|
||||||
|
'path' => '/anime/add',
|
||||||
|
'action' => 'add',
|
||||||
|
'verb' => 'post',
|
||||||
|
],
|
||||||
|
'anime.random' => [
|
||||||
|
'path' => '/anime/details/random',
|
||||||
|
'action' => 'random',
|
||||||
|
],
|
||||||
|
'anime.details' => [
|
||||||
|
'path' => '/anime/details/{id}',
|
||||||
|
'action' => 'details',
|
||||||
|
'tokens' => [
|
||||||
|
'id' => SLUG_PATTERN,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'anime.delete' => [
|
||||||
|
'path' => '/anime/delete',
|
||||||
|
'action' => 'delete',
|
||||||
|
'verb' => 'post',
|
||||||
|
],
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
// Manga Routes
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
'manga.search' => [
|
||||||
|
'path' => '/manga/search',
|
||||||
|
'action' => 'search',
|
||||||
|
],
|
||||||
|
'manga.add.get' => [
|
||||||
|
'path' => '/manga/add',
|
||||||
|
'action' => 'addForm',
|
||||||
|
],
|
||||||
|
'manga.add.post' => [
|
||||||
|
'path' => '/manga/add',
|
||||||
|
'action' => 'add',
|
||||||
|
'verb' => 'post',
|
||||||
|
],
|
||||||
|
'manga.delete' => [
|
||||||
|
'path' => '/manga/delete',
|
||||||
|
'action' => 'delete',
|
||||||
|
'verb' => 'post',
|
||||||
|
],
|
||||||
|
'manga.random' => [
|
||||||
|
'path' => '/manga/details/random',
|
||||||
|
'action' => 'random',
|
||||||
|
],
|
||||||
|
'manga.details' => [
|
||||||
|
'path' => '/manga/details/{id}',
|
||||||
|
'action' => 'details',
|
||||||
|
'tokens' => [
|
||||||
|
'id' => SLUG_PATTERN,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
// Anime Collection Routes
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
'anime.collection.search' => [
|
||||||
|
'path' => '/anime-collection/search',
|
||||||
|
'action' => 'search',
|
||||||
|
],
|
||||||
|
'anime.collection.add.get' => [
|
||||||
|
'path' => '/anime-collection/add',
|
||||||
|
'action' => 'form',
|
||||||
|
],
|
||||||
|
'anime.collection.edit.get' => [
|
||||||
|
'path' => '/anime-collection/edit/{id}',
|
||||||
|
'action' => 'form',
|
||||||
|
'tokens' => [
|
||||||
|
'id' => NUM_PATTERN,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'anime.collection.add.post' => [
|
||||||
|
'path' => '/anime-collection/add',
|
||||||
|
'action' => 'add',
|
||||||
|
'verb' => 'post',
|
||||||
|
],
|
||||||
|
'anime.collection.edit.post' => [
|
||||||
|
'path' => '/anime-collection/edit',
|
||||||
|
'action' => 'edit',
|
||||||
|
'verb' => 'post',
|
||||||
|
],
|
||||||
|
'anime.collection.view' => [
|
||||||
|
'path' => '/anime-collection/view{/view}',
|
||||||
|
'action' => 'view',
|
||||||
|
'tokens' => [
|
||||||
|
'view' => ALPHA_SLUG_PATTERN,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'anime.collection.delete' => [
|
||||||
|
'path' => '/anime-collection/delete',
|
||||||
|
'action' => 'delete',
|
||||||
|
'verb' => 'post',
|
||||||
|
],
|
||||||
|
'anime.collection.redirect' => [
|
||||||
|
'path' => '/anime-collection',
|
||||||
|
],
|
||||||
|
'anime.collection.redirect2' => [
|
||||||
|
'path' => '/anime-collection/',
|
||||||
|
],
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
// Manga Collection Routes
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
'manga.collection.search' => [
|
||||||
|
'path' => '/manga-collection/search',
|
||||||
|
'action' => 'search',
|
||||||
|
],
|
||||||
|
'manga.collection.add.get' => [
|
||||||
|
'path' => '/manga-collection/add',
|
||||||
|
'action' => 'form',
|
||||||
|
],
|
||||||
|
'manga.collection.edit.get' => [
|
||||||
|
'path' => '/manga-collection/edit/{id}',
|
||||||
|
'action' => 'form',
|
||||||
|
'tokens' => [
|
||||||
|
'id' => NUM_PATTERN,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'manga.collection.add.post' => [
|
||||||
|
'path' => '/manga-collection/add',
|
||||||
|
'action' => 'add',
|
||||||
|
'verb' => 'post',
|
||||||
|
],
|
||||||
|
'manga.collection.edit.post' => [
|
||||||
|
'path' => '/manga-collection/edit',
|
||||||
|
'action' => 'edit',
|
||||||
|
'verb' => 'post',
|
||||||
|
],
|
||||||
|
'manga.collection.view' => [
|
||||||
|
'path' => '/manga-collection/view{/view}',
|
||||||
|
'tokens' => [
|
||||||
|
'view' => ALPHA_SLUG_PATTERN,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'manga.collection.delete' => [
|
||||||
|
'path' => '/manga-collection/delete',
|
||||||
|
'action' => 'delete',
|
||||||
|
'verb' => 'post',
|
||||||
|
],
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
// Other Routes
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
'character' => [
|
||||||
|
'path' => '/character/{slug}',
|
||||||
|
'tokens' => [
|
||||||
|
'slug' => SLUG_PATTERN,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'person' => [
|
||||||
|
'path' => '/people/{slug}',
|
||||||
|
'tokens' => [
|
||||||
|
'slug' => SLUG_PATTERN,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'default_user_info' => [
|
||||||
|
'path' => '/me',
|
||||||
|
'action' => 'me',
|
||||||
|
'controller' => 'user',
|
||||||
|
],
|
||||||
|
'user_info' => [
|
||||||
|
'path' => '/user/{username}',
|
||||||
|
'controller' => 'user',
|
||||||
|
'action' => 'about',
|
||||||
|
'tokens' => [
|
||||||
|
'username' => '.*?',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
// Default / Shared routes
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
'anilist-redirect' => [
|
||||||
|
'path' => '/anilist-redirect',
|
||||||
|
'action' => 'anilistRedirect',
|
||||||
|
'controller' => 'settings',
|
||||||
|
],
|
||||||
|
'anilist-callback' => [
|
||||||
|
'path' => '/anilist-oauth',
|
||||||
|
'action' => 'anilistCallback',
|
||||||
|
'controller' => 'settings',
|
||||||
|
],
|
||||||
|
'image_proxy' => [
|
||||||
|
'path' => '/public/images/{type}/{file}',
|
||||||
|
'action' => 'cache',
|
||||||
|
'controller' => 'images',
|
||||||
|
'tokens' => [
|
||||||
|
'type' => SLUG_PATTERN,
|
||||||
|
'file' => '[a-z0-9\-]+\.[a-z]{3,4}',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'settings' => [
|
||||||
|
'path' => '/settings',
|
||||||
|
],
|
||||||
|
'settings-post' => [
|
||||||
|
'path' => '/settings/update',
|
||||||
|
'action' => 'update',
|
||||||
|
'verb' => 'post',
|
||||||
|
],
|
||||||
|
'login' => [
|
||||||
|
'path' => '/login',
|
||||||
|
'action' => 'login',
|
||||||
|
],
|
||||||
|
'login.post' => [
|
||||||
|
'path' => '/login',
|
||||||
|
'action' => 'loginAction',
|
||||||
|
'verb' => 'post',
|
||||||
|
],
|
||||||
|
'logout' => [
|
||||||
|
'path' => '/logout',
|
||||||
|
'action' => 'logout',
|
||||||
|
],
|
||||||
|
'history' => [
|
||||||
|
'controller' => 'history',
|
||||||
|
'path' => '/history/{type}',
|
||||||
|
'tokens' => [
|
||||||
|
'type' => SLUG_PATTERN,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'increment' => [
|
||||||
|
'path' => '/{controller}/increment',
|
||||||
|
'action' => 'increment',
|
||||||
|
'verb' => 'post',
|
||||||
|
'tokens' => [
|
||||||
|
'controller' => ALPHA_SLUG_PATTERN,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'update' => [
|
||||||
|
'path' => '/{controller}/update',
|
||||||
|
'action' => 'update',
|
||||||
|
'verb' => 'post',
|
||||||
|
'tokens' => [
|
||||||
|
'controller' => ALPHA_SLUG_PATTERN,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'update.post' => [
|
||||||
|
'path' => '/{controller}/update_form',
|
||||||
|
'action' => 'formUpdate',
|
||||||
|
'verb' => 'post',
|
||||||
|
'tokens' => [
|
||||||
|
'controller' => ALPHA_SLUG_PATTERN,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'edit' => [
|
||||||
|
'path' => '/{controller}/edit/{id}/{status}',
|
||||||
|
'action' => 'edit',
|
||||||
|
'tokens' => [
|
||||||
|
'id' => SLUG_PATTERN,
|
||||||
|
'status' => SLUG_PATTERN,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'list' => [
|
||||||
|
'path' => '/{controller}/{status}{/view}',
|
||||||
|
'tokens' => [
|
||||||
|
'status' => ALPHA_SLUG_PATTERN,
|
||||||
|
'view' => ALPHA_SLUG_PATTERN,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'index_redirect' => [
|
||||||
|
'path' => '/',
|
||||||
|
'action' => 'redirectToDefaultRoute',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
$defaultMap = [
|
||||||
|
'action' => DEFAULT_CONTROLLER_METHOD,
|
||||||
|
'controller' => DEFAULT_CONTROLLER,
|
||||||
|
'params' => [],
|
||||||
|
'verb' => 'get',
|
||||||
|
];
|
||||||
|
|
||||||
|
$routes = [];
|
||||||
|
foreach ($base_routes as $name => $route)
|
||||||
|
{
|
||||||
|
$routes[$name] = array_merge($defaultMap, $route);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $routes;
|
@ -1,92 +1,197 @@
|
|||||||
<?php
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bootstrap / Dependency Injection
|
* Hummingbird Anime List Client
|
||||||
|
*
|
||||||
|
* An API client for Kitsu to manage anime and manga watch lists
|
||||||
|
*
|
||||||
|
* PHP version 8.1
|
||||||
|
*
|
||||||
|
* @copyright 2015 - 2023 Timothy J. Warren <tim@timshome.page>
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||||
|
* @version 5.2
|
||||||
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Aviat\AnimeClient;
|
namespace Aviat\AnimeClient;
|
||||||
|
|
||||||
use Aura\Html\HelperLocatorFactory;
|
use Aura\Html\HelperLocatorFactory;
|
||||||
use Aura\Web\WebFactory;
|
use Aura\Router\RouterContainer;
|
||||||
use Aura\Router\RouterFactory;
|
|
||||||
use Aura\Session\SessionFactory;
|
use Aura\Session\SessionFactory;
|
||||||
use Monolog\Logger;
|
use Aviat\AnimeClient\API\{Anilist, Kitsu};
|
||||||
|
use Aviat\AnimeClient\{Component, Model};
|
||||||
|
use Aviat\Banker\Teller;
|
||||||
|
use Aviat\Ion\Config;
|
||||||
|
use Aviat\Ion\Di\{Container, ContainerInterface};
|
||||||
|
use Laminas\Diactoros\ServerRequestFactory;
|
||||||
|
use Monolog\Formatter\JsonFormatter;
|
||||||
use Monolog\Handler\RotatingFileHandler;
|
use Monolog\Handler\RotatingFileHandler;
|
||||||
use Monolog\Handler\BrowserConsoleHandler;
|
use Monolog\Logger;
|
||||||
|
use Psr\SimpleCache\CacheInterface;
|
||||||
|
|
||||||
use Aviat\Ion\Di\Container;
|
use function Aviat\Ion\_dir;
|
||||||
use Aviat\AnimeClient\Auth\HummingbirdAuth;
|
|
||||||
use Aviat\AnimeClient\Model;
|
if ( ! defined('HB_APP_DIR'))
|
||||||
|
{
|
||||||
|
define('HB_APP_DIR', __DIR__);
|
||||||
|
define('ROOT_DIR', dirname(HB_APP_DIR));
|
||||||
|
define('TEMPLATE_DIR', _dir(HB_APP_DIR, 'templates'));
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Setup DI container
|
// Setup DI container
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
return function(array $config_array = []) {
|
return static function (array $configArray = []): Container {
|
||||||
$container = new Container();
|
$container = new Container();
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// Logging
|
// Logging
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
|
$LOG_DIR = _dir(HB_APP_DIR, 'logs');
|
||||||
|
|
||||||
$app_logger = new Logger('animeclient');
|
$appLogger = new Logger('animeclient');
|
||||||
$app_logger->pushHandler(new RotatingFileHandler(__DIR__ . '/logs/app.log', Logger::NOTICE));
|
$appLogger->pushHandler(new RotatingFileHandler(_dir($LOG_DIR, 'app.log'), 2, Logger::WARNING));
|
||||||
$container->setLogger($app_logger, 'default');
|
$container->setLogger($appLogger);
|
||||||
|
|
||||||
|
foreach (['anilist-request', 'kitsu-request', 'kitsu-graphql'] as $channel)
|
||||||
|
{
|
||||||
|
$logger = new Logger($channel);
|
||||||
|
$handler = new RotatingFileHandler(_dir($LOG_DIR, "{$channel}.log"), 2, Logger::WARNING);
|
||||||
|
$handler->setFormatter(new JsonFormatter());
|
||||||
|
$logger->pushHandler($handler);
|
||||||
|
|
||||||
|
$container->setLogger($logger, $channel);
|
||||||
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// Injected Objects
|
// Injected Objects
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
// Create Config Object
|
// Create Config Object
|
||||||
$config = new Config($config_array);
|
$container->set('config', static fn () => new Config($configArray));
|
||||||
$container->set('config', $config);
|
|
||||||
|
// Create Cache Object
|
||||||
|
$container->set('cache', static function (ContainerInterface $container): CacheInterface {
|
||||||
|
$logger = $container->getLogger();
|
||||||
|
$config = $container->get('config')->get('cache');
|
||||||
|
|
||||||
|
return new Teller($config, $logger);
|
||||||
|
});
|
||||||
|
|
||||||
// Create Aura Router Object
|
// Create Aura Router Object
|
||||||
$aura_router = (new RouterFactory())->newInstance();
|
$container->set('aura-router', static fn () => new RouterContainer());
|
||||||
$container->set('aura-router', $aura_router);
|
|
||||||
|
|
||||||
// Create Html helper Object
|
// Create Html helpers
|
||||||
$html_helper = (new HelperLocatorFactory)->newInstance();
|
$container->set('html-helper', static function (ContainerInterface $container) {
|
||||||
$html_helper->set('menu', function() use ($container) {
|
$htmlHelper = (new HelperLocatorFactory())->newInstance();
|
||||||
$menu_helper = new Helper\Menu();
|
$helpers = [
|
||||||
$menu_helper->setContainer($container);
|
'menu' => Helper\Menu::class,
|
||||||
return $menu_helper;
|
'field' => Helper\Form::class,
|
||||||
|
'picture' => Helper\Picture::class,
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($helpers as $name => $class)
|
||||||
|
{
|
||||||
|
$htmlHelper->set($name, static function () use ($class, $container) {
|
||||||
|
$helper = new $class();
|
||||||
|
$helper->setContainer($container);
|
||||||
|
|
||||||
|
return $helper;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return $htmlHelper;
|
||||||
});
|
});
|
||||||
$container->set('html-helper', $html_helper);
|
|
||||||
|
|
||||||
// Create Request/Response Objects
|
// Create Component helpers
|
||||||
$web_factory = new WebFactory([
|
$container->set('component-helper', static function (ContainerInterface $container) {
|
||||||
'_GET' => $_GET,
|
$helper = (new HelperLocatorFactory())->newInstance();
|
||||||
'_POST' => $_POST,
|
$components = [
|
||||||
'_COOKIE' => $_COOKIE,
|
'animeCover' => Component\AnimeCover::class,
|
||||||
'_SERVER' => $_SERVER,
|
'mangaCover' => Component\MangaCover::class,
|
||||||
'_FILES' => $_FILES
|
'character' => Component\Character::class,
|
||||||
]);
|
'media' => Component\Media::class,
|
||||||
$container->set('request', $web_factory->newRequest());
|
'tabs' => Component\Tabs::class,
|
||||||
$container->set('response', $web_factory->newResponse());
|
'verticalTabs' => Component\VerticalTabs::class,
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($components as $name => $componentClass)
|
||||||
|
{
|
||||||
|
$helper->set($name, static function () use ($container, $componentClass) {
|
||||||
|
$helper = new $componentClass();
|
||||||
|
$helper->setContainer($container);
|
||||||
|
|
||||||
|
return $helper;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return $helper;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create Request Object
|
||||||
|
$container->set('request', static fn () => ServerRequestFactory::fromGlobals(
|
||||||
|
$GLOBALS['_SERVER'],
|
||||||
|
$_GET,
|
||||||
|
$_POST,
|
||||||
|
$_COOKIE,
|
||||||
|
$_FILES
|
||||||
|
));
|
||||||
|
|
||||||
// Create session Object
|
// Create session Object
|
||||||
$session = (new SessionFactory())->newInstance($_COOKIE);
|
$container->set('session', static fn () => (new SessionFactory())->newInstance($_COOKIE));
|
||||||
$container->set('session', $session);
|
|
||||||
|
|
||||||
$container->set('url-generator', new UrlGenerator($container));
|
|
||||||
|
|
||||||
|
|
||||||
// Miscellaneous helper methods
|
|
||||||
$anime_client = new AnimeClient();
|
|
||||||
$anime_client->setContainer($container);
|
|
||||||
$container->set('anime-client', $anime_client);
|
|
||||||
|
|
||||||
// Models
|
// Models
|
||||||
$container->set('api-model', new Model\API($container));
|
$container->set('kitsu-model', static function (ContainerInterface $container): Kitsu\Model {
|
||||||
$container->set('anime-model', new Model\Anime($container));
|
$requestBuilder = new Kitsu\RequestBuilder($container);
|
||||||
$container->set('manga-model', new Model\Manga($container));
|
$requestBuilder->setLogger($container->getLogger('kitsu-request'));
|
||||||
$container->set('anime-collection-model', new Model\AnimeCollection($container));
|
|
||||||
|
|
||||||
$container->set('auth', new HummingbirdAuth($container));
|
$listItem = new Kitsu\ListItem();
|
||||||
|
$listItem->setContainer($container);
|
||||||
|
$listItem->setRequestBuilder($requestBuilder);
|
||||||
|
|
||||||
|
$model = new Kitsu\Model($listItem);
|
||||||
|
$model->setContainer($container);
|
||||||
|
$model->setRequestBuilder($requestBuilder);
|
||||||
|
|
||||||
|
$cache = $container->get('cache');
|
||||||
|
$model->setCache($cache);
|
||||||
|
|
||||||
|
return $model;
|
||||||
|
});
|
||||||
|
$container->set('anilist-model', static function (ContainerInterface $container): Anilist\Model {
|
||||||
|
$requestBuilder = new Anilist\RequestBuilder($container);
|
||||||
|
$requestBuilder->setLogger($container->getLogger('anilist-request'));
|
||||||
|
|
||||||
|
$listItem = new Anilist\ListItem();
|
||||||
|
$listItem->setContainer($container);
|
||||||
|
$listItem->setRequestBuilder($requestBuilder);
|
||||||
|
|
||||||
|
$model = new Anilist\Model($listItem);
|
||||||
|
$model->setContainer($container);
|
||||||
|
$model->setRequestBuilder($requestBuilder);
|
||||||
|
|
||||||
|
return $model;
|
||||||
|
});
|
||||||
|
$container->set('settings-model', static function ($container) {
|
||||||
|
$model = new Model\Settings($container->get('config'));
|
||||||
|
$model->setContainer($container);
|
||||||
|
|
||||||
|
return $model;
|
||||||
|
});
|
||||||
|
|
||||||
|
$container->setSimple('anime-model', Model\Anime::class);
|
||||||
|
$container->setSimple('manga-model', Model\Manga::class);
|
||||||
|
$container->setSimple('anime-collection-model', Model\AnimeCollection::class);
|
||||||
|
|
||||||
|
// Miscellaneous Classes
|
||||||
|
$container->setSimple('util', Util::class);
|
||||||
|
$container->setSimple('auth', Kitsu\Auth::class);
|
||||||
|
$container->setSimple('url-generator', UrlGenerator::class);
|
||||||
|
$container->setSimple('render-helper', RenderHelper::class);
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// Dispatcher
|
// Dispatcher
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
$container->set('dispatcher', new Dispatcher($container));
|
$container->setSimple('dispatcher', Dispatcher::class);
|
||||||
|
|
||||||
return $container;
|
return $container;
|
||||||
};
|
};
|
||||||
|
6
app/config/anilist.toml.example
Normal file
6
app/config/anilist.toml.example
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
################################################################################
|
||||||
|
# Anilist API #
|
||||||
|
################################################################################
|
||||||
|
client_id = "your_client_id"
|
||||||
|
client_secret = "your_client_secret"
|
||||||
|
username = "user123"
|
@ -1,34 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Hummingbird Anime Client
|
|
||||||
*
|
|
||||||
* An API client for Hummingbird to manage anime and manga watch lists
|
|
||||||
*
|
|
||||||
* @package HummingbirdAnimeClient
|
|
||||||
* @author Timothy J. Warren
|
|
||||||
* @copyright Copyright (c) 2015 - 2016
|
|
||||||
* @link https://github.com/timw4mail/HummingBirdAnimeClient
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// Lower level configuration
|
|
||||||
//
|
|
||||||
// You shouldn't generally need to change anything below this line
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
$APP_DIR = realpath(__DIR__ . '/../');
|
|
||||||
$ROOT_DIR = realpath("{$APP_DIR}/../");
|
|
||||||
|
|
||||||
$base_config = [
|
|
||||||
// Template file path
|
|
||||||
'view_path' => "{$APP_DIR}/views",
|
|
||||||
|
|
||||||
// Cache paths
|
|
||||||
'data_cache_path' => "{$APP_DIR}/cache",
|
|
||||||
'img_cache_path' => "{$ROOT_DIR}/public/images",
|
|
||||||
|
|
||||||
// Included config files
|
|
||||||
'database' => require 'database.php',
|
|
||||||
'menus' => require 'menus.php',
|
|
||||||
'routes' => require 'routes.php',
|
|
||||||
];
|
|
22
app/config/cache.toml.example
Normal file
22
app/config/cache.toml.example
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
################################################################################
|
||||||
|
# Cache Setup #
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
# See https://git.timshomepage.net/aviat/banker for more information
|
||||||
|
|
||||||
|
# Available drivers are memcached, redis or null
|
||||||
|
# Null cache driver means no caching
|
||||||
|
driver = "redis"
|
||||||
|
|
||||||
|
[connection]
|
||||||
|
# Host or socket to connect to
|
||||||
|
host = "127.0.0.1"
|
||||||
|
|
||||||
|
# Connection port
|
||||||
|
#port = 6379
|
||||||
|
|
||||||
|
# Connection password
|
||||||
|
#password = ""
|
||||||
|
|
||||||
|
# Database number
|
||||||
|
database = 2
|
@ -1,37 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Hummingbird Anime Client
|
|
||||||
*
|
|
||||||
* An API client for Hummingbird to manage anime and manga watch lists
|
|
||||||
*
|
|
||||||
* @package HummingbirdAnimeClient
|
|
||||||
* @author Timothy J. Warren
|
|
||||||
* @copyright Copyright (c) 2015 - 2016
|
|
||||||
* @link https://github.com/timw4mail/HummingBirdAnimeClient
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
$config = [
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// Username for anime and manga lists
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
'hummingbird_username' => 'timw4mail',
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// Whose list is it?
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
'whose_list' => 'Tim',
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// General config
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// do you wish to show the anime collection?
|
|
||||||
'show_anime_collection' => TRUE,
|
|
||||||
|
|
||||||
// do you wish to show the manga collection?
|
|
||||||
'show_manga_collection' => FALSE,
|
|
||||||
|
|
||||||
// path to public directory on the server
|
|
||||||
'asset_dir' => realpath(__DIR__ . '/../../public'),
|
|
||||||
];
|
|
39
app/config/config.toml.example
Normal file
39
app/config/config.toml.example
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
################################################################################
|
||||||
|
# Main User Configuration #
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
# Username for anime and manga lists
|
||||||
|
kitsu_username = "johnsmith"
|
||||||
|
|
||||||
|
# Whose list is it?
|
||||||
|
whose_list = "Someone"
|
||||||
|
|
||||||
|
# do you wish to show the anime collection?
|
||||||
|
show_anime_collection = true
|
||||||
|
|
||||||
|
# do you wish to show the manga collection?
|
||||||
|
show_manga_collection = false
|
||||||
|
|
||||||
|
# what theme would you like to use? light, dark, or auto
|
||||||
|
theme = "auto"
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Default views and paths
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
# Which list should be the default?
|
||||||
|
default_list = "anime" # anime or manga
|
||||||
|
|
||||||
|
# Default pages for anime/manga
|
||||||
|
default_anime_list_path = "watching" # watching|plan_to_watch|on_hold|dropped|completed|all
|
||||||
|
default_manga_list_path = "reading" # reading|plan_to_read|on_hold|dropped|completed|all
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Not on Settings Page
|
||||||
|
#
|
||||||
|
# These settings are not available to change on the settings page
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
# Use HTTPs for URLs
|
||||||
|
# It is not recommended to change this setting
|
||||||
|
secure_urls = true
|
@ -1,25 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Hummingbird Anime Client
|
|
||||||
*
|
|
||||||
* An API client for Hummingbird to manage anime and manga watch lists
|
|
||||||
*
|
|
||||||
* @package HummingbirdAnimeClient
|
|
||||||
* @author Timothy J. Warren
|
|
||||||
* @copyright Copyright (c) 2015 - 2016
|
|
||||||
* @link https://github.com/timw4mail/HummingBirdAnimeClient
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
return [
|
|
||||||
'collection' => [
|
|
||||||
'type' => 'sqlite',
|
|
||||||
'host' => '',
|
|
||||||
'user' => '',
|
|
||||||
'pass' => '',
|
|
||||||
'port' => '',
|
|
||||||
'name' => 'default',
|
|
||||||
'database' => '',
|
|
||||||
'file' => __DIR__ . '/../../anime_collection.sqlite',
|
|
||||||
]
|
|
||||||
];
|
|
11
app/config/database.toml.example
Normal file
11
app/config/database.toml.example
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
################################################################################
|
||||||
|
# Database Configuration #
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
type = "sqlite"
|
||||||
|
host = ""
|
||||||
|
user = ""
|
||||||
|
pass = ""
|
||||||
|
port = ""
|
||||||
|
database = ""
|
||||||
|
file = "anime_collection.sqlite3"
|
@ -1,37 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Hummingbird Anime Client
|
|
||||||
*
|
|
||||||
* An API client for Hummingbird to manage anime and manga watch lists
|
|
||||||
*
|
|
||||||
* @package HummingbirdAnimeClient
|
|
||||||
* @author Timothy J. Warren
|
|
||||||
* @copyright Copyright (c) 2015 - 2016
|
|
||||||
* @link https://github.com/timw4mail/HummingBirdAnimeClient
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
return [
|
|
||||||
'anime_list' => [
|
|
||||||
'route_prefix' => '/anime',
|
|
||||||
'items' => [
|
|
||||||
'watching' => '/watching',
|
|
||||||
'plan_to_watch' => '/plan_to_watch',
|
|
||||||
'on_hold' => '/on_hold',
|
|
||||||
'dropped' => '/dropped',
|
|
||||||
'completed' => '/completed',
|
|
||||||
'all' => '/all'
|
|
||||||
]
|
|
||||||
],
|
|
||||||
'manga_list' => [
|
|
||||||
'route_prefix' => '/manga',
|
|
||||||
'items' => [
|
|
||||||
'reading' => '/reading',
|
|
||||||
'plan_to_read' => '/plan_to_read',
|
|
||||||
'on_hold' => '/on_hold',
|
|
||||||
'dropped' => '/dropped',
|
|
||||||
'completed' => '/completed',
|
|
||||||
'all' => '/all'
|
|
||||||
]
|
|
||||||
]
|
|
||||||
];
|
|
@ -1,59 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Hummingbird Anime Client
|
|
||||||
*
|
|
||||||
* An API client for Hummingbird to manage anime and manga watch lists
|
|
||||||
*
|
|
||||||
* @package HummingbirdAnimeClient
|
|
||||||
* @author Timothy J. Warren
|
|
||||||
* @copyright Copyright (c) 2015 - 2016
|
|
||||||
* @link https://github.com/timw4mail/HummingBirdAnimeClient
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/* $config = */require 'config.php';
|
|
||||||
|
|
||||||
// Should we use myth to preprocess?
|
|
||||||
$use_myth = FALSE;
|
|
||||||
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| CSS Folder
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| The folder where css files exist, in relation to the document root
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
$css_root = $config['asset_dir'] . '/css/';
|
|
||||||
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| Path from
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| Path fragment to rewrite in css files
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
$path_from = '';
|
|
||||||
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| Path to
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| The path fragment replacement for the css files
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
$path_to = '';
|
|
||||||
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| JS Folder
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| The folder where javascript files exist, in relation to the document root
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
$js_root = $config['asset_dir'] . '/js/';
|
|
@ -1,37 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Hummingbird Anime Client
|
|
||||||
*
|
|
||||||
* An API client for Hummingbird to manage anime and manga watch lists
|
|
||||||
*
|
|
||||||
* @package HummingbirdAnimeClient
|
|
||||||
* @author Timothy J. Warren
|
|
||||||
* @copyright Copyright (c) 2015 - 2016
|
|
||||||
* @link https://github.com/timw4mail/HummingBirdAnimeClient
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is the config array for css files to concatenate and minify
|
|
||||||
*/
|
|
||||||
return [
|
|
||||||
/*-----
|
|
||||||
Css
|
|
||||||
-----*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
For each group create an array like so
|
|
||||||
|
|
||||||
'my_group' => array(
|
|
||||||
'path/to/css/file1.css',
|
|
||||||
'path/to/css/file2.css'
|
|
||||||
),
|
|
||||||
*/
|
|
||||||
'base' => [
|
|
||||||
'marx.css',
|
|
||||||
'base.css'
|
|
||||||
]
|
|
||||||
];
|
|
||||||
// End of css_groups.php
|
|
@ -1,61 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Hummingbird Anime Client
|
|
||||||
*
|
|
||||||
* An API client for Hummingbird to manage anime and manga watch lists
|
|
||||||
*
|
|
||||||
* @package HummingbirdAnimeClient
|
|
||||||
* @author Timothy J. Warren
|
|
||||||
* @copyright Copyright (c) 2015 - 2016
|
|
||||||
* @link https://github.com/timw4mail/HummingBirdAnimeClient
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is the config array for javascript files to concatenate and minify
|
|
||||||
*/
|
|
||||||
return [
|
|
||||||
/*
|
|
||||||
For each group create an array like so
|
|
||||||
|
|
||||||
'my_group' => array(
|
|
||||||
'path/to/js/file1.js',
|
|
||||||
'path/to/js/file2.js'
|
|
||||||
),
|
|
||||||
*/
|
|
||||||
'table' => [
|
|
||||||
'lib/jquery.min.js',
|
|
||||||
'lib/table_sorter/jquery.tablesorter.min.js',
|
|
||||||
'sort_tables.js'
|
|
||||||
],
|
|
||||||
'edit' => [
|
|
||||||
'lib/jquery.min.js',
|
|
||||||
'show_message.js',
|
|
||||||
'anime_edit.js',
|
|
||||||
'manga_edit.js'
|
|
||||||
],
|
|
||||||
'table_edit' => [
|
|
||||||
'lib/jquery.min.js',
|
|
||||||
'lib/table_sorter/jquery.tablesorter.min.js',
|
|
||||||
'sort_tables.js',
|
|
||||||
'show_message.js',
|
|
||||||
'anime_edit.js',
|
|
||||||
'manga_edit.js'
|
|
||||||
],
|
|
||||||
'anime_collection' => [
|
|
||||||
'lib/jquery.min.js',
|
|
||||||
'lib/jquery.throttle-debounce.js',
|
|
||||||
'lib/jsrender.js',
|
|
||||||
'anime_collection.js'
|
|
||||||
],
|
|
||||||
'manga_collection' => [
|
|
||||||
'lib/jquery.min.js',
|
|
||||||
'lib/jquery.throttle-debounce.js',
|
|
||||||
'lib/jsrender.js',
|
|
||||||
'manga_collection.js'
|
|
||||||
]
|
|
||||||
];
|
|
||||||
|
|
||||||
// End of js_groups.php
|
|
@ -1,150 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Hummingbird Anime Client
|
|
||||||
*
|
|
||||||
* An API client for Hummingbird to manage anime and manga watch lists
|
|
||||||
*
|
|
||||||
* @package HummingbirdAnimeClient
|
|
||||||
* @author Timothy J. Warren
|
|
||||||
* @copyright Copyright (c) 2015 - 2016
|
|
||||||
* @link https://github.com/timw4mail/HummingBirdAnimeClient
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
use Aviat\AnimeClient\AnimeClient;
|
|
||||||
|
|
||||||
return [
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
// Routing options
|
|
||||||
//
|
|
||||||
// Specify default paths and views
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
'route_config' => [
|
|
||||||
// Subfolder prefix for url, if in a subdirectory of the web root
|
|
||||||
'subfolder_prefix' => '',
|
|
||||||
|
|
||||||
// Path to public directory, where images/css/javascript are located,
|
|
||||||
// appended to the url
|
|
||||||
'asset_path' => '/public',
|
|
||||||
|
|
||||||
// Which list should be the default?
|
|
||||||
'default_list' => 'anime', // anime or manga
|
|
||||||
|
|
||||||
// Default pages for anime/manga
|
|
||||||
'default_anime_list_path' => "watching", // watching|plan_to_watch|on_hold|dropped|completed|all
|
|
||||||
'default_manga_list_path' => "reading", // reading|plan_to_read|on_hold|dropped|completed|all
|
|
||||||
|
|
||||||
// Default view type (cover_view/list_view)
|
|
||||||
'default_view_type' => 'cover_view',
|
|
||||||
],
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
// Routing Config
|
|
||||||
//
|
|
||||||
// Maps paths to controlers and methods
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
'routes' => [
|
|
||||||
// ---------------------------------------------------------------------
|
|
||||||
// Anime List Routes
|
|
||||||
// ---------------------------------------------------------------------
|
|
||||||
'anime_add_form' => [
|
|
||||||
'path' => '/anime/add',
|
|
||||||
'action' => 'add_form',
|
|
||||||
'verb' => 'get'
|
|
||||||
],
|
|
||||||
'anime_add' => [
|
|
||||||
'path' => '/anime/add',
|
|
||||||
'action' => 'add',
|
|
||||||
'verb' => 'post'
|
|
||||||
],
|
|
||||||
// ---------------------------------------------------------------------
|
|
||||||
// Anime Collection Routes
|
|
||||||
// ---------------------------------------------------------------------
|
|
||||||
'collection_search' => [
|
|
||||||
'path' => '/collection/search',
|
|
||||||
'action' => 'search'
|
|
||||||
],
|
|
||||||
'collection_add_form' => [
|
|
||||||
'path' => '/collection/add',
|
|
||||||
'action' => 'form',
|
|
||||||
'params' => [],
|
|
||||||
],
|
|
||||||
'collection_edit_form' => [
|
|
||||||
'path' => '/collection/edit/{id}',
|
|
||||||
'action' => 'form',
|
|
||||||
'tokens' => [
|
|
||||||
'id' => '[0-9]+'
|
|
||||||
]
|
|
||||||
],
|
|
||||||
'collection_add' => [
|
|
||||||
'path' => '/collection/add',
|
|
||||||
'action' => 'add',
|
|
||||||
'verb' => 'post'
|
|
||||||
],
|
|
||||||
'collection_edit' => [
|
|
||||||
'path' => '/collection/edit',
|
|
||||||
'action' => 'edit',
|
|
||||||
'verb' => 'post'
|
|
||||||
],
|
|
||||||
'collection' => [
|
|
||||||
'path' => '/collection/view{/view}',
|
|
||||||
'action' => 'index',
|
|
||||||
'params' => [],
|
|
||||||
'tokens' => [
|
|
||||||
'view' => '[a-z_]+'
|
|
||||||
]
|
|
||||||
],
|
|
||||||
// ---------------------------------------------------------------------
|
|
||||||
// Default / Shared routes
|
|
||||||
// ---------------------------------------------------------------------
|
|
||||||
'login' => [
|
|
||||||
'path' => '/{controller}/login',
|
|
||||||
'action' => 'login',
|
|
||||||
],
|
|
||||||
'login_post' => [
|
|
||||||
'path' => '/{controller}/login',
|
|
||||||
'action' => 'login_action',
|
|
||||||
'verb' => 'post'
|
|
||||||
],
|
|
||||||
'logout' => [
|
|
||||||
'path' => '/{controller}/logout',
|
|
||||||
'action' => 'logout'
|
|
||||||
],
|
|
||||||
'update' => [
|
|
||||||
'path' => '/{controller}/update',
|
|
||||||
'action' => 'update',
|
|
||||||
'verb' => 'post',
|
|
||||||
'tokens' => [
|
|
||||||
'controller' => '[a-z_]+'
|
|
||||||
]
|
|
||||||
],
|
|
||||||
'update_form' => [
|
|
||||||
'path' => '/{controller}/update_form',
|
|
||||||
'action' => 'form_update',
|
|
||||||
'verb' => 'post',
|
|
||||||
'tokens' => [
|
|
||||||
'controller' => '[a-z_]+'
|
|
||||||
]
|
|
||||||
],
|
|
||||||
'edit' => [
|
|
||||||
'path' => '/{controller}/edit/{id}/{status}',
|
|
||||||
'action' => 'edit',
|
|
||||||
'tokens' => [
|
|
||||||
'id' => '[0-9a-z_]+',
|
|
||||||
'status' => '[a-zA-z\- ]+',
|
|
||||||
]
|
|
||||||
],
|
|
||||||
'list' => [
|
|
||||||
'path' => '/{controller}/{type}{/view}',
|
|
||||||
'action' => AnimeClient::DEFAULT_CONTROLLER_METHOD,
|
|
||||||
'tokens' => [
|
|
||||||
'type' => '[a-z_]+',
|
|
||||||
'view' => '[a-z_]+'
|
|
||||||
]
|
|
||||||
],
|
|
||||||
'index_redirect' => [
|
|
||||||
'path' => '/',
|
|
||||||
'controller' => AnimeClient::DEFAULT_CONTROLLER_NAMESPACE,
|
|
||||||
'action' => 'redirect_to_default'
|
|
||||||
],
|
|
||||||
]
|
|
||||||
];
|
|
0
app/logs/.gitkeep
Normal file → Executable file
0
app/logs/.gitkeep
Normal file → Executable file
100
app/templates/anime-cover.php
Normal file
100
app/templates/anime-cover.php
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
<article
|
||||||
|
class="media"
|
||||||
|
data-kitsu-id="<?= $item['id'] ?>"
|
||||||
|
data-anilist-id="<?= $item['anilist_id'] ?>"
|
||||||
|
data-mal-id="<?= $item['mal_id'] ?>"
|
||||||
|
>
|
||||||
|
<?php if ($_->isAuthenticated()): ?>
|
||||||
|
<button title="Increment episode count" class="plus-one" hidden>+1 Episode</button>
|
||||||
|
<?php endif ?>
|
||||||
|
<?= $_->h->img($item['anime']['cover_image'], ['width' => 220, 'loading' => 'lazy']) ?>
|
||||||
|
|
||||||
|
<div class="name">
|
||||||
|
<a href="<?= $_->urlFromRoute('anime.details', ['id' => $item['anime']['slug']]) ?>">
|
||||||
|
<span class="canonical"><?= $item['anime']['title'] ?></span>
|
||||||
|
<?php foreach ($item['anime']['titles'] as $title): ?>
|
||||||
|
<br/>
|
||||||
|
<small><?= $title ?></small>
|
||||||
|
<?php endforeach ?>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="table">
|
||||||
|
<?php if (isset($item['private']) || isset($item['rewatching'])): ?>
|
||||||
|
<div class="row">
|
||||||
|
<?php foreach (['private', 'rewatching'] as $attr): ?>
|
||||||
|
<?php if ($item[$attr]): ?>
|
||||||
|
<span class="item-<?= $attr ?>"><?= ucfirst($attr) ?></span>
|
||||||
|
<?php endif ?>
|
||||||
|
<?php endforeach ?>
|
||||||
|
</div>
|
||||||
|
<?php endif ?>
|
||||||
|
|
||||||
|
<?php if ($item['rewatched'] > 0): ?>
|
||||||
|
<div class="row">
|
||||||
|
<?php if ($item['rewatched'] == 1): ?>
|
||||||
|
<div>Rewatched once</div>
|
||||||
|
<?php elseif ($item['rewatched'] == 2): ?>
|
||||||
|
<div>Rewatched twice</div>
|
||||||
|
<?php elseif ($item['rewatched'] == 3): ?>
|
||||||
|
<div>Rewatched thrice</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<div>Rewatched <?= $item['rewatched'] ?> times</div>
|
||||||
|
<?php endif ?>
|
||||||
|
</div>
|
||||||
|
<?php endif ?>
|
||||||
|
|
||||||
|
<?php if (count($item['anime']['streaming_links']) > 0): ?>
|
||||||
|
<div class="row">
|
||||||
|
<?php foreach ($item['anime']['streaming_links'] as $link): ?>
|
||||||
|
<div class="cover-streaming-link">
|
||||||
|
<?php if ($link['meta']['link']): ?>
|
||||||
|
<a href="<?= $link['link'] ?>"
|
||||||
|
title="Stream '<?= $item['anime']['title'] ?>' on <?= $link['meta']['name'] ?>">
|
||||||
|
<?= $_->h->img("/public/images/{$link['meta']['image']}", [
|
||||||
|
'class' => 'streaming-logo',
|
||||||
|
'width' => 20,
|
||||||
|
'height' => 20,
|
||||||
|
'alt' => "{$link['meta']['name']} logo",
|
||||||
|
]); ?>
|
||||||
|
</a>
|
||||||
|
<?php else: ?>
|
||||||
|
<?= $_->h->img("/public/images/{$link['meta']['image']}", [
|
||||||
|
'class' => 'streaming-logo',
|
||||||
|
'width' => 20,
|
||||||
|
'height' => 20,
|
||||||
|
'alt' => "{$link['meta']['name']} logo",
|
||||||
|
]); ?>
|
||||||
|
<?php endif ?>
|
||||||
|
</div>
|
||||||
|
<?php endforeach ?>
|
||||||
|
</div>
|
||||||
|
<?php endif ?>
|
||||||
|
|
||||||
|
<?php if ($_->isAuthenticated()): ?>
|
||||||
|
<div class="row">
|
||||||
|
<span class="edit">
|
||||||
|
<a class="bracketed" title="Edit information about this anime" href="<?=
|
||||||
|
$_->urlFromRoute('edit', [
|
||||||
|
'controller' => 'anime',
|
||||||
|
'id' => $item['id'],
|
||||||
|
'status' => $item['watching_status']
|
||||||
|
]);
|
||||||
|
?>">Edit</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<?php endif ?>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="user-rating">Rating: <?= $item['user_rating'] ?> / 10</div>
|
||||||
|
<div class="completion">Episodes:
|
||||||
|
<span class="completed_number"><?= $item['episodes']['watched'] ?></span> /
|
||||||
|
<span class="total_number"><?= $item['episodes']['total'] ?></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="media_type"><?= $_->escape->html($item['anime']['show_type']) ?></div>
|
||||||
|
<div class="airing-status"><?= $_->escape->html($item['airing']['status']) ?></div>
|
||||||
|
<div class="age-rating"><?= $_->escape->html($item['anime']['age_rating']) ?></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
6
app/templates/character.php
Normal file
6
app/templates/character.php
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<article class="<?= $className ?>">
|
||||||
|
<div class="name">
|
||||||
|
<a href="<?= $link ?>"><?= $name ?></a>
|
||||||
|
</div>
|
||||||
|
<a href="<?= $link ?>"><?= $picture ?></a>
|
||||||
|
</article>
|
68
app/templates/manga-cover.php
Normal file
68
app/templates/manga-cover.php
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
<article class="media" data-kitsu-id="<?= $item['id'] ?>" data-mal-id="<?= $item['mal_id'] ?>">
|
||||||
|
<?php if ($_->isAuthenticated()): ?>
|
||||||
|
<div class="edit-buttons" hidden>
|
||||||
|
<button class="plus-one-chapter">+1 Chapter</button>
|
||||||
|
</div>
|
||||||
|
<?php endif ?>
|
||||||
|
<?= $_->h->img($item['manga']['image'], ['width' => 220, 'loading' => 'lazy']) ?>
|
||||||
|
<div class="name">
|
||||||
|
<a href="<?= $_->urlFromRoute('manga.details', ['id' => $item['manga']['slug']]) ?>">
|
||||||
|
<?= $_->escape->html($item['manga']['title']) ?>
|
||||||
|
<?php foreach($item['manga']['titles'] as $title): ?>
|
||||||
|
<br /><small><?= $title ?></small>
|
||||||
|
<?php endforeach ?>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="table">
|
||||||
|
<?php if ($_->isAuthenticated()): ?>
|
||||||
|
<div class="row">
|
||||||
|
<span class="edit">
|
||||||
|
<a class="bracketed"
|
||||||
|
title="Edit information about this manga"
|
||||||
|
href="<?= $_->urlFromRoute('edit', [
|
||||||
|
'controller' => 'manga',
|
||||||
|
'id' => $item['id'],
|
||||||
|
'status' => $name
|
||||||
|
]) ?>">
|
||||||
|
Edit
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<?php endif ?>
|
||||||
|
<div class="row">
|
||||||
|
<div><?= $item['manga']['type'] ?></div>
|
||||||
|
<div class="user-rating">Rating: <?= $item['user_rating'] ?> / 10</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if ($item['rereading']): ?>
|
||||||
|
<div class="row">
|
||||||
|
<?php foreach(['rereading'] as $attr): ?>
|
||||||
|
<?php if($item[$attr]): ?>
|
||||||
|
<span class="item-<?= $attr ?>"><?= ucfirst($attr) ?></span>
|
||||||
|
<?php endif ?>
|
||||||
|
<?php endforeach ?>
|
||||||
|
</div>
|
||||||
|
<?php endif ?>
|
||||||
|
|
||||||
|
<?php if ($item['reread'] > 0): ?>
|
||||||
|
<div class="row">
|
||||||
|
<?php if ($item['reread'] == 1): ?>
|
||||||
|
<div>Reread once</div>
|
||||||
|
<?php elseif ($item['reread'] == 2): ?>
|
||||||
|
<div>Reread twice</div>
|
||||||
|
<?php elseif ($item['reread'] == 3): ?>
|
||||||
|
<div>Reread thrice</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<div>Reread <?= $item['reread'] ?> times</div>
|
||||||
|
<?php endif ?>
|
||||||
|
</div>
|
||||||
|
<?php endif ?>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="chapter_completion">
|
||||||
|
Chapters: <span class="chapters_read"><?= $item['chapters']['read'] ?></span> /
|
||||||
|
<span class="chapter_count"><?= $item['chapters']['total'] ?></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
12
app/templates/media.php
Normal file
12
app/templates/media.php
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<article class="<?= $className ?>">
|
||||||
|
<a href="<?= $link ?>"><?= $picture ?></a>
|
||||||
|
<div class="name">
|
||||||
|
<a href="<?= $link ?>">
|
||||||
|
<?= array_shift($titles) ?>
|
||||||
|
<?php foreach ($titles as $title): ?>
|
||||||
|
<br />
|
||||||
|
<small><?= $title ?></small>
|
||||||
|
<?php endforeach ?>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</article>
|
5
app/templates/single-tab.php
Normal file
5
app/templates/single-tab.php
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<section class="<?= $className ?>">
|
||||||
|
<?php foreach ($data as $tabName => $tabData): ?>
|
||||||
|
<?= $callback($tabData, $tabName) ?>
|
||||||
|
<?php endforeach ?>
|
||||||
|
</section>
|
32
app/templates/tabs.php
Normal file
32
app/templates/tabs.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<div class="tabs">
|
||||||
|
<?php $i = 0; foreach ($data as $tabName => $tabData): ?>
|
||||||
|
<?php if ( ! empty($tabData)): ?>
|
||||||
|
<?php $id = "{$name}-{$i}"; ?>
|
||||||
|
<input
|
||||||
|
role='tab'
|
||||||
|
aria-controls="_<?= $id ?>"
|
||||||
|
type="radio"
|
||||||
|
name="<?= $name ?>"
|
||||||
|
id="<?= $id ?>"
|
||||||
|
<?= ($i === 0) ? 'checked="checked"' : '' ?>
|
||||||
|
/>
|
||||||
|
<label for="<?= $id ?>"><?= ucfirst($tabName) ?></label>
|
||||||
|
|
||||||
|
<?php if ($hasSectionWrapper): ?>
|
||||||
|
<div class="content full-height">
|
||||||
|
<?php endif ?>
|
||||||
|
|
||||||
|
<section
|
||||||
|
id="_<?= $id ?>"
|
||||||
|
role="tabpanel"
|
||||||
|
class="<?= $className ?>"
|
||||||
|
>
|
||||||
|
<?= $callback($tabData, $tabName) ?>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<?php if ($hasSectionWrapper): ?>
|
||||||
|
</div>
|
||||||
|
<?php endif ?>
|
||||||
|
<?php endif ?>
|
||||||
|
<?php $i++; endforeach ?>
|
||||||
|
</div>
|
25
app/templates/vertical-tabs.php
Normal file
25
app/templates/vertical-tabs.php
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<div class="vertical-tabs">
|
||||||
|
<?php $i = 0; ?>
|
||||||
|
<?php foreach ($data as $tabName => $tabData): ?>
|
||||||
|
<?php $id = "{$name}-{$i}" ?>
|
||||||
|
<div class="tab">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
role='tab'
|
||||||
|
aria-controls="_<?= $id ?>"
|
||||||
|
name="<?= $name ?>"
|
||||||
|
id="<?= $id ?>"
|
||||||
|
<?= $i === 0 ? 'checked="checked"' : '' ?>
|
||||||
|
/>
|
||||||
|
<label for="<?= $id ?>"><?= $tabName ?></label>
|
||||||
|
<section
|
||||||
|
id='_<?= $id ?>'
|
||||||
|
role="tabpanel"
|
||||||
|
class="<?= $className ?>"
|
||||||
|
>
|
||||||
|
<?= $callback($tabData, $tabName) ?>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
<?php $i++; ?>
|
||||||
|
<?php endforeach ?>
|
||||||
|
</div>
|
@ -1,4 +1,6 @@
|
|||||||
<main>
|
<main>
|
||||||
<h1>404</h1>
|
<h1>404</h1>
|
||||||
<h2>Page Not Found</h2>
|
<h2><?= $message ?></h2>
|
||||||
|
<pre>(╯°□°)╯︵ ┻━┻
|
||||||
|
┬─┬ノ( º _ ºノ)</pre>
|
||||||
</main>
|
</main>
|
||||||
|
@ -1,14 +1,20 @@
|
|||||||
<?php if ($auth->is_authenticated()): ?>
|
<?php if ($_->isAuthenticated()): ?>
|
||||||
<main>
|
<main>
|
||||||
<h2>Add Anime to your List</h2>
|
<h2>Add Anime to your List</h2>
|
||||||
<form action="<?= $action_url ?>" method="post">
|
<form action="<?= $action_url ?>" method="post">
|
||||||
|
<?php include realpath(__DIR__ . '/../js-warning.php') ?>
|
||||||
<section>
|
<section>
|
||||||
|
<div class="cssload-loader" hidden="hidden">
|
||||||
|
<div class="cssload-inner cssload-one"></div>
|
||||||
|
<div class="cssload-inner cssload-two"></div>
|
||||||
|
<div class="cssload-inner cssload-three"></div>
|
||||||
|
</div>
|
||||||
<label for="search">Search for anime by name: <input type="search" id="search" /></label>
|
<label for="search">Search for anime by name: <input type="search" id="search" /></label>
|
||||||
<section id="series_list" class="media-wrap">
|
<section id="series-list" class="media-wrap">
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
<br />
|
<br />
|
||||||
<table class="form">
|
<table class="invisible form">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td><label for="status">Watching Status</label></td>
|
<td><label for="status">Watching Status</label></td>
|
||||||
@ -23,6 +29,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td> </td>
|
<td> </td>
|
||||||
<td>
|
<td>
|
||||||
|
<input type="hidden" name="type" value="anime" />
|
||||||
<button type="submit">Save</button>
|
<button type="submit">Save</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -30,11 +37,4 @@
|
|||||||
</table>
|
</table>
|
||||||
</form>
|
</form>
|
||||||
</main>
|
</main>
|
||||||
<template id="show_list">
|
|
||||||
<article class="media">
|
|
||||||
<div class="name"><label><input type="radio" name="id" value="{{:slug}}" /> <span>{{:title}}<br />{{:alternate_title}}</span></label></div>
|
|
||||||
<img src="{{:cover_image}}" alt="{{:title}}" />
|
|
||||||
</article>
|
|
||||||
</template>
|
|
||||||
<script src="<?= $urlGenerator->asset_url('js.php?g=anime_collection') ?>"></script>
|
|
||||||
<?php endif ?>
|
<?php endif ?>
|
@ -1,69 +1,30 @@
|
|||||||
<main>
|
<main class="media-list">
|
||||||
<?php if ($auth->is_authenticated()): ?>
|
<?php if ($_->isAuthenticated()): ?>
|
||||||
<a class="bracketed" href="<?= $urlGenerator->url('anime/add', 'anime') ?>">Add Item</a>
|
<a class="bracketed" href="<?= $_->urlFromRoute('anime.add.get') ?>">Add Item</a>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
<?php if (empty($sections)): ?>
|
<?php if (empty($sections)): ?>
|
||||||
<h3>There's nothing here!</h3>
|
<h3>There's nothing here!</h3>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
|
<br />
|
||||||
|
<label>Filter: <input type='text' class='media-filter' /></label>
|
||||||
|
<br />
|
||||||
<?php foreach ($sections as $name => $items): ?>
|
<?php foreach ($sections as $name => $items): ?>
|
||||||
|
<?php if (empty($items)): ?>
|
||||||
<section class="status">
|
<section class="status">
|
||||||
<h2><?= $escape->html($name) ?></h2>
|
<h2><?= $_->escape->html($name) ?></h2>
|
||||||
|
<h3>There's nothing here!</h3>
|
||||||
|
</section>
|
||||||
|
<?php else: ?>
|
||||||
|
<section class="status">
|
||||||
|
<h2><?= $_->escape->html($name) ?></h2>
|
||||||
<section class="media-wrap">
|
<section class="media-wrap">
|
||||||
<?php foreach($items as $item): ?>
|
<?php foreach($items as $item): ?>
|
||||||
<?php if ($item['private'] && ! $auth->is_authenticated()) continue; ?>
|
<?php if ($item['private'] && ! $_->isAuthenticated()) continue; ?>
|
||||||
<article class="media" id="<?= $item['anime']['slug'] ?>">
|
<?= $_->component->animeCover($item) ?>
|
||||||
<?php if ($auth->is_authenticated()): ?>
|
|
||||||
<button title="Increment episode count" class="plus_one" hidden>+1 Episode</button>
|
|
||||||
<?php endif ?>
|
|
||||||
<?= $helper->img($item['anime']['image']); ?>
|
|
||||||
<div class="name">
|
|
||||||
<a href="<?= $escape->attr($item['anime']['url']) ?>" target="_blank">
|
|
||||||
<?= $escape->html($item['anime']['title']) ?>
|
|
||||||
<?= ($item['anime']['alternate_title'] != "") ? "<br />({$item['anime']['alternate_title']})" : ""; ?>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="table">
|
|
||||||
<?php if ($auth->is_authenticated()): ?>
|
|
||||||
<div class="row">
|
|
||||||
<span class="edit">
|
|
||||||
<a class="bracketed" title="Edit information about this anime" href="<?= $urlGenerator->url("anime/edit/{$item['id']}/{$item['watching_status']}") ?>">Edit</a>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<?php endif ?>
|
|
||||||
<?php if ($item['private'] || $item['rewatching']): ?>
|
|
||||||
<div class="row">
|
|
||||||
<?php foreach(['private', 'rewatching'] as $attr): ?>
|
|
||||||
<?php if($item[$attr]): ?>
|
|
||||||
<span class="item-<?= $attr ?>"><?= ucfirst($attr) ?></span>
|
|
||||||
<?php endif ?>
|
|
||||||
<?php endforeach ?>
|
|
||||||
</div>
|
|
||||||
<?php endif ?>
|
|
||||||
<?php if ($item['rewatched'] > 0): ?>
|
|
||||||
<div class="row">
|
|
||||||
<div>Rewatched <?= $item['rewatched'] ?> time(s)</div>
|
|
||||||
</div>
|
|
||||||
<?php endif ?>
|
|
||||||
<div class="row">
|
|
||||||
<div class="user_rating">Rating: <?= $item['user_rating'] ?> / 10</div>
|
|
||||||
<div class="completion">Episodes:
|
|
||||||
<span class="completed_number"><?= $item['episodes']['watched'] ?></span> /
|
|
||||||
<span class="total_number"><?= $item['episodes']['total'] ?></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="media_type"><?= $escape->html($item['anime']['type']) ?></div>
|
|
||||||
<div class="airing_status"><?= $escape->html($item['airing']['status']) ?></div>
|
|
||||||
<div class="age_rating"><?= $escape->html($item['anime']['age_rating']) ?></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
<?php endforeach ?>
|
<?php endforeach ?>
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
|
<?php endif ?>
|
||||||
<?php endforeach ?>
|
<?php endforeach ?>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
</main>
|
</main>
|
||||||
<?php if ($auth->is_authenticated()): ?>
|
|
||||||
<script src="<?= $urlGenerator->asset_url('js.php?g=edit') ?>"></script>
|
|
||||||
<?php endif ?>
|
|
||||||
|
205
app/views/anime/details.php
Normal file
205
app/views/anime/details.php
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use function Aviat\AnimeClient\friendlyTime;
|
||||||
|
|
||||||
|
?>
|
||||||
|
<main class="details fixed">
|
||||||
|
<section class="flex">
|
||||||
|
<aside class="info">
|
||||||
|
<?= $_->h->img($data['cover_image'], ['width' => '390']) ?>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<table class="media-details">
|
||||||
|
<tr>
|
||||||
|
<td class="align-right">Airing Status</td>
|
||||||
|
<td><?= $data['status'] ?></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<?php if ( ! empty($data['airDate'])): ?>
|
||||||
|
<tr>
|
||||||
|
<td>Original Airing</td>
|
||||||
|
<td><?= $data['airDate'] ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endif ?>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>Show Type</td>
|
||||||
|
<td><?= (strlen($data['show_type']) > 3) ? ucfirst(strtolower($data['show_type'])) : $data['show_type'] ?></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<?php if ($data['episode_count'] !== 1): ?>
|
||||||
|
<tr>
|
||||||
|
<td>Episode Count</td>
|
||||||
|
<td><?= $data['episode_count'] ?? '-' ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endif ?>
|
||||||
|
|
||||||
|
<?php if (( ! empty($data['episode_length'])) && $data['episode_count'] !== 1): ?>
|
||||||
|
<tr>
|
||||||
|
<td>Episode Length</td>
|
||||||
|
<td><?= friendlyTime($data['episode_length']) ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endif ?>
|
||||||
|
|
||||||
|
<?php if (isset($data['total_length'], $data['episode_count']) && $data['total_length'] > 0): ?>
|
||||||
|
<tr>
|
||||||
|
<td>Total Length</td>
|
||||||
|
<td><?= friendlyTime($data['total_length']) ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endif ?>
|
||||||
|
|
||||||
|
<?php if ( ! empty($data['age_rating'])): ?>
|
||||||
|
<tr>
|
||||||
|
<td>Age Rating</td>
|
||||||
|
<td><abbr title="<?= $data['age_rating_guide'] ?>"><?= $data['age_rating'] ?></abbr>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endif ?>
|
||||||
|
|
||||||
|
<?php if (count($data['links']) > 0): ?>
|
||||||
|
<tr>
|
||||||
|
<td>External Links</td>
|
||||||
|
<td>
|
||||||
|
<?php foreach ($data['links'] as $urlName => $externalUrl): ?>
|
||||||
|
<a rel='external' href="<?= $externalUrl ?>"><?= $urlName ?></a><br />
|
||||||
|
<?php endforeach ?>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endif ?>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>Genres</td>
|
||||||
|
<td>
|
||||||
|
<?= implode(', ', $data['genres']) ?>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
</aside>
|
||||||
|
<article class="text">
|
||||||
|
<h2 class="toph"><?= $data['title'] ?></h2>
|
||||||
|
<?php foreach ($data['titles_more'] as $title): ?>
|
||||||
|
<h3><?= $title ?></h3>
|
||||||
|
<?php endforeach ?>
|
||||||
|
<br />
|
||||||
|
<div class="description">
|
||||||
|
<p><?= str_replace("\n", '</p><p>', $data['synopsis']) ?></p>
|
||||||
|
</div>
|
||||||
|
<?php if (count($data['streaming_links']) > 0): ?>
|
||||||
|
<hr />
|
||||||
|
<h4>Streaming on:</h4>
|
||||||
|
<table class="full-width invisible streaming-links">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="align-left">Service</th>
|
||||||
|
<th>Subtitles</th>
|
||||||
|
<th>Dubs</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($data['streaming_links'] as $link): ?>
|
||||||
|
<tr>
|
||||||
|
<td class="align-left">
|
||||||
|
<?php if ($link['meta']['link'] !== FALSE): ?>
|
||||||
|
<a
|
||||||
|
href="<?= $link['link'] ?>"
|
||||||
|
title="Stream '<?= $data['title'] ?>' on <?= $link['meta']['name'] ?>"
|
||||||
|
>
|
||||||
|
<?= $_->h->img("/public/images/{$link['meta']['image']}", [
|
||||||
|
'class' => 'streaming-logo',
|
||||||
|
'width' => 50,
|
||||||
|
'height' => 50,
|
||||||
|
'alt' => "{$link['meta']['name']} logo",
|
||||||
|
]) ?>
|
||||||
|
<?= $link['meta']['name'] ?>
|
||||||
|
</a>
|
||||||
|
<?php else: ?>
|
||||||
|
<?= $_->h->img("/public/images/{$link['meta']['image']}", [
|
||||||
|
'class' => 'streaming-logo',
|
||||||
|
'width' => 50,
|
||||||
|
'height' => 50,
|
||||||
|
'alt' => "{$link['meta']['name']} logo",
|
||||||
|
]) ?>
|
||||||
|
<?= $link['meta']['name'] ?>
|
||||||
|
<?php endif ?>
|
||||||
|
</td>
|
||||||
|
<td><?= implode(', ', array_map(fn ($sub) => Locale::getDisplayLanguage($sub, 'en'), $link['subs'])) ?></td>
|
||||||
|
<td><?= implode(', ', array_map(fn ($dub) => Locale::getDisplayLanguage($dub, 'en'), $link['dubs'])) ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<?php endif ?>
|
||||||
|
<?php if ( ! empty($data['trailer_id'])): ?>
|
||||||
|
<div class="responsive-iframe">
|
||||||
|
<h4>Trailer</h4>
|
||||||
|
<iframe
|
||||||
|
width="560"
|
||||||
|
height="315"
|
||||||
|
role='img'
|
||||||
|
src="https://www.youtube.com/embed/<?= $data['trailer_id'] ?>"
|
||||||
|
allow="autoplay; encrypted-media"
|
||||||
|
allowfullscreen
|
||||||
|
tabindex='0'
|
||||||
|
title="<?= $data['title'] ?> trailer video"
|
||||||
|
></iframe>
|
||||||
|
</div>
|
||||||
|
<?php endif ?>
|
||||||
|
</article>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<?php if (count($data['characters']) > 0): ?>
|
||||||
|
<section>
|
||||||
|
<h2>Characters</h2>
|
||||||
|
|
||||||
|
<?= $_->component->tabs('character-types', $data['characters'], static function ($characterList, $role)
|
||||||
|
use ($_) {
|
||||||
|
$rendered = [];
|
||||||
|
foreach ($characterList as $id => $character):
|
||||||
|
if (empty($character['image']))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$rendered[] = $_->component->character(
|
||||||
|
$character['name'],
|
||||||
|
$_->urlFromRoute('character', ['slug' => $character['slug']]),
|
||||||
|
$_->h->img($character['image']),
|
||||||
|
(strtolower($role) !== 'main') ? 'small-character' : 'character'
|
||||||
|
);
|
||||||
|
endforeach;
|
||||||
|
|
||||||
|
return implode('', array_map('mb_trim', $rendered));
|
||||||
|
}) ?>
|
||||||
|
</section>
|
||||||
|
<?php endif ?>
|
||||||
|
|
||||||
|
<?php if (count($data['staff']) > 0): ?>
|
||||||
|
<section>
|
||||||
|
<h2>Staff</h2>
|
||||||
|
|
||||||
|
<?= $_->component->verticalTabs('staff-role', $data['staff'], static function ($staffList)
|
||||||
|
use ($_) {
|
||||||
|
$rendered = [];
|
||||||
|
foreach ($staffList as $id => $person):
|
||||||
|
if (empty($person['image']))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$rendered[] = $_ ->component->character(
|
||||||
|
$person['name'],
|
||||||
|
$_->urlFromRoute('person', ['slug' => $person['slug']]),
|
||||||
|
$_->h->img($person['image']),
|
||||||
|
'character small-person',
|
||||||
|
);
|
||||||
|
endforeach;
|
||||||
|
|
||||||
|
return implode('', array_map('mb_trim', $rendered));
|
||||||
|
}) ?>
|
||||||
|
</section>
|
||||||
|
<?php endif ?>
|
||||||
|
</main>
|
@ -1,89 +1,112 @@
|
|||||||
<?php if ($auth->is_authenticated()): ?>
|
<?php if ($_->isAuthenticated()): ?>
|
||||||
<main>
|
<main>
|
||||||
<h2>Edit Anime List Item</h2>
|
<h2>Edit Anime List Item</h2>
|
||||||
<form action="<?= $action ?>" method="post">
|
<form action="<?= $action ?>" method="post">
|
||||||
<table class="form">
|
<table class="invisible form">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>
|
<th>
|
||||||
<h3><?= $escape->html($item['anime']['title']) ?></h3>
|
<h3><?= $_->escape->html($item['anime']['title']) ?></h3>
|
||||||
<?php if($item['anime']['alternate_title'] != ""): ?>
|
<?php foreach($item['anime']['titles'] as $title): ?>
|
||||||
<h4><?= $escape->html($item['anime']['alternate_title']) ?></h4>
|
<h4><?= $_->escape->html($title) ?></h4>
|
||||||
<?php endif ?>
|
<?php endforeach ?>
|
||||||
</th>
|
</th>
|
||||||
<th>
|
</tr>
|
||||||
<article class="media">
|
</thead>
|
||||||
<?= $helper->img($item['anime']['image']); ?>
|
<tbody>
|
||||||
</article>
|
<tr>
|
||||||
</th>
|
<td rowspan="9">
|
||||||
</tr>
|
<?= $_->h->img($item['anime']['cover_image']) ?>
|
||||||
</thead>
|
</td>
|
||||||
<tbody>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><label for="private">Is Private?</label></td>
|
<td><label for="private">Is Private?</label></td>
|
||||||
<td>
|
<td>
|
||||||
<input type="checkbox" name="private" id="private"
|
<input type="checkbox" name="private" id="private"
|
||||||
<?php if($item['private']): ?>checked="checked"<?php endif ?>
|
<?php if($item['private']): ?>checked="checked"<?php endif ?>
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><label for="watching_status">Watching Status</label></td>
|
<td><label for="watching_status">Watching Status</label></td>
|
||||||
<td>
|
<td>
|
||||||
<select name="watching_status" id="watching_status">
|
<select name="watching_status" id="watching_status">
|
||||||
<?php foreach($statuses as $status_key => $status_title): ?>
|
<?php foreach($statuses as $status_key => $status_title): ?>
|
||||||
<option <?php if($item['watching_status'] === $status_key): ?>selected="selected"<?php endif ?>
|
<option <?php if(strtolower($item['watching_status']) === $status_key): ?>selected="selected"<?php endif ?>
|
||||||
value="<?= $status_key ?>"><?= $status_title ?></option>
|
value="<?= $status_key ?>"><?= $status_title ?></option>
|
||||||
<?php endforeach ?>
|
<?php endforeach ?>
|
||||||
</select>
|
</select>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><label for="series_rating">Rating</label></td>
|
<td><label for="series_rating">Rating</label></td>
|
||||||
<td>
|
<td>
|
||||||
<input type="number" min="0" max="10" maxlength="2" name="user_rating" id="series_rating" value="<?= $item['user_rating'] ?>" id="series_rating" size="2" /> / 10
|
<input type="number" min="0" max="10" maxlength="2" name="user_rating" id="series_rating" value="<?= $item['user_rating'] ?>" id="series_rating" size="2" /> / 10
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><label for="episodes_watched">Episodes Watched</label></td>
|
<td><label for="episodes_watched">Episodes Watched</label></td>
|
||||||
<td>
|
<td>
|
||||||
<input type="number" min="0" size="4" maxlength="4" value="<?= $item['episodes']['watched'] ?>" name="episodes_watched" id="episodes_watched" />
|
<input type="number" min="0" size="4" maxlength="4" value="<?= $item['episodes']['watched'] ?>" name="episodes_watched" id="episodes_watched" />
|
||||||
<?php if($item['episodes']['total'] > 0): ?>
|
<?php if($item['episodes']['total'] > 0): ?>
|
||||||
/ <?= $item['episodes']['total'] ?>
|
/ <?= $item['episodes']['total'] ?>
|
||||||
|
<?php endif ?>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><label for="rewatching_flag">Rewatching?</label></td>
|
||||||
|
<td>
|
||||||
|
<input type="checkbox" name="rewatching" id="rewatching_flag"
|
||||||
|
<?php if($item['rewatching'] === TRUE): ?>checked="checked"<?php endif ?>
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><label for="rewatched">Rewatch Count</label></td>
|
||||||
|
<td>
|
||||||
|
<input type="number" min="0" id="rewatched" name="rewatched" value="<?= $item['rewatched'] ?>" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><label for="notes">Notes</label></td>
|
||||||
|
<td>
|
||||||
|
<textarea name="notes" id="notes"><?= $_->escape->html($item['notes']) ?></textarea>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td> </td>
|
||||||
|
<td>
|
||||||
|
<input type="hidden" value="<?= $item['id'] ?>" name="id" />
|
||||||
|
<?php if ( ! empty($item['mal_id'])): ?>
|
||||||
|
<input type="hidden" value="<?= $item['mal_id'] ?? '' ?>" name="mal_id" />
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
</td>
|
<input type="hidden" value="true" name="edit" />
|
||||||
</tr>
|
<button type="submit">Submit</button>
|
||||||
<tr>
|
</td>
|
||||||
<td><label for="rewatching_flag">Rewatching?</label></td>
|
</tr>
|
||||||
<td>
|
</tbody>
|
||||||
<input type="checkbox" name="rewatching" id="rewatching_flag"
|
</table>
|
||||||
<?php if($item['rewatching'] === TRUE): ?>checked="checked"<?php endif ?>
|
</form>
|
||||||
/>
|
<form class="js-delete" action="<?= $_->urlFromRoute('anime.delete') ?>" method="post">
|
||||||
</td>
|
<fieldset>
|
||||||
</tr>
|
<legend>Danger Zone</legend>
|
||||||
<tr>
|
<table class="form invisible">
|
||||||
<td><label for="rewatched">Rewatch Count</label></td>
|
<tbody>
|
||||||
<td>
|
<tr>
|
||||||
<input type="number" min="0" id="rewatched" name="rewatched" value="<?= $item['rewatched'] ?>" />
|
<td class="danger">
|
||||||
</td>
|
<strong>Permanently</strong> remove this list item and <strong>all</strong> its data?
|
||||||
</tr>
|
</td>
|
||||||
<tr>
|
<td>
|
||||||
<td><label for="notes">Notes</label></td>
|
<input type="hidden" value="<?= $item['id'] ?>" name="id" />
|
||||||
<td>
|
<?php if (!empty($item['mal_id'])): ?>
|
||||||
<textarea name="notes" id="notes"><?= $escape->html($item['notes']) ?></textarea>
|
<input type="hidden" value="<?= $item['mal_id'] ?? '' ?>" name="mal_id" />
|
||||||
</td>
|
<?php endif ?>
|
||||||
</tr>
|
<button type="submit" class="danger">Delete Entry</button>
|
||||||
<tr>
|
</td>
|
||||||
<td> </td>
|
</tr>
|
||||||
<td>
|
</tbody>
|
||||||
<input type="hidden" value="<?= $item['anime']['slug'] ?>" name="id" />
|
</table>
|
||||||
<input type="hidden" value="true" name="edit" />
|
</fieldset>
|
||||||
<button type="submit">Submit</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</form>
|
</form>
|
||||||
</main>
|
</main>
|
||||||
<script src="<?= $urlGenerator->asset_url('js.php?g=edit') ?>"></script>
|
|
||||||
<?php endif ?>
|
<?php endif ?>
|
@ -1,77 +1,113 @@
|
|||||||
<main>
|
<?php use function Aviat\AnimeClient\colNotEmpty; ?>
|
||||||
<?php if ($auth->is_authenticated()): ?>
|
<main class="media-list">
|
||||||
<a class="bracketed" href="<?= $urlGenerator->url('anime/add', 'anime') ?>">Add Item</a>
|
<?php if ($_->isAuthenticated()): ?>
|
||||||
|
<a class="bracketed" href="<?= $_->urlFromRoute('anime.add.get') ?>">Add Item</a>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
<?php if (empty($sections)): ?>
|
<?php if (empty($sections)): ?>
|
||||||
<h3>There's nothing here!</h3>
|
<h3>There's nothing here!</h3>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
|
<br />
|
||||||
|
<label>Filter: <input type='text' class='media-filter' /></label>
|
||||||
|
<br />
|
||||||
<?php foreach ($sections as $name => $items): ?>
|
<?php foreach ($sections as $name => $items): ?>
|
||||||
<h2><?= $name ?></h2>
|
<h2><?= $name ?></h2>
|
||||||
<table>
|
<?php if (empty($items)): ?>
|
||||||
<thead>
|
<h3>There's nothing here!</h3>
|
||||||
<tr>
|
<?php else: ?>
|
||||||
<?php if($auth->is_authenticated()): ?>
|
<?php
|
||||||
<th> </th>
|
$hasNotes = colNotEmpty($items, 'notes');
|
||||||
<?php endif ?>
|
?>
|
||||||
<th>Title</th>
|
<table class='media-wrap'>
|
||||||
<th>Airing Status</th>
|
<thead>
|
||||||
<th>Score</th>
|
<tr>
|
||||||
<th>Type</th>
|
<?php if($_->isAuthenticated()): ?>
|
||||||
<th>Progress</th>
|
<td class="no-border"> </td>
|
||||||
<th>Rated</th>
|
|
||||||
<th>Attributes</th>
|
|
||||||
<th>Notes</th>
|
|
||||||
<th>Genres</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<?php foreach($items as $item): ?>
|
|
||||||
<?php if ($item['private'] && ! $auth->is_authenticated()) continue; ?>
|
|
||||||
<tr id="a-<?= $item['id'] ?>">
|
|
||||||
<?php if ($auth->is_authenticated()): ?>
|
|
||||||
<td>
|
|
||||||
<a class="bracketed" href="<?= $urlGenerator->url("/anime/edit/{$item['id']}/{$item['watching_status']}") ?>">Edit</a>
|
|
||||||
</td>
|
|
||||||
<?php endif ?>
|
|
||||||
<td class="align_left">
|
|
||||||
<a href="<?= $item['anime']['url'] ?>" target="_blank">
|
|
||||||
<?= $item['anime']['title'] ?>
|
|
||||||
</a>
|
|
||||||
<?= ( ! empty($item['anime']['alternate_title'])) ? " <br /> " . $item['anime']['alternate_title'] : "" ?>
|
|
||||||
</td>
|
|
||||||
<td class="align_left"><?= $item['airing']['status'] ?></td>
|
|
||||||
<td><?= $item['user_rating'] ?> / 10 </td>
|
|
||||||
<td><?= $item['anime']['type'] ?></td>
|
|
||||||
<td id="<?= $item['anime']['slug'] ?>">
|
|
||||||
Episodes: <br />
|
|
||||||
<span class="completed_number"><?= $item['episodes']['watched'] ?></span> / <span class="total_number"><?= $item['episodes']['total'] ?></span>
|
|
||||||
</td>
|
|
||||||
<td><?= $item['anime']['age_rating'] ?></td>
|
|
||||||
<td>
|
|
||||||
<?php if ($item['rewatched'] > 0): ?>
|
|
||||||
Rewatched <?= $item['rewatched'] ?> time(s)<br />
|
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
<?php $attr_list = []; ?>
|
<th>Title</th>
|
||||||
<?php foreach(['private','rewatching'] as $attr): ?>
|
<th>Airing Status</th>
|
||||||
<?php if($item[$attr]): ?>
|
<th class='numeric'>Score</th>
|
||||||
<?php $attr_list[] = ucfirst($attr); ?>
|
<th>Type</th>
|
||||||
|
<th class='numeric'>Progress</th>
|
||||||
|
<th class='rating'>Age Rating</th>
|
||||||
|
<th>Attributes</th>
|
||||||
|
<?php if($hasNotes): ?><th>Notes</th><?php endif ?>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach($items as $item): ?>
|
||||||
|
<?php if ($item['private'] && ! $_->isAuthenticated()) continue; ?>
|
||||||
|
<tr id="a-<?= $item['id'] ?>">
|
||||||
|
<?php if ($_->isAuthenticated()): ?>
|
||||||
|
<td>
|
||||||
|
<a class="bracketed" href="<?= $_->urlFromRoute('edit', [
|
||||||
|
'controller' => 'anime',
|
||||||
|
'id' => $item['id'],
|
||||||
|
'status' => $item['watching_status']
|
||||||
|
]) ?>">Edit</a>
|
||||||
|
</td>
|
||||||
|
<?php endif ?>
|
||||||
|
<td class="align-left justify">
|
||||||
|
<a href="<?= $_->urlFromRoute('anime.details', ['id' => $item['anime']['slug']]) ?>">
|
||||||
|
<?= $item['anime']['title'] ?>
|
||||||
|
</a>
|
||||||
|
<br />
|
||||||
|
<?= implode('<br />', $item['anime']['titles']) ?>
|
||||||
|
</td>
|
||||||
|
<td><?= $item['airing']['status'] ?></td>
|
||||||
|
<td><?= $item['user_rating'] ?> / 10 </td>
|
||||||
|
<td><?= $item['anime']['show_type'] ?></td>
|
||||||
|
<td id="<?= $item['anime']['slug'] ?>">
|
||||||
|
Episodes: <br />
|
||||||
|
<span class="completed_number"><?= $item['episodes']['watched'] ?></span> / <span class="total_number"><?= $item['episodes']['total'] ?></span>
|
||||||
|
</td>
|
||||||
|
<td><?= $item['anime']['age_rating'] ?></td>
|
||||||
|
<td>
|
||||||
|
<?php foreach($item['anime']['streaming_links'] as $link): ?>
|
||||||
|
<?php if ($link['meta']['link'] !== FALSE): ?>
|
||||||
|
<a href="<?= $link['link'] ?>" title="Stream '<?= $item['anime']['title'] ?>' on <?= $link['meta']['name'] ?>">
|
||||||
|
<?= $_->h->img("/public/images/{$link['meta']['image']}", [
|
||||||
|
'class' => 'small-streaming-logo',
|
||||||
|
'width' => 25,
|
||||||
|
'height' => 25,
|
||||||
|
'alt' => "{$link['meta']['name']} logo",
|
||||||
|
]) ?>
|
||||||
|
</a>
|
||||||
|
<?php else: ?>
|
||||||
|
<?= $_->h->img("/public/images/{$link['meta']['image']}", [
|
||||||
|
'class' => 'small-streaming-logo',
|
||||||
|
'width' => 25,
|
||||||
|
'height' => 25,
|
||||||
|
'alt' => "{$link['meta']['name']} logo",
|
||||||
|
]) ?>
|
||||||
|
<?php endif ?>
|
||||||
|
<?php endforeach ?>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<?php if ($item['rewatched'] > 0): ?>
|
||||||
|
<?php if ($item['rewatched'] == 1): ?>
|
||||||
|
<li>Rewatched once</li>
|
||||||
|
<?php elseif ($item['rewatched'] == 2): ?>
|
||||||
|
<li>Rewatched twice</li>
|
||||||
|
<?php elseif ($item['rewatched'] == 3): ?>
|
||||||
|
<li>Rewatched thrice</li>
|
||||||
|
<?php else: ?>
|
||||||
|
<li>Rewatched <?= $item['rewatched'] ?> times</li>
|
||||||
|
<?php endif ?>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
<?php endforeach ?>
|
<?php foreach(['private','rewatching'] as $attr): ?>
|
||||||
<?= implode(', ', $attr_list); ?>
|
<?php if($item[$attr]): ?><li><?= ucfirst($attr); ?></li><?php endif ?>
|
||||||
</td>
|
<?php endforeach ?>
|
||||||
<td>
|
</ul>
|
||||||
<p><?= $escape->html($item['notes']) ?></p>
|
</td>
|
||||||
</td>
|
<?php if ($hasNotes): ?><td><p><?= $_->escape->html($item['notes']) ?></p></td><?php endif ?>
|
||||||
<td class="align_left">
|
</tr>
|
||||||
<?php sort($item['anime']['genres']) ?>
|
<?php endforeach ?>
|
||||||
<?= join(', ', $item['anime']['genres']) ?>
|
</tbody>
|
||||||
</td>
|
</table>
|
||||||
</tr>
|
<?php endif ?>
|
||||||
<?php endforeach ?>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<?php endforeach ?>
|
<?php endforeach ?>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
</main>
|
</main>
|
||||||
<?php $group = ($auth->is_authenticated()) ? 'table_edit' : 'table' ?>
|
<script defer="defer" src="<?= $_->assetUrl('js/tables.min.js') ?>"></script>
|
||||||
<script src="<?= $urlGenerator->asset_url("js.php?g={$group}") ?>"></script>
|
|
3
app/views/blank.php
Normal file
3
app/views/blank.php
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<main>
|
||||||
|
<h1><?= $title ?></h1>
|
||||||
|
</main>
|
162
app/views/character/details.php
Normal file
162
app/views/character/details.php
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use function Aviat\AnimeClient\getLocalImg;
|
||||||
|
use Aviat\AnimeClient\Kitsu;
|
||||||
|
|
||||||
|
?>
|
||||||
|
<main class="character-page details fixed">
|
||||||
|
<section class="flex flex-no-wrap">
|
||||||
|
<aside>
|
||||||
|
<?= $_->h->img($data['image']) ?>
|
||||||
|
</aside>
|
||||||
|
<div>
|
||||||
|
<h2 class="toph"><?= $data['name'] ?></h2>
|
||||||
|
<?php foreach ($data['names'] as $name): ?>
|
||||||
|
<h3><?= $name ?></h3>
|
||||||
|
<?php endforeach ?>
|
||||||
|
|
||||||
|
<?php if ( ! empty($data['otherNames'])): ?>
|
||||||
|
<h4>Also Known As:</h4>
|
||||||
|
<ul>
|
||||||
|
<?php foreach ($data['otherNames'] as $name): ?>
|
||||||
|
<li><h5><?= $name ?></h5></li>
|
||||||
|
<?php endforeach ?>
|
||||||
|
</ul>
|
||||||
|
<?php endif ?>
|
||||||
|
<br />
|
||||||
|
<hr />
|
||||||
|
<div class="description">
|
||||||
|
<p><?= nl2br($data['description']) ?></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<?php if ( ! (empty($data['media']['anime']) || empty($data['media']['manga']))): ?>
|
||||||
|
<h3>Media</h3>
|
||||||
|
|
||||||
|
<?= $_->component->tabs('character-media', $data['media'], static function ($media, $mediaType) use ($_) {
|
||||||
|
$rendered = [];
|
||||||
|
foreach ($media as $id => $item)
|
||||||
|
{
|
||||||
|
$rendered[] = $_->component->media(
|
||||||
|
array_merge([$item['title']], $item['titles']),
|
||||||
|
$_->urlFromRoute("{$mediaType}.details", ['id' => $item['slug']]),
|
||||||
|
$_->h->img(Kitsu::getPosterImage($item), ['width' => 220, 'loading' => 'lazy']),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return implode('', array_map('mb_trim', $rendered));
|
||||||
|
}, 'media-wrap content') ?>
|
||||||
|
<?php endif ?>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<?php if (count($data['castings']) > 0): ?>
|
||||||
|
<h3>Castings</h3>
|
||||||
|
<?php
|
||||||
|
$vas = $data['castings']['Voice Actor'];
|
||||||
|
unset($data['castings']['Voice Actor']);
|
||||||
|
ksort($vas)
|
||||||
|
?>
|
||||||
|
|
||||||
|
<?php foreach ($data['castings'] as $role => $entries): ?>
|
||||||
|
<h4><?= $role ?></h4>
|
||||||
|
<?php foreach ($entries as $language => $casting): ?>
|
||||||
|
<h5><?= $language ?></h5>
|
||||||
|
<table class="min-table">
|
||||||
|
<tr>
|
||||||
|
<th>Cast Member</th>
|
||||||
|
<th>Series</th>
|
||||||
|
</tr>
|
||||||
|
<?php foreach ($casting as $cid => $c): ?>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<article class="character">
|
||||||
|
<?php
|
||||||
|
$link = $_->urlFromRoute('person', ['id' => $c['person']['id']]);
|
||||||
|
?>
|
||||||
|
<a href="<?= $link ?>">
|
||||||
|
<?= $_->h->img($c['person']['image']) ?>
|
||||||
|
<div class="name">
|
||||||
|
<?= $c['person']['name'] ?>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</article>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<section class="align-left media-wrap">
|
||||||
|
<?php foreach ($c['series'] as $series): ?>
|
||||||
|
<article class="media">
|
||||||
|
<?php
|
||||||
|
$link = $_->urlFromRoute('anime.details', ['id' => $series['attributes']['slug']]);
|
||||||
|
$titles = Kitsu::filterTitles($series['attributes']);
|
||||||
|
?>
|
||||||
|
<a href="<?= $link ?>">
|
||||||
|
<?= $_->h->img(Kitsu::getPosterImage($series['attributes'])) ?>
|
||||||
|
</a>
|
||||||
|
<div class="name">
|
||||||
|
<a href="<?= $link ?>">
|
||||||
|
<?= array_shift($titles) ?>
|
||||||
|
<?php foreach ($titles as $title): ?>
|
||||||
|
<br />
|
||||||
|
<small><?= $title ?></small>
|
||||||
|
<?php endforeach ?>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
<?php endforeach ?>
|
||||||
|
</section>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</table>
|
||||||
|
<?php endforeach ?>
|
||||||
|
<?php endforeach ?>
|
||||||
|
|
||||||
|
<?php if ( ! empty($vas)): ?>
|
||||||
|
<h4>Voice Actors</h4>
|
||||||
|
|
||||||
|
<?= $_->component->tabs('character-vas', $vas, static function ($casting) use ($_) {
|
||||||
|
$castings = [];
|
||||||
|
foreach ($casting as $id => $c):
|
||||||
|
$person = $_->component->character(
|
||||||
|
$c['person']['name'],
|
||||||
|
$_->urlFromRoute('person', ['slug' => $c['person']['slug']]),
|
||||||
|
$_->h->img($c['person']['image']['original']['url']),
|
||||||
|
);
|
||||||
|
$medias = array_map(fn ($series) => $_->component->media(
|
||||||
|
array_merge([$series['title']], $series['titles']),
|
||||||
|
$_->urlFromRoute('anime.details', ['id' => $series['slug']]),
|
||||||
|
$_->h->img(Kitsu::getPosterImage($series)),
|
||||||
|
), $c['series']);
|
||||||
|
$media = implode('', array_map('mb_trim', $medias));
|
||||||
|
|
||||||
|
$castings[] = <<<HTML
|
||||||
|
<tr>
|
||||||
|
<td>{$person}</td>
|
||||||
|
<td width="75%">
|
||||||
|
<section class="align-left media-wrap-flex">
|
||||||
|
{$media}
|
||||||
|
</section>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
HTML;
|
||||||
|
endforeach;
|
||||||
|
|
||||||
|
$languages = implode('', array_map('mb_trim', $castings));
|
||||||
|
|
||||||
|
return <<<HTML
|
||||||
|
<table class="borderless max-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Cast Member</th>
|
||||||
|
<th>Series</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>{$languages}</tbody>
|
||||||
|
</table>
|
||||||
|
HTML;
|
||||||
|
}, 'content') ?>
|
||||||
|
<?php endif ?>
|
||||||
|
<?php endif ?>
|
||||||
|
</section>
|
||||||
|
</main>
|
@ -1,23 +1,25 @@
|
|||||||
<?php if ($auth->is_authenticated()): ?>
|
<?php if ($_->isAuthenticated()): ?>
|
||||||
<main>
|
<main>
|
||||||
<h2>Add Anime to your Collection</h2>
|
<h2>Add <?= ucfirst($collection_type) ?> to your Collection</h2>
|
||||||
<form action="<?= $action_url ?>" method="post">
|
<form action="<?= $action_url ?>" method="post">
|
||||||
|
<?php include realpath(__DIR__ . '/../js-warning.php') ?>
|
||||||
<section>
|
<section>
|
||||||
<label for="search">Search for anime by name: <input type="search" id="search" name="search" /></label>
|
<div class="cssload-loader" hidden="hidden">
|
||||||
<section id="series_list" class="media-wrap">
|
<div class="cssload-inner cssload-one"></div>
|
||||||
|
<div class="cssload-inner cssload-two"></div>
|
||||||
|
<div class="cssload-inner cssload-three"></div>
|
||||||
|
</div>
|
||||||
|
<label for="search-anime-collection">Search for <?= $collection_type ?> by name: <input type="search" id="search-anime-collection" name="search" /></label>
|
||||||
|
<section id="series-list" class="media-wrap">
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
<br />
|
<br />
|
||||||
<table class="form">
|
<table class="invisible form">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td><label for="media_id">Media</label></td>
|
<td class="align-right"><label for="media_id">Media</label></td>
|
||||||
<td>
|
<td class='align-left'>
|
||||||
<select name="media_id" id="media_id">
|
<?php include 'media-select-list.php' ?>
|
||||||
<?php foreach($media_items as $id => $name): ?>
|
|
||||||
<option value="<?= $id ?>"><?= $name ?></option>
|
|
||||||
<?php endforeach ?>
|
|
||||||
</select>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@ -34,11 +36,4 @@
|
|||||||
</table>
|
</table>
|
||||||
</form>
|
</form>
|
||||||
</main>
|
</main>
|
||||||
<template id="show_list">
|
|
||||||
<article class="media">
|
|
||||||
<div class="name"><label><input type="radio" name="id" value="{{:id}}" /> <span>{{:title}}<br />{{:alternate_title}}</span></label></div>
|
|
||||||
<img src="{{:cover_image}}" alt="{{:title}}" />
|
|
||||||
</article>
|
|
||||||
</template>
|
|
||||||
<script src="<?= $urlGenerator->asset_url('js.php?g=anime_collection') ?>"></script>
|
|
||||||
<?php endif ?>
|
<?php endif ?>
|
28
app/views/collection/cover-item.php
Normal file
28
app/views/collection/cover-item.php
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<article class="media" id="a-<?= $item['hummingbird_id'] ?>">
|
||||||
|
<?= $_->h->picture("images/anime/{$item['hummingbird_id']}.webp") ?>
|
||||||
|
<div class="name">
|
||||||
|
<a href="<?= $_->urlFromRoute('anime.details', ['id' => $item['slug']]) ?>">
|
||||||
|
<?= $item['title'] ?>
|
||||||
|
<?= ($item['alternate_title'] != "") ? "<small><br />{$item['alternate_title']}</small>" : ""; ?>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="table">
|
||||||
|
<?php if ($_->isAuthenticated()): ?>
|
||||||
|
<div class="row">
|
||||||
|
<span class="edit">
|
||||||
|
<a class="bracketed"
|
||||||
|
href="<?= $_->urlFromRoute($collection_type . '.collection.edit.get', [
|
||||||
|
'id' => $item['hummingbird_id']
|
||||||
|
]) ?>">Edit</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<?php endif ?>
|
||||||
|
<div class="row">
|
||||||
|
<?php if ($item['episode_count'] > 1): ?>
|
||||||
|
<div class="completion">Episodes: <?= $item['episode_count'] ?></div>
|
||||||
|
<?php endif ?>
|
||||||
|
<div class="media_type"><?= $item['show_type'] ?></div>
|
||||||
|
<div class="age-rating"><?= $item['age_rating'] ?></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
@ -1,40 +1,26 @@
|
|||||||
<main>
|
<?php use function Aviat\AnimeClient\renderTemplate; ?>
|
||||||
<?php if ($auth->is_authenticated()): ?>
|
<main class="media-list">
|
||||||
<a class="bracketed" href="<?= $urlGenerator->url('collection/add', 'anime') ?>">Add Item</a>
|
<?php if ($_->isAuthenticated()): ?>
|
||||||
|
<a class="bracketed" href="<?= $_->urlFromRoute($collection_type . '.collection.add.get') ?>">Add Item</a>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
<?php if (empty($sections)): ?>
|
<?php if (empty($sections)): ?>
|
||||||
<h3>There's nothing here!</h3>
|
<h3>There's nothing here!</h3>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<?php foreach ($sections as $name => $items): ?>
|
<br />
|
||||||
<section class="status">
|
<label>Filter: <input type='text' class='media-filter' /></label>
|
||||||
<h2><?= $name ?></h2>
|
<br />
|
||||||
<section class="media-wrap">
|
<?= $_->component->tabs('collection-tab', $sections, static function ($items) use ($_, $collection_type) {
|
||||||
<?php foreach($items as $item): ?>
|
$rendered = [];
|
||||||
<article class="media" id="a-<?= $item['hummingbird_id'] ?>">
|
foreach ($items as $item)
|
||||||
<img src="<?= $urlGenerator->asset_url('images', 'anime', basename($item['cover_image'])) ?>" />
|
{
|
||||||
<div class="name">
|
$rendered[] = renderTemplate(__DIR__ . '/cover-item.php', [
|
||||||
<a href="https://hummingbird.me/anime/<?= $item['slug'] ?>">
|
'_' => $_,
|
||||||
<?= $item['title'] ?>
|
'collection_type' => $collection_type,
|
||||||
<?= ($item['alternate_title'] != "") ? "<br />({$item['alternate_title']})" : ""; ?>
|
'item' => $item,
|
||||||
</a>
|
]);
|
||||||
</div>
|
}
|
||||||
<div class="table">
|
|
||||||
<?php if ($auth->is_authenticated()): ?>
|
return implode('', array_map('mb_trim', $rendered));
|
||||||
<div class="row">
|
}, 'media-wrap', true) ?>
|
||||||
<span class="edit"><a class="bracketed" href="<?= $urlGenerator->url("collection/edit/{$item['hummingbird_id']}") ?>">Edit</a></span>
|
|
||||||
<?php /*<span class="delete"><a class="bracketed" href="<?= $urlGenerator->url("collection/delete/{$item['hummingbird_id']}") ?>">Delete</a></span> */ ?>
|
|
||||||
</div>
|
|
||||||
<?php endif ?>
|
|
||||||
<div class="row">
|
|
||||||
<div class="completion">Episodes: <?= $item['episode_count'] ?></div>
|
|
||||||
<div class="media_type"><?= $item['show_type'] ?></div>
|
|
||||||
<div class="age_rating"><?= $item['age_rating'] ?></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
<?php endforeach ?>
|
|
||||||
</section>
|
|
||||||
</section>
|
|
||||||
<?php endforeach ?>
|
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
</main>
|
</main>
|
||||||
|
@ -1,37 +1,36 @@
|
|||||||
<?php if ($auth->is_authenticated()): ?>
|
<?php use function Aviat\AnimeClient\renderTemplate ?>
|
||||||
|
<?php if ($_->isAuthenticated()): ?>
|
||||||
<main>
|
<main>
|
||||||
<h2>Edit Anime Collection Item</h2>
|
<h2>Edit Anime Collection Item</h2>
|
||||||
<form action="<?= $action_url ?>" method="post">
|
<form action="<?= $action_url ?>" method="post">
|
||||||
<table class="form">
|
<table class="invisible form">
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>
|
|
||||||
<h3><?= $escape->html($item['title']) ?></h3>
|
|
||||||
<?php if($item['alternate_title'] != ""): ?>
|
|
||||||
<h4><?= $escape->html($item['alternate_title']) ?></h4>
|
|
||||||
<?php endif ?>
|
|
||||||
</th>
|
|
||||||
<th>
|
|
||||||
<article class="media">
|
|
||||||
<?= $helper->img($item['cover_image']); ?>
|
|
||||||
</article>
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td><label for="media_id">Media</label></td>
|
<td rowspan="6" class="align-center">
|
||||||
<td>
|
<?= $_->h->picture("images/anime/{$item['hummingbird_id']}-original.webp", "jpg", [], ["width" => "390"]) ?>
|
||||||
<select name="media_id" id="media_id">
|
</td>
|
||||||
<?php foreach($media_items as $id => $name): ?>
|
</tr>
|
||||||
<option <?= $item['media_id'] == $id ? 'selected="selected"' : '' ?> value="<?= $id ?>"><?= $name ?></option>
|
<tr>
|
||||||
<?php endforeach ?>
|
<td class="align-right"><label for="title">Title</label></td>
|
||||||
</select>
|
<td class="align-left">
|
||||||
|
<input type="text" id="title" name="title" value="<?= $item['title'] ?>" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="align-right"><label for="alternate_title">Alternate Title</label></td>
|
||||||
|
<td class="align-left">
|
||||||
|
<input type="text" id="alternate_title" name="alternate_title" value="<?= $item['alternate_title'] ?>"/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="align-right"><label for="media_id">Media</label></td>
|
||||||
|
<td class="align-left">
|
||||||
|
<?php include 'media-select-list.php' ?>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><label for="notes">Notes</label></td>
|
<td><label for="notes">Notes</label></td>
|
||||||
<td><textarea id="notes" name="notes"><?= $escape->html($item['notes']) ?></textarea></td>
|
<td><textarea id="notes" name="notes"><?= $_->escape->html($item['notes']) ?></textarea></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td> </td>
|
<td> </td>
|
||||||
@ -45,12 +44,23 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</form>
|
</form>
|
||||||
|
<form class="js-delete" action="<?= $_->urlFromRoute($collection_type . '.collection.delete') ?>" method="post">
|
||||||
|
<fieldset>
|
||||||
|
<legend>Danger Zone</legend>
|
||||||
|
<table class="form invisible">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td class="danger">
|
||||||
|
<strong>Permanently</strong> remove this list item and <strong>all</strong> its data?
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="hidden" value="<?= $item['hummingbird_id'] ?>" name="hummingbird_id" />
|
||||||
|
<button type="submit" class="danger">Delete Entry</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
</main>
|
</main>
|
||||||
<template id="show_list">
|
|
||||||
<article class="media">
|
|
||||||
<div class="name"><label><input type="radio" name="id" value="{{:id}}" /> <span>{{:title}}<br />{{:alternate_title}}</span></label></div>
|
|
||||||
<img src="{{:cover_image}}" alt="{{:title}}" />
|
|
||||||
</article>
|
|
||||||
</template>
|
|
||||||
<script src="<?= $urlGenerator->asset_url('js.php?g=anime_collection') ?>"></script>
|
|
||||||
<?php endif ?>
|
<?php endif ?>
|
23
app/views/collection/list-item.php
Normal file
23
app/views/collection/list-item.php
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<tr>
|
||||||
|
<?php if ($_->isAuthenticated()): ?>
|
||||||
|
<td>
|
||||||
|
<a class="bracketed"
|
||||||
|
href="<?= $_->urlFromRoute($collection_type . '.collection.edit.get', ['id' => $item['hummingbird_id']]) ?>">Edit</a>
|
||||||
|
</td>
|
||||||
|
<?php endif ?>
|
||||||
|
<td class="align-left">
|
||||||
|
<a href="<?= $_->urlFromRoute('anime.details', ['id' => $item['slug']]) ?>">
|
||||||
|
<?= $item['title'] ?>
|
||||||
|
</a>
|
||||||
|
<?= ! empty($item['alternate_title']) ? ' <br /><small> ' . $item['alternate_title'] . '</small>' : '' ?>
|
||||||
|
</td>
|
||||||
|
<?php if ($hasMedia): ?>
|
||||||
|
<td><?= implode(', ', $item['media']) ?></td>
|
||||||
|
<?php endif ?>
|
||||||
|
<td><?= ($item['episode_count'] > 1) ? $item['episode_count'] : '-' ?></td>
|
||||||
|
<td><?= $item['episode_length'] ?></td>
|
||||||
|
<td><?= $item['show_type'] ?></td>
|
||||||
|
<td><?= $item['age_rating'] ?></td>
|
||||||
|
<?php if ($hasNotes): ?><td class="align-left"><?= nl2br($item['notes'] ?? '', TRUE) ?></td><?php endif ?>
|
||||||
|
<td class="align-left"><?= implode(', ', $item['genres']) ?></td>
|
||||||
|
</tr>
|
@ -1,52 +1,55 @@
|
|||||||
|
<?php use function Aviat\AnimeClient\{colNotEmpty, renderTemplate}; ?>
|
||||||
<main>
|
<main>
|
||||||
<?php if ($auth->is_authenticated()): ?>
|
<?php if ($_->isAuthenticated()): ?>
|
||||||
<a class="bracketed" href="<?= $urlGenerator->full_url('collection/add', 'anime') ?>">Add Item</a>
|
<a class="bracketed" href="<?= $_->urlFromRoute($collection_type . '.collection.add.get') ?>">Add Item</a>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
<?php if (empty($sections)): ?>
|
<?php if (empty($sections)): ?>
|
||||||
<h3>There's nothing here!</h3>
|
<h3>There's nothing here!</h3>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<?php foreach ($sections as $name => $items): ?>
|
<br />
|
||||||
<h2><?= $name ?></h2>
|
<label>Filter: <input type='text' class='media-filter' /></label>
|
||||||
<table>
|
<br />
|
||||||
<thead>
|
<?= $_->component->tabs('collection-tab', $sections, static function ($items, $section) use ($_, $helper, $collection_type) {
|
||||||
<tr>
|
$hasNotes = colNotEmpty($items, 'notes');
|
||||||
<?php if($auth->is_authenticated()): ?>
|
$hasMedia = $section === 'All';
|
||||||
<th>Actions</th>
|
$firstTh = ($_->isAuthenticated()) ? '<td> </td>' : '';
|
||||||
<?php endif ?>
|
$mediaTh = ($hasMedia) ? '<th>Media</th>' : '';
|
||||||
<th>Title</th>
|
$noteTh = ($hasNotes) ? '<th>Notes</th>' : '';
|
||||||
<th>Episode Count</th>
|
|
||||||
<th>Episode Length</th>
|
$rendered = [];
|
||||||
<th>Show Type</th>
|
foreach ($items as $item)
|
||||||
<th>Age Rating</th>
|
{
|
||||||
<th>Notes</th>
|
$rendered[] = renderTemplate(__DIR__ . '/list-item.php', [
|
||||||
</tr>
|
'_' => $_,
|
||||||
</thead>
|
'collection_type' => $collection_type,
|
||||||
<tbody>
|
'hasMedia' => $hasMedia,
|
||||||
<?php foreach($items as $item): ?>
|
'hasNotes' => $hasNotes,
|
||||||
<tr>
|
'helper' => $helper,
|
||||||
<?php if($auth->is_authenticated()): ?>
|
'item' => $item,
|
||||||
<td>
|
]);
|
||||||
<a class="bracketed" href="<?= $urlGenerator->full_url("collection/edit/{$item['hummingbird_id']}") ?>">Edit</a>
|
}
|
||||||
<?php /*<a class="bracketed" href="<?= $urlGenerator->full_url("collection/delete/{$item['hummingbird_id']}") ?>">Delete</a>*/ ?>
|
$rows = implode('', array_map('mb_trim', $rendered));
|
||||||
</td>
|
|
||||||
<?php endif ?>
|
return <<<HTML
|
||||||
<td class="align_left">
|
<table class="full-width media-wrap">
|
||||||
<a href="https://hummingbird.me/anime/<?= $item['slug'] ?>">
|
<thead>
|
||||||
<?= $item['title'] ?>
|
<tr>
|
||||||
</a>
|
{$firstTh}
|
||||||
<?= ( ! empty($item['alternate_title'])) ? " · " . $item['alternate_title'] : "" ?>
|
<th>Title</th>
|
||||||
</td>
|
{$mediaTh}
|
||||||
<td><?= $item['episode_count'] ?></td>
|
<th class='numeric'>Episode Count</th>
|
||||||
<td><?= $item['episode_length'] ?></td>
|
<th class='numeric'>Episode Length</th>
|
||||||
<td><?= $item['show_type'] ?></td>
|
<th>Show Type</th>
|
||||||
<td><?= $item['age_rating'] ?></td>
|
<th class='rating'>Age Rating</th>
|
||||||
<td class="align_left"><?= $item['notes'] ?></td>
|
{$noteTh}
|
||||||
</tr>
|
<th>Genres</th>
|
||||||
<?php endforeach ?>
|
</tr>
|
||||||
</tbody>
|
</thead>
|
||||||
</table>
|
<tbody>{$rows}</tbody>
|
||||||
<br />
|
</table>
|
||||||
<?php endforeach ?>
|
HTML;
|
||||||
<?php endif ?>
|
|
||||||
|
}) ?>
|
||||||
|
<?php endif ?>
|
||||||
</main>
|
</main>
|
||||||
<script src="<?= $urlGenerator->asset_url('js.php?g=table') ?>"></script>
|
<script defer="defer" src="<?= $_->assetUrl('js/tables.min.js') ?>"></script>
|
11
app/views/collection/media-select-list.php
Normal file
11
app/views/collection/media-select-list.php
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<select name="media_id[]" id="media_id" multiple size="13">
|
||||||
|
<?php foreach ($media_items as $group => $items): ?>
|
||||||
|
<optgroup label='<?= $group ?>'>
|
||||||
|
<?php foreach ($items as $id => $name): ?>
|
||||||
|
<option <?= in_array($id, ($item['media_id'] ?? []), FALSE) ? 'selected="selected"' : '' ?> value="<?= $id ?>">
|
||||||
|
<?= $name ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach ?>
|
||||||
|
</optgroup>
|
||||||
|
<?php endforeach ?>
|
||||||
|
</select>
|
@ -1,2 +1,15 @@
|
|||||||
|
<section id="loading-shadow" hidden="hidden">
|
||||||
|
<div class="loading-wrapper">
|
||||||
|
<div class="loading-content">
|
||||||
|
<h3>Updating List Item...</h3>
|
||||||
|
<div class="cssload-loader">
|
||||||
|
<div class="cssload-inner cssload-one"></div>
|
||||||
|
<div class="cssload-inner cssload-two"></div>
|
||||||
|
<div class="cssload-inner cssload-three"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<script async="async" defer="defer" src="<?= $_->assetUrl('js/scripts.min.js') ?>"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
@ -1,58 +1,42 @@
|
|||||||
<?php namespace Aviat\AnimeClient ?>
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
|
<meta charset="utf-8"/>
|
||||||
<title><?= $title ?></title>
|
<title><?= $title ?></title>
|
||||||
<meta charset="utf-8" />
|
<meta http-equiv="cache-control" content="no-store" />
|
||||||
<link rel="stylesheet" href="<?= $urlGenerator->asset_url('css.php?g=base') ?>" />
|
<meta http-equiv="Content-Security-Policy" content="script-src 'self'" />
|
||||||
<script>
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1, user-scalable=1" />
|
||||||
var BASE_URL = "<?= $urlGenerator->base_url($url_type) ?>";
|
<link rel="stylesheet" href="<?= $_->assetUrl('css/' . $_->config->get('theme') . '.min.css') ?>" />
|
||||||
var CONTROLLER = "<?= $url_type ?>";
|
<link rel="<?= $_->config->get('theme') === 'dark' ? '' : 'alternate ' ?>stylesheet" title="Dark Theme" href="<?= $_->assetUrl('css/dark.min.css') ?>" />
|
||||||
</script>
|
<link rel="icon" href="<?= $_->assetUrl('images/icons/favicon.ico') ?>" />
|
||||||
|
<link rel="apple-touch-icon" sizes="57x57" href="<?= $_->assetUrl('images/icons/apple-icon-57x57.png') ?>">
|
||||||
|
<link rel="apple-touch-icon" sizes="60x60" href="<?= $_->assetUrl('images/icons/apple-icon-60x60.png') ?>">
|
||||||
|
<link rel="apple-touch-icon" sizes="72x72" href="<?= $_->assetUrl('images/icons/apple-icon-72x72.png') ?>">
|
||||||
|
<link rel="apple-touch-icon" sizes="76x76" href="<?= $_->assetUrl('images/icons/apple-icon-76x76.png') ?>">
|
||||||
|
<link rel="apple-touch-icon" sizes="114x114" href="<?= $_->assetUrl('images/icons/apple-icon-114x114.png') ?>">
|
||||||
|
<link rel="apple-touch-icon" sizes="120x120" href="<?= $_->assetUrl('images/icons/apple-icon-120x120.png') ?>">
|
||||||
|
<link rel="apple-touch-icon" sizes="144x144" href="<?= $_->assetUrl('images/icons/apple-icon-144x144.png') ?>">
|
||||||
|
<link rel="apple-touch-icon" sizes="152x152" href="<?= $_->assetUrl('images/icons/apple-icon-152x152.png') ?>">
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" href="<?= $_->assetUrl('images/icons/apple-icon-180x180.png') ?>">
|
||||||
|
<link rel="icon" type="image/png" sizes="192x192" href="<?= $_->assetUrl('images/icons/android-icon-192x192.png') ?>">
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href="<?= $_->assetUrl('images/icons/favicon-32x32.png') ?>">
|
||||||
|
<link rel="icon" type="image/png" sizes="96x96" href="<?= $_->assetUrl('images/icons/favicon-96x96.png') ?>">
|
||||||
|
<link rel="icon" type="image/png" sizes="16x16" href="<?= $_->assetUrl('images/icons/favicon-16x16.png') ?>">
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body class="<?= $escape->attr($url_type) ?> list">
|
<body class="<?= $_->escape->attr($url_type) ?> list">
|
||||||
|
<?php include 'setup-check.php' ?>
|
||||||
<header>
|
<header>
|
||||||
<h1 class="flex flex-align-end flex-wrap">
|
<?php
|
||||||
<span class="flex-no-wrap grow-1">
|
include 'main-menu.php';
|
||||||
<?php if(strpos($route_path, 'collection') === FALSE): ?>
|
if(isset($message) && is_array($message))
|
||||||
<a href="<?= $escape->attr($urlGenerator->default_url($url_type)) ?>">
|
{
|
||||||
<?= $config->get('whose_list') ?>'s <?= ucfirst($url_type) ?> List
|
foreach($message as $m)
|
||||||
</a>
|
{
|
||||||
<?php if($config->get("show_{$url_type}_collection")): ?>
|
$message = $m['message'];
|
||||||
[<a href="<?= $urlGenerator->url('collection/view') ?>"><?= ucfirst($url_type) ?> Collection</a>]
|
$message_type = $m['message_type'];
|
||||||
<?php endif ?>
|
include 'message.php';
|
||||||
[<a href="<?= $urlGenerator->default_url($other_type) ?>"><?= ucfirst($other_type) ?> List</a>]
|
}
|
||||||
<?php else: ?>
|
}
|
||||||
<a href="<?= $urlGenerator->url('collection/view') ?>">
|
?>
|
||||||
<?= $config->get('whose_list') ?>'s <?= ucfirst($url_type) ?> Collection
|
|
||||||
</a>
|
|
||||||
[<a href="<?= $urlGenerator->default_url('anime') ?>">Anime List</a>]
|
|
||||||
[<a href="<?= $urlGenerator->default_url('manga') ?>">Manga List</a>]
|
|
||||||
<?php endif ?>
|
|
||||||
</span>
|
|
||||||
<span class="flex-no-wrap small-font">
|
|
||||||
<?php if ($auth->is_authenticated()): ?>
|
|
||||||
<a class="bracketed" href="<?= $urlGenerator->url("/{$url_type}/logout", $url_type) ?>">Logout</a>
|
|
||||||
<?php else: ?>
|
|
||||||
[<a href="<?= $urlGenerator->url("/{$url_type}/login", $url_type) ?>"><?= $config->get('whose_list') ?>'s Login</a>]
|
|
||||||
<?php endif ?>
|
|
||||||
</span>
|
|
||||||
</h1>
|
|
||||||
<nav>
|
|
||||||
<?php if ($container->get('anime-client')->is_view_page()): ?>
|
|
||||||
<?= $helper->menu($menu_name) ?>
|
|
||||||
<br />
|
|
||||||
<ul>
|
|
||||||
<li class="<?= AnimeClient::is_not_selected('list', $urlGenerator->last_segment()) ?>"><a href="<?= $urlGenerator->url($route_path) ?>">Cover View</a></li>
|
|
||||||
<li class="<?= AnimeClient::is_selected('list', $urlGenerator->last_segment()) ?>"><a href="<?= $urlGenerator->url("{$route_path}/list") ?>">List View</a></li>
|
|
||||||
</ul>
|
|
||||||
<?php endif ?>
|
|
||||||
</nav>
|
|
||||||
</header>
|
</header>
|
||||||
<?php if(isset($message) && is_array($message)): ?>
|
|
||||||
<div class="message <?= $escape->attr($message['message_type']) ?>">
|
|
||||||
<span class="icon"></span>
|
|
||||||
<?= $escape->html($message['message']) ?>
|
|
||||||
<span class="close" onclick="this.parentElement.style.display='none'">x</span>
|
|
||||||
</div>
|
|
||||||
<?php endif ?>
|
|
||||||
|
47
app/views/history.php
Normal file
47
app/views/history.php
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<main class="details fixed">
|
||||||
|
<?php if (empty($items)): ?>
|
||||||
|
<h3>No recent history.</h3>
|
||||||
|
<?php else: ?>
|
||||||
|
<section>
|
||||||
|
<?php foreach ($items as $name => $item): ?>
|
||||||
|
<article class="flex flex-no-wrap flex-justify-start">
|
||||||
|
<section class="flex-self-center history-img">
|
||||||
|
<a href="<?= $item['url'] ?>">
|
||||||
|
<?= $_->h->img(
|
||||||
|
$item['coverImg'],
|
||||||
|
['width' => '110px', 'height' => '156px'],
|
||||||
|
) ?>
|
||||||
|
</a>
|
||||||
|
</section>
|
||||||
|
<section class="flex-self-center">
|
||||||
|
<?= $_->h->a($item['url'], $item['title']) ?>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<?= $item['action'] ?>
|
||||||
|
<br />
|
||||||
|
<small>
|
||||||
|
<?php if ( ! empty($item['dateRange'])):
|
||||||
|
[$startDate, $endDate] = array_map(
|
||||||
|
fn ($date) => $date->format('l, F d'),
|
||||||
|
$item['dateRange']
|
||||||
|
);
|
||||||
|
[$startTime, $endTime] = array_map(
|
||||||
|
fn ($date) => $date->format('h:i:s A'),
|
||||||
|
$item['dateRange']
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
<?php if ($startDate === $endDate): ?>
|
||||||
|
<?= "{$startDate}, {$startTime} – {$endTime}" ?>
|
||||||
|
<?php else: ?>
|
||||||
|
<?= "{$startDate} {$startTime} – {$endDate} {$endTime}" ?>
|
||||||
|
<?php endif ?>
|
||||||
|
<?php else: ?>
|
||||||
|
<?= $item['updated']->format('l, F d h:i:s A') ?>
|
||||||
|
<?php endif ?>
|
||||||
|
</small>
|
||||||
|
</section>
|
||||||
|
</article>
|
||||||
|
<?php endforeach ?>
|
||||||
|
</section>
|
||||||
|
<?php endif ?>
|
||||||
|
</main>
|
6
app/views/js-warning.php
Normal file
6
app/views/js-warning.php
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<noscript>
|
||||||
|
<div class="message error">
|
||||||
|
<span class="icon"></span>
|
||||||
|
This feature requires Javascript to function :(
|
||||||
|
</div>
|
||||||
|
</noscript>
|
@ -1,7 +1,7 @@
|
|||||||
<main>
|
<main>
|
||||||
<h2><?= $config->get('whose_list'); ?>'s Login</h2>
|
<h2><?= $_->config->get('whose_list'); ?>'s Login</h2>
|
||||||
<?= $message ?>
|
<?= $message ?>
|
||||||
<form method="post" action="<?= $urlGenerator->full_url($urlGenerator->path(), $url_type) ?>">
|
<form method="post" action="<?= $_->urlFromRoute('login.post') ?>">
|
||||||
<table class="form invisible">
|
<table class="form invisible">
|
||||||
<tr>
|
<tr>
|
||||||
<td><label for="password">Password: </label></td>
|
<td><label for="password">Password: </label></td>
|
||||||
|
114
app/views/main-menu.php
Normal file
114
app/views/main-menu.php
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
/**
|
||||||
|
* Hummingbird Anime List Client
|
||||||
|
*
|
||||||
|
* An API client for Kitsu to manage anime and manga watch lists
|
||||||
|
*
|
||||||
|
* PHP version 8.1
|
||||||
|
*
|
||||||
|
* @copyright 2015 - 2023 Timothy J. Warren <tim@timshome.page>
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||||
|
* @version 5.2
|
||||||
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Aviat\AnimeClient;
|
||||||
|
|
||||||
|
$whose = $_->config->get('whose_list') . "'s ";
|
||||||
|
$lastSegment = $_->lastSegment();
|
||||||
|
$extraSegment = $lastSegment === 'list' ? '/list' : '';
|
||||||
|
$hasAnime = str_contains($GLOBALS['_SERVER']['REQUEST_URI'], 'anime');
|
||||||
|
$hasManga = str_contains($GLOBALS['_SERVER']['REQUEST_URI'], 'manga');
|
||||||
|
|
||||||
|
?>
|
||||||
|
<div id="main-nav" class="flex flex-align-end flex-wrap">
|
||||||
|
<span class="flex-no-wrap grow-1">
|
||||||
|
<?php if( ! str_contains($route_path, 'collection')): ?>
|
||||||
|
<?= $_->h->a(
|
||||||
|
$_->defaultUrl($url_type),
|
||||||
|
$whose . ucfirst($url_type) . ' List',
|
||||||
|
['aria-current'=> 'page']
|
||||||
|
) ?>
|
||||||
|
<?php if($_->config->get("show_{$url_type}_collection")): ?>
|
||||||
|
[<?= $_->h->a(
|
||||||
|
$_->urlFromRoute("{$url_type}.collection.view") . $extraSegment,
|
||||||
|
ucfirst($url_type) . ' Collection'
|
||||||
|
) ?>]
|
||||||
|
<?php endif ?>
|
||||||
|
<?php if($_->config->get("show_{$other_type}_collection")): ?>
|
||||||
|
[<?= $_->h->a(
|
||||||
|
$_->urlFromRoute("{$other_type}.collection.view") . $extraSegment,
|
||||||
|
ucfirst($other_type) . ' Collection'
|
||||||
|
) ?>]
|
||||||
|
<?php endif ?>
|
||||||
|
[<?= $_->h->a(
|
||||||
|
$_->defaultUrl($other_type) . $extraSegment,
|
||||||
|
ucfirst($other_type) . ' List'
|
||||||
|
) ?>]
|
||||||
|
<?php else: ?>
|
||||||
|
<?= $_->h->a(
|
||||||
|
$_->urlFromRoute("{$url_type}.collection.view") . $extraSegment,
|
||||||
|
$whose . ucfirst($url_type) . ' Collection',
|
||||||
|
['aria-current'=> 'page']
|
||||||
|
) ?>
|
||||||
|
<?php if($_->config->get("show_{$other_type}_collection")): ?>
|
||||||
|
[<?= $_->h->a(
|
||||||
|
$_->urlFromRoute("{$other_type}.collection.view") . $extraSegment,
|
||||||
|
ucfirst($other_type) . ' Collection'
|
||||||
|
) ?>]
|
||||||
|
<?php endif ?>
|
||||||
|
[<?= $_->h->a($_->defaultUrl('anime') . $extraSegment, 'Anime List') ?>]
|
||||||
|
[<?= $_->h->a($_->defaultUrl('manga') . $extraSegment, 'Manga List') ?>]
|
||||||
|
<?php endif ?>
|
||||||
|
<?php if ($_->isAuthenticated() && $_->config->get(['cache', 'driver']) !== 'null'): ?>
|
||||||
|
<span class="flex-no-wrap small-font">
|
||||||
|
<button type="button" class="js-clear-cache user-btn">Clear API Cache</button>
|
||||||
|
</span>
|
||||||
|
<?php endif ?>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span class="flex-no-wrap small-font">[<?= $_->h->a(
|
||||||
|
$_->urlFromRoute('default_user_info'),
|
||||||
|
'About '. $_->config->get('whose_list')
|
||||||
|
) ?>]</span>
|
||||||
|
|
||||||
|
<?php if ($_->isAuthenticated()): ?>
|
||||||
|
<span class="flex-no-wrap small-font">
|
||||||
|
<?= $_->h->a(
|
||||||
|
$_->urlFromRoute('settings'),
|
||||||
|
'Settings',
|
||||||
|
['class' => 'bracketed']
|
||||||
|
) ?>
|
||||||
|
</span>
|
||||||
|
<span class="flex-no-wrap small-font">
|
||||||
|
<?= $_->h->a(
|
||||||
|
$_->urlFromRoute('logout'),
|
||||||
|
'Logout',
|
||||||
|
['class' => 'bracketed']
|
||||||
|
) ?>
|
||||||
|
</span>
|
||||||
|
<?php else: ?>
|
||||||
|
<span class="flex-no-wrap small-font">
|
||||||
|
[<?= $_->h->a($_->urlFromRoute('login'), "{$whose} Login") ?>]
|
||||||
|
</span>
|
||||||
|
<?php endif ?>
|
||||||
|
</div>
|
||||||
|
<?php if ($_->isViewPage() && ($hasAnime || $hasManga)): ?>
|
||||||
|
<nav>
|
||||||
|
<?= $_->h->menu($menu_name) ?>
|
||||||
|
<?php if (stripos($GLOBALS['_SERVER']['REQUEST_URI'], 'history') === FALSE): ?>
|
||||||
|
<br />
|
||||||
|
<ul>
|
||||||
|
<?php $currentView = Util::eq('list', $lastSegment) ? 'list' : 'cover' ?>
|
||||||
|
<li class="<?= Util::isNotSelected('list', $lastSegment) ?>">
|
||||||
|
<a aria-current="<?= Util::ariaCurrent($currentView === 'cover') ?>"
|
||||||
|
href="<?= $_->urlFromPath($route_path) ?>">Cover View</a>
|
||||||
|
</li>
|
||||||
|
<li class="<?= Util::isSelected('list', $lastSegment) ?>">
|
||||||
|
<a aria-current="<?= Util::ariaCurrent($currentView === 'list') ?>"
|
||||||
|
href="<?= $_->urlFromPath("{$route_path}/list") ?>">List View</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<?php endif ?>
|
||||||
|
</nav>
|
||||||
|
<?php endif ?>
|
@ -0,0 +1,40 @@
|
|||||||
|
<?php if ($_->isAuthenticated()): ?>
|
||||||
|
<main>
|
||||||
|
<h2>Add Manga to your List</h2>
|
||||||
|
<form action="<?= $action_url ?>" method="post">
|
||||||
|
<?php include realpath(__DIR__ . '/../js-warning.php') ?>
|
||||||
|
<section>
|
||||||
|
<div class="cssload-loader" hidden="hidden">
|
||||||
|
<div class="cssload-inner cssload-one"></div>
|
||||||
|
<div class="cssload-inner cssload-two"></div>
|
||||||
|
<div class="cssload-inner cssload-three"></div>
|
||||||
|
</div>
|
||||||
|
<label for="search">Search for manga by name: <input type="search" id="search" /></label>
|
||||||
|
<section id="series-list" class="media-wrap">
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
<br />
|
||||||
|
<table class="invisible form">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><label for="status">Reading Status</label></td>
|
||||||
|
<td>
|
||||||
|
<select name="status" id="status">
|
||||||
|
<?php foreach($status_list as $status_key => $status_title): ?>
|
||||||
|
<option value="<?= $status_key ?>"><?= $status_title ?></option>
|
||||||
|
<?php endforeach ?>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td> </td>
|
||||||
|
<td>
|
||||||
|
<input type="hidden" name="type" value="manga" />
|
||||||
|
<button type="submit">Save</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</form>
|
||||||
|
</main>
|
||||||
|
<?php endif ?>
|
@ -1,57 +1,29 @@
|
|||||||
<main>
|
<main class="media-list">
|
||||||
|
<?php if ($_->isAuthenticated()): ?>
|
||||||
|
<a class="bracketed" href="<?= $_->urlFromRoute('manga.add.get') ?>">Add Item</a>
|
||||||
|
<?php endif ?>
|
||||||
<?php if (empty($sections)): ?>
|
<?php if (empty($sections)): ?>
|
||||||
<h3>There's nothing here!</h3>
|
<h3>There's nothing here!</h3>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
|
<br />
|
||||||
|
<label>Filter: <input type='text' class='media-filter' /></label>
|
||||||
|
<br />
|
||||||
<?php foreach ($sections as $name => $items): ?>
|
<?php foreach ($sections as $name => $items): ?>
|
||||||
|
<?php if (empty($items)): ?>
|
||||||
<section class="status">
|
<section class="status">
|
||||||
<h2><?= $escape->html($name) ?></h2>
|
<h2><?= $_->escape->html($name) ?></h2>
|
||||||
|
<h3>There's nothing here!</h3>
|
||||||
|
</section>
|
||||||
|
<?php else: ?>
|
||||||
|
<section class="status">
|
||||||
|
<h2><?= $_->escape->html($name) ?></h2>
|
||||||
<section class="media-wrap">
|
<section class="media-wrap">
|
||||||
<?php foreach($items as $item): ?>
|
<?php foreach($items as $item): ?>
|
||||||
<article class="media" id="manga-<?= $item['id'] ?>">
|
<?= $component->mangaCover($item, $name) ?>
|
||||||
<?php if ($auth->is_authenticated()): ?>
|
|
||||||
<div class="edit_buttons" hidden>
|
|
||||||
<button class="plus_one_chapter">+1 Chapter</button>
|
|
||||||
<button class="plus_one_volume">+1 Volume</button>
|
|
||||||
</div>
|
|
||||||
<?php endif ?>
|
|
||||||
<img src="<?= $escape->attr($item['manga']['image']) ?>" />
|
|
||||||
<div class="name">
|
|
||||||
<a href="<?= $item['manga']['url'] ?>">
|
|
||||||
<?= $escape->html($item['manga']['title']) ?>
|
|
||||||
<?= (isset($item['manga']['alternate_title'])) ? "<br />({$item['manga']['alternate_title']})" : ""; ?>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="table">
|
|
||||||
<?php if ($auth->is_authenticated()): ?>
|
|
||||||
<div class="row">
|
|
||||||
<span class="edit">
|
|
||||||
<a class="bracketed" title="Edit information about this manga" href="<?= $urlGenerator->url("manga/edit/{$item['id']}/{$name}") ?>">Edit</a>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<?php endif ?>
|
|
||||||
<div class="row">
|
|
||||||
<div class="user_rating">Rating: <?= $item['user_rating'] ?> / 10</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="chapter_completion">
|
|
||||||
Chapters: <span class="chapters_read"><?= $item['chapters']['read'] ?></span> /
|
|
||||||
<span class="chapter_count"><?= $item['chapters']['total'] ?></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="volume_completion">
|
|
||||||
Volumes: <span class="volumes_read"><?= $item['volumes']['read'] ?></span> /
|
|
||||||
<span class="volume_count"><?= $item['volumes']['total'] ?></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
<?php endforeach ?>
|
<?php endforeach ?>
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
|
<?php endif ?>
|
||||||
<?php endforeach ?>
|
<?php endforeach ?>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
</main>
|
</main>
|
||||||
<?php if ($auth->is_authenticated()): ?>
|
|
||||||
<script src="<?= $urlGenerator->asset_url('js.php?g=edit') ?>"></script>
|
|
||||||
<?php endif ?>
|
|
||||||
|
105
app/views/manga/details.php
Normal file
105
app/views/manga/details.php
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
<main class="details fixed">
|
||||||
|
<section class="flex flex-no-wrap">
|
||||||
|
<aside class="info">
|
||||||
|
<?= $_->h->img($data['cover_image'], ['class' => 'cover', 'width' => '350']) ?>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<table class="media-details">
|
||||||
|
<tr>
|
||||||
|
<td class="align-right">Publishing Status</td>
|
||||||
|
<td><?= $data['status'] ?></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Manga Type</td>
|
||||||
|
<td><?= ucfirst(strtolower($data['manga_type'])) ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php if ( ! empty($data['volume_count'])): ?>
|
||||||
|
<tr>
|
||||||
|
<td>Volume Count</td>
|
||||||
|
<td><?= $data['volume_count'] ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endif ?>
|
||||||
|
<?php if ( ! empty($data['chapter_count'])): ?>
|
||||||
|
<tr>
|
||||||
|
<td>Chapter Count</td>
|
||||||
|
<td><?= $data['chapter_count'] ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endif ?>
|
||||||
|
|
||||||
|
<?php if ( ! empty($data['age_rating'])): ?>
|
||||||
|
<tr>
|
||||||
|
<td>Age Rating</td>
|
||||||
|
<td><abbr title="<?= $data['age_rating_guide'] ?>"><?= $data['age_rating'] ?></abbr>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endif ?>
|
||||||
|
|
||||||
|
<?php if (count($data['links']) > 0): ?>
|
||||||
|
<tr>
|
||||||
|
<td>External Links</td>
|
||||||
|
<td>
|
||||||
|
<?php foreach ($data['links'] as $urlName => $externalUrl): ?>
|
||||||
|
<a rel='external' href="<?= $externalUrl ?>"><?= $urlName ?></a><br />
|
||||||
|
<?php endforeach ?>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endif ?>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>Genres</td>
|
||||||
|
<td>
|
||||||
|
<?= implode(', ', $data['genres']); ?>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
</aside>
|
||||||
|
<article class="text">
|
||||||
|
<h2 class="toph"><?= $data['title'] ?></h2>
|
||||||
|
<?php foreach ($data['titles_more'] as $title): ?>
|
||||||
|
<h3><?= $title ?></h3>
|
||||||
|
<?php endforeach ?>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<div class="description">
|
||||||
|
<p><?= str_replace("\n", '</p><p>', $data['synopsis']) ?></p>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<?php if (count($data['characters']) > 0): ?>
|
||||||
|
<h2>Characters</h2>
|
||||||
|
|
||||||
|
<?= $component->tabs('manga-characters', $data['characters'], static function($list, $role) use ($component, $helper, $_) {
|
||||||
|
$rendered = [];
|
||||||
|
foreach ($list as $id => $char)
|
||||||
|
{
|
||||||
|
$rendered[] = $component->character(
|
||||||
|
$char['name'],
|
||||||
|
$_->urlFromRoute('character', ['slug' => $char['slug']]),
|
||||||
|
$_->h->img($char['image'], ['loading' => 'lazy']),
|
||||||
|
($role !== 'main') ? 'small-character' : 'character'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return implode('', array_map('mb_trim', $rendered));
|
||||||
|
}) ?>
|
||||||
|
<?php endif ?>
|
||||||
|
|
||||||
|
<?php if (count($data['staff']) > 0): ?>
|
||||||
|
<h2>Staff</h2>
|
||||||
|
|
||||||
|
<?= $component->verticalTabs('manga-staff', $data['staff'],
|
||||||
|
fn($people) => implode('', array_map(
|
||||||
|
fn ($person) => $component->character(
|
||||||
|
$person['name'],
|
||||||
|
$_->urlFromRoute('person', ['slug' => $person['slug']]),
|
||||||
|
$_->h->img($person['image']),
|
||||||
|
),
|
||||||
|
$people
|
||||||
|
))
|
||||||
|
) ?>
|
||||||
|
<?php endif ?>
|
||||||
|
</main>
|
@ -1,89 +1,104 @@
|
|||||||
<?php if ($auth->is_authenticated()): ?>
|
<?php if ($_->isAuthenticated()): ?>
|
||||||
<main>
|
<main>
|
||||||
<h1>
|
<h2>
|
||||||
Edit <?= $item['manga']['title'] ?>
|
Edit Manga List Item
|
||||||
<?= ($item['manga']['alternate_title'] != "") ? "({$item['manga']['alternate_title']})" : ""; ?>
|
</h2>
|
||||||
</h1>
|
|
||||||
<form action="<?= $action ?>" method="post">
|
<form action="<?= $action ?>" method="post">
|
||||||
<table class="form">
|
<table class="invisible form">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>
|
<th>
|
||||||
<h3><?= $escape->html($item['manga']['title']) ?></h3>
|
<h3><?= $_->escape->html($item['manga']['title']) ?></h3>
|
||||||
<?php if($item['manga']['alternate_title'] != ""): ?>
|
<?php foreach ($item['manga']['titles'] as $title): ?>
|
||||||
<h4><?= $escape->html($item['manga']['alternate_title']) ?></h4>
|
<h4><?= $_->escape->html($title) ?></h4>
|
||||||
<?php endif ?>
|
<?php endforeach ?>
|
||||||
</th>
|
|
||||||
<th>
|
|
||||||
<article class="media">
|
|
||||||
<?= $helper->img($item['manga']['image']); ?>
|
|
||||||
</article>
|
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td rowspan="9">
|
||||||
|
<?= $_->h->img($item['manga']['image']) ?>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><label for="status">Reading Status</label></td>
|
<td><label for="status">Reading Status</label></td>
|
||||||
<td>
|
<td>
|
||||||
<select name="status" id="status">
|
<select name="status" id="status">
|
||||||
<?php foreach($status_list as $status): ?>
|
<?php foreach ($status_list as $val => $status): ?>
|
||||||
<option <?php if($item['reading_status'] === $status): ?>selected="selected"<?php endif ?>
|
<option <?php if ($item['reading_status'] === $val): ?>selected="selected"<?php endif ?>
|
||||||
value="<?= $status ?>"><?= $status ?></option>
|
value="<?= $val ?>"><?= $status ?></option>
|
||||||
<?php endforeach ?>
|
<?php endforeach ?>
|
||||||
</select>
|
</select>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><label for="series_rating">Rating</label></td>
|
<td><label for="series_rating">Rating</label></td>
|
||||||
<td>
|
<td>
|
||||||
<input type="number" min="0" max="10" maxlength="2" name="new_rating" value="<?= $item['user_rating'] ?>" id="series_rating" size="2" /> / 10
|
<input type="number" min="0" max="10" maxlength="2" name="new_rating"
|
||||||
|
value="<?= $item['user_rating'] ?>" id="series_rating" size="2"/> / 10
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><label for="chapters_read">Chapters Read</label></td>
|
<td><label for="chapters_read">Chapters Read</label></td>
|
||||||
<td>
|
<td>
|
||||||
<input type="number" min="0" name="chapters_read" id="chapters_read" value="<?= $item['chapters']['read'] ?>" /> / <?= $item['chapters']['total'] ?>
|
<input type="number" min="0" name="chapters_read" id="chapters_read"
|
||||||
</td>
|
value="<?= $item['chapters']['read'] ?>"/> / <?= $item['chapters']['total'] ?>
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><label for="volumes_read">Volumes Read</label></td>
|
|
||||||
<td>
|
|
||||||
<input type="number" min="0" name="volumes_read" id="volumes_read" value="<?= $item['volumes']['read'] ?>" /> / <?= $item['volumes']['total'] ?>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><label for="rereading_flag">Rereading?</label></td>
|
<td><label for="rereading_flag">Rereading?</label></td>
|
||||||
<td>
|
<td>
|
||||||
<input type="checkbox" name="reareading" id="rereading_flag"
|
<input type="checkbox" name="rereading" id="rereading_flag"
|
||||||
<?php if($item['rereading'] === TRUE): ?>checked="checked"<?php endif ?>
|
<?php if ($item['rereading'] === TRUE): ?>checked="checked"<?php endif ?>
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><label for="reread_count">Reread Count</label></td>
|
<td><label for="reread_count">Reread Count</label></td>
|
||||||
<td>
|
<td>
|
||||||
<input type="number" min="0" id="reread_count" name="reread_count" value="<?= $item['reread'] ?>" />
|
<input type="number" min="0" id="reread_count" name="reread_count"
|
||||||
|
value="<?= $item['reread'] ?>"/>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><label for="notes">Notes</label></td>
|
<td><label for="notes">Notes</label></td>
|
||||||
<td>
|
<td>
|
||||||
<textarea name="notes" id="notes"><?= $escape->html($item['notes']) ?></textarea>
|
<textarea name="notes" id="notes"><?= $_->escape->html($item['notes']) ?></textarea>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td> </td>
|
<td> </td>
|
||||||
<td>
|
<td>
|
||||||
<input type="hidden" value="<?= $item['id'] ?>" name="id" />
|
<input type="hidden" value="<?= $item['id'] ?>" name="id"/>
|
||||||
<input type="hidden" value="<?= $item['manga']['slug'] ?>" name="manga_id" />
|
<input type="hidden" value="<?= $item['mal_id'] ?>" name="mal_id"/>
|
||||||
<input type="hidden" value="<?= $item['user_rating'] ?>" name="old_rating" />
|
<input type="hidden" value="<?= $item['manga']['slug'] ?>" name="manga_id"/>
|
||||||
<input type="hidden" value="true" name="edit" />
|
<input type="hidden" value="<?= $item['user_rating'] ?>" name="old_rating"/>
|
||||||
|
<input type="hidden" value="true" name="edit"/>
|
||||||
<button type="submit">Submit</button>
|
<button type="submit">Submit</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</form>
|
</form>
|
||||||
|
<fieldset>
|
||||||
|
<legend>Danger Zone</legend>
|
||||||
|
<form class="js-delete" action="<?= $_->urlFromRoute('manga.delete') ?>" method="post">
|
||||||
|
<table class="form invisible">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td class="danger">
|
||||||
|
<strong>Permanently</strong> remove this list item and <strong>all</strong> its data?
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="hidden" value="<?= $item['id'] ?>" name="id"/>
|
||||||
|
<input type="hidden" value="<?= $item['mal_id'] ?>" name="mal_id"/>
|
||||||
|
<button type="submit" class="danger">Delete Entry</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</form>
|
||||||
|
</fieldset>
|
||||||
</main>
|
</main>
|
||||||
<script src="<?= $urlGenerator->asset_url('js.php?g=edit') ?>"></script>
|
|
||||||
<?php endif ?>
|
<?php endif ?>
|
@ -1,45 +1,78 @@
|
|||||||
<main>
|
<main class="media-list">
|
||||||
|
<?php if ($_->isAuthenticated()): ?>
|
||||||
|
<a class="bracketed" href="<?= $_->urlFromRoute('manga.add.get') ?>">Add Item</a>
|
||||||
|
<?php endif ?>
|
||||||
<?php if (empty($sections)): ?>
|
<?php if (empty($sections)): ?>
|
||||||
<h3>There's nothing here!</h3>
|
<h3>There's nothing here!</h3>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
|
<br />
|
||||||
|
<label>Filter: <input type='text' class='media-filter' /></label>
|
||||||
|
<br />
|
||||||
<?php foreach ($sections as $name => $items): ?>
|
<?php foreach ($sections as $name => $items): ?>
|
||||||
<h2><?= $name ?></h2>
|
<h2><?= $name ?></h2>
|
||||||
<table>
|
<?php if (empty($items)): ?>
|
||||||
<thead>
|
<h3>There's nothing here!</h3>
|
||||||
<tr>
|
<?php else: ?>
|
||||||
<?php if ($auth->is_authenticated()): ?>
|
<table class='media-wrap'>
|
||||||
<th> </th>
|
<thead>
|
||||||
<?php endif ?>
|
<tr>
|
||||||
<th>Title</th>
|
<?php if ($_->isAuthenticated()): ?>
|
||||||
<th>Rating</th>
|
<td> </td>
|
||||||
<th>Chapters</th>
|
<?php endif ?>
|
||||||
<th>Volumes</th>
|
<th>Title</th>
|
||||||
<th>Type</th>
|
<th class='numeric'>Score</th>
|
||||||
</tr>
|
<th class='numeric'>Completed Chapters</th>
|
||||||
</thead>
|
<th>Attributes</th>
|
||||||
<tbody>
|
<th>Type</th>
|
||||||
<?php foreach($items as $item): ?>
|
</tr>
|
||||||
<tr id="manga-<?= $item['id'] ?>">
|
</thead>
|
||||||
<?php if($auth->is_authenticated()): ?>
|
<tbody>
|
||||||
<td>
|
<?php foreach($items as $item): ?>
|
||||||
<a class="bracketed" href="<?= $urlGenerator->url("manga/edit/{$item['id']}/{$name}") ?>">Edit</a>
|
<tr id="manga-<?= $item['id'] ?>">
|
||||||
</td>
|
<?php if($_->isAuthenticated()): ?>
|
||||||
<?php endif ?>
|
<td>
|
||||||
<td class="align_left">
|
<a class="bracketed" href="<?= $_->urlFromRoute('edit', [
|
||||||
<a href="<?= $item['manga']['url'] ?>">
|
'controller' => 'manga',
|
||||||
<?= $item['manga']['title'] ?>
|
'id' => $item['id'],
|
||||||
</a>
|
'status' => $name
|
||||||
<?= ( ! is_null($item['manga']['alternate_title'])) ? " · " . $item['manga']['alternate_title'] : "" ?>
|
]) ?>">Edit</a>
|
||||||
</td>
|
</td>
|
||||||
<td><?= $item['user_rating'] ?> / 10</td>
|
<?php endif ?>
|
||||||
<td><?= $item['chapters']['read'] ?> / <?= $item['chapters']['total'] ?></td>
|
<td class="align-left">
|
||||||
<td><?= $item['volumes']['read'] ?> / <?= $item['volumes']['total'] ?></td>
|
<a href="<?= $_->urlFromRoute('manga.details', ['id' => $item['manga']['slug']]) ?>">
|
||||||
<td><?= $item['manga']['type'] ?></td>
|
<?= $item['manga']['title'] ?>
|
||||||
</tr>
|
</a>
|
||||||
<?php endforeach ?>
|
<?php foreach($item['manga']['titles'] as $title): ?>
|
||||||
</tbody>
|
<br /><?= $title ?>
|
||||||
</table>
|
<?php endforeach ?>
|
||||||
|
</td>
|
||||||
|
<td><?= $item['user_rating'] ?> / 10</td>
|
||||||
|
<td><?= $item['chapters']['read'] ?> / <?= $item['chapters']['total'] ?></td>
|
||||||
|
<td>
|
||||||
|
<ul>
|
||||||
|
<?php if ($item['reread'] == 1): ?>
|
||||||
|
<li>Reread once</li>
|
||||||
|
<?php elseif ($item['reread'] == 2): ?>
|
||||||
|
<li>Reread twice</li>
|
||||||
|
<?php elseif ($item['reread'] == 3): ?>
|
||||||
|
<li>Reread thrice</li>
|
||||||
|
<?php elseif ($item['reread'] > 3): ?>
|
||||||
|
<li>Reread <?= $item['reread'] ?> times</li>
|
||||||
|
<?php endif ?>
|
||||||
|
<?php foreach(['rereading'] as $attr): ?>
|
||||||
|
<?php if($item[$attr]): ?>
|
||||||
|
<li><?= ucfirst($attr); ?></li>
|
||||||
|
<?php endif ?>
|
||||||
|
<?php endforeach ?>
|
||||||
|
</ul>
|
||||||
|
</td>
|
||||||
|
<td><?= $item['manga']['type'] ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<?php endif ?>
|
||||||
<?php endforeach ?>
|
<?php endforeach ?>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
</main>
|
</main>
|
||||||
<script src="<?= $urlGenerator->asset_url('js.php?g=table') ?>"></script>
|
<script defer="defer" src="<?= $_->assetUrl('js/tables.min.js') ?>"></script>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<div class="message <?= $escape->attr($message_type) ?>">
|
<div class="message <?= $_->escape->attr($message_type) ?>">
|
||||||
<span class="icon"></span>
|
<span class="icon"></span>
|
||||||
<?= $escape->html($message) ?>
|
<?= $_->escape->html($message) ?>
|
||||||
<span class="close" onclick="this.parentElement.style.display='none'">x</span>
|
<span class="close"></span>
|
||||||
</div>
|
</div>
|
104
app/views/person/details.php
Normal file
104
app/views/person/details.php
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
<main class="details fixed">
|
||||||
|
<section class="flex flex-no-wrap">
|
||||||
|
<div>
|
||||||
|
<?= $_->h->img($data['image'], ['class' => 'cover' ]) ?>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h2 class="toph"><?= $data['name'] ?></h2>
|
||||||
|
<?php foreach ($data['names'] as $name): ?>
|
||||||
|
<h3><?= $name ?></h3>
|
||||||
|
<?php endforeach ?>
|
||||||
|
<?php if ( ! empty($data['birthday'])): ?>
|
||||||
|
<h4><?= $data['birthday'] ?></h4>
|
||||||
|
<?php endif ?>
|
||||||
|
<br />
|
||||||
|
<hr />
|
||||||
|
<div class="description">
|
||||||
|
<p><?= str_replace("\n", '</p><p>', $data['description']) ?></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<?php if ( ! empty($data['staff'])): ?>
|
||||||
|
<section>
|
||||||
|
<h3>Castings</h3>
|
||||||
|
|
||||||
|
<div class="vertical-tabs">
|
||||||
|
<?php $i = 0 ?>
|
||||||
|
<?php foreach ($data['staff'] as $role => $entries): ?>
|
||||||
|
<div class="tab">
|
||||||
|
<input
|
||||||
|
type="radio" name="staff-roles" id="staff-role<?= $i ?>" <?= $i === 0 ? 'checked' : '' ?> />
|
||||||
|
<label for="staff-role<?= $i ?>"><?= $role ?></label>
|
||||||
|
<?php foreach ($entries as $type => $casting): ?>
|
||||||
|
<?php if (isset($entries['manga'], $entries['anime'])): ?>
|
||||||
|
<h4><?= ucfirst($type) ?></h4>
|
||||||
|
<?php endif ?>
|
||||||
|
<section class="content media-wrap flex flex-wrap flex-justify-start">
|
||||||
|
<?php foreach ($casting as $sid => $series): ?>
|
||||||
|
<?php $mediaType = in_array($type, ['anime', 'manga'], TRUE) ? $type : 'anime'; ?>
|
||||||
|
<?= $_->component->media(
|
||||||
|
$series['titles'],
|
||||||
|
$_->urlFromRoute("{$mediaType}.details", ['id' => $series['slug']]),
|
||||||
|
$_->h->img($series['image'], ['width' => 220, 'loading' => 'lazy'])
|
||||||
|
) ?>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</section>
|
||||||
|
<?php endforeach ?>
|
||||||
|
</div>
|
||||||
|
<?php $i++ ?>
|
||||||
|
<?php endforeach ?>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<?php endif ?>
|
||||||
|
|
||||||
|
<?php if ( ! empty($data['characters'])): ?>
|
||||||
|
<section>
|
||||||
|
<h3>Voice Acting Roles</h3>
|
||||||
|
<?= $component->tabs('voice-acting-roles', $data['characters'], static function ($characterList) use ($component, $helper, $_) {
|
||||||
|
$voiceRoles = [];
|
||||||
|
foreach ($characterList as $cid => $item):
|
||||||
|
$character = $component->character(
|
||||||
|
$item['character']['canonicalName'],
|
||||||
|
$_->urlFromRoute('character', ['slug' => $item['character']['slug']]),
|
||||||
|
$_->h->img($item['character']['image'], ['loading' => 'lazy']),
|
||||||
|
);
|
||||||
|
$medias = [];
|
||||||
|
foreach ($item['media'] as $sid => $series)
|
||||||
|
{
|
||||||
|
$medias[] = $component->media(
|
||||||
|
$series['titles'],
|
||||||
|
$_->urlFromRoute('anime.details', ['id' => $series['slug']]),
|
||||||
|
$_->h->img($series['image'], ['width' => 220, 'loading' => 'lazy'])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$media = implode('', array_map('mb_trim', $medias));
|
||||||
|
|
||||||
|
$voiceRoles[] = <<<HTML
|
||||||
|
<tr>
|
||||||
|
<td>{$character}</td>
|
||||||
|
<td>
|
||||||
|
<section class="align-left media-wrap">{$media}</section>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
HTML;
|
||||||
|
endforeach;
|
||||||
|
|
||||||
|
$roles = implode('', array_map('mb_trim', $voiceRoles));
|
||||||
|
|
||||||
|
return <<<HTML
|
||||||
|
<table class="borderless max-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Character</th>
|
||||||
|
<th>Series</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>{$roles}</tbody>
|
||||||
|
</table>
|
||||||
|
HTML;
|
||||||
|
|
||||||
|
}) ?>
|
||||||
|
</section>
|
||||||
|
<?php endif ?>
|
||||||
|
</main>
|
24
app/views/settings/_anilist.php
Normal file
24
app/views/settings/_anilist.php
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?php if ( ! $hasRequiredAnilistConfig): ?>
|
||||||
|
<p class="static-message info">See the <a href="https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient/wiki/anilist">wiki</a> to learn how to set up Anilist integration. </p>
|
||||||
|
<?php else: ?>
|
||||||
|
<?php $auth = $anilistModel->checkAuth(); ?>
|
||||||
|
<?php if (array_key_exists('errors', $auth)): ?>
|
||||||
|
<p class="static-message error">Anilist API Client is Not Authorized.</p>
|
||||||
|
<?= $_->h->a(
|
||||||
|
$_->urlFromRoute('anilist-redirect'),
|
||||||
|
'Link Anilist Account',
|
||||||
|
['class' => 'bracketed user-btn']
|
||||||
|
) ?>
|
||||||
|
<?php else: ?>
|
||||||
|
<?php $expires = $_->config->get(['anilist', 'access_token_expires']); ?>
|
||||||
|
<p class="static-message info">
|
||||||
|
Linked to Anilist. Your access token will expire around <?= date('F j, Y, g:i a T', $expires) ?>
|
||||||
|
</p>
|
||||||
|
<?php require __DIR__ . '/_form.php' ?>
|
||||||
|
<?= $_->h->a(
|
||||||
|
$_->urlFromRoute('anilist-redirect'),
|
||||||
|
'Update Access Token',
|
||||||
|
['class' => 'bracketed user-btn']
|
||||||
|
) ?>
|
||||||
|
<?php endif ?>
|
||||||
|
<?php endif ?>
|
5
app/views/settings/_field.php
Normal file
5
app/views/settings/_field.php
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<article>
|
||||||
|
<label for="<?= $fieldName ?>"><?= $field['title'] ?></label><br />
|
||||||
|
<small><?= $field['description'] ?></small><br />
|
||||||
|
<?= $_->h->field($fieldName, $field); ?>
|
||||||
|
</article>
|
24
app/views/settings/_form.php
Normal file
24
app/views/settings/_form.php
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
// Higher scoped variables:
|
||||||
|
// $fields
|
||||||
|
// $hiddenFields
|
||||||
|
// $nestedPrefix
|
||||||
|
?>
|
||||||
|
|
||||||
|
<?php foreach ($fields as $name => $field): ?>
|
||||||
|
<?php
|
||||||
|
$fieldName = ($section === 'config' || $nestedPrefix !== 'config')
|
||||||
|
? "{$nestedPrefix}[{$name}]"
|
||||||
|
: "{$nestedPrefix}[{$section}][{$name}]";
|
||||||
|
?>
|
||||||
|
<?php if ($field['type'] === 'subfield'): ?>
|
||||||
|
<section>
|
||||||
|
<h4><?= $field['title'] ?></h4>
|
||||||
|
<?php include '_subfield.php'; ?>
|
||||||
|
</section>
|
||||||
|
<?php elseif ( ! empty($field['display'])): ?>
|
||||||
|
<?php include '_field.php' ?>
|
||||||
|
<?php else: ?>
|
||||||
|
<?php $hiddenFields[] = $_->h->field($fieldName, $field); ?>
|
||||||
|
<?php endif ?>
|
||||||
|
<?php endforeach ?>
|
20
app/views/settings/_subfield.php
Normal file
20
app/views/settings/_subfield.php
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
// Higher scoped variables:
|
||||||
|
// $field
|
||||||
|
// $fields
|
||||||
|
// $hiddenFields
|
||||||
|
// $nestedPrefix
|
||||||
|
?>
|
||||||
|
|
||||||
|
<?php foreach ($field['fields'] as $name => $field): ?>
|
||||||
|
<?php
|
||||||
|
$fieldName = ($section === 'config' || $nestedPrefix !== 'config')
|
||||||
|
? "{$nestedPrefix}[{$name}]"
|
||||||
|
: "{$nestedPrefix}[{$section}][{$name}]";
|
||||||
|
?>
|
||||||
|
<?php if ( ! empty($field['display'])): ?>
|
||||||
|
<?php include '_field.php' ?>
|
||||||
|
<?php else: ?>
|
||||||
|
<?php $hiddenFields[] = $_->h->field($fieldName, $field); ?>
|
||||||
|
<?php endif ?>
|
||||||
|
<?php endforeach ?>
|
46
app/views/settings/settings.php
Normal file
46
app/views/settings/settings.php
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
if ( ! $_->isAuthenticated())
|
||||||
|
{
|
||||||
|
echo '<h1>Not Authorized</h1>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sectionMapping = [
|
||||||
|
'anilist' => 'Anilist API Integration',
|
||||||
|
'config' => 'General Settings',
|
||||||
|
'cache' => 'Caching',
|
||||||
|
'database' => 'Collection Database Settings',
|
||||||
|
];
|
||||||
|
|
||||||
|
$hiddenFields = [];
|
||||||
|
$nestedPrefix = 'config';
|
||||||
|
?>
|
||||||
|
|
||||||
|
<form action="<?= $_->urlFromRoute('settings-post') ?>" method="POST">
|
||||||
|
<main class='settings form'>
|
||||||
|
<button type="submit">Save Changes</button>
|
||||||
|
<div class="tabs">
|
||||||
|
<?php $i = 0; ?>
|
||||||
|
|
||||||
|
<?php foreach ($form as $section => $fields): ?>
|
||||||
|
<input <?= $i === 0 ? 'checked="checked"' : '' ?> type="radio" id="settings-tab<?= $i ?>"
|
||||||
|
name="settings-tabs"
|
||||||
|
/>
|
||||||
|
<label for="settings-tab<?= $i ?>"><h3><?= $sectionMapping[$section] ?></h3></label>
|
||||||
|
<section class="content">
|
||||||
|
<?php
|
||||||
|
($section === 'anilist')
|
||||||
|
? require __DIR__ . '/_anilist.php'
|
||||||
|
: require __DIR__ . '/_form.php'
|
||||||
|
?>
|
||||||
|
</section>
|
||||||
|
<?php $i++; ?>
|
||||||
|
<?php endforeach ?>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<?php foreach ($hiddenFields as $field): ?>
|
||||||
|
<?= $field->__toString() ?>
|
||||||
|
<?php endforeach ?>
|
||||||
|
<button type="submit">Save Changes</button>
|
||||||
|
</main>
|
||||||
|
</form>
|
30
app/views/setup-check.php
Normal file
30
app/views/setup-check.php
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
use function Aviat\AnimeClient\checkFolderPermissions;
|
||||||
|
|
||||||
|
$setupErrors = checkFolderPermissions($_->config);
|
||||||
|
?>
|
||||||
|
|
||||||
|
<?php if ( ! empty($setupErrors)): ?>
|
||||||
|
<aside class="message error">
|
||||||
|
<h1>Issues with server setup:</h1>
|
||||||
|
|
||||||
|
<?php if (array_key_exists('missing', $setupErrors)): ?>
|
||||||
|
<h3>The following folders need to be created, and writable.</h3>
|
||||||
|
<ul>
|
||||||
|
<?php foreach ($setupErrors['missing'] as $error): ?>
|
||||||
|
<li><?= $error ?></li>
|
||||||
|
<?php endforeach ?>
|
||||||
|
</ul>
|
||||||
|
<?php endif ?>
|
||||||
|
|
||||||
|
<?php if (array_key_exists('writable', $setupErrors)): ?>
|
||||||
|
<h3>The following folders are not writable by the server.</h3>
|
||||||
|
<ul>
|
||||||
|
<?php foreach($setupErrors['writable'] as $error): ?>
|
||||||
|
<li><?= $error ?></li>
|
||||||
|
<?php endforeach ?>
|
||||||
|
</ul>
|
||||||
|
<?php endif ?>
|
||||||
|
</aside>
|
||||||
|
<?php endif ?>
|
110
app/views/user/details.php
Normal file
110
app/views/user/details.php
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
<?php
|
||||||
|
use Aviat\AnimeClient\Kitsu;
|
||||||
|
?>
|
||||||
|
<main class="user-page details">
|
||||||
|
<h2 class="toph">
|
||||||
|
About
|
||||||
|
<?= $_->h->a(
|
||||||
|
"https://kitsu.io/users/{$data['slug']}",
|
||||||
|
$data['name'], [
|
||||||
|
'title' => 'View profile on Kitsu'
|
||||||
|
])
|
||||||
|
?>
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<section class="flex flex-no-wrap">
|
||||||
|
<aside class="info">
|
||||||
|
<table class="media-details invisible">
|
||||||
|
<tr>
|
||||||
|
<?php if($data['avatar'] !== null): ?>
|
||||||
|
<td><?= $_->h->img($data['avatar'], ['alt' => '', 'width' => '225']); ?></td>
|
||||||
|
<?php endif ?>
|
||||||
|
<td><?= $_->escape->html($data['about']) ?></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<br />
|
||||||
|
<table class="media-details">
|
||||||
|
<?php foreach ([
|
||||||
|
'joinDate' => 'Joined',
|
||||||
|
'birthday' => 'Birthday',
|
||||||
|
'gender' => 'Gender',
|
||||||
|
'location' => 'Location'
|
||||||
|
] as $key => $label): ?>
|
||||||
|
<?php if ($data[$key] !== null): ?>
|
||||||
|
<tr>
|
||||||
|
<td><?= $label ?></td>
|
||||||
|
<td><?= $data[$key] ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endif ?>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
|
||||||
|
<?php if ($data['website'] !== null): ?>
|
||||||
|
<tr>
|
||||||
|
<td>Website</td>
|
||||||
|
<td><?= $_->h->a($data['website'], $data['website']) ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endif ?>
|
||||||
|
|
||||||
|
<?php if ($data['waifu']['character'] !== null): ?>
|
||||||
|
<tr>
|
||||||
|
<td><?= $_->escape->html($data['waifu']['label']) ?></td>
|
||||||
|
<td>
|
||||||
|
<?php
|
||||||
|
$character = $data['waifu']['character'];
|
||||||
|
echo $_->component->character(
|
||||||
|
$character['names']['canonical'],
|
||||||
|
$_->urlFromRoute('character', ['slug' => $character['slug']]),
|
||||||
|
$_->h->img(Kitsu::getImage($character))
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endif ?>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h3>User Stats</h3><br />
|
||||||
|
<table class="media-details">
|
||||||
|
<?php foreach($data['stats'] as $label => $stat): ?>
|
||||||
|
<tr>
|
||||||
|
<td><?= $label ?></td>
|
||||||
|
<td><?= $stat ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach ?>
|
||||||
|
</table>
|
||||||
|
</aside>
|
||||||
|
<article>
|
||||||
|
<?php if ( ! empty($data['favorites'])): ?>
|
||||||
|
<h3>Favorites</h3>
|
||||||
|
<?= $_->component->tabs('user-favorites', $data['favorites'], static function ($items, $type) use ($_) {
|
||||||
|
if ($type === 'character')
|
||||||
|
{
|
||||||
|
uasort($items, fn ($a, $b) => $a['names']['canonical'] <=> $b['names']['canonical']);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
uasort($items, fn ($a, $b) => $a['titles']['canonical'] <=> $b['titles']['canonical']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$rendered = array_map(fn ($item) => match ($type) {
|
||||||
|
'character' => $_->component->character(
|
||||||
|
$item['names']['canonical'],
|
||||||
|
$_->urlFromRoute('character', ['slug' => $item['slug']]),
|
||||||
|
$_->h->img(Kitsu::getImage($item))
|
||||||
|
),
|
||||||
|
default => $_->component->media(
|
||||||
|
array_merge(
|
||||||
|
[$item['titles']['canonical']],
|
||||||
|
Kitsu::getFilteredTitles($item['titles']),
|
||||||
|
),
|
||||||
|
$_->urlFromRoute("{$type}.details", ['id' => $item['slug']]),
|
||||||
|
$_->h->img(Kitsu::getPosterImage($item), ['width' => 220]),
|
||||||
|
),
|
||||||
|
}, $items);
|
||||||
|
|
||||||
|
return implode('', array_map('mb_trim', $rendered));
|
||||||
|
|
||||||
|
}, 'content full-width media-wrap') ?>
|
||||||
|
<?php endif ?>
|
||||||
|
</article>
|
||||||
|
</section>
|
||||||
|
</main>
|
158
build.xml
158
build.xml
@ -1,158 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project default="full-build" name="animeclient" basedir=".">
|
|
||||||
<!-- By default, we assume all tools to be on the $PATH -->
|
|
||||||
<property name="pdepend" value="pdepend" />
|
|
||||||
<property name="phpcpd" value="phpcpd" />
|
|
||||||
<property name="phpdox" value="phpdox" />
|
|
||||||
<property name="phploc" value="phploc" />
|
|
||||||
<property name="phpmd" value="phpmd" />
|
|
||||||
<property name="phpunit" value="phpunit" />
|
|
||||||
<property name="sonar" value="sonar-runner" />
|
|
||||||
|
|
||||||
<target name="full-build"
|
|
||||||
depends="prepare,static-analysis,phpunit,phpdox,sonar"
|
|
||||||
description="Performs static analysis, runs the tests, and generates project documentation"
|
|
||||||
/>
|
|
||||||
<target name="full-build-parallel"
|
|
||||||
depends="prepare,static-analysis-parallel,phpunit,phpdox"
|
|
||||||
description="Performs static analysis (executing the tools in parallel), runs the tests, and generates project documentation"
|
|
||||||
/>
|
|
||||||
<target name="quick-build"
|
|
||||||
depends="prepare,lint,phpunit-no-coverage"
|
|
||||||
description="Performs a lint check and runs the tests (without generating code coverage reports)"
|
|
||||||
/>
|
|
||||||
<target name="static-analysis"
|
|
||||||
depends="lint,phploc-ci,pdepend,phpcpd-ci"
|
|
||||||
description="Performs static analysis"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Adjust the threadCount attribute's value to the number of CPUs -->
|
|
||||||
<target name="static-analysis-parallel" description="Performs static analysis (executing the tools in parallel)">
|
|
||||||
<parallel threadCount="6">
|
|
||||||
<sequential>
|
|
||||||
<antcall target="pdepend" />
|
|
||||||
</sequential>
|
|
||||||
<antcall target="lint" />
|
|
||||||
<antcall target="phpcpd-ci" />
|
|
||||||
<antcall target="phploc-ci" />
|
|
||||||
</parallel>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="clean" unless="clean.done" description="Cleanup build artifacts">
|
|
||||||
<delete dir="build/api" />
|
|
||||||
<delete dir="build/coverage" />
|
|
||||||
<delete dir="build/logs" />
|
|
||||||
<delete dir="build/pdepend" />
|
|
||||||
<delete dir="build/phpdox" />
|
|
||||||
<property name="clean.done" value="true" />
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="prepare" depends="clean" unless="prepare.done" description="Prepare for build">
|
|
||||||
<mkdir dir="build/api" />
|
|
||||||
<mkdir dir="build/coverage" />
|
|
||||||
<mkdir dir="build/logs" />
|
|
||||||
<mkdir dir="build/pdepend" />
|
|
||||||
<mkdir dir="build/phpdox" />
|
|
||||||
<property name="prepare.done" value="true" />
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="lint" unless="lint.done" description="Perform syntax check of sourcecode files">
|
|
||||||
<apply executable="php" taskname="lint">
|
|
||||||
<arg value="-l" />
|
|
||||||
|
|
||||||
<fileset dir="src">
|
|
||||||
<include name="**/*.php" />
|
|
||||||
</fileset>
|
|
||||||
|
|
||||||
<fileset dir="tests">
|
|
||||||
<include name="**/*.php" />
|
|
||||||
</fileset>
|
|
||||||
</apply>
|
|
||||||
|
|
||||||
<property name="lint.done" value="true" />
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="phploc" unless="phploc.done" description="Measure project size using PHPLOC and print human readable output. Intended for usage on the command line.">
|
|
||||||
<exec executable="${phploc}" taskname="phploc">
|
|
||||||
<arg value="--count-tests" />
|
|
||||||
<arg path="src" />
|
|
||||||
<arg path="tests" />
|
|
||||||
</exec>
|
|
||||||
|
|
||||||
<property name="phploc.done" value="true" />
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="phploc-ci" depends="prepare" unless="phploc.done" description="Measure project size using PHPLOC and log result in CSV and XML format. Intended for usage within a continuous integration environment.">
|
|
||||||
<exec executable="${phploc}" taskname="phploc">
|
|
||||||
<arg value="--count-tests" />
|
|
||||||
<arg value="--log-csv" />
|
|
||||||
<arg path="build/logs/phploc.csv" />
|
|
||||||
<arg value="--log-xml" />
|
|
||||||
<arg path="build/logs/phploc.xml" />
|
|
||||||
<arg path="src" />
|
|
||||||
<arg path="tests" />
|
|
||||||
</exec>
|
|
||||||
|
|
||||||
<property name="phploc.done" value="true" />
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="pdepend" depends="prepare" unless="pdepend.done" description="Calculate software metrics using PHP_Depend and log result in XML format. Intended for usage within a continuous integration environment.">
|
|
||||||
<exec executable="${pdepend}" taskname="pdepend">
|
|
||||||
<arg value="--jdepend-xml=build/logs/jdepend.xml" />
|
|
||||||
<arg value="--jdepend-chart=build/pdepend/dependencies.svg" />
|
|
||||||
<arg value="--overview-pyramid=build/pdepend/overview-pyramid.svg" />
|
|
||||||
<arg path="src" />
|
|
||||||
</exec>
|
|
||||||
|
|
||||||
<property name="pdepend.done" value="true" />
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="phpcpd" unless="phpcpd.done" description="Find duplicate code using PHPCPD and print human readable output. Intended for usage on the command line before committing.">
|
|
||||||
<exec executable="${phpcpd}" taskname="phpcpd">
|
|
||||||
<arg path="src" />
|
|
||||||
</exec>
|
|
||||||
|
|
||||||
<property name="phpcpd.done" value="true" />
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="phpcpd-ci" depends="prepare" unless="phpcpd.done" description="Find duplicate code using PHPCPD and log result in XML format. Intended for usage within a continuous integration environment.">
|
|
||||||
<exec executable="${phpcpd}" taskname="phpcpd">
|
|
||||||
<arg value="--log-pmd" />
|
|
||||||
<arg path="build/logs/pmd-cpd.xml" />
|
|
||||||
<arg path="src" />
|
|
||||||
</exec>
|
|
||||||
|
|
||||||
<property name="phpcpd.done" value="true" />
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="phpunit" unless="phpunit.done" depends="prepare" description="Run unit tests with PHPUnit">
|
|
||||||
<exec executable="${phpunit}" taskname="phpunit">
|
|
||||||
<arg value="--configuration" />
|
|
||||||
<arg path="build/phpunit.xml" />
|
|
||||||
</exec>
|
|
||||||
|
|
||||||
<property name="phpunit.done" value="true" />
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="phpunit-no-coverage" depends="prepare" unless="phpunit.done" description="Run unit tests with PHPUnit (without generating code coverage reports)">
|
|
||||||
<exec executable="${phpunit}" failonerror="true" taskname="phpunit">
|
|
||||||
<arg value="--configuration" />
|
|
||||||
<arg path="build/phpunit.xml" />
|
|
||||||
<arg value="--no-coverage" />
|
|
||||||
</exec>
|
|
||||||
|
|
||||||
<property name="phpunit.done" value="true" />
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="phpdox" depends="phploc-ci,phpunit" unless="phpdox.done" description="Generate project documentation using phpDox">
|
|
||||||
<exec dir="build" executable="${phpdox}" taskname="phpdox" />
|
|
||||||
|
|
||||||
<property name="phpdox.done" value="true" />
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="sonar" depends="phpunit" unless="sonar.done" description="Generate code analysis with sonarqube">
|
|
||||||
<exec executable="${sonar}" taskname="sonar" />
|
|
||||||
|
|
||||||
<property name="sonar.done" value="true" />
|
|
||||||
</target>
|
|
||||||
</project>
|
|
@ -1,11 +0,0 @@
|
|||||||
/**
|
|
||||||
* Hummingbird Anime Client
|
|
||||||
*
|
|
||||||
* An API client for Hummingbird to manage anime and manga watch lists
|
|
||||||
*
|
|
||||||
* @package HummingbirdAnimeClient
|
|
||||||
* @author Timothy J. Warren
|
|
||||||
* @copyright Copyright (c) 2015 - 2016
|
|
||||||
* @link https://github.com/timw4mail/HummingBirdAnimeClient
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
@ -1,10 +0,0 @@
|
|||||||
/**
|
|
||||||
* Ion
|
|
||||||
*
|
|
||||||
* Building blocks for web development
|
|
||||||
*
|
|
||||||
* @package Ion
|
|
||||||
* @author Timothy J. Warren
|
|
||||||
* @copyright Copyright (c) 2015 - 2016
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
84
build/phpcs.xml
Normal file
84
build/phpcs.xml
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<ruleset name="Tim's Coding Standard">
|
||||||
|
<description>A variation of the CodeIgniter standard</description>
|
||||||
|
|
||||||
|
<file>../src/</file>
|
||||||
|
|
||||||
|
<encoding>utf-8</encoding>
|
||||||
|
|
||||||
|
<rule ref="Generic.Files.LineEndings">
|
||||||
|
<properties>
|
||||||
|
<property name="eolChar" value="\n"/>
|
||||||
|
</properties>
|
||||||
|
</rule>
|
||||||
|
|
||||||
|
<!-- PHP files should OMIT the closing PHP tag -->
|
||||||
|
<rule ref="Zend.Files.ClosingTag"/>
|
||||||
|
<!-- Always use full PHP opening tags -->
|
||||||
|
<rule ref="Generic.PHP.DisallowShortOpenTag"/>
|
||||||
|
|
||||||
|
<!-- Constants should always be fully uppercase -->
|
||||||
|
<rule ref="Generic.NamingConventions.UpperCaseConstantName"/>
|
||||||
|
<!-- TRUE, FALSE, and NULL keywords should always be fully uppercase -->
|
||||||
|
<rule ref="Generic.PHP.UpperCaseConstant"/>
|
||||||
|
|
||||||
|
<!-- One statement per line -->
|
||||||
|
<rule ref="Generic.Formatting.DisallowMultipleStatements"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Classes and functions should be commented -->
|
||||||
|
<rule ref="PEAR.Commenting.ClassComment">
|
||||||
|
<exclude name="PEAR.Commenting.ClassComment.MissingCategoryTag" />
|
||||||
|
<exclude name="PEAR.Commenting.ClassComment.MissingPackageTag" />
|
||||||
|
<exclude name="PEAR.Commenting.ClassComment.MissingAuthorTag" />
|
||||||
|
<exclude name="PEAR.Commenting.ClassComment.MissingLicenseTag" />
|
||||||
|
<exclude name="PEAR.Commenting.ClassComment.MissingLinkTag" />
|
||||||
|
</rule>
|
||||||
|
<rule ref="PEAR.Commenting.FunctionComment">
|
||||||
|
<!-- Exclude this sniff because it doesn't understand multiple types -->
|
||||||
|
<exclude name="PEAR.Commenting.FunctionComment.MissingParamComment" />
|
||||||
|
<exclude name="PEAR.Commenting.FunctionComment.SpacingAfterParamType" />
|
||||||
|
<exclude name="PEAR.Commenting.FunctionComment.SpacingAfterParamName" />
|
||||||
|
</rule>
|
||||||
|
|
||||||
|
<!-- Use warnings for docblock comments for files and variables, since nothing is clearly explained -->
|
||||||
|
<rule ref="PEAR.Commenting.FileComment">
|
||||||
|
<exclude name="PEAR.Commenting.FileComment.InvalidVersion" />
|
||||||
|
<exclude name="PEAR.Commenting.FileComment.MissingCategoryTag" />
|
||||||
|
<properties>
|
||||||
|
<property name="error" value="false"/>
|
||||||
|
</properties>
|
||||||
|
</rule>
|
||||||
|
|
||||||
|
<rule ref="Squiz.Commenting.FunctionCommentThrowTag"/>
|
||||||
|
<rule ref="Squiz.Commenting.VariableComment">
|
||||||
|
<properties>
|
||||||
|
<property name="error" value="false"/>
|
||||||
|
</properties>
|
||||||
|
</rule>
|
||||||
|
|
||||||
|
<!-- Use Allman style indenting. With the exception of Class declarations,
|
||||||
|
braces are always placed on a line by themselves, and indented at the same level as the control statement that "owns" them. -->
|
||||||
|
<rule ref="Generic.Functions.OpeningFunctionBraceBsdAllman"/>
|
||||||
|
<rule ref="PEAR.WhiteSpace.ScopeClosingBrace">
|
||||||
|
<exclude name="PEAR.WhiteSpace.ScopeClosingBrace.BreakIndent" />
|
||||||
|
</rule>
|
||||||
|
<rule ref="Generic.Functions.FunctionCallArgumentSpacing"/>
|
||||||
|
|
||||||
|
<!-- Use only short array syntax -->
|
||||||
|
<rule ref="Generic.Arrays.DisallowLongArraySyntax" />
|
||||||
|
|
||||||
|
<rule ref="Generic.PHP.ForbiddenFunctions">
|
||||||
|
<properties>
|
||||||
|
<property name="forbiddenFunctions" type="array" value="create_function=>null,eval=>null" />
|
||||||
|
</properties>
|
||||||
|
</rule>
|
||||||
|
|
||||||
|
<!-- Inherit CodeIgniter Rules -->
|
||||||
|
<rule ref="./CodeIgniter">
|
||||||
|
<properties>
|
||||||
|
<property name="error" value="false" />
|
||||||
|
</properties>
|
||||||
|
</rule>
|
||||||
|
</ruleset>
|
131
build/phpdox.xml
131
build/phpdox.xml
@ -1,131 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" ?>
|
|
||||||
<!-- This is a skeleton phpDox config file - Check http://phpDox.de for latest version and more info -->
|
|
||||||
<phpdox xmlns="http://xml.phpdox.net/config" silent="false">
|
|
||||||
<!-- @silent: true | false to enable or disable visual output of progress -->
|
|
||||||
|
|
||||||
<!-- Additional bootstrap files to load for additional parsers, enrichers and/or engines -->
|
|
||||||
<!-- Place as many require nodes as you feel like in this container -->
|
|
||||||
<!-- syntax: <require file="/path/to/file.php" /> -->
|
|
||||||
<bootstrap />
|
|
||||||
|
|
||||||
<!-- A phpDox project to process, you can have multiple projects in one config file -->
|
|
||||||
<project name="Hummingbird Anime Client" source="../src" workdir="phpdox/xml">
|
|
||||||
<!-- @name - The name of the project -->
|
|
||||||
<!-- @source - The source directory of the application to process -->
|
|
||||||
<!-- @workdir - The directory to store the xml data files in -->
|
|
||||||
|
|
||||||
<!-- A phpDox config file can define additional variables (properties) per project -->
|
|
||||||
<!-- <property name="some.name" value="the.value" /> -->
|
|
||||||
|
|
||||||
<!-- Values can make use of previously defined properties -->
|
|
||||||
<!-- The following are defined by default:
|
|
||||||
|
|
||||||
${basedir} Directory the loaded config file is in
|
|
||||||
|
|
||||||
${phpDox.home} Directory of the phpDox installation
|
|
||||||
${phpDox.file} The current config file
|
|
||||||
${phpDox.version} phpDox' version number
|
|
||||||
|
|
||||||
${phpDox.project.name} The value of project/@name if set, otherwise 'unnamed'
|
|
||||||
${phpDox.project.source} The value of project/@source if set, otherwise '${basedir}/src'
|
|
||||||
${phpDox.project.workdir} The value of project/@workdir if set, otherwise '${basedir}/build/phpdox/xml'
|
|
||||||
|
|
||||||
${phpDox.php.version} The PHP Version of the interpreter in use
|
|
||||||
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!-- Additional configuration for the collecting process (parsing of php code, generation of xml data) -->
|
|
||||||
<collector publiconly="false" backend="parser" encoding="auto">
|
|
||||||
<!-- @publiconly - Flag to disable/enable processing of non public methods and members -->
|
|
||||||
<!-- @backend - The collector backend to use, currently only shipping with 'parser' -->
|
|
||||||
<!-- @encoding - Charset encoding of source files (overwrite default 'auto' if detection fails) -->
|
|
||||||
|
|
||||||
<!-- <include / exclude filter for filelist generator, mask must follow fnmatch() requirements -->
|
|
||||||
<include mask="*.php" />
|
|
||||||
<exclude mask="" />
|
|
||||||
|
|
||||||
<!-- How to handle inheritance -->
|
|
||||||
<inheritance resolve="true">
|
|
||||||
<!-- @resolve - Flag to enable/disable resolving of inheritance -->
|
|
||||||
|
|
||||||
<!-- You can define multiple (external) dependencies to be included -->
|
|
||||||
<!-- <dependency path="" -->
|
|
||||||
<!-- @path - path to a directory containing an index.xml for a dependency project -->
|
|
||||||
</inheritance>
|
|
||||||
|
|
||||||
</collector>
|
|
||||||
|
|
||||||
<!-- Configuration of generation process -->
|
|
||||||
<generator output="../docs">
|
|
||||||
<!-- @output - (Base-)Directory to store output data in -->
|
|
||||||
|
|
||||||
<!-- A generation process consists of one or more build tasks and of (optional) enrich sources -->
|
|
||||||
|
|
||||||
<enrich base="logs">
|
|
||||||
<!-- @base - (Base-)Directory of datafiles used for enrich process -->
|
|
||||||
|
|
||||||
<!--<source type="...">-->
|
|
||||||
<!-- @type - the handler for the enrichment -->
|
|
||||||
<!-- known types by default are: build, checkstyle, git, phpcs, phploc, phpunit, pmd -->
|
|
||||||
|
|
||||||
<!-- every enrichment source can have additional configuration nodes, most probably need a logfile -->
|
|
||||||
<!-- <file name="path/to/log.xml" /> -->
|
|
||||||
<!--</source> -->
|
|
||||||
|
|
||||||
<!-- add phploc output -->
|
|
||||||
<source type="phploc">
|
|
||||||
<file name="phploc.xml" />
|
|
||||||
</source>
|
|
||||||
|
|
||||||
<!-- git vcs information -->
|
|
||||||
<source type="git">
|
|
||||||
<git binary="/usr/bin/git" />
|
|
||||||
<history enabled="true" limit="15" cache="${phpDox.project.workdir}/gitlog.xml" />
|
|
||||||
</source>
|
|
||||||
|
|
||||||
<!-- PHP Code Sniffer findings -->
|
|
||||||
<!--
|
|
||||||
<source type="phpcs">
|
|
||||||
<file name="logs/phpcs.xml" />
|
|
||||||
</source>
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!-- PHPMessDetector -->
|
|
||||||
<!--
|
|
||||||
<source type="pmd">
|
|
||||||
<file name="pmd.xml" />
|
|
||||||
</source>
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!-- PHPUnit Coverage XML -->
|
|
||||||
<source type="phpunit">
|
|
||||||
<!-- <coverage path="clover.xml" />-->
|
|
||||||
<!-- @path - the directory where the xml code coverage report can be found -->
|
|
||||||
<!--<filter directory="${phpDox.project.source}" />-->
|
|
||||||
<!-- @directory - path of the phpunit config whitelist filter directory -->
|
|
||||||
</source>
|
|
||||||
<!--
|
|
||||||
<source type="phpunit">
|
|
||||||
<filter directory="${phpDox.project.source}" />
|
|
||||||
</source>
|
|
||||||
-->
|
|
||||||
|
|
||||||
</enrich>
|
|
||||||
|
|
||||||
<!-- <build engine="..." enabled="true" output="..." /> -->
|
|
||||||
<!-- @engine - The name of the engine this build task uses, use ./phpDox - -engines to get a list of available engines -->
|
|
||||||
<!-- @enabled - Flag to enable/disable this engine, default: enabled=true -->
|
|
||||||
<!-- @output - (optional) Output directory; if relative (no / as first char) it is interpreted as relative to generator/@output -->
|
|
||||||
|
|
||||||
<!-- An engine and thus build node can have additional configuration child nodes, please check the documentation for the engine to find out more -->
|
|
||||||
|
|
||||||
<!-- default engine "html" -->
|
|
||||||
<build engine="html" enabled="true">
|
|
||||||
<template dir="${phpDox.home}/templates/html" />
|
|
||||||
<file extension="html" />
|
|
||||||
</build>
|
|
||||||
|
|
||||||
</generator>
|
|
||||||
</project>
|
|
||||||
|
|
||||||
</phpdox>
|
|
@ -1,37 +1,32 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<phpunit
|
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" colors="true" stopOnFailure="false" bootstrap="../tests/bootstrap.php" beStrictAboutTestsThatDoNotTestAnything="true" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.1/phpunit.xsd">
|
||||||
colors="true"
|
<coverage>
|
||||||
stopOnFailure="false"
|
<report>
|
||||||
bootstrap="../tests/bootstrap.php"
|
<clover outputFile="logs/clover.xml"/>
|
||||||
beStrictAboutTestsThatDoNotTestAnything="true"
|
<html outputDirectory="../coverage"/>
|
||||||
checkForUnintentionallyCoveredCode="true"
|
</report>
|
||||||
>
|
</coverage>
|
||||||
<filter>
|
<testsuites>
|
||||||
<whitelist>
|
<testsuite name="AnimeClient">
|
||||||
<directory suffix=".php">../src/Aviat/Ion</directory>
|
<directory>../tests/AnimeClient</directory>
|
||||||
<directory suffix=".php">../src/Aviat/AnimeClient</directory>
|
</testsuite>
|
||||||
</whitelist>
|
<testsuite name="Ion">
|
||||||
</filter>
|
<directory>../tests/Ion</directory>
|
||||||
<testsuites>
|
</testsuite>
|
||||||
<testsuite name="Ion">
|
</testsuites>
|
||||||
<directory>../tests/Ion</directory>
|
<logging>
|
||||||
</testsuite>
|
<junit outputFile="logs/junit.xml"/>
|
||||||
<testsuite name="AnimeClient">
|
</logging>
|
||||||
<directory>../tests/AnimeClient</directory>
|
<php>
|
||||||
</testsuite>
|
<server name="HTTP_USER_AGENT" value="Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:38.0) Gecko/20100101 Firefox/38.0"/>
|
||||||
</testsuites>
|
<server name="HTTP_HOST" value="localhost"/>
|
||||||
<logging>
|
<server name="SERVER_NAME" value="localhost"/>
|
||||||
<log type="coverage-html" target="coverage"/>
|
<server name="REQUEST_URI" value="/"/>
|
||||||
<log type="coverage-clover" target="logs/clover.xml"/>
|
<server name="REQUEST_METHOD" value="GET"/>
|
||||||
<log type="coverage-crap4j" target="logs/crap4j.xml"/>
|
</php>
|
||||||
<log type="coverage-xml" target="logs/coverage" />
|
<source>
|
||||||
<log type="junit" target="logs/junit.xml" logIncompleteSkipped="false"/>
|
<include>
|
||||||
</logging>
|
<directory suffix=".php">../src</directory>
|
||||||
<php>
|
</include>
|
||||||
<server name="HTTP_USER_AGENT" value="Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:38.0) Gecko/20100101 Firefox/38.0" />
|
</source>
|
||||||
<server name="HTTP_HOST" value="localhost" />
|
|
||||||
<server name="SERVER_NAME" value="localhost" />
|
|
||||||
<server name="REQUEST_URI" value="/" />
|
|
||||||
<server name="REQUEST_METHOD" value="GET" />
|
|
||||||
</php>
|
|
||||||
</phpunit>
|
</phpunit>
|
@ -1,89 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
$animeclient_file_patterns = [
|
|
||||||
'app/config/*.php',
|
|
||||||
'app/booststrap.php',
|
|
||||||
'src/functions.php',
|
|
||||||
'src/Aviat/AnimeClient/*.php'
|
|
||||||
];
|
|
||||||
|
|
||||||
$ion_file_patterns = [
|
|
||||||
'src/Aviat/Ion/*.php'
|
|
||||||
];
|
|
||||||
|
|
||||||
if ( ! function_exists('glob_recursive'))
|
|
||||||
{
|
|
||||||
// Does not support flag GLOB_BRACE
|
|
||||||
|
|
||||||
function glob_recursive($pattern, $flags = 0)
|
|
||||||
{
|
|
||||||
$files = glob($pattern, $flags);
|
|
||||||
|
|
||||||
foreach (glob(dirname($pattern) . '/*', GLOB_ONLYDIR | GLOB_NOSORT) as $dir)
|
|
||||||
{
|
|
||||||
$files = array_merge($files, glob_recursive($dir . '/' . basename($pattern), $flags));
|
|
||||||
}
|
|
||||||
|
|
||||||
return $files;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function get_text_to_replace($tokens)
|
|
||||||
{
|
|
||||||
if ($tokens[0][0] !== T_OPEN_TAG)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there is already a docblock, as the second token after the
|
|
||||||
// open tag, get the contents of that token to replace
|
|
||||||
if ($tokens[1][0] === T_DOC_COMMENT)
|
|
||||||
{
|
|
||||||
return "<?php\n" . $tokens[1][1];
|
|
||||||
}
|
|
||||||
else if ($tokens[1][0] !== T_DOC_COMMENT)
|
|
||||||
{
|
|
||||||
return "<?php";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function get_tokens($source)
|
|
||||||
{
|
|
||||||
return token_get_all($source);
|
|
||||||
}
|
|
||||||
|
|
||||||
function replace_files(array $files, $template)
|
|
||||||
{
|
|
||||||
foreach ($files as $file)
|
|
||||||
{
|
|
||||||
$source = file_get_contents($file);
|
|
||||||
$tokens = get_tokens($source);
|
|
||||||
$text_to_replace = get_text_to_replace($tokens);
|
|
||||||
|
|
||||||
$header = file_get_contents(__DIR__ . $template);
|
|
||||||
$new_text = "<?php\n{$header}";
|
|
||||||
|
|
||||||
$new_source = str_replace($text_to_replace, $new_text, $source);
|
|
||||||
file_put_contents($file, $new_source);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($animeclient_file_patterns as $glob)
|
|
||||||
{
|
|
||||||
$files = glob_recursive($glob);
|
|
||||||
replace_files($files, '/animeclient_header_comment.txt');
|
|
||||||
}
|
|
||||||
$loose_files = [
|
|
||||||
__DIR__ . '/../index.php',
|
|
||||||
__DIR__ . '/../public/css.php',
|
|
||||||
__DIR__ . '/../public/js.php'
|
|
||||||
];
|
|
||||||
replace_files($loose_files, '/animeclient_header_comment.txt');
|
|
||||||
|
|
||||||
foreach ($ion_file_patterns as $glob)
|
|
||||||
{
|
|
||||||
$files = glob_recursive($glob);
|
|
||||||
replace_files($files, '/ion_header_comment.txt');
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "Successfully updated headers \n";
|
|
@ -1,21 +1,78 @@
|
|||||||
{
|
{
|
||||||
"name": "timw4mail/hummingbird-anime-client",
|
"name": "aviat/hummingbird-anime-client",
|
||||||
"description": "A self-hosted anime/manga client for hummingbird.",
|
"description": "A self-hosted anime/manga client for Kitsu.",
|
||||||
"license":"MIT",
|
"license": "MIT",
|
||||||
"require": {
|
"authors": [
|
||||||
"abeautifulsite/simpleimage": "2.5.*",
|
{
|
||||||
"aura/html": "2.*",
|
"name": "Timothy J. Warren",
|
||||||
"aura/router": "2.2.*",
|
"email": "tim@timshomepage.net",
|
||||||
"aura/session": "2.*",
|
"homepage": "https://timshomepage.net",
|
||||||
"aura/web": "2.*",
|
"role": "Developer"
|
||||||
"aviat4ion/query": "2.5.*",
|
|
||||||
"container-interop/container-interop": "1.*",
|
|
||||||
"danielstjules/stringy": "~2.1",
|
|
||||||
"filp/whoops": "2.0.*",
|
|
||||||
"guzzlehttp/guzzle": "6.*",
|
|
||||||
"monolog/monolog": "1.*",
|
|
||||||
"psr/log": "~1.0",
|
|
||||||
"robmorgan/phinx": "0.4.*",
|
|
||||||
"yosymfony/toml": "0.3.*"
|
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"src/Ion/functions.php",
|
||||||
|
"src/AnimeClient.php",
|
||||||
|
"src/AnimeClient/constants.php"
|
||||||
|
],
|
||||||
|
"psr-4": {
|
||||||
|
"Aviat\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload-dev": {
|
||||||
|
"psr-4": {
|
||||||
|
"Aviat\\AnimeClient\\Tests\\": "tests/AnimeClient",
|
||||||
|
"Aviat\\Ion\\Tests\\": "tests/Ion"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"lock": false
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"amphp/http-client": "^v5.0.0",
|
||||||
|
"aura/html": "^2.5.0",
|
||||||
|
"aura/router": "^3.3.0",
|
||||||
|
"aura/session": "^2.1.0",
|
||||||
|
"aviat/banker": "^4.1.2",
|
||||||
|
"aviat/query": "^4.1.0",
|
||||||
|
"ext-dom": "*",
|
||||||
|
"ext-gd": "*",
|
||||||
|
"ext-intl": "*",
|
||||||
|
"ext-json": "*",
|
||||||
|
"ext-mbstring": "*",
|
||||||
|
"ext-pdo": "*",
|
||||||
|
"laminas/laminas-diactoros": "^3.0.0",
|
||||||
|
"laminas/laminas-httphandlerrunner": "^2.6.1",
|
||||||
|
"maximebf/consolekit": "^1.0.3",
|
||||||
|
"monolog/monolog": "^3.0.0",
|
||||||
|
"php": ">= 8.2.0",
|
||||||
|
"psr/http-message": "^1.0.1 || ^2.0.0",
|
||||||
|
"symfony/polyfill-mbstring": "^1.0.0",
|
||||||
|
"symfony/polyfill-util": "^1.0.0",
|
||||||
|
"tracy/tracy": "^2.8.0",
|
||||||
|
"yosymfony/toml": "^1.0.4"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpstan/phpstan": "^1.2.0",
|
||||||
|
"phpunit/phpunit": "^10.0.0",
|
||||||
|
"roave/security-advisories": "dev-master",
|
||||||
|
"spatie/phpunit-snapshot-assertions": "^5.0.1"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build:css": "cd public && npm run build:css && cd ..",
|
||||||
|
"build:js": "cd public && npm run build:js && cd ..",
|
||||||
|
"coverage": "php -dpcov.enabled=1 -dpcov.directory=. -dpcov.exclude=\"~vendor~\" ./vendor/bin/phpunit -c build",
|
||||||
|
"phpstan": "phpstan analyse -c phpstan.neon",
|
||||||
|
"watch:css": "cd public && npm run watch:css",
|
||||||
|
"watch:js": "cd public && npm run watch:js",
|
||||||
|
"test": "vendor/bin/phpunit -c build --no-coverage",
|
||||||
|
"test-update": "vendor/bin/phpunit -c build --no-coverage -d --update-snapshots"
|
||||||
|
},
|
||||||
|
"scripts-descriptions": {
|
||||||
|
"build:css": "Generate browser css",
|
||||||
|
"coverage": "Generate a test coverage report",
|
||||||
|
"phpstan": "Run PHP Static analysis",
|
||||||
|
"test": "Run the unit tests"
|
||||||
|
}
|
||||||
}
|
}
|
33
console
Executable file
33
console
Executable file
@ -0,0 +1,33 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
// Set up autoloader for third-party dependencies
|
||||||
|
require_once __DIR__ . '/vendor/autoload.php';
|
||||||
|
|
||||||
|
use Aviat\AnimeClient\Command;
|
||||||
|
use ConsoleKit\Console;
|
||||||
|
|
||||||
|
$GLOBALS['_SERVER']['HTTP_HOST'] = 'localhost';
|
||||||
|
|
||||||
|
const APP_DIR = __DIR__ . '/app';
|
||||||
|
const TEMPLATE_DIR = APP_DIR . '/templates';
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Start console script
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
try
|
||||||
|
{
|
||||||
|
(new Console([
|
||||||
|
'clear:cache' => Command\CacheClear::class,
|
||||||
|
'clear:thumbnails' => Command\ClearThumbnails::class,
|
||||||
|
'refresh:cache' => Command\CachePrime::class,
|
||||||
|
'refresh:thumbnails' => Command\UpdateThumbnails::class,
|
||||||
|
'lists:sync' => Command\SyncLists::class,
|
||||||
|
'sync:lists' => Command\SyncLists::class
|
||||||
|
]))->run();
|
||||||
|
}
|
||||||
|
catch (Throwable)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
70
frontEndSrc/css.js
Normal file
70
frontEndSrc/css.js
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
/**
|
||||||
|
* Script for optimizing css
|
||||||
|
*/
|
||||||
|
const fs = require('fs');
|
||||||
|
const postcss = require('postcss');
|
||||||
|
const atImport = require('postcss-import');
|
||||||
|
const cssNext = require('postcss-preset-env');
|
||||||
|
const cssNano = require('cssnano');
|
||||||
|
|
||||||
|
const lightCss = fs.readFileSync('css/light.css', 'utf-8');
|
||||||
|
const darkCss = fs.readFileSync('css/src/dark-override.css', 'utf-8');
|
||||||
|
const fullDarkCss = fs.readFileSync('css/dark.css', 'utf-8');
|
||||||
|
|
||||||
|
const minOptions = {
|
||||||
|
autoprefixer: false,
|
||||||
|
colormin: false,
|
||||||
|
minifyFontValues: false,
|
||||||
|
options: {
|
||||||
|
sourcemap: false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const processOptions = {
|
||||||
|
browser: '> 0.5%',
|
||||||
|
features: {
|
||||||
|
'custom-properties': true,
|
||||||
|
},
|
||||||
|
stage: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
(async () => {
|
||||||
|
// Basic theme
|
||||||
|
const lightMin = await postcss()
|
||||||
|
.use(atImport())
|
||||||
|
.use(cssNext(processOptions))
|
||||||
|
.use(cssNano(minOptions))
|
||||||
|
.process(lightCss, {
|
||||||
|
from: 'css/light.css',
|
||||||
|
to: '/public/css/light.min.css',
|
||||||
|
}).catch(console.error);
|
||||||
|
fs.writeFileSync('../public/css/light.min.css', lightMin.css);
|
||||||
|
|
||||||
|
// Dark theme
|
||||||
|
const darkFullMin = await postcss()
|
||||||
|
.use(atImport())
|
||||||
|
.use(cssNext(processOptions))
|
||||||
|
.use(cssNano(minOptions))
|
||||||
|
.process(fullDarkCss, {
|
||||||
|
from: 'css/dark.css',
|
||||||
|
to: '/public/css/dark.min.css',
|
||||||
|
});
|
||||||
|
fs.writeFileSync('../public/css/dark.min.css', darkFullMin.css);
|
||||||
|
|
||||||
|
// Dark override
|
||||||
|
const darkMin = await postcss()
|
||||||
|
.use(atImport())
|
||||||
|
.use(cssNext(processOptions))
|
||||||
|
.use(cssNano(minOptions))
|
||||||
|
.process(darkCss, {
|
||||||
|
from: 'css/dark-override.css',
|
||||||
|
to: '/public/css/dark.min.css',
|
||||||
|
}).catch(console.error);
|
||||||
|
const autoDarkCss = `${lightMin} @media (prefers-color-scheme: dark) { ${darkMin.css} }`
|
||||||
|
fs.writeFileSync('../public/css/auto.min.css', autoDarkCss)
|
||||||
|
|
||||||
|
})();
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
}
|
3
frontEndSrc/css/auto.css
Normal file
3
frontEndSrc/css/auto.css
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
@import "src/dark-override.css";
|
||||||
|
}
|
5
frontEndSrc/css/dark.css
Normal file
5
frontEndSrc/css/dark.css
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
@import "src/-marx-.css";
|
||||||
|
@import "src/general.css";
|
||||||
|
@import "src/components.css";
|
||||||
|
@import "src/responsive.css";
|
||||||
|
@import "src/dark-override.css";
|
4
frontEndSrc/css/light.css
Normal file
4
frontEndSrc/css/light.css
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
@import "src/-marx-.css";
|
||||||
|
@import "src/general.css";
|
||||||
|
@import "src/components.css";
|
||||||
|
@import "src/responsive.css";
|
531
frontEndSrc/css/src/-marx-.css
Normal file
531
frontEndSrc/css/src/-marx-.css
Normal file
@ -0,0 +1,531 @@
|
|||||||
|
:root {
|
||||||
|
--default-font-list: system-ui,sans-serif;
|
||||||
|
--monospace-font-list:'Anonymous Pro','Fira Code',Menlo,Monaco,Consolas,'Courier New',monospace;
|
||||||
|
--serif-font-list:Georgia,Times,'Times New Roman',serif;
|
||||||
|
-ms-text-size-adjust:100%;
|
||||||
|
-webkit-text-size-adjust:100%;
|
||||||
|
box-sizing:border-box;
|
||||||
|
cursor:default;
|
||||||
|
font-family:var(--default-font-list);
|
||||||
|
line-height:1.4;
|
||||||
|
overflow-y:scroll;
|
||||||
|
text-size-adjust:100%;
|
||||||
|
scroll-behavior:smooth;
|
||||||
|
}
|
||||||
|
|
||||||
|
audio:not([controls]) {
|
||||||
|
display:none;
|
||||||
|
}
|
||||||
|
|
||||||
|
details {
|
||||||
|
display:block;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=search] {
|
||||||
|
-webkit-appearance:textfield;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration {
|
||||||
|
-webkit-appearance:none;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
display:block;
|
||||||
|
margin:0 auto;
|
||||||
|
padding:0 1.6em 1.6em;
|
||||||
|
padding:0 1.6rem 1.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
summary {
|
||||||
|
display:block;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
background:#efefef;
|
||||||
|
color:#444;
|
||||||
|
display:block;
|
||||||
|
font-family:var(--monospace-font-list);
|
||||||
|
font-size:1.4em;
|
||||||
|
font-size:1.4rem;
|
||||||
|
margin:1.6em 0;
|
||||||
|
margin:1.6rem 0;
|
||||||
|
overflow:auto;
|
||||||
|
padding:1.6em;
|
||||||
|
padding:1.6rem;
|
||||||
|
word-break:break-all;
|
||||||
|
word-wrap:break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
progress {
|
||||||
|
display:inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
small {
|
||||||
|
color:#777;
|
||||||
|
font-size:75%;
|
||||||
|
}
|
||||||
|
|
||||||
|
big {
|
||||||
|
font-size:125%;
|
||||||
|
}
|
||||||
|
|
||||||
|
template {
|
||||||
|
display:none;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
border:.1rem solid #ccc;
|
||||||
|
border-radius:0;
|
||||||
|
display:block;
|
||||||
|
margin-bottom:.8rem;
|
||||||
|
overflow:auto;
|
||||||
|
padding:.8rem;
|
||||||
|
resize:vertical;
|
||||||
|
vertical-align:middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
[hidden] {
|
||||||
|
display:none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[unselectable] {
|
||||||
|
-moz-user-select:none;
|
||||||
|
-ms-user-select:none;
|
||||||
|
-webkit-user-select:none;
|
||||||
|
user-select:none;
|
||||||
|
}
|
||||||
|
|
||||||
|
*,::before,::after {
|
||||||
|
/* border-style:solid;
|
||||||
|
border-width:0; */
|
||||||
|
box-sizing:inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
font-size:inherit;
|
||||||
|
line-height:inherit;
|
||||||
|
margin:0;
|
||||||
|
padding:0;
|
||||||
|
}
|
||||||
|
|
||||||
|
::before,::after {
|
||||||
|
text-decoration:inherit;
|
||||||
|
vertical-align:inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
-webkit-transition:.25s ease;
|
||||||
|
color:#1271db;
|
||||||
|
text-decoration:none;
|
||||||
|
transition:.25s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
audio,canvas,iframe,img,svg,video {
|
||||||
|
vertical-align:middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
input,/*select*/,textarea {
|
||||||
|
border:.1rem solid #ccc;
|
||||||
|
color:inherit;
|
||||||
|
font-family:inherit;
|
||||||
|
font-style:inherit;
|
||||||
|
font-weight:inherit;
|
||||||
|
min-height:1.4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
code,kbd,pre,samp {
|
||||||
|
font-family:var(--monospace-font-list);
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
border-collapse:collapse;
|
||||||
|
border-spacing:0;
|
||||||
|
margin-bottom:1.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-moz-selection {
|
||||||
|
background-color:#b3d4fc;
|
||||||
|
text-shadow:none;
|
||||||
|
}
|
||||||
|
|
||||||
|
::selection {
|
||||||
|
background-color:#b3d4fc;
|
||||||
|
text-shadow:none;
|
||||||
|
}
|
||||||
|
|
||||||
|
button::-moz-focus-inner {
|
||||||
|
border:0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
color:#444;
|
||||||
|
font-family:var(--default-font-list);
|
||||||
|
font-size:1.6rem;
|
||||||
|
font-style:normal;
|
||||||
|
font-weight:400;
|
||||||
|
padding:0;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin:0 0 1.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1,h2,h3,h4,h5,h6 {
|
||||||
|
font-family:var(--default-font-list);
|
||||||
|
margin:2em 0 1.6em;
|
||||||
|
margin:2rem 0 1.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
border-bottom:.1rem solid rgba(0,0,0,0.2);
|
||||||
|
font-size:3.6em;
|
||||||
|
font-size:3.6rem;
|
||||||
|
font-style:normal;
|
||||||
|
font-weight:500;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size:3em;
|
||||||
|
font-size:3rem;
|
||||||
|
font-style:normal;
|
||||||
|
font-weight:500;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size:2.4em;
|
||||||
|
font-size:2.4rem;
|
||||||
|
font-style:normal;
|
||||||
|
font-weight:500;
|
||||||
|
margin:1.6rem 0 .4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-size:1.8em;
|
||||||
|
font-size:1.8rem;
|
||||||
|
font-style:normal;
|
||||||
|
font-weight:600;
|
||||||
|
margin:1.6rem 0 .4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h5 {
|
||||||
|
font-size:1.6em;
|
||||||
|
font-size:1.6rem;
|
||||||
|
font-style:normal;
|
||||||
|
font-weight:600;
|
||||||
|
margin:1.6rem 0 .4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h6 {
|
||||||
|
color:#777;
|
||||||
|
font-size:1.4em;
|
||||||
|
font-size:1.4rem;
|
||||||
|
font-style:normal;
|
||||||
|
font-weight:600;
|
||||||
|
margin:1.6rem 0 .4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
background:#efefef;
|
||||||
|
color:#444;
|
||||||
|
font-family:var(--monospace-font-list);
|
||||||
|
font-size:1.4rem;
|
||||||
|
word-break:break-all;
|
||||||
|
word-wrap:break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover,a:focus {
|
||||||
|
text-decoration:none;
|
||||||
|
}
|
||||||
|
|
||||||
|
dl {
|
||||||
|
margin-bottom:1.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
dd {
|
||||||
|
margin-left:4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul,ol {
|
||||||
|
margin-bottom:.8rem;
|
||||||
|
padding-left:2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote {
|
||||||
|
border-left:.2rem solid #1271db;
|
||||||
|
font-family:var(--serif-font-list);
|
||||||
|
font-style:italic;
|
||||||
|
margin:1.6rem 0;
|
||||||
|
padding-left:1.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
figcaption {
|
||||||
|
font-family:var(--serif-font-list);
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-size:62.5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
main,header,footer,article,section,aside,details,summary {
|
||||||
|
display:block;
|
||||||
|
height:auto;
|
||||||
|
margin:0 auto;
|
||||||
|
width:100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
border-top:.1rem solid rgba(0,0,0,0.2);
|
||||||
|
clear:both;
|
||||||
|
display:inline-block;
|
||||||
|
float:left;
|
||||||
|
max-width:100%;
|
||||||
|
padding:1rem 0;
|
||||||
|
text-align:center;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
border-top:.1rem solid rgba(0,0,0,0.2);
|
||||||
|
display:block;
|
||||||
|
margin-bottom:1.6rem;
|
||||||
|
width:100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
height:auto;
|
||||||
|
/* max-width:100%; */
|
||||||
|
vertical-align:baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=text],input[type=password],input[type=email],input[type=url],input[type=date],input[type=month],input[type=time],input[type=datetime],input[type=datetime-local],input[type=week],input[type=number],input[type=search],input[type=tel],input[type=color]/*,select */ {
|
||||||
|
border:.1rem solid #ccc;
|
||||||
|
border-radius:0;
|
||||||
|
display:inline-block;
|
||||||
|
padding:.8rem;
|
||||||
|
vertical-align:middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:not([type]) {
|
||||||
|
-webkit-appearance:none;
|
||||||
|
background-clip:padding-box;
|
||||||
|
background-color:#fff;
|
||||||
|
border:.1rem solid #ccc;
|
||||||
|
border-radius:0;
|
||||||
|
color:#444;
|
||||||
|
display:inline-block;
|
||||||
|
padding:.8rem;
|
||||||
|
text-align:left;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=color] {
|
||||||
|
padding:.8rem 1.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=text]:focus,input[type=password]:focus,input[type=email]:focus,input[type=url]:focus,input[type=date]:focus,input[type=month]:focus,input[type=time]:focus,input[type=datetime]:focus,input[type=datetime-local]:focus,input[type=week]:focus,input[type=number]:focus,input[type=search]:focus,input[type=tel]:focus,input[type=color]:focus,/* select:focus */,textarea:focus {
|
||||||
|
border-color:#b3d4fc;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:not([type]):focus {
|
||||||
|
border-color:#b3d4fc;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=radio],input[type=checkbox] {
|
||||||
|
vertical-align:middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=file]:focus,input[type=radio]:focus,input[type=checkbox]:focus {
|
||||||
|
outline:.1rem solid thin #444;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=text][disabled],input[type=password][disabled],input[type=email][disabled],input[type=url][disabled],input[type=date][disabled],input[type=month][disabled],input[type=time][disabled],input[type=datetime][disabled],input[type=datetime-local][disabled],input[type=week][disabled],input[type=number][disabled],input[type=search][disabled],input[type=tel][disabled],input[type=color][disabled],/*select[disabled]*/,textarea[disabled] {
|
||||||
|
background-color:#efefef;
|
||||||
|
color:#777;
|
||||||
|
cursor:not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:not([type])[disabled] {
|
||||||
|
background-color:#efefef;
|
||||||
|
color:#777;
|
||||||
|
cursor:not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[readonly],/*select[readonly]*/,textarea[readonly] {
|
||||||
|
background-color:#efefef;
|
||||||
|
border-color:#ccc;
|
||||||
|
color:#777;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:focus:invalid,textarea:focus:invalid/*,select:focus:invalid*/ {
|
||||||
|
border-color:#e9322d;
|
||||||
|
color:#b94a48;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=file]:focus:invalid:focus,input[type=radio]:focus:invalid:focus,input[type=checkbox]:focus:invalid:focus {
|
||||||
|
outline-color:#ff4136;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* select {
|
||||||
|
background-color:#fff;
|
||||||
|
border:.1rem solid #ccc;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
select[multiple] {
|
||||||
|
height:auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
line-height:2;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
border:0;
|
||||||
|
margin:0;
|
||||||
|
padding:.8rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
legend {
|
||||||
|
border-bottom:.1rem solid #ccc;
|
||||||
|
color:#444;
|
||||||
|
display:block;
|
||||||
|
margin-bottom:.8rem;
|
||||||
|
padding:.8rem 0;
|
||||||
|
width:100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=submit],button {
|
||||||
|
-moz-user-select:none;
|
||||||
|
-ms-user-select:none;
|
||||||
|
-webkit-transition:.25s ease;
|
||||||
|
-webkit-user-drag:none;
|
||||||
|
-webkit-user-select:none;
|
||||||
|
border:.2rem solid #444;
|
||||||
|
border-radius:0;
|
||||||
|
color:#444;
|
||||||
|
cursor:pointer;
|
||||||
|
display:inline-block;
|
||||||
|
margin-bottom:.8rem;
|
||||||
|
margin-right:.4rem;
|
||||||
|
padding:.8rem 1.6rem;
|
||||||
|
text-align:center;
|
||||||
|
text-decoration:none;
|
||||||
|
text-transform:uppercase;
|
||||||
|
transition:.25s ease;
|
||||||
|
user-select:none;
|
||||||
|
vertical-align:baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=submit] a,button a {
|
||||||
|
color:#444;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=submit]::-moz-focus-inner,button::-moz-focus-inner {
|
||||||
|
padding:0;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=submit]:hover,button:hover {
|
||||||
|
background:#444;
|
||||||
|
border-color:#444;
|
||||||
|
color:#fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=submit]:hover a,button:hover a {
|
||||||
|
color:#fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=submit]:active,button:active {
|
||||||
|
background:#6a6a6a;
|
||||||
|
border-color:#6a6a6a;
|
||||||
|
color:#fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=submit]:active a,button:active a {
|
||||||
|
color:#fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=submit]:disabled,button:disabled {
|
||||||
|
box-shadow:none;
|
||||||
|
cursor:not-allowed;
|
||||||
|
opacity:.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav ul {
|
||||||
|
list-style:none;
|
||||||
|
margin:0;
|
||||||
|
padding:0;
|
||||||
|
text-align:center;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav ul li {
|
||||||
|
display:inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav a {
|
||||||
|
-webkit-transition:.25s ease;
|
||||||
|
border-bottom:.2rem solid transparent;
|
||||||
|
color:#444;
|
||||||
|
padding:.8rem 1.6rem;
|
||||||
|
text-decoration:none;
|
||||||
|
transition:.25s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav a:hover,nav li.selected a {
|
||||||
|
border-color:rgba(0,0,0,0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
nav a:active {
|
||||||
|
border-color:rgba(0,0,0,0.56);
|
||||||
|
}
|
||||||
|
|
||||||
|
caption {
|
||||||
|
padding:.8rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
thead th {
|
||||||
|
background:#efefef;
|
||||||
|
color:#444;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr {
|
||||||
|
background:#fff;
|
||||||
|
margin-bottom:.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
th,td {
|
||||||
|
border:.1rem solid #ccc;
|
||||||
|
padding:.8rem 1.6rem;
|
||||||
|
text-align:center;
|
||||||
|
vertical-align:inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
tfoot tr {
|
||||||
|
background:none;
|
||||||
|
}
|
||||||
|
|
||||||
|
tfoot td {
|
||||||
|
color:#efefef;
|
||||||
|
font-size:.8rem;
|
||||||
|
font-style:italic;
|
||||||
|
padding:1.6rem .4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen {
|
||||||
|
[hidden~=screen] {
|
||||||
|
display:inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
[hidden~=screen]:not(:active):not(:focus):not(:target) {
|
||||||
|
clip:rect(0000)!important;
|
||||||
|
position:absolute!important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and max-width 40rem {
|
||||||
|
article,section,aside {
|
||||||
|
clear:both;
|
||||||
|
display:block;
|
||||||
|
max-width:100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
margin-right:1.6rem;
|
||||||
|
}
|
||||||
|
}
|
271
frontEndSrc/css/src/components.css
Normal file
271
frontEndSrc/css/src/components.css
Normal file
@ -0,0 +1,271 @@
|
|||||||
|
/* -----------------------------------------------------------------------------
|
||||||
|
CSS loading icon
|
||||||
|
------------------------------------------------------------------------------*/
|
||||||
|
.cssload-loader {
|
||||||
|
position: relative;
|
||||||
|
left: calc(50% - 31px);
|
||||||
|
width: 62px;
|
||||||
|
height: 62px;
|
||||||
|
border-radius: 50%;
|
||||||
|
perspective: 780px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cssload-inner {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cssload-inner.cssload-one {
|
||||||
|
left: 0%;
|
||||||
|
top: 0%;
|
||||||
|
animation: cssload-rotate-one 1.15s linear infinite;
|
||||||
|
border-bottom: 3px solid rgb(0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cssload-inner.cssload-two {
|
||||||
|
right: 0%;
|
||||||
|
top: 0%;
|
||||||
|
animation: cssload-rotate-two 1.15s linear infinite;
|
||||||
|
border-right: 3px solid rgb(0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cssload-inner.cssload-three {
|
||||||
|
right: 0%;
|
||||||
|
bottom: 0%;
|
||||||
|
animation: cssload-rotate-three 1.15s linear infinite;
|
||||||
|
border-top: 3px solid rgb(0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes cssload-rotate-one {
|
||||||
|
0% {
|
||||||
|
transform: rotateX(35deg) rotateY(-45deg) rotateZ(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotateX(35deg) rotateY(-45deg) rotateZ(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes cssload-rotate-two {
|
||||||
|
0% {
|
||||||
|
transform: rotateX(50deg) rotateY(10deg) rotateZ(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotateX(50deg) rotateY(10deg) rotateZ(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes cssload-rotate-three {
|
||||||
|
0% {
|
||||||
|
transform: rotateX(35deg) rotateY(55deg) rotateZ(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotateX(35deg) rotateY(55deg) rotateZ(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
Loading overlay
|
||||||
|
-----------------------------------------------------------------------------*/
|
||||||
|
#loading-shadow {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: rgba(0, 0, 0, 0.8);
|
||||||
|
z-index: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
#loading-shadow .loading-wrapper {
|
||||||
|
position: fixed;
|
||||||
|
z-index: 501;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#loading-shadow .loading-content {
|
||||||
|
position: relative;
|
||||||
|
color: #fff
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-content .cssload-inner.cssload-one,
|
||||||
|
.loading-content .cssload-inner.cssload-two,
|
||||||
|
.loading-content .cssload-inner.cssload-three {
|
||||||
|
border-color: #fff
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
CSS Tabs
|
||||||
|
-----------------------------------------------------------------------------*/
|
||||||
|
.tabs {
|
||||||
|
display: inline-block;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
background: #efefef;
|
||||||
|
box-shadow: 0 48px 80px -32px rgba(0, 0, 0, 0.3);
|
||||||
|
margin-top: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs > label {
|
||||||
|
border: 1px solid #e5e5e5;
|
||||||
|
width: 100%;
|
||||||
|
padding: 20px 30px;
|
||||||
|
background: #e5e5e5;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 18px;
|
||||||
|
color: #7f7f7f;
|
||||||
|
transition: background 0.1s, color 0.1s;
|
||||||
|
/* margin-left: 4em; */
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs > label:hover {
|
||||||
|
background: #d8d8d8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs > label:active {
|
||||||
|
background: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs > [type=radio]:focus + label {
|
||||||
|
box-shadow: inset 0px 0px 0px 3px #2aa1c0;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs > [type=radio] {
|
||||||
|
position: absolute;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs > [type=radio]:checked + label {
|
||||||
|
border-bottom: 1px solid #fff;
|
||||||
|
background: #fff;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs > [type=radio]:checked + label + .content {
|
||||||
|
border: 1px solid #e5e5e5;
|
||||||
|
border-top: 0;
|
||||||
|
display: block;
|
||||||
|
padding: 15px;
|
||||||
|
background: #fff;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0 auto;
|
||||||
|
overflow: auto;
|
||||||
|
/* text-align: center; */
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs .content, .single-tab {
|
||||||
|
display: none;
|
||||||
|
max-height: 950px;
|
||||||
|
border: 1px solid #e5e5e5;
|
||||||
|
border-top: 0;
|
||||||
|
padding: 15px;
|
||||||
|
background: #fff;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0 auto;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-tab {
|
||||||
|
display: block;
|
||||||
|
border: 1px solid #e5e5e5;
|
||||||
|
box-shadow: 0 48px 80px -32px rgba(0, 0, 0, 0.3);
|
||||||
|
margin-top: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs .content.full-height, .single-tab.full-height {
|
||||||
|
max-height: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 800px) {
|
||||||
|
.tabs > label {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs .content {
|
||||||
|
order: 99;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------------------------------------------------------------------------
|
||||||
|
Vertical Tabs
|
||||||
|
----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
.vertical-tabs {
|
||||||
|
border: 1px solid #e5e5e5;
|
||||||
|
box-shadow: 0 48px 80px -32px rgba(0, 0, 0, 0.3);
|
||||||
|
margin: 0 auto;
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vertical-tabs input[type="radio"] {
|
||||||
|
position: absolute;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vertical-tabs .tab {
|
||||||
|
align-items: center;
|
||||||
|
display: inline-block;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vertical-tabs .tab label {
|
||||||
|
align-items: center;
|
||||||
|
background: #e5e5e5;
|
||||||
|
border: 1px solid #e5e5e5;
|
||||||
|
color: #7f7f7f;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 0 20px;
|
||||||
|
width: 28%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vertical-tabs .tab label:hover {
|
||||||
|
background: #d8d8d8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vertical-tabs .tab label:active {
|
||||||
|
background: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vertical-tabs .tab .content {
|
||||||
|
display: none;
|
||||||
|
border: 1px solid #e5e5e5;
|
||||||
|
border-left: 0;
|
||||||
|
border-right: 0;
|
||||||
|
max-height: 950px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vertical-tabs .tab .content.full-height {
|
||||||
|
max-height: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vertical-tabs [type=radio]:checked + label {
|
||||||
|
border: 0;
|
||||||
|
background: #fff;
|
||||||
|
color: #000;
|
||||||
|
width: 38%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vertical-tabs [type=radio]:focus + label {
|
||||||
|
box-shadow: inset 0px 0px 0px 3px #2aa1c0;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vertical-tabs [type=radio]:checked ~ .content {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
172
frontEndSrc/css/src/dark-override.css
Normal file
172
frontEndSrc/css/src/dark-override.css
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
a {
|
||||||
|
color: rgb(25, 120, 226);
|
||||||
|
text-shadow: var(--link-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: #9e34fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
body,
|
||||||
|
legend,
|
||||||
|
nav ul li a {
|
||||||
|
background: #333;
|
||||||
|
color: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav a:hover, nav li.selected a {
|
||||||
|
border-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
header button {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
td, th {
|
||||||
|
border-color: #111;
|
||||||
|
}
|
||||||
|
|
||||||
|
thead td,
|
||||||
|
thead th {
|
||||||
|
background: #333;
|
||||||
|
color: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody > tr:nth-child(2n) {
|
||||||
|
background: #555;
|
||||||
|
color: #eee;
|
||||||
|
}
|
||||||
|
tbody > tr:nth-child(2n+1) {
|
||||||
|
background: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer, legend, hr {
|
||||||
|
border-color: #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
small {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
input, input[type], select, textarea {
|
||||||
|
border-color: #bbb;
|
||||||
|
color: #bbb;
|
||||||
|
background: #333;
|
||||||
|
padding:.8em;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
background: #444;
|
||||||
|
background: linear-gradient(#666, #555, #444, #555, #666);
|
||||||
|
border-radius: 0.5em;
|
||||||
|
margin: 0;
|
||||||
|
text-transform: none;
|
||||||
|
border-color: #ddd;
|
||||||
|
color: #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background: #222;
|
||||||
|
background: linear-gradient(#444, #333, #222, #333, #444);
|
||||||
|
border-color: #ddd;
|
||||||
|
color: #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:active {
|
||||||
|
background: #333;
|
||||||
|
background: linear-gradient(#333, #333);
|
||||||
|
}
|
||||||
|
|
||||||
|
.media:hover button {
|
||||||
|
background: linear-gradient(#666, #555, #444, #555, #666);
|
||||||
|
}
|
||||||
|
|
||||||
|
.media:hover button:hover {
|
||||||
|
background: linear-gradient(#444, #555, #666, #555, #444);
|
||||||
|
}
|
||||||
|
|
||||||
|
.message, .static-message {
|
||||||
|
text-shadow: var(--white-link-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.message.success, .static-message.success {
|
||||||
|
background: #1f8454;
|
||||||
|
border-color: #70dda9;
|
||||||
|
}
|
||||||
|
.message.error, .static-message.error {
|
||||||
|
border-color:#f3e6e6;
|
||||||
|
background: #924949;
|
||||||
|
}
|
||||||
|
.message.info, .static-message.info {
|
||||||
|
border-color: #FFFFCC;
|
||||||
|
background: #bfbe3a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invisible tr,
|
||||||
|
.invisible td,
|
||||||
|
.invisible th,
|
||||||
|
.invisible tbody > tr:nth-child(2n),
|
||||||
|
.invisible tbody > tr:nth-child(2n+1) {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
#main-nav {
|
||||||
|
border-bottom: .1rem solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs,
|
||||||
|
.vertical-tabs{
|
||||||
|
background: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs > label,
|
||||||
|
.vertical-tabs .tab label {
|
||||||
|
background: #222;
|
||||||
|
border: 0;
|
||||||
|
color: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vertical-tabs .tab label {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs > label:hover,
|
||||||
|
.vertical-tabs .tab > label:hover {
|
||||||
|
background: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs > label:active,
|
||||||
|
.vertical-tabs .tab > label:active {
|
||||||
|
background: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs > [type="radio"]:checked + label,
|
||||||
|
.tabs > [type="radio"]:checked + label + .content,
|
||||||
|
.vertical-tabs [type="radio"]:checked + label,
|
||||||
|
.vertical-tabs [type="radio"]:checked ~ .content,
|
||||||
|
.single-tab {
|
||||||
|
/* border-color: #333; */
|
||||||
|
border: 0;
|
||||||
|
background: #666;
|
||||||
|
color: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vertical-tabs {
|
||||||
|
background: #222;
|
||||||
|
border: 1px solid #444;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vertical-tabs .tab {
|
||||||
|
background: #666;
|
||||||
|
border-bottom: 1px solid #444;
|
||||||
|
}
|
||||||
|
|
||||||
|
.streaming-logo {
|
||||||
|
-webkit-filter: drop-shadow(0 0 2px #fff);
|
||||||
|
filter: drop-shadow(0 0 2px #fff);
|
||||||
|
}
|
||||||
|
|
931
frontEndSrc/css/src/general.css
Normal file
931
frontEndSrc/css/src/general.css
Normal file
@ -0,0 +1,931 @@
|
|||||||
|
:root {
|
||||||
|
--blue-link: rgb(18, 113, 219);
|
||||||
|
--link-shadow: 1px 1px 1px #000;
|
||||||
|
--white-link-shadow: 1px 1px 1px #fff;
|
||||||
|
--shadow: 2px 2px 2px #000;
|
||||||
|
--title-overlay: rgba(0, 0, 0, 0.45);
|
||||||
|
--title-overlay-fallback: #000;
|
||||||
|
--text-color: #ffffff;
|
||||||
|
--normal-padding: 0.25em 0.125em;
|
||||||
|
--link-hover-color: #7d12db;
|
||||||
|
--edit-link-hover-color: #db7d12;
|
||||||
|
--edit-link-color: #12db18;
|
||||||
|
--radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
template, [hidden="hidden"], .media[hidden] {
|
||||||
|
display: none
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
button {
|
||||||
|
background: #fff;
|
||||||
|
background: linear-gradient(#ddd, #eee, #fff, #eee, #ddd);
|
||||||
|
border-radius: 0.5em;
|
||||||
|
margin: 0;
|
||||||
|
text-transform: none;
|
||||||
|
border-color: #555;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background: #bbb;
|
||||||
|
background: linear-gradient(#cfcfcf, #dfdfdf, #efefef, #dfdfdf, #cfcfcf);
|
||||||
|
border-color: #555;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:active {
|
||||||
|
background: #ddd;
|
||||||
|
background: linear-gradient(#ddd, #ddd);
|
||||||
|
}
|
||||||
|
|
||||||
|
.media:hover button {
|
||||||
|
background: linear-gradient(#bbb, #ccc, #ddd, #ccc, #bbb);
|
||||||
|
}
|
||||||
|
|
||||||
|
.media:hover button:hover {
|
||||||
|
background: linear-gradient(#afafaf, #bfbfbf, #cfcfcf, #bfbfbf, #afafaf);
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
/* min-width: 85%; */
|
||||||
|
box-shadow: 0 48px 80px -32px rgba(0, 0, 0, 0.3);
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
padding: 1em;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
thead td, thead th {
|
||||||
|
padding: 0.5em;
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=number] {
|
||||||
|
min-width: 0;
|
||||||
|
width: 4.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=checkbox], input[type=radio] {
|
||||||
|
min-width: auto;
|
||||||
|
vertical-align: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
input, textarea {
|
||||||
|
min-width: 30em;
|
||||||
|
min-width: 30rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody > tr:nth-child(odd) {
|
||||||
|
background: #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover, a:active {
|
||||||
|
color: var(--link-hover-color)
|
||||||
|
}
|
||||||
|
|
||||||
|
iframe {
|
||||||
|
display: block;
|
||||||
|
margin: 0 auto;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -----------------------------------------------------------------------------
|
||||||
|
Utility classes
|
||||||
|
------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
.bracketed {
|
||||||
|
color: var(--edit-link-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bracketed, #main-nav a {
|
||||||
|
text-shadow: var(--link-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bracketed:before {
|
||||||
|
content: '[\00a0'
|
||||||
|
}
|
||||||
|
|
||||||
|
.bracketed:after {
|
||||||
|
content: '\00a0]'
|
||||||
|
}
|
||||||
|
|
||||||
|
.bracketed:hover, .bracketed:active {
|
||||||
|
color: var(--edit-link-hover-color)
|
||||||
|
}
|
||||||
|
|
||||||
|
.grow-1 {
|
||||||
|
flex-grow: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-wrap {
|
||||||
|
flex-wrap: wrap
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-no-wrap {
|
||||||
|
flex-wrap: nowrap
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-align-start {
|
||||||
|
align-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-align-end {
|
||||||
|
align-items: flex-end
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-align-space-around {
|
||||||
|
align-content: space-around
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-justify-start {
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-justify-space-around {
|
||||||
|
justify-content: space-around
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-center {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-self-center {
|
||||||
|
align-self: center
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-space-evenly {
|
||||||
|
justify-content: space-evenly;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex {
|
||||||
|
display: inline-block;
|
||||||
|
display: flex
|
||||||
|
}
|
||||||
|
|
||||||
|
.small-font {
|
||||||
|
font-size: 1.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.justify {
|
||||||
|
text-align: justify
|
||||||
|
}
|
||||||
|
|
||||||
|
.align-center {
|
||||||
|
text-align: center !important
|
||||||
|
}
|
||||||
|
|
||||||
|
.align-left {
|
||||||
|
text-align: left !important
|
||||||
|
}
|
||||||
|
|
||||||
|
.align-right {
|
||||||
|
text-align: right !important
|
||||||
|
}
|
||||||
|
|
||||||
|
.valign-top {
|
||||||
|
vertical-align: top
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-border {
|
||||||
|
border: none
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-wrap {
|
||||||
|
text-align: center;
|
||||||
|
margin: 0 auto;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-wrap-flex {
|
||||||
|
display: inline-block;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-content: space-evenly;
|
||||||
|
justify-content: space-between;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
td .media-wrap-flex {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.danger {
|
||||||
|
background-color: #ff4136;
|
||||||
|
border-color: #924949;
|
||||||
|
color: #924949;
|
||||||
|
/* color: #fff; */
|
||||||
|
}
|
||||||
|
|
||||||
|
.danger:hover, .danger:active {
|
||||||
|
background-color: #924949;
|
||||||
|
border-color: #ff4136;
|
||||||
|
color: #ff4136;
|
||||||
|
/* color: #fff; */
|
||||||
|
}
|
||||||
|
|
||||||
|
td.danger, td.danger:hover, td.danger:active {
|
||||||
|
background-color: transparent;
|
||||||
|
color: #924949;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-btn {
|
||||||
|
background: transparent;
|
||||||
|
border-color: var(--edit-link-color);
|
||||||
|
color: var(--edit-link-color);
|
||||||
|
text-shadow: var(--link-shadow);
|
||||||
|
padding: 0 0.5em;
|
||||||
|
padding: 0 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-btn:hover, .user-btn:active {
|
||||||
|
background: transparent;
|
||||||
|
border-color: var(--edit-link-hover-color);
|
||||||
|
color: var(--edit-link-hover-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-btn:active {
|
||||||
|
background: var(--edit-link-hover-color);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.full-width {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.full-height {
|
||||||
|
max-height: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toph {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -----------------------------------------------------------------------------
|
||||||
|
Main Nav
|
||||||
|
------------------------------------------------------------------------------*/
|
||||||
|
#main-nav {
|
||||||
|
font-family: var(--default-font-list);
|
||||||
|
margin: 2em 0 1.6em;
|
||||||
|
margin: 2rem 0 1.6rem;
|
||||||
|
border-bottom: .1rem solid rgba(0, 0, 0, 0.2);
|
||||||
|
font-size: 3.6em;
|
||||||
|
font-size: 3.6rem;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -----------------------------------------------------------------------------
|
||||||
|
Table sorting and form styles
|
||||||
|
------------------------------------------------------------------------------*/
|
||||||
|
.sorting,
|
||||||
|
.sorting-asc,
|
||||||
|
.sorting-desc {
|
||||||
|
vertical-align: text-bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sorting::before {
|
||||||
|
content: " ↕\00a0";
|
||||||
|
}
|
||||||
|
|
||||||
|
.sorting-asc::before {
|
||||||
|
content: " ↑\00a0";
|
||||||
|
}
|
||||||
|
|
||||||
|
.sorting-desc::before {
|
||||||
|
content: " ↓\00a0";
|
||||||
|
}
|
||||||
|
|
||||||
|
.form {
|
||||||
|
/* width: 100%; */
|
||||||
|
}
|
||||||
|
|
||||||
|
.form thead th, .form thead tr {
|
||||||
|
background: inherit;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form tr > td:nth-child(odd) {
|
||||||
|
text-align: right;
|
||||||
|
min-width: 25px;
|
||||||
|
max-width: 30%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form tr > td:nth-child(even) {
|
||||||
|
text-align: left;
|
||||||
|
/* width: 70%; */
|
||||||
|
}
|
||||||
|
|
||||||
|
.invisible tbody > tr:nth-child(odd) {
|
||||||
|
background: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.borderless,
|
||||||
|
.borderless tr,
|
||||||
|
.borderless td,
|
||||||
|
.borderless th,
|
||||||
|
.invisible tr,
|
||||||
|
.invisible td,
|
||||||
|
.invisible th,
|
||||||
|
table.invisible {
|
||||||
|
box-shadow: none;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -----------------------------------------------------------------------------
|
||||||
|
Message boxes
|
||||||
|
------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
.message, .static-message {
|
||||||
|
position: relative;
|
||||||
|
margin: 0.5em auto;
|
||||||
|
padding: 0.5em;
|
||||||
|
width: 95%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message .close {
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
position: absolute;
|
||||||
|
right: 0.5em;
|
||||||
|
top: 0.5em;
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
line-height: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message:hover .close:after {
|
||||||
|
content: '☒';
|
||||||
|
}
|
||||||
|
|
||||||
|
.message:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message .icon {
|
||||||
|
left: 0.5em;
|
||||||
|
top: 0.5em;
|
||||||
|
margin-right: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message.error, .static-message.error {
|
||||||
|
border: 1px solid #924949;
|
||||||
|
background: #f3e6e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message.error .icon::after {
|
||||||
|
content: '✘';
|
||||||
|
}
|
||||||
|
|
||||||
|
.message.success, .static-message.success {
|
||||||
|
border: 1px solid #1f8454;
|
||||||
|
background: #70dda9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message.success .icon::after {
|
||||||
|
content: '✔'
|
||||||
|
}
|
||||||
|
|
||||||
|
.message.info, .static-message.info {
|
||||||
|
border: 1px solid #bfbe3a;
|
||||||
|
background: #FFFFCC;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message.info .icon::after {
|
||||||
|
content: '⚠';
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -----------------------------------------------------------------------------
|
||||||
|
Base list styles
|
||||||
|
------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
.media, .character, .small-character {
|
||||||
|
position: relative;
|
||||||
|
vertical-align: top;
|
||||||
|
display: inline-block;
|
||||||
|
text-align: center;
|
||||||
|
width: 220px;
|
||||||
|
height: 312px;
|
||||||
|
margin: var(--normal-padding);
|
||||||
|
z-index: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.details picture.cover,
|
||||||
|
picture.cover {
|
||||||
|
display: initial;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media > img,
|
||||||
|
.character > img,
|
||||||
|
.small-character > img {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media .edit-buttons > button {
|
||||||
|
margin: 0.5em auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name,
|
||||||
|
.media-metadata > div,
|
||||||
|
.medium-metadata > div,
|
||||||
|
.row {
|
||||||
|
text-shadow: var(--shadow);
|
||||||
|
color: var(--text-color);
|
||||||
|
padding: var(--normal-padding);
|
||||||
|
text-align: right;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-type, .age-rating {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media > .media-metadata {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media > .medium-metadata {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media > .name {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media > .name a {
|
||||||
|
display: inline-block;
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media .name a::before {
|
||||||
|
/* background: var(--title-overlay-fallback);
|
||||||
|
background: var(--title-overlay); */
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
height: 312px;
|
||||||
|
left: 0;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
width: 220px;
|
||||||
|
z-index: -1; /* Put the pseudo-element behind its parent */
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-list .media:hover .name a::before {
|
||||||
|
/* transition: .25s ease; */
|
||||||
|
background: rgba(0, 0, 0, 0.75);
|
||||||
|
}
|
||||||
|
|
||||||
|
.media > .name span.canonical {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media > .name small {
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media:hover .name {
|
||||||
|
background: rgba(0, 0, 0, 0.75);
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-list .media > .name a:hover,
|
||||||
|
.media-list .media > .name a:hover small {
|
||||||
|
color: var(--blue-link);
|
||||||
|
}
|
||||||
|
|
||||||
|
.media:hover > button[hidden],
|
||||||
|
.media:hover > .edit-buttons[hidden] {
|
||||||
|
|
||||||
|
transition: .25s ease;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media:hover {
|
||||||
|
transition: .25s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.small-character > .name a,
|
||||||
|
.small-character > .name a small,
|
||||||
|
.character > .name a,
|
||||||
|
.character > .name a small,
|
||||||
|
.media > .name a,
|
||||||
|
.media > .name a small {
|
||||||
|
background: none;
|
||||||
|
color: #fff;
|
||||||
|
text-shadow: var(--shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -----------------------------------------------------------------------------
|
||||||
|
Anime-list-specific styles
|
||||||
|
------------------------------------------------------------------------------*/
|
||||||
|
.anime .name, .manga .name {
|
||||||
|
background: var(--title-overlay-fallback);
|
||||||
|
background: var(--title-overlay);
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.5em 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.anime .media-type,
|
||||||
|
.anime .airing-status,
|
||||||
|
.anime .user-rating,
|
||||||
|
.anime .completion,
|
||||||
|
.anime .age-rating,
|
||||||
|
.anime .edit,
|
||||||
|
.anime .delete {
|
||||||
|
background: none;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.anime .table, .manga .table {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.anime .row, .manga .row {
|
||||||
|
width: 100%;
|
||||||
|
display: inline-block;
|
||||||
|
display: flex;
|
||||||
|
align-content: space-around;
|
||||||
|
justify-content: space-around;
|
||||||
|
text-align: center;
|
||||||
|
padding: 0 inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.anime .row > span, .manga .row > span {
|
||||||
|
text-align: left;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.anime .row > div, .manga .row > div {
|
||||||
|
font-size: 0.8em;
|
||||||
|
display: inline-block;
|
||||||
|
display: flex-item;
|
||||||
|
align-self: center;
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.anime .media > button.plus-one {
|
||||||
|
border-color: hsla(0, 0%, 100%, .65);
|
||||||
|
position: absolute;
|
||||||
|
top: 138px;
|
||||||
|
top: calc(50% - 21.2px);
|
||||||
|
left: 44px;
|
||||||
|
left: calc(50% - 57.8px);
|
||||||
|
z-index: 50;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -----------------------------------------------------------------------------
|
||||||
|
Manga-list-specific styles
|
||||||
|
------------------------------------------------------------------------------*/
|
||||||
|
.manga .row {
|
||||||
|
padding: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.manga .media {
|
||||||
|
/* border: 1px solid #ddd; */
|
||||||
|
height: 310px;
|
||||||
|
margin: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.manga .media > .edit-buttons {
|
||||||
|
position: absolute;
|
||||||
|
top: 86px;
|
||||||
|
/* top: calc(50% - 58.5px); */
|
||||||
|
top: calc(50% - 21.2px);
|
||||||
|
left: 43.5px;
|
||||||
|
left: calc(50% - 57.8px);
|
||||||
|
z-index: 40;
|
||||||
|
}
|
||||||
|
|
||||||
|
.manga .media > .edit-buttons button {
|
||||||
|
border-color: hsla(0, 0%, 100%, .65);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -----------------------------------------------------------------------------
|
||||||
|
Search page styles
|
||||||
|
------------------------------------------------------------------------------*/
|
||||||
|
.media.search > .name {
|
||||||
|
background-color: #555;
|
||||||
|
background-color: rgba(000, 000, 000, 0.35);
|
||||||
|
background-size: cover;
|
||||||
|
background-size: contain;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
/* There are two .name elements, just darken them both in this case! */
|
||||||
|
.media.search.disabled .name {
|
||||||
|
background-color: #000;
|
||||||
|
background-color: rgba(0, 0, 0, 0.75);
|
||||||
|
background-size: cover;
|
||||||
|
background-size: contain;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media.search > .row {
|
||||||
|
z-index: 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.big-check, .mal-check {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.big-check:checked + label {
|
||||||
|
transition: .25s ease;
|
||||||
|
background: rgba(0, 0, 0, 0.75);
|
||||||
|
}
|
||||||
|
|
||||||
|
.big-check:checked + label:after {
|
||||||
|
content: '✓';
|
||||||
|
font-size: 15em;
|
||||||
|
font-size: 15rem;
|
||||||
|
text-align: center;
|
||||||
|
color: greenyellow;
|
||||||
|
position: absolute;
|
||||||
|
top: 147px;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
z-index: 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
#series-list article.media {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
#series-list .name, #series-list .name label {
|
||||||
|
position: absolute;
|
||||||
|
display: block;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
vertical-align: middle;
|
||||||
|
line-height: 1.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#series-list .name small {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
Details page styles
|
||||||
|
-----------------------------------------------------------------------------*/
|
||||||
|
.details {
|
||||||
|
margin: 1.5rem auto 0 auto;
|
||||||
|
padding: 1rem;
|
||||||
|
font-size: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* .description {
|
||||||
|
max-width: 80rem;
|
||||||
|
columns: 4 28rem;
|
||||||
|
columns: 4 28em;
|
||||||
|
|
||||||
|
margin-bottom: 1.6em;
|
||||||
|
margin-bottom: 1.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.description br + br {
|
||||||
|
page-break-before: avoid;
|
||||||
|
page-break-after: auto;
|
||||||
|
page-break-inside: avoid;
|
||||||
|
break-inside: avoid;
|
||||||
|
break-after: auto;
|
||||||
|
break-before: avoid;
|
||||||
|
} */
|
||||||
|
|
||||||
|
.fixed {
|
||||||
|
max-width: 115em;
|
||||||
|
max-width: 115rem;
|
||||||
|
/* max-width: 80%; */
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details .cover {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details .flex > * {
|
||||||
|
margin: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details .media-details td {
|
||||||
|
padding: 0 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details p {
|
||||||
|
text-align: justify;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details .media-details td:nth-child(odd) {
|
||||||
|
width: 1%;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details .media-details td:nth-child(even) {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details a h1,
|
||||||
|
.details a h2 {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.character,
|
||||||
|
.small-character,
|
||||||
|
.person {
|
||||||
|
/* background: rgba(0,0,0,0.5); */
|
||||||
|
width: 225px;
|
||||||
|
height: 350px;
|
||||||
|
vertical-align: middle;
|
||||||
|
white-space: nowrap;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.person {
|
||||||
|
width: 225px;
|
||||||
|
height: 338px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.small-person {
|
||||||
|
width: 200px;
|
||||||
|
height: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.character a {
|
||||||
|
height: 350px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.character:hover .name,
|
||||||
|
.small-character:hover .name {
|
||||||
|
background: rgba(0, 0, 0, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.small-character a {
|
||||||
|
display: inline-block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.small-character .name,
|
||||||
|
.character .name {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.small-character img,
|
||||||
|
.character img,
|
||||||
|
.small-character picture,
|
||||||
|
.character picture,
|
||||||
|
.person img,
|
||||||
|
.person picture {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
z-index: 5;
|
||||||
|
max-height: 350px;
|
||||||
|
max-width: 225px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.person img,
|
||||||
|
.person picture {
|
||||||
|
max-height: 338px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.small-person img,
|
||||||
|
.small-person picture {
|
||||||
|
max-height: 300px;
|
||||||
|
max-width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.min-table {
|
||||||
|
min-width: 0;
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.max-table {
|
||||||
|
min-width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
aside.info {
|
||||||
|
/* max-width: 390px; */
|
||||||
|
max-width: 33%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fixed aside {
|
||||||
|
max-width: 390px;
|
||||||
|
}
|
||||||
|
|
||||||
|
aside picture, aside img {
|
||||||
|
display: block;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
User page styles
|
||||||
|
-----------------------------------------------------------------------------*/
|
||||||
|
.small-character {
|
||||||
|
width: 160px;
|
||||||
|
height: 250px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.small-character img,
|
||||||
|
.small-character picture {
|
||||||
|
max-height: 250px;
|
||||||
|
max-width: 160px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-page .media-wrap {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media a {
|
||||||
|
display: inline-block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
Images / Logos
|
||||||
|
-----------------------------------------------------------------------------*/
|
||||||
|
.streaming-logo {
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.small-streaming-logo {
|
||||||
|
width: 25px;
|
||||||
|
height: 25px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cover-streaming-link {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media:hover .cover-streaming-link {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cover-streaming-link .streaming-logo {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
-webkit-filter: drop-shadow(0 -1px 4px #fff);
|
||||||
|
filter: drop-shadow(0 -1px 4px #fff);
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-img {
|
||||||
|
width: 110px;
|
||||||
|
height: 156px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
Settings Form
|
||||||
|
-----------------------------------------------------------------------------*/
|
||||||
|
.settings.form .content article {
|
||||||
|
margin: 1em;
|
||||||
|
display: inline-block;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
iFrame container
|
||||||
|
-----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
.responsive-iframe {
|
||||||
|
margin-top: 1em;
|
||||||
|
overflow: hidden;
|
||||||
|
padding-bottom: 56.25%;
|
||||||
|
position: relative;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.responsive-iframe iframe {
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
position: absolute;
|
||||||
|
}
|
137
frontEndSrc/css/src/responsive.css
Normal file
137
frontEndSrc/css/src/responsive.css
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
Viewport-based styles
|
||||||
|
-----------------------------------------------------------------------------*/
|
||||||
|
@media screen and (max-width: 1100px) {
|
||||||
|
.flex {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
aside.info,
|
||||||
|
aside.info + article,
|
||||||
|
.fixed aside.info,
|
||||||
|
.fixed aside.info + article {
|
||||||
|
max-width: none;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* aside.info {
|
||||||
|
order: 1;
|
||||||
|
} */
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 800px) {
|
||||||
|
* {
|
||||||
|
max-width: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
body,
|
||||||
|
.details .flex > * {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
table,
|
||||||
|
table th,
|
||||||
|
table td,
|
||||||
|
table .align-right,
|
||||||
|
table.align-center {
|
||||||
|
border: 0;
|
||||||
|
/* display: block; */
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
text-align: left;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
table td {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
table tbody,
|
||||||
|
table.media-details {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.media-details td {
|
||||||
|
display: block;
|
||||||
|
text-align: left !important;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
table thead {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details .media-details td:nth-child(2n+1) {
|
||||||
|
font-weight: bold;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.streaming-links tr td:not(:first-child) {
|
||||||
|
display:none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 40em) {
|
||||||
|
nav a {
|
||||||
|
line-height: 4em;
|
||||||
|
line-height: 4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
img,
|
||||||
|
picture {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
padding: 0 0, 5em 0.5em;
|
||||||
|
padding: 0 0.5rem 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media {
|
||||||
|
margin: 2px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details {
|
||||||
|
padding: 0.5em;
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Expand tabs */
|
||||||
|
.tabs > [type="radio"]:checked + label {
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Expand vertical tabs */
|
||||||
|
.vertical-tabs .tab {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs .content,
|
||||||
|
.tabs > [type="radio"]:checked + label + .content,
|
||||||
|
.vertical-tabs .tab .content {
|
||||||
|
display: block;
|
||||||
|
border: 0;
|
||||||
|
max-height: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs > label,
|
||||||
|
.tabs > label:active,
|
||||||
|
.tabs > label:hover,
|
||||||
|
.tabs > [type="radio"]:checked + label,
|
||||||
|
.vertical-tabs .tab label,
|
||||||
|
.vertical-tabs .tab label:active,
|
||||||
|
.vertical-tabs .tab label:hover,
|
||||||
|
.vertical-tabs [type=radio]:focus + label,
|
||||||
|
.vertical-tabs [type=radio]:checked + label {
|
||||||
|
background: #fff;
|
||||||
|
border: 0;
|
||||||
|
width: 100%;
|
||||||
|
cursor: default;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
3
frontEndSrc/cssfilter.js
Normal file
3
frontEndSrc/cssfilter.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
module.exports = function filter(filename) {
|
||||||
|
return ! String(filename).includes('min');
|
||||||
|
}
|
353
frontEndSrc/js/anime-client.js
Normal file
353
frontEndSrc/js/anime-client.js
Normal file
@ -0,0 +1,353 @@
|
|||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// ! Base
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
const matches = (elm, selector) => {
|
||||||
|
let m = (elm.document || elm.ownerDocument).querySelectorAll(selector);
|
||||||
|
let i = matches.length;
|
||||||
|
while (--i >= 0 && m.item(i) !== elm) {};
|
||||||
|
return i > -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AnimeClient = {
|
||||||
|
/**
|
||||||
|
* Placeholder function
|
||||||
|
*/
|
||||||
|
noop: () => {},
|
||||||
|
/**
|
||||||
|
* DOM selector
|
||||||
|
*
|
||||||
|
* @param {string} selector - The dom selector string
|
||||||
|
* @param {Element} [context]
|
||||||
|
* @return array of dom elements
|
||||||
|
*/
|
||||||
|
$(selector, context = null) {
|
||||||
|
if (typeof selector !== 'string') {
|
||||||
|
return selector;
|
||||||
|
}
|
||||||
|
|
||||||
|
context = (context !== null && context.nodeType === 1)
|
||||||
|
? context
|
||||||
|
: document;
|
||||||
|
|
||||||
|
let elements = [];
|
||||||
|
if (selector.match(/^#([\w]+$)/)) {
|
||||||
|
elements.push(document.getElementById(selector.split('#')[1]));
|
||||||
|
} else {
|
||||||
|
elements = [].slice.apply(context.querySelectorAll(selector));
|
||||||
|
}
|
||||||
|
|
||||||
|
return elements;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Does the selector exist on the current page?
|
||||||
|
*
|
||||||
|
* @param {string} selector
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
hasElement (selector) {
|
||||||
|
return AnimeClient.$(selector).length > 0;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Scroll to the top of the Page
|
||||||
|
*
|
||||||
|
* @return {void}
|
||||||
|
*/
|
||||||
|
scrollToTop () {
|
||||||
|
const el = AnimeClient.$('header')[0];
|
||||||
|
el.scrollIntoView(true);
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Hide the selected element
|
||||||
|
*
|
||||||
|
* @param {string|Element|Element[]} sel - the selector of the element to hide
|
||||||
|
* @return {void}
|
||||||
|
*/
|
||||||
|
hide (sel) {
|
||||||
|
if (typeof sel === 'string') {
|
||||||
|
sel = AnimeClient.$(sel);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(sel)) {
|
||||||
|
sel.forEach(el => el.setAttribute('hidden', 'hidden'));
|
||||||
|
} else {
|
||||||
|
sel.setAttribute('hidden', 'hidden');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* UnHide the selected element
|
||||||
|
*
|
||||||
|
* @param {string|Element|Element[]} sel - the selector of the element to hide
|
||||||
|
* @return {void}
|
||||||
|
*/
|
||||||
|
show (sel) {
|
||||||
|
if (typeof sel === 'string') {
|
||||||
|
sel = AnimeClient.$(sel);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(sel)) {
|
||||||
|
sel.forEach(el => el.removeAttribute('hidden'));
|
||||||
|
} else {
|
||||||
|
sel.removeAttribute('hidden');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Display a message box
|
||||||
|
*
|
||||||
|
* @param {string} type - message type: info, error, success
|
||||||
|
* @param {string} message - the message itself
|
||||||
|
* @return {void}
|
||||||
|
*/
|
||||||
|
showMessage (type, message) {
|
||||||
|
let template =
|
||||||
|
`<div class='message ${type}'>
|
||||||
|
<span class='icon'></span>
|
||||||
|
${message}
|
||||||
|
<span class='close'></span>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
let sel = AnimeClient.$('.message');
|
||||||
|
if (sel[0] !== undefined) {
|
||||||
|
sel[0].remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimeClient.$('header')[0].insertAdjacentHTML('beforeend', template);
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Finds the closest parent element matching the passed selector
|
||||||
|
*
|
||||||
|
* @param {Element} current - the current Element
|
||||||
|
* @param {string} parentSelector - selector for the parent element
|
||||||
|
* @return {Element|null} - the parent element
|
||||||
|
*/
|
||||||
|
closestParent (current, parentSelector) {
|
||||||
|
if (Element.prototype.closest !== undefined) {
|
||||||
|
return current.closest(parentSelector);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (current !== document.documentElement) {
|
||||||
|
if (matches(current, parentSelector)) {
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
current = current.parentElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Generate a full url from a relative path
|
||||||
|
*
|
||||||
|
* @param {string} path - url path
|
||||||
|
* @return {string} - full url
|
||||||
|
*/
|
||||||
|
url (path) {
|
||||||
|
let uri = `//${document.location.host}`;
|
||||||
|
uri += (path.charAt(0) === '/') ? path : `/${path}`;
|
||||||
|
|
||||||
|
return uri;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Throttle execution of a function
|
||||||
|
*
|
||||||
|
* @see https://remysharp.com/2010/07/21/throttling-function-calls
|
||||||
|
* @see https://jsfiddle.net/jonathansampson/m7G64/
|
||||||
|
* @param {Number} interval - the minimum throttle time in ms
|
||||||
|
* @param {Function} fn - the function to throttle
|
||||||
|
* @param {Object} [scope] - the 'this' object for the function
|
||||||
|
* @return {Function}
|
||||||
|
*/
|
||||||
|
throttle (interval, fn, scope) {
|
||||||
|
let wait = false;
|
||||||
|
return function (...args) {
|
||||||
|
const context = scope || this;
|
||||||
|
|
||||||
|
if ( ! wait) {
|
||||||
|
fn.apply(context, args);
|
||||||
|
wait = true;
|
||||||
|
setTimeout(function() {
|
||||||
|
wait = false;
|
||||||
|
}, interval);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// ! Events
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function addEvent(sel, event, listener) {
|
||||||
|
// Recurse!
|
||||||
|
if (! event.match(/^([\w\-]+)$/)) {
|
||||||
|
event.split(' ').forEach((evt) => {
|
||||||
|
addEvent(sel, evt, listener);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
sel.addEventListener(event, listener, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function delegateEvent(sel, target, event, listener) {
|
||||||
|
// Attach the listener to the parent
|
||||||
|
addEvent(sel, event, (e) => {
|
||||||
|
// Get live version of the target selector
|
||||||
|
AnimeClient.$(target, sel).forEach((element) => {
|
||||||
|
if(e.target == element) {
|
||||||
|
listener.call(element, e);
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an event listener
|
||||||
|
*
|
||||||
|
* @param {string|Element} sel - the parent selector to bind to
|
||||||
|
* @param {string} event - event name(s) to bind
|
||||||
|
* @param {string|Element|function} target - the element to directly bind the event to
|
||||||
|
* @param {function} [listener] - event listener callback
|
||||||
|
* @return {void}
|
||||||
|
*/
|
||||||
|
AnimeClient.on = (sel, event, target, listener) => {
|
||||||
|
if (listener === undefined) {
|
||||||
|
listener = target;
|
||||||
|
AnimeClient.$(sel).forEach((el) => {
|
||||||
|
addEvent(el, event, listener);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
AnimeClient.$(sel).forEach((el) => {
|
||||||
|
delegateEvent(el, target, event, listener);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// ! Ajax
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Url encoding for non-get requests
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
* @returns {string}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
function ajaxSerialize(data) {
|
||||||
|
let pairs = [];
|
||||||
|
|
||||||
|
Object.keys(data).forEach((name) => {
|
||||||
|
let value = data[name].toString();
|
||||||
|
|
||||||
|
name = encodeURIComponent(name);
|
||||||
|
value = encodeURIComponent(value);
|
||||||
|
|
||||||
|
pairs.push(`${name}=${value}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
return pairs.join('&');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make an ajax request
|
||||||
|
*
|
||||||
|
* Config:{
|
||||||
|
* data: // data to send with the request
|
||||||
|
* type: // http verb of the request, defaults to GET
|
||||||
|
* success: // success callback
|
||||||
|
* error: // error callback
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @param {string} url - the url to request
|
||||||
|
* @param {Object} config - the configuration object
|
||||||
|
* @return {XMLHttpRequest}
|
||||||
|
*/
|
||||||
|
AnimeClient.ajax = (url, config) => {
|
||||||
|
// Set some sane defaults
|
||||||
|
const defaultConfig = {
|
||||||
|
data: {},
|
||||||
|
type: 'GET',
|
||||||
|
dataType: '',
|
||||||
|
success: AnimeClient.noop,
|
||||||
|
mimeType: 'application/x-www-form-urlencoded',
|
||||||
|
error: AnimeClient.noop
|
||||||
|
}
|
||||||
|
|
||||||
|
config = {
|
||||||
|
...defaultConfig,
|
||||||
|
...config,
|
||||||
|
}
|
||||||
|
|
||||||
|
let request = new XMLHttpRequest();
|
||||||
|
let method = String(config.type).toUpperCase();
|
||||||
|
|
||||||
|
if (method === 'GET') {
|
||||||
|
url += (url.match(/\?/))
|
||||||
|
? ajaxSerialize(config.data)
|
||||||
|
: `?${ajaxSerialize(config.data)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
request.open(method, url);
|
||||||
|
|
||||||
|
request.onreadystatechange = () => {
|
||||||
|
if (request.readyState === 4) {
|
||||||
|
let responseText = '';
|
||||||
|
|
||||||
|
if (request.responseType === 'json') {
|
||||||
|
responseText = JSON.parse(request.responseText);
|
||||||
|
} else {
|
||||||
|
responseText = request.responseText;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.status > 299) {
|
||||||
|
config.error.call(null, request.status, responseText, request.response);
|
||||||
|
} else {
|
||||||
|
config.success.call(null, responseText, request.status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (config.dataType === 'json') {
|
||||||
|
config.data = JSON.stringify(config.data);
|
||||||
|
config.mimeType = 'application/json';
|
||||||
|
} else {
|
||||||
|
config.data = ajaxSerialize(config.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
request.setRequestHeader('Content-Type', config.mimeType);
|
||||||
|
|
||||||
|
if (method === 'GET') {
|
||||||
|
request.send(null);
|
||||||
|
} else {
|
||||||
|
request.send(config.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return request
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do a get request
|
||||||
|
*
|
||||||
|
* @param {string} url
|
||||||
|
* @param {object|function} data
|
||||||
|
* @param {function} [callback]
|
||||||
|
* @return {XMLHttpRequest}
|
||||||
|
*/
|
||||||
|
AnimeClient.get = (url, data, callback = null) => {
|
||||||
|
if (callback === null) {
|
||||||
|
callback = data;
|
||||||
|
data = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return AnimeClient.ajax(url, {
|
||||||
|
data,
|
||||||
|
success: callback
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Export
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export default AnimeClient;
|
128
frontEndSrc/js/anime.js
Normal file
128
frontEndSrc/js/anime.js
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
import _ from './anime-client.js'
|
||||||
|
import { renderSearchResults } from './template-helpers.js'
|
||||||
|
import { getNestedProperty, hasNestedProperty } from "./fns";
|
||||||
|
|
||||||
|
const search = (query, isCollection = false) => {
|
||||||
|
// Show the loader
|
||||||
|
_.show('.cssload-loader');
|
||||||
|
|
||||||
|
// Do the api search
|
||||||
|
return _.get(_.url('/anime-collection/search'), { query }, (searchResults, status) => {
|
||||||
|
searchResults = JSON.parse(searchResults);
|
||||||
|
|
||||||
|
// Hide the loader
|
||||||
|
_.hide('.cssload-loader');
|
||||||
|
|
||||||
|
// Show the results
|
||||||
|
_.$('#series-list')[ 0 ].innerHTML = renderSearchResults('anime', searchResults, isCollection);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Anime list search
|
||||||
|
if (_.hasElement('.anime #search')) {
|
||||||
|
let prevRequest = null;
|
||||||
|
|
||||||
|
_.on('#search', 'input', _.throttle(250, (e) => {
|
||||||
|
const query = encodeURIComponent(e.target.value);
|
||||||
|
if (query === '') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prevRequest !== null) {
|
||||||
|
prevRequest.abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
prevRequest = search(query);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Anime collection search
|
||||||
|
if (_.hasElement('#search-anime-collection')) {
|
||||||
|
let prevRequest = null;
|
||||||
|
|
||||||
|
_.on('#search-anime-collection', 'input', _.throttle(250, (e) => {
|
||||||
|
const query = encodeURIComponent(e.target.value);
|
||||||
|
if (query === '') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prevRequest !== null) {
|
||||||
|
prevRequest.abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
prevRequest = search(query, true);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Action to increment episode count
|
||||||
|
_.on('body.anime.list', 'click', '.plus-one', (e) => {
|
||||||
|
let parentSel = _.closestParent(e.target, 'article');
|
||||||
|
let watchedCount = parseInt(_.$('.completed_number', parentSel)[ 0 ].textContent, 10) || 0;
|
||||||
|
let totalCount = parseInt(_.$('.total_number', parentSel)[ 0 ].textContent, 10);
|
||||||
|
let title = _.$('.name a', parentSel)[ 0 ].textContent;
|
||||||
|
|
||||||
|
// Setup the update data
|
||||||
|
let data = {
|
||||||
|
id: parentSel.dataset.kitsuId,
|
||||||
|
anilist_id: parentSel.dataset.anilistId,
|
||||||
|
mal_id: parentSel.dataset.malId,
|
||||||
|
data: {
|
||||||
|
progress: watchedCount + 1
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const displayMessage = (type, message) => {
|
||||||
|
_.hide('#loading-shadow');
|
||||||
|
_.showMessage(type, `${message} ${title}`);
|
||||||
|
_.scrollToTop();
|
||||||
|
}
|
||||||
|
|
||||||
|
const showError = () => displayMessage('error', 'Failed to update');
|
||||||
|
|
||||||
|
// If the episode count is 0, and incremented,
|
||||||
|
// change status to currently watching
|
||||||
|
if (isNaN(watchedCount) || watchedCount === 0) {
|
||||||
|
data.data.status = 'CURRENT';
|
||||||
|
}
|
||||||
|
|
||||||
|
// If you increment at the last episode, mark as completed
|
||||||
|
if ((!isNaN(watchedCount)) && (watchedCount + 1) === totalCount) {
|
||||||
|
data.data.status = 'COMPLETED';
|
||||||
|
}
|
||||||
|
|
||||||
|
_.show('#loading-shadow');
|
||||||
|
|
||||||
|
// okay, lets actually make some changes!
|
||||||
|
_.ajax(_.url('/anime/increment'), {
|
||||||
|
data,
|
||||||
|
dataType: 'json',
|
||||||
|
type: 'POST',
|
||||||
|
success: (res) => {
|
||||||
|
try {
|
||||||
|
const resData = JSON.parse(res);
|
||||||
|
|
||||||
|
// Do a rough sanity check for weird errors
|
||||||
|
let updatedProgress = getNestedProperty(resData, 'data.libraryEntry.update.libraryEntry.progress');
|
||||||
|
if (hasNestedProperty(resData, 'error') || updatedProgress !== data.data.progress) {
|
||||||
|
showError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We've completed the series
|
||||||
|
if (getNestedProperty(resData, 'data.libraryEntry.update.libraryEntry.status') === 'COMPLETED') {
|
||||||
|
_.hide(parentSel);
|
||||||
|
displayMessage('success', 'Completed')
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Just a normal update
|
||||||
|
_.$('.completed_number', parentSel)[ 0 ].textContent = ++watchedCount;
|
||||||
|
displayMessage('success', 'Updated');
|
||||||
|
} catch (_) {
|
||||||
|
showError();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: showError,
|
||||||
|
});
|
||||||
|
});
|
83
frontEndSrc/js/base/sort-tables.js
Normal file
83
frontEndSrc/js/base/sort-tables.js
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
const LightTableSorter = (() => {
|
||||||
|
let th = null;
|
||||||
|
let cellIndex = null;
|
||||||
|
let order = '';
|
||||||
|
const text = (row) => row.cells.item(cellIndex).textContent.toLowerCase();
|
||||||
|
const sort = (a, b) => {
|
||||||
|
let textA = text(a);
|
||||||
|
let textB = text(b);
|
||||||
|
console.log("Comparing " + textA + " and " + textB)
|
||||||
|
|
||||||
|
if(th.classList.contains("numeric")){
|
||||||
|
let arrayA = textA.replace('episodes: ','').replace('-',0).split("/");
|
||||||
|
let arrayB = textB.replace('episodes: ','').replace('-',0).split("/");
|
||||||
|
if(arrayA.length > 1) {
|
||||||
|
textA = parseInt(arrayA[0],10) / parseInt(arrayA[1],10);
|
||||||
|
textB = parseInt(arrayB[0],10) / parseInt(arrayB[1],10);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
textA = parseInt(arrayA[0],10);
|
||||||
|
textB = parseInt(arrayB[0],10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (parseInt(textA, 10)) {
|
||||||
|
textA = parseInt(textA, 10);
|
||||||
|
textB = parseInt(textB, 10);
|
||||||
|
}
|
||||||
|
if (textA > textB) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (textA < textB) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
const toggle = () => {
|
||||||
|
const c = order !== 'sorting-asc' ? 'sorting-asc' : 'sorting-desc';
|
||||||
|
th.className = (th.className.replace(order, '') + ' ' + c).trim();
|
||||||
|
return order = c;
|
||||||
|
};
|
||||||
|
const reset = () => {
|
||||||
|
th.classList.remove('sorting-asc', 'sorting-desc');
|
||||||
|
th.classList.add('sorting');
|
||||||
|
return order = '';
|
||||||
|
};
|
||||||
|
const onClickEvent = (e) => {
|
||||||
|
if (th && (cellIndex !== e.target.cellIndex)) {
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
th = e.target;
|
||||||
|
if (th.nodeName.toLowerCase() === 'th') {
|
||||||
|
cellIndex = th.cellIndex;
|
||||||
|
const tbody = th.offsetParent.getElementsByTagName('tbody')[0];
|
||||||
|
let rows = Array.from(tbody.rows);
|
||||||
|
if (rows) {
|
||||||
|
rows.sort(sort);
|
||||||
|
if (order === 'sorting-asc') {
|
||||||
|
rows.reverse();
|
||||||
|
}
|
||||||
|
toggle();
|
||||||
|
tbody.innerHtml = '';
|
||||||
|
|
||||||
|
rows.forEach(row => {
|
||||||
|
tbody.appendChild(row);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
init: () => {
|
||||||
|
let ths = document.getElementsByTagName('th');
|
||||||
|
let results = [];
|
||||||
|
for (let i = 0, len = ths.length; i < len; i++) {
|
||||||
|
let th = ths[i];
|
||||||
|
th.classList.add('sorting');
|
||||||
|
th.classList.add('testing');
|
||||||
|
results.push(th.onclick = onClickEvent);
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
LightTableSorter.init();
|
110
frontEndSrc/js/events.js
Normal file
110
frontEndSrc/js/events.js
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
import _ from './anime-client.js';
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Event subscriptions
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
_.on('header', 'click', '.message', hide);
|
||||||
|
_.on('form.js-delete', 'submit', confirmDelete);
|
||||||
|
_.on('.js-clear-cache', 'click', clearAPICache);
|
||||||
|
_.on('.vertical-tabs input', 'change', scrollToSection);
|
||||||
|
_.on('.media-filter', 'input', filterMedia);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Handler functions
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide the html element attached to the event
|
||||||
|
*
|
||||||
|
* @param {MouseEvent} event
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function hide (event) {
|
||||||
|
_.hide(event.target)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Confirm deletion of an item
|
||||||
|
*
|
||||||
|
* @param {MouseEvent} event
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function confirmDelete (event) {
|
||||||
|
const proceed = confirm('Are you ABSOLUTELY SURE you want to delete this item?');
|
||||||
|
|
||||||
|
if (proceed === false) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the API cache, and show a message if the cache is cleared
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function clearAPICache () {
|
||||||
|
_.get('/cache_purge', () => {
|
||||||
|
_.showMessage('success', 'Successfully purged api cache');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scroll to the accordion/vertical tab section just opened
|
||||||
|
*
|
||||||
|
* @param {InputEvent} event
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function scrollToSection (event) {
|
||||||
|
const el = event.currentTarget.parentElement;
|
||||||
|
const rect = el.getBoundingClientRect();
|
||||||
|
|
||||||
|
const top = rect.top + window.pageYOffset;
|
||||||
|
|
||||||
|
window.scrollTo({
|
||||||
|
top,
|
||||||
|
behavior: 'smooth',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter an anime or manga list
|
||||||
|
*
|
||||||
|
* @param {InputEvent} event
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function filterMedia (event) {
|
||||||
|
const rawFilter = event.target.value;
|
||||||
|
const filter = new RegExp(rawFilter, 'i');
|
||||||
|
|
||||||
|
// console.log('Filtering items by: ', filter);
|
||||||
|
|
||||||
|
if (rawFilter !== '') {
|
||||||
|
// Filter the cover view
|
||||||
|
_.$('article.media').forEach(article => {
|
||||||
|
const titleLink = _.$('.name a', article)[0];
|
||||||
|
const title = String(titleLink.textContent).trim();
|
||||||
|
if ( ! filter.test(title)) {
|
||||||
|
_.hide(article);
|
||||||
|
} else {
|
||||||
|
_.show(article);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Filter the list view
|
||||||
|
_.$('table.media-wrap tbody tr').forEach(tr => {
|
||||||
|
const titleCell = _.$('td.align-left', tr)[0];
|
||||||
|
const titleLink = _.$('a', titleCell)[0];
|
||||||
|
const linkTitle = String(titleLink.textContent).trim();
|
||||||
|
const textTitle = String(titleCell.textContent).trim();
|
||||||
|
if ( ! (filter.test(linkTitle) || filter.test(textTitle))) {
|
||||||
|
_.hide(tr);
|
||||||
|
} else {
|
||||||
|
_.show(tr);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
_.show('article.media');
|
||||||
|
_.show('table.media-wrap tbody tr');
|
||||||
|
}
|
||||||
|
}
|
103
frontEndSrc/js/fns.js
Normal file
103
frontEndSrc/js/fns.js
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
/**
|
||||||
|
* Make sure properties are in an easily splittable format
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {String} props
|
||||||
|
* @param {String} [sep='.'] The default separator
|
||||||
|
* @return {String}
|
||||||
|
*/
|
||||||
|
function _normalizeProperty(props, sep = '.') {
|
||||||
|
// Since we split by period, and property lookup
|
||||||
|
// is the same by dot or [], replace bracket lookups
|
||||||
|
// with periods
|
||||||
|
return props.replace(/\[(.*?)]/g, sep + '$1');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tell if a nested object has a given property (or array a given index)
|
||||||
|
* given an object such as a.b.c.d = 5, hasNestedProperty(a, 'b.c.d') will return true.
|
||||||
|
*
|
||||||
|
* @param {Object} object the object to get the property from
|
||||||
|
* @param {String} property the path to the property as a string
|
||||||
|
* @returns {boolean} true when property in object, false otherwise
|
||||||
|
*/
|
||||||
|
export function hasNestedProperty(object, property) {
|
||||||
|
if (object && typeof object === 'object') {
|
||||||
|
if (typeof property === 'string' && property !== '') {
|
||||||
|
property = _normalizeProperty(property);
|
||||||
|
|
||||||
|
let split = property.split('.');
|
||||||
|
return split.reduce((obj, prop, idx, array) => {
|
||||||
|
if (idx === array.length - 1) {
|
||||||
|
return !!(obj && obj.hasOwnProperty(prop));
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj && obj[prop];
|
||||||
|
}, object);
|
||||||
|
} else if (typeof property === 'number') {
|
||||||
|
return property in object;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value of a deeply nested property in an object
|
||||||
|
*
|
||||||
|
* @param {Object} object the object to get the property
|
||||||
|
* @param {string} property the path to the property as a string
|
||||||
|
* @param {string} [sep='.'] The default separator to split on
|
||||||
|
* @return {*} the value of the property
|
||||||
|
*/
|
||||||
|
export function getNestedProperty(object, property, sep = '.') {
|
||||||
|
if (isType('string', property) && property !== '') {
|
||||||
|
// convert numbers to dot syntax
|
||||||
|
property = _normalizeProperty(property, sep);
|
||||||
|
const levels = property.split(sep);
|
||||||
|
|
||||||
|
try {
|
||||||
|
return levels.reduce((obj, prop) => obj[prop], object);
|
||||||
|
} catch (e) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reliably get the type of the value of a variable
|
||||||
|
*
|
||||||
|
* @param {*} x The variable to get the type of
|
||||||
|
* @return {string} The name of the type
|
||||||
|
*/
|
||||||
|
export function getType(x) {
|
||||||
|
// is it an array?
|
||||||
|
if (Array.isArray(x)) {
|
||||||
|
return 'array';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use typeof for truthy primitives
|
||||||
|
if (typeof x !== 'object') {
|
||||||
|
return (typeof x).toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
const type = function () {
|
||||||
|
return Object.prototype.toString.call(this).slice(8, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, strip the type out of the '[Object x]' toString value
|
||||||
|
return type.call(x).toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the value matches the passed type name
|
||||||
|
*
|
||||||
|
* @param {string} type Javascript type name
|
||||||
|
* @param {*} val The value to type check
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
export function isType(type, val) {
|
||||||
|
return getType(val) === String(type).toLowerCase();
|
||||||
|
}
|
5
frontEndSrc/js/index.js
Normal file
5
frontEndSrc/js/index.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import './sw.js';
|
||||||
|
import './events.js';
|
||||||
|
import './session-check.js';
|
||||||
|
import './anime.js';
|
||||||
|
import './manga.js';
|
114
frontEndSrc/js/manga.js
Normal file
114
frontEndSrc/js/manga.js
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
import _ from './anime-client.js'
|
||||||
|
import { renderSearchResults } from './template-helpers.js'
|
||||||
|
import { getNestedProperty, hasNestedProperty } from "./fns";
|
||||||
|
|
||||||
|
const search = (query) => {
|
||||||
|
_.show('.cssload-loader');
|
||||||
|
return _.get(_.url('/manga/search'), { query }, (searchResults, status) => {
|
||||||
|
searchResults = JSON.parse(searchResults);
|
||||||
|
_.hide('.cssload-loader');
|
||||||
|
_.$('#series-list')[ 0 ].innerHTML = renderSearchResults('manga', searchResults);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (_.hasElement('.manga #search')) {
|
||||||
|
let prevRequest = null
|
||||||
|
|
||||||
|
_.on('#search', 'input', _.throttle(250, (e) => {
|
||||||
|
let query = encodeURIComponent(e.target.value);
|
||||||
|
if (query === '') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prevRequest !== null) {
|
||||||
|
prevRequest.abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
prevRequest = search(query);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Javascript for editing manga, if logged in
|
||||||
|
*/
|
||||||
|
_.on('.manga.list', 'click', '.edit-buttons button', (e) => {
|
||||||
|
let thisSel = e.target;
|
||||||
|
let parentSel = _.closestParent(e.target, 'article');
|
||||||
|
let type = thisSel.classList.contains('plus-one-chapter') ? 'chapter' : 'volume';
|
||||||
|
let completed = parseInt(_.$(`.${type}s_read`, parentSel)[ 0 ].textContent, 10) || 0;
|
||||||
|
let total = parseInt(_.$(`.${type}_count`, parentSel)[ 0 ].textContent, 10);
|
||||||
|
let title = _.$('.name', parentSel)[ 0 ].textContent;
|
||||||
|
|
||||||
|
if (isNaN(completed)) {
|
||||||
|
completed = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup the update data
|
||||||
|
let data = {
|
||||||
|
id: parentSel.dataset.kitsuId,
|
||||||
|
anilist_id: parentSel.dataset.anilistId,
|
||||||
|
mal_id: parentSel.dataset.malId,
|
||||||
|
data: {
|
||||||
|
progress: completed
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const displayMessage = (type, message) => {
|
||||||
|
_.hide('#loading-shadow');
|
||||||
|
_.showMessage(type, `${message} ${title}`);
|
||||||
|
_.scrollToTop();
|
||||||
|
}
|
||||||
|
|
||||||
|
const showError = () => displayMessage('error', 'Failed to update');
|
||||||
|
|
||||||
|
// If the episode count is 0, and incremented,
|
||||||
|
// change status to currently reading
|
||||||
|
if (isNaN(completed) || completed === 0) {
|
||||||
|
data.data.status = 'CURRENT';
|
||||||
|
}
|
||||||
|
|
||||||
|
// If you increment at the last chapter, mark as completed
|
||||||
|
if ((!isNaN(completed)) && (completed + 1) === total) {
|
||||||
|
data.data.status = 'COMPLETED';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the total count
|
||||||
|
data.data.progress = ++completed;
|
||||||
|
|
||||||
|
_.show('#loading-shadow');
|
||||||
|
|
||||||
|
_.ajax(_.url('/manga/increment'), {
|
||||||
|
data,
|
||||||
|
dataType: 'json',
|
||||||
|
type: 'POST',
|
||||||
|
mimeType: 'application/json',
|
||||||
|
success: (res) => {
|
||||||
|
try {
|
||||||
|
const resData = JSON.parse(res);
|
||||||
|
|
||||||
|
// Do a rough sanity check for weird errors
|
||||||
|
let updatedProgress = getNestedProperty(resData, 'data.libraryEntry.update.libraryEntry.progress');
|
||||||
|
if (hasNestedProperty(resData, 'error') || updatedProgress !== data.data.progress) {
|
||||||
|
showError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We've completed the series
|
||||||
|
if (getNestedProperty(resData, 'data.libraryEntry.update.libraryEntry.status') === 'COMPLETED') {
|
||||||
|
_.hide(parentSel);
|
||||||
|
displayMessage('success', 'Completed')
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Just a normal update
|
||||||
|
_.$(`.${type}s_read`, parentSel)[ 0 ].textContent = String(completed);
|
||||||
|
displayMessage('success', 'Updated');
|
||||||
|
|
||||||
|
} catch (_) {
|
||||||
|
showError();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: showError,
|
||||||
|
});
|
||||||
|
});
|
40
frontEndSrc/js/session-check.js
Normal file
40
frontEndSrc/js/session-check.js
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import _ from './anime-client.js';
|
||||||
|
|
||||||
|
(() => {
|
||||||
|
let hidden = null;
|
||||||
|
let visibilityChange = null;
|
||||||
|
|
||||||
|
if (typeof document.hidden !== "undefined") {
|
||||||
|
hidden = "hidden";
|
||||||
|
visibilityChange = "visibilitychange";
|
||||||
|
} else if (typeof document.msHidden !== "undefined") {
|
||||||
|
hidden = "msHidden";
|
||||||
|
visibilityChange = "msvisibilitychange";
|
||||||
|
} else if (typeof document.webkitHidden !== "undefined") {
|
||||||
|
hidden = "webkitHidden";
|
||||||
|
visibilityChange = "webkitvisibilitychange";
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleVisibilityChange() {
|
||||||
|
// Check the user's session to see if they are currently logged-in
|
||||||
|
// when the page becomes visible
|
||||||
|
if ( ! document[hidden]) {
|
||||||
|
_.get('/heartbeat', (beat) => {
|
||||||
|
const status = JSON.parse(beat)
|
||||||
|
|
||||||
|
// If the session is expired, immediately reload so that
|
||||||
|
// you can't attempt to do an action that requires authentication
|
||||||
|
if (status.hasAuth !== true) {
|
||||||
|
document.removeEventListener(visibilityChange, handleVisibilityChange, false);
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hidden === null) {
|
||||||
|
console.info('Page visibility API not supported, JS session check will not work');
|
||||||
|
} else {
|
||||||
|
document.addEventListener(visibilityChange, handleVisibilityChange, false);
|
||||||
|
}
|
||||||
|
})();
|
8
frontEndSrc/js/sw.js
Normal file
8
frontEndSrc/js/sw.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
// Start the service worker, if you can
|
||||||
|
if ('serviceWorker' in navigator) {
|
||||||
|
navigator.serviceWorker.register('/sw.js').then(reg => {
|
||||||
|
console.log('Service worker registered', reg.scope);
|
||||||
|
}).catch(error => {
|
||||||
|
console.error('Failed to register service worker', error);
|
||||||
|
});
|
||||||
|
}
|
81
frontEndSrc/js/template-helpers.js
Normal file
81
frontEndSrc/js/template-helpers.js
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import _ from './anime-client.js';
|
||||||
|
|
||||||
|
// Click on hidden MAL checkbox so
|
||||||
|
// that MAL id is passed
|
||||||
|
_.on('main', 'change', '.big-check', (e) => {
|
||||||
|
const id = e.target.id;
|
||||||
|
document.getElementById(`mal_${id}`).checked = true;
|
||||||
|
document.getElementById(`anilist_${id}`).checked = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On search results with an existing library entry, this shows that fact, with an edit link for the existing
|
||||||
|
* library entry
|
||||||
|
*
|
||||||
|
* @param {'anime'|'manga'} type
|
||||||
|
* @param {Object} item
|
||||||
|
* @param isCollection
|
||||||
|
* @returns {String}
|
||||||
|
*/
|
||||||
|
function renderEditLink (type, item, isCollection = false) {
|
||||||
|
if (isCollection || item.libraryEntry === null) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div class="row">
|
||||||
|
<span class="edit"><big>[ Already in List ]</big></span>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<span class="edit">
|
||||||
|
<a class="bracketed" href="/${type}/edit/${item.libraryEntry.id}/${item.libraryEntry.status}">Edit</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="row"><span class="edit"> </span></div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the search results for a media item
|
||||||
|
*
|
||||||
|
* @param {'anime'|'manga'} type
|
||||||
|
* @param {Object} data
|
||||||
|
* @param {boolean} isCollection
|
||||||
|
* @returns {String}
|
||||||
|
*/
|
||||||
|
export function renderSearchResults (type, data, isCollection = false) {
|
||||||
|
return data.map(item => {
|
||||||
|
const titles = item.titles.join('<br />');
|
||||||
|
let disabled = item.libraryEntry !== null ? 'disabled' : '';
|
||||||
|
const editLink = renderEditLink(type, item, isCollection);
|
||||||
|
|
||||||
|
if (isCollection) {
|
||||||
|
disabled = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return `
|
||||||
|
<article class="media search ${disabled}">
|
||||||
|
<div class="name">
|
||||||
|
<input type="radio" class="mal-check" id="anilist_${item.slug}" name="anilist_id" value="${item.anilist_id}" ${disabled} />
|
||||||
|
<input type="radio" class="mal-check" id="mal_${item.slug}" name="mal_id" value="${item.mal_id}" ${disabled} />
|
||||||
|
<input type="radio" class="big-check" id="${item.slug}" name="id" value="${item.id}" ${disabled} />
|
||||||
|
<label for="${item.slug}">
|
||||||
|
<img src="${item.coverImage}" alt="" width="220" />
|
||||||
|
<span class="name">
|
||||||
|
${item.canonicalTitle}<br />
|
||||||
|
<small>${titles}</small>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="table">
|
||||||
|
${editLink}
|
||||||
|
<div class="row">
|
||||||
|
<span class="edit">
|
||||||
|
<a class="bracketed" href="/${type}/details/${item.slug}">Info Page</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
`;
|
||||||
|
}).join('');
|
||||||
|
}
|
21
frontEndSrc/package.json
Normal file
21
frontEndSrc/package.json
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"license": "MIT",
|
||||||
|
"scripts": {
|
||||||
|
"build": "npm run build:css && npm run build:js",
|
||||||
|
"build:css": "node ./css.js",
|
||||||
|
"build:js": "spack",
|
||||||
|
"watch:css": "watch 'npm run build:css' --filter=./cssfilter.js",
|
||||||
|
"watch:js": "watch 'npm run build:js' ./js",
|
||||||
|
"watch": "concurrently \"npm:watch:css\" \"npm:watch:js\" --kill-others"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@swc/cli": "^0.3.9",
|
||||||
|
"@swc/core": "^1.4.1",
|
||||||
|
"concurrently": "^8.2.2",
|
||||||
|
"cssnano": "^6.0.3",
|
||||||
|
"postcss": "^8.4.35",
|
||||||
|
"postcss-import": "^16.0.1",
|
||||||
|
"postcss-preset-env": "^9.3.0",
|
||||||
|
"watch": "^1.0.2"
|
||||||
|
}
|
||||||
|
}
|
35
frontEndSrc/spack.config.js
Normal file
35
frontEndSrc/spack.config.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
const { config } = require("@swc/core/spack");
|
||||||
|
|
||||||
|
module.exports = config({
|
||||||
|
entry: {
|
||||||
|
'scripts.min': __dirname + '/js/index.js',
|
||||||
|
'tables.min': __dirname + '/js/base/sort-tables.js',
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
path: '../public/js',
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
jsc: {
|
||||||
|
parser: {
|
||||||
|
syntax: "ecmascript",
|
||||||
|
jsx: false,
|
||||||
|
},
|
||||||
|
target: 'es2018',
|
||||||
|
loose: false,
|
||||||
|
// keepClassNames: true,
|
||||||
|
// preserveAllComments: true,
|
||||||
|
minify: {
|
||||||
|
compress: {
|
||||||
|
unused: true,
|
||||||
|
},
|
||||||
|
mangle: true,
|
||||||
|
format: {
|
||||||
|
comments: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
minify: true,
|
||||||
|
sourceMaps: true,
|
||||||
|
isModule: true,
|
||||||
|
}
|
||||||
|
});
|
2245
frontEndSrc/yarn.lock
Normal file
2245
frontEndSrc/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
131
index.php
131
index.php
@ -1,94 +1,77 @@
|
|||||||
<?php
|
<?php declare(strict_types=1);
|
||||||
/**
|
/**
|
||||||
* Hummingbird Anime Client
|
* Hummingbird Anime List Client
|
||||||
*
|
*
|
||||||
* An API client for Hummingbird to manage anime and manga watch lists
|
* An API client for Kitsu to manage anime and manga watch lists
|
||||||
*
|
*
|
||||||
* @package HummingbirdAnimeClient
|
* PHP version 8.1
|
||||||
* @author Timothy J. Warren
|
*
|
||||||
* @copyright Copyright (c) 2015 - 2016
|
* @copyright 2015 - 2023 Timothy J. Warren <tim@timshome.page>
|
||||||
* @link https://github.com/timw4mail/HummingBirdAnimeClient
|
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||||
* @license MIT
|
* @version 5.2
|
||||||
|
* @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
|
||||||
*/
|
*/
|
||||||
use Whoops\Handler\PrettyPageHandler;
|
|
||||||
use Whoops\Handler\JsonResponseHandler;
|
|
||||||
|
|
||||||
// Work around the silly timezone error
|
namespace Aviat\AnimeClient;
|
||||||
$timezone = ini_get('date.timezone');
|
|
||||||
if ($timezone === '' || $timezone === FALSE)
|
|
||||||
{
|
|
||||||
ini_set('date.timezone', 'GMT');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
use Aviat\AnimeClient\Types\Config as ConfigType;
|
||||||
* Joins paths together. Variadic to take an
|
use Tracy\Debugger;
|
||||||
* arbitrary number of arguments
|
use function Aviat\Ion\_dir;
|
||||||
*
|
|
||||||
* @return string
|
setlocale(LC_CTYPE, 'en_US');
|
||||||
*/
|
|
||||||
function _dir()
|
// Load composer autoloader
|
||||||
{
|
require_once __DIR__ . '/vendor/autoload.php';
|
||||||
return implode(DIRECTORY_SEPARATOR, func_get_args());
|
|
||||||
}
|
Debugger::$strictMode = E_ALL;
|
||||||
|
Debugger::$showBar = FALSE;
|
||||||
|
Debugger::enable(Debugger::DEVELOPMENT, __DIR__ . '/app/logs');
|
||||||
|
|
||||||
// Define base directories
|
// Define base directories
|
||||||
$APP_DIR = _dir(__DIR__, 'app');
|
$APP_DIR = _dir(__DIR__, 'app');
|
||||||
$SRC_DIR = _dir(__DIR__, 'src');
|
$APPCONF_DIR = _dir($APP_DIR, 'appConf');
|
||||||
$CONF_DIR = _dir($APP_DIR, 'config');
|
$CONF_DIR = _dir($APP_DIR, 'config');
|
||||||
|
|
||||||
/**
|
|
||||||
* Set up autoloaders
|
|
||||||
*
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
spl_autoload_register(function($class) use ($SRC_DIR) {
|
|
||||||
$class_parts = explode('\\', $class);
|
|
||||||
$ns_path = $SRC_DIR . '/' . implode('/', $class_parts) . ".php";
|
|
||||||
|
|
||||||
if (file_exists($ns_path))
|
|
||||||
{
|
|
||||||
require_once($ns_path);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
require _dir(__DIR__, '/vendor/autoload.php');
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
// Setup error handling
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
$whoops = new \Whoops\Run();
|
|
||||||
|
|
||||||
// Set up default handler for general errors
|
|
||||||
$defaultHandler = new PrettyPageHandler();
|
|
||||||
$whoops->pushHandler($defaultHandler);
|
|
||||||
|
|
||||||
// Set up json handler for ajax errors
|
|
||||||
$jsonHandler = new JsonResponseHandler();
|
|
||||||
$whoops->pushHandler($jsonHandler);
|
|
||||||
|
|
||||||
// Register as the error handler
|
|
||||||
$whoops->register();
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Dependency Injection setup
|
// Dependency Injection setup
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
require _dir($CONF_DIR, 'base_config.php'); // $base_config
|
$baseConfig = require _dir($APPCONF_DIR, 'base_config.php');
|
||||||
require _dir($CONF_DIR, 'config.php'); // $config
|
$di = require "{$APP_DIR}/bootstrap.php";
|
||||||
$config_array = array_merge($base_config, $config);
|
|
||||||
$di = require _dir($APP_DIR, 'bootstrap.php');
|
$config = loadConfig($CONF_DIR);
|
||||||
|
|
||||||
|
$overrideFile = "{$CONF_DIR}/admin-override.toml";
|
||||||
|
$overrideConfig = file_exists($overrideFile)
|
||||||
|
? loadTomlFile($overrideFile)
|
||||||
|
: [];
|
||||||
|
|
||||||
|
$configArray = array_replace_recursive($baseConfig, $config, $overrideConfig);
|
||||||
|
|
||||||
|
$checkedConfig = ConfigType::check($configArray);
|
||||||
|
|
||||||
|
// Set the timezone for date display
|
||||||
|
// First look in app config, then PHP config, and at last
|
||||||
|
// resort, just set to UTC.
|
||||||
|
$timezone = ini_get('date.timezone');
|
||||||
|
if (is_array($checkedConfig) && array_key_exists('timezone', $checkedConfig) && ! empty($checkedConfig['timezone']))
|
||||||
|
{
|
||||||
|
date_default_timezone_set($checkedConfig['timezone']);
|
||||||
|
}
|
||||||
|
elseif (is_string($timezone) && $timezone !== '')
|
||||||
|
{
|
||||||
|
date_default_timezone_set($timezone);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
date_default_timezone_set('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
$container = $di($checkedConfig);
|
||||||
|
|
||||||
// Unset 'constants'
|
// Unset 'constants'
|
||||||
unset($APP_DIR);
|
unset($APP_DIR, $CONF_DIR, $APPCONF_DIR);
|
||||||
unset($SRC_DIR);
|
|
||||||
unset($CONF_DIR);
|
|
||||||
|
|
||||||
$container = $di($config_array);
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Dispatch to the current route
|
// Dispatch to the current route
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
$container->get('dispatcher')->__invoke();
|
$container->get('dispatcher')();
|
||||||
|
|
||||||
// End of index.php
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user