repo: janusweb
action: commit
revision: 
path_from: 
revision_from: a70de0b1859f27669896866331aff66aab3094e7:
path_to: 
revision_to: 
git.thebackupbox.net
janusweb
git clone git://git.thebackupbox.net/janusweb
commit a70de0b1859f27669896866331aff66aab3094e7
Author: James Baicoianu 
Date:   Wed Jan 31 22:54:27 2018 -0800

    Added WebUI

diff --git a/media/assets/webui/apps/appdrawer/appdrawer.js b/media/assets/webui/apps/appdrawer/appdrawer.js
new file mode 100644
index 0000000000000000000000000000000000000000..4604ea1170ed04a448b91cc9e9084c82580d044e
--- /dev/null
+++ b/media/assets/webui/apps/appdrawer/appdrawer.js
@@ -0,0 +1,11 @@
+elation.elements.define('janus.ui.appdrawer', class extends elation.elements.ui.panel {
+  create() {
+    this.buttons = elation.elements.create('ui-buttonbar', {
+      append: this
+    });
+    var inventorybutton = elation.elements.create('ui-button', {
+      append: this.buttons,
+      label: 'Inventory',
+    });
+  }
+});
diff --git a/media/assets/webui/apps/appdrawer/appdrawer.json b/media/assets/webui/apps/appdrawer/appdrawer.json
new file mode 100644
index 0000000000000000000000000000000000000000..7cc371cf36c39daabd3c17a0ec5eeb2a16efcb75
--- /dev/null
+++ b/media/assets/webui/apps/appdrawer/appdrawer.json
@@ -0,0 +1,8 @@
+{
+  "templates": {
+  },
+  "scripts":  ["./appdrawer.js"],
+  "css":      ["./appdrawer.css"]
+}
+
+
diff --git a/media/assets/webui/apps/avatar/avatar.json b/media/assets/webui/apps/avatar/avatar.json
new file mode 100644
index 0000000000000000000000000000000000000000..0967ef424bce6791893e9a57bb952f80fd536e93
--- /dev/null
+++ b/media/assets/webui/apps/avatar/avatar.json
@@ -0,0 +1 @@
+{}
diff --git a/media/assets/webui/apps/bookmarks/bookmarks.css b/media/assets/webui/apps/bookmarks/bookmarks.css
new file mode 100644
index 0000000000000000000000000000000000000000..b8ddb46e597abdd0266e02d25e7c475a28726127
--- /dev/null
+++ b/media/assets/webui/apps/bookmarks/bookmarks.css
@@ -0,0 +1,41 @@
+janus-ui-bookmarks ui-grid>ui-item {
+  width: 200px;
+  height: 120px;
+  position: relative;
+  background-image: url(../popular/nopreview.png);
+  background-repeat: no-repeat;
+  background-size: contain;
+  overflow: hidden;
+  padding: 0;
+  transition: transform 100ms ease-out;
+  z-index: 1;
+}
+janus-ui-bookmarks ui-grid>ui-item:hover {
+  transform: scale(1.1);
+  z-index: 2;
+}
+.bookmarkedroom_thumb {
+  height: 130px;
+  width: 100%;
+  object-fit: cover;
+}
+.bookmarkedroom {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+}
+
+.bookmarkedroom h4 {
+  position: absolute;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  background: rgba(0,0,0,.5);
+  color: white;
+  text-shadow: 0 0 2px black;
+  white-space: nowrap;
+  text-overflow: ellipsis;
+  overflow: hidden;
+  padding: 0 .2em;
+}
+
diff --git a/media/assets/webui/apps/bookmarks/bookmarks.html b/media/assets/webui/apps/bookmarks/bookmarks.html
new file mode 100644
index 0000000000000000000000000000000000000000..b7c3b82a3f7c63d606ee5ba69195f8ba856879f6
--- /dev/null
+++ b/media/assets/webui/apps/bookmarks/bookmarks.html
@@ -0,0 +1,7 @@
+    
+    
+
+    
+
diff --git a/media/assets/webui/apps/bookmarks/bookmarks.js b/media/assets/webui/apps/bookmarks/bookmarks.js
new file mode 100644
index 0000000000000000000000000000000000000000..819a6b7905e3a3a56f81f4e57673841a22c331bc
--- /dev/null
+++ b/media/assets/webui/apps/bookmarks/bookmarks.js
@@ -0,0 +1,11 @@
+elation.elements.define('janus.ui.bookmarks', class extends elation.elements.base {
+  create() {
+    this.innerHTML = elation.template.get('janus.ui.bookmarks');
+    this.grid = this.getElementsByTagName('ui-grid');
+    elation.events.add(this.grid, 'select', (ev) => this.handleSelect(ev));
+  }
+  handleSelect(ev) {
+    this.dispatchEvent({type: 'select', data: ev.data});
+  }
+});
+
diff --git a/media/assets/webui/apps/bookmarks/bookmarks.json b/media/assets/webui/apps/bookmarks/bookmarks.json
new file mode 100644
index 0000000000000000000000000000000000000000..5052eb01149950867c06113521c4b3bc7cf03959
--- /dev/null
+++ b/media/assets/webui/apps/bookmarks/bookmarks.json
@@ -0,0 +1,13 @@
+{
+  "templates": {
+    "janus.ui.bookmarks": "./bookmarks.html",
+    "janus.ui.bookmarks.room": "./room.html"
+  },
+  "css": [
+    "./bookmarks.css"
+  ],
+  "scripts": [
+    "./bookmarks.js"
+  ]
+}
+
diff --git a/media/assets/webui/apps/bookmarks/room.html b/media/assets/webui/apps/bookmarks/room.html
new file mode 100644
index 0000000000000000000000000000000000000000..561afa63d5d869cec8febd61ee0c6289fb0d7012
--- /dev/null
+++ b/media/assets/webui/apps/bookmarks/room.html
@@ -0,0 +1,4 @@
+
+  {?thumbnail}{/thumbnail}
+  

{?title}{title}{:else}{url}{/title}

+
diff --git a/media/assets/webui/apps/buttons/buttons.js b/media/assets/webui/apps/buttons/buttons.js new file mode 100644 index 0000000000000000000000000000000000000000..d56c83ff4a93a48076feaac325ee9b9f9777d059 --- /dev/null +++ b/media/assets/webui/apps/buttons/buttons.js @@ -0,0 +1,62 @@ +elation.elements.define('janus-buttons-display', class extends elation.elements.ui.dropdownbutton { + init() { + super.init(); + + var detect = { + fullscreen: () => this.supportsFullscreen(), + webvr: () => this.supportsWebVR(), + chromecast: () => this.supportsChromecast(), + equirectangular: () => this.supportsEquirectangular(), + sbs3d: () => this.supportsSBS3D(), + ou3d: () => this.supportsOU3D(), + peppersghost: () => this.supportsPeppersGhost(), + }; + + } + + supportsFullscreen() { + } + supportsWebVR() { + } + supportsChromecast() { + } + supportsEquirectangular() { + } + supportsSBS3D() { + } + supportsOU3D() { + } + supportsPeppersGhost() { + } +}); + +elation.elements.define('janus-buttons-fullscreen', class extends elation.elements.ui.togglebutton { + create() { + super.create(); + elation.events.add(document, 'fullscreenchange', (ev) => { + if (document.fullscreen) { + this.active = true; + } else { + this.active = false; + } + }); + } + onactivate(ev) { + this.client.fullscreen(true); + } + ondeactivate(ev) { + this.client.fullscreen(false); + } +}); +elation.elements.define('janus-buttons-webvr', class extends elation.elements.ui.togglebutton { +}); +elation.elements.define('janus-buttons-chromecast', class extends elation.elements.ui.togglebutton { +}); +elation.elements.define('janus-buttons-equirectangular', class extends elation.elements.ui.togglebutton { +}); +elation.elements.define('janus-buttons-sbs3d', class extends elation.elements.ui.togglebutton { +}); +elation.elements.define('janus-buttons-ou3d', class extends elation.elements.ui.togglebutton { +}); +elation.elements.define('janus-buttons-peppersghost', class extends elation.elements.ui.togglebutton { +}); diff --git a/media/assets/webui/apps/buttons/buttons.json b/media/assets/webui/apps/buttons/buttons.json new file mode 100644 index 0000000000000000000000000000000000000000..a25b87648d4731dcb7d8706a6dae1bcf00aa60aa --- /dev/null +++ b/media/assets/webui/apps/buttons/buttons.json @@ -0,0 +1,11 @@ +{ + "templates": { + }, + "css": [ + "./buttons.css" + ], + "scripts": [ + "./buttons.js" + ] +} + diff --git a/media/assets/webui/apps/chat/chat.html b/media/assets/webui/apps/chat/chat.html new file mode 100644 index 0000000000000000000000000000000000000000..755da6168a8fe65fecdc922a3eae9a30e6fdb583 --- /dev/null +++ b/media/assets/webui/apps/chat/chat.html @@ -0,0 +1,5 @@ + + + + + diff --git a/media/assets/webui/apps/chat/chat.js b/media/assets/webui/apps/chat/chat.js new file mode 100644 index 0000000000000000000000000000000000000000..db988ff517ab25cac0252ee0d0f42054aa05a33e --- /dev/null +++ b/media/assets/webui/apps/chat/chat.js @@ -0,0 +1,6 @@ +elation.elements.define('janus.ui.chat', class extends elation.elements.base { + create() { + this.innerHTML = elation.template.get('janus.ui.chat'); + } +}); + diff --git a/media/assets/webui/apps/chat/chat.json b/media/assets/webui/apps/chat/chat.json new file mode 100644 index 0000000000000000000000000000000000000000..2c63c0851048d8f7bff41ecf0f8cee05f52fd120 --- /dev/null +++ b/media/assets/webui/apps/chat/chat.json @@ -0,0 +1,2 @@ +{ +} diff --git a/media/assets/webui/apps/inventory/inventory-item-poly.html b/media/assets/webui/apps/inventory/inventory-item-poly.html new file mode 100644 index 0000000000000000000000000000000000000000..48fcc29e3efa59fdeed34951b6681299c6ec8880 --- /dev/null +++ b/media/assets/webui/apps/inventory/inventory-item-poly.html @@ -0,0 +1,6 @@ + + +

