Add a new template to have a scalable A-frame based project

dev
Unboring SL 5 years ago
parent f4810a6ab7
commit 07c397dc56
  1. 3
      .babelrc
  2. 1
      .gitignore
  3. 21
      LICENSE
  4. 2
      LICENSE.md
  5. 36
      README.md
  6. BIN
      dist/assets/img/enter-vr-button-background.png
  7. BIN
      dist/assets/img/favicon.png
  8. BIN
      dist/assets/img/loadingLogo.png
  9. 32
      dist/index.html
  10. 111
      flat.html
  11. 150
      index.html
  12. 42
      package.json
  13. 173
      pimvrhelpers.js
  14. 187
      setup/index.html
  15. 1411
      setup/upload/server/php/UploadHandler.php
  16. 18
      setup/upload/server/php/files/usersdb.js
  17. 17
      setup/upload/server/php/index.php
  18. 6
      src/home.html
  19. 58
      src/index.css
  20. 13
      src/index.js
  21. 66
      webpack.config.js

@ -0,0 +1,3 @@
{
"presets": ["babel-preset-env"]
}

1
.gitignore vendored

@ -0,0 +1 @@
node_modules

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2020 Fabien Benetou
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@ -0,0 +1,2 @@
CreativeCommons Attribution-NonCommercial-ShareAlike 4.0 International.
http://creativecommons.org/licenses/by-nc-sa/4.0/

@ -1,35 +1 @@
# relax-plus-think-space # relax-think
an infinite space for your big ideas
## Principle
Natural interaction (6DoF controllers)
in an immersive environment (relaxing setup to induce state of flow)
with work related content (e.g. Github issues, photos of work posters and post-it notes)
to be freely organised in visual categories (e.g. kanban).
## UX flow for demos and tests
### new user on phone without own photos :
1. visit https://learnwebvr.xyz and get redirected to https://learnwebvr.xyz/setup/
1. upload photos
1. generate a personalised link
1. share that link to target device (e.g. Firefox Send Tab to Devices on Oculus Quest)
1. experience on device
1. remove device and visit 2D links e.g. https://learnwebvr.xyz/flat.html?email=fabien@benetou.fr
### returning user on 6DoF device
1. visit https://learnwebvr.xyz
1 ...
### new user on 6DoF device
1. visit https://learnwebvr.xyz
1 ...
## Code
* frontend : `index.html` and `setup/index.html`
* backend : `setup/server/upload.php`
## License
MIT.
## Property and rights
Iterative Explorations SCS based in Belgium

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

32
dist/index.html vendored

@ -0,0 +1,32 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Relax-Think</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<meta name="theme-color" content="#353449">
<style>
body {
margin: 0;
}
canvas {
display: block;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
outline: none;
-webkit-tap-highlight-color: rgba(255, 255, 255, 0); /* mobile webkit */
}
</style>
<link rel="shortcut icon" href="assets/img/favicon.png" type="image/x-icon">
<script src="https://aframe.io/releases/1.0.3/aframe.min.js"></script>
<script src="bundle.js"></script>
</head>
<body>
<div id="app"></div>
<a id="vrButton" href="#" title="Enter VR / Fullscreen"></a>
</body>
</html>

