From 4c4afe3bdf4ff50ab26c7d140155ebf8f4d0907c Mon Sep 17 00:00:00 2001 From: versustunez Date: Sat, 8 Aug 2020 21:58:15 +0200 Subject: [PATCH] WIP --- build/task/js.js | 4 +- empty.txt | 0 favicon.ico | Bin 0 -> 15086 bytes favicon/android-chrome-192x192.png | Bin 0 -> 1108 bytes favicon/android-chrome-512x512.png | Bin 0 -> 2507 bytes favicon/apple-touch-icon.png | Bin 0 -> 1115 bytes favicon/favicon-16x16.png | Bin 0 -> 311 bytes favicon/favicon-32x32.png | Bin 0 -> 407 bytes favicon/mstile-150x150.png | Bin 0 -> 890 bytes favicon/safari-pinned-tab.svg | 1 + index.html | 13 +- manifest.json | 21 ++ out/gui/wave2d.json | 2 +- out/js/scripts.js | 307 ++++++++++++++++++++------ out/js/scripts.min.js | 2 +- raw/gui/wave2d.json | 31 +++ raw/javascript/FetchHandler.js | 36 +++ raw/javascript/app.js | 12 +- raw/javascript/gl/Camera.js | 78 +++++++ raw/javascript/gl/glUtils.js | 105 ++++++--- raw/javascript/gl/handler.js | 7 +- raw/javascript/gui.js | 3 + raw/javascript/keys.js | 6 +- raw/javascript/player.js | 10 + raw/javascript/playerConfigHandler.js | 2 +- raw/javascript/template.js | 12 +- raw/javascript/visuals/wave2d.js | 36 +-- shaders/wave2d.vert | 2 +- sw.js | 44 ++++ 29 files changed, 595 insertions(+), 139 deletions(-) create mode 100644 empty.txt create mode 100755 favicon.ico create mode 100755 favicon/android-chrome-192x192.png create mode 100755 favicon/android-chrome-512x512.png create mode 100755 favicon/apple-touch-icon.png create mode 100755 favicon/favicon-16x16.png create mode 100755 favicon/favicon-32x32.png create mode 100755 favicon/mstile-150x150.png create mode 100755 favicon/safari-pinned-tab.svg create mode 100755 manifest.json create mode 100644 raw/javascript/FetchHandler.js create mode 100644 raw/javascript/gl/Camera.js create mode 100644 sw.js diff --git a/build/task/js.js b/build/task/js.js index ec727ac..58439ba 100644 --- a/build/task/js.js +++ b/build/task/js.js @@ -16,10 +16,12 @@ const config = { src: [ basePath + 'utils.js', basePath + 'gl/glUtils.js', + basePath + 'gl/Camera.js', basePath + 'template.js', basePath + 'gl/handler.js', basePath + 'audio.js', basePath + 'FileHandler.js', + basePath + 'FetchHandler.js', basePath + 'playerConfigHandler.js', basePath + 'player.js', basePath + 'gui.js', @@ -42,7 +44,7 @@ function build() { .pipe(concat('scripts.js')) .pipe(gulp.dest(config.dest)) .pipe(rename('scripts.min.js')) - .pipe(terser()) + .pipe(terser().on('error', console.error)) .pipe(gulp.dest(config.dest)); } diff --git a/empty.txt b/empty.txt new file mode 100644 index 0000000..e69de29 diff --git a/favicon.ico b/favicon.ico new file mode 100755 index 0000000000000000000000000000000000000000..07f434f80dee5c8747cdfa46a0e1dece8f293503 GIT binary patch literal 15086 zcmeI3ziL!b5XO%r5D>%#i5_btX!(o19PBf5Jk;xiBk1OkCTAP@)y0)apv5C{YUf&UbN z8@3l*BJ914pPRl=3U)SPo;Y1c^74AQepYSOl2pEQFR8U_yq7UHW-QSSGj17wE;y-& zFRv$^x~07frN$=uP?NqnVWX#K>VG$;7xtVc%};F4n0Z%?eO;YPb699Afg|;(J1wyT zIi))|YkvIf--emFrXuRl|04A+Ge5&AzlU-`Vs}XEo( z2m}IwKp+qZ1OkCTAP@)y0)ar_Zz3=pQA$@w&eVH1ZJwpNu7@Vq2d8pvP!CK$*{SPe zlgHaD^~rKdgX;E3*1wkO;ZjO~T?@Tq;)hGlQR3cYqyU$O@G9Cxm@Ajq`YXo;>bgPY-O6YOn{k{BDS8>n~q^ zU)V+Lnd3w+Y!G*$^T<&6&};?QXm>DRj}mD=zZYla$j=&8EpU}h5+8F(Cx)4j*mEk)q!{nx?3oy^`*ZH|I_IE3AP@)y0)c;o z!0Goh=lI;M<_5>rI()ca?HpFscJ+2x8TW_#EA!{5Gz#Va$vVbeUESkfJR9T^xl_H+M9WCijWi-X*q z7}lMWc?smm1^9%x`eYUYnYe)0p1-C*InI(GzhH(%zKWkR+OF>Y%@lc@fq^;3)5S5Q z;?~<4XZwW=C62Yv;-dF}n>`PLP3=ni;LFIcks?!1jB|KB)kncklm-eSf!|MYSf z>si|smnd)B!fEODa{Kk~OCB7U7wrO+JN>DXYmVTi$*g&tlAXGRc5|NVcg7~1=brOo z37czppQ2*4Ytfv#pH8zlE@ZNq$Z$t0ELq*vBBJwo!B$V@Mn5aDnOc!?%PtCwUbZ-Q zxh~)64AW2ki1`-WQ;x($g$WwhJ58>V%ko04N%py6BGcM5 zd4sG^_?M_2PZM0U@t!a5LhF^UKThS1`1I(Zqj^Bit3bEd6CN2H8QZQ3*RF8?#n+n8 z;%_RJpyu0iQ-0-CKaM%g2PQ~NK6ZU~Q9S!ImDj9!tm|jIiVdAPMJiPJK;8e-Ne^5u zF0%Z6(P6<_%j_R=+()xajE~RTec(V&_P%?C&!tzNEuXl#Ve6(T_jq;)TiJT;JD(P> zAIqYn_Vjh%hUa!qj-R?+%+A>D`Sa`CkI(qkcOJ@%)pxkP?rm$Ce!k`T-yt%+yO;Pi z98E~I*Yf-6WuedB&)u3kx8drN&^NUo-+cVDeDbTHRM8Eyw(PpEnI3mx|IV3rg;)Ay zv6z(}zppU&*VJuu_T2K=E*Pq?WtK^?<_EDR1(pA4J*ul~m@|25XhPn) zB~K-|zcL+WFUS#IVVrqOGjM%^YRUh<8#%XR`OG?I`DoqMSy5ZJb6%M>Wo^)!l2e@} z8#r_ByvtZvxX_eIai#Ifm!U@tUB0S?>WlYIve_OU^!DqDPTyFcRWfFa7|pKrK1;f^ zD8+r5$PKU7ZcoYZWpCwTJUm5MncOFD^3iMYn)`WJ&U_jH7(a*>Y3P! zhTKOktzK?;|Dan(vx36DHJwbwo)2d4yeDfj`GE50nQ#2(us+)8`QThp?!@?Wx=e5T z;u5|aZ{py&!^)H>v;T+mi<8xr@(ZOcF*kLgM0QTG2 zo^t{KurLJ!P>3+VTsu330UBtDw*-J^x#HWtB0>g5JK0zPwSDpnLXdW}cfr{QMGDiu zy~m_vMWKSlI5{{2VRD~ptvjj);{X6YZFkPnC1GM^ikUQFh)(xi7TwYdYI1-@f$Shl z=?&fmuNKUZZF~|!SC)eR`Mh!dub~I0Uzc1%?{yd(Gp=<)|2F6!<3QZb9DA}*pq|UB z+T5Mqp8s~9jzspiWO^m1K2B*U74?GWR#s2{(hj=C>mTDQ>~iS5*p}j9*5wyti-MB3 z>$F6!rQD}M`&@!R$#i%8`;IJqYA=2x`j_M!b;y_kE8IIyIqyhc@QmC=0}g8_3Zo85 zSlFIw+KgBq;>*q#%qMx}qTf_*%(U^iv%E;O-LY$&r7@pG1yUC<@nJOuW`Fd2za7Sh zuP`v#=`!jkQpYP9z$RYkHW9^d2woGIo}=X=5g!r_d}hV31ssqx3-mqY-<{M5yv=4W z6r`2ETZ{OBpq?hDyo86Dg548(Qn7lUfwFBOrh8)^|FWdq!1YbqkHf8o3+; z!qV1*Q+#<~0i1b)GWzzUkN)gjCaAFu%^Z#=Fwmzp<%_>&g3d`>vSz#|jL~PIME5Jp zGg-7i^O0$j)Z1X5BJ3mbGnzRystx>>`e-l4$OsaLB>1hY2;wQA0*i61Z$sdlRsMT- zmf{d1iGQ}&y=6t~VeHP;wyCB!%AemOziQu|kX?yuHYh!!jU)Egq6C|!w{80Q3B6R(R_rySXFBpuVsIdm0~ z#rEp?#<^{g{qaJBVYV)&5#Cj7m9FL|eQIkni>TlUohu(3MC5otiXsbbN0G9wH|LD( zLD%_e&E(WajPUdxmI-7u50&SCpyLO&%nxy+1hPSrczfS8N2@(QwIkxb{9g3f@^CZ7 zM|9(nfsVkW1Tin!YCjH@G{}@WWg~eJdVPoL%QShyLSnKtC~s3c>OAf)Xr`5e-P>OJ zo#!4p{j|7V7mi`TyS^(8tMn0}Y2K1+OBwclv-{@BkFCuYmox0^+hn1RL7U~-eX%g# zQb!Nbb5L5n3TH9nCbo(N#FdLkzK~H~&A1uTespie9i-{9rRudqeRk=}eSu#MkmXUK zP|rVGl-zZ@>G*X(FN^kCkx`;_y~_$?LarF9T<-Y;&FIN1?2?l_@#WGq)~(katW5159N1{@A|x!dnT(wW{gf0 zJa6hAeUd@qyi|D$$$WKUbr^Y(n^Ey@N2&~@DXopyz8OC;ac^!5c4MlY4MJzR9qoPb zvDqss>fY>b5F3aIAaaBIpZUf`l`rqK*Ma=2Kg{Q}H`lR}lO{QNVoQi zZd11^!57z0JW)Q9;MNK0_?ks~su<;sU1_=a=Tc>79dNo`IU$J_pH9@T)%}9Xl~vLN zdX)_$N%Czb!-siuEw*wNWkZ=pAFvhCC9=F?nPZY@aicTkzz7l% zUam(9v)x>`0=s`cXgREkY0T57KS;N*1txFtEv9R-1FKZPdciVcD_T&L6O3oL z>kb_zDm8PlZ?I%lT%BqCT|RaZa*8zjKj9^4jtTD753r8ZR&Im{?md7vRE?Ip9aH_B z&+P&|RHv+H-1!k}wOai91qlpZDs(qW_viW!4Z@7q`Dx&QG?TOZeP$A2#9eHP0&Zln zpRLgwu}XBUA_3qeEH>_)4HPpp67|#`(Iva!)|%E0==#~UXYQ=YukWMT{scvqKar(( zf7aEKNv#>sCdc5`)>y4rCClhCDT_ap6ATx7xio&Ha~84YyNIKj1|=hsK8Q#$W4QGK zUBb`5B*$XQGD(4VG2~l9Vw)d#01Xie6Gf;DcynUb^gcT|^61XsOx~8U(Gwv5AoJtT z^+t4H=peKC*N{|ikfuhJ?aE16D_uwfu5$DKlb?m)a(V*nC|0ihiiT0z;(+A|L;otYR z(!TCQ;Z&^yLa>Vw@R)eRm7P~nc`q33Y<&4jK z=3f3Z|7u#P0?U3qxb_zK5o{k@2l48|rF<#OqwxoTsyVluf2RNXG7k=>)_CAR3EPG_ zR)F~URRd>PfB_W!8%FKaS2$e=!idwTqO>{(LCb8YG}s+~3oN<)@ZLN;q5G0f3YGvG zx3N(Jb!5{VfWj-7->u(-=?;p~4jYmmUtunYvEv>{lWn9%7NmEvbI?*M2Tk4Gq+)=v zTANzB8uRCWd4GXt6i~TC4s4-28Sj?NjDCaP2}k|oGvfanF{LDseN(pUA!TvlEd{tG zxwywT`Nm@+qQV1$LN8-t5+g2Sf+Ax403e}+v%U`=>3aNl7ooeyZq5mSsED8R6Gy3_ z&KIB|D9kysUaLPurQQY8JvP$O*3%~6CfZH5rTyz9uqY!Hvu<^!@UPK!IEQn!Rz%8w E0974XPyhe` literal 0 HcmV?d00001 diff --git a/favicon/apple-touch-icon.png b/favicon/apple-touch-icon.png new file mode 100755 index 0000000000000000000000000000000000000000..aea74d11822cddf24c92330fa5a06b6eaa754f0c GIT binary patch literal 1115 zcmV-h1f=_kP)Px#Do{*RMgRZ*0002;^70D{3&Fv`K0ZEeZEfJ-;Gm$O zA|fIyD=UYGhgMcrva+(mRHE|$00V4EL_t(&-tCydYZE~f#)nXvK!S_n3xJyr=-yrlM|r=_-(#7l~oLQh3S#8Xd&dTEdz^kjPx5h0k1e~vRdvzs@Y zH-EsMQLoq4MP{e2 z2vs#ejj4-~sRFGi0| z1l+IJmyEsQyopM;qsKAXV_uRUA*%D+rwZBf?t6%M;j?~NW7%~+6Qa@+#^;FAifVy> zxVNW}O=b5W;?9D;j9!_=-IqP$r9o*f`dV4N6nffM%+7}8(WV;Rc;us@ZQHA3?{_SZ z*1#;pe(9TY>q1zsRJJ@5PJS0(PX@~6UEHaaN@aM>*{r9Uodxwy)JGPus$Wh&4&Ad_ zPY%L`Tv7^Q-!u950-toGMd78QeZu=%FOpOiwe%XfJ&_6ulQ}&K(byE2W2_PBHRV41cF6lU-25Wxo zi1SC=mGVgG?!W@hgaSz=GxKeu@#BtL+HF11Tad5wI*ylXPU-PzFL0srAM6U5Ev>y& zc1f=qVQE`1%SuaDC{3l=Nup$}4y6s^- z#2kS8}0prh{I?0coKan9whYK2hI+Rs{~u9w$r~890>)J21whsf=z$v>MX;gmqJ`x z?)kpblP8$;@-NG_Tjl=J893eN4Mpm(aNc+lU*2sPJoWy0UD4Q&?wN`~nf@g~5ClOG z1VIo4K@bE%5ClOG|C~Q8p#n!~yZXrh0038dR9JLUVRs;Ka&Km7Y-J#Hd2nSQX>fF7 z004NL;1lBNlUWF4Vg<9No?-(^aFzu51v7A&dPE85DD?+4I|Ks-ojhF}LpZJ{ zCn&HQ7;&_8v9WO(Y-rh~vRZYggn@zd?3qTI9ij#XyE=CnYD+MSY8vk})ZV6HV8F3q z^(HPY(FrNc44UWIn~sP7-3v4=yecH3Bq*_5p`a)~Ei)%op`@}PRUxyWB$Jc1ICAEQ%n|m} d4IT@;^cY@=3zmFxGMx(Yo~Nsy%Q~loCIIz3R|Nn7 literal 0 HcmV?d00001 diff --git a/favicon/favicon-32x32.png b/favicon/favicon-32x32.png new file mode 100755 index 0000000000000000000000000000000000000000..eef4bd3091ca966e0349514f2c348bed7c55674c GIT binary patch literal 407 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfvmUKs7M+SzC{oH>NS%G}U;vjb? zhIQv;UIIBP0X`wFKAD9;CVo&~5tRy5!BZ0C7tG+xzOB!CrFwyQ;_-z@q zLp07OCrBJ}Xl!ikboA`>^z>xpXk^lskO(tWi(V-qt#4XZ&dTND|KN_)-herw0ULrg zC~;~mxOQOT1|E-M4g-S_b_U6wX_HwONvj)dFflOL(ZG}H6|?5Rj2u3pgu8s()UI%8 zykZgx$Y$U+h%z#8WD)ZZVBpqC5Ma)JcxUc34l|d=g#iUj%+-v`7cBS@z?Z}@zfF1x zXF|kvpbNvRLLy3n63Z0|it^Jkb5a#bDhpB-G7CzQ85k<&JpRPPQ5dG7amxSn8PBId z49v>hddb|v%EI20MVN&ZTpCOcr!XsT4pBILXBmi|@!Oa=yvHz|`gG;uunK z>+OxR`Lhiqju#raoKv}47I$M)T;}Ed3p}r8UkQCQYoE`~aFc)E)1~)2D0qi{eDmdR zj1z~heUAOH2`WHi(Se)s%%>ku=REr+x%2MBr+N9C4eR>yj~eAHcMW{izy5sd#x2%+ z)N502{4lNS^V!?VHuKioH$jo_t=hT7PVTvRLv89V;TO}r|IVMAUHtAz;9u?Iir4Em z3QH(d*K#d4ntgNcD)Y8|FG_@8$b9?Uo_BDU$ce&aX+^WMy{qEGj!nI{nak_D>h-l{ z6EtRIN+_HSxVidkyL5ri%f0!}cJ93Jt-`YK&rD`Jdxx^RiZy35?|pjrUVQbrzMWQf z30oL9eLI|-ZvFhl_v60Lf2}V5`|ah-GrhZ)@m;#7ai{#3bk=a{ z%3Ie&&Hi4NUw&6+;~DqoRd;%?$K5R}<_OIVW_-W)*VfNBuCA1s8FIDl=jrq5GcSGq z^>&lYxrNVX#Avn8KOcL1|E=FQW9?=*ugP+r-`-dL{qORHGAkpl{*v-Yp8KNog1_kU ziDi6uE0@Oftvq)zj^8&;oPX6lz9#}}qN269!%g1u{kV7DP;gCD^{U*>Y*IU3mlSz~ zt&J7Uw3$3bRsNw^9hT6mwr0%-CR}Ly1`mh=h=j{ zx85mjz0a!HUUK!;?Ah{i?*bp1_T8S~e4nv?_i~P%(R1rVO8Wh@${bHDlz(x@`Rdvm z1}nuDrq20SyN-*0=GKV5*KY&+QuF^Vl#h8*oKjiyJ>gI1=V#6rpPYE)-Ql#OX7Yzu zOFu7D%iKHn9)IHDC=AokIOTu(jOWuJ24-b$y<~1-Wnu5hBFw@HE)6D!Q<#-EhbWxB naplC3Ge=~Ou%B-5Sm33{@Jd{;RFK7 \ No newline at end of file diff --git a/index.html b/index.html index 62a7feb..af4f46a 100644 --- a/index.html +++ b/index.html @@ -2,7 +2,18 @@ - VS3D-Vis + + + + + + + + + + + VIS3D diff --git a/manifest.json b/manifest.json new file mode 100755 index 0000000..b66eddf --- /dev/null +++ b/manifest.json @@ -0,0 +1,21 @@ +{ + "name": "VIS3D by VersusTuneZ", + "short_name": "VIS3D", + "icons": [ + { + "src": "/favicon/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/favicon/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#3949ab", + "background_color": "#212121", + "display": "standalone", + "start_url": "/index.html", + "orientation": "landscape-primary" +} diff --git a/out/gui/wave2d.json b/out/gui/wave2d.json index d19c5a8..3727c67 100644 --- a/out/gui/wave2d.json +++ b/out/gui/wave2d.json @@ -1 +1 @@ -[{"group":"","name":"fftSize","showName":"FFT-Size","options":[2048,4096,8192,16384,32768],"value":16384,"type":"select","tooltip":"How Many Items should the FFT Capture and Render","dataType":"int"},{"group":"rotation","name":["X","Y","Z"],"props":["x","y","z"],"type":"slider","max":360,"min":-360,"value":0,"dataType":"int"},{"group":"rotation","name":["X-Inc","Y-Inc","Z-Inc"],"props":["x-inc","y-inc","z-inc"],"type":"slider","max":1,"min":-1,"value":0,"stepSize":0.01,"dataType":"float"},{"group":"","name":"baseColor","showName":"Base Color","type":"color","value":"#0089ff","dataType":"rgb","tooltip":"Base Color!"},{"group":"","name":"gradientToColor","showName":"Second Color","type":"color","value":"#ff0000","dataType":"rgb","tooltip":"Second Color!"}] \ No newline at end of file +[{"group":"","name":"fftSize","showName":"FFT-Size","options":[2048,4096,8192,16384,32768],"value":16384,"type":"select","tooltip":"How Many Items should the FFT Capture and Render","dataType":"int"},{"group":"rotation","name":["X","Y","Z"],"props":["x","y","z"],"type":"slider","max":360,"min":-360,"value":0,"dataType":"int"},{"group":"rotation","name":["X-Inc","Y-Inc","Z-Inc"],"props":["x-inc","y-inc","z-inc"],"type":"slider","max":1,"min":-1,"value":0,"stepSize":0.01,"dataType":"float"},{"group":"translate","name":["X","Y","Z"],"props":["x","y","z"],"type":"slider","max":1,"min":-1,"value":0,"stepSize":0.01,"dataType":"float"},{"group":"","name":"fudgeFactor","showName":"Fudge Factor","type":"slider","value":1,"max":2,"min":0,"tooltip":"z to w Fudge","stepSize":0.1,"dataType":"float"},{"group":"","name":"baseColor","showName":"Base Color","type":"color","value":"#0089ff","dataType":"rgb","tooltip":"Base Color!"},{"group":"","name":"gradientToColor","showName":"Second Color","type":"color","value":"#ff0000","dataType":"rgb","tooltip":"Second Color!"}] \ No newline at end of file diff --git a/out/js/scripts.js b/out/js/scripts.js index de8de6b..4beaecc 100644 --- a/out/js/scripts.js +++ b/out/js/scripts.js @@ -479,6 +479,15 @@ class TDUtils { return dst; } + static projection(width, height, depth) { + return [ + 2 / width, 0, 0, 0, + 0, -2 / height, 0, 0, + 0, 0, 2 / depth, 0, + -1, 1, 0, 1, + ]; + } + static inverse(m, dst) { dst = dst || new Float32Array(16); let m00 = m[0], @@ -567,42 +576,13 @@ class TDUtils { static aspectView(aspect) { return [ - 1 * aspect, 0, 0, 0, + 1 / aspect, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ] } - static getMatrix(fov, aspect, near, far, camAngle, radius) { - let lMat = this.lastMatrix, - u = TDUtils; - if (!u.isSame('fov', fov) - || !u.isSame('aspect', aspect) - || !u.isSame('near', near) - || !u.isSame('far', far) - || !u.isSame('cam', camAngle) - || !u.isSame('radius', radius) - ) { - let matrix = TDUtils.perspective(TDUtils.degToRad(fov), aspect, near, far), - cameraMatrix = TDUtils.yRotation(TDUtils.degToRad(camAngle)); - cameraMatrix = TDUtils.translate(cameraMatrix, 0, 0, radius * 1.5); - let viewMatrix = TDUtils.inverse(cameraMatrix); - matrix = TDUtils.multiply(matrix, viewMatrix) - lMat.m = matrix; - } - return lMat.m; - } - - static isSame(key, value) { - let lMat = this.lastMatrix; - if (lMat[key] !== value) { - lMat[key] = value; - return false; - } - return true; - } - static updateRotate(rotation, def) { let value = vConf.get(rotation, def) + vConf.get(rotation + '-inc', 0) if (value > 360) { @@ -612,6 +592,149 @@ class TDUtils { } vConf.set(rotation, value); } + + static makeZToWMatrix(fudgeFactor) { + return [ + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, fudgeFactor, + 0, 0, 0, 1, + ]; + } +} + +class GLHelper { + constructor(program) { + this.matrix = new Float32Array(16); + this.program = program; + } + + static uniform4fv(program, name, data) { + let uniform = gl.getUniformLocation(program, name); + gl.uniform4fv(uniform, data); + } + + static uniform3fv(program, name, data) { + let uniform = gl.getUniformLocation(program, name); + gl.uniform3fv(uniform, data); + } + + static uniform1f(program, name, data) { + let uniform = gl.getUniformLocation(program, name); + gl.uniform1f(uniform, data); + } + + rotateX(deg) { + this.matrix = TDUtils.multiply(this.matrix, TDUtils.xRotation(deg)); + } + + rotateY(deg) { + this.matrix = TDUtils.multiply(this.matrix, TDUtils.yRotation(deg)); + } + + rotateZ(deg) { + this.matrix = TDUtils.multiply(this.matrix, TDUtils.zRotation(deg)); + } + + scale(scaling) { + this.matrix = TDUtils.multiply(this.matrix, TDUtils.scale(scaling[0], scaling[1], scaling[2])) + } + + project(depth) { + depth = depth || (c.width > c.height) ? c.width : c.height; + this.matrix = TDUtils.projection(c.width, c.height, depth) + } + + translate(t) { + this.matrix = TDUtils.translate(this.matrix, t[0] || 0, t[1] || 0, t[2] || 0); + } + + addFudgeFactor(fudgeFactor) { + this.matrix = TDUtils.multiply(TDUtils.makeZToWMatrix(fudgeFactor), this.matrix); + } + + applyMatrix() { + let matrix = gl.getUniformLocation(this.program, "u_matrix"); + gl.uniformMatrix4fv(matrix, false, this.matrix); + } +} +class Camera { + constructor() { + this.mouse; + this.rotation = { + x: 0, + y: 0 + } + this.lastMouse; + this.mousePressed = false; + this.translate = { + x: 0, + y: 0, + z: 0 + } + } + + async init() { + this.mouse = { + x: 0, + y: 0 + } + window.addEventListener('mousedown', this.mouseDown.bind(this)); + window.addEventListener('mouseup', this.mouseUp.bind(this)); + window.addEventListener('mousemove', this.mouseMove.bind(this), {passive: true}); + eventHandler.addEvent('keys-ArrowUp, keys-ArrowDown, keys-ArrowLeft, keys-ArrowRight, keys-KeyQ, keys-KeyE', this.keyPressed.bind(this)); + } + + mouseDown() { + this.mousePressed = true; + this.lastMouse = null; + } + + mouseUp() { + this.mousePressed = false; + this.lastMouse = null; + } + + mouseMove(event) { + if (!this.mousePressed || gui.modal.open) { + return; + } + if (this.lastMouse) { + let mouse = this.mouse, + rotate = this.rotation; + mouse.x += (this.lastMouse.x - event.clientX) * 0.2; + mouse.y += (this.lastMouse.y - event.clientY) * 0.2; + rotate.x = VTUtils.map(mouse.x, -c.width, c.width, 180, -180, false); + rotate.y = VTUtils.map(mouse.y, -c.height, c.height, 180, -180, false); + } + this.lastMouse = { + x: event.clientX, + y: event.clientY + } + } + + keyPressed(data) { + switch (data) { + case 'keys-ArrowUp': + this.translate.z += 10; + break; + case 'keys-ArrowDown': + this.translate.z -= 10; + break; + case 'keys-ArrowLeft': + this.translate.x -= 10; + break; + case 'keys-ArrowRight': + this.translate.x += 10; + break; + case 'keys-KeyQ': + this.translate.y += 10; + break; + case 'keys-KeyE': + this.translate.y -= 10; + break; + } + } } class Template { constructor() { @@ -621,9 +744,7 @@ class Template { async loadTemplate(name) { let self = this; if (!this.tpl[name]) { - await fetch(templateDir + name + ".tpl").then((r) => r.text()).then(c => { - self.tpl[name] = c; - }) + self.tpl[name] = await FetchHandler.loadFile(templateDir + name + '.tpl', false) } } @@ -651,16 +772,10 @@ class Template { } return d; } - - parseFromAPI(url, name, cb) { - fetch(url).then((r) => r.json()).then(d => { - cb(this.parseTemplate(name, d)) - }).catch(console.error) - } } const templateEx = /\$(.*?)\$/gm; -const templateDir = "out/tpl/" +const templateDir = "/out/tpl/" class ShaderHandler { constructor(gl) { this.gl = gl; @@ -682,8 +797,8 @@ class ShaderHandler { async load(name, url, type) { let realName = name + "_" + type; if (!this.shaders[realName]) { - let data = await fetch(url); - let shader = this.createShader(await data.text(), type); + let data = await FetchHandler.loadFile(url, false); + let shader = this.createShader(data, type); if (shader) { this.shaders[realName] = shader; } @@ -733,8 +848,7 @@ class ShaderHandler { gl.attachShader(pro, this.getShader(shaders[i], gl.FRAGMENT_SHADER)); } gl.linkProgram(pro); - var success = gl.getProgramParameter(pro, gl.LINK_STATUS); - if (success) { + if (gl.getProgramParameter(pro, gl.LINK_STATUS)) { this.programs[name] = pro; return pro; } @@ -883,6 +997,42 @@ class AudioPlayerFile { return template.parseTemplate('audio-information', tag); } } +class FetchHandler { + static files = {}; + + static async loadFiles(array, isJSON) { + let content = []; + for (let i = 0; i < array; i++) { + content.push(await FetchHandler.loadFile(array[i], isJSON)); + } + return content; + } + + static async loadFile(filename, isJSON) { + filename += '?v=' + version; + let files = FetchHandler.files; + if (files[filename]) { + return files[filename]; + } + let data = await FetchHandler.tryFromCache(filename); + if (isJSON) { + data = JSON.parse(data); + } + files[filename] = data; + return data; + } + + static async tryFromCache(filename) { + if (caches) { + let cache = await caches.open('vis3d-pwa-1'); + let data = await cache.match(filename); + if (!data) { + data = await fetch(filename); + } + return await data.text(); + } + } +} class PlayerConfigHandler { async init() { await template.loadArray([ @@ -984,7 +1134,7 @@ class VisualConfig { let tem = VisualConfig.visualTemplates; if (!tem[name]) { //load config and save it - tem[name] = await fetch('/out/gui/' + name + ".json").then((res) => res.json()); + tem[name] = await FetchHandler.loadFile('/out/gui/' + name + ".json", true); } return tem[name]; } @@ -1020,6 +1170,16 @@ class Player { window.dispatchEvent(new CustomEvent('playSong')); } + stop() { + if (!audioHandler.lastSong) { + return; + } + let audioFile = audioHandler.audioFile; + audioFile.pause(); + audioFile.currentTime = 0; + window.dispatchEvent(new CustomEvent('playSong')); + } + playByID(number) { this.playlist.index = number; let next = this.playlist.getCurrent(); @@ -1378,6 +1538,7 @@ class Modal { let self = this; self.currentModal = ''; self.modal = $('#modal'); + self.open = false; self.parent = self.modal.parentNode; self.modal.addDelegatedEventListener('click', 'header .close', this.closeModal.bind(this)); } @@ -1411,6 +1572,7 @@ class Modal { closeModal() { this.parent.addClass("hide") + this.open = false; } isCurrent(title) { @@ -1419,6 +1581,7 @@ class Modal { showModal() { this.parent.removeClass("hide") + this.open = true; } } class Visual { @@ -1843,13 +2006,15 @@ class Wave2D extends Visual { updateData() { let data = audioHandler.getFloatArray(); - let add = 2 / data.length, - x = -1; + let add = c.width / data.length, + x = 0, + y = c.height / 2, + goTrough = y / 2; let outerLoop = 0; for (let i = 0; i < data.length; i++) { //first this.data[outerLoop] = x; - this.data[outerLoop + 1] = data[i]; + this.data[outerLoop + 1] = y + (data[i] * goTrough); this.data[outerLoop + 2] = data[i]; outerLoop += 3; x += add; @@ -1872,17 +2037,18 @@ class Wave2D extends Visual { } rotate(program) { - let matrix = [ - 1, 0, 0, 0, - 0, 0.6, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 - ] - matrix = TDUtils.multiply(matrix, TDUtils.xRotation(vConf.get("rotation-x", 0))); - matrix = TDUtils.multiply(matrix, TDUtils.yRotation(vConf.get("rotation-y", 0))); - matrix = TDUtils.multiply(matrix, TDUtils.zRotation(vConf.get("rotation-z", 0))); - let rotate = gl.getUniformLocation(program, "u_matrix"); - gl.uniformMatrix4fv(rotate, false, matrix); + let glHelper = new GLHelper(program); + glHelper.project(); + glHelper.addFudgeFactor(vConf.get("fudgeFactor", 1)); + glHelper.translate([ + camera.translate.x, + camera.translate.y, + camera.translate.z + ]); + glHelper.rotateX(camera.mouse.x); + glHelper.rotateY(camera.mouse.y); + glHelper.rotateZ(vConf.get("rotation-z", 0)); + glHelper.applyMatrix(); } setup() { @@ -1896,9 +2062,8 @@ class Wave2D extends Visual { prepare(program) { this.position = gl.getAttribLocation(program, "a_position"); - this.color = gl.getUniformLocation(program, "u_color"); - let lightPos = gl.getUniformLocation(program, "u_lightPos"); - gl.uniform3fv(lightPos, vConf.get("light", [0, 5, -56])); + //GLHelper.uniform1f(program, "u_fudgeFactor", vConf.get("fudgeFactor", 1)); + GLHelper.uniform3fv(program, "u_lightPos", vConf.get("light", [0, 5, -56])); } afterDraw() { @@ -2186,7 +2351,8 @@ class KeyHandler { media.setActionHandler('play', player.playStop.bind(player)); media.setActionHandler('pause', player.playStop.bind(player)); media.setActionHandler('previoustrack', player.prevSong.bind(player)); - media.setActionHandler('nexttrack', player.prevSong.bind(player)); + media.setActionHandler('nexttrack', player.nextSong.bind(player)); + media.setActionHandler('stop', player.stop.bind(player)); } } @@ -2224,7 +2390,8 @@ class KeyHandler { if (eventHandler.handleEvent({ data: { - cmd: name + cmd: name, + data: name } })) { event.preventDefault(); @@ -2267,9 +2434,11 @@ const shaderHandler = new ShaderHandler(null), startup = new Startup(), eventHandler = new EventHandler(), playerConf = new PlayerConfigHandler(), - keyHandler = new KeyHandler(); + keyHandler = new KeyHandler(), + version = 1, + camera = new Camera(); -let c, gl, cInfo, ctx; +let c, gl, cInfo, ctx, sw; worker.addEventListener('message', e => { if (e.data.status === 'startup') { @@ -2286,6 +2455,9 @@ window.addEventListener('startupFin', e => { }) async function startUP() { + if ('serviceWorker' in navigator) { + sw = await navigator.serviceWorker.register('/sw.js'); + } pConf.loadConfigByName('default'); c = $('#c'), gl = c.getContext("webgl2"), @@ -2296,10 +2468,11 @@ async function startUP() { return false; } shaderHandler.setGL(gl) - await shaderHandler.loadArray(["wave", "sphere", "water", "wave2d"], 'shaders/'); + await shaderHandler.loadArray(["wave", "sphere", "water", "wave2d"], '/shaders/'); await NotificationHandler.instance.init(); await audioHandler.init(); await player.init(); + await camera.init(); await visual.init(); await gui.init(); await imageUploader.init(); diff --git a/out/js/scripts.min.js b/out/js/scripts.min.js index b2b97cf..9e8f3cd 100644 --- a/out/js/scripts.min.js +++ b/out/js/scripts.min.js @@ -1 +1 @@ -class VTUtils{static random(e,t){let a=Math.random();if(void 0===e)return a;if(void 0===t)return e instanceof Array?e[Math.floor(a*e.length)]:a*e;if(e>t){let a=e;e=t,t=a}return a*(t-e)+e}static randomInt(e,t){return Math.floor(VTUtils.random(e,t))}static normalize(e,t,a){return(e-a)/(t-a)}static distance(e,t,a,i){let s=e-a,n=t-i;return Math.sqrt(s*s+n*n)}static map(e,t,a,i,s,n){let l=(e-t)/(a-t)*(s-i)+i;return n?i>16&255)/255,(t>>8&255)/255,(255&t)/255]}Node.prototype.addDelegatedEventListener=function(e,t,a){this.addEventListener(e,e=>{let i=e.target;if(i.matches(t))a(e,i);else{let s=i.closest(t);if(s)try{a(e,s)}catch(e){NotificationHandler.createNotification("FATAL ERROR WITHIN HANDLER!","error",1e3)}}})},Node.prototype.hasClass=function(e){let t=e.split(","),a=null;for(let e of t){if(!1===a)break;a=this.classList.contains(e.trim())}return!0===a},Node.prototype.addClass=function(e){let t=e.split(",");for(let e of t)this.classList.add(e.trim());return this},Node.prototype.removeClass=function(e){let t=e.split(",");for(let e of t)this.classList.remove(e.trim());return this},Node.prototype.toggleClass=function(e,t){let a=e.split(",");for(let e of a)this.classList.toggle(e.trim(),t)},Node.prototype.switchClass=function(e,t,a){let i=this.classList;a?(i.remove(e),i.add(t)):(i.remove(t),i.add(e))},Node.prototype.toggleCheck=function(e,t){let a=this.classList,i=e.split(",");for(let e of i){let i=e.trim();t?a.add(i):a.remove(i)}},String.prototype.firstUpper=function(){return this.charAt(0).toUpperCase()+this.slice(1)},File.prototype.toBase64=function(e){const t=new FileReader;t.onloadend=e,t.readAsDataURL(this)};class TDUtils{static lastMatrix={m:null};static multiply(e,t){let a=t[0],i=t[1],s=t[2],n=t[3],l=t[4],r=t[5],o=t[6],d=t[7],c=t[8],h=t[9],u=t[10],p=t[11],g=t[12],f=t[13],m=t[14],y=t[15],v=e[0],C=e[1],w=e[2],T=e[3],S=e[4],b=e[5],H=e[6],x=e[7],U=e[8],D=e[9],A=e[10],E=e[11],L=e[12],R=e[13],F=e[14],M=e[15];return[a*v+i*S+s*U+n*L,a*C+i*b+s*D+n*R,a*w+i*H+s*A+n*F,a*T+i*x+s*E+n*M,l*v+r*S+o*U+d*L,l*C+r*b+o*D+d*R,l*w+r*H+o*A+d*F,l*T+r*x+o*E+d*M,c*v+h*S+u*U+p*L,c*C+h*b+u*D+p*R,c*w+h*H+u*A+p*F,c*T+h*x+u*E+p*M,g*v+f*S+m*U+y*L,g*C+f*b+m*D+y*R,g*w+f*H+m*A+y*F,g*T+f*x+m*E+y*M]}static translate(e,t,a,i,s){s=s||new Float32Array(16);let n=e[0],l=e[1],r=e[2],o=e[3],d=e[4],c=e[5],h=e[6],u=e[7],p=e[8],g=e[9],f=e[10],m=e[11],y=e[12],v=e[13],C=e[14],w=e[15];return s[0]=n,s[1]=l,s[2]=r,s[3]=o,s[4]=d,s[5]=c,s[6]=h,s[7]=u,s[8]=p,s[9]=g,s[10]=f,s[11]=m,s[12]=n*t+d*a+p*i+y,s[13]=l*t+c*a+g*i+v,s[14]=r*t+h*a+f*i+C,s[15]=o*t+u*a+m*i+w,s}static xRotation(e){e=TDUtils.degToRad(e);let t=Math.cos(e),a=Math.sin(e);return[1,0,0,0,0,t,a,0,0,-a,t,0,0,0,0,1]}static yRotation(e){e=TDUtils.degToRad(e);let t=Math.cos(e),a=Math.sin(e);return[t,0,-a,0,0,1,0,0,a,0,t,0,0,0,0,1]}static zRotation(e){e=TDUtils.degToRad(e);let t=Math.cos(e),a=Math.sin(e);return[t,a,0,0,-a,t,0,0,0,0,1,0,0,0,0,1]}static degToRad(e){return e*Math.PI/180}static scale(e,t,a,i){return(i=i||new Float32Array(16))[0]=e,i[5]=t,i[10]=a,i}static lookAt(e,t,a,i){i=i||new Float32Array(16);let s=TDUtils.normalize(TDUtils.subtractVectors(e,t)),n=TDUtils.normalize(TDUtils.cross(a,s)),l=TDUtils.normalize(TDUtils.cross(s,n));return i[0]=n[0],i[1]=n[1],i[2]=n[2],i[4]=l[0],i[5]=l[1],i[6]=l[2],i[8]=s[0],i[9]=s[1],i[10]=s[2],i[12]=e[0],i[13]=e[1],i[14]=e[2],i[15]=1,i}static cross(e,t,a){return(a=a||new Float32Array(3))[0]=e[1]*t[2]-e[2]*t[1],a[1]=e[2]*t[0]-e[0]*t[2],a[2]=e[0]*t[1]-e[1]*t[0],a}static normalize(e,t){t=t||new Float32Array(3);let a=Math.sqrt(e[0]*e[0]+e[1]*e[1]+e[2]*e[2]);return a>1e-5&&(t[0]=e[0]/a,t[1]=e[1]/a,t[2]=e[2]/a),t}static subtractVectors(e,t,a){return(a=a||new Float32Array(3))[0]=e[0]-t[0],a[1]=e[1]-t[1],a[2]=e[2]-t[2],a}static perspective(e,t,a,i,s){s=s||new Float32Array(16);let n=Math.tan(.5*Math.PI-.5*e),l=1/(a-i);return s[0]=n/t,s[5]=n,s[10]=(a+i)*l,s[11]=-1,s[14]=a*i*l*2,s}static inverse(e,t){t=t||new Float32Array(16);let a=e[0],i=e[1],s=e[2],n=e[3],l=e[4],r=e[5],o=e[6],d=e[7],c=e[8],h=e[9],u=e[10],p=e[11],g=e[12],f=e[13],m=e[14],y=e[15],v=u*y,C=m*p,w=o*y,T=m*d,S=o*p,b=u*d,H=s*y,x=m*n,U=s*p,D=u*n,A=s*d,E=o*n,L=c*f,R=g*h,F=l*f,M=g*r,k=l*h,I=c*r,N=a*f,P=g*i,V=a*h,$=c*i,B=a*r,O=l*i,z=v*r+T*h+S*f-(C*r+w*h+b*f),_=C*i+H*h+D*f-(v*i+x*h+U*f),G=w*i+x*r+A*f-(T*i+H*r+E*f),j=b*i+U*r+E*h-(S*i+D*r+A*h),K=1/(a*z+l*_+c*G+g*j);return t[0]=K*z,t[1]=K*_,t[2]=K*G,t[3]=K*j,t[4]=K*(C*l+w*c+b*g-(v*l+T*c+S*g)),t[5]=K*(v*a+x*c+U*g-(C*a+H*c+D*g)),t[6]=K*(T*a+H*l+E*g-(w*a+x*l+A*g)),t[7]=K*(S*a+D*l+A*c-(b*a+U*l+E*c)),t[8]=K*(L*d+M*p+k*y-(R*d+F*p+I*y)),t[9]=K*(R*n+N*p+$*y-(L*n+P*p+V*y)),t[10]=K*(F*n+P*d+B*y-(M*n+N*d+O*y)),t[11]=K*(I*n+V*d+O*p-(k*n+$*d+B*p)),t[12]=K*(F*u+I*m+R*o-(k*m+L*o+M*u)),t[13]=K*(V*m+L*s+P*u-(N*u+$*m+R*s)),t[14]=K*(N*o+O*m+M*s-(B*m+F*s+P*o)),t[15]=K*(B*u+k*s+$*o-(V*o+O*u+I*s)),t}static aspectView(e){return[1*e,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]}static getMatrix(e,t,a,i,s,n){let l=this.lastMatrix,r=TDUtils;if(!(r.isSame("fov",e)&&r.isSame("aspect",t)&&r.isSame("near",a)&&r.isSame("far",i)&&r.isSame("cam",s)&&r.isSame("radius",n))){let r=TDUtils.perspective(TDUtils.degToRad(e),t,a,i),o=TDUtils.yRotation(TDUtils.degToRad(s));o=TDUtils.translate(o,0,0,1.5*n);let d=TDUtils.inverse(o);r=TDUtils.multiply(r,d),l.m=r}return l.m}static isSame(e,t){let a=this.lastMatrix;return a[e]===t||(a[e]=t,!1)}static updateRotate(e,t){let a=vConf.get(e,t)+vConf.get(e+"-inc",0);a>360?a-=360:a<-360&&(a+=360),vConf.set(e,a)}}class Template{constructor(){this.tpl={}}async loadTemplate(e){let t=this;this.tpl[e]||await fetch(templateDir+e+".tpl").then(e=>e.text()).then(a=>{t.tpl[e]=a})}async loadArray(e){for(let t of e)await this.loadTemplate(t)}parseTemplate(e,t){if(!this.tpl[e])return"";let a,i=this.tpl[e];for(;null!==(a=templateEx.exec(i));){a.index===templateEx.lastIndex&&templateEx.lastIndex++;let e=a[0],s=t[a[1]];null==s&&(s=""),i=i.replace(e,s)}return i}parseFromAPI(e,t,a){fetch(e).then(e=>e.json()).then(e=>{a(this.parseTemplate(t,e))}).catch(console.error)}}const templateEx=/\$(.*?)\$/gm,templateDir="out/tpl/";class ShaderHandler{constructor(e){this.gl=e,this.shaderNames=[],this.shaders={},this.programs={}}setGL(e){this.gl=e}async loadShader(e,t){this.shaderNames.push(e),await this.load(e,t+e+".vert",this.gl.VERTEX_SHADER),await this.load(e,t+e+".frag",this.gl.FRAGMENT_SHADER)}async load(e,t,a){let i=e+"_"+a;if(!this.shaders[i]){let e=await fetch(t),s=this.createShader(await e.text(),a);s&&(this.shaders[i]=s)}return!!this.shaders[i]}getShader(e,t){let a=e+"_"+t;return this.shaders[a]}getAllShaders(){return this.shaderNames}async createProgramForEach(e){e=e||this.shaderNames;for(let t=0;t Currently no Song is uploaded!","error",2e3),!1;let t=this,a=e.file;t.lastSong&&URL.revokeObjectURL(t.lastSong),t.lastSong=this.audioFile.src=URL.createObjectURL(a),this.isStarted||this.start().catch(alert),this.audioFile.play().then(t=>{pConf.get("showPlaying","true")&&NotificationHandler.createNotification("Now Playing:"+e.getAudioName(),"info",pConf.get("showPlayingTime",1e3)),window.dispatchEvent(new CustomEvent("playSong"))}).catch(e=>{NotificationHandler.createNotification(e.message,"error",1e3),player.nextSong()})}getIntArray(e){let t=new Uint8Array(e);return this.analyser.getByteFrequencyData(t),t}getFloatArray(){let e=new Float32Array(this.analyser.fftSize);return this.analyser.getFloatTimeDomainData(e),e}}class AudioPlayerFile{constructor(e,t){this.file=e,this.name=this.getName(),this.id3=null,this.index=t}getName(){let e=this.file.name.split(".");return e.pop(),e=e.join("."),e}getID3Tag(e){return e||null===this.id3?(eventHandler.sendData("getData",{file:this.file,name:this.name,index:this.index,force:!0===e}),{title:this.name,artist:"VA"}):this.id3}getAudioName(){let e=this.getID3Tag();return template.parseTemplate("audio-information",e)}}class PlayerConfigHandler{async init(){await template.loadArray(["config/nav","config/content","config/visualitem"]),this.last="base",$(".settings-icon").addEventListener("click",this.open.bind(this)),$("modal-content").addDelegatedEventListener("click",".config-nav .item",this.navHandler.bind(this))}open(){if(void 0===this.content){let e=template.parseTemplate("config/nav",{});e+=template.parseTemplate("config/content",{content:""}),this.content=e}gui.modal.renderModal("Settings",this.content,"by VersusTuneZ"),this.handleById(),gui.modal.showModal()}navHandler(e,t){this.last=t.dataset.id,this.handleById()}handleById(){let e=this.last;new VisualConfig("visual"===e,"base"===e);let t=$(".config-nav .item.active"),a=$('.config-nav .item[data-id="'+e+'"]');t&&t.removeClass("active"),a&&a.addClass("active")}}class VisualConfig{static visualTemplates={};constructor(e,t){this.content=$("modal-content .config-content"),e?this.renderVisualConfig(visual.c):t?this.renderBase():this.renderVisuals()}renderVisuals(){let e=Object.keys(visual.visuals),t='
';for(let a=0;a",this.content.innerHTML=t}async renderBase(){let e=await this.loadVisualConfig("base"),t=create("section");t.addClass("base"),t.innerHTML=GUIHelper.fromJSON(e,pConf),this.content.innerHTML=t.outerHTML}async renderVisualConfig(e){let t=await this.loadVisualConfig(e,vConf),a=create("section");a.addClass("visual"),a.innerHTML=GUIHelper.fromJSON(t,vConf),a.innerHTML+=GUIHelper.createButton({action:"resetVConf",name:"Reset Visual Config"}),a.innerHTML+=GUIHelper.createButton({action:"makeModalTransparent",name:"toggle Modal Opacity"}),this.content.innerHTML=a.outerHTML}async loadVisualConfig(e){let t=VisualConfig.visualTemplates;return t[e]||(t[e]=await fetch("/out/gui/"+e+".json").then(e=>e.json())),t[e]}}class Player{async init(){this.playlist=new Playlist}nextSong(){let e=this.playlist.getNext();audioHandler.loadSong(e)}prevSong(){let e=this.playlist.getPrevious();audioHandler.loadSong(e)}playStop(){if(!audioHandler.lastSong){let e=this.playlist.getCurrent();return void audioHandler.loadSong(e)}let e=audioHandler.audioFile;e.paused?e.play():e.pause(),window.dispatchEvent(new CustomEvent("playSong"))}playByID(e){this.playlist.index=e;let t=this.playlist.getCurrent();audioHandler.loadSong(t)}}const PAGINATIONLIMIT=50;class Playlist{constructor(){this.list=[],this.shuffled=[],this.index=0,this.page=0,this.isShuffle=pConf.get("shuffle",!1),$("body").addDelegatedEventListener("change",'input[type="file"]',this.changeFiles.bind(this)),$("body").addDelegatedEventListener("click",".pagination .item",this.handlePagination.bind(this)),eventHandler.addEvent("id3-request",this.handle.bind(this)),eventHandler.addEvent("id3-request-force",this.forceID3.bind(this))}shuffle(){let e=this.list.length;if(e<3)this.shuffled=[0,1,2];else for(let t=0;tt&&(a=0),this.index=a,e[this.getRealIndex()]}getPrevious(){let e=this.list,t=e.length-1,a=this.index-1;return a<0&&(a=t),this.index=a,e[this.getRealIndex()]}getCurrent(){return this.list[this.getRealIndex()]}setPlaylist(e){this.index=0,this.forceData=void 0,this.list=e,this.shuffle()}handlePagination(e,t){t.hasClass("inactive")||(t.hasClass("next-site")?this.renderPagination(this.page+1):this.renderPagination(this.page-1))}renderPagination(e){void 0===e&&(e=this.page);let t=this.list.length,a=Math.ceil(t/50)-1;e<0&&(e=0),e>a&&(e=a);let i=50*e,s=i+50,n="";if(this.page=e,s>=t&&(s=t),t>0){let e=this.list;for(let t=i;t1&&e0?"active":"inactive",nextActive:l?"active":"inactive",page:e+1+" / "+parseInt(a+1)}))}changeFiles(e,t){if("upload-dir"!==t.id)return;let a=[],i=0;for(let e of t.files)if(e&&-1!==e.type.indexOf("audio")&&null===e.name.match(".m3u")){let t=new AudioPlayerFile(e,i++);a.push(t)}this.setPlaylist(a),a.length>0?(NotificationHandler.createNotification("Songs added successfully!
Songs: "+a.length,"success",3e3),this.renderPagination(0)):NotificationHandler.createNotification("File Upload failed!","error",3e3)}getRealIndex(e){return void 0===e&&(e=this.index),this.isShuffle?this.shuffled[e]:e}handle(e){let t=e.index;"waiting"!==e.status&&(this.list[t].id3=e,this.timeout&&window.clearTimeout(this.timeout),this.timeout=setTimeout(this.renderPagination.bind(this),100))}forceID3(e){let t=this;t.forceData||(t.forceData={},t.forceNotification=NotificationHandler.createNotification("TagReader -> 0 / "+t.list.length,"info",-1));let a=e.index;if("waiting"===e.status)return;t.list[a].id3=e,t.forceData[a]=!0;let i=Object.keys(t.forceData).length;this.forceNotification.updateMessageOnly("TagReader -> "+i+" / "+t.list.length),i===t.list.length&&t.forceNotification.remove()}}class GUI{async init(){this.data={},this.modal=new Modal,await template.loadArray(["playlist-item","playlist","playlist-footer","audio-information","inputs/color","inputs/input","inputs/slider","inputs/switch","inputs/select","inputs/option","help"]),this.initDropZone()}openHelp(){gui.modal.renderModal("Help",template.parseTemplate("help",{})),gui.modal.showModal()}initDropZone(){"drag dragstart dragend dragover dragenter dragleave drop".split(" ").forEach(e=>{window.addEventListener(e,async e=>{e.preventDefault(),e.stopPropagation(),"drop"===e.type&&(e.dataTransfer.files.length>0?(e.dataTransfer.id="upload-dir",player.playlist.changeFiles(e,e.dataTransfer)):alert("Sorry you need to upload files!"))})})}}class GUIHelper{static fromJSON(e,t){let a=[];for(let i of e)switch(i.type){case"slider":a.push(GUIHelper.createSliders(i,t));break;case"color":a.push(GUIHelper.createColorPicker(i,t));break;case"checkbox":a.push(GUIHelper.createCheckbox(i,t));break;case"input":a.push(GUIHelper.createInputField(i,t));break;case"select":a.push(GUIHelper.createSelect(i,t));break;case"button":a.push(GUIHelper.createButton(i,t));break;default:console.error("Unknown Type: "+i.type)}return a.join(" ")}static createSliders(e,t){let a="";if("object"==typeof e.name)for(let i=0;i${e.name}`}}class Modal{constructor(){this.currentModal="",this.modal=$("#modal"),this.parent=this.modal.parentNode,this.modal.addDelegatedEventListener("click","header .close",this.closeModal.bind(this))}resetModal(){this.renderModal("","","")}renderModal(e,t,a){$("#modal").removeClass("lightMode"),this.currentModal=e,this.renderHeader(e),this.renderContent(t),this.renderFooter(a)}renderHeader(e){$("header .headline",this.modal).innerHTML=e}renderContent(e){$("modal-content",this.modal).innerHTML=e}renderFooter(e){$("modal-footer .inner",this.modal).innerHTML=e||"by VersusTuneZ"}closeModal(){this.parent.addClass("hide")}isCurrent(e){return e===this.currentModal}showModal(){this.parent.removeClass("hide")}}class Visual{constructor(){this.data=[],this.dataArray=[],this.name="Default"}updateData(){}updateFFT(e){}draw(){}setup(){}}class VisualDrawer{constructor(){this.visuals={wave:new Wave,wave2d:new Wave2D},this.lastMainColor={base:"#-1",color:[0,0,0]},this.lastSecondColor={base:"#-1",color:[0,0,0]}}init(){this.switch(pConf.get("visual","wave2d")),this.updateLoop()}switch(e){null!=this.visuals[e]&&(this.c=e,vConf.loadConfigByName(this.c),this.visuals[this.c].setup(),pConf.set("visual",this.c),pConf.save())}updateLoop(){let e=this.visuals[this.c],t=shaderHandler.use(this.c);this.updateSeekbar(),this.prepare(t),e.updateData(),e.draw(t),requestAnimationFrame(this.updateLoop.bind(this))}updateSeekbar(){cInfo.width=window.innerWidth,cInfo.height=window.innerHeight;let e=audioHandler.audioFile;if(ctx.clearRect(0,0,cInfo.width,cInfo.height),!e.paused&&pConf.get("showSeekbar",!0)){let t=e.duration,a=e.currentTime/t*cInfo.width;ctx.fillStyle=pConf.get("seekColor","#fff"),ctx.fillRect(0,c.height-10,a,c.height)}}prepare(e){c.width=window.innerWidth,c.height=window.innerHeight,gl.viewport(0,0,gl.canvas.width,gl.canvas.height),gl.clearColor(0,0,0,parseFloat(pConf.get("alphaValue",0))),gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT),gl.enable(gl.DEPTH_TEST),gl.depthFunc(gl.LEQUAL),gl.enable(gl.CULL_FACE),this.setColor(e)}setColor(e){let t=gl.getUniformLocation(e,"u_baseColor"),a=gl.getUniformLocation(e,"u_maxColor"),i=this.lastMainColor,s=this.lastSecondColor;this.updateColor("lastMainColor","baseColor"),this.updateColor("lastSecondColor","gradientToColor"),gl.uniform3fv(t,i.color),gl.uniform3fv(a,s.color)}updateColor(e,t){let a=this[e],i=vConf.get(t,"#ffffff");i!==a.base&&(a.color=hexToRgb(i),a.base=i)}}class ImageUploader{async init(){this.image=pConf.get("bgURL",""),this.color=pConf.get("bgColour","#000000"),this.alpha=pConf.get("alphaValue",.5),this.getRealImage(),this.applyValues(),$("#modal").addDelegatedEventListener("change",'#image-upload input:not([type="color"])',this.changeHandler.bind(this)),$("#modal").addDelegatedEventListener("input","#image-upload input#color",this.changeHandler.bind(this))}async renderModal(){await template.loadTemplate("image"),gui.modal.resetModal(),gui.modal.renderModal("Background-Image",template.parseTemplate("image",{value:this.image,bgValue:this.color,alphaValue:this.alpha}),""),gui.modal.showModal()}changeHandler(e,t){"color"===t.id?this.color=t.value:"alphaValue"===t.id?this.alpha=t.value:(pConf.set("bgMode",t.id),"image"===t.id?(t.files[0].toBase64((e,t)=>{t?alert("Error converting image!"):(pConf.set("bgURL",e.currentTarget.result),pConf.save())}),this.image=URL.createObjectURL(t.files[0])):(this.image=t.value,pConf.set("bgURL",this.image))),pConf.set("bgColour",this.color),pConf.set("alphaValue",this.alpha),this.applyValues(),pConf.save()}applyValues(){let e=$("body");e.style.backgroundImage="url("+this.image+")",e.style.backgroundColor=this.color}getRealImage(){let e=pConf.get("bgMode"),t=pConf.get("bgURL","");if("image"===e){if(""!==t&&t.startsWith("data:image")){let e=t.split(";"),a=e.shift(),i=e.join(";").replace("base64,","");this.image=URL.createObjectURL(b64toBlob(i,a))}}else this.image=t}}const imageUploader=new ImageUploader;class NotificationHandler{static instance=new NotificationHandler;constructor(){this.outer=$(".notification"),this.notifications=[]}async init(){await template.loadTemplate("notification")}static createNotification(e,t,a){a=parseInt(a||"3000");let i=NotificationHandler.instance,s=new Notification(e,t,a);return i.notifications.push(s),s.show(),s}}class Notification{constructor(e,t,a){this.outer=NotificationHandler.instance.outer,this.message=e,this.type=t,this.time=a,this.isRemoved=!1}async show(){let e=this,t=-1===e.time;e.item=create("div"),e.item.addClass("notification-item, "+e.type),t&&(e.type+=" endless"),e.updateContent(e.message),this.outer.prepend(e.item),t||setTimeout(this.remove.bind(this),e.time)}async remove(){if(this.isRemoved)return;this.isRemoved=!0,this.outer.removeChild(this.item);let e=NotificationHandler.instance.notifications,t=e.indexOf(this);e.splice(t,1)}updateContent(e){let t={message:e,time:-1===this.time?1e3:this.time+1,type:this.type};this.item.innerHTML=template.parseTemplate("notification",t)}updateMessageOnly(e){let t=$(".message",this.item);t&&(t.innerHTML=e)}}class Config{static allConfigs={};constructor(e){this.config={},this.name="",this.type=e,Config.allConfigs[e]=this}loadConfigByName(e){this.save(),this.name="config-"+e;let t=localStorage.getItem(this.name);t&&(this.config=JSON.parse(t))}save(){""!==this.name&&localStorage.setItem(this.name,JSON.stringify(this.config))}set(e,t){this.config[e]=t}remove(e){delete this.config[e]}get(e,t){let a=this.config[e];return null==a&&(this.config[e]=t,a=t),a}reset(){NotificationHandler.createNotification("CONFIG REQUEST SUCCESS FOR "+this.type,"success",2e3),this.config={},this.save()}}class Sphere extends Visual{constructor(){super(),this.name="Sphere"}draw(){}setup(){}}class Wave extends Visual{constructor(){super(),this.name="3D Wave"}updateData(){let e=audioHandler.getFloatArray(),t=2/e.length,a=-1,i=0;for(let s=0;s "+e.message)}return!0}return!1}}async function initHandler(){let e=$("body");$(".playlist.menu-icon").addEventListener("click",e=>{player.playlist.renderPagination(player.playlist.page),gui.modal.showModal()}),e.addDelegatedEventListener("click",".playlist-item",(e,t)=>{let a=t.dataset.index;player.playByID(parseInt(a)),togglePlayButton("pause")}),e.addDelegatedEventListener("click",".controls button",(e,t)=>{switch(t.id){case"previous":player.prevSong();break;case"next":player.nextSong();break;case"play":player.playStop();break;case"shuffle":toggleShuffle()}}),window.addEventListener("playSong",setActiveOnPlaylist),window.addEventListener("playSong",e=>{togglePlayButton(audioHandler.audioFile.paused?"play":"pause")}),$(".upload-image").addEventListener("click",imageUploader.renderModal.bind(imageUploader)),e.addDelegatedEventListener("click",".readAll",forceAllRead),e.addDelegatedEventListener("input",'.input-range input[type="range"]',(e,t)=>{$(".current",t.parentNode).innerText=t.value}),e.addDelegatedEventListener("input",'input[type="color"]',(e,t)=>{$(".colorBlob",t.parentNode).style.backgroundColor=t.value}),e.addDelegatedEventListener("click",".visual-item",(e,t)=>{visual.switch(t.dataset.id||"wave"),$("modal-content .visuals .active").removeClass("active"),t.addClass("active")}),e.addDelegatedEventListener("input","section.base input",(e,t)=>{"checkbox"===t.type?pConf.set(t.name,t.checked):setValue(t.name,t.value,pConf,t.dataset.type),pConf.save()}),e.addDelegatedEventListener("input","section.visual input",(e,t)=>{"checkbox"===t.type?vConf.set(t.name,t.checked):setValue(t.name,t.value,vConf,t.dataset.type),vConf.save()}),e.addDelegatedEventListener("click",".button[data-action]",(e,t)=>{switch(t.dataset.action){case"resetVConf":vConf.reset(),setTimeout(e=>{playerConf.handleById()},30);break;case"makeModalTransparent":$("#modal").toggleClass("lightMode")}}),$(".help.menu-icon").addEventListener("click",gui.openHelp),document.onfullscreenchange=t=>{e.hasClass("fullscreen")?e.removeClass("fullscreen"):e.addClass("fullscreen")}}function forceAllRead(){let e=player.playlist.list;for(let t=0;t{t.dataset.name===e?t.removeClass("hide"):t.addClass("hide")})}!function(){const e=$("body");e.addDelegatedEventListener("click","custom-select .label",(e,t)=>{let a=t.parentNode,i=$$("custom-option",a),s=$("custom-options",a);if(a.hasClass("open"))s.style.maxHeight="",a.removeClass("open");else{let e=0;i.forEach((function(t){e+=t.offsetHeight})),s.style.maxHeight=e+"px",a.addClass("open")}}),e.addDelegatedEventListener("click","custom-select custom-option",(e,t)=>{let a=t.closest("custom-select"),i=$("input",a);$$("custom-option.active").forEach(e=>{e.removeClass("active")}),t.addClass("active"),i&&(i.value=t.dataset.value||t.innerText,$(".label",a).innerText=t.innerText,a.removeClass("open"),t.parentNode.style.maxHeight="",window.dispatchEvent(new CustomEvent("selectChanged",{detail:{select:a,event:a.dataset.event,value:i.value,name:i.name}})))}),window.addEventListener("selectChanged",e=>{"visualConf"===e.detail.event&&(e.preventDefault(),e.stopPropagation(),function(e){try{let t=e.value,a=Config.allConfigs[e.select.dataset.conf];"fftSize"===e.name&&(t=parseInt(e.value),visual.visuals[visual.c].updateFFT(t)),a.set(e.name,t),a.save()}catch(e){console.error(e)}}(e.detail))})}();class KeyHandler{async init(){await this.mediaKeys(),await this.addKeyHandler(),window.addEventListener("keydown",this.keyHandler.bind(this))}async mediaKeys(){if("mediaSession"in navigator){let e=navigator.mediaSession;e.setActionHandler("play",player.playStop.bind(player)),e.setActionHandler("pause",player.playStop.bind(player)),e.setActionHandler("previoustrack",player.prevSong.bind(player)),e.setActionHandler("nexttrack",player.prevSong.bind(player))}}async addKeyHandler(){eventHandler.addEvent("keys-Space",player.playStop.bind(player)),eventHandler.addEvent("keys-KeyN",player.nextSong.bind(player)),eventHandler.addEvent("keys-KeyV",player.prevSong.bind(player)),eventHandler.addEvent("keys-KeyS",playerConf.open.bind(playerConf)),eventHandler.addEvent("keys-KeyS-shift",toggleShuffle),eventHandler.addEvent("keys-KeyB",imageUploader.renderModal.bind(imageUploader)),eventHandler.addEvent("keys-KeyF-shift",forceAllRead),eventHandler.addEvent("keys-KeyH",gui.openHelp),eventHandler.addEvent("keys-KeyP",e=>{player.playlist.renderPagination(player.playlist.page),gui.modal.showModal()}),eventHandler.addEvent("keys-Escape, keys-KeyC-shift",e=>{gui.modal.resetModal(),gui.modal.closeModal()}),eventHandler.addEvent("keys-F11",e=>{document.fullscreenElement?document.exitFullscreen().catch(console.error):document.body.requestFullscreen().catch(console.error)})}async keyHandler(e){let t="keys-"+e.code+(e.shiftKey?"-shift":"")+(e.ctrlKey?"-ctrl":"");eventHandler.handleEvent({data:{cmd:t}})&&(e.preventDefault(),e.stopPropagation())}}class Startup{constructor(){this.modules={startup:!1,"id3-ready":!1}}moduleLoaded(e){this.modules[e]=!0,this.allModulesLoaded()}allModulesLoaded(){for(let e in this.modules)if(!this.modules[e])return!1;return window.dispatchEvent(new CustomEvent("startupFin")),!0}}const shaderHandler=new ShaderHandler(null),audioHandler=new AudioHandler,gui=new GUI,visual=new VisualDrawer,template=new Template,player=new Player,vConf=new Config("visual"),pConf=new Config("player"),worker=new Worker("/out/js/worker.min.js"),startup=new Startup,eventHandler=new EventHandler,playerConf=new PlayerConfigHandler,keyHandler=new KeyHandler;let c,gl,cInfo,ctx;async function startUP(){if(pConf.loadConfigByName("default"),c=$("#c"),gl=c.getContext("webgl2"),cInfo=$("#cInfo"),ctx=cInfo.getContext("2d"),!gl)return alert("SORRY THE BROWSER DOESN'T SUPPORT WEBGL2"),!1;shaderHandler.setGL(gl),await shaderHandler.loadArray(["wave","sphere","water","wave2d"],"shaders/"),await NotificationHandler.instance.init(),await audioHandler.init(),await player.init(),await visual.init(),await gui.init(),await imageUploader.init(),await playerConf.init(),await keyHandler.init(),await initHandler(),toggleShuffle(!1)}worker.addEventListener("message",e=>{"startup"!==e.data.status?eventHandler.handleEvent(e):startup.moduleLoaded(e.data.cmd)}),window.addEventListener("startupFin",e=>{setTimeout(e=>{$(".loading-screen").remove()},100)}),startUP().then(e=>{startup.moduleLoaded("startup")}); \ No newline at end of file +class VTUtils{static random(e,t){let a=Math.random();if(void 0===e)return a;if(void 0===t)return e instanceof Array?e[Math.floor(a*e.length)]:a*e;if(e>t){let a=e;e=t,t=a}return a*(t-e)+e}static randomInt(e,t){return Math.floor(VTUtils.random(e,t))}static normalize(e,t,a){return(e-a)/(t-a)}static distance(e,t,a,i){let s=e-a,n=t-i;return Math.sqrt(s*s+n*n)}static map(e,t,a,i,s,n){let r=(e-t)/(a-t)*(s-i)+i;return n?i>16&255)/255,(t>>8&255)/255,(255&t)/255]}Node.prototype.addDelegatedEventListener=function(e,t,a){this.addEventListener(e,e=>{let i=e.target;if(i.matches(t))a(e,i);else{let s=i.closest(t);if(s)try{a(e,s)}catch(e){NotificationHandler.createNotification("FATAL ERROR WITHIN HANDLER!","error",1e3)}}})},Node.prototype.hasClass=function(e){let t=e.split(","),a=null;for(let e of t){if(!1===a)break;a=this.classList.contains(e.trim())}return!0===a},Node.prototype.addClass=function(e){let t=e.split(",");for(let e of t)this.classList.add(e.trim());return this},Node.prototype.removeClass=function(e){let t=e.split(",");for(let e of t)this.classList.remove(e.trim());return this},Node.prototype.toggleClass=function(e,t){let a=e.split(",");for(let e of a)this.classList.toggle(e.trim(),t)},Node.prototype.switchClass=function(e,t,a){let i=this.classList;a?(i.remove(e),i.add(t)):(i.remove(t),i.add(e))},Node.prototype.toggleCheck=function(e,t){let a=this.classList,i=e.split(",");for(let e of i){let i=e.trim();t?a.add(i):a.remove(i)}},String.prototype.firstUpper=function(){return this.charAt(0).toUpperCase()+this.slice(1)},File.prototype.toBase64=function(e){const t=new FileReader;t.onloadend=e,t.readAsDataURL(this)};class TDUtils{static lastMatrix={m:null};static multiply(e,t){let a=t[0],i=t[1],s=t[2],n=t[3],r=t[4],l=t[5],o=t[6],d=t[7],c=t[8],h=t[9],u=t[10],p=t[11],g=t[12],f=t[13],m=t[14],y=t[15],v=e[0],w=e[1],C=e[2],T=e[3],x=e[4],H=e[5],b=e[6],S=e[7],U=e[8],A=e[9],E=e[10],L=e[11],D=e[12],k=e[13],F=e[14],M=e[15];return[a*v+i*x+s*U+n*D,a*w+i*H+s*A+n*k,a*C+i*b+s*E+n*F,a*T+i*S+s*L+n*M,r*v+l*x+o*U+d*D,r*w+l*H+o*A+d*k,r*C+l*b+o*E+d*F,r*T+l*S+o*L+d*M,c*v+h*x+u*U+p*D,c*w+h*H+u*A+p*k,c*C+h*b+u*E+p*F,c*T+h*S+u*L+p*M,g*v+f*x+m*U+y*D,g*w+f*H+m*A+y*k,g*C+f*b+m*E+y*F,g*T+f*S+m*L+y*M]}static translate(e,t,a,i,s){s=s||new Float32Array(16);let n=e[0],r=e[1],l=e[2],o=e[3],d=e[4],c=e[5],h=e[6],u=e[7],p=e[8],g=e[9],f=e[10],m=e[11],y=e[12],v=e[13],w=e[14],C=e[15];return s[0]=n,s[1]=r,s[2]=l,s[3]=o,s[4]=d,s[5]=c,s[6]=h,s[7]=u,s[8]=p,s[9]=g,s[10]=f,s[11]=m,s[12]=n*t+d*a+p*i+y,s[13]=r*t+c*a+g*i+v,s[14]=l*t+h*a+f*i+w,s[15]=o*t+u*a+m*i+C,s}static xRotation(e){e=TDUtils.degToRad(e);let t=Math.cos(e),a=Math.sin(e);return[1,0,0,0,0,t,a,0,0,-a,t,0,0,0,0,1]}static yRotation(e){e=TDUtils.degToRad(e);let t=Math.cos(e),a=Math.sin(e);return[t,0,-a,0,0,1,0,0,a,0,t,0,0,0,0,1]}static zRotation(e){e=TDUtils.degToRad(e);let t=Math.cos(e),a=Math.sin(e);return[t,a,0,0,-a,t,0,0,0,0,1,0,0,0,0,1]}static degToRad(e){return e*Math.PI/180}static scale(e,t,a,i){return(i=i||new Float32Array(16))[0]=e,i[5]=t,i[10]=a,i}static lookAt(e,t,a,i){i=i||new Float32Array(16);let s=TDUtils.normalize(TDUtils.subtractVectors(e,t)),n=TDUtils.normalize(TDUtils.cross(a,s)),r=TDUtils.normalize(TDUtils.cross(s,n));return i[0]=n[0],i[1]=n[1],i[2]=n[2],i[4]=r[0],i[5]=r[1],i[6]=r[2],i[8]=s[0],i[9]=s[1],i[10]=s[2],i[12]=e[0],i[13]=e[1],i[14]=e[2],i[15]=1,i}static cross(e,t,a){return(a=a||new Float32Array(3))[0]=e[1]*t[2]-e[2]*t[1],a[1]=e[2]*t[0]-e[0]*t[2],a[2]=e[0]*t[1]-e[1]*t[0],a}static normalize(e,t){t=t||new Float32Array(3);let a=Math.sqrt(e[0]*e[0]+e[1]*e[1]+e[2]*e[2]);return a>1e-5&&(t[0]=e[0]/a,t[1]=e[1]/a,t[2]=e[2]/a),t}static subtractVectors(e,t,a){return(a=a||new Float32Array(3))[0]=e[0]-t[0],a[1]=e[1]-t[1],a[2]=e[2]-t[2],a}static perspective(e,t,a,i,s){s=s||new Float32Array(16);let n=Math.tan(.5*Math.PI-.5*e),r=1/(a-i);return s[0]=n/t,s[5]=n,s[10]=(a+i)*r,s[11]=-1,s[14]=a*i*r*2,s}static projection(e,t,a){return[2/e,0,0,0,0,-2/t,0,0,0,0,2/a,0,-1,1,0,1]}static inverse(e,t){t=t||new Float32Array(16);let a=e[0],i=e[1],s=e[2],n=e[3],r=e[4],l=e[5],o=e[6],d=e[7],c=e[8],h=e[9],u=e[10],p=e[11],g=e[12],f=e[13],m=e[14],y=e[15],v=u*y,w=m*p,C=o*y,T=m*d,x=o*p,H=u*d,b=s*y,S=m*n,U=s*p,A=u*n,E=s*d,L=o*n,D=c*f,k=g*h,F=r*f,M=g*l,R=r*h,I=c*l,N=a*f,P=g*i,V=a*h,$=c*i,B=a*l,z=r*i,O=v*l+T*h+x*f-(w*l+C*h+H*f),_=w*i+b*h+A*f-(v*i+S*h+U*f),G=C*i+S*l+E*f-(T*i+b*l+L*f),j=H*i+U*l+L*h-(x*i+A*l+E*h),K=1/(a*O+r*_+c*G+g*j);return t[0]=K*O,t[1]=K*_,t[2]=K*G,t[3]=K*j,t[4]=K*(w*r+C*c+H*g-(v*r+T*c+x*g)),t[5]=K*(v*a+S*c+U*g-(w*a+b*c+A*g)),t[6]=K*(T*a+b*r+L*g-(C*a+S*r+E*g)),t[7]=K*(x*a+A*r+E*c-(H*a+U*r+L*c)),t[8]=K*(D*d+M*p+R*y-(k*d+F*p+I*y)),t[9]=K*(k*n+N*p+$*y-(D*n+P*p+V*y)),t[10]=K*(F*n+P*d+B*y-(M*n+N*d+z*y)),t[11]=K*(I*n+V*d+z*p-(R*n+$*d+B*p)),t[12]=K*(F*u+I*m+k*o-(R*m+D*o+M*u)),t[13]=K*(V*m+D*s+P*u-(N*u+$*m+k*s)),t[14]=K*(N*o+z*m+M*s-(B*m+F*s+P*o)),t[15]=K*(B*u+R*s+$*o-(V*o+z*u+I*s)),t}static aspectView(e){return[1/e,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]}static updateRotate(e,t){let a=vConf.get(e,t)+vConf.get(e+"-inc",0);a>360?a-=360:a<-360&&(a+=360),vConf.set(e,a)}static makeZToWMatrix(e){return[1,0,0,0,0,1,0,0,0,0,1,e,0,0,0,1]}}class GLHelper{constructor(e){this.matrix=new Float32Array(16),this.program=e}static uniform4fv(e,t,a){let i=gl.getUniformLocation(e,t);gl.uniform4fv(i,a)}static uniform3fv(e,t,a){let i=gl.getUniformLocation(e,t);gl.uniform3fv(i,a)}static uniform1f(e,t,a){let i=gl.getUniformLocation(e,t);gl.uniform1f(i,a)}rotateX(e){this.matrix=TDUtils.multiply(this.matrix,TDUtils.xRotation(e))}rotateY(e){this.matrix=TDUtils.multiply(this.matrix,TDUtils.yRotation(e))}rotateZ(e){this.matrix=TDUtils.multiply(this.matrix,TDUtils.zRotation(e))}scale(e){this.matrix=TDUtils.multiply(this.matrix,TDUtils.scale(e[0],e[1],e[2]))}project(e){e=e||c.width>c.height?c.width:c.height,this.matrix=TDUtils.projection(c.width,c.height,e)}translate(e){this.matrix=TDUtils.translate(this.matrix,e[0]||0,e[1]||0,e[2]||0)}addFudgeFactor(e){this.matrix=TDUtils.multiply(TDUtils.makeZToWMatrix(e),this.matrix)}applyMatrix(){let e=gl.getUniformLocation(this.program,"u_matrix");gl.uniformMatrix4fv(e,!1,this.matrix)}}class Camera{constructor(){this.mouse,this.rotation={x:0,y:0},this.lastMouse,this.mousePressed=!1,this.translate={x:0,y:0,z:0}}async init(){this.mouse={x:0,y:0},window.addEventListener("mousedown",this.mouseDown.bind(this)),window.addEventListener("mouseup",this.mouseUp.bind(this)),window.addEventListener("mousemove",this.mouseMove.bind(this),{passive:!0}),eventHandler.addEvent("keys-ArrowUp, keys-ArrowDown, keys-ArrowLeft, keys-ArrowRight, keys-KeyQ, keys-KeyE",this.keyPressed.bind(this))}mouseDown(){this.mousePressed=!0,this.lastMouse=null}mouseUp(){this.mousePressed=!1,this.lastMouse=null}mouseMove(e){if(this.mousePressed&&!gui.modal.open){if(this.lastMouse){let t=this.mouse,a=this.rotation;t.x+=.2*(this.lastMouse.x-e.clientX),t.y+=.2*(this.lastMouse.y-e.clientY),a.x=VTUtils.map(t.x,-c.width,c.width,180,-180,!1),a.y=VTUtils.map(t.y,-c.height,c.height,180,-180,!1)}this.lastMouse={x:e.clientX,y:e.clientY}}}keyPressed(e){switch(e){case"keys-ArrowUp":this.translate.z+=10;break;case"keys-ArrowDown":this.translate.z-=10;break;case"keys-ArrowLeft":this.translate.x-=10;break;case"keys-ArrowRight":this.translate.x+=10;break;case"keys-KeyQ":this.translate.y+=10;break;case"keys-KeyE":this.translate.y-=10}}}class Template{constructor(){this.tpl={}}async loadTemplate(e){let t=this;this.tpl[e]||(t.tpl[e]=await FetchHandler.loadFile(templateDir+e+".tpl",!1))}async loadArray(e){for(let t of e)await this.loadTemplate(t)}parseTemplate(e,t){if(!this.tpl[e])return"";let a,i=this.tpl[e];for(;null!==(a=templateEx.exec(i));){a.index===templateEx.lastIndex&&templateEx.lastIndex++;let e=a[0],s=t[a[1]];null==s&&(s=""),i=i.replace(e,s)}return i}}const templateEx=/\$(.*?)\$/gm,templateDir="/out/tpl/";class ShaderHandler{constructor(e){this.gl=e,this.shaderNames=[],this.shaders={},this.programs={}}setGL(e){this.gl=e}async loadShader(e,t){this.shaderNames.push(e),await this.load(e,t+e+".vert",this.gl.VERTEX_SHADER),await this.load(e,t+e+".frag",this.gl.FRAGMENT_SHADER)}async load(e,t,a){let i=e+"_"+a;if(!this.shaders[i]){let e=await FetchHandler.loadFile(t,!1),s=this.createShader(e,a);s&&(this.shaders[i]=s)}return!!this.shaders[i]}getShader(e,t){let a=e+"_"+t;return this.shaders[a]}getAllShaders(){return this.shaderNames}async createProgramForEach(e){e=e||this.shaderNames;for(let t=0;t Currently no Song is uploaded!","error",2e3),!1;let t=this,a=e.file;t.lastSong&&URL.revokeObjectURL(t.lastSong),t.lastSong=this.audioFile.src=URL.createObjectURL(a),this.isStarted||this.start().catch(alert),this.audioFile.play().then(t=>{pConf.get("showPlaying","true")&&NotificationHandler.createNotification("Now Playing:"+e.getAudioName(),"info",pConf.get("showPlayingTime",1e3)),window.dispatchEvent(new CustomEvent("playSong"))}).catch(e=>{NotificationHandler.createNotification(e.message,"error",1e3),player.nextSong()})}getIntArray(e){let t=new Uint8Array(e);return this.analyser.getByteFrequencyData(t),t}getFloatArray(){let e=new Float32Array(this.analyser.fftSize);return this.analyser.getFloatTimeDomainData(e),e}}class AudioPlayerFile{constructor(e,t){this.file=e,this.name=this.getName(),this.id3=null,this.index=t}getName(){let e=this.file.name.split(".");return e.pop(),e=e.join("."),e}getID3Tag(e){return e||null===this.id3?(eventHandler.sendData("getData",{file:this.file,name:this.name,index:this.index,force:!0===e}),{title:this.name,artist:"VA"}):this.id3}getAudioName(){let e=this.getID3Tag();return template.parseTemplate("audio-information",e)}}class FetchHandler{static files={};static async loadFiles(e,t){let a=[];for(let i=0;i';for(let a=0;a",this.content.innerHTML=t}async renderBase(){let e=await this.loadVisualConfig("base"),t=create("section");t.addClass("base"),t.innerHTML=GUIHelper.fromJSON(e,pConf),this.content.innerHTML=t.outerHTML}async renderVisualConfig(e){let t=await this.loadVisualConfig(e,vConf),a=create("section");a.addClass("visual"),a.innerHTML=GUIHelper.fromJSON(t,vConf),a.innerHTML+=GUIHelper.createButton({action:"resetVConf",name:"Reset Visual Config"}),a.innerHTML+=GUIHelper.createButton({action:"makeModalTransparent",name:"toggle Modal Opacity"}),this.content.innerHTML=a.outerHTML}async loadVisualConfig(e){let t=VisualConfig.visualTemplates;return t[e]||(t[e]=await FetchHandler.loadFile("/out/gui/"+e+".json",!0)),t[e]}}class Player{async init(){this.playlist=new Playlist}nextSong(){let e=this.playlist.getNext();audioHandler.loadSong(e)}prevSong(){let e=this.playlist.getPrevious();audioHandler.loadSong(e)}playStop(){if(!audioHandler.lastSong){let e=this.playlist.getCurrent();return void audioHandler.loadSong(e)}let e=audioHandler.audioFile;e.paused?e.play():e.pause(),window.dispatchEvent(new CustomEvent("playSong"))}stop(){if(!audioHandler.lastSong)return;let e=audioHandler.audioFile;e.pause(),e.currentTime=0,window.dispatchEvent(new CustomEvent("playSong"))}playByID(e){this.playlist.index=e;let t=this.playlist.getCurrent();audioHandler.loadSong(t)}}const PAGINATIONLIMIT=50;class Playlist{constructor(){this.list=[],this.shuffled=[],this.index=0,this.page=0,this.isShuffle=pConf.get("shuffle",!1),$("body").addDelegatedEventListener("change",'input[type="file"]',this.changeFiles.bind(this)),$("body").addDelegatedEventListener("click",".pagination .item",this.handlePagination.bind(this)),eventHandler.addEvent("id3-request",this.handle.bind(this)),eventHandler.addEvent("id3-request-force",this.forceID3.bind(this))}shuffle(){let e=this.list.length;if(e<3)this.shuffled=[0,1,2];else for(let t=0;tt&&(a=0),this.index=a,e[this.getRealIndex()]}getPrevious(){let e=this.list,t=e.length-1,a=this.index-1;return a<0&&(a=t),this.index=a,e[this.getRealIndex()]}getCurrent(){return this.list[this.getRealIndex()]}setPlaylist(e){this.index=0,this.forceData=void 0,this.list=e,this.shuffle()}handlePagination(e,t){t.hasClass("inactive")||(t.hasClass("next-site")?this.renderPagination(this.page+1):this.renderPagination(this.page-1))}renderPagination(e){void 0===e&&(e=this.page);let t=this.list.length,a=Math.ceil(t/50)-1;e<0&&(e=0),e>a&&(e=a);let i=50*e,s=i+50,n="";if(this.page=e,s>=t&&(s=t),t>0){let e=this.list;for(let t=i;t1&&e0?"active":"inactive",nextActive:r?"active":"inactive",page:e+1+" / "+parseInt(a+1)}))}changeFiles(e,t){if("upload-dir"!==t.id)return;let a=[],i=0;for(let e of t.files)if(e&&-1!==e.type.indexOf("audio")&&null===e.name.match(".m3u")){let t=new AudioPlayerFile(e,i++);a.push(t)}this.setPlaylist(a),a.length>0?(NotificationHandler.createNotification("Songs added successfully!
Songs: "+a.length,"success",3e3),this.renderPagination(0)):NotificationHandler.createNotification("File Upload failed!","error",3e3)}getRealIndex(e){return void 0===e&&(e=this.index),this.isShuffle?this.shuffled[e]:e}handle(e){let t=e.index;"waiting"!==e.status&&(this.list[t].id3=e,this.timeout&&window.clearTimeout(this.timeout),this.timeout=setTimeout(this.renderPagination.bind(this),100))}forceID3(e){let t=this;t.forceData||(t.forceData={},t.forceNotification=NotificationHandler.createNotification("TagReader -> 0 / "+t.list.length,"info",-1));let a=e.index;if("waiting"===e.status)return;t.list[a].id3=e,t.forceData[a]=!0;let i=Object.keys(t.forceData).length;this.forceNotification.updateMessageOnly("TagReader -> "+i+" / "+t.list.length),i===t.list.length&&t.forceNotification.remove()}}class GUI{async init(){this.data={},this.modal=new Modal,await template.loadArray(["playlist-item","playlist","playlist-footer","audio-information","inputs/color","inputs/input","inputs/slider","inputs/switch","inputs/select","inputs/option","help"]),this.initDropZone()}openHelp(){gui.modal.renderModal("Help",template.parseTemplate("help",{})),gui.modal.showModal()}initDropZone(){"drag dragstart dragend dragover dragenter dragleave drop".split(" ").forEach(e=>{window.addEventListener(e,async e=>{e.preventDefault(),e.stopPropagation(),"drop"===e.type&&(e.dataTransfer.files.length>0?(e.dataTransfer.id="upload-dir",player.playlist.changeFiles(e,e.dataTransfer)):alert("Sorry you need to upload files!"))})})}}class GUIHelper{static fromJSON(e,t){let a=[];for(let i of e)switch(i.type){case"slider":a.push(GUIHelper.createSliders(i,t));break;case"color":a.push(GUIHelper.createColorPicker(i,t));break;case"checkbox":a.push(GUIHelper.createCheckbox(i,t));break;case"input":a.push(GUIHelper.createInputField(i,t));break;case"select":a.push(GUIHelper.createSelect(i,t));break;case"button":a.push(GUIHelper.createButton(i,t));break;default:console.error("Unknown Type: "+i.type)}return a.join(" ")}static createSliders(e,t){let a="";if("object"==typeof e.name)for(let i=0;i${e.name}`}}class Modal{constructor(){this.currentModal="",this.modal=$("#modal"),this.open=!1,this.parent=this.modal.parentNode,this.modal.addDelegatedEventListener("click","header .close",this.closeModal.bind(this))}resetModal(){this.renderModal("","","")}renderModal(e,t,a){$("#modal").removeClass("lightMode"),this.currentModal=e,this.renderHeader(e),this.renderContent(t),this.renderFooter(a)}renderHeader(e){$("header .headline",this.modal).innerHTML=e}renderContent(e){$("modal-content",this.modal).innerHTML=e}renderFooter(e){$("modal-footer .inner",this.modal).innerHTML=e||"by VersusTuneZ"}closeModal(){this.parent.addClass("hide"),this.open=!1}isCurrent(e){return e===this.currentModal}showModal(){this.parent.removeClass("hide"),this.open=!0}}class Visual{constructor(){this.data=[],this.dataArray=[],this.name="Default"}updateData(){}updateFFT(e){}draw(){}setup(){}}class VisualDrawer{constructor(){this.visuals={wave:new Wave,wave2d:new Wave2D},this.lastMainColor={base:"#-1",color:[0,0,0]},this.lastSecondColor={base:"#-1",color:[0,0,0]}}init(){this.switch(pConf.get("visual","wave2d")),this.updateLoop()}switch(e){null!=this.visuals[e]&&(this.c=e,vConf.loadConfigByName(this.c),this.visuals[this.c].setup(),pConf.set("visual",this.c),pConf.save())}updateLoop(){let e=this.visuals[this.c],t=shaderHandler.use(this.c);this.updateSeekbar(),this.prepare(t),e.updateData(),e.draw(t),requestAnimationFrame(this.updateLoop.bind(this))}updateSeekbar(){cInfo.width=window.innerWidth,cInfo.height=window.innerHeight;let e=audioHandler.audioFile;if(ctx.clearRect(0,0,cInfo.width,cInfo.height),!e.paused&&pConf.get("showSeekbar",!0)){let t=e.duration,a=e.currentTime/t*cInfo.width;ctx.fillStyle=pConf.get("seekColor","#fff"),ctx.fillRect(0,c.height-10,a,c.height)}}prepare(e){c.width=window.innerWidth,c.height=window.innerHeight,gl.viewport(0,0,gl.canvas.width,gl.canvas.height),gl.clearColor(0,0,0,parseFloat(pConf.get("alphaValue",0))),gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT),gl.enable(gl.DEPTH_TEST),gl.depthFunc(gl.LEQUAL),gl.enable(gl.CULL_FACE),this.setColor(e)}setColor(e){let t=gl.getUniformLocation(e,"u_baseColor"),a=gl.getUniformLocation(e,"u_maxColor"),i=this.lastMainColor,s=this.lastSecondColor;this.updateColor("lastMainColor","baseColor"),this.updateColor("lastSecondColor","gradientToColor"),gl.uniform3fv(t,i.color),gl.uniform3fv(a,s.color)}updateColor(e,t){let a=this[e],i=vConf.get(t,"#ffffff");i!==a.base&&(a.color=hexToRgb(i),a.base=i)}}class ImageUploader{async init(){this.image=pConf.get("bgURL",""),this.color=pConf.get("bgColour","#000000"),this.alpha=pConf.get("alphaValue",.5),this.getRealImage(),this.applyValues(),$("#modal").addDelegatedEventListener("change",'#image-upload input:not([type="color"])',this.changeHandler.bind(this)),$("#modal").addDelegatedEventListener("input","#image-upload input#color",this.changeHandler.bind(this))}async renderModal(){await template.loadTemplate("image"),gui.modal.resetModal(),gui.modal.renderModal("Background-Image",template.parseTemplate("image",{value:this.image,bgValue:this.color,alphaValue:this.alpha}),""),gui.modal.showModal()}changeHandler(e,t){"color"===t.id?this.color=t.value:"alphaValue"===t.id?this.alpha=t.value:(pConf.set("bgMode",t.id),"image"===t.id?(t.files[0].toBase64((e,t)=>{t?alert("Error converting image!"):(pConf.set("bgURL",e.currentTarget.result),pConf.save())}),this.image=URL.createObjectURL(t.files[0])):(this.image=t.value,pConf.set("bgURL",this.image))),pConf.set("bgColour",this.color),pConf.set("alphaValue",this.alpha),this.applyValues(),pConf.save()}applyValues(){let e=$("body");e.style.backgroundImage="url("+this.image+")",e.style.backgroundColor=this.color}getRealImage(){let e=pConf.get("bgMode"),t=pConf.get("bgURL","");if("image"===e){if(""!==t&&t.startsWith("data:image")){let e=t.split(";"),a=e.shift(),i=e.join(";").replace("base64,","");this.image=URL.createObjectURL(b64toBlob(i,a))}}else this.image=t}}const imageUploader=new ImageUploader;class NotificationHandler{static instance=new NotificationHandler;constructor(){this.outer=$(".notification"),this.notifications=[]}async init(){await template.loadTemplate("notification")}static createNotification(e,t,a){a=parseInt(a||"3000");let i=NotificationHandler.instance,s=new Notification(e,t,a);return i.notifications.push(s),s.show(),s}}class Notification{constructor(e,t,a){this.outer=NotificationHandler.instance.outer,this.message=e,this.type=t,this.time=a,this.isRemoved=!1}async show(){let e=this,t=-1===e.time;e.item=create("div"),e.item.addClass("notification-item, "+e.type),t&&(e.type+=" endless"),e.updateContent(e.message),this.outer.prepend(e.item),t||setTimeout(this.remove.bind(this),e.time)}async remove(){if(this.isRemoved)return;this.isRemoved=!0,this.outer.removeChild(this.item);let e=NotificationHandler.instance.notifications,t=e.indexOf(this);e.splice(t,1)}updateContent(e){let t={message:e,time:-1===this.time?1e3:this.time+1,type:this.type};this.item.innerHTML=template.parseTemplate("notification",t)}updateMessageOnly(e){let t=$(".message",this.item);t&&(t.innerHTML=e)}}class Config{static allConfigs={};constructor(e){this.config={},this.name="",this.type=e,Config.allConfigs[e]=this}loadConfigByName(e){this.save(),this.name="config-"+e;let t=localStorage.getItem(this.name);t&&(this.config=JSON.parse(t))}save(){""!==this.name&&localStorage.setItem(this.name,JSON.stringify(this.config))}set(e,t){this.config[e]=t}remove(e){delete this.config[e]}get(e,t){let a=this.config[e];return null==a&&(this.config[e]=t,a=t),a}reset(){NotificationHandler.createNotification("CONFIG REQUEST SUCCESS FOR "+this.type,"success",2e3),this.config={},this.save()}}class Sphere extends Visual{constructor(){super(),this.name="Sphere"}draw(){}setup(){}}class Wave extends Visual{constructor(){super(),this.name="3D Wave"}updateData(){let e=audioHandler.getFloatArray(),t=2/e.length,a=-1,i=0;for(let s=0;s "+e.message)}return!0}return!1}}async function initHandler(){let e=$("body");$(".playlist.menu-icon").addEventListener("click",e=>{player.playlist.renderPagination(player.playlist.page),gui.modal.showModal()}),e.addDelegatedEventListener("click",".playlist-item",(e,t)=>{let a=t.dataset.index;player.playByID(parseInt(a)),togglePlayButton("pause")}),e.addDelegatedEventListener("click",".controls button",(e,t)=>{switch(t.id){case"previous":player.prevSong();break;case"next":player.nextSong();break;case"play":player.playStop();break;case"shuffle":toggleShuffle()}}),window.addEventListener("playSong",setActiveOnPlaylist),window.addEventListener("playSong",e=>{togglePlayButton(audioHandler.audioFile.paused?"play":"pause")}),$(".upload-image").addEventListener("click",imageUploader.renderModal.bind(imageUploader)),e.addDelegatedEventListener("click",".readAll",forceAllRead),e.addDelegatedEventListener("input",'.input-range input[type="range"]',(e,t)=>{$(".current",t.parentNode).innerText=t.value}),e.addDelegatedEventListener("input",'input[type="color"]',(e,t)=>{$(".colorBlob",t.parentNode).style.backgroundColor=t.value}),e.addDelegatedEventListener("click",".visual-item",(e,t)=>{visual.switch(t.dataset.id||"wave"),$("modal-content .visuals .active").removeClass("active"),t.addClass("active")}),e.addDelegatedEventListener("input","section.base input",(e,t)=>{"checkbox"===t.type?pConf.set(t.name,t.checked):setValue(t.name,t.value,pConf,t.dataset.type),pConf.save()}),e.addDelegatedEventListener("input","section.visual input",(e,t)=>{"checkbox"===t.type?vConf.set(t.name,t.checked):setValue(t.name,t.value,vConf,t.dataset.type),vConf.save()}),e.addDelegatedEventListener("click",".button[data-action]",(e,t)=>{switch(t.dataset.action){case"resetVConf":vConf.reset(),setTimeout(e=>{playerConf.handleById()},30);break;case"makeModalTransparent":$("#modal").toggleClass("lightMode")}}),$(".help.menu-icon").addEventListener("click",gui.openHelp),document.onfullscreenchange=t=>{e.hasClass("fullscreen")?e.removeClass("fullscreen"):e.addClass("fullscreen")}}function forceAllRead(){let e=player.playlist.list;for(let t=0;t{t.dataset.name===e?t.removeClass("hide"):t.addClass("hide")})}!function(){const e=$("body");e.addDelegatedEventListener("click","custom-select .label",(e,t)=>{let a=t.parentNode,i=$$("custom-option",a),s=$("custom-options",a);if(a.hasClass("open"))s.style.maxHeight="",a.removeClass("open");else{let e=0;i.forEach((function(t){e+=t.offsetHeight})),s.style.maxHeight=e+"px",a.addClass("open")}}),e.addDelegatedEventListener("click","custom-select custom-option",(e,t)=>{let a=t.closest("custom-select"),i=$("input",a);$$("custom-option.active").forEach(e=>{e.removeClass("active")}),t.addClass("active"),i&&(i.value=t.dataset.value||t.innerText,$(".label",a).innerText=t.innerText,a.removeClass("open"),t.parentNode.style.maxHeight="",window.dispatchEvent(new CustomEvent("selectChanged",{detail:{select:a,event:a.dataset.event,value:i.value,name:i.name}})))}),window.addEventListener("selectChanged",e=>{"visualConf"===e.detail.event&&(e.preventDefault(),e.stopPropagation(),function(e){try{let t=e.value,a=Config.allConfigs[e.select.dataset.conf];"fftSize"===e.name&&(t=parseInt(e.value),visual.visuals[visual.c].updateFFT(t)),a.set(e.name,t),a.save()}catch(e){console.error(e)}}(e.detail))})}();class KeyHandler{async init(){await this.mediaKeys(),await this.addKeyHandler(),window.addEventListener("keydown",this.keyHandler.bind(this))}async mediaKeys(){if("mediaSession"in navigator){let e=navigator.mediaSession;e.setActionHandler("play",player.playStop.bind(player)),e.setActionHandler("pause",player.playStop.bind(player)),e.setActionHandler("previoustrack",player.prevSong.bind(player)),e.setActionHandler("nexttrack",player.nextSong.bind(player)),e.setActionHandler("stop",player.stop.bind(player))}}async addKeyHandler(){eventHandler.addEvent("keys-Space",player.playStop.bind(player)),eventHandler.addEvent("keys-KeyN",player.nextSong.bind(player)),eventHandler.addEvent("keys-KeyV",player.prevSong.bind(player)),eventHandler.addEvent("keys-KeyS",playerConf.open.bind(playerConf)),eventHandler.addEvent("keys-KeyS-shift",toggleShuffle),eventHandler.addEvent("keys-KeyB",imageUploader.renderModal.bind(imageUploader)),eventHandler.addEvent("keys-KeyF-shift",forceAllRead),eventHandler.addEvent("keys-KeyH",gui.openHelp),eventHandler.addEvent("keys-KeyP",e=>{player.playlist.renderPagination(player.playlist.page),gui.modal.showModal()}),eventHandler.addEvent("keys-Escape, keys-KeyC-shift",e=>{gui.modal.resetModal(),gui.modal.closeModal()}),eventHandler.addEvent("keys-F11",e=>{document.fullscreenElement?document.exitFullscreen().catch(console.error):document.body.requestFullscreen().catch(console.error)})}async keyHandler(e){let t="keys-"+e.code+(e.shiftKey?"-shift":"")+(e.ctrlKey?"-ctrl":"");eventHandler.handleEvent({data:{cmd:t,data:t}})&&(e.preventDefault(),e.stopPropagation())}}class Startup{constructor(){this.modules={startup:!1,"id3-ready":!1}}moduleLoaded(e){this.modules[e]=!0,this.allModulesLoaded()}allModulesLoaded(){for(let e in this.modules)if(!this.modules[e])return!1;return window.dispatchEvent(new CustomEvent("startupFin")),!0}}const shaderHandler=new ShaderHandler(null),audioHandler=new AudioHandler,gui=new GUI,visual=new VisualDrawer,template=new Template,player=new Player,vConf=new Config("visual"),pConf=new Config("player"),worker=new Worker("/out/js/worker.min.js"),startup=new Startup,eventHandler=new EventHandler,playerConf=new PlayerConfigHandler,keyHandler=new KeyHandler,version=1,camera=new Camera;let c,gl,cInfo,ctx,sw;async function startUP(){if("serviceWorker"in navigator&&(sw=await navigator.serviceWorker.register("/sw.js")),pConf.loadConfigByName("default"),c=$("#c"),gl=c.getContext("webgl2"),cInfo=$("#cInfo"),ctx=cInfo.getContext("2d"),!gl)return alert("SORRY THE BROWSER DOESN'T SUPPORT WEBGL2"),!1;shaderHandler.setGL(gl),await shaderHandler.loadArray(["wave","sphere","water","wave2d"],"/shaders/"),await NotificationHandler.instance.init(),await audioHandler.init(),await player.init(),await camera.init(),await visual.init(),await gui.init(),await imageUploader.init(),await playerConf.init(),await keyHandler.init(),await initHandler(),toggleShuffle(!1)}worker.addEventListener("message",e=>{"startup"!==e.data.status?eventHandler.handleEvent(e):startup.moduleLoaded(e.data.cmd)}),window.addEventListener("startupFin",e=>{setTimeout(e=>{$(".loading-screen").remove()},100)}),startUP().then(e=>{startup.moduleLoaded("startup")}); \ No newline at end of file diff --git a/raw/gui/wave2d.json b/raw/gui/wave2d.json index 2cec793..bac8e08 100644 --- a/raw/gui/wave2d.json +++ b/raw/gui/wave2d.json @@ -52,6 +52,37 @@ "stepSize": 0.01, "dataType": "float" }, + { + "group": "translate", + "name": [ + "X", + "Y", + "Z" + ], + "props": [ + "x", + "y", + "z" + ], + "type": "slider", + "max": 1, + "min": -1, + "value": 0, + "stepSize": 0.01, + "dataType": "float" + }, + { + "group": "", + "name": "fudgeFactor", + "showName": "Fudge Factor", + "type": "slider", + "value": 1, + "max": 2, + "min": 0, + "tooltip": "z to w Fudge", + "stepSize": 0.1, + "dataType": "float" + }, { "group": "", "name": "baseColor", diff --git a/raw/javascript/FetchHandler.js b/raw/javascript/FetchHandler.js new file mode 100644 index 0000000..53b58cf --- /dev/null +++ b/raw/javascript/FetchHandler.js @@ -0,0 +1,36 @@ +class FetchHandler { + static files = {}; + + static async loadFiles(array, isJSON) { + let content = []; + for (let i = 0; i < array; i++) { + content.push(await FetchHandler.loadFile(array[i], isJSON)); + } + return content; + } + + static async loadFile(filename, isJSON) { + filename += '?v=' + version; + let files = FetchHandler.files; + if (files[filename]) { + return files[filename]; + } + let data = await FetchHandler.tryFromCache(filename); + if (isJSON) { + data = JSON.parse(data); + } + files[filename] = data; + return data; + } + + static async tryFromCache(filename) { + if (caches) { + let cache = await caches.open('vis3d-pwa-1'); + let data = await cache.match(filename); + if (!data) { + data = await fetch(filename); + } + return await data.text(); + } + } +} \ No newline at end of file diff --git a/raw/javascript/app.js b/raw/javascript/app.js index 4d42497..969f55f 100644 --- a/raw/javascript/app.js +++ b/raw/javascript/app.js @@ -10,9 +10,11 @@ const shaderHandler = new ShaderHandler(null), startup = new Startup(), eventHandler = new EventHandler(), playerConf = new PlayerConfigHandler(), - keyHandler = new KeyHandler(); + keyHandler = new KeyHandler(), + version = 1, + camera = new Camera(); -let c, gl, cInfo, ctx; +let c, gl, cInfo, ctx, sw; worker.addEventListener('message', e => { if (e.data.status === 'startup') { @@ -29,6 +31,9 @@ window.addEventListener('startupFin', e => { }) async function startUP() { + if ('serviceWorker' in navigator) { + sw = await navigator.serviceWorker.register('/sw.js'); + } pConf.loadConfigByName('default'); c = $('#c'), gl = c.getContext("webgl2"), @@ -39,10 +44,11 @@ async function startUP() { return false; } shaderHandler.setGL(gl) - await shaderHandler.loadArray(["wave", "sphere", "water", "wave2d"], 'shaders/'); + await shaderHandler.loadArray(["wave", "sphere", "water", "wave2d"], '/shaders/'); await NotificationHandler.instance.init(); await audioHandler.init(); await player.init(); + await camera.init(); await visual.init(); await gui.init(); await imageUploader.init(); diff --git a/raw/javascript/gl/Camera.js b/raw/javascript/gl/Camera.js new file mode 100644 index 0000000..5783af0 --- /dev/null +++ b/raw/javascript/gl/Camera.js @@ -0,0 +1,78 @@ +class Camera { + constructor() { + this.mouse; + this.rotation = { + x: 0, + y: 0 + } + this.lastMouse; + this.mousePressed = false; + this.translate = { + x: 0, + y: 0, + z: 0 + } + } + + async init() { + this.mouse = { + x: 0, + y: 0 + } + window.addEventListener('mousedown', this.mouseDown.bind(this)); + window.addEventListener('mouseup', this.mouseUp.bind(this)); + window.addEventListener('mousemove', this.mouseMove.bind(this), {passive: true}); + eventHandler.addEvent('keys-ArrowUp, keys-ArrowDown, keys-ArrowLeft, keys-ArrowRight, keys-KeyQ, keys-KeyE', this.keyPressed.bind(this)); + } + + mouseDown() { + this.mousePressed = true; + this.lastMouse = null; + } + + mouseUp() { + this.mousePressed = false; + this.lastMouse = null; + } + + mouseMove(event) { + if (!this.mousePressed || gui.modal.open) { + return; + } + if (this.lastMouse) { + let mouse = this.mouse, + rotate = this.rotation; + mouse.x += (this.lastMouse.x - event.clientX) * 0.2; + mouse.y += (this.lastMouse.y - event.clientY) * 0.2; + rotate.x = VTUtils.map(mouse.x, -c.width, c.width, 180, -180, false); + rotate.y = VTUtils.map(mouse.y, -c.height, c.height, 180, -180, false); + } + this.lastMouse = { + x: event.clientX, + y: event.clientY + } + } + + keyPressed(data) { + switch (data) { + case 'keys-ArrowUp': + this.translate.z += 10; + break; + case 'keys-ArrowDown': + this.translate.z -= 10; + break; + case 'keys-ArrowLeft': + this.translate.x -= 10; + break; + case 'keys-ArrowRight': + this.translate.x += 10; + break; + case 'keys-KeyQ': + this.translate.y += 10; + break; + case 'keys-KeyE': + this.translate.y -= 10; + break; + } + } +} \ No newline at end of file diff --git a/raw/javascript/gl/glUtils.js b/raw/javascript/gl/glUtils.js index 65f0f40..e6bf427 100644 --- a/raw/javascript/gl/glUtils.js +++ b/raw/javascript/gl/glUtils.js @@ -210,6 +210,15 @@ class TDUtils { return dst; } + static projection(width, height, depth) { + return [ + 2 / width, 0, 0, 0, + 0, -2 / height, 0, 0, + 0, 0, 2 / depth, 0, + -1, 1, 0, 1, + ]; + } + static inverse(m, dst) { dst = dst || new Float32Array(16); let m00 = m[0], @@ -298,42 +307,13 @@ class TDUtils { static aspectView(aspect) { return [ - 1 * aspect, 0, 0, 0, + 1 / aspect, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ] } - static getMatrix(fov, aspect, near, far, camAngle, radius) { - let lMat = this.lastMatrix, - u = TDUtils; - if (!u.isSame('fov', fov) - || !u.isSame('aspect', aspect) - || !u.isSame('near', near) - || !u.isSame('far', far) - || !u.isSame('cam', camAngle) - || !u.isSame('radius', radius) - ) { - let matrix = TDUtils.perspective(TDUtils.degToRad(fov), aspect, near, far), - cameraMatrix = TDUtils.yRotation(TDUtils.degToRad(camAngle)); - cameraMatrix = TDUtils.translate(cameraMatrix, 0, 0, radius * 1.5); - let viewMatrix = TDUtils.inverse(cameraMatrix); - matrix = TDUtils.multiply(matrix, viewMatrix) - lMat.m = matrix; - } - return lMat.m; - } - - static isSame(key, value) { - let lMat = this.lastMatrix; - if (lMat[key] !== value) { - lMat[key] = value; - return false; - } - return true; - } - static updateRotate(rotation, def) { let value = vConf.get(rotation, def) + vConf.get(rotation + '-inc', 0) if (value > 360) { @@ -343,4 +323,69 @@ class TDUtils { } vConf.set(rotation, value); } + + static makeZToWMatrix(fudgeFactor) { + return [ + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, fudgeFactor, + 0, 0, 0, 1, + ]; + } +} + +class GLHelper { + constructor(program) { + this.matrix = new Float32Array(16); + this.program = program; + } + + static uniform4fv(program, name, data) { + let uniform = gl.getUniformLocation(program, name); + gl.uniform4fv(uniform, data); + } + + static uniform3fv(program, name, data) { + let uniform = gl.getUniformLocation(program, name); + gl.uniform3fv(uniform, data); + } + + static uniform1f(program, name, data) { + let uniform = gl.getUniformLocation(program, name); + gl.uniform1f(uniform, data); + } + + rotateX(deg) { + this.matrix = TDUtils.multiply(this.matrix, TDUtils.xRotation(deg)); + } + + rotateY(deg) { + this.matrix = TDUtils.multiply(this.matrix, TDUtils.yRotation(deg)); + } + + rotateZ(deg) { + this.matrix = TDUtils.multiply(this.matrix, TDUtils.zRotation(deg)); + } + + scale(scaling) { + this.matrix = TDUtils.multiply(this.matrix, TDUtils.scale(scaling[0], scaling[1], scaling[2])) + } + + project(depth) { + depth = depth || (c.width > c.height) ? c.width : c.height; + this.matrix = TDUtils.projection(c.width, c.height, depth) + } + + translate(t) { + this.matrix = TDUtils.translate(this.matrix, t[0] || 0, t[1] || 0, t[2] || 0); + } + + addFudgeFactor(fudgeFactor) { + this.matrix = TDUtils.multiply(TDUtils.makeZToWMatrix(fudgeFactor), this.matrix); + } + + applyMatrix() { + let matrix = gl.getUniformLocation(this.program, "u_matrix"); + gl.uniformMatrix4fv(matrix, false, this.matrix); + } } \ No newline at end of file diff --git a/raw/javascript/gl/handler.js b/raw/javascript/gl/handler.js index 5a6f1a6..bf7cf2c 100644 --- a/raw/javascript/gl/handler.js +++ b/raw/javascript/gl/handler.js @@ -19,8 +19,8 @@ class ShaderHandler { async load(name, url, type) { let realName = name + "_" + type; if (!this.shaders[realName]) { - let data = await fetch(url); - let shader = this.createShader(await data.text(), type); + let data = await FetchHandler.loadFile(url, false); + let shader = this.createShader(data, type); if (shader) { this.shaders[realName] = shader; } @@ -70,8 +70,7 @@ class ShaderHandler { gl.attachShader(pro, this.getShader(shaders[i], gl.FRAGMENT_SHADER)); } gl.linkProgram(pro); - var success = gl.getProgramParameter(pro, gl.LINK_STATUS); - if (success) { + if (gl.getProgramParameter(pro, gl.LINK_STATUS)) { this.programs[name] = pro; return pro; } diff --git a/raw/javascript/gui.js b/raw/javascript/gui.js index 1f6b59b..8e9436d 100644 --- a/raw/javascript/gui.js +++ b/raw/javascript/gui.js @@ -159,6 +159,7 @@ class Modal { let self = this; self.currentModal = ''; self.modal = $('#modal'); + self.open = false; self.parent = self.modal.parentNode; self.modal.addDelegatedEventListener('click', 'header .close', this.closeModal.bind(this)); } @@ -192,6 +193,7 @@ class Modal { closeModal() { this.parent.addClass("hide") + this.open = false; } isCurrent(title) { @@ -200,5 +202,6 @@ class Modal { showModal() { this.parent.removeClass("hide") + this.open = true; } } \ No newline at end of file diff --git a/raw/javascript/keys.js b/raw/javascript/keys.js index f3410f3..cf52774 100644 --- a/raw/javascript/keys.js +++ b/raw/javascript/keys.js @@ -11,7 +11,8 @@ class KeyHandler { media.setActionHandler('play', player.playStop.bind(player)); media.setActionHandler('pause', player.playStop.bind(player)); media.setActionHandler('previoustrack', player.prevSong.bind(player)); - media.setActionHandler('nexttrack', player.prevSong.bind(player)); + media.setActionHandler('nexttrack', player.nextSong.bind(player)); + media.setActionHandler('stop', player.stop.bind(player)); } } @@ -49,7 +50,8 @@ class KeyHandler { if (eventHandler.handleEvent({ data: { - cmd: name + cmd: name, + data: name } })) { event.preventDefault(); diff --git a/raw/javascript/player.js b/raw/javascript/player.js index de34de3..23281fb 100644 --- a/raw/javascript/player.js +++ b/raw/javascript/player.js @@ -28,6 +28,16 @@ class Player { window.dispatchEvent(new CustomEvent('playSong')); } + stop() { + if (!audioHandler.lastSong) { + return; + } + let audioFile = audioHandler.audioFile; + audioFile.pause(); + audioFile.currentTime = 0; + window.dispatchEvent(new CustomEvent('playSong')); + } + playByID(number) { this.playlist.index = number; let next = this.playlist.getCurrent(); diff --git a/raw/javascript/playerConfigHandler.js b/raw/javascript/playerConfigHandler.js index 44c36c2..14683e8 100644 --- a/raw/javascript/playerConfigHandler.js +++ b/raw/javascript/playerConfigHandler.js @@ -99,7 +99,7 @@ class VisualConfig { let tem = VisualConfig.visualTemplates; if (!tem[name]) { //load config and save it - tem[name] = await fetch('/out/gui/' + name + ".json").then((res) => res.json()); + tem[name] = await FetchHandler.loadFile('/out/gui/' + name + ".json", true); } return tem[name]; } diff --git a/raw/javascript/template.js b/raw/javascript/template.js index e951fa8..0b89aad 100644 --- a/raw/javascript/template.js +++ b/raw/javascript/template.js @@ -6,9 +6,7 @@ class Template { async loadTemplate(name) { let self = this; if (!this.tpl[name]) { - await fetch(templateDir + name + ".tpl").then((r) => r.text()).then(c => { - self.tpl[name] = c; - }) + self.tpl[name] = await FetchHandler.loadFile(templateDir + name + '.tpl', false) } } @@ -36,13 +34,7 @@ class Template { } return d; } - - parseFromAPI(url, name, cb) { - fetch(url).then((r) => r.json()).then(d => { - cb(this.parseTemplate(name, d)) - }).catch(console.error) - } } const templateEx = /\$(.*?)\$/gm; -const templateDir = "out/tpl/" \ No newline at end of file +const templateDir = "/out/tpl/" \ No newline at end of file diff --git a/raw/javascript/visuals/wave2d.js b/raw/javascript/visuals/wave2d.js index 0a48e12..3b3cabc 100644 --- a/raw/javascript/visuals/wave2d.js +++ b/raw/javascript/visuals/wave2d.js @@ -6,13 +6,15 @@ class Wave2D extends Visual { updateData() { let data = audioHandler.getFloatArray(); - let add = 2 / data.length, - x = -1; + let add = c.width / data.length, + x = 0, + y = c.height / 2, + goTrough = y / 2; let outerLoop = 0; for (let i = 0; i < data.length; i++) { //first this.data[outerLoop] = x; - this.data[outerLoop + 1] = data[i]; + this.data[outerLoop + 1] = y + (data[i] * goTrough); this.data[outerLoop + 2] = data[i]; outerLoop += 3; x += add; @@ -35,17 +37,18 @@ class Wave2D extends Visual { } rotate(program) { - let matrix = [ - 1, 0, 0, 0, - 0, 0.6, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 - ] - matrix = TDUtils.multiply(matrix, TDUtils.xRotation(vConf.get("rotation-x", 0))); - matrix = TDUtils.multiply(matrix, TDUtils.yRotation(vConf.get("rotation-y", 0))); - matrix = TDUtils.multiply(matrix, TDUtils.zRotation(vConf.get("rotation-z", 0))); - let rotate = gl.getUniformLocation(program, "u_matrix"); - gl.uniformMatrix4fv(rotate, false, matrix); + let glHelper = new GLHelper(program); + glHelper.project(); + glHelper.addFudgeFactor(vConf.get("fudgeFactor", 1)); + glHelper.translate([ + camera.translate.x, + camera.translate.y, + camera.translate.z + ]); + glHelper.rotateX(camera.mouse.x); + glHelper.rotateY(camera.mouse.y); + glHelper.rotateZ(vConf.get("rotation-z", 0)); + glHelper.applyMatrix(); } setup() { @@ -59,9 +62,8 @@ class Wave2D extends Visual { prepare(program) { this.position = gl.getAttribLocation(program, "a_position"); - this.color = gl.getUniformLocation(program, "u_color"); - let lightPos = gl.getUniformLocation(program, "u_lightPos"); - gl.uniform3fv(lightPos, vConf.get("light", [0, 5, -56])); + //GLHelper.uniform1f(program, "u_fudgeFactor", vConf.get("fudgeFactor", 1)); + GLHelper.uniform3fv(program, "u_lightPos", vConf.get("light", [0, 5, -56])); } afterDraw() { diff --git a/shaders/wave2d.vert b/shaders/wave2d.vert index 39d5ee0..718cd9c 100644 --- a/shaders/wave2d.vert +++ b/shaders/wave2d.vert @@ -2,11 +2,11 @@ in vec3 a_position; uniform mat4 u_matrix; +uniform float u_fudgeFactor; out vec4 pos; void main() { pos = u_matrix * vec4(a_position, 1); - pos.y = pos.y * 0.6; gl_Position = pos; } \ No newline at end of file diff --git a/sw.js b/sw.js new file mode 100644 index 0000000..d0673a1 --- /dev/null +++ b/sw.js @@ -0,0 +1,44 @@ +const cacheName = 'vis3d-pwa-1', + FILES_TO_CACHE = [ + '/index.html', + '/empty.txt' + ]; + +self.addEventListener('install', function (e) { + e.waitUntil( + caches.open(cacheName).then(function (cache) { + return cache.addAll(FILES_TO_CACHE); + }) + ); +}); + +self.addEventListener('fetch', function (event) { + if (event.request.method !== 'GET') return; + event.respondWith(handle.bind(event).call().then(r => { + return r + })) +}); + +async function handle() { + let event = this, + url = event.request.url; + if (url === self.registration.scope) { + url += 'index.html'; + } + const cache = await caches.open(cacheName); + let response = await fetch(url).then((r) => { + return r + }).catch(async err => { + let cachedResponse = await cache.match(url); + if (cachedResponse) { + return cachedResponse; + } + cachedResponse = await cache.match('/empty.txt'); + return cachedResponse; + }); + if (response) { + await cache.put(event.request, response.clone()); + return response; + } + +} \ No newline at end of file