{displayName}

+

By {authorName}

+
+ diff --git a/media/assets/webui/apps/inventory/inventory.css b/media/assets/webui/apps/inventory/inventory.css new file mode 100644 index 0000000000000000000000000000000000000000..72a4b60fdb67386171202b7d2d61ad2fa9e04ade --- /dev/null +++ b/media/assets/webui/apps/inventory/inventory.css @@ -0,0 +1,82 @@ +ui-grid.models { + width: 30em; +} +ui-grid.models>ui-item { + width: calc(20% - 2px); + height: auto; + padding: 0; + position: relative; + transition: transform ease-out 100ms; + border-radius: 10px; + box-shadow: 0 0 5px rgba(0,0,0,.5); + margin: 0; +} +ui-grid.models>ui-item h2 { + font-size: .8em; + font-weight: bold; + margin: 0; + background: transparent; + border: none; + box-shadow: none; + color: white; + position: absolute; + bottom: -1px; + left: 0; + right: 0; + background: rgba(0,0,0,.5); + padding: .2em .4em; + border-radius: 0 0 10px 10px; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; +} +ui-grid.models>ui-item h3 { + font-size: .8em; + margin: 0; + display: none; +} +ui-grid.models>ui-item img { + width: 100%; + border-radius: .5em; + margin: 0 auto; + display: block; +} +ui-grid.models>ui-item a { + display: block; + margin: 0; + padding: 0; +} +ui-grid.models>ui-item p { + display: none; + font-size: .8em; +} +ui-grid.models>ui-item:hover p { + position: absolute; + display: none; + width: 100%; + background: rgba(0,0,0,.5); + border: 1px solid black; +} +ui-grid.models>ui-item:hover { + transform: scale(1.1); + position: relative; + z-index: 1; +} +/* +#pagination { + position: fixed; + bottom: 0; + left: 0; + right: 0; + text-align: center; +} +ui-input { + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 5; + padding: .5em; +} +*/ + diff --git a/media/assets/webui/apps/inventory/inventory.html b/media/assets/webui/apps/inventory/inventory.html new file mode 100644 index 0000000000000000000000000000000000000000..1e4d96edd43c810980e0ebb2a4f36627b54ed779 --- /dev/null +++ b/media/assets/webui/apps/inventory/inventory.html @@ -0,0 +1,29 @@ + + + + + + + + + + + Next Page + + + + + +ding + + + + diff --git a/media/assets/webui/apps/inventory/inventory.js b/media/assets/webui/apps/inventory/inventory.js new file mode 100644 index 0000000000000000000000000000000000000000..7fe154953e5b94d0fbdba8ac990674847cc53b39 --- /dev/null +++ b/media/assets/webui/apps/inventory/inventory.js @@ -0,0 +1,5 @@ +elation.elements.define('janus.ui.inventory', class extends elation.elements.base { + create() { + this.innerHTML = elation.template.get('janus.ui.inventory'); + } +}); diff --git a/media/assets/webui/apps/inventory/inventory.json b/media/assets/webui/apps/inventory/inventory.json new file mode 100644 index 0000000000000000000000000000000000000000..651346bcc3dfea6fba8535ba2ec447904a2f41a1 --- /dev/null +++ b/media/assets/webui/apps/inventory/inventory.json @@ -0,0 +1,8 @@ +{ + "templates": { + "janus.ui.inventory": "./inventory.html", + "janus.ui.inventory.item.poly": "./inventory-item-poly.html" + }, + "scripts": ["./inventory.js"], + "css": ["./inventory.css"] +} diff --git a/media/assets/webui/apps/navigation/navigation.css b/media/assets/webui/apps/navigation/navigation.css new file mode 100644 index 0000000000000000000000000000000000000000..9ca6ac72614c7636552eb0ecad5616f50661f1bd --- /dev/null +++ b/media/assets/webui/apps/navigation/navigation.css @@ -0,0 +1,123 @@ +janus-ui-navigation { + top: .5em; + left: .5em; + position: relative; +} +janus-ui-statusindicator { + font-size: .7em; + width: 5em; + height: 5em; + border-radius: 50%; + border: 1px solid #999; + position: absolute; + top: -1px; + left: -1px; + background: white; + box-shadow: 0 0 5px rgba(0,0,0,.8); +} +janus-ui-statusindicator .inner { + width: 4em; + height: 4em; + border: 1px solid #999; + margin: .4em; + border-radius: 50%; + background: #768692; + position: relative; +} +janus-ui-statusindicator .inner:after { + display: block; + content: ' '; + background: url(https://janusvr.com/images/logos/logoonly.png); + background-size: 100%; + background-repeat: no-repeat; + background-position: center; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; +} +janus-ui-urlbar>ui-input>input { + margin-left: 1.5em; + margin-top: .1em; + padding-left: 1.5em; + border: 1px solid #999; + outline: none; + box-shadow: 0 0 2px rgba(0,0,0,.8); + font-size: 1.2em; + width: 20em; +} +@keyframes indicator-pulsate { + 0% { + transform: scale(1,1); + } + 50% { + transform: scale(1.2,1.2); + } + 100% { + transform: scale(1,1); + } +} +janus-ui-statusindicator.queued .inner { + background-color: blue; +} +janus-ui-statusindicator.downloading .inner { + background-color: green; +} +janus-ui-statusindicator.processing .inner { + background-color: yellow; +} +janus-ui-statusindicator.error .inner { + background-color: red; +} +janus-ui-statusindicator.queued .inner:after, +janus-ui-statusindicator.downloading .inner:after, +janus-ui-statusindicator.processing .inner:after { + animation: indicator-pulsate 2s linear infinite; +} +/* +janus-ui-statusindicator.processed .inner, +janus-ui-statusindicator.complete .inner { + background: + linear-gradient(36deg, #272b66 42.34%, transparent 42.34%) 0 0, + linear-gradient(72deg, #2d559f 75.48%, transparent 75.48%) 0 0, + linear-gradient(-36deg, #9ac147 42.34%, transparent 42.34%) 100% 0, + linear-gradient(-72deg, #639b47 75.48%, transparent 75.48%) 100% 0, + linear-gradient(36deg, transparent 57.66%, #e1e23b 57.66%) 100% 100%, + linear-gradient(72deg, transparent 24.52%, #f7941e 24.52%) 100% 100%, + linear-gradient(-36deg, transparent 57.66%, #662a6c 57.66%) 0 100%, + linear-gradient(-72deg, transparent 24.52%, #9a1d34 24.52%) 0 100%, + #43a1cd linear-gradient(#ba3e2e, #ba3e2e) 50% 100%; + background-repeat: no-repeat; + background-size: 50% 50%; +} +*/ +/* +janus-ui-statusindicator.complete .inner { + background-color: #768692; +} +*/ +janus-ui-statusindicator.complete .inner:after { + animation: none; +} +/* +janus-ui-statusindicator.processed .inner, +janus-ui-statusindicator.complete .inner { + background: + linear-gradient(0, yellow 100%, transparent 100%) 0 0, + linear-gradient(45deg, purple 75%, transparent 75%) 0 0, + linear-gradient(-36deg, #000000 42.34%, transparent 42.34%) 100% 0, + linear-gradient(-72deg, #000000 75.48%, transparent 75.48%) 100% 0, + linear-gradient(36deg, transparent 57.66%, #000000 57.66%) 100% 100%, + linear-gradient(72deg, transparent 24.52%, #000000 24.52%) 100% 100%, + linear-gradient(-36deg, transparent 57.66%, #000000 57.66%) 0 100%, + linear-gradient(-72deg, transparent 24.52%, #000000 24.52%) 0 100%, + #000000 linear-gradient(#000000, #000000) 50% 100%; + background-repeat: no-repeat; + background-size: 50% 50%; +} +*/ +janus-ui-statusindicator>ui-tooltip ul { + font-size: .8em; + margin-left: 1em; +} diff --git a/media/assets/webui/apps/navigation/navigation.html b/media/assets/webui/apps/navigation/navigation.html new file mode 100644 index 0000000000000000000000000000000000000000..3d5491c109f8e4fb1ce2a2f3e42e6fadd42a908f --- /dev/null +++ b/media/assets/webui/apps/navigation/navigation.html @@ -0,0 +1,4 @@ + + + + diff --git a/media/assets/webui/apps/navigation/navigation.js b/media/assets/webui/apps/navigation/navigation.js new file mode 100644 index 0000000000000000000000000000000000000000..00d9868331921efee3e8974f44cadf6b2d5d3802 --- /dev/null +++ b/media/assets/webui/apps/navigation/navigation.js @@ -0,0 +1,361 @@ +elation.elements.define('janus.ui.navigation', class extends elation.elements.ui.panel { + init() { + super.init(); + } + create() { + this.innerHTML = elation.template.get('janus.ui.navigation'); + this.client = this.getClient(); + this.janusweb = this.client.janusweb; + + this.urlbar = this.getElementsByTagName('janus-ui-urlbar')[0]; + this.statusindicator = this.getElementsByTagName('janus-ui-statusindicator')[0]; + this.notifications = this.getElementsByTagName('janus-ui-notifications')[0]; + + elation.events.add(this.janusweb, 'room_change', (ev) => this.updateCurrentURL()); + } + getClient() { + var node = this; + while (node) { + if (node.dataset['elationComponent'] == 'janusweb.client') { + return elation.component.fetch(node); + } + node = node.parentNode + } + } + updateCurrentURL() { + var room = this.janusweb.currentroom; + console.log('this is the room guy', room); + } +}); +elation.elements.define('janus.ui.statusindicator', class extends elation.elements.ui.indicator { + init() { + super.init(); + } + create() { + this.inner = elation.elements.create('div', { + class: 'inner', + append: this + }); + this.client = this.getClient(); + this.janusweb = this.client.janusweb; + this.updateCurrentURL(); + + this.tooltip = elation.elements.create('ui-tooltip', { + append: this, + }); + + this.canvas = document.createElement('canvas'); + this.inner.appendChild(this.canvas); + + elation.events.add(this.janusweb, 'room_load_start', (ev) => this.updateCurrentURL(ev.data)); + + this.percent = 0; + + this.refresh(); + + + elation.events.add(this, 'mouseover', (ev) => this.handleMouseOver(ev)); + elation.events.add([this, this.tooltip], 'mouseout', (ev) => this.handleMouseOut(ev)); + } + render() { + var canvas = this.canvas; + var ctx = canvas.getContext('2d'); + canvas.width = this.inner.offsetWidth; + canvas.height = this.inner.offsetHeight; + + ctx.beginPath(); + ctx.moveTo(canvas.width / 2, canvas.height / 2); + ctx.arc(canvas.width / 2, canvas.height / 2, canvas.width / 2, -Math.PI/2, (2 * Math.PI * this.percent - Math.PI/2)); + ctx.closePath(); + ctx.fillStyle = (this.currentstatus == 'complete' ? 'green' : 'darkgreen'); + ctx.fill(); + } + getClient() { + var node = this; + while (node) { + if (node.dataset['elationComponent'] == 'janusweb.client') { + return elation.component.fetch(node); + } + node = node.parentNode + } + } + updateCurrentURL(room) { + if (!room) { + room = this.janusweb.currentroom; + } + if (this.room !== room) { +console.log('room changed!', this.room, room); + + elation.events.add(room, 'room_load_queued', elation.bind(this, this.updateStatus, 'queued')); + elation.events.add(room, 'room_load_start', elation.bind(this, this.updateStatus, 'downloading')); + elation.events.add(room, 'room_load_processing', elation.bind(this, this.updateStatus, 'processing')); + elation.events.add(room, 'room_load_processed', elation.bind(this, this.updateRoomAssets)); + elation.events.add(room, 'room_load_complete', elation.bind(this, this.updateStatus, 'complete')); + elation.events.add(room, 'room_load_error', elation.bind(this, this.updateStatus, 'error')); + elation.events.add(room, 'room_load_progress', elation.bind(this, this.updateProgress)); + elation.events.add(room, 'room_add_asset', elation.bind(this, this.roomAddAsset)); + + this.room = room; + + if (room.loaded) { + this.updateRoomAssets(); + } + } + this.loading = {}; + this.percent = 0; + //this.progress.set(0); + //this.progress.show(); + } + updateStatus(status, ev) { + if (this.room.parseerror) { + // Force error status to remain if an error is thrown + status = 'error'; + } + + if (this.currentstatus && this.currentstatus != status) { + this.removeclass(this.currentstatus); + } + this.addclass(status); + this.currentstatus = status; + + if (status == 'complete') { + this.percent = 1; + this.refresh(); + } + } + updateRoomAssets(ev) { + this.updateStatus('processed'); + var room = this.janusweb.currentroom; + var assets = room.getActiveAssets(); + for (var type in assets) { + for (var id in assets[type]) { + var asset = assets[type][id]; + elation.events.add(asset, 'asset_load_progress', elation.bind(this, this.updateProgress)); + elation.events.add(asset, 'asset_load_complete', elation.bind(this, this.updateProgress)); + } + } + } + roomAddAsset(ev) { + var room = this.janusweb.currentroom; + var asset = ev.data; + + var url = asset.getProxiedURL(); + if (!this.loading[url]) { +//console.log('ADDED ASSET', asset.src, asset); + //this.loading[url] = { loaded: 0, total: 1024*1024 }; + elation.events.add(asset, 'asset_load_progress', elation.bind(this, this.updateProgress)); + elation.events.add(asset, 'asset_load_complete', elation.bind(this, this.updateProgress)); + } + } + updateProgress(ev) { + //console.log('room had some progress', this.loading); + var progress; + if (ev.data) { + progress = ev.data; + } else { + progress = { + src: ev.element.getProxiedURL(), + loaded: ev.element.size, + total: ev.element.size + }; + } + + var url = progress.src; + if (!this.loading[url]) { + this.loading[url] = progress; + } + this.loading[url].loaded = progress.loaded; + this.loading[url].total = progress.total; + + var loaded = 0, total = 0, count = 0; + for (var k in this.loading) { + count++; + loaded += this.loading[k].loaded; + total += this.loading[k].total; + } + var percent = loaded / total; + console.log('room had some progress', count + ' files, ' + loaded + ' / ' + total + ' bytes, ' + (percent * 100).toFixed(2) + '%', url); + + this.percent = percent; + + var segment = Math.floor(percent * 10); + if (this.segment != segment) { + this.removeclass('segment' + this.segment); + this.segment = segment; + this.addclass('segment' + this.segment); + } + + this.updateTooltip(); + this.refresh(); + + //this.progress.set(percent); + } + handleMouseOver(ev) { + this.updateTooltip(); + } + handleMouseOut(ev) { + this.tooltip.setcontent(''); + this.tooltip.hide(); + } + updateTooltip() { + var summary = this.getSummary(); + this.tooltip.setcontent(summary); + this.tooltip.show(); + } + getSummary() { + // TODO - use a template for this! + var summary = ''; + if (this.room) { + var assets = this.room.roomassets; + + if (this.room.parseerror) { + summary += '

' + this.room.parseerror + '

'; + } + + var modelstats = { + objects: 0, + faces: 0, + materials: 0 + }; + for (var k in assets) { + var arr = Object.entries(assets[k]); + var count = arr.length; + if (count > 0) { + var finished = 0, + finishedsize = 0, + totalsize = 0; + + for (var i = 0; i < count; i++) { + var assetname = arr[i][0], + asset = arr[i][1], + url = asset.getProxiedURL(asset.src); + //var size = arr.reduce((total, v) => [total[0] + parseInt(v[1].size), total[1] + (v[1].loaded ? 1 : 0)], [0, 0]); + if (asset.loaded) { + finished++; + + if (k == 'model') { + asset.stats(modelstats); + } + } + if (this.loading[url]) { + finishedsize += this.loading[url].loaded; + totalsize += asset.size || this.loading[url].total || this.loading[url].loaded; + } + } + summary += '

' + k + 's (' + finished + ' / ' + count + ')

'; + summary += '
  • ' + this.numberFormat(finishedsize) + ' / ' + this.numberFormat(totalsize, 'bytes') + '
  • '; + if (k == 'model') { + summary += '
  • ' + this.numberFormat(modelstats.objects, 'objects') + '
  • '; + summary += '
  • ' + this.numberFormat(modelstats.faces, 'faces') + '
  • '; + summary += '
  • ' + this.numberFormat(modelstats.materials, 'materials') + '
  • '; + } + summary += '
'; + } + } + } + return summary; + } + numberFormat(num, units) { + return num.toLocaleString() + (units ? ' ' + units : ''); + } +}); +elation.elements.define('janus.ui.urlbar', class extends elation.elements.ui.panel { + init() { + super.init(); + this.updateTitle = this.updateTitle.bind(this); + this.updateRoom = this.updateRoom.bind(this); + } + create() { + this.client = this.getClient(); + this.janusweb = this.client.janusweb; + + this.input = elation.elements.create('ui.input', { + append: this, + value: (this.janusweb.currentroom ? this.janusweb.currentroom.title : 'JanusWeb') + }); + elation.events.add(this.input, 'input', (ev) => this.handleInput(ev)); + elation.events.add(this.input, 'focus', (ev) => this.handleFocus(ev)); + elation.events.add(this.input, 'blur', (ev) => this.handleBlur(ev)); + this.suggestions = elation.elements.create('janus.ui.urlbar.suggestions', { + append: this + }); + this.suggestions.hide(); +/* + this.urlinput = elation.elements.create('ui.input', { + append: this, + value: (this.janusweb.currentroom ? this.janusweb.currentroom.url : document.location.href) + }); +*/ + elation.events.add(this.janusweb, 'room_change', this.updateRoom); + this.updateRoom(); + } + getClient() { + var node = this; + while (node) { + if (node.dataset['elationComponent'] == 'janusweb.client') { + return elation.component.fetch(node); + } + node = node.parentNode + } + } + updateRoom() { + elation.events.remove(this.room, 'room_load_processed', this.updateTitle); + this.room = this.janusweb.currentroom; + console.log('this is the room guy', this.room); + elation.events.add(this.room, 'room_load_processed', this.updateTitle); + this.updateTitle(); + } + updateTitle() { + var room = this.janusweb.currentroom; + if (room) { + this.input.value = room.title; + } + } + handleInput(ev) { + if (this.updatetimer) { + clearTimeout(this.updatetimer); + } + if (this.input.value.length > 0) { + this.updatetimer = setTimeout(() => { + this.suggestions.update(this.input.value); + }, 200); + this.suggestions.show(); + } else { + this.suggestions.hide(); + } + } + handleFocus(ev) { + if (this.input.value.length > 0) { + this.suggestions.show(); + } + } + handleBlur(ev) { + setTimeout(() => { + this.suggestions.hide(); + }, 100); + } +}); +elation.elements.define('janus.ui.urlbar.suggestions', class extends elation.elements.ui.panel { + create() { + this.popular = elation.elements.create('collection.jsonapi', { + host: "https://api.janusvr.com", + endpoint: "/getPopularRooms", + itempath: "data", + apiargs: { + desc: "true", + limit: 10, + urlContains: '' + } + }); + this.poplist = elation.elements.create('ui.list', { + collection: this.popular, + append: this, + itemtemplate: 'janus.ui.popular.room' + }); + } + update(search) { + this.popular.apiargs.urlContains = search; + this.popular.load(); +console.log(this.popular.items); + } +}) diff --git a/media/assets/webui/apps/navigation/navigation.json b/media/assets/webui/apps/navigation/navigation.json new file mode 100644 index 0000000000000000000000000000000000000000..c66a56993753f47bf475a2738f6b7448512a3018 --- /dev/null +++ b/media/assets/webui/apps/navigation/navigation.json @@ -0,0 +1,8 @@ +{ + "templates": { + "janus.ui.navigation": "./navigation.html" + }, + "scripts": ["./navigation.js"], + "css": ["./navigation.css"] +} + diff --git a/media/assets/webui/apps/notifications/notifications.json b/media/assets/webui/apps/notifications/notifications.json new file mode 100644 index 0000000000000000000000000000000000000000..0967ef424bce6791893e9a57bb952f80fd536e93 --- /dev/null +++ b/media/assets/webui/apps/notifications/notifications.json @@ -0,0 +1 @@ +{} diff --git a/media/assets/webui/apps/partymode/partymode.html b/media/assets/webui/apps/partymode/partymode.html new file mode 100644 index 0000000000000000000000000000000000000000..f05804e457a65a2c5c8c4e4f3c6818664cc2c55d --- /dev/null +++ b/media/assets/webui/apps/partymode/partymode.html @@ -0,0 +1,9 @@ + + + + + + diff --git a/media/assets/webui/apps/partymode/partymode.js b/media/assets/webui/apps/partymode/partymode.js new file mode 100644 index 0000000000000000000000000000000000000000..1ef21ad968805673d5c4a2259d8a68756c26f1fc --- /dev/null +++ b/media/assets/webui/apps/partymode/partymode.js @@ -0,0 +1,32 @@ +elation.elements.define('janus.ui.partymode', class extends elation.elements.base { + create() { + this.innerHTML = elation.template.get('janus.ui.partymode'); + + this.collection = this.getElementsByTagName('collection-jsonapi')[0]; + + this.list = this.getElementsByTagName('ui-list'); + elation.events.add(this.list, 'select', (ev) => this.handleSelect(ev)); + } + handleSelect(ev) { + this.dispatchEvent({type: 'select', data: ev.data}); + } + updateData() { + // If offsetParent is null, it means one of our parents is set to display: none, and we should do nothing + if (this.offsetParent) { + this.collection.load(); + } + } + show() { + super.show(); + if (this.updatetimer) clearInterval(this.updatetimer); + this.updatetimer = setInterval(() => this.updateData(), 10000); + } + hide() { + super.hide(); + if (this.updatetimer) { + clearInterval(this.updatetimer); + this.updatetimer = false; + } + } +}); + diff --git a/media/assets/webui/apps/partymode/partymode.json b/media/assets/webui/apps/partymode/partymode.json new file mode 100644 index 0000000000000000000000000000000000000000..d2c1c295544742e9984116accb040b31d9eb89c2 --- /dev/null +++ b/media/assets/webui/apps/partymode/partymode.json @@ -0,0 +1,13 @@ +{ + "templates": { + "janus.ui.partymode": "./partymode.html", + "janus.ui.partymode.user": "./user.html" + }, + "css": [ + "./partymode.css" + ], + "scripts": [ + "./partymode.js" + ] +} + diff --git a/media/assets/webui/apps/partymode/user.html b/media/assets/webui/apps/partymode/user.html new file mode 100644 index 0000000000000000000000000000000000000000..8b1f558504e16818da573e5f586e33de0ee39288 --- /dev/null +++ b/media/assets/webui/apps/partymode/user.html @@ -0,0 +1,6 @@ + +

{userId}

+
{name}
+
{url}
+
+ diff --git a/media/assets/webui/apps/popular/nopreview.png b/media/assets/webui/apps/popular/nopreview.png new file mode 100644 index 0000000000000000000000000000000000000000..439139a5d5e82d8668ddb49be3b25f717013b2e6 Binary files /dev/null and b/media/assets/webui/apps/popular/nopreview.png differ diff --git a/media/assets/webui/apps/popular/popular.css b/media/assets/webui/apps/popular/popular.css new file mode 100644 index 0000000000000000000000000000000000000000..b0d25c14833b91f266c5faae0535e4ec12fc4e92 --- /dev/null +++ b/media/assets/webui/apps/popular/popular.css @@ -0,0 +1,40 @@ +.popularrooms>ui-item { + width: 200px; + height: 120px; + position: relative; + background-image: url(nopreview.png); + background-repeat: no-repeat; + background-size: contain; + overflow: hidden; + padding: 0; + transition: transform 100ms ease-out; + z-index: 1; +} +.popularrooms>ui-item:hover { + transform: scale(1.1); + z-index: 2; +} +.popularroom_thumb { + height: 130px; + width: 100%; + object-fit: cover; +} +.popularroom { + display: flex; + flex-direction: column; + height: 100%; +} + +.popularroom h4 { + position: absolute; + bottom: 0; + left: 0; + right: 0; + background: rgba(0,0,0,.5); + color: white; + text-shadow: 0 0 2px black; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + padding: 0 .2em; +} diff --git a/media/assets/webui/apps/popular/popular.html b/media/assets/webui/apps/popular/popular.html new file mode 100644 index 0000000000000000000000000000000000000000..a0665d2abbe311fa36efff15a4479b5f9a89c468 --- /dev/null +++ b/media/assets/webui/apps/popular/popular.html @@ -0,0 +1,10 @@ + + + + + diff --git a/media/assets/webui/apps/popular/popular.js b/media/assets/webui/apps/popular/popular.js new file mode 100644 index 0000000000000000000000000000000000000000..8783af5f334ee171158ce2476735806a2a851c18 --- /dev/null +++ b/media/assets/webui/apps/popular/popular.js @@ -0,0 +1,12 @@ +elation.elements.define('janus.ui.popular', class extends elation.elements.base { + create() { + this.innerHTML = elation.template.get('janus.ui.popular'); + + this.grid = this.getElementsByTagName('ui-grid'); + elation.events.add(this.grid, 'select', (ev) => this.handleSelect(ev)); + } + handleSelect(ev) { + this.dispatchEvent({type: 'select', data: ev.data}); + } +}); + diff --git a/media/assets/webui/apps/popular/popular.json b/media/assets/webui/apps/popular/popular.json new file mode 100644 index 0000000000000000000000000000000000000000..1503a40b23074875d9349e22e18272eb831cd1b5 --- /dev/null +++ b/media/assets/webui/apps/popular/popular.json @@ -0,0 +1,13 @@ +{ + "templates": { + "janus.ui.popular": "./popular.html", + "janus.ui.popular.room": "./room.html" + }, + "css": [ + "./popular.css" + ], + "scripts": [ + "./popular.js" + ] +} + diff --git a/media/assets/webui/apps/popular/room.html b/media/assets/webui/apps/popular/room.html new file mode 100644 index 0000000000000000000000000000000000000000..12dbdc37d7df269f5b9a239be0b32acb37f952f5 --- /dev/null +++ b/media/assets/webui/apps/popular/room.html @@ -0,0 +1,4 @@ + + {?thumbnail}{/thumbnail} +

{?roomName}{roomName}{:else}{roomUrl}{/roomName}

+
diff --git a/media/assets/webui/apps/settings/settings.json b/media/assets/webui/apps/settings/settings.json new file mode 100644 index 0000000000000000000000000000000000000000..0967ef424bce6791893e9a57bb952f80fd536e93 --- /dev/null +++ b/media/assets/webui/apps/settings/settings.json @@ -0,0 +1 @@ +{} diff --git a/media/assets/webui/apps/splash/splash.css b/media/assets/webui/apps/splash/splash.css new file mode 100644 index 0000000000000000000000000000000000000000..882000a8b32b91fb2f2537459db3211263f72b86 --- /dev/null +++ b/media/assets/webui/apps/splash/splash.css @@ -0,0 +1,39 @@ +janus-ui-splash { + position: absolute; + top: 50%; + left: 50%; + width: 98%; + max-width: 70em; + background: rgba(0,0,0,.5); + border: 1px solid red; + border-radius: 5px; + z-index: 500; + display: flex; + flex-direction: column; + transform: translate(-50%, -50%); +} + +janus-ui-splash janus-ui-navigation { + font-size: 2em; +} +janus-ui-splash ui-tabs { + clear: left; +} +janus-ui-splash ui-tabs>ui-tabbar>ui-button { + font-size: 2em; + font-family: 'Montserrat',sans-serif +} + +janus-ui-splash h1 { + margin-top: 1em; + font-size: 3em; + font-family: 'Montserrat',sans-serif; +} +janus-ui-splash .logo { + width: 12em; + height: 12em; + float: left; +} +janus-ui-splash janus-ui-popular ui-grid { + height: 28em; +} diff --git a/media/assets/webui/apps/splash/splash.html b/media/assets/webui/apps/splash/splash.html new file mode 100644 index 0000000000000000000000000000000000000000..4757dfc13749c636a004524ca00c687eed8dc12c --- /dev/null +++ b/media/assets/webui/apps/splash/splash.html @@ -0,0 +1,22 @@ +
+ +

JanusWeb

+

+
+ + + + + + + + + + + + + +
+ Dismiss +
+ diff --git a/media/assets/webui/apps/splash/splash.js b/media/assets/webui/apps/splash/splash.js new file mode 100644 index 0000000000000000000000000000000000000000..1ae19774a88e9b4d4550cd0c1ebb228f78868cb5 --- /dev/null +++ b/media/assets/webui/apps/splash/splash.js @@ -0,0 +1,45 @@ +elation.elements.define('janus.ui.splash', class extends elation.elements.base { + create() { + this.innerHTML = elation.template.get('janus.ui.splash'); + + this.popular = this.getElementsByTagName('janus-ui-popular')[0]; + this.partymode = this.getElementsByTagName('janus-ui-partymode')[0]; + this.bookmarks = this.getElementsByTagName('janus-ui-bookmarks')[0]; + + elation.events.add([this.popular, this.partymode, this.bookmarks], 'select', (ev) => this.handleSelect(ev)); + + elation.events.add(document, 'keydown', (ev) => this.handleKeyDown(ev)); + elation.events.add(document, 'pointerlockchange', (ev) => this.handlePointerlockChange(ev)); + + this.show(); + } + handleSelect(ev) { + this.hide(); + } + show() { + super.show(); + this.partymode.show(); + } + hide() { + super.hide(); + this.partymode.hide(); + } + handleKeyDown(ev) { + if (ev.key == 'Escape') { + if (this.hidden) { + this.show(); + } else { + this.hide(); + } + } + } + handlePointerlockChange(ev) { + console.log('pointer lock', document.pointerLockElement, ev); + if (document.pointerLockElement) { + this.hide(); + } else { + this.show(); + } + } +}); + diff --git a/media/assets/webui/apps/splash/splash.json b/media/assets/webui/apps/splash/splash.json new file mode 100644 index 0000000000000000000000000000000000000000..de74ba498d348d2d85c8752572d378f48d870b40 --- /dev/null +++ b/media/assets/webui/apps/splash/splash.json @@ -0,0 +1,12 @@ +{ + "templates": { + "janus.ui.splash": "./splash.html" + }, + "css": [ + "./splash.css" + ], + "scripts": [ + "./splash.js" + ] +} + diff --git a/media/assets/webui/apps/virtualgamepad/virtualgamepad.css b/media/assets/webui/apps/virtualgamepad/virtualgamepad.css new file mode 100644 index 0000000000000000000000000000000000000000..9438df43c51232aaa176d653e10127e8a8d5afdb --- /dev/null +++ b/media/assets/webui/apps/virtualgamepad/virtualgamepad.css @@ -0,0 +1,76 @@ +janus-controls-analog { + display: block; + position: relative; + border: 1px solid black; + width: 100px; + height: 100px; + border-radius: 50px; + margin-left: 10px; + margin-bottom: 10px; + cursor: -webkit-grab; + cursor: grab; + z-index: 10; + transform: translate(-50%, -50%); +} +janus-controls-analog[top][left] { + transform: translate(0,0); +} +janus-controls-analog[top][right] { + transform: translate(-100%,0); +} +janus-controls-analog[bottom][left] { + transform: translate(0,-100%); +} +janus-controls-analog[bottom][right] { + transform: translate(-100%,-100%); +} +janus-controls-analogstick { + position: absolute; + display: block; + width: 20px; + height: 20px; + border-radius: 10px; + border: 1px solid red; +} +janus-controls-button { + display: block; + text-align: center; + vertical-align: middle; + border: 1px solid black; + width: 80px; + height: 80px; + line-height: 80px; + border-radius: 40px; + background: #900; + transition: transform linear 40ms; + cursor: pointer; + transform: translate(-50%, -50%); +} +janus-controls-button[top][left] { + transform: translate(0,0); +} +janus-controls-button[top][right] { + transform: translate(-100%,0); +} +janus-controls-button[bottom][left] { + transform: translate(0,-100%); +} +janus-controls-button[bottom][right] { + transform: translate(-100%,-100%); +} +janus-controls-button[pressed] { + background: #f00; + transform: translate(-50%, -50%) scale(.9); +} +janus-controls-button[top][left][pressed] { + transform: translate(0,0) scale(.9); +} +janus-controls-button[top][right][pressed] { + transform: translate(-100%,0) scale(.9); +} +janus-controls-button[bottom][left][pressed] { + transform: translate(0,-100%) scale(.9); +} +janus-controls-button[bottom][right][pressed] { + transform: translate(-100%,-100%) scale(.9); +} diff --git a/media/assets/webui/apps/virtualgamepad/virtualgamepad.js b/media/assets/webui/apps/virtualgamepad/virtualgamepad.js new file mode 100644 index 0000000000000000000000000000000000000000..74b81812b68fdad6e3846b3ab20b2bcddb1569b0 --- /dev/null +++ b/media/assets/webui/apps/virtualgamepad/virtualgamepad.js @@ -0,0 +1,291 @@ +elation.elements.define('janus.controls.gamepad', class extends elation.elements.base { + init() { + super.init(); + this.defineAttributes({ + id: { type: 'string', default: 'Virtual Gamepad' }, + connected: { type: 'boolean', default: true }, + numaxes: { type: 'integer', default: 2 }, + numbuttons: { type: 'integer', default: 2 }, + }); + + this.cfg = { + sticks: [ + { + axes: [0, 1], + style: 'analog', + position: [ 0, 1 ], + bottom: 1, + left: 1 + }, + { + axes: [2, 3], + style: 'analog', + position: [ 1, 1 ], + bottom: 1, + right: 1 + } + ], + buttons: [ + { + label: 'A', + position: [ .95, .99 ], + axis: 4, + right: 1, + bottom: 1 + }, + { + label: 'B', + position: [ .975, .91 ], + axis: 5, + right: 1, + bottom: 1 + } + ] + }; + + this.timestamp = performance.now(); + } + create() { + this.axes = []; + this.sticks = []; + this.buttons = []; + + for (var i = 0; i < this.cfg.sticks.length; i++) { + let stickcfg = this.cfg.sticks[i]; + let style = stickcfg.style || 'analog' + let stick = elation.elements.create('janus.controls.' + style, { + append: this, + axes: stickcfg.axes, + position: stickcfg.position, + top: (stickcfg.top || false), + left: (stickcfg.left || false), + bottom: (stickcfg.bottom || false), + right: (stickcfg.right || false), + }); + elation.events.add(stick, 'axis_change', (ev) => { this.axes[ev.data.axis] = ev.data.value; }); + this.sticks[i] = stick; + setTimeout(() => { stick.resetStickPosition(); }, 10); + } + for (var i = 0; i < this.cfg.buttons.length; i++) { + let buttoncfg = this.cfg.buttons[i]; + let button = elation.elements.create('janus.controls.button', { + append: this, + label: buttoncfg.label, + position: buttoncfg.position, + top: (buttoncfg.top || false), + left: (buttoncfg.left || false), + bottom: (buttoncfg.bottom || false), + right: (buttoncfg.right || false), + }); + this.buttons[i] = button; + } + + if (this.position) { + this.style.position = 'fixed'; + this.style.left = (this.position[0] * 100) + '%'; + this.style.top = (this.position[1] * 100) + '%'; + } + + let janus = elation.component.fetch(this.queryParentSelector('[data-elation-component="janusweb.client"]')) + janus.engine.systems.controls.addVirtualGamepad(this); + } +}); +elation.elements.define('janus.controls.button', class extends elation.elements.base { + init(axes, buttons) { + super.init(); + this.defineAttributes({ + label: { type: 'string' }, + pressed: { type: 'boolean', default: false }, + touched: { type: 'boolean', default: false }, + value: { type: 'float', default: 0.0 }, + position: { type: 'array' }, + }); + } + create() { + if (this.label) { + this.innerHTML = this.label; + } + elation.events.add(this, 'mousedown', (ev) => this.handleMouseDown(ev)); + elation.events.add(this, 'mouseup', (ev) => this.handleMouseUp(ev)); + elation.events.add(this, 'touchstart', (ev) => this.handleTouchStart(ev)); + elation.events.add(this, 'touchend', (ev) => this.handleTouchEnd(ev)); + + if (this.position) { + this.style.position = 'fixed'; + this.style.left = (this.position[0] * 100) + '%'; + this.style.top = (this.position[1] * 100) + '%'; + } + } + touch() { + this.touched = true; + } + untouch() { + this.touched = false; + } + press() { + this.pressed = true; + } + unpress() { + this.pressed = false; + } + handleMouseDown(ev) { + this.touch(); + this.press(); + ev.preventDefault(); + ev.stopPropagation(); + } + handleMouseUp(ev) { + this.unpress(); + this.untouch(); + } + handleTouchStart(ev) { + this.touch(); + this.press(); + ev.preventDefault(); + ev.stopPropagation(); + } + handleTouchEnd(ev) { + this.unpress(); + this.untouch(); + } +}); +elation.elements.define('janus.controls.analog', class extends elation.elements.base { + init() { + super.init(); + this.defineAttributes({ + x: { type: 'float', default: 0 }, + y: { type: 'float', default: 0 }, + axes: { type: 'array', default: [0, 1] }, + position: { type: 'array' } + }); + } + create() { + this.stick = elation.elements.create('janus.controls.analogstick', { + append: this + }); + this.handleTouchStart = elation.bind(this, this.handleTouchStart); + this.handleTouchMove = elation.bind(this, this.handleTouchMove); + this.handleTouchEnd = elation.bind(this, this.handleTouchEnd); + this.handleMouseDown = elation.bind(this, this.handleMouseDown); + this.handleMouseMove = elation.bind(this, this.handleMouseMove); + this.handleMouseUp = elation.bind(this, this.handleMouseUp); + + elation.events.add(this, 'touchstart', this.handleTouchStart); + elation.events.add(this, 'mousedown', this.handleMouseDown); + + this.stick.setPosition(0, 0); + + if (this.position) { + this.style.position = 'fixed'; + this.style.left = (this.position[0] * 100) + '%'; + this.style.top = (this.position[1] * 100) + '%'; + } + } + update() { + } + handleMouseDown(ev) { + elation.events.add(window, 'mousemove', this.handleMouseMove); + elation.events.add(window, 'mouseup', this.handleMouseUp); + ev.preventDefault(); + } + handleMouseMove(ev) { + this.setStickPosition(ev.clientX, ev.clientY); + } + handleMouseUp(ev) { + this.resetStickPosition(); + elation.events.remove(window, 'mousemove', this.handleMouseMove); + elation.events.remove(window, 'mouseup', this.handleMouseUp); + } + handleTouchStart(ev) { + this.currenttouch = ev.changedTouches[0].identifier; + elation.events.add(window, 'touchmove', this.handleTouchMove); + elation.events.add(window, 'touchend', this.handleTouchEnd); + console.log('boop', ev); + ev.preventDefault(); + } + handleTouchMove(ev) { + for (var i = 0; i < ev.changedTouches.length; i++) { + if (ev.changedTouches[i].identifier === this.currenttouch) { + var x = ev.changedTouches[i].clientX; + var y = ev.changedTouches[i].clientY; + this.setStickPosition(x, y); + } + } + } + handleTouchEnd(ev) { + for (var i = 0; i < ev.changedTouches.length; i++) { + if (ev.changedTouches[i].identifier === this.currenttouch) { + this.resetStickPosition(); + elation.events.remove(window, 'touchmove', this.handleTouchMove); + elation.events.remove(window, 'touchend', this.handleTouchEnd); + this.currenttouch = null; + } + } + } + setStickPosition(x, y) { + var rect = this.getBoundingClientRect(); + + //console.log('blep', x, y); + + var halfwidth = rect.width / 2, + halfheight = rect.height / 2; + + var mx = rect.x + halfwidth, + my = rect.y + halfheight; + + var dx = (x - mx) / halfwidth, + dy = (y - my) / halfheight; + + var angle = Math.atan2(dy, dx); + var len = Math.min(1, Math.sqrt(dx * dx + dy * dy)); +console.log(angle, len); + + var lastx = this.x, + lasty = this.y; + this.x = Math.max(-1, Math.min(1, (x - (rect.x + halfwidth)) / halfwidth)); + this.y = Math.max(-1, Math.min(1, (y - (rect.y + halfheight)) / halfheight)); + + if (lastx != this.x) { + this.dispatchEvent({type: 'axis_change', data: { axis: this.axes[0], value: this.x } }); + } + if (lasty != this.y) { + this.dispatchEvent({type: 'axis_change', data: { axis: this.axes[1], value: this.y } }); + } + this.stick.setAngleValue(angle, len); + } + resetStickPosition() { + this.x = 0; + this.y = 0; + this.dispatchEvent({type: 'axis_change', data: { axis: this.axes[0], value: this.x } }); + this.dispatchEvent({type: 'axis_change', data: { axis: this.axes[1], value: this.y } }); + this.stick.setPosition(0, 0); + } +}); +elation.elements.define('janus.controls.analogstick', class extends elation.elements.base { + create() { + } + setPosition(x, y) { + var len = Math.sqrt(x * x + y * y); + if (len > 1) { + x /= len; + y /= len; + } + var px = (((x + 1) / 2) * this.parentNode.offsetWidth) - (this.offsetWidth / 2), + py = (((y + 1) / 2) * this.parentNode.offsetHeight) - (this.offsetHeight / 2); + + this.style.transform = 'translate(' + px + 'px, ' + py + 'px)'; +//console.log(px, py, this.style.transform); + } + setAngleValue(angle, value) { + var x = Math.cos(angle) * value, + y = Math.sin(angle) * value; + + this.setPosition(x, y); + } +}); +elation.elements.define('janus.controls.dpad', class extends elation.elements.base { +}); +/* +elation.elements.define('janus.controls.button', class extends elation.elements.base { +}); +*/ diff --git a/media/assets/webui/apps/virtualgamepad/virtualgamepad.json b/media/assets/webui/apps/virtualgamepad/virtualgamepad.json new file mode 100644 index 0000000000000000000000000000000000000000..b204b2571ce9b2b390963fc9b0f6cabcfbc74c1c --- /dev/null +++ b/media/assets/webui/apps/virtualgamepad/virtualgamepad.json @@ -0,0 +1,8 @@ +{ + "templates": { + }, + "scripts": ["./virtualgamepad.js"], + "css": ["./virtualgamepad.css"] +} + + diff --git a/media/assets/webui/default.html b/media/assets/webui/default.html new file mode 100644 index 0000000000000000000000000000000000000000..c502421fdd5b8029688c69bec088cb7c69962344 --- /dev/null +++ b/media/assets/webui/default.html @@ -0,0 +1,23 @@ + + + + + + + + + + + + Share + Expand + WebVR + Config + + + + + + + + diff --git a/media/assets/webui/default.json b/media/assets/webui/default.json new file mode 100644 index 0000000000000000000000000000000000000000..e30134fc217a84586c930a56990daf7a2363d0e0 --- /dev/null +++ b/media/assets/webui/default.json @@ -0,0 +1,25 @@ +{ + "includes": [ + "./apps/splash/splash.json", + "./apps/popular/popular.json", + "./apps/partymode/partymode.json", + "./apps/bookmarks/bookmarks.json", + "./apps/navigation/navigation.json", + "./apps/appdrawer/appdrawer.json", + "./apps/virtualgamepad/virtualgamepad.json", + "./apps/buttons/buttons.json", + "./apps/chat/chat.json", + "./apps/inventory/inventory.json", + "./apps/avatar/avatar.json", + "./apps/notifications/notifications.json", + "./apps/settings/settings.json" + ], + "templates": { + "janusweb.ui": "./default.html" + }, + "css": [ + "./themes/default.css" + ], + "scripts": [ + ] +} diff --git a/media/assets/webui/fonts/Montserrat-Regular.ttf b/media/assets/webui/fonts/Montserrat-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..5b4b5afe6ee4b560b65b2f2040ad38f6c094b347 Binary files /dev/null and b/media/assets/webui/fonts/Montserrat-Regular.ttf differ diff --git a/media/assets/webui/themes/default.css b/media/assets/webui/themes/default.css new file mode 100644 index 0000000000000000000000000000000000000000..e3cd36b7a21545bbe59f34f6ab5c12da4c818a1a --- /dev/null +++ b/media/assets/webui/themes/default.css @@ -0,0 +1,722 @@ +@font-face { + font-family: "Montserrat"; + src: url('../fonts/Montserrat-Regular.ttf'); +} + + +html { + margin: 0; + padding: 0; + height: 100%; +} +body { + background: #333; + color: #eee; + font-family: "Montserrat", sans-serif; + height: 100%; + margin: 0; + padding: 0; +} +h2 { + border: 1px solid black; + background: #4cb96f; + padding: 0 .2em; + box-shadow: 0px 0px 5px rgba(0,0,0,.8); + color: black; +} + +.dark *.error { + color: red; +} + +/************** + * Containers * + **************/ + +/* */ +ui-list, +ui-checklist { + display: block; + list-style: none; + position: relative; + overflow: auto; + border-radius: .5em; + border: 1px solid #111; + background: rgba(0,0,0,.1); + margin: 0; + padding: 0; +} +ui-list>ui-item, +ui-checklist>ui-checklistitem { + display: block; + transform: translate3d(0, 0, 0); + position: relative; + user-select: none; + border: 1px solid transparent; + padding: .2em .5em; + border-bottom: 1px solid rgba(0,0,0,.2); +} +ui-list>ui-item:first-of-type +ui-checklist>ui-checklistitem:first-of-type { +} +ui-list>ui-item:last-of-type, +ui-checklist>ui-checklistitem:last-of-type { + border-bottom: 0; +} +ui-list>ui-item[hover], +ui-checklist>ui-checklistitem[hover] { + background-color: rgba(128,128,255,.2); +} +ui-list>ui-item.state_selected, +ui-checklist>ui-checklistitem.state_selected { + background: #eef; +} + +/* */ +ui-grid { + margin: 0; + padding: 0; + display: block; +} +ui-grid[scrollable] { + overflow-x: auto; + overflow-y: auto; +} +ui-grid[scrollable-x] { + overflow-x: auto; + overflow-y: hidden; +} +ui-grid[scrollable-y] { + overflow-x: hidden; + overflow-y: auto; +} +ui-grid>ui-item { + display: inline-block; + width: 10em; + height: 6em; + border-radius: .5em; + border: 1px solid #111; + background: rgba(0,0,0,.1); + margin: .2em; + padding: .2em; +} +ui-grid>ui-item[hover] { + background-color: rgba(128,128,255,.2); +} + +/* */ +ui-tabs { + display: block; + position: relative; +} +/* */ +ui-tab { + display: block +} +ui-tabs>ui-tab { + display: none; + position: relative; + border: 1px solid black; + border-top: 0; + padding: .5em; + background: #3a3f44; +} +ui-tabs>ui-tabbar { + position: relative; + display: block; + color: white; + border-bottom: 1px solid #1c1e22; + font-size: .8em; + z-index: -1px; +} +ui-tabs>ui-tabbar>ui-button { + min-width: 5em; + text-align: center; + padding: .25em .4em; + border: 1px solid #1c1e22; + border-bottom: 1px solid #1c1e22; + border-radius: .5em .5em 0 0; + margin-bottom: 0; +} +ui-tabs>ui-tabbar>ui-button[selected], +ui-tabs>ui-tabbar>ui-button[selected][hover] { + background-image: linear-gradient(rgba(128,255,128,.25), rgba(128,255,128,.28) 60%, rgba(128,255,128,.3)); + background-repeat: no-repeat; + + border: 1px solid #1c1e22; + border-bottom-color: transparent; +} +ui-tabs>ui-tabbar>ui-button[hover] { + background-image: linear-gradient(rgba(128,128,128,.25), rgba(128,128,128,.28) 60%, rgba(128,128,128,.3)); + background-repeat: no-repeat; + border-color: #1c1e22 #1c1e22 transparent #1c1e22; +} +ui-tabs>ui-tabbar>ui-button[disabled] { + color: #777; +} +ui-tabs>ui-tab[selected] { + display: block; +} +/* */ +ui-panel { + position: absolute; + z-index: 5; +} +ui-panel[top] { + top: 0; +} +ui-panel[bottom] { + bottom: 0; +} +ui-panel[left] { + left: 0; +} +ui-panel[right] { + right: 0; +} + +ui-flexpanel { + display: flex; + width: calc(100% - 1em); + height: calc(100% - 1em); + padding: .5em; +} +ui-flexpanel[vertical] { + flex-direction: column; +} +ui-flexpanel>* { + flex: 1 1; +} +ui-flexpanel>*[noflex] { + flex: 0 1; +} + +/* */ +ui-window { + position: absolute; + top: 0; + left: 0; + vertical-align: top; + z-index: 5; + border: 1px solid black; + background-color: rgba(0,0,0,.5); + border-radius: .5em; + border: 1px solid #666; + box-shadow: 1px 1px 10px rgba(0,0,0,1); + color: white; +} +ui-window>ui-window-titlebar { + display: flex; + position: relative; + cursor: default; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + margin: 0; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + vertical-align: middle; + border-bottom: 1px solid #666; +} +ui-window.state_movable>ui-window-titlebar:hover { + cursor: -webkit-grab; + cursor: -moz-grab; +} +ui-window.state_movable>ui-window-titlebar.state_dragging { + cursor: -webkit-grabbing; + cursor: -moz-grabbing; +} +ui-window>ui-window-titlebar>span { + flex: 1; + order: 1; + padding: 0 0 0 .5em; + font-size: 1em; + line-height: 1.8em; +} +ui-window>ui-window-titlebar>ui-buttonbar { + flex: 0; + order: 2; +} +ui-window>ui-window-titlebar>ui-buttonbar>ui-button { + background: none; + border: none; +} +ui-window>.ui_window_resizer { + position: absolute; + bottom: -2px; + right: -2px; + width: 16px; + height: 16px; + cursor: se-resize; + border-bottom: 2px solid #ccc; + border-right: 2px solid #ccc; + border-radius: 0 0 5px 0; + z-index: 10; +} +ui-window>.ui_window_content { + overflow: auto; + min-width: 100%; + padding: .5em; + box-sizing: border-box; + -moz-box-sizing: border-box; + position: relative; +} +ui-window[maximized] { + z-index: 1000; + border-width: 0; + border-radius: 0; +} +ui-window[maximized]>.ui_window_content { + max-width: none; + max-height: none; + padding: 0; +} +ui-window>.ui_window_resizer, +ui-window[left]>.ui_window_resizer { + right: -2px; + left: auto; + border-left: 0; + border-right: 2px solid #ccc; +} +ui-window[right]>.ui_window_resizer { + right: auto; + left: -2px; + border-right: 0; + border-left: 2px solid #ccc; +} +ui-window[top]>.ui_window_resizer { + bottom: -2px; + top: auto; + border-top: 0; + border-bottom: 2px solid #ccc; +} +ui-window[bottom]>.ui_window_resizer { + top: -2px; + bottom: auto; + border-bottom: 0; + border-top: 2px solid #ccc; +} +ui-window[top][right]>.ui_window_resizer { + border-radius: 0 0 0 5px; + cursor: sw-resize; +} +ui-window[top][left]>.ui_window_resizer { + border-radius: 0 0 5px 0; + cursor: se-resize; +} +ui-window[bottom][right]>.ui_window_resizer { + border-radius: 5px 0 0 0; + cursor: se-resize; +} +ui-window[bottom][left]>.ui_window_resizer { + border-radius: 0 5px 0 0; + cursor: sw-resize; +} + +ui-tooltip { + position: absolute; + top: 0; + left: 0; + vertical-align: top; + z-index: 5; + border: 1px solid black; + background-color: rgba(0,0,0,.5); + border-radius: .5em; + border: 1px solid #666; + box-shadow: 1px 1px 10px rgba(0,0,0,1); + color: white; + pointer-events: none; + white-space: nowrap; + padding: .2em .4em; +} + +/***************** + * Form Elements * + *****************/ + +/* */ +ui-input { +} +ui-input>input { +} +ui-input>input[hover] { +} +ui-input>input:focus { +} +ui-input>input:active { +} +ui-input>input:disabled { +} + +/* */ +ui-textarea { +} +ui-textarea>textarea { + resize: vertical; +} +ui-textarea>textarea[hover] { +} +ui-textarea>textarea:focus { +} +ui-textarea>textarea:active { +} +ui-textarea>textarea:disabled { +} + +/* */ +ui-dropdown { +} +ui-dropdown>ui-list { +} + +/* */ +ui-slider { +} +ui-slider[hover] { +} +ui-slider:active { +} +ui-slider-track { +} +ui-slider-track[hover] { +} +ui-slider-track:active { +} +ui-slider-handle { +} +ui-slider-handle[hover] { +} +ui-slider-handle:active { +} +ui-slider:disabled { +} +ui-slider:disabled ui-slider-track { +} +ui-slider:disabled ui-slider-handle { +} + +/* */ +ui-toggle>div { + border: 1px solid black; + border-radius: .5em; + padding: .5em; + position: relative; + user-select: none; + -ms-user-select: none; + cursor: pointer; + z-index: 2; + display: inline-block; + width: 2em; + vertical-align: middle; + margin: .2em .8em .2em .4em; +} +ui-toggle input { + display: none; +} +ui-toggle span { + cursor: pointer; +} +ui-toggle>div::before { + display: block; + position: absolute; + top: 0; + right: 0; + bottom: 0; + width: 50%; + background: #700; + content: ' '; + z-index: -1; + border-radius: 0 .5em .5em 0; + box-shadow: 2px 0 5px rgba(0,0,0,.8); +} +ui-toggle>div::after { + display: block; + position: absolute; + top: 0; + left: 0; + bottom: 0; + width: 50%; + content: ' '; + z-index: -1; + border-radius: .5em 0 0 .5em; + box-shadow: 0px 0px 5px rgba(0,0,0,.8); +} +ui-toggle[checked]>div::before { + right: auto; + left: 0; + background: green; + box-shadow: -2px 0 5px rgba(0,0,0,.8); + border-radius: .5em 0 0 .5em; +} +ui-toggle[checked]>div::after { + right: 0; + left: auto; + border-radius: 0 .5em .5em 0; +} +ui-toggle[disabled]>div { + color: #999; + cursor: not-allowed; +} +ui-toggle[disabled] span { + cursor: not-allowed; +} +ui-toggle[disabled]>div::before { + cursor: not-allowed; + background: #666; +} + +/* */ +ui-checkbox { + display: inline-block; + white-space: nowrap; + min-width: 6em; + cursor: pointer; + user-select: none; + -ms-user-select: none; + -moz-user-select: none; +} +ui-checkbox>input { + -webkit-appearance: none; + background-color: #fafafa; + border: 1px solid #cacece; + box-shadow: 0 1px 2px rgba(0,0,0,0.05), inset 0px -15px 10px -12px rgba(0,0,0,0.05); + padding: 9px; + border-radius: 3px; + display: inline-block; + position: relative; + vertical-align: middle; + cursor: pointer; +} +ui-checkbox>input:active, ui-checkbox>input:checked:active { + box-shadow: 0 1px 2px rgba(0,0,0,0.05), inset 0px 1px 3px rgba(0,0,0,0.1); +} +ui-checkbox[hover]>input, +ui-checkbox[hover]>input:checked { + box-shadow: 0 0 6px rgba(0,255,0,1); + background: #ddffee; +} + +ui-checkbox>input:checked { + background-color: #e9ecee; + border: 1px solid #adb8c0; + box-shadow: 0 1px 2px rgba(0,0,0,0.05), inset 0px -15px 10px -12px rgba(0,0,0,0.05), inset 15px 10px -12px rgba(255,255,255,0.1); + color: #99a1a7; +} +ui-checkbox>input:checked:after { + /* content: '\1f4a9'; */ + content: '\2714'; + font-size: 1em; + position: absolute; + top: 0px; + left: 0px; + right: 0; + bottom: 0; + color: #000; + text-align: center; + vertical-align: middle; +} +ui-checkbox[disabled] { + cursor: not-allowed; + color: #666; +} +ui-checkbox[disabled]>input { + background: #aaa; + color: #666; + pointer-events: none; +} +ui-checkbox[disabled][hover]>input, +ui-checkbox[disabled][hover]>input:checked { + box-shadow: none; + background: #aaa; +} +ui-slider { + display: inline-block; + min-width: 8em; +} +ui-slider>ui-slider-track { + width: 100%; + height: .5em; + margin: .5em 0; + background: #4cb96f; + border: 1px solid black; + border-radius: .2em; + display: inline-block; + cursor: pointer; + position: relative; +} +ui-slider-handle { + display: block; + position: absolute; + width: .5em; + height: 1.2em; + border-radius: .5em; + border: 1px solid black; + background: #999; + cursor: pointer; + box-shadow: 0 0 5px rgba(0,0,0,.8); +} + + +/*********** + * Buttons * + ***********/ + +/* Common styling */ + +ui-button, +ui-togglebutton, +ui-dropdownbutton, +ui-notificationbutton { + border: none; + border-radius: .5em; + padding: .25em .5em; + color: white; + cursor: pointer; + text-align: center; + -ms-user-select: none; + -moz-user-select: none; + user-select: none; + + background-image: linear-gradient(#484e55, #3a3f44 60%, #313539); + background-color: transparent; +} +ui-button[hover], +ui-togglebutton[hover], +ui-dropdownbutton[hover], +ui-notificationbutton[hover] { + background-image: linear-gradient(#323232, #404142 40%, #393b3d); +} +ui-button:active, +ui-togglebutton:active, +ui-dropdownbutton:active, +ui-notificationbutton:active { + background-image: linear-gradient(#020202, #101112 40%, #191b1d); +} +ui-button[disabled], +ui-togglebutton:disabled, +ui-dropdownbutton:disabled, +ui-notificationbutton:disabled { + color: #999; + background-image: linear-gradient(#484e55, #3a3f44 60%, #313539); + cursor: not-allowed; +} + +/* */ +ui-button { +} +ui-button[hover] { +} +ui-button:active { +} +ui-button:disabled { +} + +/* */ +ui-togglebutton { +} +ui-togglebutton[hover] { +} +ui-togglebutton:active { +} +ui-togglebutton:disabled { +} + +/* */ +ui-dropdownbutton { + display: inline-block; + position: relative; + padding-right: 1em; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} +ui-dropdownbutton::after { + display: inline-block; + content: '\25be'; + position: absolute; + right: 0; + top: 0; + padding-top: .2em; +} +ui-dropdownbutton[hover] { +} +ui-dropdownbutton:active { +} +ui-dropdownbutton:disabled { +} +ui-dropdownbutton>ui-button { + display: none; + border: none; + border-radius: 0; + width: 100%; +} +ui-dropdownbutton:active>ui-button { + display: block; +} + +/* */ +ui-notificationbutton { + position: relative; +} +ui-notificationbutton ui-indicator { + position: absolute; + min-width: 1em; + height: 1em; + top: -.8em; + right: -.8em; + font-size: .8em; + background: rgba(255,0,0,.8); + border: 1px solid rgba(0,0,0,.5); + border-radius: .5em; + text-align: center; + vertical-align: middle; + box-shadow: 0 0 5px rgba(0,0,0,.8); + padding: .2em; +} +ui-notificationbutton[hover] { +} +ui-notificationbutton:active { +} +ui-dropdownbutton:disabled { +} + +/* */ +ui-buttonbar { + display: flex; +} +ui-buttonbar>ui-button { + border-radius: 0; + border-right: 1px solid #333; + border-left: 0; + display: inline-block; +} +ui-buttonbar>ui-button:first-of-type { + border-radius: .5em 0 0 .5em; + border-left: 1px solid transparent; +} +ui-buttonbar>ui-button:last-of-type { + border-radius: 0 .5em .5em 0; + border-right: 1px solid transparent; +} + +/* */ +ui-buttonlist { + display: block; +} +ui-buttonlist>ui-button { + border-radius: 0; + border-right: 1px solid #333; + border-left: 0; + display: block; + text-align: center; +} +ui-buttonlist>ui-button:first-of-type { + border-radius: .5em .5em 0 0; + border-left: 1px solid transparent; +} +ui-buttonlist>ui-button:last-of-type { + border-radius: 0 0 .5em .5em; + border-right: 1px solid transparent; +} +

-----END OF PAGE-----