@ -1,111 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
<link href="https://getbootstrap.com/docs/4.0/examples/signin/signin.css" rel="stylesheet">
<script src="setup/upload/server/php/files/usersdb.js"></script>
<script src="https://aframe.io/releases/1.0.0/aframe.min.js"></script>
</head>
<style>
body { background-color: transparent; }
#email, #sms {
font-size: xx-large;
}
</style>
<body>
<img src="setup/productlogo.png">
<h3>flat viewer by category sorted by position</h3>
<div id="spacesholder">Your spaces:
<ul id="spaces"></ul>
</div>
<div>Already have an account? <span onclick="login()" style="text-decoration: underline;">Log-in</span>
<form>
<div id="login" style="display:none"><input id="useremail"/>
<button style="margin-top:2px;" type="button" onclick="loginViaEmail()" id="loginemail" class="btn btn-lg btn-primary btn-block">Login</button>
</div>
</form>
<div style="display:none" id="nouser">User not found. Double check your email address then contact fabien@iterative-explorations.com</a></div>
</div>
<script>
const urlParams = new URLSearchParams(window.location.search);
const email = urlParams.get('email');
if (email) loginViaEmail()
var uploadedURL = "setup/upload/server/php/files/";
var images = []
var url = "/"
var urlParameters = "?customimages="
var userspace
function login(){
document.querySelector("#login").style.display = "block"
}
function findCategory(image){
if (!userspace) return
console.log(image.filename)
var imagePos = new THREE.Vector3();
imagePos.copy ( AFRAME.utils.coordinates.parse(image.position) )
var closest
var smallestDistance = 1000
for (var category of userspace.categories){
var categoryPos = new THREE.Vector3();
categoryPos.copy ( AFRAME.utils.coordinates.parse(category.position) )
var distance = categoryPos.distanceTo( imagePos )
if (distance < smallestDistance){
smallestDistance = distance
closest = category.label
}
console.log(distance, category.label)
}
return closest
}
function loginViaEmail(){
var path = "setup/"
if (!email)
email = document.querySelector("#useremail").value
userspace = database[email]
if (!userspace){
document.querySelector("#nouser").style.display = "block"
return
}
document.querySelector("#spacesholder").style.display = "block"
var spaces = document.querySelector("#spaces")
var space = document.createElement("li")
var spacelink = document.createElement("a")
var images = userspace.files
spacelink.href = url + urlParameters
spacelink.target = "_blank"
spacelink.innerHTML = userspace.last_login
space.appendChild(spacelink)
for (var image of images){
urlParameters += image.filename + ","
space.innerHTML += path + image.filename + " " + findCategory( image )
}
console.log(space)
spaces.appendChild(space)
console.log("userspace", userspace)
}
</script>
<h3 style="position:absolute; bottom:0px; right:0px;">A product by <a href="https://iterative-explorations.com"><img width="200px" src="https://iterative-explorations.com/logo.svg"></a>.</h3>
</body>
</html>

