From d86be23a21d954f268212f4ad86b0718ddb4d49c Mon Sep 17 00:00:00 2001 From: Leon van Kammen Date: Thu, 15 Oct 2026 11:18:05 +0200 Subject: [PATCH] added peertube component + moved content into tech-demos + disabled openbrush downloads when internal-storage is used --- src/janusweb/index.html | 77 + src/janusweb/media/assets.json | 133 ++ .../asset/button.click.mp3 | Bin 0 -> 12373 bytes .../janus-script-peertube/asset/button.glb | Bin 0 -> 1988 bytes .../asset/button.hover.mp3 | Bin 0 -> 12373 bytes .../asset/button.icon.glb | Bin 0 -> 1924 bytes .../janus-script-peertube.js | 1752 +++++++++++++++++ .../janus-script-peertube/translator.js | 104 + .../{ => tech-demos}/icosa-gallery.png | Bin .../{ => tech-demos}/icosa-gallery/README.md | 0 .../icosa-gallery/icosa-gallery.desktop | 0 .../{ => tech-demos}/janusxr_tutorial.png | Bin .../janusxr_tutorial/AFRAME.jpg | Bin .../janusxr_tutorial/AFRAME/README.md | 0 .../janusxr_tutorial/AFRAME/index.html | 0 .../janusxr_tutorial/AFRAME/tree2.glb | Bin .../janusxr_tutorial/arwindow.jpg | Bin .../janusxr_tutorial/arwindow/README.md | 0 ... the Woods by Don Carson - 38m6Q1H12DU.glb | Bin .../janusxr_tutorial/arwindow/index.html | 0 .../janusxr_tutorial/arwindow/occludebox.glb | Bin .../janusxr_tutorial/helloworld.jpg | Bin .../janusxr_tutorial/helloworld/README.md | 0 .../janusxr_tutorial/helloworld/janusxr.xml | 0 .../janusxr_tutorial/tronverse | 0 .../janusxr_tutorial/tronverse.png | 0 src/myverse/{ => tech-demos}/tronverse.png | Bin .../{ => tech-demos}/tronverse/janusxr | 0 .../{ => tech-demos}/tronverse/tron/README.md | 0 .../{ => tech-demos}/tronverse/tron/tron.glb | Bin .../{ => tech-demos}/tronverse/tron/tron.mp3 | Bin .../{ => tech-demos}/tronverse/tron/tron.png | Bin .../tronverse/tronverse-icosagallery.jpg | Bin .../tronverse-icosagallery/README.md | 0 src/plugin/openbrush.lua | 8 +- src/util/build.sh | 17 +- 36 files changed, 2082 insertions(+), 9 deletions(-) create mode 100644 src/janusweb/index.html create mode 100644 src/janusweb/media/assets.json create mode 100644 src/janusweb/media/component/janus-script-peertube/asset/button.click.mp3 create mode 100644 src/janusweb/media/component/janus-script-peertube/asset/button.glb create mode 100644 src/janusweb/media/component/janus-script-peertube/asset/button.hover.mp3 create mode 100644 src/janusweb/media/component/janus-script-peertube/asset/button.icon.glb create mode 100644 src/janusweb/media/component/janus-script-peertube/janus-script-peertube.js create mode 100644 src/janusweb/media/component/janus-script-peertube/translator.js rename src/myverse/{ => tech-demos}/icosa-gallery.png (100%) rename src/myverse/{ => tech-demos}/icosa-gallery/README.md (100%) rename src/myverse/{ => tech-demos}/icosa-gallery/icosa-gallery.desktop (100%) rename src/myverse/{ => tech-demos}/janusxr_tutorial.png (100%) rename src/myverse/{ => tech-demos}/janusxr_tutorial/AFRAME.jpg (100%) rename src/myverse/{ => tech-demos}/janusxr_tutorial/AFRAME/README.md (100%) rename src/myverse/{ => tech-demos}/janusxr_tutorial/AFRAME/index.html (100%) rename src/myverse/{ => tech-demos}/janusxr_tutorial/AFRAME/tree2.glb (100%) rename src/myverse/{ => tech-demos}/janusxr_tutorial/arwindow.jpg (100%) rename src/myverse/{ => tech-demos}/janusxr_tutorial/arwindow/README.md (100%) rename src/myverse/{ => tech-demos}/janusxr_tutorial/arwindow/Walk in the Woods by Don Carson - 38m6Q1H12DU.glb (100%) rename src/myverse/{ => tech-demos}/janusxr_tutorial/arwindow/index.html (100%) rename src/myverse/{ => tech-demos}/janusxr_tutorial/arwindow/occludebox.glb (100%) rename src/myverse/{ => tech-demos}/janusxr_tutorial/helloworld.jpg (100%) rename src/myverse/{ => tech-demos}/janusxr_tutorial/helloworld/README.md (100%) rename src/myverse/{ => tech-demos}/janusxr_tutorial/helloworld/janusxr.xml (100%) rename src/myverse/{ => tech-demos}/janusxr_tutorial/tronverse (100%) rename src/myverse/{ => tech-demos}/janusxr_tutorial/tronverse.png (100%) rename src/myverse/{ => tech-demos}/tronverse.png (100%) rename src/myverse/{ => tech-demos}/tronverse/janusxr (100%) rename src/myverse/{ => tech-demos}/tronverse/tron/README.md (100%) rename src/myverse/{ => tech-demos}/tronverse/tron/tron.glb (100%) rename src/myverse/{ => tech-demos}/tronverse/tron/tron.mp3 (100%) rename src/myverse/{ => tech-demos}/tronverse/tron/tron.png (100%) rename src/myverse/{ => tech-demos}/tronverse/tronverse-icosagallery.jpg (100%) rename src/myverse/{ => tech-demos}/tronverse/tronverse-icosagallery/README.md (100%) diff --git a/src/janusweb/index.html b/src/janusweb/index.html new file mode 100644 index 0000000..0280133 --- /dev/null +++ b/src/janusweb/index.html @@ -0,0 +1,77 @@ + + + + JanusWeb + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/janusweb/media/assets.json b/src/janusweb/media/assets.json new file mode 100644 index 0000000..ddb3d0e --- /dev/null +++ b/src/janusweb/media/assets.json @@ -0,0 +1,133 @@ +[ + { "assettype":"model", "name":"capsule", "src":"assets/primitives/capsule.obj" }, + { "assettype":"model", "name":"cone", "src":"assets/primitives/cone.obj" }, + { "assettype":"model", "name":"cube", "src":"assets/primitives/cube.obj" }, + { "assettype":"model", "name":"cylinder", "src":"assets/primitives/cylinder.obj" }, + { "assettype":"model", "name":"pipe", "src":"assets/primitives/pipe.obj" }, + { "assettype":"model", "name":"plane", "src":"assets/primitives/plane.obj" }, + { "assettype":"model", "name":"pyramid", "src":"assets/primitives/pyramid.obj" }, + { "assettype":"model", "name":"sphere", "src":"assets/primitives/sphere.obj" }, + { "assettype":"model", "name":"torus", "src":"assets/primitives/torus.obj" }, + + { "assettype":"image", "name":"room1_tex0", "src":"assets/rooms/room1.png", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"room2_tex0", "src":"assets/rooms/room2.png", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"room3_tex0", "src":"assets/rooms/room3.png", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"room4_tex0", "src":"assets/rooms/room4.png", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"room5_tex0", "src":"assets/rooms/room5.png", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"room6_tex0", "src":"assets/rooms/room6.png", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"room_1pedestal_tex0", "src":"assets/rooms/room_1pedestal.png", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"room_2pedestal_tex0", "src":"assets/rooms/room_2pedestal.png", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"room_3_narrow_tex0", "src":"assets/rooms/room_3_narrow.png", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"room_3_wide_tex0", "src":"assets/rooms/room_3_wide.png", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"room_4_narrow_tex0", "src":"assets/rooms/room_4_narrow.png", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"room_4_wide_tex0", "src":"assets/rooms/room_4_wide.png", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"room_box_small_tex0", "src":"assets/rooms/room_box_small.png", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"room_box_medium_tex0", "src":"assets/rooms/room_box_medium.png", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"room1_new_tex0", "src":"assets/rooms/room1_new.png", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"room_home_tex0", "src":"assets/rooms/room_home.png", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"room_plane_tex0", "src":"assets/rooms/tile_d.png", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"room_plane2_tex0", "src":"assets/rooms/room_plane2.png", "hasalpha": false, "preload": false }, + + { "assettype":"model", "name":"room1", "src":"assets/rooms/room1.obj", "tex0":"room1_tex0", "hasalpha": false }, + { "assettype":"model", "name":"room2", "src":"assets/rooms/room2.obj", "tex0":"room2_tex0", "hasalpha": false }, + { "assettype":"model", "name":"room3", "src":"assets/rooms/room3.obj", "tex0":"room3_tex0", "hasalpha": false }, + { "assettype":"model", "name":"room4", "src":"assets/rooms/room4.obj", "tex0":"room4_tex0", "hasalpha": false }, + { "assettype":"model", "name":"room5", "src":"assets/rooms/room5.obj", "tex0":"room5_tex0", "hasalpha": false }, + { "assettype":"model", "name":"room6", "src":"assets/rooms/room6.obj", "tex0":"room6_tex0", "hasalpha": false }, + { "assettype":"model", "name":"room_1pedestal", "src":"assets/rooms/room_1pedestal.obj", "tex0":"room_1pedestal_tex0", "hasalpha": false }, + { "assettype":"model", "name":"room_2pedestal", "src":"assets/rooms/room_2pedestal.obj", "tex0":"room_2pedestal_tex0", "hasalpha": false }, + { "assettype":"model", "name":"room_3_narrow", "src":"assets/rooms/room_3_narrow.obj", "tex0":"room_3_narrow_tex0", "hasalpha": false }, + { "assettype":"model", "name":"room_3_wide", "src":"assets/rooms/room_3_wide.obj", "tex0":"room_3_wide_tex0", "hasalpha": false }, + { "assettype":"model", "name":"room_4_narrow", "src":"assets/rooms/room_4_narrow.obj", "tex0":"room_4_narrow_tex0", "hasalpha": false }, + { "assettype":"model", "name":"room_4_wide", "src":"assets/rooms/room_4_wide.obj", "tex0":"room_4_wide_tex0", "hasalpha": false }, + { "assettype":"model", "name":"room_box_small", "src":"assets/rooms/room_box_small.obj", "tex0":"room_box_small_tex0", "hasalpha": false }, + { "assettype":"model", "name":"room_box_medium", "src":"assets/rooms/room_box_medium.obj", "tex0":"room_box_medium_tex0", "hasalpha": false }, + { "assettype":"model", "name":"room1_new", "src":"assets/rooms/room1_new.obj", "tex0":"room1_new_tex0", "hasalpha": false }, + { "assettype":"model", "name":"room_home", "src":"assets/rooms/room_home.obj", "tex0":"room_home_tex0", "hasalpha": false }, + { "assettype":"model", "name":"room_plane", "src":"assets/rooms/room_plane.obj", "tex0":"room_plane_tex0", "hasalpha": false }, + { "assettype":"model", "name":"room_plane2", "src":"assets/rooms/room_plane2.obj", "tex0":"room_plane2_tex0", "hasalpha": false }, + + { "assettype":"model", "name":"room1_collision", "src":"assets/rooms/room1_collision.obj" }, + { "assettype":"model", "name":"room2_collision", "src":"assets/rooms/room2_collision.obj" }, + { "assettype":"model", "name":"room3_collision", "src":"assets/rooms/room3_collision.obj" }, + { "assettype":"model", "name":"room4_collision", "src":"assets/rooms/room4_collision.obj" }, + { "assettype":"model", "name":"room5_collision", "src":"assets/rooms/room5_collision.obj" }, + { "assettype":"model", "name":"room6_collision", "src":"assets/rooms/room6_collision.obj" }, + { "assettype":"model", "name":"room_1pedestal_collision", "src":"assets/rooms/room_1pedestal_collision.obj" }, + { "assettype":"model", "name":"room_2pedestal_collision", "src":"assets/rooms/room_2pedestal_collision.obj" }, + { "assettype":"model", "name":"room_3_narrow_collision", "src":"assets/rooms/room_3_narrow_collision.obj" }, + { "assettype":"model", "name":"room_3_wide_collision", "src":"assets/rooms/room_3_wide_collision.obj" }, + { "assettype":"model", "name":"room_4_narrow_collision", "src":"assets/rooms/room_4_narrow_collision.obj" }, + { "assettype":"model", "name":"room_4_wide_collision", "src":"assets/rooms/room_4_wide_collision.obj" }, + { "assettype":"model", "name":"room_box_small_collision", "src":"assets/rooms/room_box_small_collision.obj" }, + { "assettype":"model", "name":"room_box_medium_collision", "src":"assets/rooms/room_box_medium_collision.obj" }, + { "assettype":"model", "name":"room1_new_collision", "src":"assets/rooms/room1_new_collision.obj" }, + { "assettype":"model", "name":"room_home_collision", "src":"assets/rooms/room_home_collision.obj" }, + { "assettype":"model", "name":"room_plane_collision", "src":"assets/rooms/room_plane_collision.obj" }, + { "assettype":"model", "name":"room_plane2_collision", "src":"assets/rooms/room_plane2_collision.obj" }, + + { "assettype":"image", "name":"black", "src":"assets/black.png", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"clear", "src":"assets/clear.png", "hasalpha": true, "preload": false }, + { "assettype":"image", "name":"dark_back", "src":"assets/skybox/dark_back.png", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"dark_down", "src":"assets/skybox/dark_down.png", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"dark_front", "src":"assets/skybox/dark_front.png", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"dark_left", "src":"assets/skybox/dark_left.png", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"dark_right", "src":"assets/skybox/dark_right.png", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"dark_up", "src":"assets/skybox/dark_up.png", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"miramar_back", "src":"assets/skybox/miramar_back.png", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"miramar_down", "src":"assets/skybox/miramar_down.png", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"miramar_front", "src":"assets/skybox/miramar_front.png", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"miramar_left", "src":"assets/skybox/miramar_left.png", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"miramar_right", "src":"assets/skybox/miramar_right.png", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"miramar_up", "src":"assets/skybox/miramar_up.png", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"skyrender_back", "src":"assets/skybox/skyrender_back.png", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"skyrender_down", "src":"assets/skybox/skyrender_down.png", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"skyrender_front", "src":"assets/skybox/skyrender_front.png", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"skyrender_left", "src":"assets/skybox/skyrender_left.png", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"skyrender_right", "src":"assets/skybox/skyrender_right.png", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"skyrender_up", "src":"assets/skybox/skyrender_up.png", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"dayskybox_back", "src":"assets/skybox/Day/Dayback.jpg", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"dayskybox_down", "src":"assets/skybox/Day/Daybottom.jpg", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"dayskybox_front", "src":"assets/skybox/Day/Dayfront.jpg", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"dayskybox_left", "src":"assets/skybox/Day/Dayleft.jpg", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"dayskybox_right", "src":"assets/skybox/Day/Dayright.jpg", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"dayskybox_up", "src":"assets/skybox/Day/Daytop.jpg", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"nightskybox_back", "src":"assets/skybox/Night/Nightback.jpg", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"nightskybox_down", "src":"assets/skybox/Night/Nightbottom.jpg", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"nightskybox_front", "src":"assets/skybox/Night/Nightfront.jpg", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"nightskybox_left", "src":"assets/skybox/Night/Nightleft.jpg", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"nightskybox_right", "src":"assets/skybox/Night/Nightright.jpg", "hasalpha": false, "preload": false }, + { "assettype":"image", "name":"nightskybox_up", "src":"assets/skybox/Night/Nighttop.jpg", "hasalpha": false, "preload": false }, + + { "assettype":"model", "name":"avatar", "src":"assets/avatar.obj", "tex0": "avatar_tex0" }, + { "assettype":"model", "name":"avatar_head", "src":"assets/avatar_head.obj", "tex0": "avatar_head_tex0" }, + { "assettype":"model", "name":"avatar_gltf", "src":"assets/janus-avatar-base.glb" }, + { "assettype":"model", "name":"avatar_animations", "src":"assets/janus-avatar-animations.glb" }, + + { "assettype":"image", "name":"avatar_tex0", "src":"assets/avatar.png", "preload": false }, + { "assettype":"image", "name":"avatar_head_tex0", "src":"assets/avatar_head.png", "preload": false }, + + { "assettype":"model", "name":"avatar_janusweb", "src":"assets/hoverscreen.obj" }, + + { "assettype":"model", "name":"loading", "src":"assets/loading.obj" }, + + { "assettype":"image", "name":"cursor_arrow", "src":"assets/cursor/arrow.png", "hasalpha": true }, + { "assettype":"image", "name":"cursor_crosshair", "src":"assets/cursor/crosshair.png", "hasalpha": true }, + { "assettype":"image", "name":"cursor_hand", "src":"assets/cursor/hand.png", "hasalpha": true }, + { "assettype":"image", "name":"cursor_dot_inactive", "src":"assets/cursor/cursor-dot-white.png", "hasalpha": true }, + { "assettype":"image", "name":"cursor_dot_active", "src":"assets/cursor/cursor-dot-green.png", "hasalpha": true }, + + + { "assettype":"model", "name":"controller_daydream", "src":"assets/controllers/daydream/vr_controller_daydream.obj", "mtl":"vr_controller_daydream.mtl" }, + { "assettype":"model", "name":"controller_vive", "src":"assets/controllers/vive/ViveController.dae.gz" }, + { "assettype":"model", "name":"controller_touch_left", "src":"assets/controllers/oculus/TouchLeftModified.dae.gz" }, + { "assettype":"model", "name":"controller_touch_right", "src":"assets/controllers/oculus/TouchRight.dae.gz" }, + { "assettype":"font", "name":"helvetiker", "src":"assets/fonts/helvetiker_regular.typeface.json" }, + { "assettype":"shader", "name":"defaultportal", "shadertype":"shadertoy","fragment_src":"assets/shaders/greenportal2.shadertoy.frag", "preload":true }, + + { "assettype":"model", "name":"button", "src":"component/janus-script-peertube/asset/button.glb", "preload":true}, + { "assettype":"model", "name":"icon", "src":"component/janus-script-peertube/asset/button.icon.glb", "preload":true}, + { "assettype":"sound", "name":"button-click", "src":"component/janus-script-peertube/asset/button.click.mp3", "preload":true}, + { "assettype":"sound", "name":"button-hover", "src":"component/janus-script-peertube/asset/button.hover.mp3", "preload":true} + +] diff --git a/src/janusweb/media/component/janus-script-peertube/asset/button.click.mp3 b/src/janusweb/media/component/janus-script-peertube/asset/button.click.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..852f5fda3015b0224c0f4b4f0628f909ac5c4def GIT binary patch literal 12373 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 literal 0 HcmV?d00001 diff --git a/src/janusweb/media/component/janus-script-peertube/asset/button.glb b/src/janusweb/media/component/janus-script-peertube/asset/button.glb new file mode 100644 index 0000000000000000000000000000000000000000..0c20185ef245d3dd870a752ccca221d3d135f1d9 GIT binary patch literal 1988 zcmeHI-)j>~6dtX%R$HyVrGmsk2*Nhb?q+tA*sy8quX=kMX=@dV=r-NNm1MWF9xWLXVoca1x zr4`FRFQ_5@MA-x_iF$5e2BOLoOXy6OnU+#gS}M(Tu1E~yyEF*<1+xrgfg>x5%6XE> zT;^$w>40BBO1MK$!t9q_M6PB%yJ83Se7I@`foD(81^8H{C-URN6T|sYv7OQU*q;L< zG$TuN;_&IAe17cESw=Gow99s8>;h8M1yRl{z?1KKOMRQ3D;6zp(zY%Noq}7LbsftI zCN9mw#<;9#5-qrM4xF5llxW5L3%08yxU9)3rgIf~l}d44QWKmjx~yvO$QWm`)hBaF zh8|UHIJ>F7WjKAyllqVqi28K$@Q?~;UZt%*VUs`e`6IgWD|F?%>gsZIkrZ9hpkZP4 z@u7i{fia3V@T>jSwu_ceUka=d%P9qLF_lybu#m_Xi{fp8OCFeB311vFnbzPuqgMQ! zE5ugHmebId*2-MRjjXV?tbW7RHP}ci8!lheg|GuH+fFkEcQbaijgZ0NQS#=&<|ZMv zIt*iX=icVj^}#v}qpQ55$v*vEhhgwXz0KZ3K^=w>>)q3=JSf&-7^lca#>57m(j}f5AZ9t#_9V5!io<;pv&+lbxLKpEh1M#SXc+^Ea)_)khm2iX9zo^(R2Bkd4d$xetJWIMz*vIU}xY=qcEdLTBFZiux) dfe3D=VC@w6EdtpiFl}PBUF0`HW&_N+$!AE63_$<@ literal 0 HcmV?d00001 diff --git a/src/janusweb/media/component/janus-script-peertube/asset/button.hover.mp3 b/src/janusweb/media/component/janus-script-peertube/asset/button.hover.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..949ee290d7814f008521d46f57c7460363e5d6ef GIT binary patch literal 12373 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 literal 0 HcmV?d00001 diff --git a/src/janusweb/media/component/janus-script-peertube/janus-script-peertube.js b/src/janusweb/media/component/janus-script-peertube/janus-script-peertube.js new file mode 100644 index 0000000..48edb38 --- /dev/null +++ b/src/janusweb/media/component/janus-script-peertube/janus-script-peertube.js @@ -0,0 +1,1752 @@ +room.registerElement('pushbutton', { + width: .5, + length: .5, + height: .5, + clicking: false, + state: 'off', + accesskey: false, + + onbuttondown: new CustomEvent('buttondown'), + onbuttonpress: new CustomEvent('buttonpress'), + onbuttonup: new CustomEvent('buttonup'), + onclick: new CustomEvent('click'), + + createChildren: function() { + this.base = room.createObject('Object', { + id: 'cube', + collision_id: 'cube', + col: V(.6,.6,.6), + scale: V(this.length,this.height / 2,this.width), + pos: V(0,this.height/4,0) + }, this); + this.button = room.createObject('Object', { + id: 'cube', + collision_id: 'cube', + col: this.col, + scale: V(this.length * .8, this.height / 4, this.width * .8), + }, this); + this.sounds = { + clickin: room.createObject('Sound', { id: 'pushbutton-click-in', }, this), + clickon: room.createObject('Sound', { id: 'pushbutton-click-on', }, this), + clickoff: room.createObject('Sound', { id: 'pushbutton-click-off', }, this), + }; + + this.button.addEventListener('mousedown', this.onMouseDown); + window.addEventListener('mouseup', this.onMouseUp); + window.addEventListener('touchend', this.onMouseUp); + this.button.addEventListener('click', this.onClick); + + if (this.accesskey) { + room.onKeyDown = this.onKeyDown; + room.onKeyUp = this.onKeyUp; + } + + this.setButtonPos(); + }, + press: function() { + this.executeCallback(this.onbuttonpress); + }, + onClick: function(ev) { + if (this.onclick) { + this.executeCallback(this.onclick); + } + }, + onMouseDown: function(ev) { + this.button.sync = true; + this.clicking = true; + this.activate(); + + this.setState('in'); + this.executeCallback(this.onbuttondown); + + if (ev) { + ev.stopPropagation(); + } + }, + onMouseUp: function(ev) { + if (this.clicking) { + this.setState('off'); + this.button.sync = true; + this.clicking = false; + this.executeCallback(this.onbuttonup); + + if (ev) { + ev.stopPropagation(); + } + } + }, + onKeyDown: function(ev) { + if (ev.keyCode == this.accesskey) { + this.onMouseDown(ev); + } + }, + onKeyUp: function(ev) { + if (ev.keyCode == this.accesskey) { + this.onMouseUp(ev); + } + }, + setActive: function(active) { + this.setState(active ? 'on' : 'off'); + }, + setButtonPos: function(pos) { + if (typeof pos == 'undefined') pos = (this.active ? .6 : 0); + var percent = Math.min(1, Math.max(0, pos)); + var defaultpos = this.height / 2; + this.button.pos = V(0, defaultpos - ((this.height / 8) * percent) + (this.height / 16), 0); + }, + setState: function(state) { + if (state != this.state) { + this.state = state; + if (state == 'in') { + this.setButtonPos(1); + this.sounds.clickin.play(); + } else if (state == 'on') { + this.setButtonPos(.6); + this.sounds.clickon.play(); + } else if (state == 'off') { + this.setButtonPos(0); + this.sounds.clickoff.play(); + } + } + } + +}); + +room.registerElement('slider', { + grabbing: false, + value: 1, + min: 0, + max: 1, + length: 1, + width: .25, + height: .2, + + ongrabstart: new CustomEvent('grabstart'), + onchange: new CustomEvent('change'), + ongrabend: new CustomEvent('grabend'), + + createChildren: function() { + this.base = room.createObject('Object', { + id: 'cube', + collision_id: 'cube', + col: V(.6,.6,.6), + scale: V(this.length,this.height/2,this.width), + pos: V(0,this.height/4,0) + }, this); + this.track = room.createObject('Object', { + id: 'cube', + collision_id: 'cube', + col: V(.02,.02,.02), + scale: V(this.length * .9, this.height / 4, this.width / 5), + pos: V(0, this.base.pos.y + this.height / 4,0) + }, this); + this.handle = room.createObject('Object', { + id: 'cube', + collision_id: 'cube', + col: this.col, + scale: V(this.width / 4, this.height / 4, this.width * .75), + pos: V(0,this.base.pos.y,0) + }, this); + this.sounds = { + clickin: room.createObject('Sound', { id: 'pushbutton-click-in', }, this), + clickon: room.createObject('Sound', { id: 'pushbutton-click-on', }, this), + clickoff: room.createObject('Sound', { id: 'pushbutton-click-off', }, this), + }; + + this.handle.addEventListener('mousedown', this.onMouseDown); + this.track.addEventListener('mousedown', this.onMouseDown); + // FIXME - bind these in mousedown, for maximum efficiency + window.addEventListener('mouseup', this.onMouseUp); + window.addEventListener('touchend', this.onMouseUp); + window.addEventListener('mousemove', this.onMouseMove); + window.addEventListener('touchmove', this.onMouseMove); + this.handle.addEventListener('click', this.onClick); + + this.setValue(this.value); + }, + setValue: function(value, skipchange) { + var realvalue = Math.min(this.max, Math.max(this.min, value)); + var percent = realvalue / (this.max - this.min); + var pos = (percent - .5) * this.length * .9; + this.handle.pos = V(pos, this.base.pos.y + this.height * 3 / 8 , 0); + this.handle.sync = true; + this.value = realvalue; + + if (this.onchange && !skipchange) { + this.executeCallback(this.onchange); + } + }, + updateValueFromCursorPos: function() { + // FIXME - vector proxies are currently broken + var sliderpos = this.parent.localToWorld(this.pos._target.clone()); + var dist = player.head_pos.distanceTo(sliderpos); + + // Cast a ray from my head to the cursor position + var dir = V(player.cursor_pos.x - player.head_pos.x, player.cursor_pos.y - player.head_pos.y, player.cursor_pos.z - player.head_pos.z); + + // Then project the ray into the same plane as the slider I'm manipulating + var pos = translate(player.head_pos, scalarMultiply(normalized(dir), dist)); + this.worldToLocal(pos); + + // The slider position is now determined based on the x position (left/right) in the slider's own coordinate system, and the length of the slider + var foo = pos.x / (this.length * .9) + .5; + + this.setValue(foo * (this.max - this.min)); + }, + onMouseDown: function(ev) { + this.grabbing = true; + this.updateValueFromCursorPos(); + this.sounds.clickin.play(); + if (this.ongrabstart) this.executeCallback(this.ongrabstart); + ev.preventDefault(); + ev.stopPropagation(); + }, + onMouseMove: function(ev) { + if (this.grabbing) { + this.updateValueFromCursorPos(); + ev.stopPropagation(); + } + }, + onMouseUp: function(ev) { + if (this.grabbing) { + this.grabbing = false; + this.sounds.clickoff.play(); + if (this.ongrabend) this.executeCallback(this.ongrabend); + ev.stopPropagation(); + } + }, + onClick: function(ev) { + } + +}); + +room.registerElement('togglebutton', { + width: .5, + length: .5, + height: .5, + clicking: false, + active: false, + + onbuttondown: new CustomEvent('buttondown'), + onbuttonpress: new CustomEvent('buttonpress'), + onbuttonup: new CustomEvent('buttonup'), + onactivate: new CustomEvent('activate'), + ondeactivate: new CustomEvent('deactivate'), + + createChildren: function() { + this.base = room.createObject('Object', { + id: 'cube', + collision_id: 'cube', + col: V(.6,.6,.6), + scale: V(this.length,this.height / 2,this.width), + pos: V(0,this.height/4,0) + }, this); + this.button = room.createObject('Object', { + id: 'cube', + collision_id: 'cube', + col: this.col, + scale: V(this.length * .8, this.height / 4, this.width * .8), + }, this); + this.sounds = { + clickin: this.createObject('Sound', { id: 'pushbutton-click-in', }), + clickon: this.createObject('Sound', { id: 'pushbutton-click-on', }), + clickoff: this.createObject('Sound', { id: 'pushbutton-click-off', }), + }; + + this.button.addEventListener('mousedown', this.onMouseDown); + window.addEventListener('mouseup', this.onMouseUp); + window.addEventListener('touchend', this.onMouseUp); + this.button.addEventListener('click', this.onClick); + + this.setButtonPos(); + }, + press: function() { + this.active = !this.active; + + if (this.active && this.onactivate) { + this.executeCallback(this.onactivate); + } else if (!this.active && this.ondeactivate) { + this.executeCallback(this.ondeactivate); + } + }, + onClick: function(ev) { + if (this.onclick) { + this.executeCallback(this.onclick); + } + }, + onMouseDown: function(ev) { + this.button.sync = true; + this.clicking = true; + this.press(); + + this.setState('in'); + + ev.stopPropagation(); + }, + onMouseUp: function(ev) { + if (this.clicking) { + if (this.active) { + this.setState('on'); + } else { + this.setState('off'); + } + this.button.sync = true; + this.clicking = false; + + ev.stopPropagation(); + } + }, + setActive: function(active) { + this.active = active; + this.setState(active ? 'on' : 'off'); + }, + setButtonPos: function(pos) { + if (typeof pos == 'undefined') pos = (this.active ? .6 : 0); + var percent = Math.min(1, Math.max(0, pos)); + var defaultpos = this.height / 2; + this.button.pos = V(0, defaultpos - ((this.height / 8) * percent) + (this.height / 16), 0); + }, + setState: function(state) { + if (state != this.state) { + this.state = state; + if (state == 'in') { + this.setButtonPos(1); + this.sounds.clickin.play(); + } else if (state == 'on') { + this.setButtonPos(.6); + this.sounds.clickon.play(); + } else if (state == 'off') { + this.setButtonPos(0); + this.sounds.clickoff.play(); + } + } + } + +}); + +/* + * ## JJQuery + * see https://codeberg.org/coderofsalvation/janus-script-jjq + */ + +$ = function(sel,opts){ + out = $$(sel,opts)[0] + return out || false +} + +$$ = function(sel, opts){ + opts = opts || {} + if( !opts.in ) opts.in =['tag','js_id','name','id'] + out = [] + db = {children: room.objects} + if( typeof sel !== 'string' && !(sel instanceof RegExp) ){ + if( sel?.length ) db.children = sel + if( sel?.children ) db.children = sel.children + sel = /.*/ + } + if( opts?.from ) db.children = opts.from + $$.traverse( function(child){ + for( let prop in opts.in ){ + found = child[ opts.in[prop] ] + if( found ){ + let match = ( typeof sel == 'string' ? String(found).toLowerCase() == sel.toLowerCase() + : String(found).match(sel) ) + if( match && typeof child != 'function' ){ + out.push( child ) + break; + } + } + } + },db) + return $$.decorate(out) +} + +$$.decorate = function(obj){ + for( let i in $$ ) obj[i] = $$[i].bind(obj) + return obj +} + +$$.each = function(cb){ + this.map( cb ) + return this +} + +$$.traverse = function(cb,me){ + me = me || room + if( !me.children ) return + for( let i in me.children ){ + proceed = cb( me.children[i] ) + if( proceed === false ) return + $$.traverse(cb, me.children[i] ) + } + return this +} + +$$.get = function(k, fallback){ + if( !k ){ // return first object + return this[0] || false + } + res = [] + this.each( function(el){ + res.push( + $.get( el, k ) + ) + }) + //res = res.filter( v => v != undefined) // delete empties + + return $$.decorate(res) +} + +$.get = function(el,path){ + return new Function('el',`return el.${path.replace(/\./,'?.')}`)(el) +} + +$.set = function set(obj, path, value) { + var last + var o = obj + path = String(path) + var vars = path.split(".") + var lastVar = vars[vars.length - 1] + vars.map(function(v) { + if (lastVar == v) return + o = (new Function("o","return o." + v)(o) || new Function("o","return o."+v+" = {}")(o)) + last = v + }) + new Function("o","v","o." + lastVar + " = v")(o, value) +} + +$$.set = function(k,v,opts){ + + this.each( function(el){ $.set( el, k, v ) }) + return this +} + +$$.wrap = function wrap(obj, method, handler, context) { + var org = obj[method]; + // Unpatch first if already patched. + if (org.unwrap) { + org = org.unwrap(); + } + // Patch the function. + obj[method] = function() { + var ctx = context || obj; + var args = [].slice.call(arguments); + args.unshift(org.bind(ctx)); + return handler.apply(ctx, args); + }; + // Provide "unpatch" function. + obj[method].unwrap = function() { + obj[method] = org; + return org; + }; + // Return the original. + return org; +} + +$$.dispatchEvent = function(event){ + this.each( (o) => { + if( o.dispatchEvent ){ + o.dispatchEvent(event) + } + }) + return this +} + +$$.addEventListener = function(eventname, cb){ + this.each( (o) => { + if( o.addEventListener ){ + o.addEventListener(eventname, cb) + } + }) + return this +} + +$$.when = function( condition ){ + res = this.filter( condition ) + return $$.decorate(res) +} + +$$.then = function( cb ){ + this.each( cb ) + return this +} + +$$.proxy = function(extra){ + let newtargets = [] + this.each( (el) => newtargets.push( $.proxy(extra,el) ) ) + return $$.decorate( newtargets ) +} + +$.proxy = (extra,el) => new Proxy(el,{ + get(me,k){ return me[k]; }, + set(me,k,v){ + if( extra[k] ){ + return extra[k].apply(me,[() => me[k] = v]) + }else me[k] = v + return true + }, +}) + +if( typeof room != 'undefined' ) room.registerElement("$$",{}) // just to notify dependencies +/* tiny (recursive) tween function for Janus objects with auto-garbage collection + * + * usage: const speed = 0.05 // closer to zero == smoother/slower + * logo.addEventListener("tween_done", alert ) + * logo.addEventListener("tween_prop_done", alert ) + * tween( logo, {"pos.y": [-0.1,speed], "pos.x": [-0.05,speed]}) + * room.update = $.tween.update // call tween.update() every frame + * + * dependency: https://codeberg.org/coderofsalvation/janus-script-jjq + */ + +mountTween = function(){ + + $.tween = function(el, opts){ + $.tween.remove = (el) => { + delete el.tween + el.tween = undefined + $.tween.items = $.tween.items.filter( (el) => el.tween ? el : false ) + } + // This is our Linear Interpolation method. It takes 3 parameters: + // a: The starting value + // b: The destination value + // n: The normal value (between 0 and 1) to control the Linear Interpolation + $.tween.lerp = function(a, b, n) { + return (1 - n) * a + n * b; + } + $.tween.update = () => { + $.tween.items.map( (el) => { + for( let prop in el.tween ){ + let diff = 0 + const to = el.tween[prop][0] + const speed = el.tween[prop][1] + let children = 0 + const lerp = (child) => { + if( $.get(child,prop) == undefined ) return + const v = $.tween.lerp( $.get(child,prop), to, speed ) + $.set(child, prop, v) + diff += Math.abs(to-v) + children++ + } + lerp(el) + $$(el).each( lerp ) + // garbage collect + if( diff < $.tween.items.resolution ){ + if( el.dispatchEvent ){ + el.dispatchEvent({type:"tween_prop_done",prop,tween:el.tween}) + } + delete el.tween[prop] + } + } + if( Object.keys(el.tween).length == 0 ){ + $.tween.remove(el) + if( el.dispatchEvent ){ + setTimeout( () => { + el.dispatchEvent({type:"tween_done",tween:el.tween}) + },50) + } + } + }) + } + $.tween.items = $.tween.items || [] + $.tween.items.resolution = 0.01 // decides when to flush tweens + + if( el.tween ) $.tween.remove(el) // overwrite previous + el.tween = opts + $.tween.items.push(el) + } + +} + +// be direct or patient +if( typeof $$ != 'undefined' ) mountTween() +else{ + room.addEventListener("registerelement", function(e){ + if( e.data == "$$") mountTween() + }) +} +function register(){ + + if( register.triggered ) return // ignore on-the-fly-loaded assetscripts + register.triggered = true + + room.registerElement('button', { + use:"button", + lighting: "false", + text: "", + locked:true, + wireframe: false, + scale: "1 1 1", + scale_back: "1 1 1", + text_scale: 0.6, + font_scale: false, + font_size: 0.08, + col: "0 0 0", + col_text: "1 1 1", + + createChildren: function(){ + if( this.billboard ){ + console.warn("billboarding on multiple transparent dialogs will tank the framerate :/") + } + this.sounds = { + click: room.createObject('Sound', { id: 'button-click', }, this), + hover: room.createObject('Sound', { id: 'button-hover', }, this), + }; + if( this.text ){ + this.label = this.createObject('text',{ + xdir: "-1 0 -0.000002", + locked:true, + pos: "0 -0.022 0.01", + scale: `${this.scale.x*this.text_scale} ${this.scale.y*this.text_scale} 0.0001`, // dereference + font_scale: this.font_scale, + font_size: this.font_size, + text: this.text, + col: `{this.col_text.r} ${this.col_text.g} ${this.col_text.b}` // dereference + }) + } + this.button = this.createObject('object',{ + id: this.use, + collision_id: 'button', + wireframe: this.wireframe, + col: `{this.col.r} ${this.col.g} ${this.col.b}`, // dereference + locked:true, + lighting: this.lighting ? "true" : "false", + scale: this.scale_back //`${this.scale_back.x} ${this.scale_back.y} ${this.scale_back.z}` + }) + + const onClick = () => { + this.sounds.click.play() + setTimeout( () => { this.button.visible = false }, 1 ) // wait for theme-change + setTimeout( () => { this.button.visible = true; }, 70 ) + room.dispatchEvent({type:"dialog_button_click", button:this}) + } + const onHover = () => { + this.sounds.hover.play() + room.dispatchEvent({type:"dialog_button_hover", button:this}) + } + this.button.addEventListener('activate', onClick) + this.button.addEventListener('click', onClick) + this.button.addEventListener('gazeenter', onHover) + this.button.addEventListener('mouseover', onHover) + } + + }) + + room.registerElement('dialog', { + "content_id":"", + "backportal": false, + "debug": false, + "theme_default": "light", + "reroute": true, // reroute window.alert e.g. + "billboard": false, + watch: "", + + theme:{ + "current":"dark", + "dark":{ /* generated from "light" via lightToDarkTheme() */ }, + "light":{ + css: (opts) => ` + .paragraphcontainer{ + max-width:unset; + min-height:unset; + text-align: center; + } + .br { height: 1em; } + .hr { margin: .5em 0; border: 1px inset #ccc; height: 0px; } + .paragraphcontent{ + overflow:hidden; + text-align:left; + height: ${opts.height} !important; /* important for crossbrowser compatibility */ + max-height: ${opts.height} !important; /* important for crossbrowser compatibility */ + width: ${opts.width} !important; /* important for crossbrowser compatibility */ + max-width: ${opts.width} !important; /* important for crossbrowser compatibility */ + background:#FFFFFFCC; + border-radius:10px; + padding:7px 15px; + display:inline-block; + } + blockquote { + margin-left: 0; + padding: 5px 15px; + border-left: 4px solid #000; + } + pre { + font-face: monospace; + padding:15px; + margin:0; + background:#00000044; + border-radius:7px; + display:inline-block; + } + `, + jml: { + "*": { + }, + object: { + back_alpha: 0 + }, + paragraph: { + scale: "2 2 2", + back_alpha: "0", + lighting: "false" + } + }, + custom: (opts) => { + $$('text', opts.db).set('col', opts.invert ? '1 1 1' : '0 0 0') + $$('paragraph', opts.db).set('col', opts.invert ? '1 1 1' : '0 0 0') + $$('object', opts.db).set('col', opts.invert ? '0 0 0' : '1 1 1') + $$('object', opts.db).set('lighting', 'false') + $$('object', opts.db).set('transparent','false') + } + } + }, + + //onbuttondown: new CustomEvent('buttondown'), + + getTheme: function(){ + if( !room.theme ){ // setup proxy so we can listen to global themechanges + room.theme = new Proxy( this.theme,{ + get(me,k){ return me[k] }, + set(me,k,v){ + me[k] = v + if( k == 'current' ){ + $$('dialog').each( (dialog) => dialog.render() ) + } + return true + } + }) + room.dispatchEvent({type:"dialog_theme_init"}) + } + return room.theme[ room.theme.current ] + }, + + createChildren: function() { + this.lightToDarkTheme() // generate extra dark theme + // attach theme to room.theme + this._theme = this.getTheme() + // set default theme + room.theme.current = this.theme_default + + // adjust for billboard + this.xdir = this.billboard ? "1 0 0" : "-1 0 -0.000002" + this.zdir = this.billboard ? "0 0 1" : "0.000002 0 -1" + + this.getContent() + this.setupWatchers() + this.render() + this.rerouteWindow() + this.optimizeAR() + + // fix janus.navigateBack() janus.navigateForward() userposition + room.addEventListener('room_active', () => { + this.optimizeAR(true) + }) + }, + + removeContent: function(js_id){ + let content = this.children[0] + if( content ){ + this.removeChild(content) + room.objects[ content.js_id ] = content + room.appendChild(content) + room.dispatchEvent({type:"dialog_content_remove", content, dialog:this}) + } + }, + + getContent: function(js_id){ + if( js_id ){ + this.removeContent() + this.content_id = js_id + } + let content = this.children[0] || $(this.content_id) + if( !content ) return + content.visible = true + content.pos = "0 0 0" + content.parentBackup = content.parent + content.parent.removeChild(content) + this.appendChild( content ) + }, + + render: function(js_id){ + if( this.rendering ) return // prevent recursion + this.rendering = true + this._theme = this.getTheme() + room.dispatchEvent({type:"dialog_content_render", dialog:this}) + try{ + if( js_id ) this.getContent(js_id) + room.dispatchEvent({type:"dialog_theme_apply", dialog:this}) + this.processParagraphs() + $$(this.children).set('collidable', this.collidable ? "true" : "false") + $$(this.children).set('pickable', this.collidable ? "true" : "false") + this.applyTheme() + }catch(e){ + console.error(e) + } + this.rendering = false + }, + + applyTheme: function(){ + let theme = this._theme = this.getTheme() + $$( this.children ) + .traverse( (child) => { + if( !child.tag ) return + let tag = String(child.tag).toLowerCase() + for( let i in theme.jml['*'] ){ // default + child[i] = theme.jml['*'][i] + if( this.debug ) console.log(`child.${tag} = theme.jml['*'][${i}] = ${theme.jml['*'][i]}`) + } + if( theme.jml[tag] ){ // tag-specific + for( let i in theme.jml[tag] ){ + if( this.debug ) console.log(`child.${tag} = theme.jml[${tag}][${i}]`) + child[i] = theme.jml[tag][i] + } + } + }) + + if( !this.children[0]?.children ) return + const db = {in:['tag'], from: this.children[0].children } + theme.custom({db}) // apply only to children of dialog + // invert colors of objects which have + $$(/.*/, db).each( (el) => { + if( el.invert || el?.args?.properties?.invert ){ + el.col.r = 1.0 - el.col.r + el.col.g = 1.0 - el.col.g + el.col.b = 1.0 - el.col.b + } + }) + return this + }, + + processParagraphs(){ + // get all elements + $$('paragraph', {in:['tag'], from:this.children}) + .each( el => { + // *FIXME* is el.args a janusweb thing? + const height = el.args.properties.height || '200px' // annoying default to urge dev to set it explicitly + const width = el.args.properties.width || '300px' // annoying default to urge dev to set it explicitly + const cssClass = el.cssClass + let tag = el.tag + el.textBackup = el.textBackup || el.text + let text = el.textBackup // reset to original template + text = this.processTemplateVars(text) + text = this.processMarkdown(text) + text = this.wrapCSS(text, this._theme.css({height,width}), cssClass ) + el.text = text + room.dispatchEvent({type:"dialog_paragraph_render", paragraph: el, dialog:this}) + if( this.debug ) console.log(el.text) + }) + return this + }, + + processMarkdown: function(text){ + return text + .replace(/(\n|^|\s+)#(?!#) *(.*)/g, "

$2

") + .replace(/(\n|^|\s+)##(?!#) *(.*)/g, "

$2

") + .replace(/(\n|^|\s+)###(?!#) *(.*)/g, "

$2

") + .replace(/(\n|^|\s+)####(?!#) *(.*)/g, "

$2

") + .replace(/(\n|^|\s+)#####(?!#) *(.*)/g, "
$2
") + .replace(/(\n|^|\s+)######(?!#) *(.*)/g, "
$2
") + .replace(/(\n|^|\s+)> *(.*)/g, "
$2
") + .replace(/\*\*(.*)\*\*/g, "$1") + .replace(/```(\S*)((?:[^`]|`(?!``))*)```/g, "
$2
") + .replace(/(<\/h[1-6]>)\n/g, "$1") + .replace(/\n/g, "
") + .replace(/\[([^\]]+)\]\(([^\)]*)\)/g, "
$1") + .replace(/(?$1"); //` closing backtick for syntax highlighting + }, + + processTemplateVars: function(text){ + return (new Function('text','return `'+text.replace(/`/g,'\\`')+'`')(text)) + }, + + wrapCSS: function(text,css,classname){ + return `\n
${text}
` + }, + + lightToDarkTheme: function(){ + // we just invert the light theme + this.theme.dark = { + css: (opts) => { + let css = this.theme.light.css(opts) + css = css.replace('.paragraphcontainer{', '.paragraphcontainer{ filter: invert(1);') + return css + }, + jml: this.theme.light.jml, + custom: (opts) => this.theme.light.custom({...opts,invert:true}) + } + }, + + setupWatchers: function(){ + const clearProxies = () => { + for( let i in room ){ + if( room[i]?.unwatch ) room[i] = room[i].unwatch() + } + } + clearProxies() + const watch = this.children[0]?.args?.properties?.watch + if( !watch ) return + let objs = watch.split(",") + objs.map( (name) => { + name = name.trim() + room[ name ] = room[ name ] || {} + if( room[ name ]?.watching ) return + room[ name ] = new Proxy( room[ name ],{ + dialog: this, + get(me,k){ return me[k]; }, + set(me,k,v){ + me[k] = v + this.dialog.render() + return true + } + }) + + }) + }, + + positionBeforeCamera: function(obj){ + + }, + + rerouteWindow: function(){ + if( !this.reroute ) return + + const resetPopup = () => { + if( room.popup ) room.removeChild(room.popup) + room.popup = false + } + + window.alert = (msg,opts) => { + opts = opts || {} + opts = {timeout:2000, ...opts} + resetPopup() + + if( !room.objects.dialog_alert ){ + const container = room.createObject('object',{ + id: "dialog_alert", + back_alpha: "0" + }) + container.createObject('paragraph',{ + width: "200px", + height: "120px", + lighting: "false", + collission_id: "false", + text: `
\n${ opts.header ? `# ${opts.header}` : ''}\n${msg}
` + }) + } + //tmpVec is the coords of -0.3 meter in front of your face + let tmpVec = translate(scalarMultiply(player.view_dir,-0.3),player.head_pos); + let pos = translate( tmpVec, V(0,-0.3,0)) + room.popup = room.createObject('dialog',{ + billboard:"y", + content_id: "dialog_alert", + pos: pos + }) + this.positionBeforeCamera( room.popup ) + if( opts.timeout ) setTimeout( resetPopup, opts.timeout) + } + }, + + optimizeAR: async function(resetHead){ + // hide or reposition the reciprocal-link in AR + const exitLink = room.objects['reciprocal-link'] + if( exitLink ){ + if( this.backportal === false ) exitLink.visible = false + else exitLink.pos.z -= 1 // move a bit backward + } + // reposition user exactly at room.pos for seamless AR-menus + // because: + // * nearby exitLink (collision) can push avatar/player away + // * janus.navigateBack() does not reset camera orientation + console.log("position player") + player.pos = room.pos || "0 0 0" + if( resetHead ) player.reset_position() + } + + }); + +} + +room.addEventListener('janus_room_scriptload', register ) +room.addEventListener('janus_room_scriptload', function(){ + + room.registerElement('peertube', { + src: "", + loop: false, + seeking: false, + seekrate: 1000, + profile: '720p', + object_id: "plane", + auto_play: false, + loop: true, + show_playbutton: true, + show_progress: true, + aspectRatio: true, + playbutton_col: "0.5 0.5 0.5", + buttoncolor_play: "0 1 0", + buttoncolor_pause: "1 0 0", + slider_pos: "", + + createChildren: function() { + this.createControls(); + this.xdir = "-1 0 -0.000002" + this.zdir = "0.000002 0 -1" + setTimeout( this.loadVideo, 1000 ) // give controls time to settle + }, + + createVideo: function(){ + this.object = this.createObject('object', { + id: this.object_id, + collision_id: this.object_id, + length: 1.5, + width: 1, + height: 1, + pos: "0 0 0", + cull_face:"none", + lighting: false + }); + }, + + createControls: function() { + this.createVideo() + if( this.show_progress ){ + this.slider = this.createObject('Slider', { + length: 1.5, + width: .1, + height: .01, + ydir: V(0,0,1), + zdir: V(0,-1,0), + visible: false, + }); + } + + // playbutton: Create three vertices and connects them into a triangle. + const arrayOfVertices = [ -1,1,0, 0.5,0,0, -1,-1,0 ]; + const arrayOfFaces = [0, 1, 2]; + room.loadNewAsset("Object", {id:"playbutton", mesh_verts:arrayOfVertices, mesh_faces:arrayOfFaces}); + this.playbutton = room.createObject('object', { + id: "playbutton", + js_id: "playbutton", + collision_id: "playbutton", + cull_face:"none", + col: this.playbutton_col, + pickable: false, + collidable: false, + lighting: false, + visible: false, + }, this); + + // we do this here because createObject does not properly set these props in time + setTimeout( () => { + this.playbutton.scale = "0.25 0.25 0.25" + this.playbutton.pos = "0.09 0.02 0.1" + this.addListeners() + },1000) + + this.addEventListener("update", this.update ) + }, + + update: function(){ + const video = this.object?.video + if( video ){ + if ( !this.object.firstframe && video && video.readyState >= video.HAVE_CURRENT_DATA ) { + this.object.firstframe = true + this.dispatchEvent({type:"peertube_video_firstframe"}) + setTimeout( () => this.resize(), 200) + this.addListeners(this.object.video) + } + if( this.playbutton ){ + this.playbutton.visible = video.paused + } + } + }, + + setSliderMax: function(){ + if( this.slider && this._data || this.object?.video?.duration ){ + this.slider.max = (this._data ? this._data.duration : this.object.video.duration) + } + }, + + updateTimeSlider: function(ev) { + if (!this.seeking && this.slider) { + this.setSliderMax() + this.slider.setValue(this.object.video.currentTime, true); + } + }, + seekstart: function() { + this.seeking = true; + }, + seekend: function() { + this.seeking = false; + }, + seek: function(ev) { + var now = performance.now(), + lastseek = this.lastseek || 0; + if (!this.lastseek || now - lastseek > this.seekrate) { + this.lastseek = now; + this.seekInstantly(); + } else { + if (!this.seektimer) { + var nextseek = Math.min(this.seekrate, now - lastseek); + this.seektimer = setTimeout(this.seekInstantly, nextseek); + } + } + }, + seekInstantly: function() { + this.setSliderMax() + if( this.slider ){ + this.object.video.currentTime = this.slider.value; + } + this.seektimer = false; + }, + + loadVideo: function(){ + this.reset() + if( !this.src ) return console.warn(" no src-attribute given") + // get HLS stream from peertube + let parts = this.src.split("/w/") + videoId = parts.pop().replace(/(\?|#).*/,"") + this.host = parts[0] + console.log(`peertube: API request ${this.host}/api/v1/videos/${videoId}`) + fetch(`${this.host}/api/v1/videos/${videoId}`) + .then(res => res.json()) + .then( data => { + this._data = data + const files = data.streamingPlaylists && data.streamingPlaylists.length ? data.streamingPlaylists[0].files : data.files + this._stream = files[0] + if( this.aspectRatio && this.slider ){ + this.object.scale = `${this.slider.length*1.015} ${this.slider.length/data.aspectRatio} 0.01` + } + // pick '720p' profile by default (to not let a 4k video tank our performance) + files.map( (stream) => { + if( stream.label == this.profile ){ + this._stream = stream + } + }) + this.dispatchEvent({type: "loaded", data}) + }) + .catch( (e) => { + console.error(e) + }) + .finally( () => this.loadAssets() ) + }, + + pollVideo: function(){ + if( this.object.video && !this.object.video?.detected ){ + this.object.video.detected = true + }else{ + console.warn("peertube: waiting for new video texture") + clearTimeout( this.pollVideo.tid ) + this.pollVideo.tid = setTimeout( () => this.pollVideo(), 300 ) + } + }, + + reset: function(){ + if( this.object.isPlaying() ) this.object.pause() + this.object.firstframe = false + }, + + resize: function(){ + const videoBBox = this.object.getBoundingBox() + if( !this.slider ) return + const sliderBBox = this.slider.getBoundingBox() + const videoHeight = videoBBox.max.y - videoBBox.min.y; + if( this.slider ){ + if( this.slider_pos ) this.slider.pos = this.slider_pos + else{ + this.slider.pos.y = -(this.slider.length / this._data.aspectRatio) / 2 + this.slider.visible = this.show_progress + } + this.slider.pos.z = 0.05 + } + this.dispatchEvent({type:"peertube_video_resize"}) + }, + + loadAssets: function(){ + if( !this._stream || !this._stream.fileUrl ) return console.error("could not get peertube stream link") + const hash = btoa(this.src) + this._peertubeThumb = this.host+"/"+this._data.thumbnailPath + this._peertubeSrc = this._stream.fileUrl + //room.loadNewAsset("image",{id:`${hash}_thumb`,src: this._peertubeThumb, proxy:false}) + room.loadNewAsset("video",{id:`${hash}_video`,src: this._peertubeSrc, auto_play: this.auto_play, loop: this.loop, proxy:false }) + + this.object.video_id = `${hash}_video` + const asset = this.getAsset('video', this.object.video_id) + this.object.video = asset.getInstance().image; + return true + }, + + playPause: function(ev){ + // not sure why this works better inside setTimeouts + setTimeout( () => { + const fn = this.object.isPlaying() ? 'pause' : 'play' + this.object[ fn ]() + this.dispatchEvent({type: `peertube_video_${fn}`}) + },100) + }, + + haltClick: function(ev){ + ev.preventDefault(); + ev.stopPropagation(); + }, + + addListeners: function(video){ + if( !this.addListeners.done ){ // once + this.addListeners.done = true + if( this.slider ){ + this.slider.ongrabstart = this.seekstart + this.slider.onchange = this.seek + this.slider.ongrabend = this.seekend + this.slider.base.addEventListener('mouseup', this.haltClick ) + this.slider.base.addEventListener('mousedown', this.haltClick ) + this.slider.base.addEventListener('click', this.haltClick ) + } + this.object.addEventListener("click", () => this.playPause() ) + } + + if( video ){ + //video.addEventListener('play', () => this.playPause() ) + //video.addEventListener('pause', () => this.playPause() ) + //this.object.video.addEventListener("click", this.playPause ) + video.addEventListener('timeupdate', (ev) => this.updateTimeSlider(ev) ); + // forwarders + video.addEventListener('playing', this.dispatchEvent ) + video.addEventListener('play', this.dispatchEvent ) + video.addEventListener('pause', this.dispatchEvent ) + video.addEventListener('ended', this.dispatchEvent ) + video.addEventListener('timeupdate', this.dispatchEvent ) + } + } + + }) + +}) +var init = async function init(){ + if( this.instance ) this.instance = String(this.instance).replace(/\/$/,'') // remove trailing slash + if( this.message ) this.state.status = this.message + init.interface.call(this) + init.stateMachine.call(this) + init.animations.call(this) + this.state.loading(true) + + if( this.autoload ) init.data.call(this) + +} + +init.data = function(){ + setTimeout( async () => { + try{ + //room.categories = await this.getApi('categories') + let vids = await this.getApi( (this.channel ? 'channel' : 'videos'),{count: this.maxcolumns * this.maxrows}) + + $('videos').grid.load(vids).render() + this.state.interface() + + }catch(e){ + console.error(e) // *TODO* handle error (error img?) + } + }, this.delay) +} + +init.interface = function(){ + + this.loader = this.createObject("object",{ + js_id:"loader", + pos: "0.15 0.2 0" + }) + this.loader.anim = this.loader.createObject("object",{ + id:"plane", + js_id: "anim", + wireframe: true, + col: "0.33 0.33 0.33", + scale: "0.1 0.1 0.1", + pos: "0 0 0", + transparent: true, + opacity: 1.0, + rotate_axis: "0 0 1", + rotate_deg_per_sec: 280.0 + }) + this.loader.text = this.loader.createObject("text",{ + locked:true, + pos: "0 -0.15 0.01", + rotation: "-180 0 180", + font_scale: false, + font_size: 0.035, + scale: `1 1 0.001`, + text: this.state.status, + col: `0.33 0.33 0.33` + }) + + // generate peertube triangle logo + const arrayOfVertices = [ -1,1,0, 1,0,0, -1,-1,0 ]; + const arrayOfFaces = [0, 1, 2]; + room.loadNewAsset("Object", {id:"triangle", mesh_verts:arrayOfVertices, mesh_faces:arrayOfFaces}); + + this.loader.logo = this.loader.createObject("object",{js_id:"logo", pos: "0 0.1 0"}) + const triangleScale = "0.082 0.088 1" + const onClickLogo = () => { + console.log("click") + if( !this.autoload ){ + console.log("dataload") + init.data.call(this) + this.state.loading.unifyLogo() + this.autoload = false + this.loader.anim.visible = true + } + } + this.loader.logo.createObject('object',{ + id:"triangle", + opacity:0.0, + transparent:true, + collision_id:"triangle", + col: "0.9 0.3 0.01", + lighting: false, + scale: triangleScale, + pos: "-0.1 0 0", + rotation: "0 0 180", + onclick: onClickLogo + }) + this.loader.logo.createObject('object',{ + id:"triangle", + collision_id:"triangle", + opacity:0.0, + transparent:true, + col: "0 0 0", + lighting: false, + scale: triangleScale, + pos: "0.073 0.088 0", + rotation: "0 0 180", + onclick: onClickLogo + }) + this.loader.logo.createObject('object',{ + id:"triangle", + collision_id:"triangle", + opacity:0.0, + transparent:true, + col: "0.33 0.33 0.33", + lighting: false, + scale: triangleScale, + pos: "0.073 -0.088 0", + rotation: "0 0 180", + onclick: onClickLogo + }) + + const videos = room.createObject('object',{ + js_id: "videos", + col: "1 0 0", + alpha: 0.5, + watch: "peertube", + }) + + videos.appendChild( + videos.grid = room.createObject('peertube-grid',{ + js_id: "grid", + spaceX: 0.70, + spaceY: 0.55, + pos: "-1.2 0.7 0", // reposition thumbnail grid + columns: this.maxcolumns, + visible: false + }) + ) + videos.grid.room = room // not sure why this is needed? + + videos.grid.load = (vids) => { + + // reset children + $$( $('videos').grid ).each( (c) => { + if( c.parent ) c.parent.removeChild(c) + c.die() + }) + + // load thumbs + this.state.status = "loading thumbnails" + const maxItems = this.maxrows * this.maxcolumns + for( let i = 0; i < vids.data.length && i < maxItems; i++ ){ + const js_id = `thumb${i}` + init.interface.thumbnail.call( this, vids, js_id, i, this.delay_asset ) + this.state.status = "loading thumbnail "+i + } + return videos.grid + } + + // close button for hiding the grid + videos.grid.close = () => { + this.state.interface.show(false) + } + videos.grid.close.button = this.createIcon(videos, { + js_id: "infoicon", + text: "x", + visible: false, + onclick: videos.grid.close + }) + + this.video = this.createObject("object",{js_id:"video"}) + + /* TODO: info button to show dialog with info */ + // info icon on video + //this.video.info = this.createIcon( this.video, { + // text:"i", + // pos: "1.06 0.75 -0.5", + // rotation: "0 0 0", + // text_pos: "0 -0.041 0.01", + // visible: false, + // onclick: () => alert("hello") + //}) + + videos.title = videos.createObject("text",{ + visible: false, + collidable: false, + pickable: false, + opacity: 0.0, + js_id:"title", + transparent:true, + align:"left", + text: "hello world", + pos: "-1.49 1 0.0000001", + font_size: .07, + font_scale: false, + lighting: false + }) + + // wrap all in dialog to trigger theming + this.dialog = { + + videos: this.createObject('dialog',{ + content_id: "videos", + pos: this.videos_pos, + rotation: "0 0 0", + js_id: "dialog_videos" + }), + + video: this.createObject('dialog',{ + content_id: "video", + rotation: "0 0 0", + js_id: "dialog_video" + }), + } + + // init themes + for( let i in this.dialog ){ + this.dialog[i].addEventListener("dialog_theme_init", () => { + room.theme.current = "light" + }) + } + +} + +init.interface.thumbnail = function(vids, js_id, i, delay){ + const maxtitle = 34 + const truncate = String(vids.data[i].name).length > maxtitle ? '..' : '' + const btn = $('videos').grid.createObject('button',{ + js_id, + scale_back: "1 1.9 1", + pos: "0 0 0.001", + data: vids.data[i], + src: `${this.instance}/w/${vids.data[i].embedPath.replace('/videos/embed/','')}`, + onclick: ((data) => () => this.playVideo(data))(vids.data[i]) + }) + btn.createObject('text',{ + js_id: "title_"+i, + text: String(vids.data[i].name).substr(0,maxtitle) + truncate, + font_scale: false, + font_size: 0.02, + align: "center", + pos: "0 -0.16 0.02" + }) + btn.createObject('text',{ + js_id: "name"+i, + text: String(vids.data[i].channel.name).substr(0,maxtitle) , + font_scale: false, + font_size: 0.02, + col: "0.9 0.3 0.01", + align: "center", + pos: "0 -0.19 0.02" + }) + // apply images without choking cpu/network + console.log(this.instance + vids.data[i].previewPath ) + setTimeout( () => { + room.loadNewAsset("image",{ id: `asset_${js_id}`, src: this.instance + vids.data[i].previewPath }) + btn.createObject("image",{ + id: `asset_${js_id}`, + lighting: false, + scale: "0.28 0.28 0.0005", + pos: "0.003 0.04 -0.09", + cull_face: "front", + collidable: false, + pickable: false + }) + }, 500 + (i*delay) ) +} + +init.stateMachine = function(){ + + this.state = new Proxy(this.state,{ + get(me,k){ return me[k] }, + set: (me,k,v) => { + me[k] = v + switch(k){ + case 'status': this.loader?.text && (this.loader.text.text = v); break; + } + return true + } + }) + + this.state.loading = (state) => { + if( !state ) this.state.status = "enjoy" + $.tween( this.loader.anim, {"opacity": [ state ? 1.0 : 0.0, 0.05]}) + $.tween( this.loader.text, {"opacity": [ state ? 1.0 : 0.0, 0.05]}) + + // show logo once + if( state && !this.loader.logo.shown){ + this.loader.logo.shown = true + $.tween( this.loader.logo, { "opacity":[ 1.0, 0.05] }) + if( this.autoload ){ + setTimeout( () => this.state.loading.unifyLogo(), 1000) + } else this.loader.anim.visible = false + } + } + + this.state.loading.unifyLogo = () => { + // unify logo triangles to center + const speed = 0.08 + $.tween( this.loader.logo, { "pos.y": [ 0, speed], + "pos.x": [ 0.01, speed], + "scale.x": [ 0.2, speed], + "scale.y": [ 0.2, speed], + "scale.z": [ 0.2, speed], + "rotation.z": [ 360, speed], + "opacity": [ 0, speed] }) + } + + this.state.interface = (state) => { + this.state.loading(false) + this.state.interface.show(true) + } + + this.state.interface.show = (state) => { + if( state ) this.state.status = "enter navigator" + $$( $('videos') ).set('visible', state) + .set('pickable', state) + $('videos').grid.close.button.visible = $('peertubevideo') && state ? true : false + } + + this.state.watchvideo = () => { + this.state.status = "almost ready" + $$( $('videos') ).set('visible', false) + .set('pickable', false) + $$( $('video') ).when( (el) => el.js_id != "playbutton" ) + .set('visible', true) + .set('pickable',true) + } + +} + +room.registerElement('peertube-grid', { + offset: V(0,0,0), + spaceX: 1, + spaceY: 1, + columns: 0, + centerX: true, + centerY: true, + + create: function() { + this.render() + }, + + render: function(){ + let children = this.children; + let i = 0; + let offset = V( this.offset.x, this.offset.y, this.offset.z ) + for (var k in children) { + children[k].pos.x = offset.x + children[k].pos.y = offset.y + offset.x += this.spaceX + i++ + if( this.columns && ((i % this.columns) == 0) ){ + offset.x = this.offset.x + offset.y -= this.spaceY + } + } + } +}); + +init.animations = function(){ + room.update = function(dt){ + if( $.tween.update ) $.tween.update() + }.bind(this) +} + +registerPeerTubeApp = () => room.registerElement('peertube-app', { + instance: "https://makertube.net", + channel: "", + search:"", + message: "hello peertube", + autoload: true, + maxrows: 3, + maxcolumns: 4, + delay: 2000, // scene-load delay to save fps + delay_asset: 300, // load asset delay to save fps + videos_pos: "0 0 0", // reposition thumbnail grid + + state: { status: "" }, + _config: { + dialog: { + rooturl: window.dialogPath || "https://codeberg.org/coderofsalvation/janus-script-dialog/raw/branch/master", + corsproxy: true + } + }, + _api: { + videos: function(opts){ return this.instance + `/api/v1/videos?${this.search?`search=${this.search}&`:''}start=${opts.start||0}&count=${opts.count||9}` }, + channel: function(opts){ return this.instance + `/api/v1/video-channels/${this.search?`search=${this.search}&`:''}${this.channel}/videos?start=${opts.start||0}&count=${opts.count||9}` }, + categories: function(opts){ return this.instance + `/api/v1/videos/categories` } + }, + + createChildren: init, + + playVideo: function(video){ + this.state.status = "loading peertube video" + this.state.interface.show(false) + this.state.loading(true) + + let src = `${this.instance}/w/${video.embedPath.replace('/videos/embed/','')}` + if( !this.video.peertube ){ + this.video.peertube = this.createObject("peertube",{ + js_id: "peertubevideo", + src, + auto_play:true, + pos: V(0.2, 0.1, 0.6), + scale: "1.9 1.9 1", + visible: false + }) + + const watch = () => { + this.video.peertube.visible = true + this.state.watchvideo() + } + this.video.peertube.addEventListener("peertube_video_firstframe", () => { + this.state.loading(false) + // style the player + //this.video.peertube.slider.children[0].col = "0 0 0" + //this.video.peertube.slider.children[1].col = "0.1 0.1 0.1" + }) + this.video.peertube.addEventListener("peertube_video_resize", watch ) + this.video.peertube.addEventListener("peertube_video_play", watch ) + this.video.peertube.addEventListener("peertube_video_pause", () => { + this.state.interface.show(true) + }) + }else{ + this.video.peertube.src = src + this.video.peertube.auto_play = true + this.video.peertube.loadVideo() + } + }, + + + /* tiny (recursive) tween function for Janus objects with auto-garbage collection + * + * usage: const speed = 0.05 // closer to zero == smoother/slower + * logo.addEventListener("tween_done", alert ) + * logo.addEventListener("tween_prop_done", alert ) + * $.tween( logo, {"pos.y": [-0.1,speed], "pos.x": [-0.05,speed]}) + * room.update = $.tween.update // call tween.update() every frame + * + * dependency: https://codeberg.org/coderofsalvation/janus-script-jjq + */ + tween: function(el, opts){ + $.tween.remove = (el) => { + delete el.tween + el.tween = undefined + $.tween.items = $.tween.items.filter( (el) => el.tween ? el : false ) + } + // This is our Linear Interpolation method. It takes 3 parameters: + // a: The starting value + // b: The destination value + // n: The normal value (between 0 and 1) to control the Linear Interpolation + $.tween.lerp = function(a, b, n) { + return (1 - n) * a + n * b; + } + $.tween.update = () => { + $.tween.items.map( (el) => { + for( let prop in el.tween ){ + let diff = 0 + const to = el.tween[prop][0] + const speed = el.tween[prop][1] + let children = 0 + const lerp = (child) => { + if( $.get(child,prop) == undefined ) return + const v = $.tween.lerp( $.get(child,prop), to, speed ) + $.set(child, prop, v) + diff += Math.abs(to-v) + children++ + } + lerp(el) + $$(el).each( lerp ) + // garbage collect + if( diff < $.tween.items.resolution ){ + if( el.dispatchEvent ){ + el.dispatchEvent({type:"tween_prop_done",prop,tween:el.tween}) + } + delete el.tween[prop] + } + } + if( Object.keys(el.tween).length == 0 ){ + $.tween.remove(el) + if( el.dispatchEvent ){ + setTimeout( () => { + el.dispatchEvent({type:"tween_done",tween:el.tween}) + },50) + } + } + }) + } + $.tween.items = $.tween.items || [] + $.tween.items.resolution = 0.01 // decides when to flush tweens + + if( el.tween ) $.tween.remove(el) // overwrite previous + el.tween = opts + $.tween.items.push(el) + }, + + getApi: async function(resource,opts){ + this.state.status = `connecting to ${String(this.instance).replace(/.*:\/\//,'')}` + opts = opts || {} + for( let i in opts ) opts[i] = String(opts[i]) // enforce strings + let res = await fetch( this._api[resource].call(this,opts) ) + return res.json() + }, + + createIcon: function(parent, opts){ + opts = opts ||{} + let btn = parent.createObject('button',{ + pos: "-0.15 -0.75 0", + use: "icon", + fontscale: 0.08, + ...opts, + }) + if( opts.pos_text ){ + setTimeout( ( )=> btn.label.pos = opts.pos_text, 100 ) // center text + } + return btn + } + +}) + +room.addEventListener("registerelement", function(e){ + room.has = room.has || {} + room.has[e.data] = true + + if( room.has.dialog && room.has.button && room.has.peertube && !room.has.inited){ + + room.has.inited = true + registerPeerTubeApp() + + + // const isTranslator = typeof janus != 'undefined' ? true : false + // if( isTranslator ){ + // room.createObject("peertube-app",{ + // js_id: "peertube-app", + // pos: "0 1.3 0" + // }) + // } + + } +}) diff --git a/src/janusweb/media/component/janus-script-peertube/translator.js b/src/janusweb/media/component/janus-script-peertube/translator.js new file mode 100644 index 0000000..55570c2 --- /dev/null +++ b/src/janusweb/media/component/janus-script-peertube/translator.js @@ -0,0 +1,104 @@ +const PeertubeTranslatorBaseURL = document.currentScript.src.replace("/translator.js","") + +elation.require([], function() { + elation.component.add('janusweb.translators.peertube', function() { + this.init = function() { + this.description = "peertube website to 3D translator" + } + this.exec = function(args) { + return new Promise(elation.bind(this, function(resolve, reject) { + + var room = this.room = args.room; + + reject() // for now + + this.setupEvents() + var datapath = elation.config.get('janusweb.datapath', '/media/janusweb'); + + var roomdata = { + assets: { + assetlist: [ + // {assettype: 'model', name: 'scene', src: args.url}, + ] + }, + room: { + pos: [0,0,0], + xdir: "1 0 0", + zdir: "0 0 1", + }, + object: [ + // {id: 'scene', js_id: 0, pos: "0 0 0", xdir: "-1 0 0", zdir: "0 0 -1", lighting: "false"} + ], + link: [] + }; + resolve(roomdata); + })); + } + + // translate XR Fragments microformat into JML + this.parseSource = async function(sourcecode, room){ + this.room = room + const isJML = /[\s\S]*?<\/fireboxroom>/si; + const isPeertube = /]*property=['"]og:platform['"]\s+[^>]*content=['"]peertube['"][^>]*\/?>/si; + const isPeertubeVideo = /]*property=['"]og:type['"]\s+[^>]*content=['"]video['"][^>]*\/?>/si; + const isPeertubeInstance = /]*property=['"]og:type['"]\s+[^>]*content=['"]website['"][^>]*\/?>/si; + const is = { + JML: sourcecode.match(isJML), + peertube: sourcecode.match(isPeertube), + peertubeVideo: sourcecode.match(isPeertubeVideo), + peertubeInstance: sourcecode.match(isPeertubeInstance), + } + if( is.JML || !is.peertube ) return // JML takes precedence over microformats + if( !is.peertubeVideo && !is.peertubeInstance ) return + + // ok..peertube time: get domtree + let el = document.createElement("div") + el.innerHTML = sourcecode + + // try to fetch title + cover + const title = el.querySelector("title") + let logo = el.querySelector("meta[property='og:image']")?.getAttribute("content") + logo = logo ? logo : el.querySelector("meta[property='og:image:url']")?.getAttribute("content") + + let url = new URL( String(room.url) ) + let instance = url?.origin + let search = url?.search ? new URLSearchParams(url.search).get("search") : "" + + room.corsproxy = '' + // return JML + let parsed = room.parseSource(` + ${ title ? title.innerText.replace(/\n.*/g,'') : room.baseurl.split("/").pop() } + + + ${ logo ? ` + + ${ is.peertubeInstance ? `` + : `` + } + + ${ logo ? `` : ''} + + + + + + `) + return parsed + } + // peertube microformat heuristic: + this.parseSource.regex = /]*property=['"]og:platform['"]\s+[^>]*content=['"]peertube['"][^>]*\/?>/si; + }); +}); + +elation.events.add(null, 'room_translator_register', function(e){ + const room = e.element + const translators = e.data.translators + // prepend translator + e.data.translators = {} + e.data.translators['!peertube'] = elation.janusweb.translators.peertube({janus: janus}) + for( let i in translators ){ + e.data.translators[i] = translators[i] + } +}) diff --git a/src/myverse/icosa-gallery.png b/src/myverse/tech-demos/icosa-gallery.png similarity index 100% rename from src/myverse/icosa-gallery.png rename to src/myverse/tech-demos/icosa-gallery.png diff --git a/src/myverse/icosa-gallery/README.md b/src/myverse/tech-demos/icosa-gallery/README.md similarity index 100% rename from src/myverse/icosa-gallery/README.md rename to src/myverse/tech-demos/icosa-gallery/README.md diff --git a/src/myverse/icosa-gallery/icosa-gallery.desktop b/src/myverse/tech-demos/icosa-gallery/icosa-gallery.desktop similarity index 100% rename from src/myverse/icosa-gallery/icosa-gallery.desktop rename to src/myverse/tech-demos/icosa-gallery/icosa-gallery.desktop diff --git a/src/myverse/janusxr_tutorial.png b/src/myverse/tech-demos/janusxr_tutorial.png similarity index 100% rename from src/myverse/janusxr_tutorial.png rename to src/myverse/tech-demos/janusxr_tutorial.png diff --git a/src/myverse/janusxr_tutorial/AFRAME.jpg b/src/myverse/tech-demos/janusxr_tutorial/AFRAME.jpg similarity index 100% rename from src/myverse/janusxr_tutorial/AFRAME.jpg rename to src/myverse/tech-demos/janusxr_tutorial/AFRAME.jpg diff --git a/src/myverse/janusxr_tutorial/AFRAME/README.md b/src/myverse/tech-demos/janusxr_tutorial/AFRAME/README.md similarity index 100% rename from src/myverse/janusxr_tutorial/AFRAME/README.md rename to src/myverse/tech-demos/janusxr_tutorial/AFRAME/README.md diff --git a/src/myverse/janusxr_tutorial/AFRAME/index.html b/src/myverse/tech-demos/janusxr_tutorial/AFRAME/index.html similarity index 100% rename from src/myverse/janusxr_tutorial/AFRAME/index.html rename to src/myverse/tech-demos/janusxr_tutorial/AFRAME/index.html diff --git a/src/myverse/janusxr_tutorial/AFRAME/tree2.glb b/src/myverse/tech-demos/janusxr_tutorial/AFRAME/tree2.glb similarity index 100% rename from src/myverse/janusxr_tutorial/AFRAME/tree2.glb rename to src/myverse/tech-demos/janusxr_tutorial/AFRAME/tree2.glb diff --git a/src/myverse/janusxr_tutorial/arwindow.jpg b/src/myverse/tech-demos/janusxr_tutorial/arwindow.jpg similarity index 100% rename from src/myverse/janusxr_tutorial/arwindow.jpg rename to src/myverse/tech-demos/janusxr_tutorial/arwindow.jpg diff --git a/src/myverse/janusxr_tutorial/arwindow/README.md b/src/myverse/tech-demos/janusxr_tutorial/arwindow/README.md similarity index 100% rename from src/myverse/janusxr_tutorial/arwindow/README.md rename to src/myverse/tech-demos/janusxr_tutorial/arwindow/README.md diff --git a/src/myverse/janusxr_tutorial/arwindow/Walk in the Woods by Don Carson - 38m6Q1H12DU.glb b/src/myverse/tech-demos/janusxr_tutorial/arwindow/Walk in the Woods by Don Carson - 38m6Q1H12DU.glb similarity index 100% rename from src/myverse/janusxr_tutorial/arwindow/Walk in the Woods by Don Carson - 38m6Q1H12DU.glb rename to src/myverse/tech-demos/janusxr_tutorial/arwindow/Walk in the Woods by Don Carson - 38m6Q1H12DU.glb diff --git a/src/myverse/janusxr_tutorial/arwindow/index.html b/src/myverse/tech-demos/janusxr_tutorial/arwindow/index.html similarity index 100% rename from src/myverse/janusxr_tutorial/arwindow/index.html rename to src/myverse/tech-demos/janusxr_tutorial/arwindow/index.html diff --git a/src/myverse/janusxr_tutorial/arwindow/occludebox.glb b/src/myverse/tech-demos/janusxr_tutorial/arwindow/occludebox.glb similarity index 100% rename from src/myverse/janusxr_tutorial/arwindow/occludebox.glb rename to src/myverse/tech-demos/janusxr_tutorial/arwindow/occludebox.glb diff --git a/src/myverse/janusxr_tutorial/helloworld.jpg b/src/myverse/tech-demos/janusxr_tutorial/helloworld.jpg similarity index 100% rename from src/myverse/janusxr_tutorial/helloworld.jpg rename to src/myverse/tech-demos/janusxr_tutorial/helloworld.jpg diff --git a/src/myverse/janusxr_tutorial/helloworld/README.md b/src/myverse/tech-demos/janusxr_tutorial/helloworld/README.md similarity index 100% rename from src/myverse/janusxr_tutorial/helloworld/README.md rename to src/myverse/tech-demos/janusxr_tutorial/helloworld/README.md diff --git a/src/myverse/janusxr_tutorial/helloworld/janusxr.xml b/src/myverse/tech-demos/janusxr_tutorial/helloworld/janusxr.xml similarity index 100% rename from src/myverse/janusxr_tutorial/helloworld/janusxr.xml rename to src/myverse/tech-demos/janusxr_tutorial/helloworld/janusxr.xml diff --git a/src/myverse/janusxr_tutorial/tronverse b/src/myverse/tech-demos/janusxr_tutorial/tronverse similarity index 100% rename from src/myverse/janusxr_tutorial/tronverse rename to src/myverse/tech-demos/janusxr_tutorial/tronverse diff --git a/src/myverse/janusxr_tutorial/tronverse.png b/src/myverse/tech-demos/janusxr_tutorial/tronverse.png similarity index 100% rename from src/myverse/janusxr_tutorial/tronverse.png rename to src/myverse/tech-demos/janusxr_tutorial/tronverse.png diff --git a/src/myverse/tronverse.png b/src/myverse/tech-demos/tronverse.png similarity index 100% rename from src/myverse/tronverse.png rename to src/myverse/tech-demos/tronverse.png diff --git a/src/myverse/tronverse/janusxr b/src/myverse/tech-demos/tronverse/janusxr similarity index 100% rename from src/myverse/tronverse/janusxr rename to src/myverse/tech-demos/tronverse/janusxr diff --git a/src/myverse/tronverse/tron/README.md b/src/myverse/tech-demos/tronverse/tron/README.md similarity index 100% rename from src/myverse/tronverse/tron/README.md rename to src/myverse/tech-demos/tronverse/tron/README.md diff --git a/src/myverse/tronverse/tron/tron.glb b/src/myverse/tech-demos/tronverse/tron/tron.glb similarity index 100% rename from src/myverse/tronverse/tron/tron.glb rename to src/myverse/tech-demos/tronverse/tron/tron.glb diff --git a/src/myverse/tronverse/tron/tron.mp3 b/src/myverse/tech-demos/tronverse/tron/tron.mp3 similarity index 100% rename from src/myverse/tronverse/tron/tron.mp3 rename to src/myverse/tech-demos/tronverse/tron/tron.mp3 diff --git a/src/myverse/tronverse/tron/tron.png b/src/myverse/tech-demos/tronverse/tron/tron.png similarity index 100% rename from src/myverse/tronverse/tron/tron.png rename to src/myverse/tech-demos/tronverse/tron/tron.png diff --git a/src/myverse/tronverse/tronverse-icosagallery.jpg b/src/myverse/tech-demos/tronverse/tronverse-icosagallery.jpg similarity index 100% rename from src/myverse/tronverse/tronverse-icosagallery.jpg rename to src/myverse/tech-demos/tronverse/tronverse-icosagallery.jpg diff --git a/src/myverse/tronverse/tronverse-icosagallery/README.md b/src/myverse/tech-demos/tronverse/tronverse-icosagallery/README.md similarity index 100% rename from src/myverse/tronverse/tronverse-icosagallery/README.md rename to src/myverse/tech-demos/tronverse/tronverse-icosagallery/README.md diff --git a/src/plugin/openbrush.lua b/src/plugin/openbrush.lua index b6320fb..0418aaa 100644 --- a/src/plugin/openbrush.lua +++ b/src/plugin/openbrush.lua @@ -2,7 +2,11 @@ local util = require('util') local apiSync = require('apiSync') app.on('init', function() - app.plugin.openbrush.api = apiSync.init( 'openbrush' ) + if not app.opts.plugin_diskscan_dir:match("^/zip/") then + app.plugin.openbrush.api = apiSync.init( 'openbrush' ) + else + print("[openbrush.lua] disabled..please create a 'myverse' dir we can store files into") + end end) local function scanTiltFile(exp) @@ -116,7 +120,7 @@ local plugin = { key='app.opts.plugin_openbrush_sync_folder', title='Sync to folder', info='openbrush (icosa.gallery) assets will be saved here', - default = "./myverse/tronverse", + default = "./myverse/tech-demos/tronverse", type = "dir", values = {} -- populated by plugin/diskscan.lua }, diff --git a/src/util/build.sh b/src/util/build.sh index 147f53e..62141b7 100755 --- a/src/util/build.sh +++ b/src/util/build.sh @@ -4,7 +4,7 @@ OUTDIR=result/bin REDBEAN_URL="https://redbean.dev/redbean-3.0.0.com" REDBEAN_CRC="382f1288bb96ace4bab5145e7df236846c33cc4f1be69233710682a9e71e7467 $OUTDIR/xrforge.com" -download(){ +get(){ redbean(){ test -d $OUTDIR || mkdir -p $OUTDIR @@ -32,18 +32,20 @@ download(){ janusweb(){ set -x - test -d janusweb || { - version=1.7.5 + test -d pkg/janusweb || { + mkdir pkg &>/dev/null || true test -f janusweb-${version}.tar.gz || { wget "https://github.com/coderofsalvation/janusweb/releases/download/v${version}-rc/janusweb-${version}.tar.gz" } test -d janusweb-${version} || tar -xvf janusweb-${version}.tar.gz - mv janusweb-${version} janusweb + mv janusweb-${version} pkg/janusweb } set -x - rsync -avL janusweb src/. + rsync -avLu pkg/janusweb src/. # overlay but leave newer files intact # cleanup rm src/janusweb/janusxr.com || true + rm src/janusweb/test || true + rm src/janusweb/media/test || true # set xrforge as defualt config cp src/janusweb/media/assets/webui/{xrforge,default}.json } @@ -52,11 +54,12 @@ download(){ } build(){ - download redbean - download janusweb + get redbean + get janusweb cd src bundle(){ zip -yr $1 * .lua .init.lua .args; } bundle ../result/bin/xrforge.com + unzip -l ../result/bin/xrforge.com test -n "$ARCH" && bundle ../result/bin/xrforge-$ARCH.com }