diff --git a/public/admin.html b/public/admin.html index fa6467a..58bc772 100644 --- a/public/admin.html +++ b/public/admin.html @@ -1 +1,148 @@ -PCFET0NUWVBFIGh0bWw+CjxodG1sIGxhbmc9ImVuIj4KPGhlYWQ+CiAgPG1ldGEgY2hhcnNldD0idXRmLTgiIC8+CiAgPG1ldGEgbmFtZT0idmlld3BvcnQiIGNvbnRlbnQ9IndpZHRoPWRldmljZS13aWR0aCwgaW5pdGlhbC1zY2FsZT0xIiAvPgogIDx0aXRsZT5OZXdidXJ5IE5pZ2h0cyDigJQgQWRtaW48L3RpdGxlPgogIDxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0iL2Nzcy9hcHAuY3NzIiAvPgogIDxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0iL2Nzcy9hZG1pbi5jc3MiIC8+CjwvaGVhZD4KPGJvZHk+CiAgPCEtLSBMb2dpbiAtLT4KICA8ZGl2IGlkPSJsb2dpbiIgY2xhc3M9ImxvZ2luLXdyYXAiPgogICAgPGRpdiBjbGFzcz0ibG9naW4tY2FyZCI+CiAgICAgIDxoMSBjbGFzcz0ibG9naW4tdGl0bGUiPk5ld2J1cnk8c3Bhbj5OaWdodHM8L3NwYW4+PC9oMT4KICAgICAgPGRpdiBjbGFzcz0ibG9naW4tc3ViIG1vbm8iPkFETUlOIENPTlNPTEU8L2Rpdj4KICAgICAgPGxhYmVsIGZvcj0ibGctdXNlciI+VXNlcm5hbWU8L2xhYmVsPgogICAgICA8aW5wdXQgaWQ9ImxnLXVzZXIiIGF1dG9jb21wbGV0ZT0idXNlcm5hbWUiIC8+CiAgICAgIDxsYWJlbCBmb3I9ImxnLXBhc3MiPlBhc3N3b3JkPC9sYWJlbD4KICAgICAgPGlucHV0IGlkPSJsZy1wYXNzIiB0eXBlPSJwYXNzd29yZCIgYXV0b2NvbXBsZXRlPSJjdXJyZW50LXBhc3N3b3JkIiAvPgogICAgICA8YnV0dG9uIGNsYXNzPSJwcmltYXJ5IiBpZD0ibGctZ28iIHN0eWxlPSJ3aWR0aDoxMDAlO21hcmdpbi10b3A6MTRweCI+U2lnbiBpbjwvYnV0dG9uPgogICAgICA8ZGl2IGlkPSJsZy1lcnJvciIgY2xhc3M9ImxnLWVycm9yIGhpZGRlbiI+PC9kaXY+CiAgICA8L2Rpdj4KICA8L2Rpdj4KCiAgPCEtLSBBcHAgLS0+CiAgPGRpdiBpZD0iYXBwIiBjbGFzcz0iaGlkZGVuIj4KICAgIDxoZWFkZXIgY2xhc3M9InRvcGJhciI+CiAgICAgIDxkaXYgY2xhc3M9ImJyYW5kIj5OZXdidXJ5PHNwYW4+TmlnaHRzPC9zcGFuPiA8c3BhbiBjbGFzcz0iYmFkZ2UgbW9ubyI+QURNSU48L3NwYW4+PC9kaXY+CiAgICAgIDxuYXYgY2xhc3M9InRhYnMiPgogICAgICAgIDxidXR0b24gY2xhc3M9InRhYiBhY3RpdmUiIGRhdGEtdGFiPSJnaG9zdHMiPkdob3N0czwvYnV0dG9uPgogICAgICAgIDxidXR0b24gY2xhc3M9InRhYiIgZGF0YS10YWI9InNldHMiPlNldHM8L2J1dHRvbj4KICAgICAgICA8YnV0dG9uIGNsYXNzPSJ0YWIiIGRhdGEtdGFiPSJhY2NvdW50Ij5BY2NvdW50PC9idXR0b24+CiAgICAgIDwvbmF2PgogICAgICA8YnV0dG9uIGlkPSJsb2dvdXQiIGNsYXNzPSJzbWFsbCI+U2lnbiBvdXQ8L2J1dHRvbj4KICAgIDwvaGVhZGVyPgoKICAgIDwhLS0gR0hPU1RTIC0tPgogICAgPHNlY3Rpb24gaWQ9InRhYi1naG9zdHMiIGNsYXNzPSJ0YWItcGFuZWwiPgogICAgICA8ZGl2IGNsYXNzPSJwYW5lbC1oZWFkIj4KICAgICAgICA8aDI+R2hvc3RzPC9oMj4KICAgICAgICA8ZGl2IGNsYXNzPSJoZWFkLWFjdGlvbnMiPgogICAgICAgICAgPGlucHV0IGlkPSJnaG9zdC1zZWFyY2giIHBsYWNlaG9sZGVyPSJTZWFyY2ggbmFtZSAvIGFiaWxpdHnigKYiIHN0eWxlPSJ3aWR0aDoyMjBweCIgLz4KICAgICAgICAgIDxidXR0b24gY2xhc3M9InByaW1hcnkiIGlkPSJuZXctZ2hvc3QiPisgTmV3IGdob3N0PC9idXR0b24+CiAgICAgICAgPC9kaXY+CiAgICAgIDwvZGl2PgogICAgICA8ZGl2IGNsYXNzPSJ0YWJsZS13cmFwIj4KICAgICAgICA8dGFibGUgaWQ9Imdob3N0cy10YWJsZSI+CiAgICAgICAgICA8dGhlYWQ+CiAgICAgICAgICAgIDx0cj48dGg+PC90aD48dGg+TmFtZSAvIERpc3BsYXk8L3RoPjx0aD5UeXBlPC90aD48dGg+UmFyaXR5PC90aD48dGg+SFA8L3RoPjx0aD5ETUc8L3RoPjx0aD5BYmlsaXR5PC90aD48dGg+U2V0PC90aD48dGg+T248L3RoPjx0aD48L3RoPjwvdHI+CiAgICAgICAgICA8L3RoZWFkPgogICAgICAgICAgPHRib2R5PjwvdGJvZHk+CiAgICAgICAgPC90YWJsZT4KICAgICAgPC9kaXY+CiAgICA8L3NlY3Rpb24+CgogICAgPCEtLSBTRVRTIC0tPgogICAgPHNlY3Rpb24gaWQ9InRhYi1zZXRzIiBjbGFzcz0idGFiLXBhbmVsIGhpZGRlbiI+CiAgICAgIDxkaXYgY2xhc3M9InBhbmVsLWhlYWQiPgogICAgICAgIDxoMj5TZXRzPC9oMj4KICAgICAgICA8YnV0dG9uIGNsYXNzPSJwcmltYXJ5IiBpZD0ibmV3LXNldCI+KyBOZXcgc2V0PC9idXR0b24+CiAgICAgIDwvZGl2PgogICAgICA8ZGl2IGlkPSJzZXRzLWxpc3QiIGNsYXNzPSJzZXRzLWxpc3QiPjwvZGl2PgogICAgPC9zZWN0aW9uPgoKICAgIDwhLS0gQUNDT1VOVCAtLT4KICAgIDxzZWN0aW9uIGlkPSJ0YWItYWNjb3VudCIgY2xhc3M9InRhYi1wYW5lbCBoaWRkZW4iPgogICAgICA8ZGl2IGNsYXNzPSJwYW5lbC1oZWFkIj48aDI+QWNjb3VudDwvaDI+PC9kaXY+CiAgICAgIDxkaXYgY2xhc3M9ImFjY291bnQtY2FyZCI+CiAgICAgICAgPHAgY2xhc3M9Im11dGVkIj5TaWduZWQgaW4gYXMgPHN0cm9uZyBpZD0iYWNjdC11c2VyIj7igJQ8L3N0cm9uZz4uIENoYW5nZSB5b3VyIHBhc3N3b3JkIGJlbG93LjwvcD4KICAgICAgICA8bGFiZWw+Q3VycmVudCBwYXNzd29yZDwvbGFiZWw+PGlucHV0IGlkPSJjcC1jdXJyZW50IiB0eXBlPSJwYXNzd29yZCIgLz4KICAgICAgICA8bGFiZWw+TmV3IHBhc3N3b3JkIChtaW4gOCBjaGFycyk8L2xhYmVsPjxpbnB1dCBpZD0iY3AtbmV3IiB0eXBlPSJwYXNzd29yZCIgLz4KICAgICAgICA8YnV0dG9uIGNsYXNzPSJwcmltYXJ5IiBpZD0iY3AtZ28iIHN0eWxlPSJtYXJnaW4tdG9wOjEycHgiPlVwZGF0ZSBwYXNzd29yZDwvYnV0dG9uPgogICAgICAgIDxkaXYgaWQ9ImNwLW1zZyIgY2xhc3M9ImNwLW1zZyBoaWRkZW4iPjwvZGl2PgogICAgICA8L2Rpdj4KICAgIDwvc2VjdGlvbj4KICA8L2Rpdj4KCiAgPCEtLSBHaG9zdCBlZGl0b3IgbW9kYWwgLS0+CiAgPGRpdiBpZD0iZ2hvc3QtbW9kYWwiIGNsYXNzPSJtb2RhbCBoaWRkZW4iPgogICAgPGRpdiBjbGFzcz0ibW9kYWwtY2FyZCI+CiAgICAgIDxoMyBpZD0iZ20tdGl0bGUiPkVkaXQgZ2hvc3Q8L2gzPgogICAgICA8ZGl2IGNsYXNzPSJncmlkMiI+CiAgICAgICAgPGRpdj48bGFiZWw+TmFtZTwvbGFiZWw+PGlucHV0IGlkPSJnbS1uYW1lIiAvPjwvZGl2PgogICAgICAgIDxkaXY+PGxhYmVsPkRpc3BsYXkgbmFtZSAobG9jay1vbiBsYWJlbCk8L2xhYmVsPjxpbnB1dCBpZD0iZ20tZGlzcGxheSIgLz48L2Rpdj4KICAgICAgICA8ZGl2PjxsYWJlbD5UeXBlPC9sYWJlbD4KICAgICAgICAgIDxzZWxlY3QgaWQ9ImdtLXR5cGUiPjxvcHRpb24gdmFsdWU9InJlZCI+UmVkPC9vcHRpb24+PG9wdGlvbiB2YWx1ZT0ieWVsbG93Ij5ZZWxsb3c8L29wdGlvbj48b3B0aW9uIHZhbHVlPSJibHVlIj5CbHVlPC9vcHRpb24+PC9zZWxlY3Q+CiAgICAgICAgPC9kaXY+CiAgICAgICAgPGRpdj48bGFiZWw+UmFyaXR5ICgx4oCTNCk8L2xhYmVsPjxpbnB1dCBpZD0iZ20tcmFyaXR5IiB0eXBlPSJudW1iZXIiIG1pbj0iMSIgbWF4PSI0IiAvPjwvZGl2PgogICAgICAgIDxkaXY+PGxhYmVsPkhlYWx0aDwvbGFiZWw+PGlucHV0IGlkPSJnbS1oZWFsdGgiIHR5cGU9Im51bWJlciIgLz48L2Rpdj4KICAgICAgICA8ZGl2PjxsYWJlbD5EYW1hZ2U8L2xhYmVsPjxpbnB1dCBpZD0iZ20tZGFtYWdlIiB0eXBlPSJudW1iZXIiIC8+PC9kaXY+CiAgICAgICAgPGRpdj48bGFiZWw+U3BlZWQgKDDigJM1KTwvbGFiZWw+PGlucHV0IGlkPSJnbS1zcGVlZCIgdHlwZT0ibnVtYmVyIiBtaW49IjAiIG1heD0iNSIgLz48L2Rpdj4KICAgICAgICA8ZGl2PjxsYWJlbD5SYW5nZSAoMOKAkzUpPC9sYWJlbD48aW5wdXQgaWQ9ImdtLXJhbmdlIiB0eXBlPSJudW1iZXIiIG1pbj0iMCIgbWF4PSI1IiAvPjwvZGl2PgogICAgICAgIDxkaXY+PGxhYmVsPkNoYXJnZSBzaG90ICgw4oCTMyk8L2xhYmVsPjxpbnB1dCBpZD0iZ20tY2hhcmdlIiB0eXBlPSJudW1iZXIiIG1pbj0iMCIgbWF4PSIzIiAvPjwvZGl2PgogICAgICAgIDxkaXY+PGxhYmVsPkFiaWxpdHk8L2xhYmVsPjxpbnB1dCBpZD0iZ20tYWJpbGl0eSIgLz48L2Rpdj4KICAgICAgICA8ZGl2PjxsYWJlbD5TZXQgbnVtYmVyIChyZWYpPC9sYWJlbD48aW5wdXQgaWQ9ImdtLXNldG51bSIgLz48L2Rpdj4KICAgICAgICA8ZGl2PjxsYWJlbD5TZXQgbmFtZSAocmVmKTwvbGFiZWw+PGlucHV0IGlkPSJnbS1zZXRuYW1lIiAvPjwvZGl2PgogICAgICA8L2Rpdj4KICAgICAgPGRpdiBjbGFzcz0iY2hlY2tzIj4KICAgICAgICA8bGFiZWwgY2xhc3M9ImNoayI+PGlucHV0IHR5cGU9ImNoZWNrYm94IiBpZD0iZ20tYm9zcyIgLz4gQm9zcyBnaG9zdDwvbGFiZWw+CiAgICAgICAgPGxhYmVsIGNsYXNzPSJjaGsiPjxpbnB1dCB0eXBlPSJjaGVja2JveCIgaWQ9ImdtLWVuYWJsZWQiIGNoZWNrZWQgLz4gRW5hYmxlZDwvbGFiZWw+CiAgICAgIDwvZGl2PgogICAgICA8ZGl2IGNsYXNzPSJpbWFnZS1yb3ciPgogICAgICAgIDxsYWJlbD5CaWxsYm9hcmQgKEdJRiAvIFBORyAvIFdlYlAsIG9yIE1QNCAvIFdlYk0gdmlkZW8pPC9sYWJlbD4KICAgICAgICA8ZGl2IGNsYXNzPSJyb3ciPgogICAgICAgICAgPGlucHV0IGlkPSJnbS1maWxlIiB0eXBlPSJmaWxlIiBhY2NlcHQ9Ii5naWYsLnBuZywuanBnLC5qcGVnLC53ZWJwLC53ZWJtLC5tcDQiIC8+CiAgICAgICAgICA8aW1nIGlkPSJnbS1wcmV2aWV3IiBjbGFzcz0iZ20tcHJldmlldyBoaWRkZW4iIGFsdD0iIiAvPgogICAgICAgICAgPHZpZGVvIGlkPSJnbS1wcmV2aWV3LXZpZCIgY2xhc3M9ImdtLXByZXZpZXcgaGlkZGVuIiBtdXRlZCBsb29wIHBsYXlzaW5saW5lPjwvdmlkZW8+CiAgICAgICAgPC9kaXY+CiAgICAgICAgPGRpdiBjbGFzcz0ibXV0ZWQiIHN0eWxlPSJmb250LXNpemU6MTFweDttYXJnaW4tdG9wOjRweCI+VXBsb2FkIGhhcHBlbnMgYWZ0ZXIgeW91IHNhdmUgdGhlIGdob3N0LiBNUDQgZmlsZXMgYXJlIGNvbnZlcnRlZCB0byBhIHRyYW5zcGFyZW50IFdlYk0gKHdpdGggYSBXZWJQIGZhbGxiYWNrKSBvbiB0aGUgc2VydmVyLjwvZGl2PgogICAgICA8L2Rpdj4KICAgICAgPGRpdiBjbGFzcz0ibW9kYWwtYWN0aW9ucyI+CiAgICAgICAgPGJ1dHRvbiBkYXRhLWNsb3NlPkNhbmNlbDwvYnV0dG9uPgogICAgICAgIDxidXR0b24gY2xhc3M9InByaW1hcnkiIGlkPSJnbS1zYXZlIj5TYXZlPC9idXR0b24+CiAgICAgIDwvZGl2PgogICAgPC9kaXY+CiAgPC9kaXY+CgogIDwhLS0gU2V0IGVkaXRvciBtb2RhbCAtLT4KICA8ZGl2IGlkPSJzZXQtbW9kYWwiIGNsYXNzPSJtb2RhbCBoaWRkZW4iPgogICAgPGRpdiBjbGFzcz0ibW9kYWwtY2FyZCI+CiAgICAgIDxoMyBpZD0ic20tdGl0bGUiPkVkaXQgc2V0PC9oMz4KICAgICAgPGRpdiBjbGFzcz0iZ3JpZDIiPgogICAgICAgIDxkaXY+PGxhYmVsPlNjYW4gY29kZSAoUVIgcGF5bG9hZCk8L2xhYmVsPjxpbnB1dCBpZD0ic20tY29kZSIgcGxhY2Vob2xkZXI9Ik5OLTcwNDE5IiAvPjwvZGl2PgogICAgICAgIDxkaXY+PGxhYmVsPlNldCBudW1iZXIgKHJlZik8L2xhYmVsPjxpbnB1dCBpZD0ic20tc2V0bnVtIiAvPjwvZGl2PgogICAgICAgIDxkaXY+PGxhYmVsPlNldCBuYW1lPC9sYWJlbD48aW5wdXQgaWQ9InNtLXNldG5hbWUiIC8+PC9kaXY+CiAgICAgICAgPGRpdj48bGFiZWw+Qm9zcyBnaG9zdDwvbGFiZWw+PHNlbGVjdCBpZD0ic20tYm9zcyI+PG9wdGlvbiB2YWx1ZT0iIj7igJQgbm9uZSDigJQ8L29wdGlvbj48L3NlbGVjdD48L2Rpdj4KICAgICAgPC9kaXY+CiAgICAgIDxsYWJlbCBjbGFzcz0iY2hrIj48aW5wdXQgdHlwZT0iY2hlY2tib3giIGlkPSJzbS1lbmFibGVkIiBjaGVja2VkIC8+IEVuYWJsZWQ8L2xhYmVsPgogICAgICA8ZGl2IGNsYXNzPSJyb3N0ZXItcGljayI+CiAgICAgICAgPGxhYmVsPlJvc3RlciAoZ2hvc3RzIHJldHVybmVkIG9uIHNjYW4pPC9sYWJlbD4KICAgICAgICA8aW5wdXQgaWQ9InNtLXJvc3Rlci1zZWFyY2giIHBsYWNlaG9sZGVyPSJGaWx0ZXIgZ2hvc3Rz4oCmIiAvPgogICAgICAgIDxkaXYgaWQ9InNtLXJvc3RlciIgY2xhc3M9InJvc3Rlci1jaGVja2xpc3QiPjwvZGl2PgogICAgICA8L2Rpdj4KICAgICAgPGRpdiBjbGFzcz0ibW9kYWwtYWN0aW9ucyI+CiAgICAgICAgPGJ1dHRvbiBkYXRhLWNsb3NlPkNhbmNlbDwvYnV0dG9uPgogICAgICAgIDxidXR0b24gY2xhc3M9InByaW1hcnkiIGlkPSJzbS1zYXZlIj5TYXZlIHNldDwvYnV0dG9uPgogICAgICA8L2Rpdj4KICAgIDwvZGl2PgogIDwvZGl2PgoKICA8ZGl2IGNsYXNzPSJkaXNjbGFpbWVyIj4KICAgIEZhbi1tYWRlIHRyaWJ1dGUuIE5vdCBhZmZpbGlhdGVkIHdpdGgsIHNwb25zb3JlZCBieSwgb3IgZW5kb3JzZWQgYnkgdGhlIExFR08gR3JvdXAuCiAgICBMRUdPwq4gYW5kIEhpZGRlbiBTaWRl4oSiIGFyZSB0cmFkZW1hcmtzIG9mIHRoZSBMRUdPIEdyb3VwLgogIDwvZGl2PgoKICA8c2NyaXB0IHR5cGU9Im1vZHVsZSIgc3JjPSIvanMvYWRtaW4uanMiPjwvc2NyaXB0Pgo8L2JvZHk+CjwvaHRtbD4K \ No newline at end of file + + + + + + Newbury Nights — Admin + + + + + +
+
+