@ -1,150 +0,0 @@
<head>
<meta charset="UTF-8">
<title>Think + Relax space by Iterative Explorations</title>
<script src="https://aframe.io/releases/1.0.0/aframe.min.js"></script>
<script src="https://rawgit.com/feiss/aframe-environment-component/master/dist/aframe-environment-component.min.js"></script>
<script src="https://cdn.rawgit.com/donmccurdy/aframe-extras/v4.1.2/dist/aframe-extras.min.js"></script>
<script src="https://unpkg.com/super-hands@3.0.0/dist/super-hands.min.js"></script>
<script src="pimvrhelpers.js"></script>
</head>
<script>
// could add an in VR edit mode for the categories, to position them, name them, etc
// should be mostly for adjustments
var timer = AFRAME.utils.getUrlParameter('timer')
if (!timer) timer = 300 //5min
function saveNewItemsPosition(){
var selector = ".notes";
var pos = ""
document.querySelectorAll(selector).forEach(
e => pos += "\n" + AFRAME.utils.coordinates.stringify( e.getAttribute("position") )
)
pimvrSaveRemote("WeWorkTest", pos) // from pimvrhelpers.js
// should be saved by username by space
// could be pointed at from the database file (thus having multiple storages)
}
AFRAME.registerComponent("watch", {
init: function() {
document.querySelector("#timer").setAttribute("text","value:"+timer)
this.tick = AFRAME.utils.throttleTick(this.tick, 1000, this);
// details https://aframe.io/docs/1.0.0/core/utils.html#aframe-utils-throttle-function-minimuminterval-optionalcontext
},
tick: function (t, dt) {
var time = Number( document.querySelector("#timer").getAttribute("text").value )
time--
document.querySelector("#timer").setAttribute("text","value:"+(time))
},
})
AFRAME.registerComponent("relaxing-introduction", {
init: function() {
// get in flow state
// for now controlled just with AFrame animation
// cf https://aframe.io/docs/1.0.0/components/animation.html
setTimeout(function(){
for (var note of document.querySelectorAll(".notes")){ note.emit("fadein") }
}, 10 * 1000) // arbitrary 10s, should be instead after the introduction is finished
}
})
AFRAME.registerComponent("decompression-conclusion", {
init: function() {
// cf PoC at 2017 W3C web authoring workshop with Roland Dubois
setTimeout(function(){
var intructions = document.querySelector("#instructions")
instructions.setAttribute("position", "-1 1.5 -1") // assumes the user is roughtly centered... could force on camera instead
instructions.setAttribute("text", "value", "Your session will end soon\n\nVisit https://learnWebVR.xyz/flat.html\n\nto work outside of VR.")
instructions.setAttribute("opacity", "1") // could be animated instead
console.log("X min voice over and notes fade-out with contextual information (time, weather, etc)")
document.querySelector("[environment]").setAttribute("environment", "preset:checkerboard")
}, timer * 1000);
}
})
AFRAME.registerComponent("environment-by-url", {
schema: {},
init: function() {
var environment = AFRAME.utils.getUrlParameter('environment')
if (environment) document.querySelector("[environment]").setAttribute("environment", "preset:" + environment)
}
})
AFRAME.registerComponent("photo-importer", {
schema: {},
init: function() {
var baseURL = "setup/"
var images = AFRAME.utils.getUrlParameter('customimages').split(',');
if (images.length == 1) window.location = baseURL; // no images? must be configured first
// could also display a tutorial instead
var i=1
for (var image of images){
if (image){
var id = "placeholder"+i
var placeholder = document.querySelector("#"+id)
if (!placeholder){
// TODO somehow not interactable anymore?!
// had a similar silly issue before but can't recall how I fixed it :(
// something basic about entities or hierarchy...
placeholder = document.createElement("a-box")
placeholder.id = id
placeholder.setAttribute("material", "shader:flat")
placeholder.setAttribute("hoverable", "")
placeholder.setAttribute("grabbable", "")
placeholder.setAttribute("draggable", "")
placeholder.setAttribute("dropppable", "")
// somehow does appear visually correct but are NOT interactable!
placeholder.setAttribute("position", "" + i/1.5-1.5 + " 1.5 -0.7")
placeholder.setAttribute("scale", "0.5 0.3 0.01")
// assume 1 * 1.5 photo ratio (landscape)
AFRAME.scenes[0].appendChild(placeholder)
}
placeholder.className += "notes"
placeholder.setAttribute("opacity", "0")
placeholder.setAttribute("animation", "property: components.material.material.opacity; to: 1; dur: 1500; easing: linear; startEvents: fadein;")
placeholder.setAttribute("src", baseURL+image)
i++
}
}
}
})
</script>
<body>
<a-scene photo-importer environment-by-url relaxing-introduction decompression-conclusion>
<a-entity environment="preset: forest;"></a-entity>
<a-entity sphere-collider="objects: a-box" super-hands hand-controls="left"></a-entity>
<a-entity sphere-collider="objects: a-box" super-hands hand-controls="right"></a-entity>
<!-- could be wrist watch as I've done via Twitter years ago -->
<a-text watch id="timer" rotation="180 0 180" position="0 1 2" value="time"></a-text>
<a-text id="instructions" position="-1 1.5 -0.65" value="welcome to your\n\nthink+relax space" opacity="0"
animation__fadein="property: components.text.material.uniforms.opacity.value; to: 0.99; dur: 1500; easing: linear"
animation__fadeout="property: components.text.material.uniforms.opacity.value; to: 0.01; dur: 1500; easing: linear; startEvents: fadeout;"
animation__leave="property: object3D.position.z; to: -100; dur: 15000; easing: linear; delay: 2500;"
></a-text>
<a-text rotation="-20 20 0" position="-2 0.2 -2" scale="1 1 1" value="waiting"></a-text>
<a-box material="wireframe:true" color="orange" position="-2 1 -2" scale="2 2 2"></a-box>
<a-text rotation="-20 0 0" position=" 0 0.2 -2" scale="1 1 1" value="in progress"></a-text>
<a-box material="wireframe:true" color="red" position="0 1 -2" scale="2 2 2"></a-box>
<a-text rotation="-20 -20 0" position=" 2 0.2 -2" scale="1 1 1" value="completed"></a-text>
<a-box material="wireframe:true" color="green" position="2 1 -2" scale="2 2 2"></a-box>
<a-box material="shader:flat" id="placeholder1" hoverable grabbable stretchable draggable dropppable position="0.5 1.5 -0.5" scale="0.5 0.3 0.01"></a-box>
<a-box material="shader:flat" id="placeholder2" hoverable grabbable stretchable draggable dropppable position="0 1.9 -0.7" scale="0.5 0.3 0.01"></a-box>
<a-box material="shader:flat" id="placeholder3" hoverable grabbable stretchable draggable dropppable position="-0.5 1.5 -0.7" scale="0.5 0.3 0.01"></a-box>
<!-- somehow creating dynamically fails... even though it did (!) work at some point (but wasn't saved via git)-->
<!-- until then will rely on a pool of objects -->
</a-scene>
</body>

@ -0,0 +1,42 @@
{
"name": "relax-think",
"version": "0.0.1",
"license": "CC-BY-NC-SA-4.0",
"description": "An infinite space for your big ideas",
"repository": {
"type": "git",
"url": ""
},
"main": "index.js",
"dependencies": {},
"scripts": {
"start": "webpack-dev-server --mode development --host 192.168.0.12",
"build": "webpack --mode production --config webpack.config.js"
},
"keywords": [],
"author": "Arturo Paracuellos",
"devDependencies": {
"aframe-environment-component": "^2.0.0",
"aframe-super-hot-loader": "^1.7.0",
"aframe-super-hot-html-loader": "^2.1.0",
"babel-core": "^6.26.3",
"babel-loader": "^7.1.4",
"babel-preset-env": "^1.7.0",
"css-loader": "^3.4.1",
"html-require-loader": "^1.0.1",
"json-loader": "^0.5.7",
"super-hands": "^3.0.0",
"style-loader": "^0.23.1",
"url-loader": "^1.1.2",
"webpack": "^4.39.3",
"webpack-cli": "^3.3.7",
"webpack-dev-server": "^3.8.0",
"webpack-glsl-loader": "^1.0.1"
},
"standard": {
"globals": [
"AFRAME",
"THREE"
]
}
}

@ -1,173 +0,0 @@
//---------PIM helper functions-----------------------------------------------------------
/*
currently PmWiki backend
could use http://fabien.benetou.fr/Site/AllRecentChanges?action=source to check for update
heavy but nearly no processing required
enough if done once per minute or so
if update, request serverrender on modified page
IFF it's being displayed
planned Evernote backend
https://github.com/evernote/evernote-sdk-js
https://github.com/wanasit/everest-js
https://stackoverflow.com/questions/24580588/how-to-list-all-the-notes-from-an-evernote-notebook-javascript-node-js
https://dev.evernote.com/doc/articles/polling_notification.php
or other popular PIMs
https://developers.trello.com/
http://www.xmind.net/developer/
ideally with webhooks on a backend abstraction with coherent API
*/
function pimvrSaveItemsStates(callback) {
function updateItemsStates(globalStates){
let pageStates = {};
let [group, page] = getPageGroup();
let elements = document.body.querySelectorAll('.pimvr-item');
for (let item of elements) {
let id = item.getAttribute("id");
let position = item.getComputedAttribute("position");
pageStates[id] = {"position": position};
}
globalStates[group+"_"+page] = pageStates;
pimvrSaveRemote("ItemsStates", JSON.stringify(globalStates));
return "Items states saved";
}
pimvrLoadRemote("ItemsStates", updateItemsStates);
}
function pimvrSaveConfiguration(callback) {
let configuration = {};
let elements = document.body.querySelectorAll('.pimvr-configuration');
for (let item of elements) {
let id = item.getAttribute("id");
let position = item.getComputedAttribute("position");
configuration[id] = {"position": position};
}
pimvrSaveRemote("Configuration", JSON.stringify(configuration));
return "Configuration saved";
}
function pimvrLoadIoTData(callback) {
// should give min/max ranges, here seems to be 0-1010
// to use (once normalized) as an attribute value
// used on http://jsbin.com/nucanat/edit?html,output
// warning HTTPS on tick is really hammering
const readURL = "https://fabien.benetou.fr/PIMVRdata/IoTData?action=source";
var myRequest = new XMLHttpRequest();
myRequest.open('GET', readURL);
myRequest.onreadystatechange = function () {
if (myRequest.readyState === 4) {
callback(myRequest.responseText);
}
};
myRequest.send();
}
function pimvrServerRender(group, page, callback) {
const readURL = "https://fabien.benetou.fr/"+group+"/"+page+"?action=serverrender";
var myRequest = new XMLHttpRequest();
myRequest.open('GET', readURL);
myRequest.onreadystatechange = function () {
if (myRequest.readyState === 4) {
callback(JSON.parse(myRequest.responseText).res);
}
};
myRequest.send();
}
function pimvrLoadRemoteSmarthWatchConfiguration(callback) {
const readURL = "https://fabien.benetou.fr/PIMVRdata/SmartWatchConfiguration?action=source";
var myRequest = new XMLHttpRequest();
myRequest.open('GET', readURL);
myRequest.onreadystatechange = function () {
if (myRequest.readyState === 4) {
callback(JSON.parse(myRequest.responseText));
}
};
myRequest.send();
}
//pimvrLoadRemoteSmarthWatchConfiguration(console.log);
// usage unclear, can be used as
// haptic feedback on interactible items e.g. vibrate on gaze
// controller backup e.g. gaze+click
// controller locator e.g. making 2 bright columns
// heart rate monitor (sadly not with PebbleTime) to reshape experience
function pimvrLoadRemoteMetadata(group, page, callback, query) {
const readURL = "https://fabien.benetou.fr/"+group+"/"+page+"?action=metajson";
var myRequest = new XMLHttpRequest();
myRequest.open('GET', readURL+"&query="+query, true);
myRequest.onreadystatechange = function () {
if (myRequest.readyState === 4) {
callback(JSON.parse(myRequest.responseText));
}
};
myRequest.send();
}
function pimvrLoadRemote(page, callback) {
const readURL = "https://fabien.benetou.fr/PIMVRdata/"+page+"?action=source";
// assumes JSON
var myRequest = new XMLHttpRequest();
myRequest.open('GET', readURL);
myRequest.onreadystatechange = function () {
if (myRequest.readyState === 4) {
callback(JSON.parse(myRequest.responseText));
}
};
myRequest.send();
}
function pimvrSaveRemote(page, data) {
const writeURL = "https://fabien.benetou.fr/PIMVRdata/"+page+"?action=edit";
var myWriteRequest = new XMLHttpRequest();
myWriteRequest.open('POST', writeURL, true);
myWriteRequest.setRequestHeader("Content-Type",
"application/x-www-form-urlencoded");
myWriteRequest.onreadystatechange = function () {
if (myWriteRequest.readyState === 4) {
//console.log(myWriteRequest.responseText);
console.log("Save on "+page+" sucessful");
}
};
console.log("trying to open "+writeURL+"post=1&author=PIMVR&authpw=edit_password&text="+data)
myWriteRequest.send("post=1&author=PIMVR&authpw=edit_password&text="+data);
// cf http://www.pmwiki.org/wiki/PmWiki/EditingAPI
}
function loadRemoteGraph(callback, params){
const myDataURL = "https://vatelier.net/MyDemo/newtooling/wiki_graph.json";
// not that as agressive as it gets cached
var myRequest = new XMLHttpRequest();
myRequest.open('GET', myDataURL);
myRequest.onreadystatechange = function () {
if (myRequest.readyState === 4) {
//window.PIMgraph = JSON.parse(myRequest.responseText).Nodes;
callback(JSON.parse(myRequest.responseText).Nodes, params);
}
};
myRequest.send();
}
function getMyNeighbours(nodes, page){
console.log(nodes[page].Targets);
}
function getPageGroup(){
let group = QueryString.group || "Main";
let page = QueryString.page || "HomePage";
return [group, page];
}

@ -1,187 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
<link href="https://getbootstrap.com/docs/4.0/examples/signin/signin.css" rel="stylesheet">
<script src="clipboard.min.js"></script>
<script src="upload/server/php/files/usersdb.js"></script>
</head>
<style>
body { background-color: transparent; }
#email, #sms {
font-size: xx-large;
}
</style>
<body>
<img src="productlogo.png">
<script>
var tickMark = "&#10004;";
var uploadedURL = "upload/server/php/files/";
var images = []
var url = "/"
var urlParameters = "?customimages="
function generateURL(){
//var url = "https://learnwebvr.xyz/importer.html"
for (var image of images){
urlParameters += image + ","
}
console.log ("URL", urlParameters );
var encodedURL = url + urlParameters
encodedURL += "&environment=" + document.querySelector("#environment").value
document.querySelector("#link").href = encodedURL
document.querySelector("#copyBtn").setAttribute("data-clipboard-text", encodedURL)
document.querySelector("#generateURL").style.display = "block"
document.querySelector("#email").href = "mailto:?&subject=session&body=" + encodeURIComponent( encodedURL )
// tested on iPhone 6S and Pixel2 and desktop
if( /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ) {
}
else {
document.querySelector("#copydiv").className = "cold-md-6"
document.querySelector("#emaildiv").className = "cold-md-6"
}
}
</script>
<div class="container">
<form class="form-signin">
Environment:
<select id="environment">
<option name="contact">contact</option>
<option name="egypt">egypt</option>
<option name="checkerboard">checkerboard</option>
<option name="forest" selected="selected">forest</option>
<option name="goaland">goaland</option>
<option name="yavapai">yavapai</option>
<option name="goldmine">goldmine</option>
<option name="threetowers">threetowers</option>
<option name="poison">poison</option>
<option name="arches">arches</option>
<option name="tron">tron</option>
<option name="japan">japan</option>
<option name="dream">dream</option>
<option name="volcano">volcano</option>
<option name="starry">starry</option>
<option name="osiris">osiris</option>
</select>
<button style="margin-top:2px;" type="button" onclick="unhide('#fileupload2');" class="btn btn-primary btn-block">Upload your office photos<br/>(post-its, white board, etc)</button>
No content available? Try <a target="_blank" href="https://learnwebvr.xyz/?customimages=upload/server/php/files/photo6035074301452988871.jpg,upload/server/php/files/photo6035074301452988870.jpg,upload/server/php/files/IMG_6496 (1).jpg"> this example instead</a>!
<input style="display:none;" id="fileupload2" type="file" name="files[]" data-url="upload/server/php/" multiple>
<div style="display:none;" id="fileuploaded2">Files uploaded: </div>
<div style="opacity:0.7;" id="uploadrate"></div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="upload/js/vendor/jquery.ui.widget.js"></script>
<script src="upload/js/jquery.iframe-transport.js"></script>
<script src="upload/js/jquery.fileupload.js"></script>
<script>
function unhide( element ){
document.querySelector(element).style.display = "block";
}
$(function () {
$('#fileupload2').fileupload({
dataType: 'json',
done: function (e, data) {
$.each(data.result.files, function (index, file) {
var imageURL = uploadedURL + (file.name)
images.push(imageURL)
var uploaded = document.querySelector("#fileuploaded2");
uploaded.style.display = "block";
uploaded.innerHTML += tickMark;
});
}
});
});
</script>
<button style="margin-top:2px;" type="button" onclick="generateURL()" id="generate-simulation" class="btn btn-lg btn-primary btn-block">Generate your<br/>relax+think space</button>
</form>
</div>
<div id="generateURL" style="display:none; text-align:center;" class="container">
<div class="container">
<a id="link" target="_blank" href=""><button type="button" class="btn btn-lg btn-primary btn-block">Launch relax+think space</button></a>
</div>
<br/>
<div class="container">
<div class="row">
<div class="col-md-4" id="copydiv">
<button class="btn" data-clipboard-text="" id="copyBtn">Copy to clipboard</button>
<script>
new Clipboard('.btn');
</script>
</div>
<div class="col-md-4" id="emaildiv">
<a id="email" href="" target="_blank">email link</a>
</div>
</div>
</div>
</div>
<script>
function login(){
document.querySelector("#login").style.display = "block"
}
function loginViaEmail(){
var email = document.querySelector("#useremail").value
var userspace = database[email]
if (!userspace){
document.querySelector("#nouser").style.display = "block"
return
}
document.querySelector("#spacesholder").style.display = "block"
var spaces = document.querySelector("#spaces")
var space = document.createElement("li")
var spacelink = document.createElement("a")
var images = userspace.files
for (var image of images){
urlParameters += image.filename + ","
}
spacelink.href = url + urlParameters
spacelink.target = "_blank"
spacelink.innerHTML = userspace.last_login
space.appendChild(spacelink)
console.log(space)
spaces.appendChild(space)
console.log("userspace", userspace)
}
</script>
<div>Already have an account? <span onclick="login()" style="text-decoration: underline;">Log-in</span>
<form>
<div id="login" style="display:none"><input id="useremail"/>
<button style="margin-top:2px;" type="button" onclick="loginViaEmail()" id="loginemail" class="btn btn-lg btn-primary btn-block">Login</button>
</div>
</form>
<div style="display:none" id="nouser">User not found. Double check your email address then contact fabien@iterative-explorations.com</a></div>
<div style="display:none" id="spacesholder">Your spaces:
<ul id="spaces"></ul>
</div>
</div>
<h3 style="position:absolute; bottom:0px; right:0px;">A product by <a href="https://iterative-explorations.com"><img width="200px" src="https://iterative-explorations.com/logo.svg"></a>.</h3>
</body>
</html>

