parent
f4810a6ab7
commit
07c397dc56
@ -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 |
|
After Width: | Height: | Size: 68 KiB |
After Width: | Height: | Size: 92 KiB |
After Width: | Height: | Size: 92 KiB |
@ -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 = "✔"; |
|
||||||
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…
Reference in new issue