From 1584ebaa0d6522b4d82e57bd2377beb63e0be4ef Mon Sep 17 00:00:00 2001 From: 4DBug <4DBug@github.com> Date: Sun, 1 Mar 2026 15:36:34 -0600 Subject: [PATCH] add guestbook --- images/emotebutton.gif | Bin 0 -> 1670 bytes images/emotes/applause.gif | Bin 0 -> 2162 bytes images/emotes/astonished.gif | Bin 0 -> 846 bytes images/emotes/caramelldansen.gif | Bin 0 -> 822 bytes images/emotes/joy.gif | Bin 0 -> 1183 bytes images/emotes/kiss.gif | Bin 0 -> 837 bytes images/emotes/laugh.gif | Bin 0 -> 2077 bytes images/emotes/love.gif | Bin 0 -> 1853 bytes images/emotes/wave.gif | Bin 0 -> 1344 bytes images/emotes/yippee.gif | Bin 0 -> 675 bytes images/pitcochatmenu.png | Bin 5047 -> 4986 bytes images/sendbutton.png | Bin 0 -> 430 bytes images/sendbuttonhover.png | Bin 0 -> 413 bytes index.html | 20 ++- js/guestbook-worker.js | 126 ++++++++++++++++ js/guestbook.js | 244 +++++++++++++++++++++++++++++++ style.css | 202 ++++++++++++++++++++++++- 17 files changed, 587 insertions(+), 5 deletions(-) create mode 100644 images/emotebutton.gif create mode 100644 images/emotes/applause.gif create mode 100644 images/emotes/astonished.gif create mode 100644 images/emotes/caramelldansen.gif create mode 100644 images/emotes/joy.gif create mode 100644 images/emotes/kiss.gif create mode 100644 images/emotes/laugh.gif create mode 100644 images/emotes/love.gif create mode 100644 images/emotes/wave.gif create mode 100644 images/emotes/yippee.gif create mode 100644 images/sendbutton.png create mode 100644 images/sendbuttonhover.png create mode 100644 js/guestbook-worker.js create mode 100644 js/guestbook.js diff --git a/images/emotebutton.gif b/images/emotebutton.gif new file mode 100644 index 0000000000000000000000000000000000000000..c0a967fcce1f8269e6a05f6103fa77802ce3ada3 GIT binary patch literal 1670 zcmZ?wbh9u|RA7)~*v!Q+bLPzdXU=$fdL||&o;h$MU}whwS0g+gcq_?mL)E7P>`8?Am?C!@1`Y} z7Z<-d;}qRAvF}Wo*xrjJUU$+CUwn1xV!;yo_30Kx*^CZG9{#otj2?{%zKlKXzP`2- zdo>d{r`meXODV6=cZglO)G@wDfm1<-T~3dKc{B4S>8)FK?A)}4W8>ECyS5$Pw*G+P zs@*%b9XP)A*xvmz=MHaW*|z5RzLR^e?Dl15zj<+${F-Od&t+zrzg@ZZ!$*nFU&I7Q zt}oA`#%1Qbj9DF7Ok38UT6Q4tLc1dO;>bVj(q|of&U&;ad~&!jlc6nvuhd6`qa}6n z30;W|zFO~Drp!7UvwUTi?e3)~KUBS`zCANbs5&!3Z)vS#k!m$Nql=l3rK1aDg?dgP zV_#=rpyTBJVq3N8P4V&R%`M50Xw>0gUdOyvdc*q7Th^}UShHc%){Xl%uG+1*blc{Q zyAN$RxMP>h$$cAGH!eT4^T>|#+X9){ubf#bzx<)}BbglC7ZYq&ym=$>{sZ=SJW0TZ z%2Bs1n)pn$Mf=*DKZF=t1Rr%#v<$Uc-`1o!Q~8q*E7QVLy3dcC4i51$o4)?#rCo2! zj(dJinU-}g`f7XvJENbOmt_?jW4&5-G-G#LbhPJ$o}xyzDV|Pq)5IX?cKxPxyVot>p}1)ChIKpkuidwGyUelOYgyMV*}v`J)-#)M;u?=GSG&J&aumz_{MwsBeOpGkRHZ#GBDw<>6dcgga#$@LWc z5e;&g)aYO;Qg$PA@~<3Dw`Fe%edM?o-Z%Ihp2yt&Qt7hq`>KPNer|nZa?3j~iqYEL zFx!KTF+Zcio1L+%)x*Jkf=5AbUkXcbR5F`>U{G+#(q(fNEX;&>Q;&mr3-e~_ZCiKl z+Psxx)3zPEw;$QQ;h^H`Jv+A_JhAQgz5_Dnk8E3e{pkKv`>yWcWM^i-acQ;u+UL?Q zWEPpfTea@vCy6g#v3v6zsyD&ORBM6SyVHw~3C?fT<$j#`$2|C5fX1SG-&}#)4~t0ot8$fI{E93-uV}sH=Z)CZGUO;@rIw<(~6Hf>h>024)&^24U1>A z_cjf*v}Y{Nv8`oi?CbP#@t)#SGI3Hy)Pf8)O&y2_mol$q=2$Pae%_h0My$Yt~7xUba>-X!DNsyVmaCdua8xJ!g(7?^?Wn>%mRO gPlCL6amzya#rLHj$i(YEYh3d3rNo=JVuB3T09z2_Pyhe` literal 0 HcmV?d00001 diff --git a/images/emotes/applause.gif b/images/emotes/applause.gif new file mode 100644 index 0000000000000000000000000000000000000000..cd68c19fc5d39d45b7b3c36dfc7f1f61fa1e5918 GIT binary patch literal 2162 zcmZ?wbhEHb6k!ly*v!fBe}%xOSq%TzuKj;9XXebAX=!O^&YUqeHvSI=3>ZN1KewN2 zNU*bGfUA+70W%{51B2pE7EU7uP6i#2Vvw;6Ecpf}Jy$P%a;kIXb6fm-Wn6OQUNC%)S&&ikf{b?Xunlsjmjbk)JqCyvpPnYh4N?O@oU% zTJ>w)`y^S*^=An|9K(g`m==SRB8S#oPvboBX1=)JVTGyB9Xuqw7I4NWxid{; zX%Y#ZFKyt-m$>3*iNoY23zX&N1*lz`F~MxTZ(3pJ+qAi%>m_t-J=R)%Hqo(ov1Q9M zM}{))7thjT%GuTL6z2J}MMMh+RGYA~_4wPicV_nW+XhVQozmakGpB8)w`<_M`K*&e zmq9}b&0&oOCucHk{o*x=;VWmmFGHiqNrjb)OhHEqmc2HUa8Fa((f47e=RjR1OksT^T=lPc``zP@4t|L7I#q3v z#nxisDBMt)rNEv# zlVy_H=1vKA4&`!d)s44x0v~+k^GKO!9wRmXn3CV4mZ+s`S6#J=m78`;e|@)rOhwSN zj|wkT&o!AXs<8X~=-qdzb`yu?!k$^RY^^o+jafFH9v$|j^$mSOJ)W(0lc%)Jo-vcP h#%t2N-jF5OV{tUFMgwa!WsPRT!CYps4!#238UTQxMD_px literal 0 HcmV?d00001 diff --git a/images/emotes/astonished.gif b/images/emotes/astonished.gif new file mode 100644 index 0000000000000000000000000000000000000000..ea797084598944a0ff3a9d9a5f442f03daf31c3b GIT binary patch literal 846 zcmZ?wbhEHb6k!lySj5io|NnnuW8*Vt&ZMQK{hxJz=FFM@L1O3?ZiXLt;P9YiUQ=d+g3*#F#+R#j3S{n>^X@K|xZ)7M<~uiQubcSVAD0i#aF%-b z%u_MHMWbQD+J9ZQ*KqIQ_F;9u_kCH?;#zYyQ-u|`U&kMxm3O@LOPy(Lp+HT!jYwx# zH`vFVBB=J4RqV=suCwmay(z028cQeKT4pgpt;T1;qSI1yG!>W+Ca@dxYVYtmzxQ6A zz@L*{77~Wx)<%q8x3U=S?nnt_a&&upcFP5RJ{h$=`)NKacAbf_WH!TzGk>z^l& zXRWO(t*#ZcFfMJbXl$3@VQwU|?VXX=h;0&e(P5+{2YWoCLQDs8#J0Rn$5ZE_RqraDE!c;eusJ zx&nn>^K$QB)0&qbaf!3t`Eh%Jm65$y!;7X%I-Cqw+E`BnMXvD=U(MlMXfZ!e@zl<{ z%IpH-t37Y%{@8ox^vZW7`Sn5u4z1Ptg`Lfu42qccXJ+iW^Ur{d(^>GmkXqGFNr?T& zSdN6}95R@*GK0Hi&ZOn9cbkR2ch`B_=JM#HvxVa9`Kk^jj(caYFl1e3%2>Vj+VS0? z9gBC~$=@#U^4|N2$}=XjDyPl&6J2@ur+}ejQw3WQn?(aw+tZM2PtVsoyo@7-v8&sy zqv__nY#o8Ljhu%+T-!6ZeQ|8RI5Stf^Wx4ARz|*F2VOC4(&cQJ+{kt!DAOc3eD~AV zbG8qXzGiO^@0YL+cR702zWR=DzeGV%LtV6naT!*t^D}nc+4pee508buG457rQHniX zp(01p1rAGcy;cx*O5ePW=YIP8?cxpW?;9H}m)-7}^lsjtbCZ646RIs}5M?$sYb#eZ;g&Vv!|Dc51#l| literal 0 HcmV?d00001 diff --git a/images/emotes/joy.gif b/images/emotes/joy.gif new file mode 100644 index 0000000000000000000000000000000000000000..8abeb88ecd3e8c1056f969ea647752fb9c89aed5 GIT binary patch literal 1183 zcmZ?wbhEHb6k!ly*v!fB|NnnuW8*Vt&ZMQK&73*&Uw`NSW7of}o@!!Z^8f#T1`MG1 zpWDwhB-q(8z|~04fSHkjfkE*n3nxDVCxZ@1G00d3mSTgGo~xHWIaR|cAuAAZPn=7c zjUgj++vN?L9CJ@=&v{U6f7)PPK8MUhCX0m}*C$D4$xq7&HNH^B&}=F-W8IePdHk+& zhmJTGdkHyk-1}gWCA<0RgKvx)(ZLo#p>pv0#baOov;Z7krDHFB$CYvGKn2aKn|fjceb(zMX2Y C_Nx5= literal 0 HcmV?d00001 diff --git a/images/emotes/kiss.gif b/images/emotes/kiss.gif new file mode 100644 index 0000000000000000000000000000000000000000..91f188b7b3276b18f0e1175889653cf88a66b591 GIT binary patch literal 837 zcmZ?wbhEHb6k!lySj5Kg-=}`&%$aFvX=l!yF*Y{-4+abj42u7`{aizWogD*Qjr0td z89_onSvXS|*cfye7#J8p+8LO$Gj`oM_i*J8C&8@(>G^WfAtDKvr*<1ma(TT;kRJ2o!CRCOFdD{(-JK^jHaKC5Si-Qv_|{plmOk?tJk)3ceN$S ztXfz3Gw(=tYL2UeUGpU&rh45bMIqycjy5(<2F0H&oJOej=Vz?icrL$HY0*p-zpUpc z&V930TEXG^OHikK)w9{>OjLW?V%`^=``>!8`@<#C4%;gU6N2Q@Pbahqq{v13iwe8X z4U7z1agyPm%=8yimwt8JtSx3_*4yg7>(*66KabtfHFe&}V&=Lo7KN5N+)O>(5I1n4 zx&agh{{-4B;x!J5J<_|ze7qvjxvMK+jpD;ytD>YB0VeZK*+WdZ2yx-lhf6oIyRuzuYV?rE18VwUYmp*jcZJNovvT(xPn9`V)imOaY53c>#b2{&| zf~QoY?sJw{qrUPhBC4{N$!ajdm_h25SINTu8+L literal 0 HcmV?d00001 diff --git a/images/emotes/laugh.gif b/images/emotes/laugh.gif new file mode 100644 index 0000000000000000000000000000000000000000..4da793067ddb45420914fa9abdad6ca0c8154c81 GIT binary patch literal 2077 zcmZ?wbhEHb6k!ly*v!fBzggt}wryLjtUiQ=&73(iEiLWLnKQ=5#{a>90Rt%h=k{|A z33hf2a5d61U}j`sU{L(e9pM|G;F4I97~vaWV_#8_n4FzjqL7rDo|$K>^nUk#C56ls zTcvPQUjyF)=hTc$kE){7;3~h6MhI|Z8xtBTx$+|-gpg^Jvqyke^gTcyO5 z{G?Q?ykaYmu)dN4SV>8?t&$^1LIGr_bAC~(f~lUdZnA-)p@Ny2o}r1UnVF%Ef{}rt zk-mYEzJZypp^25LnU#^b0u(6ODcBUHq*(>IxIyhIN=dU-$|xx*u+rBrFE7_CH`dE9 zO4m2Ew6p}7VPvFRl#-@fT$xvrSfQI&tPC^3CAB!YD6^m>Ge1uOWMX1cerbuVk`mO| zirfNUU%0_}#d=^5=_Tjq>Q^Kd=o{)8=;!8w`~~)KaY;}r!Wmdq7l%|9r0NHy7U!21 zC8q|ZrYR#?h2J8O!UA7kEB~U*^vt}(9GCp$(%jU%5>FRfrHb4Fy_C!pD-&}wS4#_L zXG=3zH&+WoLrViAXGa4gH$y`cH%9{_Czu)7^tu>0I~iKKnz*`Ini(3pS~yx58#!CL zIvYD#y1JMd7{c^=<`tJD<|U`X?9I$fv4ZL~#;e!LxhOTUB)=#mKR*YS0s=DfOY(~| z@(UE4gUu8)!ZY(y^2>`gLD2&adn=dJqRg_?6t|-MTm^9WT4iFfpRm6ndQ->=GkrS+ z8+}mXKuRnyp&%DG5EGoPL22Ah0g-u9^HOY;ij?f_82|tK`{(zspFh5T`}*bcr;i`r zzkB=U^{baJoVgsyL#pFrHdENpF4Zz^r@34jvqUEVojbN~+qz}*ri~lcuUorj^{SOCmM>enWbvYf3+B(8J7@N+nKPzOn>uCk zq=^&y`+9r2yE;4C+ge+in;IMH>uPJNt12tX%Sua%iwXi?qaq{1!$L!Xg8~Em{d|4Ay*xeK-CSLqog5wP?QCtVtt>6f%}h;lT# zwKO%<)l^lKl@t}^Y@7%@A-)Ty)$+;U(906{F_&v7+B<{@Bku>)_2% zeL}9^!%}PVw$#&|5lVak%RU@& zq4ZY|R?QXQZP00KbWwC|?@$9---pBcQQwc0xMwv&P5H3~Cp}m1waE4@+TRk`a4b=< zgJYRm>!z#GL99vB-RE5|pZ#Im0)fEUf7?zZTx9W4O5el0zH`F2zD60t(5+9IHZUtO j-0M1OW~9^jZYq!SVwLn2x8LvR&O3kq$Nz?UK?Z98X|n_( literal 0 HcmV?d00001 diff --git a/images/emotes/love.gif b/images/emotes/love.gif new file mode 100644 index 0000000000000000000000000000000000000000..b46c317533b84320e86c8ac00c0f991ce185fcb6 GIT binary patch literal 1853 zcmZ?wbhEHb6k!ly*v!T7f60;~l9KW{2vS$P=Vrq zZa>$MU}whwS0gd$U}a!tU|``4kkp)PdQ4525h2UMz{|kJz|O$HA{($WWr>#itda@- zoSPa|G=iS|n5wc!!Chzfr1Z;zARU}w^B5Rd8H5@585meX0z5;PP5pW)<%?VR#DaMh z4U=cgd3eI>*bb!<+1)R6-xvLPWizj_)S0FH*i1c6AC;-bv(CRmgX4$}_pcO@2!3Oi#or=OM*V7wbx>HPXl<kSCs-jUj)fVx7#LXe0(f0RIlf-A zvf^z~6}oXGb)hKNjb}}IFXv^cUf#T{aNf^zLY?d$JiBU+KFJXPdkbL%Bn({xPAab6 z`X$UsyY@VvD9-eY@X21Blq?C!b^Mk0PTJynZE{ijHn|WVutv+|} z)XU;(iH-Zzed3E$Ac4ba#WZ+=W%M?jrpU*L4{^AtV)aA<| z=?2};HDviYjHmOB@WH1^8-qE`y!9sYKHOO8J8#v^>~b-~I`tP(%3=&Bn%<|>a0YX5 zHaRx5Mm8mORWfj}rzHx-uuu1&F(XDdcHTU3w0IO@5Mf|o84aq@prS&mLMtBQV8vs# zzGQ`Hr=ePh>$K++gH}f$U7w<|qx;S8x!3LH6hwHP$T^~NuHcK}ltar6MW?V8URwER znR7wlio1@dWyR;;JRC9Y@6I!am6_J9JKir;SQ~#owxTLl6q1n9tTcpK39WR>It@kH z(^c4&)HxUJE%Cd3vwOKUuha6!sRHdwE@@1$Gz6Q1UarfanKCbSd#&((V^UZ36lKS84}C39ZJ zsBs45v57#SE?m<(J&!kl0OSQz*jcoH&^OR&P%4F&&8EO^A!Zx(p?>2?hvw=tvg3(){ci^i>DRe zUbTg3kzU&Fqblo|kPSx!K^n|ix+m|uGBk6r$T`en^6GVnKKRLGMb-lOUu&;%%IA5k z`N?FLcusw{DA%gl4_c~^1^8)nO0963ebj;5agW+A*MnPPoTuN^>-6(ozq)k#K?n7H z%KB`afn`kQlI5vMqJr2$Bo$_LwYp@5-~j`sM%QV}Cj_mIKDyq7!9wcI@7~kf<`n#> z{@LNta7O%tY~k6YM`0Qd@|Z%_t?~(6tD2jTJ+-%Worz$@InH~pqZV08?0NrvS7(vT zn|FcXB8&|c`7P~2V4EQ!%FMvaz|X+Wz`(+5(Bh&!)eIc(C%_Sl9$OhOx50yD1LupQ zVUJB$bV}VRkK8Z*;Qy9WI)WEosvV8h4%G1QVtTYBGjmni3bBSL#!dV79pn+1%5;*+ zu_D%Nem!G+gZj>;@4s8VRug&rDbriTp1Go;Nw_kz``KRAi%)Dq67qlr(f>>J*KP1q*xvcV2f83x9Cxh|~0094^mU3d(dZ%J4j>*mKv#lCP{H(4t0& zv9YqCtrIyFA+k;!%x*(TVZj0x2Co)_tfjNIWScQK2%P!acDaV(glXK`2ZzpZEmJW} zvz*0p{81{qCBiqS?& r83-#iVOD}d30z>ACTMeSx$+^2Q6S=LEo+nC$9-G^*B84mFjxZs($Asq literal 0 HcmV?d00001 diff --git a/images/emotes/yippee.gif b/images/emotes/yippee.gif new file mode 100644 index 0000000000000000000000000000000000000000..c7fac01b58faa3eb0e19779f435c82c4e181d3c7 GIT binary patch literal 675 zcmZ?wbhEHb6k!lySj5UObLPynw6rs4&KMgT{|5sGFi`x@?dKX2?CcoeYNTht%*epN zp!k!8la+y$K?kG&q@95|C1Tf|e+EpPjGX8B((~009VC@`?t_ zRHZS!{j9wx=io8H3gawpPHYw@fGzgoOsZ{Q@o76PrCz~fVWzf{#l@P>;LNU9ckcbD z6)T%}e)&B9<6M0GEXnC=G7PrODn66nY)N2RnWS(cXmMb8c<6yX_q!%<1daDEn$324 z?Yjh~bK8y84qm^8-O|j6U3bnsSowp2b1F}IzL>O&K*Hsz-UgGLU$4@NT9KX0u95pb z+mqkqE{DaZjcnqR(kuivq^YC}b?R7oU(w>YvT5P2D8`IP9?ez0A0rNzE{IOrvgE<5 z!n2cBg-#3q{gK~7&!MJKSF;_v?Ku&vHk`|66iS+@?3cCt#JO*_N-J1ge(~ybFM2%l zoQdp_jJ21O?Vjws=|8n`qXr9O;)Ep4>XXV{Cqvd6g}zGnUfi_8w{geqk}cC7ZJCjk zX?b1u^rBflv!8sa@`;mJ@wP@i+`hVqQHQ&|ys;4+=B#Luoq^KSs#80Chn9zt){@!*8J>+X2z{~{!>M!wAQXMp3@YnJA3sSkJX`Z%Vlq@tIU+z Xe13Jg$^`W;UCS!Qs)p7!kngPlaE%0S literal 0 HcmV?d00001 diff --git a/images/pitcochatmenu.png b/images/pitcochatmenu.png index 51c92554a675cb5f7b0ecda8ec18c4fa2c0352f8..ef678ebf55e7ac98f3b3dcbf83baba5f021e15f0 100644 GIT binary patch delta 4225 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 Gp$P!xs?&b} diff --git a/images/sendbutton.png b/images/sendbutton.png new file mode 100644 index 0000000000000000000000000000000000000000..d59c7cf9d0aa3c2c837ccc9e2f5c30c8b155beb7 GIT binary patch literal 430 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4mJh`hT^KKFANL}jKx9jP7LeL$-HD>V9fAz zaSVxQJv)7G?hyl?)~C*F9?1$z#VsD(>N((My`=DEV$80cvNtMPviVOi=`rs($>t}_ zYSh1^PDi!!>_MZY-)Fj?3S(wy%UI0Zdi966{}ma*)w`mz&E*fV-w?R?<#UiIgJ8M9 z^2N`DOVXP3toF?p|2+5USw~F)+x?&4n5qpGgzx7Sd78co@pYF!p!GRDvT{> ziWOei30&!Ve9mK+_-Yk~{lQDrecmp6qo=Xdo;~4z)9vHStzLe88o5{h?DJ=xrXT*5 z-He^T{(HOOx))1sS;W78vx7sy_e1Db?iZJfP8s&FbzEHko;f7cSRj~L^$1hTtu;&j zJ$Fl)QaA0&dG7+g@ADEE?>N;>U2m}|^!}}bYb@S9^Vld{!Y`1-cE>5?j)eH@!14Rn-kx%3>0g0{lvM|R2z&`=>Oa-6x?rtZ29LP(UDlX{Gt0xyJ$%Nl m@H`;UiI?GuUEk_oO!GG$|NN?LUp4~+1B0ilpUXO@geCxJyu8c+ literal 0 HcmV?d00001 diff --git a/images/sendbuttonhover.png b/images/sendbuttonhover.png new file mode 100644 index 0000000000000000000000000000000000000000..247834fb3dac7df23d15fbbd9f02bef4a6482cc2 GIT binary patch literal 413 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4mJh`hT^KKFANL}jKx9jP7LeL$-HD>U<~(k zaSVxQJv-en@2~-n>(c|#`VSYLFl1UEQ^O;F>iC66rXLKu`1ZMMXLuLH%Htd)qO1Pb zTVUp;`a}=4ECvOE+>(Wxr2Z^h`poc{^WFBu8wM9+(G+s&&u7|Of8aC+}Pd6#wTmokQL+CP5%2r-{qzwhevOLckkBbc`=__fqETUJ{5 z-PVtL^h*0V5^~LXZz&bC?J>+_=#H~vU%_{_VJTZyN9oC}k-mS=JBmz+)X$uA{f*qa z@+MXf_J>wG;_`B?E@y20?y*t0#9tswuqgE0+QN+ze%AiFrL!0_duL5OVZvz{tatD7 zu4R@9tp^3Zgff|@DunI`6ECQWM7;UC^No6rSc=3A2s`v}fPA)Am T`Yp=9z`)??>gTe~DWM4f38}AP literal 0 HcmV?d00001 diff --git a/index.html b/index.html index 0ba2448..3af149e 100644 --- a/index.html +++ b/index.html @@ -8,7 +8,7 @@ - + @@ -213,15 +213,31 @@
- UPDATES + GUESTBOOK
+ +
+ + + + + + + + + +
+ + +
diff --git a/js/guestbook-worker.js b/js/guestbook-worker.js new file mode 100644 index 0000000..ebc62fa --- /dev/null +++ b/js/guestbook-worker.js @@ -0,0 +1,126 @@ + +const CORS_HEADERS = { + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', + 'Access-Control-Allow-Headers': 'Content-Type', +}; + +const KV_KEY = 'messages'; +const MAX_MESSAGES = 500; +const MAX_NAME_LENGTH = 30; +const MAX_MESSAGE_LENGTH = 500; + + +function jsonResponse(data, status = 200) { + return new Response(JSON.stringify(data), { + status, + headers: { 'Content-Type': 'application/json', ...CORS_HEADERS }, + }); +} + +function generateId() { + return crypto.randomUUID(); +} + +function formatDate() { + return new Date().toISOString(); +} + +function sanitize(str) { + return str + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); +} + +async function hashIP(ip) { + const encoder = new TextEncoder(); + const data = encoder.encode(ip + '_guestbook_salt'); + const hash = await crypto.subtle.digest('SHA-256', data); + return Array.from(new Uint8Array(hash)).map(b => b.toString(16).padStart(2, '0')).join('').slice(0, 16); +} + +async function getMessages(env) { + const raw = await env.GUESTBOOK.get(KV_KEY); + return raw ? JSON.parse(raw) : []; +} + +async function saveMessages(env, messages) { + await env.GUESTBOOK.put(KV_KEY, JSON.stringify(messages)); +} + +export default { + async fetch(request, env) { + if (request.method === 'OPTIONS') { + return new Response(null, { headers: CORS_HEADERS }); + } + + const url = new URL(request.url); + const path = url.pathname; + + if (request.method === 'GET' && path === '/messages') { + const messages = await getMessages(env); + const sanitized = messages.map(m => ({ + ...m, + likes: Array.isArray(m.likes) ? m.likes.length : (typeof m.likes === 'number' ? m.likes : 0), + })); + return jsonResponse(sanitized); + } + + if (request.method === 'POST' && path === '/messages') { + const body = await request.json().catch(() => null); + if (!body || !body.message || !body.message.trim()) { + return jsonResponse({ error: 'Message is required' }, 400); + } + + const name = sanitize((body.name || 'Anonymous').trim().slice(0, MAX_NAME_LENGTH)); + const message = sanitize(body.message.trim().slice(0, MAX_MESSAGE_LENGTH)); + + const messages = await getMessages(env); + const entry = { + id: generateId(), + name, + message, + date: formatDate(), + likes: [], + replies: [], + }; + + messages.unshift(entry); + if (messages.length > MAX_MESSAGES) messages.length = MAX_MESSAGES; + await saveMessages(env, messages); + + return jsonResponse(entry, 201); + } + + const likeMatch = path.match(/^\/messages\/([^/]+)\/like$/); + if (request.method === 'POST' && likeMatch) { + const messageId = likeMatch[1]; + const ip = request.headers.get('CF-Connecting-IP') || 'unknown'; + const ipHash = await hashIP(ip); + + const messages = await getMessages(env); + const msg = messages.find(m => m.id === messageId); + if (!msg) return jsonResponse({ error: 'Message not found' }, 404); + + if (!Array.isArray(msg.likes)) { + const oldCount = typeof msg.likes === 'number' ? msg.likes : 0; + msg.likes = []; + + for (let i = 0; i < oldCount; i++) msg.likes.push('legacy_' + i); + } + const alreadyLiked = msg.likes.includes(ipHash); + if (!alreadyLiked) { + msg.likes.push(ipHash); + } + + await saveMessages(env, messages); + + return jsonResponse({ likes: msg.likes.length, liked: true }); + } + + return jsonResponse({ error: 'Not found' }, 404); + }, +}; diff --git a/js/guestbook.js b/js/guestbook.js new file mode 100644 index 0000000..a788374 --- /dev/null +++ b/js/guestbook.js @@ -0,0 +1,244 @@ +const GUESTBOOK_API = 'https://guestbook.bug-dev-mail.workers.dev'; + +const EMOTES = { + ':happy:': 'images/emotes/joy.gif', + ':love:': 'images/emotes/love.gif', + ':laugh:': 'images/emotes/laugh.gif', + ':shock:': 'images/emotes/astonished.gif', + ':hello:': 'images/emotes/wave.gif', + ':kiss:': 'images/emotes/kiss.gif', + ':clap:': 'images/emotes/applause.gif', + ':dance:': 'images/emotes/caramelldansen.gif', + ':yippee:': 'images/emotes/yippee.gif', +}; + +function replaceLinks(text) { + function cleanUrl(url) { + return url.replace(/&/g, '&'); + } + var result = text.replace(/\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)/g, + function(_, label, url) { + return '' + label + ''; + }); + result = result.replace(/(^|[^"'])(https?:\/\/[^\s<]+)/g, + function(_, pre, url) { + return pre + '' + url + ''; + }); + return result; +} + +function replaceEmotes(text) { + let result = replaceLinks(text); + for (const [code, src] of Object.entries(EMOTES)) { + const escaped = code.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + result = result.replace(new RegExp(escaped, 'g'), + `${code}`); + } + return result; +} + +function timeAgo(dateStr) { + if (!dateStr.includes('T') && !dateStr.includes('-')) { + return dateStr; + } + const d = new Date(dateStr); + const now = new Date(); + const diffMs = now - d; + const diffMin = Math.floor(diffMs / 60000); + const diffHr = Math.floor(diffMs / 3600000); + const diffDay = Math.floor(diffMs / 86400000); + + if (diffMin < 1) return 'just now'; + if (diffMin < 60) return diffMin + ' min ago'; + if (diffHr < 24) return diffHr + ' hr ago'; + if (diffDay === 1) return '1 day ago'; + return diffDay + ' days ago'; +} + +function createMessageBubble(msg) { + const entry = document.createElement('div'); + entry.style.setProperty('--col', '#90c9ff'); + + const nameDiv = document.createElement('div'); + nameDiv.textContent = msg.name || 'Anonymous'; + + const dateDiv = document.createElement('div'); + dateDiv.className = 'gb-date'; + dateDiv.textContent = timeAgo(msg.date); + + const msgDiv = document.createElement('div'); + msgDiv.className = 'gb-msg-content'; + msgDiv.innerHTML = replaceEmotes(msg.message); + + entry.appendChild(nameDiv); + entry.appendChild(dateDiv); + entry.appendChild(msgDiv); + + const actions = document.createElement('div'); + actions.className = 'gb-actions'; + + const likeBtn = document.createElement('button'); + likeBtn.className = 'gb-like-btn'; + const likeCount = typeof msg.likes === 'number' ? msg.likes : (Array.isArray(msg.likes) ? msg.likes.length : 0); + likeBtn.innerHTML = ` ${likeCount}`; + likeBtn.onclick = () => likeMessage(msg.id, likeBtn); + + actions.appendChild(likeBtn); + entry.appendChild(actions); + + return entry; +} + +async function loadMessages() { + const picto = document.querySelector('#pictochat-content .picto'); + if (!picto) return; + picto.innerHTML = ''; + + try { + const res = await fetch(GUESTBOOK_API + '/messages'); + const messages = await res.json(); + messages.forEach(msg => { + picto.appendChild(createMessageBubble(msg)); + }); + } catch (e) { + const err = document.createElement('div'); + err.style.setProperty('--col', '#90c9ff'); + const nameDiv = document.createElement('div'); + nameDiv.textContent = 'system'; + const msgDiv = document.createElement('div'); + msgDiv.textContent = 'No one has commented yet. Be the first!'; + err.appendChild(nameDiv); + err.appendChild(document.createTextNode('')); + err.appendChild(document.createElement('br')); + err.appendChild(msgDiv); + picto.appendChild(err); + } +} + +async function sendMessage() { + const nameInput = document.getElementById('gb-name'); + const msgInput = document.getElementById('gb-message'); + const sendBtn = document.getElementById('gb-send'); + + const message = msgInput.value.trim(); + if (!message) return; + + sendBtn.classList.add('gb-disabled'); + try { + await fetch(GUESTBOOK_API + '/messages', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + name: nameInput.value.trim() || 'Anonymous', + message: message, + }), + }); + msgInput.value = ''; + await loadMessages(); + setTimeout(replaceTextWithEmotes, 500); + } catch (e) { + + } + sendBtn.classList.remove('gb-disabled'); +} + +async function likeMessage(id, btn) { + try { + const res = await fetch(GUESTBOOK_API + `/messages/${id}/like`, { method: 'POST' }); + const data = await res.json(); + btn.innerHTML = ` ${data.likes}`; + btn.classList.add('gb-liked'); + } catch (e) { + + } +} + +function toggleEmoteSelector() { + const els = document.getElementsByClassName('gb-toggle-div'); + for (let i = 0; i < els.length; i++) { + if (els[i].style.display === 'none' || els[i].style.display === '') { + els[i].style.display = 'block'; + } else { + els[i].style.display = 'none'; + } + } +} + +function addText(text) { + const textarea = document.getElementById('gb-message'); + textarea.value += text; + textarea.focus(); +} + +function replaceTextWithEmotes() { + const picto = document.querySelector('#pictochat-content .picto'); + if (!picto) return; + + function replaceTextNodes(element) { + element.childNodes.forEach(function (node) { + if (node.nodeType === Node.TEXT_NODE) { + let replacedText = node.textContent; + let changed = false; + for (const [key, src] of Object.entries(EMOTES)) { + const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + const imgTag = ""; + const newText = replacedText.replace(new RegExp(escapedKey, 'g'), imgTag); + if (newText !== replacedText) { + replacedText = newText; + changed = true; + } + } + if (changed) { + const newElement = document.createElement('span'); + newElement.innerHTML = replacedText; + node.parentNode.replaceChild(newElement, node); + } + } else { + replaceTextNodes(node); + } + }); + } + + replaceTextNodes(picto); +} + +(function () { + const picto = document.querySelector('#pictochat-content .picto'); + if (!picto) return; + + const sendBtn = document.getElementById('gb-send'); + if (sendBtn) sendBtn.addEventListener('click', sendMessage); + + const msgInput = document.getElementById('gb-message'); + if (msgInput) { + msgInput.addEventListener('keydown', (e) => { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + sendMessage(); + } + }); + } + + const emoteBtn = document.getElementById('gb-emote-toggle'); + if (emoteBtn) emoteBtn.addEventListener('click', toggleEmoteSelector); + + document.addEventListener('click', function (e) { + const selector = document.getElementById('gb-emote-selector'); + const toggle = document.getElementById('gb-emote-toggle'); + if (selector && selector.style.display === 'block' && + !selector.contains(e.target) && e.target !== toggle) { + selector.style.display = 'none'; + } + }); + + const emoteItems = document.querySelectorAll('#gb-emote-selector .gb-emote-item'); + emoteItems.forEach(function (img) { + img.addEventListener('click', function () { + addText(img.title + ' '); + }); + }); + + loadMessages().then(function () { + replaceTextWithEmotes(); + }); +})(); diff --git a/style.css b/style.css index edfef5d..cc425e4 100644 --- a/style.css +++ b/style.css @@ -847,10 +847,10 @@ a:hover { border-radius: 18px; box-shadow: inset 0px 0px 5px 0px rgb(202, 202, 202), 0px -4px 5px -3px #bbbbbb, 0px 5px 1px -3px #ffffff; z-index: 1; - overflow: hidden; } #pictochat-content { + position: relative; display: flex; flex-direction: column; align-items: center; @@ -936,8 +936,9 @@ a:hover { color: var(--col); } -#pictochat-content .picto > div > div:last-of-type { +#pictochat-content .picto > div > .gb-msg-content { padding: 3px; + padding-top: 20px; text-indent: 0px; line-height: 18px; } @@ -975,7 +976,7 @@ a:hover { calc(100% - 4px) calc(100% - 0px), calc(100% - 4px) calc(100% - 2px), calc(100% - 2px) calc(100% - 2px), calc(100% - 2px) calc(100% - 4px), calc(100% - 0px) calc(100% - 4px)); } -#pictochat-content .picto > div img:first-of-type { +#pictochat-content .picto > div img:first-of-type:not(.gb-emote) { position: absolute; top: 0; left: auto; @@ -1301,3 +1302,198 @@ a:hover { letter-spacing: 4px; text-shadow: 0 0 3px #93bdec66; } + + +#gb-name { + position: absolute; + top: 153px; + left: 24px; + height: 17px; + width: 125px; + font-size: 11px; + font-family: 'DSWare'; + color: #30baf3; + font-weight: 500; + border: rgba(255, 0, 0, 0) solid 1px; + background-color: rgba(255, 255, 255, 0); + outline: none; + z-index: 10; +} + +#gb-name::placeholder { + font-size: 10px; + font-weight: normal; + font-family: 'DSWare'; + color: #30baf3; +} + +#gb-name:focus { + outline: none; + border: 1px solid rgba(163, 197, 11, 0); +} + +#gb-message { + position: absolute; + top: 167px; + left: 20px; + width: 200px; + height: 64px; + resize: none; + padding-top: 1px; + line-height: 1.6; + font-size: 10px; + font-family: 'DSWare'; + color: #494949; + border: rgba(0, 255, 21, 0) solid 1px; + background-color: rgba(255, 255, 255, 0); + outline: none; + z-index: 10; +} + +#gb-message:focus { + outline: none; + border: 1px solid rgba(163, 197, 11, 0); +} + +#gb-message::placeholder { + font-size: 10px; + font-family: 'DSWare'; + color: #b0bcc8; +} + +#gb-send { + position: absolute !important; + top: 244px; + left: 217.5px; + width: 32px !important; + height: 32px !important; + margin: 0px; + padding: 0px; + cursor: pointer; + z-index: 100; + image-rendering: pixelated; + background: none; + border: none; + animation: gb-sendPulse 2s infinite ease; +} + +#gb-send:hover { + content: url('images/sendbuttonhover.png'); +} + +#gb-send.gb-disabled { + opacity: 0.4; + cursor: default; + animation: none; + pointer-events: none; +} + +@keyframes gb-sendPulse { + 0%, 100% { opacity: 1; } + 30% { opacity: 0.3; } +} + +#gb-emote-toggle { + top: 275px; + left: 217.5px; + cursor: pointer; +} + +#gb-emote-selector { + display: none; + position: relative; + padding: 8px 6px; + z-index: 100; + top: -52px; + left: 15px; + width: 205px; + box-sizing: border-box; + image-rendering: pixelated; + animation: gbFadeIn 0.3s; + background: linear-gradient(90deg, #fafbfb 0%, #f7f7f7 50%, #fafbfb 100%); + border: 1.5px solid #c9c9c9; + border-radius: 18px; + box-shadow: inset 0px 0px 5px 0px rgb(202, 202, 202), 0px 0px 5px 0px #dadada; +} + +#gb-emote-selector.gb-visible { + display: block; +} + +.gb-emote-item { + width: 22px; + height: 22px; + cursor: pointer; + image-rendering: pixelated; + border-radius: 3px; + padding: 2px; + transition: background 0.1s; +} + +.gb-emote-item:hover { + background: #e0f0ff; +} + +.gb-date { + position: absolute !important; + top: 2px !important; + right: 10px !important; + left: auto !important; + width: auto !important; + height: auto !important; + text-indent: 0 !important; + font-size: 9px; + color: #494949; + font-family: 'DSWare'; + z-index: 1; + background: none !important; +} + +.gb-date::before, +.gb-date::after { + display: none !important; +} + +.gb-emote { + display: inline; + height: 16px; + width: 16px; + vertical-align: middle; + image-rendering: pixelated; +} + +.gb-actions { + display: flex; + gap: 6px; + margin-top: 3px; + padding-top: 0; + padding-left: 2px; + text-indent: 0; +} + +.gb-like-btn { + background: none; + border: none; + font-family: 'DSWare'; + font-size: 9px; + color: #30baf3; + cursor: pointer; + padding: 0 2px; +} + +.gb-like-btn:hover { + color: #0080d0; +} + +.gb-liked { + color: #ff6b8a !important; +} + +.gb-like-icon { + font-size: 10px; +} + +@keyframes gbFadeIn { + from { opacity: 0; transform: translateY(4px); } + to { opacity: 1; transform: translateY(0); } +}