File diff suppressed because it is too large Load Diff

@ -1,18 +0,0 @@
var database = {
"fabien@benetou.fr" : {
"username": "fabien",
"password": "test",
"last_login": 1576749381516,
"environment": "forest",
"files" : [
{ "position": "-2 1 -2", "filename" : "upload/server/php/files/C1C4E2F7-865A-4DB4-9274-1EA69B6C4A7E.jpeg" },
{ "position": "0 1 -2", "filename" : "upload/server/php/files/4A21A76C-F375-4884-9040-BC51A24057A6.jpeg" },
{ "position": "2 1 -2", "filename" : "upload/server/php/files/2CD9932F-891C-4BA5-BDDA-A3B39B39CF09.jpeg" }
],
"categories" : [
{ "position": "-2 1 -2", "color" : "orange", "label" : "waiting" },
{ "position": "0 1 -2", "color" : "red", "label" : "in progress" },
{ "position": "2 1 -2", "color" : "green", "label" : "completed" }
]
}
}

@ -1,17 +0,0 @@
<?php
/*
* jQuery File Upload Plugin PHP Example
* https://github.com/blueimp/jQuery-File-Upload
*
* Copyright 2010, Sebastian Tschan
* https://blueimp.net
*
* Licensed under the MIT license:
* https://opensource.org/licenses/MIT
*/
error_reporting(E_ALL | E_STRICT);
require('UploadHandler.php');
$upload_handler = new UploadHandler(array(
'accept_file_types' => '/\.(png|jpe?g|mp4)$/i'
));

