|
@@ -0,0 +1,1289 @@
|
|
|
+import { Lang } from '/lib/polyglot/language.js';
|
|
|
+import { Helpers } from '/helpers.js';
|
|
|
+import { IndexApp } from '/web/index-app.js';
|
|
|
+import { Widgets } from '/lib/widgets.js';
|
|
|
+
|
|
|
+
|
|
|
+class App {
|
|
|
+ constructor() {
|
|
|
+ console.log("app constructor");
|
|
|
+ this.widgets = new Widgets;
|
|
|
+
|
|
|
+ //globals
|
|
|
+ window._app = this;
|
|
|
+ window._cellWidgets = this.widgets;
|
|
|
+ window._LangManager = new Lang;
|
|
|
+ window._noty = new Noty;
|
|
|
+
|
|
|
+ _LangManager.setLanguage().then(res => {
|
|
|
+ return this.initDB()
|
|
|
+ }).then(res => {
|
|
|
+
|
|
|
+ this.helpers = new Helpers;
|
|
|
+ this.initUser();
|
|
|
+
|
|
|
+ //client routes
|
|
|
+ page('/', this.HandleIndex);
|
|
|
+ page('/setup', this.HandleSetupIndex);
|
|
|
+ page('/profile', this.HandleUserIndex);
|
|
|
+ page('/worlds', this.HandleIndex);
|
|
|
+ page('/:user/worlds', this.HandleUserWorlds);
|
|
|
+ page('/:user/worlds/:type', this.HandleUserWorldsWithType);
|
|
|
+ page('/:user/:type/:name/edit/:file', this.HandleFileEdit);
|
|
|
+ page('/:user/:space', this.HandleParsableRequestGenID);
|
|
|
+ page('/:user/:space/:id', this.HandleParsableRequestWithID);
|
|
|
+ page('/:user/:space/index.vwf/:id', this.HandleParsableRequestWithID);
|
|
|
+ page('/:user/:space/load/:savename', this.HandleParsableLoadRequest);
|
|
|
+ page('/:user/:space/:id/load/:savename', this.HandleParsableRequestWithID);
|
|
|
+
|
|
|
+ page('/:user/:space/load/:savename/:rev', this.HandleParsableLoadRequestWithRev);
|
|
|
+ page('/:user/:space/:id/load/:savename/:rev', this.HandleParsableRequestWithID);
|
|
|
+
|
|
|
+ page('*', this.HandleNoPage);
|
|
|
+
|
|
|
+ page();
|
|
|
+
|
|
|
+
|
|
|
+ })
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ initDB() {
|
|
|
+
|
|
|
+ var config = JSON.parse(localStorage.getItem('lcs_config'));
|
|
|
+ if (!config) {
|
|
|
+ config = {
|
|
|
+ 'dbhost': 'https://' + window.location.hostname + ':8080/gun', //'http://localhost:8080/gun',
|
|
|
+ 'reflector': 'https://' + window.location.hostname + ':3002',
|
|
|
+ 'language': 'en'
|
|
|
+ }
|
|
|
+ localStorage.setItem('lcs_config', JSON.stringify(config));
|
|
|
+ }
|
|
|
+
|
|
|
+ const dbConnection = new Promise((resolve, reject) => {
|
|
|
+
|
|
|
+ this.db = Gun(this.dbHost);
|
|
|
+ this.user = this.db.user();
|
|
|
+ window._LCSDB = this.db;
|
|
|
+ window._LCSUSER = this.user;
|
|
|
+ window._LCS_SYS_USER = undefined;
|
|
|
+ window._LCS_WORLD_USER = undefined;
|
|
|
+
|
|
|
+ _LCSDB.get('lcs/app').get('pub').once(res => {
|
|
|
+
|
|
|
+ if (res) {
|
|
|
+ window._LCS_SYS_USER = this.db.user(res);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ _LCSDB.on('hi', function (peer) {
|
|
|
+
|
|
|
+ let msg = 'Connected to ' + peer.url;
|
|
|
+
|
|
|
+ let noty = new Noty({
|
|
|
+ text: msg,
|
|
|
+ timeout: 2000,
|
|
|
+ theme: 'mint',
|
|
|
+ layout: 'bottomRight',
|
|
|
+ type: 'success'
|
|
|
+ });
|
|
|
+ noty.show();
|
|
|
+ console.log(msg)
|
|
|
+ })
|
|
|
+
|
|
|
+ _LCSDB.on('bye', function (peer) {
|
|
|
+
|
|
|
+ let msg = 'No connection to ' + peer.url;
|
|
|
+ let noty = new Noty({
|
|
|
+ text: msg,
|
|
|
+ timeout: 1000,
|
|
|
+ theme: 'mint',
|
|
|
+ layout: 'bottomRight',
|
|
|
+ type: 'error'
|
|
|
+ });
|
|
|
+ noty.show();
|
|
|
+ console.log(msg)
|
|
|
+ })
|
|
|
+ resolve('ok');
|
|
|
+ });
|
|
|
+ return dbConnection
|
|
|
+ }
|
|
|
+
|
|
|
+ initUser() {
|
|
|
+ _LCSUSER.recall({ sessionStorage: 1 });
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ get reflectorHost() {
|
|
|
+
|
|
|
+ var res = "";
|
|
|
+ let config = localStorage.getItem('lcs_config');
|
|
|
+ if (config) {
|
|
|
+ res = JSON.parse(config).reflector;
|
|
|
+ }
|
|
|
+ return res;
|
|
|
+ }
|
|
|
+
|
|
|
+ get dbHost() {
|
|
|
+
|
|
|
+ var res = "";
|
|
|
+ let config = localStorage.getItem('lcs_config');
|
|
|
+ if (config) {
|
|
|
+ res = JSON.parse(config).dbhost;
|
|
|
+ }
|
|
|
+ return res;
|
|
|
+ }
|
|
|
+
|
|
|
+ async loadProxyDefaults() {
|
|
|
+
|
|
|
+ //load to DB default proxy files (VWF & A-Frame components)
|
|
|
+
|
|
|
+ let proxyResponse = await fetch('/proxy-files', { method: 'get' });
|
|
|
+ let proxyFiles = await proxyResponse.json();
|
|
|
+ let filterProxyFiles = proxyFiles.filter(el => (el !== null));
|
|
|
+ console.log(filterProxyFiles);
|
|
|
+
|
|
|
+ var origin = window.location.origin;
|
|
|
+ //var userPub = _LCSUSER.is.pub;
|
|
|
+
|
|
|
+ let proxyObj = {};
|
|
|
+
|
|
|
+ for (var index in filterProxyFiles) {
|
|
|
+
|
|
|
+ let el = filterProxyFiles[index];
|
|
|
+
|
|
|
+ if (el) {
|
|
|
+ var url = origin + el;
|
|
|
+ var entryName = url.replace(origin + '/defaults/', "").split(".").join("_");
|
|
|
+ let proxyFile = await fetch(url, { method: 'get' });
|
|
|
+ let responseText = await proxyFile.text();
|
|
|
+
|
|
|
+ if (responseText) {
|
|
|
+ let obj = {
|
|
|
+ //'owner': userPub,
|
|
|
+ 'file': responseText
|
|
|
+ }
|
|
|
+ proxyObj[entryName] = obj;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ console.log(proxyObj);
|
|
|
+
|
|
|
+ Object.keys(proxyObj).forEach(el => {
|
|
|
+ _LCSDB.user().get('proxy').get(el).put(proxyObj[el]);
|
|
|
+ })
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ async loadWorldsDefaults() {
|
|
|
+
|
|
|
+ //load to DB default worlds
|
|
|
+
|
|
|
+ let worldsResponse = await fetch('/world-files', { method: 'get' });
|
|
|
+ let worldFiles = await worldsResponse.json();
|
|
|
+ let filterworldFiles = worldFiles.filter(el => (el !== null));
|
|
|
+ console.log(filterworldFiles);
|
|
|
+
|
|
|
+ let worldsObj = {};
|
|
|
+
|
|
|
+ for (var index in filterworldFiles) {
|
|
|
+
|
|
|
+ let el = filterworldFiles[index];
|
|
|
+
|
|
|
+ if (el) {
|
|
|
+ let url = window.location.origin + el;
|
|
|
+ var entryName = url.replace(window.location.origin + '/defaults/worlds/', "").split(".").join("_");
|
|
|
+
|
|
|
+ let worldName = entryName.split("/")[0];
|
|
|
+ let userPub = _LCSUSER.is.pub;
|
|
|
+
|
|
|
+ let worldFile = await fetch(url, { method: 'get' });
|
|
|
+ let worldSource = await worldFile.text();
|
|
|
+ if (worldSource) {
|
|
|
+ let modified = new Date().valueOf();
|
|
|
+
|
|
|
+ let obj = {
|
|
|
+ 'file': worldSource,
|
|
|
+ 'modified': modified
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!worldsObj[worldName]) {
|
|
|
+ worldsObj[worldName] = {
|
|
|
+ 'parent': '-',
|
|
|
+ 'owner': userPub,
|
|
|
+ 'featured': true,
|
|
|
+ 'published': true
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ let entry = entryName.replace(worldName + '/', "");
|
|
|
+ worldsObj[worldName][entry] = obj;
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log(worldsObj);
|
|
|
+
|
|
|
+ Object.entries(worldsObj).forEach(el => {
|
|
|
+
|
|
|
+ let worldName = el[0];
|
|
|
+ let files = el[1];
|
|
|
+ Object.entries(files).forEach(file => {
|
|
|
+
|
|
|
+ _LCSDB.user().get('worlds').get(worldName).get(file[0]).put(file[1]);
|
|
|
+
|
|
|
+ })
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ async loadEmptyDefaultProto() {
|
|
|
+
|
|
|
+ //empty proto world
|
|
|
+ let userPub = _LCSUSER.is.pub;
|
|
|
+ let worldsObj = {};
|
|
|
+ let emptyWorld = {
|
|
|
+
|
|
|
+ "index_vwf_yaml": YAML.stringify(
|
|
|
+ {
|
|
|
+ "extends": "http://vwf.example.com/aframe/ascene.vwf"
|
|
|
+ }, 4),
|
|
|
+
|
|
|
+ "index_vwf_config_yaml": YAML.stringify(
|
|
|
+ {
|
|
|
+ "info": {
|
|
|
+ "title": "Empty World"
|
|
|
+ },
|
|
|
+ "model": {
|
|
|
+ "vwf/model/aframe": null
|
|
|
+ },
|
|
|
+ "view": {
|
|
|
+ "vwf/view/aframe": null,
|
|
|
+ "vwf/view/editor-new": null
|
|
|
+ }
|
|
|
+ }, 4),
|
|
|
+
|
|
|
+ "index_vwf_html": "",
|
|
|
+ "appui_js": "",
|
|
|
+ "info_json": JSON.stringify ({
|
|
|
+ "info": {
|
|
|
+ "en": {
|
|
|
+ "title": "Empty World",
|
|
|
+ "imgUrl": "",
|
|
|
+ "text": "Empty World"
|
|
|
+ },
|
|
|
+ "ru": {
|
|
|
+ "title": "Новый Мир",
|
|
|
+ "imgUrl": "",
|
|
|
+ "text": "Новый Мир"
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ worldsObj['empty'] = {
|
|
|
+ 'parent': '-',
|
|
|
+ 'owner': userPub,
|
|
|
+ 'featured': true,
|
|
|
+ 'published': true
|
|
|
+ }
|
|
|
+
|
|
|
+ Object.keys(emptyWorld).forEach(el=>{
|
|
|
+ let modified = new Date().valueOf();
|
|
|
+ let obj = {
|
|
|
+ 'file': emptyWorld[el],
|
|
|
+ 'modified': modified
|
|
|
+ }
|
|
|
+ worldsObj['empty'][el] = obj;
|
|
|
+ })
|
|
|
+
|
|
|
+ console.log(worldsObj);
|
|
|
+
|
|
|
+ Object.entries(worldsObj).forEach(el => {
|
|
|
+
|
|
|
+ let worldName = el[0];
|
|
|
+ let files = el[1];
|
|
|
+ Object.entries(files).forEach(file => {
|
|
|
+
|
|
|
+ _LCSDB.user().get('worlds').get(worldName).get(file[0]).put(file[1]);
|
|
|
+
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ //load defaults for first registered user running ./setup
|
|
|
+
|
|
|
+ HandleSetupIndex() {
|
|
|
+
|
|
|
+ window._app.hideProgressBar();
|
|
|
+ window._app.hideUIControl();
|
|
|
+
|
|
|
+ let el = document.createElement("div");
|
|
|
+ el.setAttribute("id", "admin");
|
|
|
+ document.body.appendChild(el);
|
|
|
+
|
|
|
+
|
|
|
+ _LCSDB.on('auth',
|
|
|
+ async function (ack) {
|
|
|
+
|
|
|
+ if (_LCSUSER.is) {
|
|
|
+
|
|
|
+ let setPubKey = {
|
|
|
+
|
|
|
+ $cell: true,
|
|
|
+ $components: [
|
|
|
+ {
|
|
|
+ $type: "p",
|
|
|
+ class: "mdc-typography--headline5",
|
|
|
+ $text: "1. Set app system user PUB key"
|
|
|
+ },
|
|
|
+ {
|
|
|
+ $type: "button",
|
|
|
+ class: "mdc-button mdc-button--raised",
|
|
|
+ $text: "Set app PUB key",
|
|
|
+ onclick: function (e) {
|
|
|
+ console.log("admin action");
|
|
|
+ _LCSDB.get('lcs/app').get('pub').put(_LCSUSER.is.pub);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ]
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ let adminComponents = [];
|
|
|
+
|
|
|
+
|
|
|
+ let defaultPub = await _LCSDB.get('lcs/app').get('pub').once().then();
|
|
|
+ if (!defaultPub) {
|
|
|
+ adminComponents.push(setPubKey);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (_LCSUSER.is.pub == defaultPub) {
|
|
|
+
|
|
|
+ let loadEmpty = {
|
|
|
+ $cell: true,
|
|
|
+ $components: [
|
|
|
+ {
|
|
|
+ $type: "p",
|
|
|
+ class: "mdc-typography--headline5",
|
|
|
+ $text: "3. Initialize empty World proto"
|
|
|
+ },
|
|
|
+ {
|
|
|
+ $type: "button",
|
|
|
+ id: "loadDefaults",
|
|
|
+ class: "mdc-button mdc-button--raised",
|
|
|
+ $text: "Init empty world",
|
|
|
+ onclick: function (e) {
|
|
|
+ console.log("admin action");
|
|
|
+ window._app.loadEmptyDefaultProto();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ let loadDefaults = {
|
|
|
+ $cell: true,
|
|
|
+ $components: [
|
|
|
+ {
|
|
|
+ $type: "p",
|
|
|
+ class: "mdc-typography--headline5",
|
|
|
+ $text: "4. Load Sample Worlds protos from server (optional)"
|
|
|
+ },
|
|
|
+ {
|
|
|
+ $type: "button",
|
|
|
+ id: "loadDefaults",
|
|
|
+ class: "mdc-button mdc-button--raised",
|
|
|
+ $text: "Load default worlds (from server)",
|
|
|
+ onclick: function (e) {
|
|
|
+ console.log("admin action");
|
|
|
+ window._app.loadWorldsDefaults();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+
|
|
|
+ let loadDefaultsProxy = {
|
|
|
+ $cell: true,
|
|
|
+ $components: [
|
|
|
+ {
|
|
|
+ $type: "p",
|
|
|
+ class: "mdc-typography--headline5",
|
|
|
+ $text: "3. Load VWF & A-Frame default components"
|
|
|
+ },
|
|
|
+ {
|
|
|
+ $type: "button",
|
|
|
+ class: "mdc-button mdc-button--raised",
|
|
|
+ $text: "Load defaults Proxy",
|
|
|
+ onclick: function (e) {
|
|
|
+ console.log("admin action");
|
|
|
+ window._app.loadProxyDefaults();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ adminComponents.push(setPubKey, loadDefaultsProxy, loadEmpty, loadDefaults);
|
|
|
+ }
|
|
|
+
|
|
|
+ document.querySelector("#admin").$cell({
|
|
|
+ $cell: true,
|
|
|
+ id: 'adminComponents',
|
|
|
+ $type: "div",
|
|
|
+ $components: adminComponents
|
|
|
+ });
|
|
|
+
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ //TODO: profile
|
|
|
+
|
|
|
+ HandleUserIndex(ctx) {
|
|
|
+
|
|
|
+ console.log("USER INDEX");
|
|
|
+ window._app.hideProgressBar();
|
|
|
+ window._app.hideUIControl();
|
|
|
+
|
|
|
+ _LCSDB.on('auth',
|
|
|
+ async function (ack) {
|
|
|
+ if(ack.pub){
|
|
|
+ document.querySelector("#profile")._status = "User: " + _LCSUSER.is.alias //+' pub: ' + _LCSUSER.is.pub;
|
|
|
+ document.querySelector("#profile").$update();
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ let el = document.createElement("div");
|
|
|
+ el.setAttribute("id", "userProfile");
|
|
|
+ document.body.appendChild(el);
|
|
|
+
|
|
|
+ let userProfile = {
|
|
|
+ $type: 'div',
|
|
|
+ id: "profile",
|
|
|
+ _status: "",
|
|
|
+ $init: function(){
|
|
|
+ this._status = "user is not signed in..."
|
|
|
+ },
|
|
|
+ $update: function(){
|
|
|
+ this.$components = [
|
|
|
+ {
|
|
|
+ $type: "h1",
|
|
|
+ class: "mdc-typography--headline4",
|
|
|
+ $text: this._status //"Profile for: " + _LCSUSER.is.alias
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ document.querySelector("#userProfile").$cell({
|
|
|
+ $cell: true,
|
|
|
+ $type: "div",
|
|
|
+ $components: [userProfile]
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ async HandleUserWorlds(ctx) {
|
|
|
+
|
|
|
+ console.log("USER WORLDS INDEX");
|
|
|
+ console.log(ctx.params);
|
|
|
+ let user = ctx.params.user;
|
|
|
+
|
|
|
+ page.redirect('/' + user + '/worlds/protos');
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ async HandleFileEdit(ctx) {
|
|
|
+
|
|
|
+ console.log("USER WORLD FILE EDIT");
|
|
|
+
|
|
|
+ let user = ctx.params.user;
|
|
|
+ let worldName = ctx.params.name;
|
|
|
+ let fileOriginal = ctx.params.file;
|
|
|
+ let type = ctx.params.type;
|
|
|
+
|
|
|
+ window._app.hideProgressBar();
|
|
|
+ window._app.hideUIControl();
|
|
|
+
|
|
|
+
|
|
|
+ _LCSDB.on('auth',
|
|
|
+ async function (ack) {
|
|
|
+
|
|
|
+ if (_LCSUSER.is) {
|
|
|
+
|
|
|
+ if (_LCSUSER.is.alias == user) {
|
|
|
+
|
|
|
+ var worldType = 'worlds';
|
|
|
+ var file = fileOriginal;
|
|
|
+ if (type == 'state') {
|
|
|
+ worldType = 'documents';
|
|
|
+ file = _app.helpers.replaceSubStringALL(fileOriginal, "~", '/');
|
|
|
+ }
|
|
|
+
|
|
|
+ let worldFile = await _LCSUSER.get(worldType).get(worldName).get(file).once().then();
|
|
|
+ if (worldFile) {
|
|
|
+ console.log(worldFile.file);
|
|
|
+
|
|
|
+ let el = document.createElement("div");
|
|
|
+ el.setAttribute("id", "worldFILE");
|
|
|
+ document.body.appendChild(el);
|
|
|
+
|
|
|
+
|
|
|
+ let aceEditorCell = {
|
|
|
+
|
|
|
+ $type: "div",
|
|
|
+ $components: [
|
|
|
+ {
|
|
|
+ class: "aceEditor",
|
|
|
+ id: "aceEditor",
|
|
|
+ //style: "width:1200px; height: 800px",
|
|
|
+ $type: "div",
|
|
|
+ $text: worldFile.file,
|
|
|
+ $init: function () {
|
|
|
+ var mode = "ace/mode/json";
|
|
|
+ if (file.includes('_yaml'))
|
|
|
+ mode = "ace/mode/yaml"
|
|
|
+ if (file.includes('_js'))
|
|
|
+ mode = "ace/mode/javascript"
|
|
|
+
|
|
|
+ var editor = ace.edit("aceEditor");
|
|
|
+ editor.setTheme("ace/theme/monokai");
|
|
|
+ editor.setFontSize(16);
|
|
|
+ editor.getSession().setMode(mode);
|
|
|
+ editor.setOptions({
|
|
|
+ maxLines: Infinity
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ $type: "button",
|
|
|
+ class: "mdc-button mdc-button--raised",
|
|
|
+ $text: "Save",
|
|
|
+ onclick: async function (e) {
|
|
|
+ console.log("save new info");
|
|
|
+ let editor = document.querySelector("#aceEditor").env.editor;
|
|
|
+ let newInfo = editor.getValue();
|
|
|
+ _LCSUSER.get(worldType).get(worldName).get(file).get('file').put(newInfo, res => {
|
|
|
+ if (res) {
|
|
|
+ let modified = new Date().valueOf();
|
|
|
+ _LCSUSER.get(worldType).get(worldName).get(file).get('modified').put(modified);
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ $type: "button",
|
|
|
+ class: "mdc-button mdc-button--raised",
|
|
|
+ $text: "Close",
|
|
|
+ onclick: function (e) {
|
|
|
+ console.log("close");
|
|
|
+ if (type == "proto")
|
|
|
+ window.location.pathname = "/" + user + '/worlds/protos'
|
|
|
+ if (type == "state")
|
|
|
+ window.location.pathname = "/" + user + '/worlds/states'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ document.querySelector("#worldFILE").$cell({
|
|
|
+ $cell: true,
|
|
|
+ $type: "div",
|
|
|
+ $components: [aceEditorCell
|
|
|
+ ]
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ async HandleUserWorldsWithType(ctx) {
|
|
|
+
|
|
|
+ console.log("USER WORLDS INDEX");
|
|
|
+ console.log(ctx.params);
|
|
|
+ let user = ctx.params.user;
|
|
|
+ let type = ctx.params.type;
|
|
|
+
|
|
|
+ window._app.hideProgressBar();
|
|
|
+ window._app.hideUIControl();
|
|
|
+
|
|
|
+ if (!_app.indexApp) {
|
|
|
+ _app.indexApp = new IndexApp;
|
|
|
+ document.querySelector('head').innerHTML += '<link rel="stylesheet" href="/web/index-app.css">';
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!document.querySelector('#app')) {
|
|
|
+ await _app.indexApp.initApp();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (type == 'protos') {
|
|
|
+ await _app.indexApp.getWorldsProtosFromUserDB(user);
|
|
|
+ } else if (type == 'states') {
|
|
|
+ await _app.indexApp.getWorldsFromUserDB(user);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ async HandleIndex() {
|
|
|
+
|
|
|
+ console.log("INDEX");
|
|
|
+
|
|
|
+ window._app.hideProgressBar();
|
|
|
+ window._app.hideUIControl();
|
|
|
+
|
|
|
+ _app.indexApp = new IndexApp;
|
|
|
+ document.querySelector('head').innerHTML += '<link rel="stylesheet" href="/web/index-app.css">';
|
|
|
+
|
|
|
+ await _app.indexApp.generateFrontPage();
|
|
|
+ await _app.indexApp.initApp();
|
|
|
+ await _app.indexApp.getAppDetailsFromDB();
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ HandleNoPage() {
|
|
|
+
|
|
|
+ console.log("no such page")
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ //handle parcable requests
|
|
|
+
|
|
|
+ HandleParsableLoadRequest(ctx) {
|
|
|
+
|
|
|
+ let app = window._app;
|
|
|
+ console.log(ctx.params);
|
|
|
+ //var pathname = ctx.pathname;
|
|
|
+ var spaceName = ctx.params.space;
|
|
|
+ var saveName = ctx.params.savename;
|
|
|
+ let user = ctx.params.user;
|
|
|
+
|
|
|
+ page.redirect('/' + user + '/' + spaceName + '/' + app.helpers.GenerateInstanceID() + '/load/' + saveName);
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ HandleParsableLoadRequestWithRev(ctx) {
|
|
|
+
|
|
|
+ let app = window._app;
|
|
|
+ console.log(ctx.params);
|
|
|
+ //var pathname = ctx.pathname;
|
|
|
+ var spaceName = ctx.params.space;
|
|
|
+ var saveName = ctx.params.savename;
|
|
|
+ var rev = ctx.params.rev;
|
|
|
+ let user = ctx.params.user;
|
|
|
+
|
|
|
+ page.redirect('/' + user + '/' + spaceName + '/' + app.helpers.GenerateInstanceID() + '/load/' + saveName + '/' + rev);
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ async setUserPaths(user) {
|
|
|
+
|
|
|
+ await _LCSDB.get('users').get(user).get('pub').once(res => {
|
|
|
+ if (res)
|
|
|
+ window._LCS_WORLD_USER = _LCSDB.user(res);
|
|
|
+ }).then();
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ async HandleParsableRequestGenID(ctx) {
|
|
|
+
|
|
|
+ let app = window._app;
|
|
|
+ console.log(ctx.params);
|
|
|
+ let user = ctx.params.user;
|
|
|
+ var pathname = ctx.pathname;
|
|
|
+
|
|
|
+ await app.setUserPaths(user);
|
|
|
+
|
|
|
+ if (pathname[pathname.length - 1] == '/') {
|
|
|
+ pathname = pathname.slice(0, -1)
|
|
|
+ }
|
|
|
+
|
|
|
+ let pathToParse = pathname.replace('/' + user, "");
|
|
|
+
|
|
|
+ app.helpers.Process(pathToParse).then(parsedRequest => {
|
|
|
+
|
|
|
+ localStorage.setItem('lcs_app', JSON.stringify({ path: parsedRequest }));
|
|
|
+ console.log(parsedRequest);
|
|
|
+
|
|
|
+ if ((parsedRequest['instance'] == undefined) && (parsedRequest['private_path'] == undefined) && (parsedRequest['public_path'] !== "/") && (parsedRequest['application'] !== undefined)) {
|
|
|
+
|
|
|
+ page.redirect(pathname + '/' + app.helpers.GenerateInstanceID());
|
|
|
+
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ async HandleParsableRequestWithID(ctx) {
|
|
|
+
|
|
|
+ let app = window._app;
|
|
|
+ console.log(ctx.params);
|
|
|
+ var pathname = ctx.pathname;
|
|
|
+ let user = ctx.params.user;
|
|
|
+
|
|
|
+ if (pathname[pathname.length - 1] == '/') {
|
|
|
+ pathname = pathname.slice(0, -1)
|
|
|
+ }
|
|
|
+
|
|
|
+ await app.setUserPaths(user);
|
|
|
+
|
|
|
+ let pathToParse = pathname.replace('/' + user, "");
|
|
|
+
|
|
|
+ app.helpers.Process(pathToParse).then(parsedRequest => {
|
|
|
+
|
|
|
+ localStorage.setItem('lcs_app', JSON.stringify({ path: parsedRequest }));
|
|
|
+ console.log(parsedRequest);
|
|
|
+
|
|
|
+ var userLibraries = { model: {}, view: {} };
|
|
|
+ var application;
|
|
|
+
|
|
|
+ vwf.loadConfiguration(application, userLibraries, compatibilityCheck);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ async HandleParsableRequest(ctx) {
|
|
|
+
|
|
|
+ let app = window._app;
|
|
|
+ console.log(ctx.params);
|
|
|
+ var pathname = ctx.pathname;
|
|
|
+
|
|
|
+ if (pathname[pathname.length - 1] == '/') {
|
|
|
+ pathname = pathname.slice(0, -1)
|
|
|
+ }
|
|
|
+
|
|
|
+ var parsedRequest = await app.helpers.Process(pathname);
|
|
|
+ localStorage.setItem('lcs_app', JSON.stringify({ path: parsedRequest }));
|
|
|
+ console.log(parsedRequest);
|
|
|
+
|
|
|
+ if ((parsedRequest['instance'] == undefined) && (parsedRequest['private_path'] == undefined) && (parsedRequest['public_path'] !== "/") && (parsedRequest['application'] !== undefined)) {
|
|
|
+
|
|
|
+ // Redirect if the url request does not include an application/file && a default 'index.vwf.yaml' exists
|
|
|
+ // page.redirect(pathname + '/' + app.helpers.GenerateInstanceID());
|
|
|
+ window.location.pathname = pathname + '/' + app.helpers.GenerateInstanceID()
|
|
|
+
|
|
|
+ //return true;
|
|
|
+ } else {
|
|
|
+
|
|
|
+ //return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ var userLibraries = { model: {}, view: {} };
|
|
|
+ var application;
|
|
|
+
|
|
|
+ vwf.loadConfiguration(application, userLibraries, compatibilityCheck);
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ //get DB application state information for reflector (called from VWF)
|
|
|
+
|
|
|
+ async getApplicationState() {
|
|
|
+
|
|
|
+ let dataJson = JSON.parse(localStorage.getItem('lcs_app'));
|
|
|
+ if (dataJson) {
|
|
|
+ if (!dataJson.path['instance']) return undefined;
|
|
|
+ }
|
|
|
+
|
|
|
+ let userAlias = await _LCS_WORLD_USER.get('alias').once().then();
|
|
|
+ let userPub = await _LCSDB.get('users').get(userAlias).get('pub').once().then();
|
|
|
+
|
|
|
+ let loadInfo = await this.getLoadInformation(dataJson);
|
|
|
+ let saveInfo = await this.loadSaveObject(loadInfo);
|
|
|
+ let loadObj = {
|
|
|
+ loadInfo: loadInfo,
|
|
|
+ path: dataJson.path,
|
|
|
+ saveObject: saveInfo,
|
|
|
+ user: userAlias
|
|
|
+ }
|
|
|
+ console.log(loadObj);
|
|
|
+
|
|
|
+ //temporary solution for syncing DB replicas using Gun.load()
|
|
|
+ await _LCS_SYS_USER.get('proxy').load(res=>{}, {wait: 200}).then();
|
|
|
+ await _LCSDB.user(userPub).get('worlds').get(loadObj.path.public_path.slice(1)).load(res=>{}, {wait: 200}).then();
|
|
|
+
|
|
|
+ return loadObj
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ // LookupSaveRevisions takes the public path and the name of a save, and provides
|
|
|
+ // an array of all revisions for that save. (If the save does not exist, this will be
|
|
|
+ // an empty array).
|
|
|
+
|
|
|
+ async lookupSaveRevisions(public_path, save_name) {
|
|
|
+ var result = [];
|
|
|
+ var states = [];
|
|
|
+ let docName = 'savestate_/' + public_path + '/' + save_name + '_vwf_json';
|
|
|
+
|
|
|
+ let revs = await _LCS_WORLD_USER.get('documents').get(public_path).get(docName).get('revs').once().then();
|
|
|
+ if (revs) {
|
|
|
+ for (const res of Object.keys(revs)) {
|
|
|
+ if (res !== '_') {
|
|
|
+ let el = await _LCS_WORLD_USER.get('documents').get(public_path).get(docName).get('revs').get(res).once().then();
|
|
|
+ if (el)
|
|
|
+ result.push(parseInt(el.revision));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return result
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // GetLoadInformation receives a parsed request {private_path, public_path, instance, application} and returns the
|
|
|
+ // details of the save that is designated by the initial request. The details are returned in an object
|
|
|
+ // composed of: save_name (name of the save) save_revision (revision of the save), explicit_revision (boolean, true if the request
|
|
|
+ // explicitly specified the revision, false if it did not), and application_path (the public_path of the application this is a save for).
|
|
|
+
|
|
|
+ async getLoadInformation(response) {
|
|
|
+ let parsedRequest = response.path;
|
|
|
+ var result = { 'save_name': undefined, 'save_revision': undefined, 'explicit_revision': undefined, 'application_path': undefined };
|
|
|
+ if (parsedRequest['private_path']) {
|
|
|
+ var segments = this.helpers.GenerateSegments(parsedRequest['private_path']);
|
|
|
+ if ((segments.length > 1) && (segments[0] == "load")) {
|
|
|
+ var potentialRevisions = await this.lookupSaveRevisions((parsedRequest['public_path']).slice(1), segments[1]);
|
|
|
+
|
|
|
+ console.log('!!!!! - ', potentialRevisions);
|
|
|
+ if (potentialRevisions.length > 0) {
|
|
|
+ result['save_name'] = segments[1];
|
|
|
+
|
|
|
+ if (segments.length > 2) {
|
|
|
+ var requestedRevision = parseInt(segments[2]);
|
|
|
+ if (requestedRevision) {
|
|
|
+ if (potentialRevisions.indexOf(requestedRevision) > -1) {
|
|
|
+ result['save_revision'] = requestedRevision;
|
|
|
+ result['explicit_revision'] = true;
|
|
|
+ result['application_path'] = parsedRequest['public_path'];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (result['explicit_revision'] == undefined) {
|
|
|
+ result['explicit_revision'] = false;
|
|
|
+ potentialRevisions.sort();
|
|
|
+ result['save_revision'] = potentialRevisions.pop();
|
|
|
+ result['application_path'] = parsedRequest['public_path'];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ async loadSaveObject(loadInfo) {
|
|
|
+
|
|
|
+ //let objName = loadInfo[ 'save_name' ] +'/'+ "savestate_" + loadInfo[ 'save_revision' ];
|
|
|
+
|
|
|
+ let objName = "savestate_" + loadInfo['application_path'] + '/' + loadInfo['save_name'] + '_vwf_json';
|
|
|
+ let objNameRev = "savestate_" + loadInfo['save_revision'] + loadInfo['application_path'] + '/' + loadInfo['save_name'] + '_vwf_json';
|
|
|
+
|
|
|
+ // if(loadInfo[ 'save_revision' ]){
|
|
|
+ // }
|
|
|
+
|
|
|
+ let worldName = this.helpers.appPath //loadInfo[ 'application_path' ].slice(1);
|
|
|
+
|
|
|
+ let saveObject = await _LCS_WORLD_USER.get('documents').get(worldName).get(objName).get('revs').get(objNameRev).once().then();
|
|
|
+ let saveInfo = saveObject ? JSON.parse(saveObject.jsonState) : saveObject;
|
|
|
+
|
|
|
+ return saveInfo;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ // GetSaveInformation is a helper function that takes the application_path (/path/to/application).
|
|
|
+ // It returns an array of all saves found for that
|
|
|
+ // application (including separate entries for individual revisions of saves ).
|
|
|
+ async getSaveInformation(application_path, userPUB) {
|
|
|
+ var result = [];
|
|
|
+ let user = _LCSDB.user(userPUB);
|
|
|
+
|
|
|
+ var docName = application_path.slice(1);
|
|
|
+ let potentialSaveNames = await user.get('documents').get(docName).once().then();
|
|
|
+
|
|
|
+ if (potentialSaveNames) {
|
|
|
+
|
|
|
+ for (const res of Object.keys(potentialSaveNames)) {
|
|
|
+ if (res !== '_') {
|
|
|
+
|
|
|
+ let el = await user.get('documents').path(docName).get(res).once().then();
|
|
|
+
|
|
|
+ let revisionList = await this.lookupSaveRevisions(application_path.slice(1), el.filename);
|
|
|
+ var latestsave = true;
|
|
|
+ revisionList.sort();
|
|
|
+ while (revisionList.length > 0) {
|
|
|
+ var newEntry = {};
|
|
|
+ newEntry['applicationpath'] = application_path;
|
|
|
+ newEntry['savename'] = el.filename;
|
|
|
+ newEntry['revision'] = revisionList.pop().toString();
|
|
|
+ newEntry['latestsave'] = latestsave;
|
|
|
+ if (latestsave) {
|
|
|
+ newEntry['url'] = this.helpers.JoinPath(window.location.origin, application_path, "load", el.filename + "/");
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ newEntry['url'] = this.helpers.JoinPath(window.location.origin, application_path, "load", el.filename + "/", newEntry['revision'] + "/");
|
|
|
+ }
|
|
|
+ latestsave = false;
|
|
|
+ result.push(newEntry);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ async getProtoWorldFiles(userPub, worldName, date) {
|
|
|
+
|
|
|
+ let fileNamesAll = await _LCSDB.user(userPub).get('worlds').get(worldName).once().then();
|
|
|
+ let worldFileNames = Object.keys(fileNamesAll).filter(el => (el !== '_') && (el !== 'owner') && (el !== 'parent')
|
|
|
+ && (el !== 'info_json'));
|
|
|
+
|
|
|
+ let worldObj = {};
|
|
|
+ for (var el in worldFileNames) {
|
|
|
+ let fn = worldFileNames[el];
|
|
|
+ let res = await _LCSDB.user(userPub).get('worlds').get(worldName).get(fn).once().then();
|
|
|
+ var data = {
|
|
|
+ 'file': res.file,
|
|
|
+ 'modified': res.modified
|
|
|
+ }
|
|
|
+ if (!date) {
|
|
|
+ data = {
|
|
|
+ 'file': res.file
|
|
|
+ }
|
|
|
+ }
|
|
|
+ worldObj[fn] = data;
|
|
|
+ }
|
|
|
+ console.log(worldObj);
|
|
|
+ return worldObj
|
|
|
+ }
|
|
|
+
|
|
|
+ async cloneWorldPrototype(worldName, userName, newWorldName) {
|
|
|
+
|
|
|
+ let userPub = await _LCSDB.get('users').get(userName).get('pub').once().then();
|
|
|
+ //let worldProto = await _LCSDB.user(userPub).get('worlds').get(worldName).once().then();
|
|
|
+
|
|
|
+ var worldID = window._app.helpers.GenerateInstanceID().toString();
|
|
|
+ if (newWorldName) {
|
|
|
+ worldID = newWorldName
|
|
|
+ }
|
|
|
+
|
|
|
+ //let modified = new Date().valueOf();
|
|
|
+ console.log('clone: ' + worldName + 'to: ' + worldID);
|
|
|
+
|
|
|
+ let newOwner = _LCSUSER.is.pub;
|
|
|
+
|
|
|
+ let worldObj = {
|
|
|
+ 'owner': newOwner,
|
|
|
+ 'parent': userName + '/' + worldName
|
|
|
+ };
|
|
|
+
|
|
|
+ let fileNamesAll = await _LCSDB.user(userPub).get('worlds').get(worldName).once().then();
|
|
|
+ let worldFileNames = Object.keys(fileNamesAll).filter(el => (el !== '_') && (el !== 'owner') && (el !== 'parent'));
|
|
|
+
|
|
|
+ for (var el in worldFileNames) {
|
|
|
+
|
|
|
+ let fn = worldFileNames[el];
|
|
|
+ let res = await _LCSDB.user(userPub).get('worlds').get(worldName).get(fn).once().then();
|
|
|
+ let data = {
|
|
|
+ 'file': res.file,
|
|
|
+ 'modified': res.modified
|
|
|
+ }
|
|
|
+ worldObj[fn] = data;
|
|
|
+ }
|
|
|
+ console.log(worldObj);
|
|
|
+
|
|
|
+ Object.keys(worldObj).forEach(el => {
|
|
|
+ _LCSUSER.get('worlds').get(worldID).get(el).put(worldObj[el]);
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ async cloneWorldState(filename) {
|
|
|
+
|
|
|
+ let myWorldProtos = await _LCSUSER.get('worlds').once().then();
|
|
|
+ let userName = this.helpers.worldUser;
|
|
|
+ let userPub = await _LCSDB.get('users').get(userName).get('pub').once().then();
|
|
|
+ let protoUserRoot = this.helpers.getRoot(true).root;
|
|
|
+ //let myName = _LCSUSER.is.alias;
|
|
|
+
|
|
|
+ //let proto = Object.keys(myWorldProtos).filter(el => el == protoUserRoot);
|
|
|
+
|
|
|
+ var protosKeys = [];
|
|
|
+ if (myWorldProtos)
|
|
|
+ protosKeys = Object.keys(myWorldProtos);
|
|
|
+
|
|
|
+ if (protosKeys.includes(protoUserRoot)) {
|
|
|
+
|
|
|
+ let userProtoFiles = await this.getProtoWorldFiles(userPub, protoUserRoot);
|
|
|
+ let myProtoFiles = await this.getProtoWorldFiles(_LCSUSER.is.pub, protoUserRoot);
|
|
|
+
|
|
|
+ let hashUP = await this.helpers.sha256(JSON.stringify(userProtoFiles));
|
|
|
+ let hashMP = await this.helpers.sha256(JSON.stringify(myProtoFiles));
|
|
|
+
|
|
|
+ if (hashUP == hashMP) {
|
|
|
+ this.saveStateAsFile(filename);
|
|
|
+ } else {
|
|
|
+
|
|
|
+ let noty = new Noty({
|
|
|
+ text: 'world prototype is modified.. could not clone world state',
|
|
|
+ timeout: 2000,
|
|
|
+ theme: 'mint',
|
|
|
+ layout: 'bottomRight',
|
|
|
+ type: 'error'
|
|
|
+ });
|
|
|
+ noty.show();
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ await this.cloneWorldPrototype(protoUserRoot, userName, protoUserRoot);
|
|
|
+ this.saveStateAsFile(filename);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //TODO: refactor and config save
|
|
|
+ saveStateAsFile(filename, otherProto) // invoke with the view as "this"
|
|
|
+ {
|
|
|
+ console.log("Saving: " + filename);
|
|
|
+
|
|
|
+ //var clients = this.nodes["http://vwf.example.com/clients.vwf"];
|
|
|
+
|
|
|
+ // Save State Information
|
|
|
+ var state = vwf.getState();
|
|
|
+ state.nodes[0].children = {};
|
|
|
+
|
|
|
+ var timestamp = state["queue"].time;
|
|
|
+ timestamp = Math.round(timestamp * 1000);
|
|
|
+
|
|
|
+ var objectIsTypedArray = function (candidate) {
|
|
|
+ var typedArrayTypes = [
|
|
|
+ Int8Array,
|
|
|
+ Uint8Array,
|
|
|
+ // Uint8ClampedArray,
|
|
|
+ Int16Array,
|
|
|
+ Uint16Array,
|
|
|
+ Int32Array,
|
|
|
+ Uint32Array,
|
|
|
+ Float32Array,
|
|
|
+ Float64Array
|
|
|
+ ];
|
|
|
+
|
|
|
+ var isTypedArray = false;
|
|
|
+
|
|
|
+ if (typeof candidate == "object" && candidate != null) {
|
|
|
+ typedArrayTypes.forEach(function (typedArrayType) {
|
|
|
+ isTypedArray = isTypedArray || candidate instanceof typedArrayType;
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ return isTypedArray;
|
|
|
+ };
|
|
|
+
|
|
|
+ var transitTransformation = function (object) {
|
|
|
+ return objectIsTypedArray(object) ?
|
|
|
+ Array.prototype.slice.call(object) : object;
|
|
|
+ };
|
|
|
+
|
|
|
+
|
|
|
+ let jsonValuePure = require("vwf/utility").transform(
|
|
|
+ state, transitTransformation
|
|
|
+ );
|
|
|
+
|
|
|
+ //remove all Ohm generated grammarsfrom state
|
|
|
+
|
|
|
+ let jsonValue = _app.helpers.removeGrammarObj(jsonValuePure);
|
|
|
+ var jsonState = JSON.stringify(jsonValue);
|
|
|
+
|
|
|
+ let rootPath = this.helpers.getRoot(true);
|
|
|
+
|
|
|
+ var inst = rootPath.inst;
|
|
|
+ if (filename == '') filename = inst;
|
|
|
+ //if (root.indexOf('.vwf') != -1) root = root.substring(0, root.lastIndexOf('/'));
|
|
|
+ var root = rootPath.root;
|
|
|
+
|
|
|
+ var json = jsonState;
|
|
|
+
|
|
|
+ if (otherProto) {
|
|
|
+
|
|
|
+ console.log('need to modify state...');
|
|
|
+ json = this.helpers.replaceSubStringALL(jsonState, '/' + root + '/', '/' + otherProto + '/');//jsonState.replace(('/' + root + '/'), ('/' + otherProto +'/') );
|
|
|
+ root = otherProto;
|
|
|
+ console.log(json);
|
|
|
+ }
|
|
|
+
|
|
|
+ //var documents = _LCSUSER.get('documents');
|
|
|
+
|
|
|
+ var saveRevision = new Date().valueOf();
|
|
|
+
|
|
|
+ var stateForStore = {
|
|
|
+ "root": root,
|
|
|
+ "filename": filename,
|
|
|
+ "inst": inst,
|
|
|
+ "timestamp": timestamp,
|
|
|
+ "extension": ".vwf.json",
|
|
|
+ "jsonState": json,
|
|
|
+ "publish": true
|
|
|
+ };
|
|
|
+
|
|
|
+ //let objName = loadInfo[ 'save_name' ] +'/'+ "savestate_" + loadInfo[ 'save_revision' ];
|
|
|
+ // "savestate_" + loadInfo[ 'save_revision' ] + '/' + loadInfo[ 'save_name' ] + '_vwf_json'
|
|
|
+
|
|
|
+ var docName = 'savestate_/' + root + '/' + filename + '_vwf_json';
|
|
|
+ _LCSUSER.get('documents').get(root).get(docName).put(stateForStore, res => {
|
|
|
+
|
|
|
+ if (res) {
|
|
|
+
|
|
|
+ let noty = new Noty({
|
|
|
+ text: 'Saved to ' + docName,
|
|
|
+ timeout: 2000,
|
|
|
+ theme: 'mint',
|
|
|
+ layout: 'bottomRight',
|
|
|
+ type: 'success'
|
|
|
+ });
|
|
|
+ noty.show();
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ _LCSUSER.get('worlds').get(root).get('info_json').once(res => {
|
|
|
+
|
|
|
+ if (res) {
|
|
|
+
|
|
|
+ let modified = new Date().valueOf();
|
|
|
+ let newOwner = _LCSUSER.is.pub;
|
|
|
+ let userName = _LCSUSER.is.alias;
|
|
|
+
|
|
|
+ let obj = {
|
|
|
+ 'parent': userName + '/' + root,
|
|
|
+ 'owner': newOwner,
|
|
|
+ 'file': res.file,
|
|
|
+ 'modified': modified
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ let docInfoName = 'savestate_/' + root + '/' + filename + '_info_vwf_json';
|
|
|
+
|
|
|
+ _LCSUSER.get('documents').get(root).get(docInfoName).not(res => {
|
|
|
+ _LCSUSER.get('documents').get(root).get(docInfoName).put(obj);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ var docNameRev = 'savestate_' + saveRevision.toString() + '/' + root + '/' + filename + '_vwf_json';
|
|
|
+ _LCSUSER.get('documents').get(root).get(docName).get('revs').get(docNameRev).put(stateForStore)
|
|
|
+ .path("revision").put(saveRevision);
|
|
|
+
|
|
|
+
|
|
|
+ // Save Config Information
|
|
|
+ var config = { "info": {}, "model": {}, "view": {} };
|
|
|
+
|
|
|
+ // Save browser title
|
|
|
+ config["info"]["title"] = document.title//$('title').html();
|
|
|
+
|
|
|
+ // Save model drivers
|
|
|
+ Object.keys(vwf_view.kernel.kernel.models).forEach(function (modelDriver) {
|
|
|
+ if (modelDriver.indexOf('vwf/model/') != -1) config["model"][modelDriver] = "";
|
|
|
+ });
|
|
|
+
|
|
|
+ // If neither glge or threejs model drivers are defined, specify nodriver
|
|
|
+ if (config["model"]["vwf/model/glge"] === undefined && config["model"]["vwf/model/threejs"] === undefined) config["model"]["nodriver"] = "";
|
|
|
+
|
|
|
+ // Save view drivers and associated parameters, if any
|
|
|
+ Object.keys(vwf_view.kernel.kernel.views).forEach(function (viewDriver) {
|
|
|
+ if (viewDriver.indexOf('vwf/view/') != -1) {
|
|
|
+ if (vwf_view.kernel.kernel.views[viewDriver].parameters) {
|
|
|
+ config["view"][viewDriver] = vwf_view.kernel.kernel.views[viewDriver].parameters;
|
|
|
+ }
|
|
|
+ else config["view"][viewDriver] = "";
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ //var jsonConfig = $.encoder.encodeForURL(JSON.stringify(config));
|
|
|
+ var jsonConfig = JSON.stringify(config);
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ let configStateForStore = {
|
|
|
+ "root": root,
|
|
|
+ "filename": filename,
|
|
|
+ "inst": inst,
|
|
|
+ "timestamp": timestamp,
|
|
|
+ "extension": "config.vwf.json",
|
|
|
+ "jsonState": jsonConfig
|
|
|
+ };
|
|
|
+
|
|
|
+ //let objName = loadInfo[ 'save_name' ] +'/'+ "savestate_" + loadInfo[ 'save_revision' ];
|
|
|
+ // "savestate_" + loadInfo[ 'save_revision' ] + '/' + loadInfo[ 'save_name' ] + '_vwf_json'
|
|
|
+
|
|
|
+ // let configName = 'savestate_/' + root + '/' + filename + '_config_vwf_json';
|
|
|
+ // let documentSaveConfigState = _LCSUSER.get(configName).put(configStateForStore);
|
|
|
+ // //documents.path(root).set(documentSaveConfigState);
|
|
|
+
|
|
|
+ // let configNameRev = 'savestate_' + saveRevision.toString() + '/' + root + '/' + filename + '_config_vwf_json';
|
|
|
+ // _LCSUSER.get(configNameRev).put(configStateForStore);
|
|
|
+ // _LCSUSER.get(configNameRev).path("revision").put(saveRevision);
|
|
|
+
|
|
|
+ //documentSaveConfigState.path('revs').set(documentSaveStateRevision);
|
|
|
+
|
|
|
+
|
|
|
+ // Save config file to server
|
|
|
+ // var xhrConfig = new XMLHttpRequest();
|
|
|
+ // xhrConfig.open("POST", "/" + root + "/save/" + filename, true);
|
|
|
+ // xhrConfig.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
|
|
|
+ // xhrConfig.send("root=" + root + "/" + filename + "&filename=saveState&inst=" + inst + "×tamp=" + timestamp + "&extension=.vwf.config.json" + "&jsonState=" + jsonConfig);
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ // LoadSavedState
|
|
|
+
|
|
|
+ async loadSavedState(filename, applicationpath, revision) {
|
|
|
+ console.log("Loading: " + filename);
|
|
|
+
|
|
|
+ let userName = await _LCS_WORLD_USER.get('alias').once().then();
|
|
|
+
|
|
|
+ if (revision) {
|
|
|
+ window.location.pathname = '/' + userName + applicationpath + '/load/' + filename + '/' + revision + '/';
|
|
|
+ }
|
|
|
+ else { // applicationpath + "/" + inst + '/load/' + filename + '/';
|
|
|
+ window.location.pathname = '/' + userName + applicationpath + '/load/' + filename + '/';
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ hideUIControl() {
|
|
|
+
|
|
|
+ var el = document.getElementById("ui-controls");
|
|
|
+ if (el) {
|
|
|
+ el.classList.remove("visible");
|
|
|
+ el.classList.add("not-visible");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ showUIControl() {
|
|
|
+
|
|
|
+ var el = document.getElementById("ui-controls");
|
|
|
+ if (el) {
|
|
|
+ el.classList.remove("not-visible");
|
|
|
+ el.classList.add("visible");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ hideProgressBar() {
|
|
|
+
|
|
|
+ var progressbar = document.getElementById("load-progressbar");
|
|
|
+ if (progressbar) {
|
|
|
+ progressbar.classList.remove("visible");
|
|
|
+ progressbar.classList.add("not-visible");
|
|
|
+ progressbar.classList.add("mdc-linear-progress--closed");
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ showProgressBar() {
|
|
|
+
|
|
|
+ let progressbar = document.getElementById("load-progressbar");
|
|
|
+ if (progressbar) {
|
|
|
+ progressbar.classList.add("visible");
|
|
|
+ progressbar.classList.add("mdc-linear-progress--indeterminate");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+export { App }
|