NewburyNights

+ + + + + + + +
+
+ + + + + + + + + + +
+ Fan-made tribute. Not affiliated with, sponsored by, or endorsed by the LEGO Group. + LEGO® and Hidden Side™ are trademarks of the LEGO Group. +
+ + + + diff --git a/public/css/admin.css b/public/css/admin.css index db8f1dd..20341df 100644 --- a/public/css/admin.css +++ b/public/css/admin.css @@ -1 +1,70 @@ -LmxvZ2luLXdyYXAgeyBtaW4taGVpZ2h0OiAxMDBkdmg7IGRpc3BsYXk6IGdyaWQ7IHBsYWNlLWl0ZW1zOiBjZW50ZXI7IHBhZGRpbmc6IDI0cHg7IH0KLmxvZ2luLWNhcmQgeyBiYWNrZ3JvdW5kOiB2YXIoLS1wYW5lbCk7IGJvcmRlcjogMXB4IHNvbGlkIHZhcigtLWxpbmUpOyBib3JkZXItcmFkaXVzOiB2YXIoLS1yYWQpOyBwYWRkaW5nOiAzMHB4OyB3aWR0aDogMTAwJTsgbWF4LXdpZHRoOiAzNjBweDsgYm94LXNoYWRvdzogdmFyKC0tc2hhZG93KTsgfQoubG9naW4tdGl0bGUgeyBmb250LXNpemU6IDM4cHg7IGxpbmUtaGVpZ2h0OiAuOTsgbWFyZ2luOiAwOyB9Ci5sb2dpbi10aXRsZSBzcGFuIHsgY29sb3I6IHZhcigtLWJsdWUpOyB9Ci5sb2dpbi1zdWIgeyBmb250LXNpemU6IDExcHg7IGxldHRlci1zcGFjaW5nOiAuM2VtOyBjb2xvcjogdmFyKC0tdGV4dC1kaW0pOyBtYXJnaW46IDZweCAwIDIycHg7IH0KLmxvZ2luLWNhcmQgbGFiZWwgeyBtYXJnaW4tdG9wOiAxMnB4OyB9Ci5sZy1lcnJvciwgLmNwLW1zZyB7IGNvbG9yOiB2YXIoLS1kYW5nZXIpOyBmb250LXNpemU6IDEzcHg7IG1hcmdpbi10b3A6IDEycHg7IH0KLmNwLW1zZy5vayB7IGNvbG9yOiB2YXIoLS1nbG9vbSk7IH0KCi50b3BiYXIgeyBkaXNwbGF5OiBmbGV4OyBhbGlnbi1pdGVtczogY2VudGVyOyBnYXA6IDE4cHg7IHBhZGRpbmc6IDE0cHggMjBweDsgYm9yZGVyLWJvdHRvbTogMXB4IHNvbGlkIHZhcigtLWxpbmUpOyBwb3NpdGlvbjogc3RpY2t5OyB0b3A6IDA7IGJhY2tncm91bmQ6IHJnYmEoNyw5LDE1LC44NSk7IGJhY2tkcm9wLWZpbHRlcjogYmx1cigxMHB4KTsgei1pbmRleDogMjA7IH0KLmJyYW5kIHsgZm9udC1mYW1pbHk6IHZhcigtLWRpc3BsYXkpOyBmb250LXNpemU6IDIwcHg7IHRleHQtdHJhbnNmb3JtOiB1cHBlcmNhc2U7IGxldHRlci1zcGFjaW5nOiAuMDRlbTsgfQouYnJhbmQgc3BhbiB7IGNvbG9yOiB2YXIoLS1ibHVlKTsgfQouYmFkZ2UgeyBmb250LXNpemU6IDEwcHg7IGxldHRlci1zcGFjaW5nOiAuMmVtOyBiYWNrZ3JvdW5kOiB2YXIoLS1wYW5lbC0yKTsgYm9yZGVyOiAxcHggc29saWQgdmFyKC0tbGluZSk7IHBhZGRpbmc6IDJweCA3cHg7IGJvcmRlci1yYWRpdXM6IDVweDsgY29sb3I6IHZhcigtLXRleHQtZGltKTsgfQoudGFicyB7IGRpc3BsYXk6IGZsZXg7IGdhcDogNnB4OyBtYXJnaW4tbGVmdDogYXV0bzsgfQoudGFiIHsgYmFja2dyb3VuZDogdHJhbnNwYXJlbnQ7IGJvcmRlci1jb2xvcjogdHJhbnNwYXJlbnQ7IGNvbG9yOiB2YXIoLS10ZXh0LWRpbSk7IHBhZGRpbmc6IDhweCAxNHB4OyB9Ci50YWIuYWN0aXZlIHsgY29sb3I6IHZhcigtLXRleHQpOyBib3JkZXItY29sb3I6IHZhcigtLWxpbmUpOyBiYWNrZ3JvdW5kOiB2YXIoLS1wYW5lbCk7IH0KI2xvZ291dCB7IHBhZGRpbmc6IDhweCAxMnB4OyB9CgoudGFiLXBhbmVsIHsgcGFkZGluZzogMjJweCAyMHB4IDYwcHg7IG1heC13aWR0aDogMTEwMHB4OyBtYXJnaW46IDAgYXV0bzsgfQoucGFuZWwtaGVhZCB7IGRpc3BsYXk6IGZsZXg7IGFsaWduLWl0ZW1zOiBjZW50ZXI7IGp1c3RpZnktY29udGVudDogc3BhY2UtYmV0d2VlbjsgZ2FwOiAxNHB4OyBtYXJnaW4tYm90dG9tOiAxNnB4OyBmbGV4LXdyYXA6IHdyYXA7IH0KLnBhbmVsLWhlYWQgaDIgeyBmb250LXNpemU6IDI0cHg7IG1hcmdpbjogMDsgfQouaGVhZC1hY3Rpb25zIHsgZGlzcGxheTogZmxleDsgZ2FwOiAxMHB4OyBhbGlnbi1pdGVtczogY2VudGVyOyB9CgoudGFibGUtd3JhcCB7IG92ZXJmbG93LXg6IGF1dG87IGJvcmRlcjogMXB4IHNvbGlkIHZhcigtLWxpbmUpOyBib3JkZXItcmFkaXVzOiB2YXIoLS1yYWQpOyB9CnRhYmxlIHsgd2lkdGg6IDEwMCU7IGJvcmRlci1jb2xsYXBzZTogY29sbGFwc2U7IGZvbnQtc2l6ZTogMTRweDsgbWluLXdpZHRoOiA3NjBweDsgfQp0aCwgdGQgeyB0ZXh0LWFsaWduOiBsZWZ0OyBwYWRkaW5nOiAxMHB4IDEycHg7IGJvcmRlci1ib3R0b206IDFweCBzb2xpZCB2YXIoLS1saW5lKTsgd2hpdGUtc3BhY2U6IG5vd3JhcDsgfQp0aCB7IGZvbnQtZmFtaWx5OiB2YXIoLS1tb25vKTsgZm9udC1zaXplOiAxMXB4OyBsZXR0ZXItc3BhY2luZzogLjEyZW07IHRleHQtdHJhbnNmb3JtOiB1cHBlcmNhc2U7IGNvbG9yOiB2YXIoLS10ZXh0LWRpbSk7IHBvc2l0aW9uOiBzdGlja3k7IHRvcDogMDsgYmFja2dyb3VuZDogdmFyKC0tYmctc29mdCk7IH0KdGJvZHkgdHI6aG92ZXIgeyBiYWNrZ3JvdW5kOiB2YXIoLS1wYW5lbCk7IH0KdGQgLnN1YiB7IGNvbG9yOiB2YXIoLS10ZXh0LWRpbSk7IGZvbnQtc2l6ZTogMTJweDsgfQoucGlsbCB7IGZvbnQtc2l6ZTogMTFweDsgcGFkZGluZzogMnB4IDhweDsgYm9yZGVyLXJhZGl1czogOTk5cHg7IGJvcmRlcjogMXB4IHNvbGlkIHZhcigtLWxpbmUpOyBmb250LWZhbWlseTogdmFyKC0tbW9ubyk7IH0KLnRvZ2dsZSB7IGN1cnNvcjogcG9pbnRlcjsgZm9udC1zaXplOiAxOHB4OyB9Ci50b2dnbGUub24geyBjb2xvcjogdmFyKC0tZ2xvb20pOyB9IC50b2dnbGUub2ZmIHsgY29sb3I6IHZhcigtLXRleHQtZGltKTsgfQoucm93LWFjdGlvbnMgeyBkaXNwbGF5OiBmbGV4OyBnYXA6IDZweDsgfQoucm93LWFjdGlvbnMgYnV0dG9uIHsgcGFkZGluZzogNXB4IDlweDsgZm9udC1zaXplOiAxMXB4OyB9Ci50aHVtYi1jZWxsIGltZywgLnRodW1iLWNlbGwgdmlkZW8geyB3aWR0aDogMzRweDsgaGVpZ2h0OiAzNHB4OyBvYmplY3QtZml0OiBjb250YWluOyBiYWNrZ3JvdW5kOiAjMDAwYTsgYm9yZGVyLXJhZGl1czogNnB4OyB9Cgouc2V0cy1saXN0IHsgZGlzcGxheTogZ3JpZDsgZ2FwOiAxNHB4OyB9Ci5zZXQtY2FyZCB7IGJvcmRlcjogMXB4IHNvbGlkIHZhcigtLWxpbmUpOyBib3JkZXItcmFkaXVzOiB2YXIoLS1yYWQpOyBwYWRkaW5nOiAxNnB4OyBiYWNrZ3JvdW5kOiB2YXIoLS1wYW5lbCk7IH0KLnNldC1jYXJkLm9mZiB7IG9wYWNpdHk6IC41NTsgfQouc2V0LWhlYWQgeyBkaXNwbGF5OiBmbGV4OyBhbGlnbi1pdGVtczogY2VudGVyOyBnYXA6IDEycHg7IGZsZXgtd3JhcDogd3JhcDsgfQouc2V0LWhlYWQgLmNvZGUgeyBmb250LWZhbWlseTogdmFyKC0tbW9ubyk7IGJhY2tncm91bmQ6IHZhcigtLWJnLXNvZnQpOyBwYWRkaW5nOiAzcHggOHB4OyBib3JkZXItcmFkaXVzOiA2cHg7IGZvbnQtc2l6ZTogMTNweDsgfQouc2V0LWhlYWQgLnNuYW1lIHsgZm9udC1mYW1pbHk6IHZhcigtLWRpc3BsYXkpOyB0ZXh0LXRyYW5zZm9ybTogdXBwZXJjYXNlOyBsZXR0ZXItc3BhY2luZzogLjAzZW07IH0KLnNldC1oZWFkIC5ncm93IHsgZmxleDogMTsgfQouc2V0LXJvc3RlciB7IG1hcmdpbi10b3A6IDEwcHg7IGRpc3BsYXk6IGZsZXg7IGZsZXgtd3JhcDogd3JhcDsgZ2FwOiA2cHg7IH0KLnJjaGlwIHsgZm9udC1zaXplOiAxMXB4OyBwYWRkaW5nOiAzcHggOHB4OyBib3JkZXItcmFkaXVzOiA2cHg7IGJvcmRlcjogMXB4IHNvbGlkIHZhcigtLWxpbmUpOyBkaXNwbGF5OiBmbGV4OyBhbGlnbi1pdGVtczogY2VudGVyOyBnYXA6IDVweDsgfQoKLmFjY291bnQtY2FyZCB7IG1heC13aWR0aDogNDIwcHg7IGJvcmRlcjogMXB4IHNvbGlkIHZhcigtLWxpbmUpOyBib3JkZXItcmFkaXVzOiB2YXIoLS1yYWQpOyBwYWRkaW5nOiAyMHB4OyBiYWNrZ3JvdW5kOiB2YXIoLS1wYW5lbCk7IH0KLmFjY291bnQtY2FyZCBsYWJlbCB7IG1hcmdpbi10b3A6IDEycHg7IH0KCi5tb2RhbCB7IHBvc2l0aW9uOiBmaXhlZDsgaW5zZXQ6IDA7IGJhY2tncm91bmQ6IHJnYmEoNyw5LDE1LC44KTsgYmFja2Ryb3AtZmlsdGVyOiBibHVyKDRweCk7IGRpc3BsYXk6IGdyaWQ7IHBsYWNlLWl0ZW1zOiBjZW50ZXI7IHotaW5kZXg6IDUwOyBwYWRkaW5nOiAxOHB4OyB9Ci5tb2RhbC1jYXJkIHsgYmFja2dyb3VuZDogdmFyKC0tcGFuZWwpOyBib3JkZXI6IDFweCBzb2xpZCB2YXIoLS1saW5lKTsgYm9yZGVyLXJhZGl1czogdmFyKC0tcmFkKTsgcGFkZGluZzogMjJweDsgd2lkdGg6IDEwMCU7IG1heC13aWR0aDogNjIwcHg7IG1heC1oZWlnaHQ6IDkwZHZoOyBvdmVyZmxvdzogYXV0bzsgYm94LXNoYWRvdzogdmFyKC0tc2hhZG93KTsgfQoubW9kYWwtY2FyZCBoMyB7IG1hcmdpbjogMCAwIDE2cHg7IGZvbnQtc2l6ZTogMjJweDsgfQouZ3JpZDIgeyBkaXNwbGF5OiBncmlkOyBncmlkLXRlbXBsYXRlLWNvbHVtbnM6IDFmciAxZnI7IGdhcDogMTJweDsgfQouY2hlY2tzIHsgZGlzcGxheTogZmxleDsgZ2FwOiAxOHB4OyBtYXJnaW46IDE0cHggMDsgfQouY2hrIHsgZGlzcGxheTogZmxleDsgYWxpZ24taXRlbXM6IGNlbnRlcjsgZ2FwOiA3cHg7IGZvbnQtc2l6ZTogMTNweDsgY29sb3I6IHZhcigtLXRleHQpOyBtYXJnaW46IDA7IH0KLmNoayBpbnB1dCB7IHdpZHRoOiBhdXRvOyB9Ci5pbWFnZS1yb3cgeyBtYXJnaW46IDZweCAwIDRweDsgfQouZ20tcHJldmlldyB7IHdpZHRoOiA0OHB4OyBoZWlnaHQ6IDQ4cHg7IG9iamVjdC1maXQ6IGNvbnRhaW47IGJhY2tncm91bmQ6ICMwMDBhOyBib3JkZXItcmFkaXVzOiA4cHg7IH0KLm1vZGFsLWFjdGlvbnMgeyBkaXNwbGF5OiBmbGV4OyBqdXN0aWZ5LWNvbnRlbnQ6IGZsZXgtZW5kOyBnYXA6IDEwcHg7IG1hcmdpbi10b3A6IDE4cHg7IH0KCi5yb3N0ZXItcGljayB7IG1hcmdpbi10b3A6IDE0cHg7IH0KLnJvc3Rlci1jaGVja2xpc3QgeyBtYXgtaGVpZ2h0OiAyNDBweDsgb3ZlcmZsb3c6IGF1dG87IGJvcmRlcjogMXB4IHNvbGlkIHZhcigtLWxpbmUpOyBib3JkZXItcmFkaXVzOiAxMHB4OyBwYWRkaW5nOiA4cHg7IG1hcmdpbi10b3A6IDhweDsgZGlzcGxheTogZ3JpZDsgZ3JpZC10ZW1wbGF0ZS1jb2x1bW5zOiAxZnIgMWZyOyBnYXA6IDRweDsgfQoucmNoZWNrIHsgZGlzcGxheTogZmxleDsgYWxpZ24taXRlbXM6IGNlbnRlcjsgZ2FwOiA3cHg7IGZvbnQtc2l6ZTogMTJweDsgcGFkZGluZzogNHB4IDZweDsgYm9yZGVyLXJhZGl1czogNnB4OyB9Ci5yY2hlY2s6aG92ZXIgeyBiYWNrZ3JvdW5kOiB2YXIoLS1wYW5lbC0yKTsgfQoucmNoZWNrIGlucHV0IHsgd2lkdGg6IGF1dG87IH0KCkBtZWRpYSAobWF4LXdpZHRoOiA2NDBweCkgewogIC5ncmlkMiwgLnJvc3Rlci1jaGVja2xpc3QgeyBncmlkLXRlbXBsYXRlLWNvbHVtbnM6IDFmcjsgfQogIC50YWJzIHsgb3JkZXI6IDM7IHdpZHRoOiAxMDAlOyB9Cn0K \ No newline at end of file +.login-wrap { min-height: 100dvh; display: grid; place-items: center; padding: 24px; } +.login-card { background: var(--panel); border: 1px solid var(--line); border-radius: var(--rad); padding: 30px; width: 100%; max-width: 360px; box-shadow: var(--shadow); } +.login-title { font-size: 38px; line-height: .9; margin: 0; } +.login-title span { color: var(--blue); } +.login-sub { font-size: 11px; letter-spacing: .3em; color: var(--text-dim); margin: 6px 0 22px; } +.login-card label { margin-top: 12px; } +.lg-error, .cp-msg { color: var(--danger); font-size: 13px; margin-top: 12px; } +.cp-msg.ok { color: var(--gloom); } + +.topbar { display: flex; align-items: center; gap: 18px; padding: 14px 20px; border-bottom: 1px solid var(--line); position: sticky; top: 0; background: rgba(7,9,15,.85); backdrop-filter: blur(10px); z-index: 20; } +.brand { font-family: var(--display); font-size: 20px; text-transform: uppercase; letter-spacing: .04em; } +.brand span { color: var(--blue); } +.badge { font-size: 10px; letter-spacing: .2em; background: var(--panel-2); border: 1px solid var(--line); padding: 2px 7px; border-radius: 5px; color: var(--text-dim); } +.tabs { display: flex; gap: 6px; margin-left: auto; } +.tab { background: transparent; border-color: transparent; color: var(--text-dim); padding: 8px 14px; } +.tab.active { color: var(--text); border-color: var(--line); background: var(--panel); } +#logout { padding: 8px 12px; } + +.tab-panel { padding: 22px 20px 60px; max-width: 1100px; margin: 0 auto; } +.panel-head { display: flex; align-items: center; justify-content: space-between; gap: 14px; margin-bottom: 16px; flex-wrap: wrap; } +.panel-head h2 { font-size: 24px; margin: 0; } +.head-actions { display: flex; gap: 10px; align-items: center; } + +.table-wrap { overflow-x: auto; border: 1px solid var(--line); border-radius: var(--rad); } +table { width: 100%; border-collapse: collapse; font-size: 14px; min-width: 760px; } +th, td { text-align: left; padding: 10px 12px; border-bottom: 1px solid var(--line); white-space: nowrap; } +th { font-family: var(--mono); font-size: 11px; letter-spacing: .12em; text-transform: uppercase; color: var(--text-dim); position: sticky; top: 0; background: var(--bg-soft); } +tbody tr:hover { background: var(--panel); } +td .sub { color: var(--text-dim); font-size: 12px; } +.pill { font-size: 11px; padding: 2px 8px; border-radius: 999px; border: 1px solid var(--line); font-family: var(--mono); } +.toggle { cursor: pointer; font-size: 18px; } +.toggle.on { color: var(--gloom); } .toggle.off { color: var(--text-dim); } +.row-actions { display: flex; gap: 6px; } +.row-actions button { padding: 5px 9px; font-size: 11px; } +.thumb-cell img, .thumb-cell video { width: 34px; height: 34px; object-fit: contain; background: #000a; border-radius: 6px; } + +.sets-list { display: grid; gap: 14px; } +.set-card { border: 1px solid var(--line); border-radius: var(--rad); padding: 16px; background: var(--panel); } +.set-card.off { opacity: .55; } +.set-head { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; } +.set-head .code { font-family: var(--mono); background: var(--bg-soft); padding: 3px 8px; border-radius: 6px; font-size: 13px; } +.set-head .sname { font-family: var(--display); text-transform: uppercase; letter-spacing: .03em; } +.set-head .grow { flex: 1; } +.set-roster { margin-top: 10px; display: flex; flex-wrap: wrap; gap: 6px; } +.rchip { font-size: 11px; padding: 3px 8px; border-radius: 6px; border: 1px solid var(--line); display: flex; align-items: center; gap: 5px; } + +.account-card { max-width: 420px; border: 1px solid var(--line); border-radius: var(--rad); padding: 20px; background: var(--panel); } +.account-card label { margin-top: 12px; } + +.modal { position: fixed; inset: 0; background: rgba(7,9,15,.8); backdrop-filter: blur(4px); display: grid; place-items: center; z-index: 50; padding: 18px; } +.modal-card { background: var(--panel); border: 1px solid var(--line); border-radius: var(--rad); padding: 22px; width: 100%; max-width: 620px; max-height: 90dvh; overflow: auto; box-shadow: var(--shadow); } +.modal-card h3 { margin: 0 0 16px; font-size: 22px; } +.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; } +.checks { display: flex; gap: 18px; margin: 14px 0; } +.chk { display: flex; align-items: center; gap: 7px; font-size: 13px; color: var(--text); margin: 0; } +.chk input { width: auto; } +.image-row { margin: 6px 0 4px; } +.gm-preview { width: 48px; height: 48px; object-fit: contain; background: #000a; border-radius: 8px; } +.modal-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 18px; } + +.roster-pick { margin-top: 14px; } +.roster-checklist { max-height: 240px; overflow: auto; border: 1px solid var(--line); border-radius: 10px; padding: 8px; margin-top: 8px; display: grid; grid-template-columns: 1fr 1fr; gap: 4px; } +.rcheck { display: flex; align-items: center; gap: 7px; font-size: 12px; padding: 4px 6px; border-radius: 6px; } +.rcheck:hover { background: var(--panel-2); } +.rcheck input { width: auto; } + +@media (max-width: 640px) { + .grid2, .roster-checklist { grid-template-columns: 1fr; } + .tabs { order: 3; width: 100%; } +} diff --git a/public/js/admin.js b/public/js/admin.js index 120b20c..90cf8f1 100644 --- a/public/js/admin.js +++ b/public/js/admin.js @@ -1 +1,357 @@ -LyogTmV3YnVyeSBOaWdodHMg4oCUIGFkbWluIGNvbnNvbGUgKi8KY29uc3QgJCA9IChzLCByID0gZG9jdW1lbnQpID0+IHIucXVlcnlTZWxlY3RvcihzKTsKY29uc3QgJCQgPSAocywgciA9IGRvY3VtZW50KSA9PiBbLi4uci5xdWVyeVNlbGVjdG9yQWxsKHMpXTsKCmxldCBUT0tFTiA9IG51bGw7CmxldCBHSE9TVFMgPSBbXTsKbGV0IFNFVFMgPSBbXTsKCmNvbnN0IGFwaSA9IChwYXRoLCBvcHRzID0ge30pID0+CiAgZmV0Y2gocGF0aCwgewogICAgLi4ub3B0cywKICAgIGhlYWRlcnM6IHsKICAgICAgLi4uKG9wdHMuYm9keSAmJiAhKG9wdHMuYm9keSBpbnN0YW5jZW9mIEZvcm1EYXRhKSA/IHsgJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJyB9IDoge30pLAogICAgICAuLi4oVE9LRU4gPyB7IEF1dGhvcml6YXRpb246IGBCZWFyZXIgJHtUT0tFTn1gIH0gOiB7fSksCiAgICAgIC4uLihvcHRzLmhlYWRlcnMgfHwge30pLAogICAgfSwKICB9KTsKCi8qIC0tLS0tLS0tLS0gQXV0aCAtLS0tLS0tLS0tICovCiQoJyNsZy1nbycpLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgbG9naW4pOwokKCcjbGctcGFzcycpLmFkZEV2ZW50TGlzdGVuZXIoJ2tleWRvd24nLCAoZSkgPT4geyBpZiAoZS5rZXkgPT09ICdFbnRlcicpIGxvZ2luKCk7IH0pOwoKYXN5bmMgZnVuY3Rpb24gbG9naW4oKSB7CiAgY29uc3QgdXNlcm5hbWUgPSAkKCcjbGctdXNlcicpLnZhbHVlLnRyaW0oKTsKICBjb25zdCBwYXNzd29yZCA9ICQoJyNsZy1wYXNzJykudmFsdWU7CiAgY29uc3QgZXJyID0gJCgnI2xnLWVycm9yJyk7CiAgZXJyLmNsYXNzTGlzdC5hZGQoJ2hpZGRlbicpOwogIGNvbnN0IHJlcyA9IGF3YWl0IGFwaSgnL2F1dGgvbG9naW4nLCB7IG1ldGhvZDogJ1BPU1QnLCBib2R5OiBKU09OLnN0cmluZ2lmeSh7IHVzZXJuYW1lLCBwYXNzd29yZCB9KSB9KTsKICBpZiAoIXJlcy5vaykgewogICAgZXJyLnRleHRDb250ZW50ID0gJ0ludmFsaWQgdXNlcm5hbWUgb3IgcGFzc3dvcmQuJzsKICAgIGVyci5jbGFzc0xpc3QucmVtb3ZlKCdoaWRkZW4nKTsKICAgIHJldHVybjsKICB9CiAgY29uc3QgZGF0YSA9IGF3YWl0IHJlcy5qc29uKCk7CiAgVE9LRU4gPSBkYXRhLnRva2VuOwogICQoJyNhY2N0LXVzZXInKS50ZXh0Q29udGVudCA9IGRhdGEudXNlci51c2VybmFtZTsKICAkKCcjbG9naW4nKS5jbGFzc0xpc3QuYWRkKCdoaWRkZW4nKTsKICAkKCcjYXBwJykuY2xhc3NMaXN0LnJlbW92ZSgnaGlkZGVuJyk7CiAgYXdhaXQgUHJvbWlzZS5hbGwoW2xvYWRHaG9zdHMoKSwgbG9hZFNldHMoKV0pOwp9CgokKCcjbG9nb3V0JykuYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCBhc3luYyAoKSA9PiB7CiAgYXdhaXQgYXBpKCcvYXV0aC9sb2dvdXQnLCB7IG1ldGhvZDogJ1BPU1QnIH0pOwogIGxvY2F0aW9uLnJlbG9hZCgpOwp9KTsKCi8qIC0tLS0tLS0tLS0gVGFicyAtLS0tLS0tLS0tICovCiQkKCcudGFiJykuZm9yRWFjaCgodCkgPT4KICB0LmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgKCkgPT4gewogICAgJCQoJy50YWInKS5mb3JFYWNoKCh4KSA9PiB4LmNsYXNzTGlzdC5yZW1vdmUoJ2FjdGl2ZScpKTsKICAgIHQuY2xhc3NMaXN0LmFkZCgnYWN0aXZlJyk7CiAgICAkJCgnLnRhYi1wYW5lbCcpLmZvckVhY2goKHApID0+IHAuY2xhc3NMaXN0LmFkZCgnaGlkZGVuJykpOwogICAgJChgI3RhYi0ke3QuZGF0YXNldC50YWJ9YCkuY2xhc3NMaXN0LnJlbW92ZSgnaGlkZGVuJyk7CiAgfSkKKTsKCi8qIC0tLS0tLS0tLS0gR2hvc3RzIC0tLS0tLS0tLS0gKi8KYXN5bmMgZnVuY3Rpb24gbG9hZEdob3N0cygpIHsKICBjb25zdCByZXMgPSBhd2FpdCBhcGkoJy9hcGkvYWRtaW4vZ2hvc3RzJyk7CiAgR0hPU1RTID0gKGF3YWl0IHJlcy5qc29uKCkpLmdob3N0czsKICByZW5kZXJHaG9zdHMoKTsKfQoKJCgnI2dob3N0LXNlYXJjaCcpLmFkZEV2ZW50TGlzdGVuZXIoJ2lucHV0JywgcmVuZGVyR2hvc3RzKTsKCmZ1bmN0aW9uIHJlbmRlckdob3N0cygpIHsKICBjb25zdCBxID0gJCgnI2dob3N0LXNlYXJjaCcpLnZhbHVlLnRvTG93ZXJDYXNlKCk7CiAgY29uc3Qgcm93cyA9IEdIT1NUUy5maWx0ZXIoCiAgICAoZykgPT4gIXEgfHwgZy5uYW1lLnRvTG93ZXJDYXNlKCkuaW5jbHVkZXMocSkgfHwgKGcuYWJpbGl0eSB8fCAnJykudG9Mb3dlckNhc2UoKS5pbmNsdWRlcyhxKQogICkubWFwKGdob3N0Um93KS5qb2luKCcnKTsKICAkKCcjZ2hvc3RzLXRhYmxlIHRib2R5JykuaW5uZXJIVE1MID0gcm93czsKCiAgJCQoJyNnaG9zdHMtdGFibGUgLmVkaXQnKS5mb3JFYWNoKChiKSA9PgogICAgYi5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsICgpID0+IG9wZW5HaG9zdCgrYi5kYXRhc2V0LmlkKSkpOwogICQkKCcjZ2hvc3RzLXRhYmxlIC5kZWwnKS5mb3JFYWNoKChiKSA9PgogICAgYi5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsICgpID0+IGRlbGV0ZUdob3N0KCtiLmRhdGFzZXQuaWQpKSk7CiAgJCQoJyNnaG9zdHMtdGFibGUgLnRvZ2dsZScpLmZvckVhY2goKGIpID0+CiAgICBiLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgKCkgPT4gdG9nZ2xlR2hvc3QoK2IuZGF0YXNldC5pZCkpKTsKfQoKZnVuY3Rpb24gZ2hvc3RSb3coZykgewogIGNvbnN0IGltZyA9IGcuaW1hZ2VfcGF0aAogICAgPyBgPGltZyBzcmM9Ii91cGxvYWRzLyR7Zy5pbWFnZV9wYXRofSIgYWx0PSIiPmAKICAgIDogKGcud2VibV9wYXRoCiAgICAgICAgPyBgPHZpZGVvIHNyYz0iL3VwbG9hZHMvJHtnLndlYm1fcGF0aH0iIG11dGVkIGxvb3AgYXV0b3BsYXkgcGxheXNpbmxpbmU+PC92aWRlbz5gCiAgICAgICAgOiAnJyk7CiAgY29uc3Qgc2V0UmVmID0gZy5zZXRfbnVtYmVyID8gYCR7Zy5zZXRfbnVtYmVyfWAgOiAn4oCUJzsKICByZXR1cm4gYDx0cj4KICAgIDx0ZCBjbGFzcz0idGh1bWItY2VsbCI+JHtpbWd9PC90ZD4KICAgIDx0ZD48ZGl2PiR7ZXNjKGcubmFtZSl9JHtnLmlzX2Jvc3MgPyAnIDxzcGFuIGNsYXNzPSJwaWxsIj5ib3NzPC9zcGFuPicgOiAnJ308L2Rpdj4KICAgICAgICAke2cuZGlzcGxheV9uYW1lICYmIGcuZGlzcGxheV9uYW1lICE9PSBnLm5hbWUgPyBgPGRpdiBjbGFzcz0ic3ViIj7ihrMgJHtlc2MoZy5kaXNwbGF5X25hbWUpfTwvZGl2PmAgOiAnJ308L3RkPgogICAgPHRkPjxzcGFuIGNsYXNzPSJkb3QgJHtnLnR5cGV9Ij48L3NwYW4+ICR7Zy50eXBlfTwvdGQ+CiAgICA8dGQgY2xhc3M9InN0YXJzIj4keyfimIUnLnJlcGVhdChnLnJhcml0eSl9PC90ZD4KICAgIDx0ZD4ke2cuaGVhbHRofTwvdGQ+PHRkPiR7Zy5kYW1hZ2V9PC90ZD4KICAgIDx0ZD4ke2VzYyhnLmFiaWxpdHkgfHwgJ+KAlCcpfTwvdGQ+CiAgICA8dGQgY2xhc3M9Im1vbm8iPiR7c2V0UmVmfTwvdGQ+CiAgICA8dGQ+PHNwYW4gY2xhc3M9InRvZ2dsZSAke2cuZW5hYmxlZCA/ICdvbicgOiAnb2ZmJ30iIGRhdGEtaWQ9IiR7Zy5pZH0iPiR7Zy5lbmFibGVkID8gJ+KXiScgOiAn4pevJ308L3NwYW4+PC90ZD4KICAgIDx0ZD48ZGl2IGNsYXNzPSJyb3ctYWN0aW9ucyI+CiAgICAgIDxidXR0b24gY2xhc3M9ImVkaXQiIGRhdGEtaWQ9IiR7Zy5pZH0iPkVkaXQ8L2J1dHRvbj4KICAgICAgPGJ1dHRvbiBjbGFzcz0iZGFuZ2VyIGRlbCIgZGF0YS1pZD0iJHtnLmlkfSI+RGVsPC9idXR0b24+CiAgICA8L2Rpdj48L3RkPgogIDwvdHI+YDsKfQoKYXN5bmMgZnVuY3Rpb24gdG9nZ2xlR2hvc3QoaWQpIHsKICBjb25zdCBnID0gR0hPU1RTLmZpbmQoKHgpID0+IHguaWQgPT09IGlkKTsKICBhd2FpdCBhcGkoYC9hcGkvYWRtaW4vZ2hvc3RzLyR7aWR9YCwgeyBtZXRob2Q6ICdQQVRDSCcsIGJvZHk6IEpTT04uc3RyaW5naWZ5KHsgZW5hYmxlZDogIWcuZW5hYmxlZCB9KSB9KTsKICBhd2FpdCBsb2FkR2hvc3RzKCk7Cn0KCmFzeW5jIGZ1bmN0aW9uIGRlbGV0ZUdob3N0KGlkKSB7CiAgY29uc3QgZyA9IEdIT1NUUy5maW5kKCh4KSA9PiB4LmlkID09PSBpZCk7CiAgaWYgKCFjb25maXJtKGBEZWxldGUgIiR7Zy5uYW1lfSI/IFRoaXMgYWxzbyByZW1vdmVzIGl0IGZyb20gYW55IHNldCByb3N0ZXJzLmApKSByZXR1cm47CiAgYXdhaXQgYXBpKGAvYXBpL2FkbWluL2dob3N0cy8ke2lkfWAsIHsgbWV0aG9kOiAnREVMRVRFJyB9KTsKICBhd2FpdCBQcm9taXNlLmFsbChbbG9hZEdob3N0cygpLCBsb2FkU2V0cygpXSk7Cn0KCi8qIGdob3N0IG1vZGFsICovCmxldCBlZGl0aW5nR2hvc3RJZCA9IG51bGw7CiQoJyNuZXctZ2hvc3QnKS5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsICgpID0+IG9wZW5HaG9zdChudWxsKSk7CgpmdW5jdGlvbiBvcGVuR2hvc3QoaWQpIHsKICBlZGl0aW5nR2hvc3RJZCA9IGlkOwogIGNvbnN0IGcgPSBpZCA/IEdIT1NUUy5maW5kKCh4KSA9PiB4LmlkID09PSBpZCkgOiBudWxsOwogICQoJyNnbS10aXRsZScpLnRleHRDb250ZW50ID0gZyA/ICdFZGl0IGdob3N0JyA6ICdOZXcgZ2hvc3QnOwogICQoJyNnbS1uYW1lJykudmFsdWUgPSBnPy5uYW1lIHx8ICcnOwogICQoJyNnbS1kaXNwbGF5JykudmFsdWUgPSBnPy5kaXNwbGF5X25hbWUgfHwgJyc7CiAgJCgnI2dtLXR5cGUnKS52YWx1ZSA9IGc/LnR5cGUgfHwgJ3JlZCc7CiAgJCgnI2dtLXJhcml0eScpLnZhbHVlID0gZz8ucmFyaXR5ID8/IDE7CiAgJCgnI2dtLWhlYWx0aCcpLnZhbHVlID0gZz8uaGVhbHRoID8/IDMwMDsKICAkKCcjZ20tZGFtYWdlJykudmFsdWUgPSBnPy5kYW1hZ2UgPz8gMTUwOwogICQoJyNnbS1zcGVlZCcpLnZhbHVlID0gZz8uc3BlZWQgPz8gMzsKICAkKCcjZ20tcmFuZ2UnKS52YWx1ZSA9IGc/LnJhbmdlID8/IDM7CiAgJCgnI2dtLWNoYXJnZScpLnZhbHVlID0gZz8uY2hhcmdlX3Nob3QgPz8gMjsKICAkKCcjZ20tYWJpbGl0eScpLnZhbHVlID0gZz8uYWJpbGl0eSB8fCAnJzsKICAkKCcjZ20tc2V0bnVtJykudmFsdWUgPSBnPy5zZXRfbnVtYmVyIHx8ICcnOwogICQoJyNnbS1zZXRuYW1lJykudmFsdWUgPSBnPy5zZXRfbmFtZSB8fCAnJzsKICAkKCcjZ20tYm9zcycpLmNoZWNrZWQgPSAhIWc/LmlzX2Jvc3M7CiAgJCgnI2dtLWVuYWJsZWQnKS5jaGVja2VkID0gZyA/ICEhZy5lbmFibGVkIDogdHJ1ZTsKICAkKCcjZ20tZmlsZScpLnZhbHVlID0gJyc7CiAgLy8gU2hvdyB0aGUgZXhpc3Rpbmcgc3RvcmVkIG1lZGlhOiBwcmVmZXIgdGhlIFdlYk0gdmlkZW8sIGVsc2UgYSBzdGlsbCBpbWFnZQogIC8vIChpbWFnZV9wYXRoIGlzIHRoZSBHSUYvUE5HLCBvciB0aGUgV2ViUCB0aHVtYm5haWwgZm9yIGNvbnZlcnRlZCBnaG9zdHMpLgogIGlmIChnPy53ZWJtX3BhdGgpIHNob3dQcmV2aWV3KGAvdXBsb2Fkcy8ke2cud2VibV9wYXRofWAsICd2aWRlbycpOwogIGVsc2UgaWYgKGc/LmltYWdlX3BhdGgpIHNob3dQcmV2aWV3KGAvdXBsb2Fkcy8ke2cuaW1hZ2VfcGF0aH1gLCAnaW1hZ2UnKTsKICBlbHNlIGlmIChnPy53ZWJwX3BhdGgpIHNob3dQcmV2aWV3KGAvdXBsb2Fkcy8ke2cud2VicF9wYXRofWAsICdpbWFnZScpOwogIGVsc2UgaGlkZVByZXZpZXcoKTsKICAkKCcjZ2hvc3QtbW9kYWwnKS5jbGFzc0xpc3QucmVtb3ZlKCdoaWRkZW4nKTsKfQoKLy8gU3dhcCB0aGUgbW9kYWwgcHJldmlldyBiZXR3ZWVuIGFuIDxpbWc+IGFuZCBhIDx2aWRlbz4gZGVwZW5kaW5nIG9uIG1lZGlhIGtpbmQuCmZ1bmN0aW9uIHNob3dQcmV2aWV3KHNyYywga2luZCkgewogIGNvbnN0IGltZyA9ICQoJyNnbS1wcmV2aWV3Jyk7CiAgY29uc3QgdmlkID0gJCgnI2dtLXByZXZpZXctdmlkJyk7CiAgaWYgKGtpbmQgPT09ICd2aWRlbycpIHsKICAgIGltZy5jbGFzc0xpc3QuYWRkKCdoaWRkZW4nKTsgaW1nLnJlbW92ZUF0dHJpYnV0ZSgnc3JjJyk7CiAgICB2aWQuc3JjID0gc3JjOyB2aWQuY2xhc3NMaXN0LnJlbW92ZSgnaGlkZGVuJyk7CiAgICB2aWQucGxheT8uKCkuY2F0Y2goKCkgPT4ge30pOwogIH0gZWxzZSB7CiAgICB2aWQuY2xhc3NMaXN0LmFkZCgnaGlkZGVuJyk7IHZpZC5wYXVzZT8uKCk7IHZpZC5yZW1vdmVBdHRyaWJ1dGUoJ3NyYycpOwogICAgaW1nLnNyYyA9IHNyYzsgaW1nLmNsYXNzTGlzdC5yZW1vdmUoJ2hpZGRlbicpOwogIH0KfQoKZnVuY3Rpb24gaGlkZVByZXZpZXcoKSB7CiAgY29uc3QgaW1nID0gJCgnI2dtLXByZXZpZXcnKTsKICBjb25zdCB2aWQgPSAkKCcjZ20tcHJldmlldy12aWQnKTsKICBpbWcuY2xhc3NMaXN0LmFkZCgnaGlkZGVuJyk7IGltZy5yZW1vdmVBdHRyaWJ1dGUoJ3NyYycpOwogIHZpZC5jbGFzc0xpc3QuYWRkKCdoaWRkZW4nKTsgdmlkLnBhdXNlPy4oKTsgdmlkLnJlbW92ZUF0dHJpYnV0ZSgnc3JjJyk7Cn0KCi8vIExpdmUgbG9jYWwgcHJldmlldyB3aGVuIGEgZmlsZSBpcyBjaG9zZW4gKGJlZm9yZSB1cGxvYWQpLgokKCcjZ20tZmlsZScpLmFkZEV2ZW50TGlzdGVuZXIoJ2NoYW5nZScsICgpID0+IHsKICBjb25zdCBmaWxlID0gJCgnI2dtLWZpbGUnKS5maWxlc1swXTsKICBpZiAoIWZpbGUpIHJldHVybjsKICBjb25zdCB1cmwgPSBVUkwuY3JlYXRlT2JqZWN0VVJMKGZpbGUpOwogIGNvbnN0IGlzVmlkZW8gPSAvXC4obXA0fHdlYm0pJC9pLnRlc3QoZmlsZS5uYW1lKSB8fCBmaWxlLnR5cGUuc3RhcnRzV2l0aCgndmlkZW8vJyk7CiAgc2hvd1ByZXZpZXcodXJsLCBpc1ZpZGVvID8gJ3ZpZGVvJyA6ICdpbWFnZScpOwp9KTsKCiQoJyNnbS1zYXZlJykuYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCBzYXZlR2hvc3QpOwoKYXN5bmMgZnVuY3Rpb24gc2F2ZUdob3N0KCkgewogIGNvbnN0IHBheWxvYWQgPSB7CiAgICBuYW1lOiAkKCcjZ20tbmFtZScpLnZhbHVlLnRyaW0oKSwKICAgIGRpc3BsYXlOYW1lOiAkKCcjZ20tZGlzcGxheScpLnZhbHVlLnRyaW0oKSB8fCAkKCcjZ20tbmFtZScpLnZhbHVlLnRyaW0oKSwKICAgIHR5cGU6ICQoJyNnbS10eXBlJykudmFsdWUsCiAgICByYXJpdHk6ICskKCcjZ20tcmFyaXR5JykudmFsdWUsCiAgICBoZWFsdGg6ICskKCcjZ20taGVhbHRoJykudmFsdWUsCiAgICBkYW1hZ2U6ICskKCcjZ20tZGFtYWdlJykudmFsdWUsCiAgICBzcGVlZDogKyQoJyNnbS1zcGVlZCcpLnZhbHVlLAogICAgcmFuZ2U6ICskKCcjZ20tcmFuZ2UnKS52YWx1ZSwKICAgIGNoYXJnZVNob3Q6ICskKCcjZ20tY2hhcmdlJykudmFsdWUsCiAgICBhYmlsaXR5OiAkKCcjZ20tYWJpbGl0eScpLnZhbHVlLnRyaW0oKSB8fCBudWxsLAogICAgc2V0TnVtYmVyOiAkKCcjZ20tc2V0bnVtJykudmFsdWUudHJpbSgpIHx8IG51bGwsCiAgICBzZXROYW1lOiAkKCcjZ20tc2V0bmFtZScpLnZhbHVlLnRyaW0oKSB8fCBudWxsLAogICAgaXNCb3NzOiAkKCcjZ20tYm9zcycpLmNoZWNrZWQsCiAgICBlbmFibGVkOiAkKCcjZ20tZW5hYmxlZCcpLmNoZWNrZWQsCiAgfTsKICBpZiAoIXBheWxvYWQubmFtZSkgeyBhbGVydCgnTmFtZSBpcyByZXF1aXJlZC4nKTsgcmV0dXJuOyB9CgogIGxldCBpZCA9IGVkaXRpbmdHaG9zdElkOwogIGlmIChpZCkgewogICAgYXdhaXQgYXBpKGAvYXBpL2FkbWluL2dob3N0cy8ke2lkfWAsIHsgbWV0aG9kOiAnUEFUQ0gnLCBib2R5OiBKU09OLnN0cmluZ2lmeShwYXlsb2FkKSB9KTsKICB9IGVsc2UgewogICAgY29uc3QgcmVzID0gYXdhaXQgYXBpKCcvYXBpL2FkbWluL2dob3N0cycsIHsgbWV0aG9kOiAnUE9TVCcsIGJvZHk6IEpTT04uc3RyaW5naWZ5KHBheWxvYWQpIH0pOwogICAgaWQgPSAoYXdhaXQgcmVzLmpzb24oKSkuaWQ7CiAgfQoKICBjb25zdCBmaWxlID0gJCgnI2dtLWZpbGUnKS5maWxlc1swXTsKICBpZiAoZmlsZSAmJiBpZCkgewogICAgY29uc3QgZmQgPSBuZXcgRm9ybURhdGEoKTsKICAgIGZkLmFwcGVuZCgnaW1hZ2UnLCBmaWxlKTsKICAgIGF3YWl0IGFwaShgL2FwaS9hZG1pbi9naG9zdHMvJHtpZH0vaW1hZ2VgLCB7IG1ldGhvZDogJ1BPU1QnLCBib2R5OiBmZCB9KTsKICB9CgogICQoJyNnaG9zdC1tb2RhbCcpLmNsYXNzTGlzdC5hZGQoJ2hpZGRlbicpOwogIGF3YWl0IFByb21pc2UuYWxsKFtsb2FkR2hvc3RzKCksIGxvYWRTZXRzKCldKTsKfQoKLyogLS0tLS0tLS0tLSBTZXRzIC0tLS0tLS0tLS0gKi8KYXN5bmMgZnVuY3Rpb24gbG9hZFNldHMoKSB7CiAgY29uc3QgcmVzID0gYXdhaXQgYXBpKCcvYXBpL2FkbWluL3NldHMnKTsKICBTRVRTID0gKGF3YWl0IHJlcy5qc29uKCkpLnNldHM7CiAgcmVuZGVyU2V0cygpOwp9CgpmdW5jdGlvbiByZW5kZXJTZXRzKCkgewogICQoJyNzZXRzLWxpc3QnKS5pbm5lckhUTUwgPSBTRVRTLm1hcChzZXRDYXJkKS5qb2luKCcnKSB8fAogICAgJzxwIGNsYXNzPSJtdXRlZCI+Tm8gc2V0cyB5ZXQuIENyZWF0ZSBvbmUgdG8gd2lyZSBhIHNjYW4gY29kZSB0byBhIGdob3N0IHJvc3Rlci48L3A+JzsKICAkJCgnI3NldHMtbGlzdCAuZWRpdC1zZXQnKS5mb3JFYWNoKChiKSA9PgogICAgYi5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsICgpID0+IG9wZW5TZXQoK2IuZGF0YXNldC5pZCkpKTsKICAkJCgnI3NldHMtbGlzdCAuZGVsLXNldCcpLmZvckVhY2goKGIpID0+CiAgICBiLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgKCkgPT4gZGVsZXRlU2V0KCtiLmRhdGFzZXQuaWQpKSk7Cn0KCmZ1bmN0aW9uIHNldENhcmQocykgewogIGNvbnN0IGNoaXBzID0gcy5yb3N0ZXIubWFwKChyKSA9PgogICAgYDxzcGFuIGNsYXNzPSJyY2hpcCI+PHNwYW4gY2xhc3M9ImRvdCAke3IudHlwZX0iPjwvc3Bhbj4ke2VzYyhyLm5hbWUpfSR7ci5pc19ib3NzID8gJyDimIUnIDogJyd9PC9zcGFuPmAKICApLmpvaW4oJycpOwogIHJldHVybiBgPGRpdiBjbGFzcz0ic2V0LWNhcmQgJHtzLmVuYWJsZWQgPyAnJyA6ICdvZmYnfSI+CiAgICA8ZGl2IGNsYXNzPSJzZXQtaGVhZCI+CiAgICAgIDxzcGFuIGNsYXNzPSJjb2RlIj4ke2VzYyhzLmNvZGUpfTwvc3Bhbj4KICAgICAgPHNwYW4gY2xhc3M9InNuYW1lIj4ke2VzYyhzLnNldF9uYW1lKX08L3NwYW4+CiAgICAgICR7cy5zZXRfbnVtYmVyID8gYDxzcGFuIGNsYXNzPSJtb25vIG11dGVkIj4jJHtlc2Mocy5zZXRfbnVtYmVyKX08L3NwYW4+YCA6ICcnfQogICAgICA8c3BhbiBjbGFzcz0iZ3JvdyI+PC9zcGFuPgogICAgICA8YnV0dG9uIGNsYXNzPSJlZGl0LXNldCIgZGF0YS1pZD0iJHtzLmlkfSI+RWRpdDwvYnV0dG9uPgogICAgICA8YnV0dG9uIGNsYXNzPSJkYW5nZXIgZGVsLXNldCIgZGF0YS1pZD0iJHtzLmlkfSI+RGVsZXRlPC9idXR0b24+CiAgICA8L2Rpdj4KICAgIDxkaXYgY2xhc3M9InNldC1yb3N0ZXIiPiR7Y2hpcHMgfHwgJzxzcGFuIGNsYXNzPSJtdXRlZCI+bm8gcm9zdGVyPC9zcGFuPid9PC9kaXY+CiAgPC9kaXY+YDsKfQoKYXN5bmMgZnVuY3Rpb24gZGVsZXRlU2V0KGlkKSB7CiAgY29uc3QgcyA9IFNFVFMuZmluZCgoeCkgPT4geC5pZCA9PT0gaWQpOwogIGlmICghY29uZmlybShgRGVsZXRlIHNldCAiJHtzLnNldF9uYW1lfSIgKCR7cy5jb2RlfSk/YCkpIHJldHVybjsKICBhd2FpdCBhcGkoYC9hcGkvYWRtaW4vc2V0cy8ke2lkfWAsIHsgbWV0aG9kOiAnREVMRVRFJyB9KTsKICBhd2FpdCBsb2FkU2V0cygpOwp9CgovKiBzZXQgbW9kYWwgKi8KbGV0IGVkaXRpbmdTZXRJZCA9IG51bGw7CiQoJyNuZXctc2V0JykuYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCAoKSA9PiBvcGVuU2V0KG51bGwpKTsKJCgnI3NtLXJvc3Rlci1zZWFyY2gnKS5hZGRFdmVudExpc3RlbmVyKCdpbnB1dCcsIGZpbHRlclJvc3RlckNoZWNrbGlzdCk7CgpmdW5jdGlvbiBvcGVuU2V0KGlkKSB7CiAgZWRpdGluZ1NldElkID0gaWQ7CiAgY29uc3QgcyA9IGlkID8gU0VUUy5maW5kKCh4KSA9PiB4LmlkID09PSBpZCkgOiBudWxsOwogICQoJyNzbS10aXRsZScpLnRleHRDb250ZW50ID0gcyA/ICdFZGl0IHNldCcgOiAnTmV3IHNldCc7CiAgJCgnI3NtLWNvZGUnKS52YWx1ZSA9IHM/LmNvZGUgfHwgJyc7CiAgJCgnI3NtLXNldG51bScpLnZhbHVlID0gcz8uc2V0X251bWJlciB8fCAnJzsKICAkKCcjc20tc2V0bmFtZScpLnZhbHVlID0gcz8uc2V0X25hbWUgfHwgJyc7CiAgJCgnI3NtLWVuYWJsZWQnKS5jaGVja2VkID0gcyA/ICEhcy5lbmFibGVkIDogdHJ1ZTsKCiAgLy8gYm9zcyBkcm9wZG93bgogIGNvbnN0IGJvc3NTZWwgPSAkKCcjc20tYm9zcycpOwogIGJvc3NTZWwuaW5uZXJIVE1MID0gJzxvcHRpb24gdmFsdWU9IiI+4oCUIG5vbmUg4oCUPC9vcHRpb24+JyArCiAgICBHSE9TVFMuZmlsdGVyKChnKSA9PiBnLmlzX2Jvc3MpLm1hcCgoZykgPT4KICAgICAgYDxvcHRpb24gdmFsdWU9IiR7Zy5pZH0iPiR7ZXNjKGcubmFtZSl9PC9vcHRpb24+YCkuam9pbignJyk7CiAgYm9zc1NlbC52YWx1ZSA9IHM/LmJvc3NfZ2hvc3RfaWQgfHwgJyc7CgogIC8vIHJvc3RlciBjaGVja2xpc3QKICBjb25zdCBzZWxlY3RlZCA9IG5ldyBTZXQoKHM/LnJvc3RlciB8fCBbXSkubWFwKChyKSA9PiByLmlkKSk7CiAgJCgnI3NtLXJvc3RlcicpLmlubmVySFRNTCA9IEdIT1NUUy5tYXAoKGcpID0+CiAgICBgPGxhYmVsIGNsYXNzPSJyY2hlY2siIGRhdGEtbmFtZT0iJHtlc2MoZy5uYW1lLnRvTG93ZXJDYXNlKCkpfSI+CiAgICAgIDxpbnB1dCB0eXBlPSJjaGVja2JveCIgdmFsdWU9IiR7Zy5pZH0iICR7c2VsZWN0ZWQuaGFzKGcuaWQpID8gJ2NoZWNrZWQnIDogJyd9PgogICAgICA8c3BhbiBjbGFzcz0iZG90ICR7Zy50eXBlfSI+PC9zcGFuPiAke2VzYyhnLm5hbWUpfSAkeyfimIUnLnJlcGVhdChnLnJhcml0eSl9CiAgICA8L2xhYmVsPmApLmpvaW4oJycpOwogICQoJyNzbS1yb3N0ZXItc2VhcmNoJykudmFsdWUgPSAnJzsKICAkKCcjc2V0LW1vZGFsJykuY2xhc3NMaXN0LnJlbW92ZSgnaGlkZGVuJyk7Cn0KCmZ1bmN0aW9uIGZpbHRlclJvc3RlckNoZWNrbGlzdCgpIHsKICBjb25zdCBxID0gJCgnI3NtLXJvc3Rlci1zZWFyY2gnKS52YWx1ZS50b0xvd2VyQ2FzZSgpOwogICQkKCcjc20tcm9zdGVyIC5yY2hlY2snKS5mb3JFYWNoKChlbCkgPT4gewogICAgZWwuc3R5bGUuZGlzcGxheSA9ICFxIHx8IGVsLmRhdGFzZXQubmFtZS5pbmNsdWRlcyhxKSA/ICcnIDogJ25vbmUnOwogIH0pOwp9CgokKCcjc20tc2F2ZScpLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgc2F2ZVNldCk7Cgphc3luYyBmdW5jdGlvbiBzYXZlU2V0KCkgewogIGNvbnN0IHBheWxvYWQgPSB7CiAgICBjb2RlOiAkKCcjc20tY29kZScpLnZhbHVlLnRyaW0oKSwKICAgIHNldE51bWJlcjogJCgnI3NtLXNldG51bScpLnZhbHVlLnRyaW0oKSB8fCBudWxsLAogICAgc2V0TmFtZTogJCgnI3NtLXNldG5hbWUnKS52YWx1ZS50cmltKCksCiAgICBib3NzR2hvc3RJZDogJCgnI3NtLWJvc3MnKS52YWx1ZSA/ICskKCcjc20tYm9zcycpLnZhbHVlIDogbnVsbCwKICAgIGVuYWJsZWQ6ICQoJyNzbS1lbmFibGVkJykuY2hlY2tlZCwKICB9OwogIGlmICghcGF5bG9hZC5jb2RlIHx8ICFwYXlsb2FkLnNldE5hbWUpIHsgYWxlcnQoJ1NjYW4gY29kZSBhbmQgc2V0IG5hbWUgYXJlIHJlcXVpcmVkLicpOyByZXR1cm47IH0KCiAgbGV0IGlkID0gZWRpdGluZ1NldElkOwogIGlmIChpZCkgewogICAgYXdhaXQgYXBpKGAvYXBpL2FkbWluL3NldHMvJHtpZH1gLCB7IG1ldGhvZDogJ1BBVENIJywgYm9keTogSlNPTi5zdHJpbmdpZnkocGF5bG9hZCkgfSk7CiAgfSBlbHNlIHsKICAgIGNvbnN0IHJlcyA9IGF3YWl0IGFwaSgnL2FwaS9hZG1pbi9zZXRzJywgeyBtZXRob2Q6ICdQT1NUJywgYm9keTogSlNPTi5zdHJpbmdpZnkocGF5bG9hZCkgfSk7CiAgICBpZiAocmVzLnN0YXR1cyA9PT0gNDA5KSB7IGFsZXJ0KCdUaGF0IHNjYW4gY29kZSBhbHJlYWR5IGV4aXN0cy4nKTsgcmV0dXJuOyB9CiAgICBpZCA9IChhd2FpdCByZXMuanNvbigpKS5pZDsKICB9CgogIGNvbnN0IGdob3N0SWRzID0gJCQoJyNzbS1yb3N0ZXIgaW5wdXQ6Y2hlY2tlZCcpLm1hcCgoaSkgPT4gK2kudmFsdWUpOwogIGF3YWl0IGFwaShgL2FwaS9hZG1pbi9zZXRzLyR7aWR9L3Jvc3RlcmAsIHsgbWV0aG9kOiAnUFVUJywgYm9keTogSlNPTi5zdHJpbmdpZnkoeyBnaG9zdElkcyB9KSB9KTsKCiAgJCgnI3NldC1tb2RhbCcpLmNsYXNzTGlzdC5hZGQoJ2hpZGRlbicpOwogIGF3YWl0IGxvYWRTZXRzKCk7Cn0KCi8qIC0tLS0tLS0tLS0gQWNjb3VudCAtLS0tLS0tLS0tICovCiQoJyNjcC1nbycpLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgYXN5bmMgKCkgPT4gewogIGNvbnN0IG1zZyA9ICQoJyNjcC1tc2cnKTsKICBtc2cuY2xhc3NMaXN0LmFkZCgnaGlkZGVuJyk7IG1zZy5jbGFzc0xpc3QucmVtb3ZlKCdvaycpOwogIGNvbnN0IHJlcyA9IGF3YWl0IGFwaSgnL2F1dGgvY2hhbmdlLXBhc3N3b3JkJywgewogICAgbWV0aG9kOiAnUE9TVCcsCiAgICBib2R5OiBKU09OLnN0cmluZ2lmeSh7CiAgICAgIGN1cnJlbnRQYXNzd29yZDogJCgnI2NwLWN1cnJlbnQnKS52YWx1ZSwKICAgICAgbmV3UGFzc3dvcmQ6ICQoJyNjcC1uZXcnKS52YWx1ZSwKICAgIH0pLAogIH0pOwogIGNvbnN0IGRhdGEgPSBhd2FpdCByZXMuanNvbigpLmNhdGNoKCgpID0+ICh7fSkpOwogIGlmIChyZXMub2spIHsKICAgIG1zZy50ZXh0Q29udGVudCA9ICdQYXNzd29yZCB1cGRhdGVkLic7IG1zZy5jbGFzc0xpc3QuYWRkKCdvaycpOwogIH0gZWxzZSB7CiAgICBtc2cudGV4dENvbnRlbnQgPSBkYXRhLmVycm9yIHx8ICdDb3VsZCBub3QgdXBkYXRlIHBhc3N3b3JkLic7CiAgfQogIG1zZy5jbGFzc0xpc3QucmVtb3ZlKCdoaWRkZW4nKTsKICAkKCcjY3AtY3VycmVudCcpLnZhbHVlID0gJyc7ICQoJyNjcC1uZXcnKS52YWx1ZSA9ICcnOwp9KTsKCi8qIC0tLS0tLS0tLS0gbW9kYWwgY2xvc2UgLS0tLS0tLS0tLSAqLwokJCgnW2RhdGEtY2xvc2VdJykuZm9yRWFjaCgoYikgPT4KICBiLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgKCkgPT4gJCQoJy5tb2RhbCcpLmZvckVhY2goKG0pID0+IG0uY2xhc3NMaXN0LmFkZCgnaGlkZGVuJykpKSk7CiQkKCcubW9kYWwnKS5mb3JFYWNoKChtKSA9PgogIG0uYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCAoZSkgPT4geyBpZiAoZS50YXJnZXQgPT09IG0pIG0uY2xhc3NMaXN0LmFkZCgnaGlkZGVuJyk7IH0pKTsKCmZ1bmN0aW9uIGVzYyhzKSB7CiAgcmV0dXJuIFN0cmluZyhzID8/ICcnKS5yZXBsYWNlKC9bJjw+IiddL2csIChjKSA9PgogICAgKHsgJyYnOiAnJmFtcDsnLCAnPCc6ICcmbHQ7JywgJz4nOiAnJmd0OycsICciJzogJyZxdW90OycsICInIjogJyYjMzk7JyB9W2NdKSk7Cn0K \ No newline at end of file +/* Newbury Nights — admin console */ +const $ = (s, r = document) => r.querySelector(s); +const $$ = (s, r = document) => [...r.querySelectorAll(s)]; + +let TOKEN = null; +let GHOSTS = []; +let SETS = []; + +const api = (path, opts = {}) => + fetch(path, { + ...opts, + headers: { + ...(opts.body && !(opts.body instanceof FormData) ? { 'Content-Type': 'application/json' } : {}), + ...(TOKEN ? { Authorization: `Bearer ${TOKEN}` } : {}), + ...(opts.headers || {}), + }, + }); + +/* ---------- Auth ---------- */ +$('#lg-go').addEventListener('click', login); +$('#lg-pass').addEventListener('keydown', (e) => { if (e.key === 'Enter') login(); }); + +async function login() { + const username = $('#lg-user').value.trim(); + const password = $('#lg-pass').value; + const err = $('#lg-error'); + err.classList.add('hidden'); + const res = await api('/auth/login', { method: 'POST', body: JSON.stringify({ username, password }) }); + if (!res.ok) { + err.textContent = 'Invalid username or password.'; + err.classList.remove('hidden'); + return; + } + const data = await res.json(); + TOKEN = data.token; + $('#acct-user').textContent = data.user.username; + $('#login').classList.add('hidden'); + $('#app').classList.remove('hidden'); + await Promise.all([loadGhosts(), loadSets()]); +} + +$('#logout').addEventListener('click', async () => { + await api('/auth/logout', { method: 'POST' }); + location.reload(); +}); + +/* ---------- Tabs ---------- */ +$$('.tab').forEach((t) => + t.addEventListener('click', () => { + $$('.tab').forEach((x) => x.classList.remove('active')); + t.classList.add('active'); + $$('.tab-panel').forEach((p) => p.classList.add('hidden')); + $(`#tab-${t.dataset.tab}`).classList.remove('hidden'); + }) +); + +/* ---------- Ghosts ---------- */ +async function loadGhosts() { + const res = await api('/api/admin/ghosts'); + GHOSTS = (await res.json()).ghosts; + renderGhosts(); +} + +$('#ghost-search').addEventListener('input', renderGhosts); + +function renderGhosts() { + const q = $('#ghost-search').value.toLowerCase(); + const rows = GHOSTS.filter( + (g) => !q || g.name.toLowerCase().includes(q) || (g.ability || '').toLowerCase().includes(q) + ).map(ghostRow).join(''); + $('#ghosts-table tbody').innerHTML = rows; + + $$('#ghosts-table .edit').forEach((b) => + b.addEventListener('click', () => openGhost(+b.dataset.id))); + $$('#ghosts-table .del').forEach((b) => + b.addEventListener('click', () => deleteGhost(+b.dataset.id))); + $$('#ghosts-table .toggle').forEach((b) => + b.addEventListener('click', () => toggleGhost(+b.dataset.id))); +} + +function ghostRow(g) { + const img = g.image_path + ? `` + : (g.webm_path + ? `` + : ''); + const setRef = g.set_number ? `${g.set_number}` : '—'; + return ` + ${img} +
${esc(g.name)}${g.is_boss ? ' boss' : ''}
+ ${g.display_name && g.display_name !== g.name ? `
↳ ${esc(g.display_name)}
` : ''} + ${g.type} + ${'★'.repeat(g.rarity)} + ${g.health}${g.damage} + ${esc(g.ability || '—')} + ${setRef} + ${g.enabled ? '◉' : '◯'} +
+ + +
+ `; +} + +async function toggleGhost(id) { + const g = GHOSTS.find((x) => x.id === id); + await api(`/api/admin/ghosts/${id}`, { method: 'PATCH', body: JSON.stringify({ enabled: !g.enabled }) }); + await loadGhosts(); +} + +async function deleteGhost(id) { + const g = GHOSTS.find((x) => x.id === id); + if (!confirm(`Delete "${g.name}"? This also removes it from any set rosters.`)) return; + await api(`/api/admin/ghosts/${id}`, { method: 'DELETE' }); + await Promise.all([loadGhosts(), loadSets()]); +} + +/* ghost modal */ +let editingGhostId = null; +$('#new-ghost').addEventListener('click', () => openGhost(null)); + +function openGhost(id) { + editingGhostId = id; + const g = id ? GHOSTS.find((x) => x.id === id) : null; + $('#gm-title').textContent = g ? 'Edit ghost' : 'New ghost'; + $('#gm-name').value = g?.name || ''; + $('#gm-display').value = g?.display_name || ''; + $('#gm-type').value = g?.type || 'red'; + $('#gm-rarity').value = g?.rarity ?? 1; + $('#gm-health').value = g?.health ?? 300; + $('#gm-damage').value = g?.damage ?? 150; + $('#gm-speed').value = g?.speed ?? 3; + $('#gm-range').value = g?.range ?? 3; + $('#gm-charge').value = g?.charge_shot ?? 2; + $('#gm-ability').value = g?.ability || ''; + $('#gm-setnum').value = g?.set_number || ''; + $('#gm-setname').value = g?.set_name || ''; + $('#gm-boss').checked = !!g?.is_boss; + $('#gm-enabled').checked = g ? !!g.enabled : true; + $('#gm-file').value = ''; + // Show the existing stored media: prefer the WebM video, else a still image + // (image_path is the GIF/PNG, or the WebP thumbnail for converted ghosts). + if (g?.webm_path) showPreview(`/uploads/${g.webm_path}`, 'video'); + else if (g?.image_path) showPreview(`/uploads/${g.image_path}`, 'image'); + else if (g?.webp_path) showPreview(`/uploads/${g.webp_path}`, 'image'); + else hidePreview(); + $('#ghost-modal').classList.remove('hidden'); +} + +// Swap the modal preview between an and a