@ -0,0 +1,6 @@
<a-scene background="color: #FAFAFA" vr-mode-ui="enterVRButton: #vrButton">
<a-box position="-1 0.5 -3" rotation="0 45 0" color="#4CC3D9" shadow></a-box>
<a-sphere position="0 1.25 -5" radius="1.25" color="#EF2D5E" shadow></a-sphere>
<a-cylinder position="1 0.75 -3" radius="0.5" height="1.5" color="#FFC65D" shadow></a-cylinder>
<a-plane position="0 0 -4" rotation="-90 0 0" width="4" height="4" color="#7BC8A4" shadow></a-plane>
</a-scene>

@ -0,0 +1,58 @@
html {
background: #000;
}
#vrButton {
position: absolute;
background: url('../dist/assets/img/enter-vr-button-background.png') no-repeat;
background-size: cover;
border: 0;
cursor: pointer;
right: 20px;
bottom: 20px;
text-decoration: none;
z-index: 9999999;
width: 64px;
height: 64px;
}
#vrButton.a-hidden {
visibility: hidden;
}
#vrButton:active {
border: 0;
}
#vrButton:hover {
background-position: 0 -64.5px;
}
#vrButton p {
bottom: 45px;
color: #FFF;
font-size: 12px;
font-family: monospace;
font-weight: bold;
text-align: center;
text-transform: uppercase;
position: relative;
}
.a-loader-title {
animation: loaderTitle 1s infinite alternate;
color: rgba(0,0,0,0);
background: none;
background-image: url('../dist/assets/img/loadingLogo.png');
height: 36.2vh;
background-repeat: no-repeat;
background-position: center;
margin-top: 0;
background-size: contain;
}
@keyframes loaderTitle {
0% { opacity: 1; }
50% { opacity: 0.8; }
100% { opacity: 1; }
}

@ -0,0 +1,13 @@
function requireAll (req) { req.keys().forEach(req); }
console.time = () => {};
console.timeEnd = () => {};
require('aframe-environment-component')
require('super-hands')
require('./index.css')
require('./home.html')
if (module.hot) { module.hot.accept(); }

@ -0,0 +1,66 @@
const path = require('path')
module.exports = {
entry: {
index: './src/index.js'
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
devServer: {
https: true,
port: 3000,
contentBase: './dist'
},
devtool: 'source-map',
plugins: [
],
optimization: {
},
module: {
rules: [
{
test: /\.js/,
exclude: /(node_modules)/,
use: ['babel-loader', 'aframe-super-hot-loader']
},
{
test: /\.json/,
exclude: /(node_modules)/,
type: 'javascript/auto',
loader: ['json-loader']
},
{
test: /\.html/,
exclude: /(node_modules)/,
use: [
'aframe-super-hot-html-loader',
{
loader: 'html-require-loader',
options: {
root: path.resolve(__dirname, 'src')
}
}
]
},
{
test: /\.glsl/,
exclude: /(node_modules)/,
loader: 'webpack-glsl-loader'
},
{
test: /\.css$/,
exclude: /(node_modules)/,
use: ['style-loader', 'css-loader']
},
{
test: /\.(png|jpg)/,
loader: 'url-loader'
}
]
},
resolve: {
modules: [path.join(__dirname, 'node_modules')]
}
}
Loading…
Cancel
Save