Compare commits
1178 Commits
Author | SHA1 | Date |
---|---|---|
Timothy Warren | 7afdab80ac | |
Timothy Warren | 144718866d | |
Timothy Warren | 1dd9fdd0c2 | |
Timothy Warren | 3794ed20a8 | |
Timothy Warren | ff8c837fd9 | |
Timothy Warren | fe1caffc0f | |
Timothy Warren | 8e7b2a04fd | |
Timothy Warren | 0e684736bd | |
Timothy Warren | 1ad4427584 | |
Timothy Warren | b0a16e7730 | |
Timothy Warren | 7559f79ef6 | |
Timothy Warren | c8b642be1c | |
Timothy Warren | 91c435cdac | |
Timothy Warren | 57249882ab | |
Timothy Warren | c28dfc09b3 | |
Timothy Warren | cd5841c4c8 | |
Timothy Warren | c83b30f43a | |
Kevin Colwell | 011160a3c2 | |
Timothy Warren | 067d888f4c | |
Kevin Colwell | 1b5a5080f2 | |
Timothy Warren | 1a97277ec4 | |
Timothy Warren | e426fdd4a3 | |
Timothy Warren | 5758de7667 | |
Timothy Warren | d21d9a86d4 | |
Timothy Warren | a435162a2b | |
Timothy Warren | 4fef2b2942 | |
Timothy Warren | 77c6419c80 | |
Timothy Warren | 97fe3b4b40 | |
Timothy Warren | 5396091b83 | |
Timothy Warren | 655ad119ce | |
Timothy Warren | 351446a2ee | |
Timothy Warren | f2b7f61030 | |
Timothy Warren | b678a3401e | |
Timothy Warren | 465cd99165 | |
Timothy Warren | 2e67b49447 | |
Timothy Warren | 45449b6907 | |
Timothy Warren | 05d4fb1ad7 | |
Timothy Warren | 81d81f4393 | |
Timothy Warren | 60ddcaa08e | |
Timothy Warren | 0e780f26b9 | |
Timothy Warren | bedd504b64 | |
Timothy Warren | 2f657dc20b | |
Timothy Warren | 4f8eefe71e | |
Timothy Warren | dd63767ddf | |
Timothy Warren | e1ff1c6e21 | |
Timothy Warren | f673a84cf6 | |
Timothy Warren | 4a2273c93c | |
Timothy Warren | 3cb90acb13 | |
Timothy Warren | ab38e03af9 | |
Timothy Warren | 2139f031f4 | |
Timothy Warren | ec280cd76d | |
Timothy Warren | 5e7f57eb0a | |
Timothy Warren | 26cbe4b592 | |
Timothy Warren | e8aa2bd42b | |
Timothy Warren | 501062ac37 | |
Timothy Warren | 4dc0bb29d2 | |
Timothy Warren | b1c4c8cb5e | |
Timothy Warren | e4fe5bbfec | |
Timothy Warren | 4b35d25849 | |
Timothy Warren | 3b57d08c0a | |
Timothy Warren | 29e70a8e3f | |
Timothy Warren | a0d30c002a | |
Timothy Warren | 327065498b | |
Timothy Warren | 047ee4cb37 | |
Timothy Warren | 9b945ca0a5 | |
Timothy Warren | e70b0fdb40 | |
Timothy Warren | c8a38d5785 | |
Timothy Warren | 10eb7794e9 | |
Timothy Warren | 0dee5f52fc | |
Timothy Warren | 7195258d7e | |
Timothy Warren | 9750d63e55 | |
Timothy Warren | e8a14fedf2 | |
Timothy Warren | eb897adbd1 | |
Timothy Warren | 5fb1b87e67 | |
Timothy Warren | a9b24f0cf7 | |
Timothy Warren | f40ee254c9 | |
Timothy Warren | 2b047634a0 | |
Timothy Warren | 7baa5bfe91 | |
Timothy Warren | 2d6058a6b3 | |
Timothy Warren | d34c79b4cd | |
Timothy Warren | ed558a6484 | |
Timothy Warren | 854987bd44 | |
Timothy Warren | 2977f7385e | |
Timothy Warren | 760cc71768 | |
Kevin Colwell | 573eac78f1 | |
Timothy Warren | 02fa04d19d | |
Timothy Warren | 6e1d190230 | |
Timothy Warren | 80b2495c11 | |
Kevin Colwell | 3d9d8202d9 | |
Kevin Colwell | 188baa5cc8 | |
Kevin Colwell | 3dbaf7ef32 | |
Timothy Warren | 545984bb18 | |
Timothy Warren | f9a5716002 | |
Timothy Warren | 818fcf114d | |
Timothy Warren | 3ca606d6f5 | |
Timothy Warren | 288e64f357 | |
Timothy Warren | 6ed1b81451 | |
Timothy Warren | 823ca8a805 | |
Timothy Warren | 7301c4852d | |
Timothy Warren | 602f0fc9c5 | |
Timothy Warren | 694e7cc01c | |
Timothy Warren | 7efa34efee | |
Timothy Warren | 49675ffbee | |
Timothy Warren | b82b7d74fc | |
Timothy Warren | 5102c7c459 | |
Timothy Warren | 1d022bc8ae | |
Timothy Warren | 489ec27602 | |
Timothy Warren | 6e4e065b75 | |
Timothy Warren | 69db87e305 | |
Timothy Warren | 2dacca5d06 | |
Timothy Warren | 1abac0ac0e | |
Timothy Warren | ba6ed8967c | |
Timothy Warren | 3d80f755a1 | |
Timothy Warren | 7a541b609f | |
Timothy Warren | 0f4383563f | |
Timothy Warren | 7b33d40de4 | |
Timothy Warren | 4c85c22c30 | |
Timothy Warren | 7839cf1515 | |
Timothy Warren | d2a9aaee54 | |
Timothy Warren | ff85cb6153 | |
Timothy Warren | 4c396ba9c6 | |
Timothy Warren | d2c397f6b9 | |
Timothy Warren | e679322122 | |
Timothy Warren | e6ae6c9e9c | |
Timothy Warren | d387b793ea | |
Timothy Warren | 50bb525f60 | |
Timothy Warren | 8de60b332d | |
Timothy Warren | 51eb460ce9 | |
Timothy Warren | 78a37c736f | |
Timothy Warren | 380c455332 | |
Timothy Warren | 6c35ade209 | |
Timothy Warren | 633f30d365 | |
Timothy Warren | 8c1d882404 | |
Timothy Warren | 6af73cea55 | |
Timothy Warren | d3732d1a54 | |
Timothy Warren | 3aecaf9161 | |
Timothy Warren | b12e94cee4 | |
Timothy Warren | 3d5d2c05ce | |
Timothy Warren | f01cc77f92 | |
Timothy Warren | c81271864d | |
Timothy Warren | 10ea494594 | |
Timothy Warren | 836b1d17e6 | |
Timothy Warren | 5004a9f332 | |
Timothy Warren | 40f134d7bc | |
Timothy Warren | 391708a49c | |
Timothy Warren | 566c9fbd1e | |
Timothy Warren | 56bda5ed71 | |
Timothy Warren | d2fbd3b56a | |
Timothy Warren | 9f680a75e3 | |
Timothy Warren | 8fadf9d589 | |
Timothy Warren | b64d8c6c5b | |
Timothy Warren | b393c695a5 | |
Timothy Warren | 1ab4dedddb | |
Timothy Warren | 73ee1a41e1 | |
Timothy Warren | 44a7d36174 | |
Timothy Warren | 77f314ee55 | |
Timothy Warren | 8d742e62ed | |
Timothy Warren | e1fd2bed59 | |
Timothy Warren | 61d44146cd | |
Timothy Warren | b2761541ec | |
Timothy Warren | 6412f1108b | |
Timothy Warren | c37f50d06e | |
Timothy Warren | c900e379c5 | |
Timothy Warren | 326436c0b5 | |
Timothy Warren | 66df53bf43 | |
Timothy Warren | 2cd9f99011 | |
Timothy Warren | 35ec3c8bfa | |
Timothy Warren | 52a0ff275d | |
Timothy Warren | a26c90ff86 | |
Timothy Warren | c173a8c196 | |
Timothy Warren | 73cf8ccd5a | |
Timothy Warren | 244372ff8c | |
Timothy Warren | 74ef13713d | |
Timothy Warren | 057d4bfae7 | |
Timothy Warren | 68bc4693cb | |
Timothy Warren | ce3260a2f3 | |
Timothy Warren | 4c5aa1e3de | |
Timothy Warren | 3c8b564ab9 | |
Timothy Warren | 7505907976 | |
Timothy Warren | 05455a518b | |
Timothy Warren | c39bc23061 | |
Timothy Warren | 9ba1bd4c90 | |
Timothy Warren | 2f789cc4cf | |
Timothy Warren | a18c0bd7b5 | |
Timothy Warren | 2b31cae57b | |
Timothy Warren | 48dcaa4bcb | |
Timothy Warren | 4342b2e795 | |
Timothy Warren | b2361c57c2 | |
Timothy Warren | e6fdea28d4 | |
Timothy Warren | 15dcdfe39c | |
Timothy Warren | 0250a50731 | |
Timothy Warren | f2aca2b76f | |
Timothy Warren | 97a7d501d0 | |
Timothy Warren | 8c3b583f92 | |
Timothy Warren | d6e174a014 | |
Timothy Warren | df7a9e311b | |
Timothy Warren | ebf22643ef | |
Timothy Warren | 3039f412aa | |
Timothy Warren | 37ab6034ba | |
Timothy Warren | d5931ff53b | |
Kevin Colwell | fe1250732c | |
Timothy Warren | 144e3f5229 | |
Timothy Warren | 5f494aa9bd | |
Timothy Warren | 7e0cbe8b83 | |
Timothy Warren | 31ed9d11ab | |
Timothy Warren | 6e3a70f9f6 | |
Timothy Warren | 3c47570cce | |
Timothy Warren | b4d9e9f21f | |
Timothy Warren | 1ea5750a76 | |
Timothy Warren | 36b396be71 | |
Timothy Warren | bf4f86a010 | |
Timothy Warren | 45b0209d8a | |
Timothy Warren | f37ec8022e | |
Timothy Warren | 415778295f | |
Timothy Warren | ad0dcb5750 | |
Timothy Warren | e0ad68b9d2 | |
Timothy Warren | 608251452f | |
Timothy Warren | 8c5547d69d | |
Timothy Warren | 6c29af4533 | |
Timothy Warren | 898dfebbde | |
Timothy Warren | 2d5ae3b1c6 | |
Timothy Warren | 8256815032 | |
Timothy Warren | fe6f737815 | |
Timothy Warren | 87d15024bb | |
Timothy Warren | 470d25f269 | |
Timothy Warren | 70a33e36c0 | |
Timothy Warren | 94d227b08e | |
Timothy Warren | ecb913322f | |
Timothy Warren | b001af868f | |
Timothy Warren | 1fbf0283ba | |
Timothy Warren | 5bcc046a12 | |
Timothy Warren | 9009da4b86 | |
Timothy Warren | 47a4be2cf9 | |
Timothy Warren | 52aabc2b12 | |
Timothy Warren | 7b1217bafe | |
Timothy Warren | a79ab842ee | |
Timothy Warren | 810731dfbd | |
Timothy Warren | c224c8d977 | |
Timothy Warren | ce3e3427dc | |
Timothy Warren | 7211aa0de7 | |
Timothy Warren | 02bd0288f2 | |
Timothy Warren | a15496e4a5 | |
Timothy Warren | a14ac3a122 | |
Timothy Warren | 1a3f1e9654 | |
Timothy Warren | 0c936b3fa7 | |
Timothy Warren | ccb9c9d331 | |
Timothy Warren | 738e39ba92 | |
Timothy Warren | 18e8d47167 | |
Timothy Warren | c429ce64d3 | |
Timothy Warren | 9003c15929 | |
Timothy Warren | eb56ab4c4f | |
Timothy Warren | 29a79577d9 | |
Timothy Warren | e890f978db | |
Timothy Warren | e944ddc75c | |
Timothy Warren | 778cda6efc | |
Timothy Warren | e912c83079 | |
Timothy Warren | 78b9146249 | |
Timothy Warren | e40a1d028f | |
Timothy Warren | edb022be13 | |
Timothy Warren | b75a99a145 | |
Timothy Warren | 7aeb74874b | |
Timothy Warren | 9749c59549 | |
Timothy Warren | 5da0ba87a7 | |
Timothy Warren | c749c7c923 | |
Timothy Warren | 9b4c9ad76f | |
Timothy Warren | 681a70fd92 | |
Timothy Warren | 67d3b7c1dc | |
Timothy Warren | 79aee53524 | |
Timothy Warren | 56f7d5142d | |
Timothy Warren | 5f7f4b6bdd | |
Timothy Warren | 0c3ff2ef11 | |
Timothy Warren | 5997ce8a0f | |
Timothy Warren | 687831efd5 | |
Timothy Warren | 5a65c7b645 | |
Timothy Warren | 9dc6643b78 | |
Timothy Warren | c7beb76404 | |
Timothy Warren | c132766486 | |
Timothy Warren | 9a112dc413 | |
Timothy Warren | 1c3216e26a | |
Timothy Warren | 78b195f966 | |
Timothy Warren | a35bce8a4b | |
Timothy Warren | 93faf7d88c | |
Timothy Warren | a0e7ebd2a0 | |
Timothy Warren | 2b54ab5497 | |
Timothy Warren | 7bfdd74f22 | |
Timothy Warren | 4582e2e917 | |
Timothy Warren | b0c75d989f | |
Timothy Warren | a3bae9255b | |
Timothy Warren | 3ab34a64d0 | |
Timothy Warren | 8110f10c3d | |
Timothy Warren | 7dae2dd6eb | |
Timothy Warren | 7c0ea492e1 | |
Timothy Warren | 9135598649 | |
Timothy Warren | 0b0e06af00 | |
Timothy Warren | 1ae99d2189 | |
Timothy Warren | 7275d81468 | |
Timothy Warren | dbfdd1c239 | |
Timothy Warren | 9eec7123a3 | |
Timothy Warren | 710d18a43b | |
Timothy Warren | 6d66ad1ea4 | |
Timothy Warren | 8d87d2fb2b | |
Timothy Warren | 61fcffdcbe | |
Timothy Warren | 057216a21c | |
Timothy Warren | abb17844fd | |
Timothy Warren | 891d8af469 | |
Timothy Warren | c701999af1 | |
Timothy Warren | af0b392e78 | |
Timothy Warren | 2cc85049f3 | |
Timothy Warren | 21a98dc48e | |
Timothy Warren | 3ecccb6ad8 | |
Timothy Warren | e724f885c8 | |
Timothy Warren | 43f07dac6c | |
Timothy Warren | 7bcff79d6e | |
Timothy Warren | f9f868be9d | |
Timothy Warren | 4a70422b23 | |
Timothy Warren | d8167ed075 | |
Timothy Warren | b6c0db7636 | |
Timothy Warren | ffd7fb8745 | |
Timothy Warren | 75bd011a2c | |
Timothy Warren | 03638991a3 | |
Timothy Warren | a7e6b3f198 | |
Timothy Warren | d5e3dcaff8 | |
Timothy Warren | 9108fe066a | |
Timothy Warren | f810e2573e | |
Timothy Warren | a371a334d0 | |
Timothy Warren | ed72eaef84 | |
Timothy Warren | bcbd76d4a9 | |
Timothy Warren | 754cf80c0b | |
Timothy Warren | ce0935333b | |
Timothy Warren | 050ff98d2c | |
Timothy Warren | 44d2c0e29d | |
Timothy Warren | c14bf3a8af | |
Timothy Warren | 42ffef32fe | |
Timothy Warren | 1cc5703cd7 | |
Timothy Warren | 62be0beae6 | |
Timothy Warren | e2e23a290f | |
Timothy Warren | 541b59bb28 | |
Timothy Warren | d81fba030c | |
Timothy Warren | 209655adc3 | |
Timothy Warren | 8be0fceb69 | |
Timothy Warren | 8094ff5927 | |
Timothy Warren | b614505499 | |
Timothy Warren | e17846f4a4 | |
Timothy Warren | 59f2d21a7f | |
Timothy Warren | 0a83184db6 | |
Timothy Warren | e3e32b4408 | |
Timothy Warren | c424e3a65a | |
Timothy Warren | 2325c8f4ec | |
Timothy Warren | 5a3d9547ae | |
Timothy Warren | bc529e57e8 | |
Timothy Warren | f71a1ee1ae | |
Timothy Warren | 797e66e520 | |
Timothy Warren | 308a564a2d | |
Timothy Warren | 174877ec81 | |
Timothy Warren | 83bb85615a | |
Timothy Warren | 570c18a069 | |
Timothy Warren | 462e93292b | |
Timothy Warren | 82cd204469 | |
Timothy Warren | 6d55d4136e | |
Timothy Warren | 546789ce40 | |
Timothy Warren | 97be2e40f8 | |
Timothy Warren | dd708bb1fa | |
Timothy Warren | 93a6dbe7d6 | |
Timothy Warren | fae3314b56 | |
Timothy Warren | dbb61372c6 | |
Timothy Warren | 0b4c2c81c3 | |
Timothy Warren | 0aee62c174 | |
Timothy Warren | 7fd881c8e9 | |
Timothy Warren | 9158d01fdf | |
Timothy Warren | ae8df3e6d6 | |
Timothy Warren | f6e00d4336 | |
Timothy Warren | 34b454c175 | |
Timothy Warren | 5ce34200c9 | |
Timothy Warren | 1a6a30ef5d | |
Timothy Warren | 8faf33c438 | |
Timothy Warren | 6e16632988 | |
Timothy Warren | 14db3f1ec9 | |
Timothy Warren | a4fe28f7b5 | |
Timothy Warren | 7c796b3d7b | |
Timothy Warren | 06529d7c92 | |
Timothy Warren | 42948017a4 | |
Timothy Warren | 055ec80236 | |
Timothy Warren | 986ff6de0b | |
Timothy Warren | e6a216704c | |
Timothy Warren | beaeb13353 | |
Timothy Warren | 0ea35cb421 | |
Timothy Warren | 0feb44a836 | |
Timothy Warren | 9b33a45189 | |
Timothy Warren | e0fa618b4e | |
Timothy Warren | 7672976f47 | |
Timothy Warren | 8036104731 | |
Timothy Warren | 76cb8ca00b | |
Timothy Warren | cc3b999bc5 | |
Timothy Warren | fb327f0c58 | |
Timothy Warren | ee914d048b | |
Timothy Warren | f57466d42c | |
Timothy Warren | 3ce928a67d | |
Timothy Warren | 622b435337 | |
Timothy Warren | 6acee9ca7a | |
Timothy Warren | 16ecfe3eb5 | |
Timothy Warren | c0fa4cfed2 | |
Timothy Warren | 00ef5c3706 | |
Timothy Warren | 618328a4c1 | |
Timothy Warren | e5ef054f5b | |
Timothy Warren | d9e81a7cf1 | |
Timothy Warren | b334a60486 | |
Timothy Warren | 17b01f6d48 | |
Timothy Warren | ef7c1da5f2 | |
Timothy Warren | 6718fc78e9 | |
Timothy Warren | 5216b60789 | |
Timothy Warren | 37c3d6ecf0 | |
Timothy Warren | eb12f57e7d | |
Timothy Warren | a03b9be329 | |
Timothy Warren | 07de5ff79b | |
Timothy Warren | 8aa94f7c14 | |
Timothy Warren | 8842df76be | |
Timothy Warren | 6047444077 | |
Timothy Warren | 95e8b7920a | |
Timothy Warren | 66b13ef7ba | |
Timothy Warren | b32968588a | |
Timothy Warren | fafd75b791 | |
Timothy Warren | 70eb4f11b3 | |
Timothy Warren | ae70eab9ea | |
Timothy Warren | 926179a72d | |
Timothy Warren | 143229bea4 | |
Timothy Warren | 3978c4d5cb | |
Timothy Warren | 705d48abad | |
Timothy Warren | 6044a676a6 | |
Timothy Warren | 245e1b4344 | |
Timothy Warren | ec9edff2f3 | |
Timothy Warren | 3fa5b7ab88 | |
Timothy Warren | ceb8159dae | |
Timothy Warren | 8b677ab7a7 | |
Timothy Warren | b4b5c63d65 | |
Timothy Warren | 347674f9e5 | |
Timothy Warren | d435a17ec8 | |
Timothy Warren | fde9b05bdf | |
Timothy Warren | 7d9d2e8990 | |
Timothy Warren | 995690a341 | |
Timothy Warren | 808b704383 | |
Timothy Warren | cd835055ec | |
Timothy Warren | 117427ced0 | |
Timothy Warren | aebb349543 | |
Timothy Warren | 37f7616ef4 | |
Timothy Warren | 28d4ce9e86 | |
Timothy Warren | 0e893f06ba | |
Timothy Warren | 58bb1ab0ba | |
Timothy Warren | 46041ccfc6 | |
Timothy Warren | 625edf5d0c | |
Timothy Warren | 4edfd9f62c | |
Timothy Warren | e5baccbf56 | |
Timothy Warren | bfd5ff32c1 | |
Timothy Warren | e1fd639ba9 | |
Timothy Warren | 4e88c27cfb | |
Timothy Warren | 0153271a62 | |
Timothy Warren | 7a529619ed | |
Timothy Warren | a6020df023 | |
Timothy Warren | cda711607a | |
Timothy Warren | c5bb555695 | |
Timothy Warren | ea5eb21941 | |
Timothy Warren | 038e61bf37 | |
Timothy Warren | bce1afa546 | |
Timothy Warren | 4502c2f183 | |
Timothy Warren | b070282899 | |
Timothy Warren | aa6965e98f | |
Timothy Warren | b2c8bff967 | |
Timothy Warren | ea2a368100 | |
Timothy Warren | 523fcbd0d9 | |
Timothy Warren | 003492a964 | |
Timothy Warren | e98699acbc | |
Timothy Warren | eb31096bff | |
Timothy Warren | d1a0147ae2 | |
Timothy Warren | 6c81ddbfd4 | |
Timothy Warren | 2fdca56501 | |
Timothy Warren | 47d6314178 | |
Timothy Warren | e73326fd6c | |
Timothy Warren | 251bbadd96 | |
Timothy Warren | 6ee198c742 | |
Timothy Warren | dfdc6b7356 | |
Timothy Warren | 94b15f455c | |
Timothy Warren | f6278a1304 | |
Timothy Warren | 04c5b135a7 | |
Timothy Warren | 8a8ea0b470 | |
Timothy Warren | 4e2437f2bc | |
Timothy Warren | 27c7f08d7f | |
Timothy Warren | b6f12ff2f6 | |
Timothy Warren | 317d8fd29b | |
Timothy Warren | b66a35843d | |
Timothy Warren | ac382a96a8 | |
Timothy Warren | 87d0960424 | |
Timothy Warren | be16ceecb2 | |
Timothy Warren | 105b0f52ca | |
Timothy Warren | 63a50f7ed8 | |
Timothy Warren | 4b9f97f49e | |
Timothy Warren | 8272cc7240 | |
Timothy Warren | 538201ef6f | |
Timothy Warren | 5e9780aad7 | |
Timothy Warren | f3265484da | |
Timothy Warren | 4c3f987b85 | |
Timothy Warren | 5e3fb46159 | |
Timothy Warren | 2c73e721d0 | |
Timothy Warren | 431f6e7d21 | |
Timothy Warren | c0e16c6d07 | |
Timothy Warren | 9c0b1e73ef | |
Timothy Warren | 05842baccb | |
Timothy Warren | 7f6b0178a8 | |
Timothy Warren | 8b938add27 | |
Timothy Warren | 8672112bdc | |
Timothy Warren | 033ea46754 | |
Timothy Warren | 24f80bc18c | |
Timothy Warren | 4b75987f21 | |
Timothy Warren | ca487901c2 | |
Timothy Warren | e195987436 | |
Timothy Warren | 350dae0109 | |
Timothy Warren | d514c319c0 | |
Timothy Warren | 4ace9b6806 | |
Timothy Warren | 64478d4507 | |
Timothy Warren | f314538972 | |
Timothy Warren | 155650961b | |
Timothy Warren | b3366131b8 | |
Timothy Warren | 040b7f3fdc | |
Timothy Warren | ef1e435c6b | |
Timothy Warren | a9c3a44f37 | |
Timothy Warren | 3842df13db | |
Timothy Warren | be2f7708ad | |
Timothy Warren | 29a4114e8c | |
Timothy Warren | 1690d8c1e0 | |
Timothy Warren | e84cbe1bb2 | |
Timothy Warren | d0af6fd9e8 | |
Timothy Warren | bcc7815ae6 | |
Timothy Warren | 5d87bd044c | |
Timothy Warren | e2b4fae83b | |
Timothy Warren | 019fff5d62 | |
Timothy Warren | cf1c495f90 | |
Timothy Warren | cee5a28816 | |
Timothy Warren | 83a6629f03 | |
Timothy Warren | 5810405f12 | |
Timothy Warren | 7b765c6d0b | |
Timothy Warren | a9acf23a15 | |
Timothy Warren | b14a827715 | |
Timothy Warren | 1cd8386559 | |
Timothy Warren | 1fa11cd50d | |
Timothy Warren | 8a7223593d | |
Timothy Warren | ccc9ad0c14 | |
Timothy Warren | 509db151e7 | |
Timothy Warren | 0bbc4fe4fb | |
Timothy Warren | 86c311dddf | |
Timothy Warren | eaf3554611 | |
Timothy Warren | 99aaf0303b | |
Timothy Warren | 6d1df75889 | |
Timothy Warren | 9c8e396c9a | |
Timothy Warren | c9ec11c2df | |
Timothy Warren | 587d5fa14e | |
Timothy Warren | 229703a6a6 | |
Timothy Warren | 5b8f0c4a9e | |
Timothy Warren | 41d71dac0c | |
Timothy Warren | 6dfa66dbde | |
Timothy Warren | 324abc0f61 | |
Timothy Warren | 3c0fd79195 | |
Timothy Warren | 0d30f57e83 | |
Timothy Warren | 247a9d0e5b | |
Timothy Warren | d6800dbc46 | |
Timothy Warren | ae283cd898 | |
Timothy Warren | a8f898822a | |
Timothy Warren | da936b325e | |
Timothy Warren | 8b3ce0f079 | |
Timothy Warren | c9ed90acb4 | |
Timothy Warren | 17a9539e94 | |
Timothy Warren | 6f717e6ab7 | |
Timothy Warren | 0f31a5e10a | |
Timothy Warren | e0376c78d1 | |
Timothy Warren | a6c253b969 | |
Timothy Warren | 5a607db93e | |
Timothy Warren | e7dc1e8e53 | |
Timothy Warren | 38a5b78295 | |
Timothy Warren | 034213fccc | |
Timothy Warren | 020f561773 | |
Timothy Warren | 2fc26bf4c6 | |
Timothy Warren | 09530efefd | |
Timothy Warren | 0624c3be67 | |
Timothy Warren | a71fb185bd | |
Timothy Warren | 98ae142757 | |
Timothy Warren | cd150d7fef | |
Timothy Warren | 3bca049cd8 | |
Timothy Warren | 95b06a7e7e | |
Timothy Warren | 81f02ad622 | |
Timothy Warren | 64d3c241f6 | |
Timothy Warren | 4a91a5cb5d | |
Timothy Warren | 226f0ced83 | |
Timothy Warren | bc2122dd98 | |
Timothy Warren | 2a2ff87b3b | |
Timothy Warren | 7c0d02758b | |
Timothy Warren | e6761807b8 | |
Timothy Warren | bfb5d6323d | |
Timothy Warren | b3a3e19146 | |
Timothy Warren | b5f8413ceb | |
Timothy Warren | b2554378e7 | |
Timothy Warren | 1fa5cce5ae | |
Timothy Warren | ea31131e0f | |
Timothy Warren | 675f8ed9b2 | |
Timothy Warren | 0dcf25e16c | |
Timothy Warren | dd46e292c4 | |
Timothy Warren | e9888c762e | |
Timothy Warren | 49295148d1 | |
Timothy Warren | d1421d2eb2 | |
Timothy Warren | 69c2482fc1 | |
Timothy Warren | 571bbf7595 | |
Timothy Warren | e01a96b8fb | |
Timothy Warren | 2c7d866677 | |
Timothy Warren | be2b387391 | |
Timothy Warren | 06c55a2094 | |
Timothy Warren | 9a7084078f | |
Timothy Warren | f71e9dbe4d | |
Timothy Warren | cecca5f9f0 | |
Timothy Warren | 79be0ebb34 | |
Timothy Warren | c7a77a2eb5 | |
Timothy Warren | c0c72e40e4 | |
Timothy Warren | 01bf7144c2 | |
Timothy Warren | f5f59b8382 | |
Timothy Warren | 75a5727a2e | |
Timothy Warren | 4f0c3f7984 | |
Timothy Warren | 83e0310ca7 | |
Timothy Warren | 9cac51bd82 | |
Timothy Warren | a434c032a2 | |
Timothy Warren | ad0154f431 | |
Timothy Warren | 080b112608 | |
Timothy Warren | 92ad051f6a | |
Timothy Warren | da8f4acb29 | |
Timothy Warren | 43ab033aec | |
Timothy Warren | 83f9d14630 | |
Timothy Warren | 634335187e | |
Timothy Warren | f06ba6e3cd | |
Timothy Warren | 675b1ef2f1 | |
Timothy Warren | a46a85bf71 | |
Timothy Warren | 3a739e3920 | |
Timothy Warren | 231babe218 | |
Timothy Warren | 4532bd1865 | |
Timothy Warren | 7b9adbf52e | |
Timothy Warren | f607403111 | |
Timothy Warren | 34996f009b | |
Timothy Warren | 39031ccf3e | |
Timothy Warren | 4451544389 | |
Timothy Warren | 5aa750cde7 | |
Timothy Warren | 11068029e8 | |
Timothy Warren | e75c52d9dc | |
Timothy Warren | 09f9aa2069 | |
Timothy Warren | bbe2fd0a5d | |
Timothy Warren | 2ad0a24483 | |
Timothy Warren | 0c52831ec6 | |
Timothy Warren | 55fca3f92f | |
Timothy Warren | 034174418b | |
Timothy Warren | e8f53542e8 | |
Timothy Warren | ae52f8737a | |
Timothy Warren | 888dbe7187 | |
Timothy Warren | 37f1391a07 | |
Timothy Warren | 8b8ece3dad | |
Timothy Warren | 015b3d0f34 | |
Timothy Warren | ee57f72ca6 | |
Timothy Warren | 9497f5c3df | |
Timothy Warren | 63fe8684c4 | |
Timothy Warren | a2fbe471e3 | |
Timothy Warren | c6f2981d63 | |
Timothy Warren | 410ff4fcc1 | |
Timothy Warren | 7e8e9a3141 | |
Timothy Warren | 98c24d4704 | |
Timothy Warren | c9533db6a1 | |
Timothy Warren | a4555ca908 | |
Timothy Warren | 45e14a7503 | |
Timothy Warren | 5eaa33ba82 | |
Timothy Warren | 38eee85752 | |
Timothy Warren | 2b9adb0395 | |
Timothy Warren | 5fb042a773 | |
Timothy Warren | 04ec5b2fd6 | |
Timothy Warren | b0ee397994 | |
Timothy Warren | 200ff1339c | |
Timothy Warren | a8e2049d08 | |
Timothy Warren | 07152cc3be | |
Timothy Warren | 89816dc062 | |
Timothy Warren | 3c4e34f1ed | |
Timothy Warren | 372b616101 | |
Timothy Warren | d93d22f7df | |
Timothy Warren | 6493f33faf | |
Timothy Warren | e66a9f885a | |
Timothy Warren | fa74e59854 | |
Timothy Warren | a15a97370b | |
Timothy Warren | 3fd7c84774 | |
Timothy Warren | 428a77b93d | |
Timothy Warren | 8e8ee81397 | |
Timothy Warren | cf12dfee76 | |
Timothy Warren | d2fc955260 | |
Timothy Warren | 4d991629b1 | |
Timothy Warren | 6111dfe6f9 | |
Timothy Warren | 921d594931 | |
Timothy Warren | ac13d57634 | |
Timothy Warren | 42d36ff4bb | |
Timothy Warren | 28da32f2ac | |
Timothy Warren | 08aff2ffe8 | |
Timothy Warren | 8b43dee20f | |
Timothy Warren | 0d2cde37a0 | |
Timothy Warren | 5cca3cf335 | |
Timothy Warren | 679560e185 | |
Timothy Warren | d157f097d1 | |
Timothy Warren | ec6f9b1189 | |
Timothy Warren | 7825e46321 | |
Timothy Warren | 6436ca2e9c | |
Timothy Warren | 81a1a927b1 | |
Timothy Warren | b210954874 | |
Timothy Warren | 472be3c4ed | |
Timothy Warren | edc6e6227e | |
Timothy Warren | 32b3617fed | |
Timothy Warren | 45bf1e1136 | |
Timothy Warren | fb3805b789 | |
Timothy Warren | b861db5d1f | |
Timothy Warren | e49ed606f5 | |
Timothy Warren | 8172d1a593 | |
Timothy Warren | a413d7d9ca | |
Timothy Warren | 8ceec846a5 | |
Timothy Warren | d2d48905d7 | |
Timothy Warren | 7d7ae73f5e | |
Timothy Warren | 82c8d36144 | |
Timothy Warren | c50b1da53b | |
Timothy Warren | ddd30fc713 | |
Timothy Warren | 1e28a1795d | |
Timothy Warren | b2300f4cfb | |
Timothy Warren | 92fe6b7146 | |
Timothy Warren | fa4ee22100 | |
Timothy Warren | 5ae6864a3f | |
Timothy Warren | fb567e85e9 | |
Timothy Warren | 2d33663318 | |
Timothy Warren | 528d3584b8 | |
Timothy Warren | 69b4d0c88b | |
Timothy Warren | e79021da29 | |
Timothy Warren | 79980683d1 | |
Timothy Warren | ec7e0cc93b | |
Timothy Warren | 15b26d8e39 | |
Timothy Warren | 377e102650 | |
Timothy Warren | 4a9e0f0293 | |
Timothy Warren | 0b18c06058 | |
Timothy Warren | 8d289f9eb5 | |
Timothy Warren | 394c1241fb | |
Timothy Warren | 0c4c0a436c | |
Timothy Warren | b8b5beeae1 | |
Timothy Warren | be0baac962 | |
Timothy Warren | cf0db8b9fa | |
Timothy Warren | dd90dd541c | |
Timothy Warren | cdd3878e55 | |
Timothy Warren | 51c26e6b30 | |
Timothy Warren | 0c2cc0e32b | |
Timothy Warren | 828b7a2154 | |
Timothy Warren | be4f54b99d | |
Timothy Warren | 25e57eb493 | |
Timothy Warren | 707a36fe53 | |
Timothy Warren | cd58e083bf | |
Timothy Warren | a259ea7b18 | |
Timothy Warren | 74602f60fa | |
Timothy Warren | 53b8ce44bf | |
Timothy Warren | e99205be54 | |
Timothy Warren | b52d301b2a | |
Timothy Warren | 52bd2773c0 | |
Timothy Warren | c9768855a5 | |
Timothy Warren | 1a45e57b7c | |
Timothy Warren | cd242596bc | |
Timothy Warren | bc6854a8e5 | |
Timothy Warren | 0d4b26e493 | |
Timothy Warren | 0e6a1b6591 | |
Timothy Warren | 84f0a27d86 | |
Timothy Warren | 8645926006 | |
Timothy Warren | 960537f8e0 | |
Timothy Warren | 5d9b7e9e63 | |
Timothy Warren | 6bf107e1ad | |
Timothy Warren | fa7651faf9 | |
Timothy Warren | 8b2cd7dd1e | |
Timothy Warren | 5d2dac5b99 | |
Timothy Warren | df7102d13d | |
Timothy Warren | 82b596ec3c | |
Timothy Warren | 497216cffa | |
Timothy Warren | 969ff75078 | |
Timothy Warren | f932a80e58 | |
Timothy Warren | ae6b1cb209 | |
Timothy Warren | d0a236e7ee | |
Timothy Warren | ed01b28c0d | |
Timothy Warren | 52e1d1822a | |
Timothy Warren | 5e9b3db1f2 | |
Timothy Warren | e0e1b59777 | |
Timothy Warren | 443ffaa132 | |
Timothy Warren | 4b0226838c | |
Timothy Warren | 79ce8c7790 | |
Timothy Warren | 0fa4a3c963 | |
Timothy Warren | 14967e9ad0 | |
Timothy Warren | b4573296d8 | |
Timothy Warren | 0127d65dfc | |
Timothy Warren | 14e8fa9f03 | |
Timothy Warren | adc331d60d | |
Timothy Warren | 65227e82ac | |
Timothy Warren | 56ae9ed80e | |
Timothy Warren | f3d9af311e | |
Timothy Warren | 136b7dab66 | |
Timothy Warren | cd7a836db0 | |
Timothy Warren | e87e5cb47c | |
Timothy Warren | 7122085590 | |
Timothy Warren | 9f0484a93b | |
Timothy Warren | 93038e61e5 | |
Timothy Warren | 30b43fd27c | |
Timothy Warren | 6efe1ffbc8 | |
Timothy Warren | 8898655a49 | |
Timothy Warren | 07b1422fad | |
Timothy Warren | 0232d18f1f | |
Timothy Warren | 0cef44c986 | |
Timothy Warren | 6ed755e252 | |
Timothy Warren | 85cd77267b | |
Timothy Warren | 488a01f8a5 | |
Timothy Warren | ee2760b2b5 | |
Timothy Warren | 84cb1cb520 | |
Timothy Warren | db07976403 | |
Timothy Warren | c96a3bbd50 | |
Timothy Warren | 8cbfaf3646 | |
Timothy Warren | 29c04e62be | |
Timothy Warren | 1224882092 | |
Timothy Warren | df8b64cff9 | |
Timothy Warren | ae42fafe84 | |
Timothy Warren | da9ebe8867 | |
Timothy Warren | 0a72e60f68 | |
Timothy Warren | 36874cbe55 | |
Timothy Warren | d94a280437 | |
Timothy Warren | 470c39cf79 | |
Timothy Warren | 941a15c4f4 | |
Timothy Warren | 3caad13577 | |
Timothy Warren | acbae86a6b | |
Timothy Warren | 34d0aaa8e5 | |
Timothy Warren | 7941303987 | |
Timothy Warren | da8b34a867 | |
Timothy Warren | 9bf362e08e | |
Timothy Warren | 6ee319b78c | |
Timothy Warren | 90f1b39db5 | |
Timothy Warren | 85a6fafd4f | |
Timothy Warren | 2bdf4be682 | |
Timothy Warren | 455adf4b11 | |
Timothy Warren | 9e49566641 | |
Timothy Warren | d1f0ab0c73 | |
Timothy Warren | 79153ae433 | |
Timothy Warren | 9c44b5189f | |
Timothy Warren | 312125f182 | |
Timothy Warren | 652eac5be0 | |
Timothy Warren | dba0d47789 | |
Timothy Warren | 4a3be8b4bf | |
Timothy Warren | 7f5966a147 | |
Timothy Warren | 8f8f528823 | |
Timothy Warren | 1ec7322b18 | |
Timothy Warren | 8f8c413927 | |
Timothy Warren | 50c543218b | |
Timothy Warren | 18af49f1f4 | |
Timothy Warren | 906a1f1efa | |
Timothy Warren | deecb5a912 | |
Timothy Warren | 02838c5024 | |
Timothy Warren | bf7f6973a4 | |
Timothy Warren | c0b54e11e1 | |
Timothy Warren | 0fe01e14e1 | |
Timothy Warren | 2f71a97327 | |
Timothy Warren | e73ea09ffd | |
Timothy Warren | bc0c3774eb | |
Timothy Warren | b10487ac13 | |
Timothy Warren | 64ed6cc0ef | |
Timothy Warren | b6c2aee17a | |
Timothy Warren | 5eea985828 | |
Timothy Warren | 1835e34690 | |
Timothy Warren | 2d0fa51c40 | |
Timothy Warren | 108c649a91 | |
Timothy Warren | c26a4ca8e8 | |
Timothy Warren | 9d9c1e2cce | |
Timothy Warren | 2cacff6b9b | |
Timothy Warren | 47a43517cb | |
Timothy Warren | 1b5590bda7 | |
Timothy Warren | 91a6d76c4b | |
Timothy Warren | 03964c446a | |
Timothy Warren | 74897faa78 | |
Timothy Warren | 2b2ec71edd | |
Timothy Warren | a80a860f8d | |
Timothy Warren | 3d54a0d62c | |
Timothy Warren | aabdf4de30 | |
Timothy Warren | 20c3d69717 | |
Timothy Warren | f57c24abe4 | |
Timothy Warren | 0ae52a13f3 | |
Timothy Warren | ece4f343e2 | |
Timothy Warren | b2fb562de6 | |
Timothy Warren | 19c2d0fddc | |
Timothy Warren | b2544fab16 | |
Timothy Warren | 96cbf78e28 | |
Timothy Warren | e30a0b867d | |
Timothy Warren | 11f9e41254 | |
Timothy Warren | a45eba3c56 | |
Timothy Warren | 9bc22baa80 | |
Timothy Warren | 7bad51d867 | |
Timothy Warren | f441b7680a | |
Timothy Warren | 10368fabe4 | |
Timothy Warren | a42a9bc785 | |
Timothy Warren | 42f152b366 | |
Timothy Warren | 0ec8d6d6b3 | |
Timothy Warren | 1eecff0178 | |
Timothy Warren | 3e72a66297 | |
Timothy Warren | 79c0c6cf90 | |
Timothy Warren | c1724397d3 | |
Timothy Warren | 9e30783ecb | |
Timothy Warren | 496eb68078 | |
Timothy Warren | 2fe45a6b57 | |
Timothy Warren | aca66ae86d | |
Timothy Warren | f619c78232 | |
Timothy Warren | d91432d960 | |
Timothy Warren | 62c06b7731 | |
Timothy Warren | e060e1b107 | |
Timothy Warren | 08c40de381 | |
Timothy Warren | c76bb4d32a | |
Timothy Warren | 93e58874de | |
Timothy Warren | 712956d564 | |
Timothy Warren | 39118d63e5 | |
Timothy Warren | 9f048b739e | |
Timothy Warren | 6e818b7f45 | |
Timothy Warren | e085754955 | |
Timothy Warren | 98bf1e455f | |
Timothy Warren | 9d84398ee7 | |
Timothy Warren | 09338e9132 | |
Timothy Warren | 239b0c055c | |
Timothy Warren | 444e18c1d9 | |
Timothy Warren | f1893d9708 | |
Timothy Warren | 3030b6b908 | |
Timothy Warren | cdf9ad0105 | |
Timothy Warren | 05c6f22f67 | |
Timothy Warren | e74fe9d2f5 | |
Timothy Warren | c9f5964d5e | |
Timothy Warren | e097b02457 | |
Timothy Warren | f2fcc8ee93 | |
Timothy Warren | 513ba4c70f | |
Timothy Warren | 66e51af8d7 | |
Timothy Warren | ba9c41f495 | |
Timothy Warren | 07ebb3e988 | |
Timothy Warren | c915ea871d | |
Timothy Warren | 656688a5f3 | |
Timothy Warren | 60c9c86580 | |
Timothy Warren | b0c49ca19c | |
Timothy Warren | dd2d25d54c | |
Timothy Warren | 230c80459e | |
Timothy Warren | f7915ba6f2 | |
Timothy Warren | ac971c5248 | |
Timothy Warren | eaa0e517c1 | |
Timothy Warren | 4e4ac58263 | |
Timothy Warren | 88e06a0052 | |
Timothy Warren | e0e63cf094 | |
Timothy Warren | 410d45029f | |
Timothy Warren | 8f3ce089a4 | |
Timothy Warren | 7e05a4dcc2 | |
Timothy Warren | 7b9b62b200 | |
Timothy Warren | 6bdd33da2a | |
Timothy Warren | 3894ba8312 | |
Timothy Warren | 473a046ed8 | |
Timothy Warren | 47e67d5d1a | |
Timothy Warren | e62558d607 | |
Timothy Warren | 6b9770698b | |
Timothy Warren | 893584696b | |
Timothy Warren | a108adfa23 | |
Timothy Warren | f00acbe2d6 | |
Timothy Warren | 563adace2f | |
Timothy Warren | cc7046f0ec | |
Timothy Warren | b415938583 | |
Timothy Warren | 2a41106638 | |
Timothy Warren | cb046dbde2 | |
Timothy Warren | 96d3dfdbb4 | |
Timothy Warren | fa22f7b493 | |
Timothy Warren | e2c6c14af8 | |
Timothy Warren | ca62a411e1 | |
Timothy Warren | 10bb167ff4 | |
Timothy Warren | ead6f8b487 | |
Timothy Warren | 672552a1e8 | |
Timothy Warren | f940606f0c | |
Timothy Warren | b271fd48a6 | |
Timothy Warren | 8e00f3a59d | |
Timothy Warren | ebc8006e04 | |
Timothy Warren | c84e76a869 | |
Timothy Warren | a178ac86c6 | |
Timothy Warren | 1e18344990 | |
Timothy Warren | 6b31e759c9 | |
Timothy Warren | 1842e1a30b | |
Timothy Warren | dd20c774ed | |
Timothy Warren | b2990e8457 | |
Timothy Warren | 4e48c8c4fa | |
Timothy Warren | 3605ec6d0b | |
Timothy Warren | 2151d64f25 | |
Timothy Warren | 89080171c9 | |
Timothy Warren | 733d3f3871 | |
Timothy Warren | a14d268652 | |
Timothy Warren | b5949466e4 | |
Timothy Warren | 1f9afd07f3 | |
Timothy Warren | 717c296e52 | |
Timothy Warren | b528b8dc17 | |
Timothy Warren | 98f709c36e | |
Timothy Warren | 330ac65e18 | |
Timothy Warren | e26e9f10b6 | |
Timothy Warren | 7d6c6fe2a0 | |
Timothy Warren | 5d0b879623 | |
Timothy Warren | 1a182a62a7 | |
Timothy Warren | 429c9861e3 | |
Timothy Warren | 99a5907e1b | |
Timothy Warren | 1e42d431c8 | |
Timothy Warren | 05a3d0e729 | |
Timothy Warren | 4b4a259f8a | |
Timothy Warren | 454324aba6 | |
Timothy Warren | 470795f21e | |
Timothy Warren | 95abe28322 | |
Timothy Warren | e35373a114 | |
Timothy Warren | ea1d342db5 | |
Timothy Warren | 83a200871d | |
Timothy Warren | 0f6c998109 | |
Timothy Warren | 440a999c2e | |
Timothy Warren | 7711765563 | |
Timothy Warren | 7efa180bbf | |
Timothy Warren | 84e4e6ce1b | |
Timothy Warren | 972d96c0e0 | |
Timothy Warren | 3bcb0442ca | |
Timothy Warren | 2afbe84afd | |
Timothy Warren | 06f07978dc | |
Timothy Warren | 2dde6f8a7d | |
Timothy Warren | c8789dc267 | |
Timothy Warren | ed16fd8d45 | |
Timothy Warren | dd74e85626 | |
Timothy Warren | ab19e9db08 | |
Timothy Warren | 98f3026a74 | |
Timothy Warren | 1ba302999d | |
Timothy Warren | 197a1f8326 | |
Timothy Warren | 9cc491a05e | |
Timothy Warren | bcd28acfc5 | |
Timothy Warren | d99f1e7595 | |
Timothy Warren | 1fb95adfbf | |
Timothy Warren | e8cc479a1e | |
Timothy Warren | fb29c90691 | |
Timothy Warren | 3027c8a4a8 | |
Timothy Warren | 0b6269edd6 | |
Timothy Warren | 12d05dd71a | |
Timothy Warren | ecf3bce14b | |
Timothy Warren | 5563902b69 | |
Timothy Warren | 874ad521a7 | |
Timothy Warren | a9f5e48bb2 | |
Timothy Warren | cbc7555cf2 | |
Timothy Warren | e08161aec2 | |
Timothy Warren | 7754377ecb | |
Timothy Warren | 0b2a0d4ea9 | |
Timothy Warren | 94f54366a7 | |
Timothy Warren | f5dc15659f | |
Timothy Warren | 1a21a23e73 | |
Timothy Warren | 4533ea0b26 | |
Timothy Warren | 540a82fe22 | |
Timothy Warren | 467763f8a6 | |
Timothy Warren | 99f2adf491 | |
Timothy Warren | 3fa8e7d8e8 | |
Timothy Warren | 9080f45601 | |
Timothy Warren | 08348fd349 | |
Timothy Warren | e109c6a06c | |
Timothy Warren | 457c2680b6 | |
Timothy Warren | a85f0e28c6 | |
Timothy Warren | b611d02a2c | |
Timothy Warren | e51159e7f3 | |
Timothy Warren | 2391ca14ca | |
Timothy Warren | 212c779552 | |
Timothy Warren | 0c2243a4f3 | |
Timothy Warren | 3981b8471a | |
Timothy Warren | 276a14a80c | |
Timothy Warren | b4949eaea2 | |
Timothy Warren | 993b625042 | |
Timothy Warren | 557b27ef77 | |
Timothy Warren | 45b04105f1 | |
Timothy Warren | ae4530b5d2 | |
Timothy Warren | 00fd23895d | |
Timothy Warren | aa67b941d4 | |
Timothy Warren | b94bf01dee | |
Timothy Warren | ba94f439bb | |
Timothy Warren | e3af767246 | |
Timothy Warren | b0f5bdf668 | |
Scrutinizer Auto-Fixer | 2b5d650ef8 | |
Timothy Warren | e9e16dd2b1 | |
Timothy Warren | 6b9be7e4d0 | |
Scrutinizer Auto-Fixer | 8b528d8659 | |
Timothy Warren | 9c81836648 | |
Timothy Warren | c04c85b999 | |
Timothy Warren | 6953cd08e6 | |
Timothy Warren | c82576e4dd | |
Scrutinizer Auto-Fixer | b70ff95f03 | |
Timothy Warren | 30b6afb601 | |
Timothy Warren | 6ca086d85b | |
Timothy Warren | bfb8500386 | |
Timothy Warren | 70bfe2bab7 | |
Timothy Warren | 7d1c8c383c | |
Timothy Warren | 3b876f2c42 | |
Timothy Warren | 56f9fa28aa | |
Timothy Warren | 2577bad0af | |
Timothy Warren | ed1e888c58 | |
Timothy Warren | 254afc990e | |
Timothy Warren | 8d1986d13b | |
Timothy Warren | 5d2cad4690 | |
Timothy Warren | 49b3f507a6 | |
Timothy Warren | 912f1b2ff2 | |
Timothy Warren | f75016ca69 | |
Scrutinizer Auto-Fixer | 4e1bcd962a | |
Timothy Warren | 69f35d222c | |
Timothy Warren | 898905c21c | |
Timothy Warren | f26adc1c28 | |
Timothy Warren | dfe91d0f1c | |
Timothy Warren | 05912cd540 | |
Timothy Warren | ab955f5154 | |
Timothy Warren | 8343fa9182 | |
Timothy Warren | 406c7c13cb | |
Scrutinizer Auto-Fixer | cd8185960b | |
Timothy Warren | 87f5324761 | |
Timothy Warren | a18f1926d9 | |
Timothy Warren | 17a335275c | |
Timothy Warren | ff13f2ce05 | |
Timothy Warren | 4937d4c099 | |
Timothy Warren | 1de8b46657 | |
Timothy Warren | 6aa51e5915 | |
Timothy Warren | 8936944743 | |
Timothy Warren | 76672d5a60 | |
Scrutinizer Auto-Fixer | 025bc4ef64 | |
Timothy Warren | a741526da1 | |
Timothy Warren | 799a3652d4 | |
Timothy Warren | 2f8886c28f | |
Timothy Warren | c3643565e3 | |
Timothy Warren | 28e03f6fb2 | |
Timothy Warren | 4ef2d6df57 | |
Timothy Warren | 779f4a00eb | |
Timothy Warren | d281c26a1c | |
Timothy Warren | 77399e3fa4 | |
Timothy Warren | 5f5bf66c75 | |
Timothy Warren | 385037b669 | |
Timothy Warren | fee09c50ae | |
Timothy Warren | 285a132d35 | |
Timothy Warren | 0b79ac3596 | |
Timothy Warren | de444857dd | |
Timothy Warren | d231b39fdc | |
Timothy Warren | d5c76a0f01 | |
Timothy Warren | 485ba46838 | |
Timothy Warren | 7f2000f180 | |
Timothy Warren | 34acf00b5e | |
Timothy Warren | bc6aed51c8 | |
Timothy Warren | 011bdda777 | |
Scrutinizer Auto-Fixer | 80f83e5f53 | |
Timothy Warren | b687dcf2a8 | |
Timothy Warren | bb3e5a643f | |
Scrutinizer Auto-Fixer | 4dddd3238c | |
Timothy Warren | a8e3c594e3 | |
Timothy Warren | 728850da08 | |
Timothy Warren | 443802332b | |
Scrutinizer Auto-Fixer | bf6550d0d9 | |
Timothy Warren | 37ef0f5621 | |
Timothy Warren | 38cfaa023d | |
Timothy Warren | f3772528c5 | |
Timothy Warren | dbbea163d1 | |
Timothy Warren | 7269c5c393 | |
Scrutinizer Auto-Fixer | 7e695edd29 | |
Timothy Warren | 6b322d18da | |
Timothy Warren | be96e2c6af | |
Timothy Warren | 5de88986ed | |
Timothy Warren | b68cbe5a26 | |
Timothy Warren | 2462e09205 | |
Timothy Warren | acb0a1e8a2 | |
Timothy Warren | a73e150ee3 | |
Timothy Warren | b8f753a424 | |
Timothy Warren | 23c16fc9c3 | |
Timothy Warren | 082b5296b9 | |
Timothy Warren | f87dd2636d | |
Timothy Warren | 60109b3531 | |
Timothy Warren | e2731db7ea | |
Timothy Warren | 156461a0b9 | |
Timothy Warren | 8a68559f0e | |
Timothy Warren | 97a5abe665 | |
Timothy Warren | d93f5a82a0 | |
Timothy Warren | 9d139a7d1c | |
Timothy Warren | e53f9abf3f | |
Timothy Warren | c5f3093a78 | |
Timothy Warren | 4622b6efa9 | |
Timothy Warren | 3e39aa0277 | |
Timothy Warren | 3e53ec1526 | |
Timothy Warren | 2abb5f3c3a |
|
@ -1,3 +1,118 @@
|
|||
|
||||
# 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
|
||||
.phing_targets
|
||||
.sonar/
|
||||
|
@ -8,15 +123,35 @@ vendor
|
|||
**/logs/**
|
||||
**/coverage/**
|
||||
**/docs/**
|
||||
public/images/*
|
||||
**/node_modules/**
|
||||
composer.lock
|
||||
*.sqlite
|
||||
*.db
|
||||
*.sqlite3
|
||||
docs/*
|
||||
apidocs/**
|
||||
tests/test_data/sessions/*
|
||||
cache.properties
|
||||
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
|
|
@ -3,5 +3,4 @@ RewriteEngine On
|
|||
RewriteBase /
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond $1 !^(public)
|
||||
RewriteRule ^(.*)$ index.php/$1 [L]
|
|
@ -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,
|
||||
],
|
||||
]);
|
23
.travis.yml
23
.travis.yml
|
@ -1,26 +1,17 @@
|
|||
language: php
|
||||
|
||||
install:
|
||||
- composer install
|
||||
- composer install --ignore-platform-reqs
|
||||
|
||||
php:
|
||||
- 5.5
|
||||
- 5.6
|
||||
- 7
|
||||
- hhvm
|
||||
- 8.0
|
||||
- 8.1
|
||||
- nightly
|
||||
|
||||
script:
|
||||
- mkdir -p build/logs
|
||||
- phpunit -c build
|
||||
- php vendor/bin/phpunit -c build
|
||||
|
||||
after_script:
|
||||
- CODECLIMATE_REPO_TOKEN=2cbddcebcb9256b3402867282e119dbe61de0b31039325356af3c7d72ed6d058 vendor/bin/test-reporter
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- php: nightly
|
||||
|
||||
addons:
|
||||
code_climate:
|
||||
repo_token: 2cbddcebcb9256b3402867282e119dbe61de0b31039325356af3c7d72ed6d058
|
||||
#matrix:
|
||||
# allow_failures:
|
||||
# - php: nightly
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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.
|
76
README.md
76
README.md
|
@ -1,10 +1,9 @@
|
|||
# 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.org/timw4mail/HummingBirdAnimeClient.svg?branch=master)](https://travis-ci.org/timw4mail/HummingBirdAnimeClient)
|
||||
[![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)
|
||||
[![Build Status](https://travis-ci.com/timw4mail/HummingBirdAnimeClient.svg?branch=master)](https://travis-ci.com/github/timw4mail/HummingBirdAnimeClient)
|
||||
[![Build Status](https://jenkins.timshome.page/buildStatus/icon?job=timw4mail/HummingBirdAnimeClient/develop)](https://jenkins.timshome.page/job/timw4mail/job/HummingBirdAnimeClient/job/develop/)
|
||||
|
||||
[[Hosted Example](https://list.timshomepage.net)]
|
||||
|
||||
|
@ -32,55 +31,36 @@ A self-hosted client that allows custom formatting of data from the hummingbird
|
|||
|
||||
### Requirements
|
||||
|
||||
* PHP 5.5+
|
||||
* PDO SQLite (For collection tab)
|
||||
* GD
|
||||
* PHP 8.2
|
||||
* ext-dom (For editing the DOM)
|
||||
* 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
|
||||
|
||||
1. Install via composer: `composer create-project timw4mail/hummingbird-anime-client`
|
||||
2. Configure settings in `app/config/config.php` to your liking
|
||||
3. Create the following directories if they don't exist, and make sure they are world writable
|
||||
* app/cache
|
||||
* public/js/cache
|
||||
* public/images/manga
|
||||
1. Install via git, then install dependencies via composer: `composer install`
|
||||
2. Duplicate `app/config/config.toml.example` file as `app/config/config.toml`
|
||||
3. Configure settings in `app/config/config.toml` to your liking
|
||||
4. Create the following directories if they don't exist, and make sure they are world writable
|
||||
* app/config
|
||||
* app/logs
|
||||
* public/images/avatars
|
||||
* public/images/anime
|
||||
* 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.
|
||||
|
||||
### Server Setup
|
||||
|
||||
#### nginx
|
||||
Basic nginx setup
|
||||
|
||||
```nginx
|
||||
server {
|
||||
location / {
|
||||
try_files $uri $uri/ /index.php$uri?$args;
|
||||
}
|
||||
|
||||
location ~ ^(.+\.php)($|/) {
|
||||
fastcgi_split_path_info ^(.+\.php)(.*)$;
|
||||
fastcgi_index index.php;
|
||||
}
|
||||
|
||||
location ^~ /vendor {
|
||||
deny all;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Apache
|
||||
Make sure to have `mod_rewrite` and `AllowOverride All` enabled in order to take
|
||||
advantage of the included `.htaccess` file. If you don't wish to use an `.htaccess` file,
|
||||
include the contents of the `.htaccess` file in your Apache configuration.
|
||||
|
||||
#### Anime Collection Additional Installation
|
||||
* Run `php /vendor/bin/phinx migrate -e development` to create the database tables
|
||||
* For importing anime:
|
||||
1. Login
|
||||
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
|
||||
See the [wiki](https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient/wiki)
|
||||
for more in-depth information
|
||||
|
||||
|
|
|
@ -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',
|
||||
]);
|
|
@ -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'
|
|
@ -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,94 +1,199 @@
|
|||
<?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;
|
||||
|
||||
use Aura\Html\HelperLocatorFactory;
|
||||
use Aura\Web\WebFactory;
|
||||
use Aura\Router\RouterFactory;
|
||||
use Aura\Router\RouterContainer;
|
||||
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\BrowserConsoleHandler;
|
||||
use Monolog\Logger;
|
||||
use Psr\SimpleCache\CacheInterface;
|
||||
|
||||
use Aviat\Ion\Di\Container;
|
||||
use Aviat\AnimeClient\Auth\HummingbirdAuth;
|
||||
use Aviat\AnimeClient\Model;
|
||||
use function Aviat\Ion\_dir;
|
||||
|
||||
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
|
||||
// -----------------------------------------------------------------------------
|
||||
return function(array $config_array = []) {
|
||||
return static function (array $configArray = []): Container {
|
||||
$container = new Container();
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Logging
|
||||
// -------------------------------------------------------------------------
|
||||
$LOG_DIR = _dir(HB_APP_DIR, 'logs');
|
||||
|
||||
$app_logger = new Logger('animeclient');
|
||||
$app_logger->pushHandler(new RotatingFileHandler(__DIR__ . '/logs/app.log', Logger::NOTICE));
|
||||
$container->setLogger($app_logger, 'default');
|
||||
$appLogger = new Logger('animeclient');
|
||||
$appLogger->pushHandler(new RotatingFileHandler(_dir($LOG_DIR, 'app.log'), 2, Logger::WARNING));
|
||||
$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
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
// Create Config Object
|
||||
$config = new Config($config_array);
|
||||
$container->set('config', $config);
|
||||
$container->set('config', static fn () => new Config($configArray));
|
||||
|
||||
// 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
|
||||
$aura_router = (new RouterFactory())->newInstance();
|
||||
$container->set('aura-router', $aura_router);
|
||||
$container->set('aura-router', static fn () => new RouterContainer());
|
||||
|
||||
// Create Html helper Object
|
||||
$html_helper = (new HelperLocatorFactory)->newInstance();
|
||||
$html_helper->set('menu', function() use ($container) {
|
||||
$menu_helper = new Helper\Menu();
|
||||
$menu_helper->setContainer($container);
|
||||
return $menu_helper;
|
||||
// Create Html helpers
|
||||
$container->set('html-helper', static function (ContainerInterface $container) {
|
||||
$htmlHelper = (new HelperLocatorFactory())->newInstance();
|
||||
$helpers = [
|
||||
'menu' => Helper\Menu::class,
|
||||
'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
|
||||
$web_factory = new WebFactory([
|
||||
'_GET' => $_GET,
|
||||
'_POST' => $_POST,
|
||||
'_COOKIE' => $_COOKIE,
|
||||
'_SERVER' => $_SERVER,
|
||||
'_FILES' => $_FILES
|
||||
]);
|
||||
$container->set('request', $web_factory->newRequest());
|
||||
$container->set('response', $web_factory->newResponse());
|
||||
// Create Component helpers
|
||||
$container->set('component-helper', static function (ContainerInterface $container) {
|
||||
$helper = (new HelperLocatorFactory())->newInstance();
|
||||
$components = [
|
||||
'animeCover' => Component\AnimeCover::class,
|
||||
'mangaCover' => Component\MangaCover::class,
|
||||
'character' => Component\Character::class,
|
||||
'media' => Component\Media::class,
|
||||
'tabs' => Component\Tabs::class,
|
||||
'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
|
||||
$session = (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);
|
||||
$container->set('session', static fn () => (new SessionFactory())->newInstance($_COOKIE));
|
||||
|
||||
// Models
|
||||
$container->set('api-model', new Model\API($container));
|
||||
$container->set('anime-model', new Model\Anime($container));
|
||||
$container->set('manga-model', new Model\Manga($container));
|
||||
$container->set('anime-collection-model', new Model\AnimeCollection($container));
|
||||
$container->set('kitsu-model', static function (ContainerInterface $container): Kitsu\Model {
|
||||
$requestBuilder = new Kitsu\RequestBuilder($container);
|
||||
$requestBuilder->setLogger($container->getLogger('kitsu-request'));
|
||||
|
||||
$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
|
||||
// -------------------------------------------------------------------------
|
||||
$container->set('dispatcher', new Dispatcher($container));
|
||||
$container->setSimple('dispatcher', Dispatcher::class);
|
||||
|
||||
return $container;
|
||||
};
|
||||
|
||||
// End of bootstrap.php
|
||||
// End of bootstrap.php
|
||||
|
|
|
@ -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',
|
||||
];
|
|
@ -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'),
|
||||
];
|
|
@ -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',
|
||||
]
|
||||
];
|
|
@ -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,81 +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';
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| 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' => '',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| CSS Groups file
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The file where the css groups are configured
|
||||
|
|
||||
*/
|
||||
'css_groups_file' => realpath(__DIR__ . '/minify_css_groups.php'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| JS Folder
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The folder where javascript files exist, in relation to the document root
|
||||
|
|
||||
*/
|
||||
'js_root' => $config['asset_dir'] . '/js/',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| JS Groups file
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The file where the javascript groups are configured
|
||||
|
|
||||
*/
|
||||
'js_groups_file' => realpath(__DIR__ . '/minify_js_groups.php'),
|
||||
|
||||
];
|
||||
// End of minify_config.php
|
|
@ -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,49 +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 [
|
||||
'base' => [
|
||||
'base/classList.js',
|
||||
'base/AnimeClient.js',
|
||||
],
|
||||
'event' => [
|
||||
'base/events.js',
|
||||
],
|
||||
'table' => [
|
||||
'base/sort_tables.js',
|
||||
],
|
||||
'table_edit' => [
|
||||
'sort_tables.js',
|
||||
'anime_edit.js',
|
||||
'manga_edit.js',
|
||||
],
|
||||
'edit' => [
|
||||
'anime_edit.js',
|
||||
'manga_edit.js',
|
||||
],
|
||||
'anime_collection' => [
|
||||
'lib/mustache.js',
|
||||
'anime_collection.js',
|
||||
],
|
||||
'manga_collection' => [
|
||||
'lib/mustache.js',
|
||||
'manga_collection.js',
|
||||
],
|
||||
];
|
||||
|
||||
// End of js_groups.php
|
|
@ -1,172 +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' => [
|
||||
// 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_detail' => [
|
||||
'path' => '/anime/details/{id}',
|
||||
'action' => 'details',
|
||||
'tokens' => [
|
||||
'id' => '[a-z0-9\-]+'
|
||||
]
|
||||
],
|
||||
// ---------------------------------------------------------------------
|
||||
// Manga Routes
|
||||
// ---------------------------------------------------------------------
|
||||
'manga_search' => [
|
||||
'path' => '/manga/search',
|
||||
'action' => 'search',
|
||||
],
|
||||
'manga_add_form' => [
|
||||
'path' => '/manga/add',
|
||||
'action' => 'add_form',
|
||||
'verb' => 'get'
|
||||
],
|
||||
'manga_add' => [
|
||||
'path' => '/manga/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_form' => [
|
||||
'path' => '/{controller}/login',
|
||||
'action' => 'login',
|
||||
'verb' => 'get'
|
||||
],
|
||||
'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\- ]|%20)+',
|
||||
]
|
||||
],
|
||||
'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,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>
|
|
@ -0,0 +1,6 @@
|
|||
<article class="<?= $className ?>">
|
||||
<div class="name">
|
||||
<a href="<?= $link ?>"><?= $name ?></a>
|
||||
</div>
|
||||
<a href="<?= $link ?>"><?= $picture ?></a>
|
||||
</article>
|
|
@ -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>
|
|
@ -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>
|
|
@ -0,0 +1,5 @@
|
|||
<section class="<?= $className ?>">
|
||||
<?php foreach ($data as $tabName => $tabData): ?>
|
||||
<?= $callback($tabData, $tabName) ?>
|
||||
<?php endforeach ?>
|
||||
</section>
|
|
@ -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>
|
|
@ -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>
|
||||
<h1>404</h1>
|
||||
<h2>Page Not Found</h2>
|
||||
<h2><?= $message ?></h2>
|
||||
<pre>(╯°□°)╯︵ ┻━┻
|
||||
┬─┬ノ( º _ ºノ)</pre>
|
||||
</main>
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
<?php if ($auth->is_authenticated()): ?>
|
||||
<?php if ($_->isAuthenticated()): ?>
|
||||
<main>
|
||||
<h2>Add Anime 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>
|
||||
|
@ -9,11 +10,11 @@
|
|||
<div class="cssload-inner cssload-three"></div>
|
||||
</div>
|
||||
<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>
|
||||
<br />
|
||||
<table class="form">
|
||||
<table class="invisible form">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><label for="status">Watching Status</label></td>
|
||||
|
@ -28,6 +29,7 @@
|
|||
<tr>
|
||||
<td> </td>
|
||||
<td>
|
||||
<input type="hidden" name="type" value="anime" />
|
||||
<button type="submit">Save</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -35,5 +37,4 @@
|
|||
</table>
|
||||
</form>
|
||||
</main>
|
||||
<script src="<?= $urlGenerator->asset_url('js.php/g/anime_collection') ?>"></script>
|
||||
<?php endif ?>
|
|
@ -1,69 +1,30 @@
|
|||
<main>
|
||||
<?php if ($auth->is_authenticated()): ?>
|
||||
<a class="bracketed" href="<?= $urlGenerator->url('anime/add', 'anime') ?>">Add Item</a>
|
||||
<main class="media-list">
|
||||
<?php if ($_->isAuthenticated()): ?>
|
||||
<a class="bracketed" href="<?= $_->urlFromRoute('anime.add.get') ?>">Add Item</a>
|
||||
<?php endif ?>
|
||||
<?php if (empty($sections)): ?>
|
||||
<h3>There's nothing here!</h3>
|
||||
<?php else: ?>
|
||||
<br />
|
||||
<label>Filter: <input type='text' class='media-filter' /></label>
|
||||
<br />
|
||||
<?php foreach ($sections as $name => $items): ?>
|
||||
<?php if (empty($items)): ?>
|
||||
<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">
|
||||
<?php foreach($items as $item): ?>
|
||||
<?php if ($item['private'] && ! $auth->is_authenticated()) continue; ?>
|
||||
<article class="media" id="<?= $item['anime']['slug'] ?>">
|
||||
<?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 if ($item['private'] && ! $_->isAuthenticated()) continue; ?>
|
||||
<?= $_->component->animeCover($item) ?>
|
||||
<?php endforeach ?>
|
||||
</section>
|
||||
</section>
|
||||
<?php endif ?>
|
||||
<?php endforeach ?>
|
||||
<?php endif ?>
|
||||
</main>
|
||||
<?php if ($auth->is_authenticated()): ?>
|
||||
<script src="<?= $urlGenerator->asset_url('js.php/g/edit') ?>"></script>
|
||||
<?php endif ?>
|
||||
|
|
|
@ -1,11 +1,205 @@
|
|||
<main>
|
||||
<h2><a href="<?= $data['url'] ?>"><?= $data['title'] ?></a></h2>
|
||||
<?php if( ! empty($data['alternate_title'])): ?>
|
||||
<h3><?= $data['alternate_title'] ?></h3>
|
||||
<?php endif ?>
|
||||
|
||||
<img src="<?= $data['cover_image'] ?>" alt="<?= $data['title'] ?> cover image" />
|
||||
<?php
|
||||
|
||||
<p><?= nl2br($data['synopsis']) ?></p>
|
||||
<pre><?= print_r($data, TRUE) ?></pre>
|
||||
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>
|
||||
<h2>Edit Anime List Item</h2>
|
||||
<form action="<?= $action ?>" method="post">
|
||||
<table class="form">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<h3><?= $escape->html($item['anime']['title']) ?></h3>
|
||||
<?php if($item['anime']['alternate_title'] != ""): ?>
|
||||
<h4><?= $escape->html($item['anime']['alternate_title']) ?></h4>
|
||||
<?php endif ?>
|
||||
</th>
|
||||
<th>
|
||||
<article class="media">
|
||||
<?= $helper->img($item['anime']['image']); ?>
|
||||
</article>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><label for="private">Is Private?</label></td>
|
||||
<td>
|
||||
<input type="checkbox" name="private" id="private"
|
||||
<?php if($item['private']): ?>checked="checked"<?php endif ?>
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="watching_status">Watching Status</label></td>
|
||||
<td>
|
||||
<select name="watching_status" id="watching_status">
|
||||
<?php foreach($statuses as $status_key => $status_title): ?>
|
||||
<option <?php if($item['watching_status'] === $status_key): ?>selected="selected"<?php endif ?>
|
||||
value="<?= $status_key ?>"><?= $status_title ?></option>
|
||||
<?php endforeach ?>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="series_rating">Rating</label></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
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="episodes_watched">Episodes Watched</label></td>
|
||||
<td>
|
||||
<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): ?>
|
||||
/ <?= $item['episodes']['total'] ?>
|
||||
<table class="invisible form">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<h3><?= $_->escape->html($item['anime']['title']) ?></h3>
|
||||
<?php foreach($item['anime']['titles'] as $title): ?>
|
||||
<h4><?= $_->escape->html($title) ?></h4>
|
||||
<?php endforeach ?>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td rowspan="9">
|
||||
<?= $_->h->img($item['anime']['cover_image']) ?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="private">Is Private?</label></td>
|
||||
<td>
|
||||
<input type="checkbox" name="private" id="private"
|
||||
<?php if($item['private']): ?>checked="checked"<?php endif ?>
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="watching_status">Watching Status</label></td>
|
||||
<td>
|
||||
<select name="watching_status" id="watching_status">
|
||||
<?php foreach($statuses as $status_key => $status_title): ?>
|
||||
<option <?php if(strtolower($item['watching_status']) === $status_key): ?>selected="selected"<?php endif ?>
|
||||
value="<?= $status_key ?>"><?= $status_title ?></option>
|
||||
<?php endforeach ?>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="series_rating">Rating</label></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
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="episodes_watched">Episodes Watched</label></td>
|
||||
<td>
|
||||
<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): ?>
|
||||
/ <?= $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 ?>
|
||||
</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['anime']['slug'] ?>" name="id" />
|
||||
<input type="hidden" value="true" name="edit" />
|
||||
<button type="submit">Submit</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<input type="hidden" value="true" name="edit" />
|
||||
<button type="submit">Submit</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</form>
|
||||
<form class="js-delete" action="<?= $_->urlFromRoute('anime.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['id'] ?>" name="id" />
|
||||
<?php if (!empty($item['mal_id'])): ?>
|
||||
<input type="hidden" value="<?= $item['mal_id'] ?? '' ?>" name="mal_id" />
|
||||
<?php endif ?>
|
||||
<button type="submit" class="danger">Delete Entry</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</fieldset>
|
||||
</form>
|
||||
</main>
|
||||
<script src="<?= $urlGenerator->asset_url('js.php/g/edit') ?>"></script>
|
||||
<?php endif ?>
|
|
@ -1,77 +1,113 @@
|
|||
<main>
|
||||
<?php if ($auth->is_authenticated()): ?>
|
||||
<a class="bracketed" href="<?= $urlGenerator->url('anime/add', 'anime') ?>">Add Item</a>
|
||||
<?php use function Aviat\AnimeClient\colNotEmpty; ?>
|
||||
<main class="media-list">
|
||||
<?php if ($_->isAuthenticated()): ?>
|
||||
<a class="bracketed" href="<?= $_->urlFromRoute('anime.add.get') ?>">Add Item</a>
|
||||
<?php endif ?>
|
||||
<?php if (empty($sections)): ?>
|
||||
<h3>There's nothing here!</h3>
|
||||
<?php else: ?>
|
||||
<br />
|
||||
<label>Filter: <input type='text' class='media-filter' /></label>
|
||||
<br />
|
||||
<?php foreach ($sections as $name => $items): ?>
|
||||
<h2><?= $name ?></h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<?php if($auth->is_authenticated()): ?>
|
||||
<td class="no_border"> </td>
|
||||
<?php endif ?>
|
||||
<th>Title</th>
|
||||
<th>Airing Status</th>
|
||||
<th>Score</th>
|
||||
<th>Type</th>
|
||||
<th>Progress</th>
|
||||
<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="justify">
|
||||
<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 class="align_left" 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 if (empty($items)): ?>
|
||||
<h3>There's nothing here!</h3>
|
||||
<?php else: ?>
|
||||
<?php
|
||||
$hasNotes = colNotEmpty($items, 'notes');
|
||||
?>
|
||||
<table class='media-wrap'>
|
||||
<thead>
|
||||
<tr>
|
||||
<?php if($_->isAuthenticated()): ?>
|
||||
<td class="no-border"> </td>
|
||||
<?php endif ?>
|
||||
<?php $attr_list = []; ?>
|
||||
<?php foreach(['private','rewatching'] as $attr): ?>
|
||||
<?php if($item[$attr]): ?>
|
||||
<?php $attr_list[] = ucfirst($attr); ?>
|
||||
<th>Title</th>
|
||||
<th>Airing Status</th>
|
||||
<th class='numeric'>Score</th>
|
||||
<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 endforeach ?>
|
||||
<?= implode(', ', $attr_list); ?>
|
||||
</td>
|
||||
<td>
|
||||
<p><?= $escape->html($item['notes']) ?></p>
|
||||
</td>
|
||||
<td class="align_left">
|
||||
<?php sort($item['anime']['genres']) ?>
|
||||
<?= join(', ', $item['anime']['genres']) ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php foreach(['private','rewatching'] as $attr): ?>
|
||||
<?php if($item[$attr]): ?><li><?= ucfirst($attr); ?></li><?php endif ?>
|
||||
<?php endforeach ?>
|
||||
</ul>
|
||||
</td>
|
||||
<?php if ($hasNotes): ?><td><p><?= $_->escape->html($item['notes']) ?></p></td><?php endif ?>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php endif ?>
|
||||
<?php endforeach ?>
|
||||
<?php endif ?>
|
||||
</main>
|
||||
<?php $group = ($auth->is_authenticated()) ? 'table_edit' : 'table' ?>
|
||||
<script src="<?= $urlGenerator->asset_url("js.php/g/{$group}") ?>"></script>
|
||||
<script defer="defer" src="<?= $_->assetUrl('js/tables.min.js') ?>"></script>
|
|
@ -0,0 +1,3 @@
|
|||
<main>
|
||||
<h1><?= $title ?></h1>
|
||||
</main>
|
|
@ -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,28 +1,25 @@
|
|||
<?php if ($auth->is_authenticated()): ?>
|
||||
<?php if ($_->isAuthenticated()): ?>
|
||||
<main>
|
||||
<h2>Add Anime to your Collection</h2>
|
||||
<h2>Add <?= ucfirst($collection_type) ?> to your Collection</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 anime by name: <input type="search" id="search" name="search" /></label>
|
||||
<section id="series_list" class="media-wrap">
|
||||
<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>
|
||||
<br />
|
||||
<table class="form">
|
||||
<table class="invisible form">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><label for="media_id">Media</label></td>
|
||||
<td>
|
||||
<select name="media_id" id="media_id">
|
||||
<?php foreach($media_items as $id => $name): ?>
|
||||
<option value="<?= $id ?>"><?= $name ?></option>
|
||||
<?php endforeach ?>
|
||||
</select>
|
||||
<td class="align-right"><label for="media_id">Media</label></td>
|
||||
<td class='align-left'>
|
||||
<?php include 'media-select-list.php' ?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -39,5 +36,4 @@
|
|||
</table>
|
||||
</form>
|
||||
</main>
|
||||
<script src="<?= $urlGenerator->asset_url('js.php/g/anime_collection') ?>"></script>
|
||||
<?php endif ?>
|
|
@ -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 if ($auth->is_authenticated()): ?>
|
||||
<a class="bracketed" href="<?= $urlGenerator->url('collection/add', 'anime') ?>">Add Item</a>
|
||||
<?php use function Aviat\AnimeClient\renderTemplate; ?>
|
||||
<main class="media-list">
|
||||
<?php if ($_->isAuthenticated()): ?>
|
||||
<a class="bracketed" href="<?= $_->urlFromRoute($collection_type . '.collection.add.get') ?>">Add Item</a>
|
||||
<?php endif ?>
|
||||
<?php if (empty($sections)): ?>
|
||||
<h3>There's nothing here!</h3>
|
||||
<?php else: ?>
|
||||
<?php foreach ($sections as $name => $items): ?>
|
||||
<section class="status">
|
||||
<h2><?= $name ?></h2>
|
||||
<section class="media-wrap">
|
||||
<?php foreach($items as $item): ?>
|
||||
<article class="media" id="a-<?= $item['hummingbird_id'] ?>">
|
||||
<img src="<?= $urlGenerator->asset_url('images', 'anime', basename($item['cover_image'])) ?>" />
|
||||
<div class="name">
|
||||
<a href="https://hummingbird.me/anime/<?= $item['slug'] ?>">
|
||||
<?= $item['title'] ?>
|
||||
<?= ($item['alternate_title'] != "") ? "<br />({$item['alternate_title']})" : ""; ?>
|
||||
</a>
|
||||
</div>
|
||||
<div class="table">
|
||||
<?php if ($auth->is_authenticated()): ?>
|
||||
<div class="row">
|
||||
<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 ?>
|
||||
<br />
|
||||
<label>Filter: <input type='text' class='media-filter' /></label>
|
||||
<br />
|
||||
<?= $_->component->tabs('collection-tab', $sections, static function ($items) use ($_, $collection_type) {
|
||||
$rendered = [];
|
||||
foreach ($items as $item)
|
||||
{
|
||||
$rendered[] = renderTemplate(__DIR__ . '/cover-item.php', [
|
||||
'_' => $_,
|
||||
'collection_type' => $collection_type,
|
||||
'item' => $item,
|
||||
]);
|
||||
}
|
||||
|
||||
return implode('', array_map('mb_trim', $rendered));
|
||||
}, 'media-wrap', true) ?>
|
||||
<?php endif ?>
|
||||
</main>
|
||||
|
|
|
@ -1,37 +1,36 @@
|
|||
<?php if ($auth->is_authenticated()): ?>
|
||||
<?php use function Aviat\AnimeClient\renderTemplate ?>
|
||||
<?php if ($_->isAuthenticated()): ?>
|
||||
<main>
|
||||
<h2>Edit Anime Collection Item</h2>
|
||||
<form action="<?= $action_url ?>" method="post">
|
||||
<table class="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>
|
||||
<table class="invisible form">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><label for="media_id">Media</label></td>
|
||||
<td>
|
||||
<select name="media_id" id="media_id">
|
||||
<?php foreach($media_items as $id => $name): ?>
|
||||
<option <?= $item['media_id'] == $id ? 'selected="selected"' : '' ?> value="<?= $id ?>"><?= $name ?></option>
|
||||
<?php endforeach ?>
|
||||
</select>
|
||||
<td rowspan="6" class="align-center">
|
||||
<?= $_->h->picture("images/anime/{$item['hummingbird_id']}-original.webp", "jpg", [], ["width" => "390"]) ?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="align-right"><label for="title">Title</label></td>
|
||||
<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>
|
||||
</tr>
|
||||
<tr>
|
||||
<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>
|
||||
<td> </td>
|
||||
|
@ -45,12 +44,23 @@
|
|||
</tbody>
|
||||
</table>
|
||||
</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>
|
||||
<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 ?>
|
|
@ -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>
|
||||
<?php if ($auth->is_authenticated()): ?>
|
||||
<a class="bracketed" href="<?= $urlGenerator->full_url('collection/add', 'anime') ?>">Add Item</a>
|
||||
<?php endif ?>
|
||||
<?php if (empty($sections)): ?>
|
||||
<h3>There's nothing here!</h3>
|
||||
<?php else: ?>
|
||||
<?php foreach ($sections as $name => $items): ?>
|
||||
<h2><?= $name ?></h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<?php if($auth->is_authenticated()): ?>
|
||||
<th>Actions</th>
|
||||
<?php endif ?>
|
||||
<th>Title</th>
|
||||
<th>Episode Count</th>
|
||||
<th>Episode Length</th>
|
||||
<th>Show Type</th>
|
||||
<th>Age Rating</th>
|
||||
<th>Notes</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach($items as $item): ?>
|
||||
<tr>
|
||||
<?php if($auth->is_authenticated()): ?>
|
||||
<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>*/ ?>
|
||||
</td>
|
||||
<?php endif ?>
|
||||
<td class="align_left">
|
||||
<a href="https://hummingbird.me/anime/<?= $item['slug'] ?>">
|
||||
<?= $item['title'] ?>
|
||||
</a>
|
||||
<?= ( ! empty($item['alternate_title'])) ? " · " . $item['alternate_title'] : "" ?>
|
||||
</td>
|
||||
<td><?= $item['episode_count'] ?></td>
|
||||
<td><?= $item['episode_length'] ?></td>
|
||||
<td><?= $item['show_type'] ?></td>
|
||||
<td><?= $item['age_rating'] ?></td>
|
||||
<td class="align_left"><?= $item['notes'] ?></td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<br />
|
||||
<?php endforeach ?>
|
||||
<?php endif ?>
|
||||
<?php if ($_->isAuthenticated()): ?>
|
||||
<a class="bracketed" href="<?= $_->urlFromRoute($collection_type . '.collection.add.get') ?>">Add Item</a>
|
||||
<?php endif ?>
|
||||
<?php if (empty($sections)): ?>
|
||||
<h3>There's nothing here!</h3>
|
||||
<?php else: ?>
|
||||
<br />
|
||||
<label>Filter: <input type='text' class='media-filter' /></label>
|
||||
<br />
|
||||
<?= $_->component->tabs('collection-tab', $sections, static function ($items, $section) use ($_, $helper, $collection_type) {
|
||||
$hasNotes = colNotEmpty($items, 'notes');
|
||||
$hasMedia = $section === 'All';
|
||||
$firstTh = ($_->isAuthenticated()) ? '<td> </td>' : '';
|
||||
$mediaTh = ($hasMedia) ? '<th>Media</th>' : '';
|
||||
$noteTh = ($hasNotes) ? '<th>Notes</th>' : '';
|
||||
|
||||
$rendered = [];
|
||||
foreach ($items as $item)
|
||||
{
|
||||
$rendered[] = renderTemplate(__DIR__ . '/list-item.php', [
|
||||
'_' => $_,
|
||||
'collection_type' => $collection_type,
|
||||
'hasMedia' => $hasMedia,
|
||||
'hasNotes' => $hasNotes,
|
||||
'helper' => $helper,
|
||||
'item' => $item,
|
||||
]);
|
||||
}
|
||||
$rows = implode('', array_map('mb_trim', $rendered));
|
||||
|
||||
return <<<HTML
|
||||
<table class="full-width media-wrap">
|
||||
<thead>
|
||||
<tr>
|
||||
{$firstTh}
|
||||
<th>Title</th>
|
||||
{$mediaTh}
|
||||
<th class='numeric'>Episode Count</th>
|
||||
<th class='numeric'>Episode Length</th>
|
||||
<th>Show Type</th>
|
||||
<th class='rating'>Age Rating</th>
|
||||
{$noteTh}
|
||||
<th>Genres</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>{$rows}</tbody>
|
||||
</table>
|
||||
HTML;
|
||||
|
||||
}) ?>
|
||||
<?php endif ?>
|
||||
</main>
|
||||
<script src="<?= $urlGenerator->asset_url('js.php/g/table') ?>"></script>
|
||||
<script defer="defer" src="<?= $_->assetUrl('js/tables.min.js') ?>"></script>
|
|
@ -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,3 +1,16 @@
|
|||
<script src="<?= $urlGenerator->asset_url('js.php/g/event') ?>"></script>
|
||||
<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 nomodule="nomodule" src="https://polyfill.io/v3/polyfill.min.js?features=es5%2CObject.assign"></script>
|
||||
<script async="async" defer="defer" src="<?= $_->assetUrl('js/scripts.min.js') ?>"></script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
|
|
@ -1,54 +1,42 @@
|
|||
<?php namespace Aviat\AnimeClient ?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title><?= $title ?></title>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="cache-control" content="no-store" />
|
||||
<meta http-equiv="Content-Security-Policy" content="script-src 'self'" />
|
||||
<link rel="stylesheet" href="<?= $urlGenerator->asset_url('css.php/g/base') ?>" />
|
||||
<script src="<?= $urlGenerator->asset_url('js.php/g/base') ?>"></script>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1, user-scalable=1" />
|
||||
<link rel="stylesheet" href="<?= $_->assetUrl('css/' . $_->config->get('theme') . '.min.css') ?>" />
|
||||
<link rel="<?= $_->config->get('theme') === 'dark' ? '' : 'alternate ' ?>stylesheet" title="Dark Theme" href="<?= $_->assetUrl('css/dark.min.css') ?>" />
|
||||
<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>
|
||||
<body class="<?= $escape->attr($url_type) ?> list">
|
||||
<body class="<?= $_->escape->attr($url_type) ?> list">
|
||||
<?php include 'setup-check.php' ?>
|
||||
<header>
|
||||
<h1 class="flex flex-align-end flex-wrap">
|
||||
<span class="flex-no-wrap grow-1">
|
||||
<?php if(strpos($route_path, 'collection') === FALSE): ?>
|
||||
<a href="<?= $escape->attr($urlGenerator->default_url($url_type)) ?>">
|
||||
<?= $config->get('whose_list') ?>'s <?= ucfirst($url_type) ?> List
|
||||
</a>
|
||||
<?php if($config->get("show_{$url_type}_collection")): ?>
|
||||
[<a href="<?= $urlGenerator->url('collection/view') ?>"><?= ucfirst($url_type) ?> Collection</a>]
|
||||
<?php endif ?>
|
||||
[<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>
|
||||
<?php if(isset($message) && is_array($message)):
|
||||
extract($message);
|
||||
include 'message.php';
|
||||
endif ?>
|
||||
<?php
|
||||
include 'main-menu.php';
|
||||
if(isset($message) && is_array($message))
|
||||
{
|
||||
foreach($message as $m)
|
||||
{
|
||||
$message = $m['message'];
|
||||
$message_type = $m['message_type'];
|
||||
include 'message.php';
|
||||
}
|
||||
}
|
||||
?>
|
||||
</header>
|
|
@ -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>
|
|
@ -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>
|
||||
<h2><?= $config->get('whose_list'); ?>'s Login</h2>
|
||||
<h2><?= $_->config->get('whose_list'); ?>'s Login</h2>
|
||||
<?= $message ?>
|
||||
<form method="post" action="<?= $urlGenerator->full_url($urlGenerator->path(), $url_type) ?>">
|
||||
<form method="post" action="<?= $_->urlFromRoute('login.post') ?>">
|
||||
<table class="form invisible">
|
||||
<tr>
|
||||
<td><label for="password">Password: </label></td>
|
||||
|
|
|
@ -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 ?>
|
|
@ -1,7 +1,8 @@
|
|||
<?php if ($auth->is_authenticated()): ?>
|
||||
<?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>
|
||||
|
@ -9,11 +10,11 @@
|
|||
<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 id="series-list" class="media-wrap">
|
||||
</section>
|
||||
</section>
|
||||
<br />
|
||||
<table class="form">
|
||||
<table class="invisible form">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><label for="status">Reading Status</label></td>
|
||||
|
@ -28,6 +29,7 @@
|
|||
<tr>
|
||||
<td> </td>
|
||||
<td>
|
||||
<input type="hidden" name="type" value="manga" />
|
||||
<button type="submit">Save</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -35,5 +37,4 @@
|
|||
</table>
|
||||
</form>
|
||||
</main>
|
||||
<script src="<?= $urlGenerator->asset_url('js.php/g/manga_collection') ?>"></script>
|
||||
<?php endif ?>
|
|
@ -1,60 +1,29 @@
|
|||
<main>
|
||||
<?php if ($auth->is_authenticated()): ?>
|
||||
<a class="bracketed" href="<?= $urlGenerator->url('manga/add') ?>">Add Item</a>
|
||||
<main class="media-list">
|
||||
<?php if ($_->isAuthenticated()): ?>
|
||||
<a class="bracketed" href="<?= $_->urlFromRoute('manga.add.get') ?>">Add Item</a>
|
||||
<?php endif ?>
|
||||
<?php if (empty($sections)): ?>
|
||||
<h3>There's nothing here!</h3>
|
||||
<?php else: ?>
|
||||
<br />
|
||||
<label>Filter: <input type='text' class='media-filter' /></label>
|
||||
<br />
|
||||
<?php foreach ($sections as $name => $items): ?>
|
||||
<?php if (empty($items)): ?>
|
||||
<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">
|
||||
<?php foreach($items as $item): ?>
|
||||
<article class="media" id="manga-<?= $item['id'] ?>">
|
||||
<?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>
|
||||
<?= $component->mangaCover($item, $name) ?>
|
||||
<?php endforeach ?>
|
||||
</section>
|
||||
</section>
|
||||
<?php endif ?>
|
||||
<?php endforeach ?>
|
||||
<?php endif ?>
|
||||
</main>
|
||||
<?php if ($auth->is_authenticated()): ?>
|
||||
<script src="<?= $urlGenerator->asset_url('js.php/g/edit') ?>"></script>
|
||||
<?php endif ?>
|
||||
|
|
|
@ -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,88 +1,104 @@
|
|||
<?php if ($auth->is_authenticated()): ?>
|
||||
<?php if ($_->isAuthenticated()): ?>
|
||||
<main>
|
||||
<h1>
|
||||
Edit <?= $item['manga']['title'] ?>
|
||||
<?= ($item['manga']['alternate_title'] != "") ? "({$item['manga']['alternate_title']})" : ""; ?>
|
||||
</h1>
|
||||
<h2>
|
||||
Edit Manga List Item
|
||||
</h2>
|
||||
<form action="<?= $action ?>" method="post">
|
||||
<table class="form">
|
||||
<thead>
|
||||
<table class="invisible form">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<h3><?= $escape->html($item['manga']['title']) ?></h3>
|
||||
<?php if($item['manga']['alternate_title'] != ""): ?>
|
||||
<h4><?= $escape->html($item['manga']['alternate_title']) ?></h4>
|
||||
<?php endif ?>
|
||||
</th>
|
||||
<th>
|
||||
<article class="media">
|
||||
<?= $helper->img($item['manga']['image']); ?>
|
||||
</article>
|
||||
<h3><?= $_->escape->html($item['manga']['title']) ?></h3>
|
||||
<?php foreach ($item['manga']['titles'] as $title): ?>
|
||||
<h4><?= $_->escape->html($title) ?></h4>
|
||||
<?php endforeach ?>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td rowspan="9">
|
||||
<?= $_->h->img($item['manga']['image']) ?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="status">Reading Status</label></td>
|
||||
<td>
|
||||
<select name="status" id="status">
|
||||
<?php foreach($status_list as $status): ?>
|
||||
<option <?php if($item['reading_status'] === $status): ?>selected="selected"<?php endif ?>
|
||||
value="<?= $status ?>"><?= $status ?></option>
|
||||
<?php endforeach ?>
|
||||
<?php foreach ($status_list as $val => $status): ?>
|
||||
<option <?php if ($item['reading_status'] === $val): ?>selected="selected"<?php endif ?>
|
||||
value="<?= $val ?>"><?= $status ?></option>
|
||||
<?php endforeach ?>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="series_rating">Rating</label></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>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="chapters_read">Chapters Read</label></td>
|
||||
<td>
|
||||
<input type="number" min="0" name="chapters_read" id="chapters_read" value="<?= $item['chapters']['read'] ?>" /> / <?= $item['chapters']['total'] ?>
|
||||
</td>
|
||||
</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'] ?>
|
||||
<input type="number" min="0" name="chapters_read" id="chapters_read"
|
||||
value="<?= $item['chapters']['read'] ?>"/> / <?= $item['chapters']['total'] ?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="rereading_flag">Rereading?</label></td>
|
||||
<td>
|
||||
<input type="checkbox" name="reareading" id="rereading_flag"
|
||||
<?php if($item['rereading'] === TRUE): ?>checked="checked"<?php endif ?>
|
||||
<input type="checkbox" name="rereading" id="rereading_flag"
|
||||
<?php if ($item['rereading'] === TRUE): ?>checked="checked"<?php endif ?>
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="reread_count">Reread Count</label></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>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="notes">Notes</label></td>
|
||||
<td>
|
||||
<textarea name="notes" id="notes"><?= $escape->html($item['notes']) ?></textarea>
|
||||
<textarea name="notes" id="notes"><?= $_->escape->html($item['notes']) ?></textarea>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>
|
||||
<input type="hidden" value="<?= $item['id'] ?>" name="id" />
|
||||
<input type="hidden" value="<?= $item['manga']['slug'] ?>" name="manga_id" />
|
||||
<input type="hidden" value="<?= $item['user_rating'] ?>" name="old_rating" />
|
||||
<input type="hidden" value="true" name="edit" />
|
||||
<input type="hidden" value="<?= $item['id'] ?>" name="id"/>
|
||||
<input type="hidden" value="<?= $item['mal_id'] ?>" name="mal_id"/>
|
||||
<input type="hidden" value="<?= $item['manga']['slug'] ?>" name="manga_id"/>
|
||||
<input type="hidden" value="<?= $item['user_rating'] ?>" name="old_rating"/>
|
||||
<input type="hidden" value="true" name="edit"/>
|
||||
<button type="submit">Submit</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</tbody>
|
||||
</table>
|
||||
</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>
|
||||
<?php endif ?>
|
|
@ -1,48 +1,78 @@
|
|||
<main>
|
||||
<?php if ($auth->is_authenticated()): ?>
|
||||
<a class="bracketed" href="<?= $urlGenerator->url('manga/add') ?>">Add Item</a>
|
||||
<main class="media-list">
|
||||
<?php if ($_->isAuthenticated()): ?>
|
||||
<a class="bracketed" href="<?= $_->urlFromRoute('manga.add.get') ?>">Add Item</a>
|
||||
<?php endif ?>
|
||||
<?php if (empty($sections)): ?>
|
||||
<h3>There's nothing here!</h3>
|
||||
<?php else: ?>
|
||||
<br />
|
||||
<label>Filter: <input type='text' class='media-filter' /></label>
|
||||
<br />
|
||||
<?php foreach ($sections as $name => $items): ?>
|
||||
<h2><?= $name ?></h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<?php if ($auth->is_authenticated()): ?>
|
||||
<th> </th>
|
||||
<?php endif ?>
|
||||
<th>Title</th>
|
||||
<th>Rating</th>
|
||||
<th>Chapters</th>
|
||||
<th>Volumes</th>
|
||||
<th>Type</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach($items as $item): ?>
|
||||
<tr id="manga-<?= $item['id'] ?>">
|
||||
<?php if($auth->is_authenticated()): ?>
|
||||
<td>
|
||||
<a class="bracketed" href="<?= $urlGenerator->url("manga/edit/{$item['id']}/{$name}") ?>">Edit</a>
|
||||
</td>
|
||||
<?php endif ?>
|
||||
<td class="align_left">
|
||||
<a href="<?= $item['manga']['url'] ?>">
|
||||
<?= $item['manga']['title'] ?>
|
||||
</a>
|
||||
<?= ( ! is_null($item['manga']['alternate_title'])) ? " · " . $item['manga']['alternate_title'] : "" ?>
|
||||
</td>
|
||||
<td><?= $item['user_rating'] ?> / 10</td>
|
||||
<td><?= $item['chapters']['read'] ?> / <?= $item['chapters']['total'] ?></td>
|
||||
<td><?= $item['volumes']['read'] ?> / <?= $item['volumes']['total'] ?></td>
|
||||
<td><?= $item['manga']['type'] ?></td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php if (empty($items)): ?>
|
||||
<h3>There's nothing here!</h3>
|
||||
<?php else: ?>
|
||||
<table class='media-wrap'>
|
||||
<thead>
|
||||
<tr>
|
||||
<?php if ($_->isAuthenticated()): ?>
|
||||
<td> </td>
|
||||
<?php endif ?>
|
||||
<th>Title</th>
|
||||
<th class='numeric'>Score</th>
|
||||
<th class='numeric'>Completed Chapters</th>
|
||||
<th>Attributes</th>
|
||||
<th>Type</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach($items as $item): ?>
|
||||
<tr id="manga-<?= $item['id'] ?>">
|
||||
<?php if($_->isAuthenticated()): ?>
|
||||
<td>
|
||||
<a class="bracketed" href="<?= $_->urlFromRoute('edit', [
|
||||
'controller' => 'manga',
|
||||
'id' => $item['id'],
|
||||
'status' => $name
|
||||
]) ?>">Edit</a>
|
||||
</td>
|
||||
<?php endif ?>
|
||||
<td class="align-left">
|
||||
<a href="<?= $_->urlFromRoute('manga.details', ['id' => $item['manga']['slug']]) ?>">
|
||||
<?= $item['manga']['title'] ?>
|
||||
</a>
|
||||
<?php foreach($item['manga']['titles'] as $title): ?>
|
||||
<br /><?= $title ?>
|
||||
<?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 endif ?>
|
||||
</main>
|
||||
<script src="<?= $urlGenerator->asset_url('js.php/g/table') ?>"></script>
|
||||
<script defer="defer" src="<?= $_->assetUrl('js/tables.min.js') ?>"></script>
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
<div class="message {{message_type}}">
|
||||
<span class="icon"></span>
|
||||
{{message}}
|
||||
<span class="close">x</span>
|
||||
</div>
|
|
@ -1,5 +1,5 @@
|
|||
<div class="message <?= $escape->attr($message_type) ?>">
|
||||
<div class="message <?= $_->escape->attr($message_type) ?>">
|
||||
<span class="icon"></span>
|
||||
<?= $escape->html($message) ?>
|
||||
<?= $_->escape->html($message) ?>
|
||||
<span class="close"></span>
|
||||
</div>
|
|
@ -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>
|
|
@ -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 ?>
|
|
@ -0,0 +1,5 @@
|
|||
<article>
|
||||
<label for="<?= $fieldName ?>"><?= $field['title'] ?></label><br />
|
||||
<small><?= $field['description'] ?></small><br />
|
||||
<?= $_->h->field($fieldName, $field); ?>
|
||||
</article>
|
|
@ -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 ?>
|
|
@ -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 ?>
|
|
@ -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>
|
|
@ -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 ?>
|
|
@ -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>
|
168
build.xml
168
build.xml
|
@ -1,168 +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="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"
|
||||
/>
|
||||
|
||||
<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">
|
||||
<parallel threadcount="6">
|
||||
<apply executable="php" passthru="true" taskname="lint">
|
||||
<arg value="-l" />
|
||||
<fileset dir=".">
|
||||
<include name="src/Aviat/AnimeClient/**/*.php" />
|
||||
</fileset>
|
||||
</apply>
|
||||
<apply executable="php" passthru="true" taskname="lint">
|
||||
<arg value="-l" />
|
||||
<fileset dir=".">
|
||||
<include name="src/Aviat/Ion/**/*.php" />
|
||||
</fileset>
|
||||
</apply>
|
||||
<apply executable="php" passthru="true" taskname="lint">
|
||||
<arg value="-l" />
|
||||
<fileset dir=".">
|
||||
<include name="tests/AnimeClient/**/*.php" />
|
||||
</fileset>
|
||||
</apply>
|
||||
<apply executable="php" passthru="true" taskname="lint">
|
||||
<arg value="-l" />
|
||||
<fileset dir=".">
|
||||
<include name="tests/Ion/**/*.php" />
|
||||
</fileset>
|
||||
</apply>
|
||||
</parallel>
|
||||
|
||||
<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}" passthru="true" 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.">
|
||||
<parallel threadcount="2">
|
||||
<phploc countTests="true" reportType="csv" reportDirectory="build/logs" reportName="phploc" taskname="csv report">
|
||||
<fileset dir=".">
|
||||
<include name="src/**/*.php" />
|
||||
<include name="tests/**/*.php" />
|
||||
</fileset>
|
||||
</phploc>
|
||||
<phploc countTests="true" reportType="xml" reportDirectory="build/logs" reportName="phploc" taskname="xml report">
|
||||
<fileset dir=".">
|
||||
<include name="src/**/*.php" />
|
||||
<include name="tests/**/*.php" />
|
||||
</fileset>
|
||||
</phploc>
|
||||
</parallel>
|
||||
|
||||
<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.">
|
||||
<phpdepend>
|
||||
<fileset dir=".">
|
||||
<include name="src/**/*.php" />
|
||||
</fileset>
|
||||
<logger type="jdepend-xml" outfile="build/logs/jdepend.xml" />
|
||||
<logger type="jdepend-chart" outfile="build/pdepend/dependencies.svg" />
|
||||
<logger type="overview-pyramid" outfile="build/pdepend/overview-pyramid.svg" />
|
||||
</phpdepend>
|
||||
|
||||
<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.">
|
||||
<phpcpd>
|
||||
<formatter type="default" usefile="false" />
|
||||
<fileset dir=".">
|
||||
<include name="src/**/*.php" />
|
||||
</fileset>
|
||||
</phpcpd>
|
||||
|
||||
<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.">
|
||||
<phpcpd>
|
||||
<formatter type="pmd" outfile="build/logs/pmd-cpd.xml" />
|
||||
<fileset dir=".">
|
||||
<include name="src/**/*.php" />
|
||||
</fileset>
|
||||
</phpcpd>
|
||||
|
||||
<property name="phpcpd.done" value="true" />
|
||||
</target>
|
||||
|
||||
<target name="phpunit" unless="phpunit.done" depends="prepare" description="Run unit tests with PHPUnit">
|
||||
<exec executable="${phpunit}" logoutput="true" passthru="true" checkreturn="true" 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}" passthru="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}" passthru="true" 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}" passthru="true" 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
|
||||
*/
|
|
@ -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"?>
|
||||
<phpunit
|
||||
colors="true"
|
||||
stopOnFailure="false"
|
||||
bootstrap="../tests/bootstrap.php"
|
||||
beStrictAboutTestsThatDoNotTestAnything="true"
|
||||
checkForUnintentionallyCoveredCode="true"
|
||||
>
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory suffix=".php">../src/Aviat/Ion</directory>
|
||||
<directory suffix=".php">../src/Aviat/AnimeClient</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
<testsuites>
|
||||
<testsuite name="Ion">
|
||||
<directory>../tests/Ion</directory>
|
||||
</testsuite>
|
||||
<testsuite name="AnimeClient">
|
||||
<directory>../tests/AnimeClient</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<logging>
|
||||
<log type="coverage-html" target="coverage"/>
|
||||
<log type="coverage-clover" target="logs/clover.xml"/>
|
||||
<log type="coverage-crap4j" target="logs/crap4j.xml"/>
|
||||
<log type="coverage-xml" target="logs/coverage" />
|
||||
<log type="junit" target="logs/junit.xml" logIncompleteSkipped="false"/>
|
||||
</logging>
|
||||
<php>
|
||||
<server name="HTTP_USER_AGENT" value="Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:38.0) Gecko/20100101 Firefox/38.0" />
|
||||
<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 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">
|
||||
<coverage>
|
||||
<report>
|
||||
<clover outputFile="logs/clover.xml"/>
|
||||
<html outputDirectory="../coverage"/>
|
||||
</report>
|
||||
</coverage>
|
||||
<testsuites>
|
||||
<testsuite name="AnimeClient">
|
||||
<directory>../tests/AnimeClient</directory>
|
||||
</testsuite>
|
||||
<testsuite name="Ion">
|
||||
<directory>../tests/Ion</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<logging>
|
||||
<junit outputFile="logs/junit.xml"/>
|
||||
</logging>
|
||||
<php>
|
||||
<server name="HTTP_USER_AGENT" value="Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:38.0) Gecko/20100101 Firefox/38.0"/>
|
||||
<server name="HTTP_HOST" value="localhost"/>
|
||||
<server name="SERVER_NAME" value="localhost"/>
|
||||
<server name="REQUEST_URI" value="/"/>
|
||||
<server name="REQUEST_METHOD" value="GET"/>
|
||||
</php>
|
||||
<source>
|
||||
<include>
|
||||
<directory suffix=".php">../src</directory>
|
||||
</include>
|
||||
</source>
|
||||
</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,24 +1,78 @@
|
|||
{
|
||||
"name": "timw4mail/hummingbird-anime-client",
|
||||
"description": "A self-hosted anime/manga client for hummingbird.",
|
||||
"license":"MIT",
|
||||
"require": {
|
||||
"abeautifulsite/simpleimage": "2.5.*",
|
||||
"aura/html": "2.*",
|
||||
"aura/router": "2.2.*",
|
||||
"aura/session": "2.*",
|
||||
"aura/web": "2.*",
|
||||
"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.*"
|
||||
},
|
||||
"require-dev": {
|
||||
"codeclimate/php-test-reporter": "dev-master"
|
||||
"name": "aviat/hummingbird-anime-client",
|
||||
"description": "A self-hosted anime/manga client for Kitsu.",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Timothy J. Warren",
|
||||
"email": "tim@timshomepage.net",
|
||||
"homepage": "https://timshomepage.net",
|
||||
"role": "Developer"
|
||||
}
|
||||
}
|
||||
],
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
@media (prefers-color-scheme: dark) {
|
||||
@import "src/dark-override.css";
|
||||
}
|
|
@ -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";
|
|
@ -0,0 +1,4 @@
|
|||
@import "src/-marx-.css";
|
||||
@import "src/general.css";
|
||||
@import "src/components.css";
|
||||
@import "src/responsive.css";
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
module.exports = function filter(filename) {
|
||||
return ! String(filename).includes('min');
|
||||
}
|
|
@ -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;
|
|
@ -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,
|
||||
});
|
||||
});
|
|
@ -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();
|
|
@ -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');
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
import './sw.js';
|
||||
import './events.js';
|
||||
import './session-check.js';
|
||||
import './anime.js';
|
||||
import './manga.js';
|
|
@ -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,
|
||||
});
|
||||
});
|
|
@ -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);
|
||||
}
|
||||
})();
|
|
@ -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);
|
||||
});
|
||||
}
|
|
@ -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('');
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
}
|
||||
});
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue