app.js 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340
  1. import page from '/lib/page.mjs';
  2. import { Lang } from '/lib/polyglot/language.js';
  3. import { Helpers } from '/helpers.js';
  4. import { IndexApp } from '/web/index-app.js';
  5. import { Widgets } from '/lib/widgets.js';
  6. class App {
  7. constructor() {
  8. console.log("app constructor");
  9. this.widgets = new Widgets;
  10. //globals
  11. window._app = this;
  12. window._cellWidgets = this.widgets;
  13. window._LangManager = new Lang;
  14. window._noty = new Noty;
  15. _LangManager.setLanguage().then(res => {
  16. return this.initDB()
  17. }).then(res => {
  18. this.helpers = new Helpers;
  19. this.initUser();
  20. //client routes
  21. page('/', this.HandleIndex);
  22. page('/setup', this.HandleSetupIndex);
  23. page('/settings', this.HandleSettingsIndex);
  24. page('/profile', this.HandleUserIndex);
  25. page('/worlds', this.HandleIndex);
  26. page('/:user/worlds', this.HandleUserWorlds);
  27. page('/:user/worlds/:type', this.HandleUserWorldsWithType);
  28. page('/:user/:type/:name/edit/:file', this.HandleFileEdit);
  29. page('/:user/:space', this.HandleParsableRequestGenID);
  30. page('/:user/:space/:id', this.HandleParsableRequestWithID);
  31. page('/:user/:space/index.vwf/:id', this.HandleParsableRequestWithID);
  32. page('/:user/:space/load/:savename', this.HandleParsableLoadRequest);
  33. page('/:user/:space/:id/load/:savename', this.HandleParsableRequestWithID);
  34. page('/:user/:space/load/:savename/:rev', this.HandleParsableLoadRequestWithRev);
  35. page('/:user/:space/:id/load/:savename/:rev', this.HandleParsableRequestWithID);
  36. page('*', this.HandleNoPage);
  37. page();
  38. })
  39. }
  40. initDB() {
  41. var config = JSON.parse(localStorage.getItem('lcs_config'));
  42. if (!config) {
  43. config = {
  44. 'dbhost': 'https://' + window.location.hostname + ':8080/gun', //'http://localhost:8080/gun',
  45. 'reflector': 'https://' + window.location.hostname + ':3002',
  46. 'language': 'en'
  47. }
  48. localStorage.setItem('lcs_config', JSON.stringify(config));
  49. }
  50. const dbConnection = new Promise((resolve, reject) => {
  51. this.db = Gun(this.dbHost);
  52. this.user = this.db.user();
  53. window._LCSDB = this.db;
  54. window._LCSUSER = this.user;
  55. window._LCS_SYS_USER = undefined;
  56. window._LCS_WORLD_USER = undefined;
  57. _LCSDB.get('lcs/app').get('pub').once(res => {
  58. if (res) {
  59. window._LCS_SYS_USER = this.db.user(res);
  60. }
  61. });
  62. _LCSDB.on('hi', function (peer) {
  63. let msg = 'Connected to ' + peer.url;
  64. let noty = new Noty({
  65. text: msg,
  66. timeout: 2000,
  67. theme: 'mint',
  68. layout: 'bottomRight',
  69. type: 'success'
  70. });
  71. noty.show();
  72. console.log(msg)
  73. })
  74. _LCSDB.on('bye', function (peer) {
  75. let msg = 'No connection to ' + peer.url;
  76. let noty = new Noty({
  77. text: msg,
  78. timeout: 1000,
  79. theme: 'mint',
  80. layout: 'bottomRight',
  81. type: 'error'
  82. });
  83. noty.show();
  84. console.log(msg)
  85. })
  86. resolve('ok');
  87. });
  88. return dbConnection
  89. }
  90. initUser() {
  91. _LCSUSER.recall({ sessionStorage: 1 });
  92. }
  93. get reflectorHost() {
  94. var res = "";
  95. let config = localStorage.getItem('lcs_config');
  96. if (config) {
  97. res = JSON.parse(config).reflector;
  98. }
  99. return res;
  100. }
  101. get dbHost() {
  102. var res = "";
  103. let config = localStorage.getItem('lcs_config');
  104. if (config) {
  105. res = JSON.parse(config).dbhost;
  106. }
  107. return res;
  108. }
  109. async loadProxyDefaults() {
  110. //load to DB default proxy files (VWF & A-Frame components)
  111. let proxyResponse = await fetch('/proxy-files', { method: 'get' });
  112. let proxyFiles = await proxyResponse.json();
  113. let filterProxyFiles = proxyFiles.filter(el => (el !== null));
  114. console.log(filterProxyFiles);
  115. var origin = window.location.origin;
  116. //var userPub = _LCSUSER.is.pub;
  117. let proxyObj = {};
  118. for (var index in filterProxyFiles) {
  119. let el = filterProxyFiles[index];
  120. if (el) {
  121. var url = origin + el;
  122. var entryName = url.replace(origin + '/defaults/', "").split(".").join("_");
  123. let proxyFile = await fetch(url, { method: 'get' });
  124. let responseText = await proxyFile.text();
  125. if (responseText) {
  126. let created = new Date().valueOf();
  127. let obj = {
  128. //'owner': userPub,
  129. 'file': responseText,
  130. 'modified': created,
  131. 'created': created
  132. }
  133. proxyObj[entryName] = obj;
  134. }
  135. }
  136. }
  137. console.log(proxyObj);
  138. Object.keys(proxyObj).forEach(el => {
  139. _LCSDB.user().get('proxy').get(el).put(proxyObj[el]);
  140. })
  141. }
  142. async loadWorldsDefaults() {
  143. //load to DB default worlds
  144. let worldsResponse = await fetch('/world-files', { method: 'get' });
  145. let worldFiles = await worldsResponse.json();
  146. let filterworldFiles = worldFiles.filter(el => (el !== null));
  147. console.log(filterworldFiles);
  148. let worldsObj = {};
  149. for (var index in filterworldFiles) {
  150. let el = filterworldFiles[index];
  151. if (el) {
  152. let url = window.location.origin + el;
  153. var entryName = url.replace(window.location.origin + '/defaults/worlds/', "").split(".").join("_");
  154. let worldName = entryName.split("/")[0];
  155. let userPub = _LCSUSER.is.pub;
  156. let worldFile = await fetch(url, { method: 'get' });
  157. let worldSource = await worldFile.text();
  158. if (worldSource) {
  159. //let modified = new Date().valueOf();
  160. let created = new Date().valueOf();
  161. let obj = {
  162. 'file': worldSource,
  163. 'modified': created,
  164. 'created': created
  165. }
  166. if (!worldsObj[worldName]) {
  167. worldsObj[worldName] = {
  168. 'parent': '-',
  169. 'owner': userPub,
  170. 'featured': true,
  171. 'published': true
  172. }
  173. }
  174. let entry = entryName.replace(worldName + '/', "");
  175. worldsObj[worldName][entry] = obj;
  176. }
  177. }
  178. }
  179. console.log(worldsObj);
  180. Object.entries(worldsObj).forEach(el => {
  181. let worldName = el[0];
  182. let files = el[1];
  183. Object.entries(files).forEach(file => {
  184. _LCSDB.user().get('worlds').get(worldName).get(file[0]).put(file[1]);
  185. })
  186. })
  187. }
  188. async loadEmptyDefaultProto() {
  189. //empty proto world
  190. let userPub = _LCSUSER.is.pub;
  191. let worldsObj = {};
  192. let emptyWorld = {
  193. "index_vwf_yaml": YAML.stringify(
  194. {
  195. "extends": "http://vwf.example.com/aframe/ascene.vwf"
  196. }, 4),
  197. "index_vwf_config_yaml": YAML.stringify(
  198. {
  199. "info": {
  200. "title": "Empty World"
  201. },
  202. "model": {
  203. "vwf/model/aframe": null
  204. },
  205. "view": {
  206. "vwf/view/aframe": null,
  207. "vwf/view/editor-new": null
  208. }
  209. }, 4),
  210. "index_vwf_html": JSON.stringify ("<!-- DEFAULT HTML -->"),
  211. "appui_js": JSON.stringify ("//appui in JS"),
  212. "info_json": JSON.stringify ({
  213. "info": {
  214. "en": {
  215. "title": "Empty World",
  216. "imgUrl": "",
  217. "text": "Empty World"
  218. },
  219. "ru": {
  220. "title": "Новый Мир",
  221. "imgUrl": "",
  222. "text": "Новый Мир"
  223. }
  224. }
  225. })
  226. }
  227. worldsObj['empty'] = {
  228. 'parent': '-',
  229. 'owner': userPub,
  230. 'featured': true,
  231. 'published': true
  232. }
  233. Object.keys(emptyWorld).forEach(el=>{
  234. //let modified = new Date().valueOf();
  235. let created = new Date().valueOf();
  236. let obj = {
  237. 'file': emptyWorld[el],
  238. 'modified': created,
  239. 'created': created
  240. }
  241. worldsObj['empty'][el] = obj;
  242. })
  243. console.log(worldsObj);
  244. Object.entries(worldsObj).forEach(el => {
  245. let worldName = el[0];
  246. let files = el[1];
  247. Object.entries(files).forEach(file => {
  248. _LCSDB.user().get('worlds').get(worldName).get(file[0]).put(file[1]);
  249. })
  250. })
  251. }
  252. //load defaults for first registered user running ./setup
  253. HandleSettingsIndex() {
  254. window._app.hideProgressBar();
  255. window._app.hideUIControl();
  256. let el = document.createElement("div");
  257. el.setAttribute("id", "appGUI");
  258. document.body.appendChild(el);
  259. _cellWidgets.reflectorGUI();
  260. }
  261. HandleSetupIndex() {
  262. window._app.hideProgressBar();
  263. window._app.hideUIControl();
  264. let el = document.createElement("div");
  265. el.setAttribute("id", "admin");
  266. document.body.appendChild(el);
  267. _LCSDB.on('auth',
  268. async function (ack) {
  269. if (_LCSUSER.is) {
  270. let setPubKey = {
  271. $cell: true,
  272. $components: [
  273. {
  274. $type: "p",
  275. class: "mdc-typography--headline5",
  276. $text: "1. Set app system user PUB key"
  277. },
  278. {
  279. $type: "button",
  280. class: "mdc-button mdc-button--raised",
  281. $text: "Set app PUB key",
  282. onclick: function (e) {
  283. console.log("admin action");
  284. _LCSDB.get('lcs/app').get('pub').put(_LCSUSER.is.pub);
  285. }
  286. }
  287. ]
  288. }
  289. let adminComponents = [];
  290. let defaultPub = await _LCSDB.get('lcs/app').get('pub').once().then();
  291. if (!defaultPub) {
  292. adminComponents.push(setPubKey);
  293. }
  294. if (_LCSUSER.is.pub == defaultPub) {
  295. let loadEmpty = {
  296. $cell: true,
  297. $components: [
  298. {
  299. $type: "p",
  300. class: "mdc-typography--headline5",
  301. $text: "3. Initialize empty World proto"
  302. },
  303. {
  304. $type: "button",
  305. id: "loadDefaults",
  306. class: "mdc-button mdc-button--raised",
  307. $text: "Init empty world",
  308. onclick: function (e) {
  309. console.log("admin action");
  310. window._app.loadEmptyDefaultProto();
  311. }
  312. }
  313. ]
  314. }
  315. let loadDefaults = {
  316. $cell: true,
  317. $components: [
  318. {
  319. $type: "p",
  320. class: "mdc-typography--headline5",
  321. $text: "4. Load Sample Worlds protos from server (optional)"
  322. },
  323. {
  324. $type: "button",
  325. id: "loadDefaults",
  326. class: "mdc-button mdc-button--raised",
  327. $text: "Load default worlds (from server)",
  328. onclick: function (e) {
  329. console.log("admin action");
  330. window._app.loadWorldsDefaults();
  331. }
  332. }
  333. ]
  334. }
  335. let loadDefaultsProxy = {
  336. $cell: true,
  337. $components: [
  338. {
  339. $type: "p",
  340. class: "mdc-typography--headline5",
  341. $text: "3. Load VWF & A-Frame default components"
  342. },
  343. {
  344. $type: "button",
  345. class: "mdc-button mdc-button--raised",
  346. $text: "Load defaults Proxy",
  347. onclick: function (e) {
  348. console.log("admin action");
  349. window._app.loadProxyDefaults();
  350. }
  351. }
  352. ]
  353. }
  354. adminComponents.push(setPubKey, loadDefaultsProxy, loadEmpty, loadDefaults);
  355. }
  356. document.querySelector("#admin").$cell({
  357. $cell: true,
  358. id: 'adminComponents',
  359. $type: "div",
  360. $components: adminComponents
  361. });
  362. }
  363. })
  364. }
  365. //TODO: profile
  366. HandleUserIndex(ctx) {
  367. console.log("USER INDEX");
  368. window._app.hideProgressBar();
  369. window._app.hideUIControl();
  370. _LCSDB.on('auth',
  371. async function (ack) {
  372. if(ack.pub){
  373. document.querySelector("#profile")._status = "User: " + _LCSUSER.is.alias //+' pub: ' + _LCSUSER.is.pub;
  374. document.querySelector("#profile").$update();
  375. }
  376. })
  377. let el = document.createElement("div");
  378. el.setAttribute("id", "userProfile");
  379. document.body.appendChild(el);
  380. let userProfile = {
  381. $type: 'div',
  382. id: "profile",
  383. _status: "",
  384. $init: function(){
  385. this._status = "user is not signed in..."
  386. },
  387. $update: function(){
  388. this.$components = [
  389. {
  390. $type: "h1",
  391. class: "mdc-typography--headline4",
  392. $text: this._status //"Profile for: " + _LCSUSER.is.alias
  393. }
  394. ]
  395. }
  396. }
  397. document.querySelector("#userProfile").$cell({
  398. $cell: true,
  399. $type: "div",
  400. $components: [userProfile]
  401. })
  402. }
  403. async HandleUserWorlds(ctx) {
  404. console.log("USER WORLDS INDEX");
  405. console.log(ctx.params);
  406. let user = ctx.params.user;
  407. page.redirect('/' + user + '/worlds/protos');
  408. }
  409. async HandleFileEdit(ctx) {
  410. console.log("USER WORLD FILE EDIT");
  411. let user = ctx.params.user;
  412. let worldName = ctx.params.name;
  413. let fileOriginal = ctx.params.file;
  414. let type = ctx.params.type;
  415. window._app.hideProgressBar();
  416. window._app.hideUIControl();
  417. _LCSDB.on('auth',
  418. async function (ack) {
  419. if (_LCSUSER.is) {
  420. if (_LCSUSER.is.alias == user) {
  421. var worldType = 'worlds';
  422. var file = fileOriginal;
  423. if (type == 'state') {
  424. worldType = 'documents';
  425. file = _app.helpers.replaceSubStringALL(fileOriginal, "~", '/');
  426. }
  427. let worldFile = await _LCSUSER.get(worldType).get(worldName).get(file).once().then();
  428. if (worldFile) {
  429. console.log(worldFile.file);
  430. let el = document.createElement("div");
  431. el.setAttribute("id", "worldFILE");
  432. document.body.appendChild(el);
  433. let aceEditorCell = {
  434. $type: "div",
  435. $components: [
  436. {
  437. class: "aceEditor",
  438. id: "aceEditor",
  439. //style: "width:1200px; height: 800px",
  440. $type: "div",
  441. $text: worldFile.file,
  442. $init: function () {
  443. var mode = "ace/mode/json";
  444. if (file.includes('_yaml'))
  445. mode = "ace/mode/yaml"
  446. if (file.includes('_js'))
  447. mode = "ace/mode/javascript"
  448. var editor = ace.edit("aceEditor");
  449. editor.setTheme("ace/theme/monokai");
  450. editor.setFontSize(16);
  451. editor.getSession().setMode(mode);
  452. editor.setOptions({
  453. maxLines: Infinity
  454. });
  455. }
  456. },
  457. {
  458. $type: "button",
  459. class: "mdc-button mdc-button--raised",
  460. $text: "Save",
  461. onclick: async function (e) {
  462. console.log("save new info");
  463. let editor = document.querySelector("#aceEditor").env.editor;
  464. let newInfo = editor.getValue();
  465. _LCSUSER.get(worldType).get(worldName).get(file).get('file').put(newInfo, res => {
  466. if (res) {
  467. let modified = new Date().valueOf();
  468. _LCSUSER.get(worldType).get(worldName).get(file).get('modified').put(modified);
  469. }
  470. })
  471. }
  472. },
  473. {
  474. $type: "button",
  475. class: "mdc-button mdc-button--raised",
  476. $text: "Close",
  477. onclick: function (e) {
  478. console.log("close");
  479. if (type == "proto")
  480. window.location.pathname = "/" + user + '/worlds/protos'
  481. if (type == "state")
  482. window.location.pathname = "/" + user + '/worlds/states'
  483. }
  484. }
  485. ]
  486. }
  487. document.querySelector("#worldFILE").$cell({
  488. $cell: true,
  489. $type: "div",
  490. $components: [aceEditorCell
  491. ]
  492. })
  493. }
  494. }
  495. }
  496. })
  497. }
  498. async HandleUserWorldsWithType(ctx) {
  499. console.log("USER WORLDS INDEX");
  500. console.log(ctx.params);
  501. let user = ctx.params.user;
  502. let type = ctx.params.type;
  503. window._app.hideProgressBar();
  504. window._app.hideUIControl();
  505. if (!_app.indexApp) {
  506. _app.indexApp = new IndexApp;
  507. document.querySelector('head').innerHTML += '<link rel="stylesheet" href="/web/index-app.css">';
  508. }
  509. if (!document.querySelector('#app')) {
  510. await _app.indexApp.initApp();
  511. }
  512. if (type == 'protos') {
  513. await _app.indexApp.getWorldsProtosFromUserDB(user);
  514. } else if (type == 'states') {
  515. await _app.indexApp.getWorldsFromUserDB(user);
  516. }
  517. }
  518. async HandleIndex() {
  519. console.log("INDEX");
  520. window._app.hideProgressBar();
  521. window._app.hideUIControl();
  522. _app.indexApp = new IndexApp;
  523. document.querySelector('head').innerHTML += '<link rel="stylesheet" href="/web/index-app.css">';
  524. await _app.indexApp.generateFrontPage();
  525. await _app.indexApp.initApp();
  526. await _app.indexApp.getAppDetailsFromDB();
  527. }
  528. HandleNoPage() {
  529. console.log("no such page")
  530. }
  531. //handle parcable requests
  532. HandleParsableLoadRequest(ctx) {
  533. let app = window._app;
  534. console.log(ctx.params);
  535. //var pathname = ctx.pathname;
  536. var spaceName = ctx.params.space;
  537. var saveName = ctx.params.savename;
  538. let user = ctx.params.user;
  539. page.redirect('/' + user + '/' + spaceName + '/' + app.helpers.GenerateInstanceID() + '/load/' + saveName);
  540. }
  541. HandleParsableLoadRequestWithRev(ctx) {
  542. let app = window._app;
  543. console.log(ctx.params);
  544. //var pathname = ctx.pathname;
  545. var spaceName = ctx.params.space;
  546. var saveName = ctx.params.savename;
  547. var rev = ctx.params.rev;
  548. let user = ctx.params.user;
  549. page.redirect('/' + user + '/' + spaceName + '/' + app.helpers.GenerateInstanceID() + '/load/' + saveName + '/' + rev);
  550. }
  551. async setUserPaths(user) {
  552. await _LCSDB.get('users').get(user).get('pub').once(res => {
  553. if (res)
  554. window._LCS_WORLD_USER = _LCSDB.user(res);
  555. }).then();
  556. }
  557. async HandleParsableRequestGenID(ctx) {
  558. let app = window._app;
  559. console.log(ctx.params);
  560. let user = ctx.params.user;
  561. var pathname = ctx.pathname;
  562. await app.setUserPaths(user);
  563. if (pathname[pathname.length - 1] == '/') {
  564. pathname = pathname.slice(0, -1)
  565. }
  566. let pathToParse = pathname.replace('/' + user, "");
  567. app.helpers.Process(pathToParse).then(parsedRequest => {
  568. localStorage.setItem('lcs_app', JSON.stringify({ path: parsedRequest }));
  569. console.log(parsedRequest);
  570. if ((parsedRequest['instance'] == undefined) && (parsedRequest['private_path'] == undefined) && (parsedRequest['public_path'] !== "/") && (parsedRequest['application'] !== undefined)) {
  571. page.redirect(pathname + '/' + app.helpers.GenerateInstanceID());
  572. }
  573. });
  574. }
  575. async HandleParsableRequestWithID(ctx) {
  576. let app = window._app;
  577. console.log(ctx.params);
  578. var pathname = ctx.pathname;
  579. let user = ctx.params.user;
  580. if (pathname[pathname.length - 1] == '/') {
  581. pathname = pathname.slice(0, -1)
  582. }
  583. await app.setUserPaths(user);
  584. let pathToParse = pathname.replace('/' + user, "");
  585. app.helpers.Process(pathToParse).then(async function(parsedRequest) {
  586. localStorage.setItem('lcs_app', JSON.stringify({ path: parsedRequest }));
  587. console.log(parsedRequest);
  588. var userLibraries = { model: {}, view: {} };
  589. var application;
  590. await vwf.loadConfiguration(application, userLibraries, compatibilityCheck);
  591. });
  592. }
  593. async HandleParsableRequest(ctx) {
  594. let app = window._app;
  595. console.log(ctx.params);
  596. var pathname = ctx.pathname;
  597. if (pathname[pathname.length - 1] == '/') {
  598. pathname = pathname.slice(0, -1)
  599. }
  600. var parsedRequest = await app.helpers.Process(pathname);
  601. localStorage.setItem('lcs_app', JSON.stringify({ path: parsedRequest }));
  602. console.log(parsedRequest);
  603. if ((parsedRequest['instance'] == undefined) && (parsedRequest['private_path'] == undefined) && (parsedRequest['public_path'] !== "/") && (parsedRequest['application'] !== undefined)) {
  604. // Redirect if the url request does not include an application/file && a default 'index.vwf.yaml' exists
  605. // page.redirect(pathname + '/' + app.helpers.GenerateInstanceID());
  606. window.location.pathname = pathname + '/' + app.helpers.GenerateInstanceID()
  607. //return true;
  608. } else {
  609. //return false;
  610. }
  611. var userLibraries = { model: {}, view: {} };
  612. var application;
  613. await vwf.loadConfiguration(application, userLibraries, compatibilityCheck);
  614. }
  615. //get DB application state information for reflector (called from VWF)
  616. async getApplicationState() {
  617. let dataJson = JSON.parse(localStorage.getItem('lcs_app'));
  618. if (dataJson) {
  619. if (!dataJson.path['instance']) return undefined;
  620. }
  621. let userAlias = await _LCS_WORLD_USER.get('alias').once().then();
  622. let userPub = await _LCSDB.get('users').get(userAlias).get('pub').once().then();
  623. let loadInfo = await this.getLoadInformation(dataJson);
  624. let saveInfo = await this.loadSaveObject(loadInfo);
  625. let loadObj = {
  626. loadInfo: loadInfo,
  627. path: dataJson.path,
  628. saveObject: saveInfo,
  629. user: userAlias
  630. }
  631. //dataJson.app = loadObj;
  632. localStorage.setItem('lcs_app', JSON.stringify(loadObj));
  633. console.log(loadObj);
  634. //temporary solution for syncing DB replicas using Gun.load()
  635. // _LCS_SYS_USER.get('proxy').load(res=>{
  636. // if (res)
  637. // {console.log('proxy loaded');
  638. // _LCSDB.user(userPub).get('worlds').get(loadObj.path.public_path.slice(1)).load(w=>{
  639. // if (w) {
  640. // console.log('world files loaded');
  641. // vwf.ready( vwf.application, loadObj)
  642. // }
  643. // }, {wait: 200});
  644. // }
  645. // }, {wait: 200});
  646. return loadObj
  647. }
  648. // LookupSaveRevisions takes the public path and the name of a save, and provides
  649. // an array of all revisions for that save. (If the save does not exist, this will be
  650. // an empty array).
  651. async lookupSaveRevisions(public_path, save_name) {
  652. var result = [];
  653. var states = [];
  654. let docName = 'savestate_/' + public_path + '/' + save_name + '_vwf_json';
  655. let revs = await _LCS_WORLD_USER.get('documents').get(public_path).get(docName).get('revs').once().then();
  656. if (revs) {
  657. for (const res of Object.keys(revs)) {
  658. if (res !== '_') {
  659. let el = await _LCS_WORLD_USER.get('documents').get(public_path).get(docName).get('revs').get(res).once().then();
  660. if (el)
  661. result.push(parseInt(el.revision));
  662. }
  663. }
  664. return result
  665. }
  666. }
  667. // GetLoadInformation receives a parsed request {private_path, public_path, instance, application} and returns the
  668. // details of the save that is designated by the initial request. The details are returned in an object
  669. // composed of: save_name (name of the save) save_revision (revision of the save), explicit_revision (boolean, true if the request
  670. // explicitly specified the revision, false if it did not), and application_path (the public_path of the application this is a save for).
  671. async getLoadInformation(response) {
  672. let parsedRequest = response.path;
  673. var result = { 'save_name': undefined, 'save_revision': undefined, 'explicit_revision': undefined, 'application_path': undefined };
  674. if (parsedRequest['private_path']) {
  675. var segments = this.helpers.GenerateSegments(parsedRequest['private_path']);
  676. if ((segments.length > 1) && (segments[0] == "load")) {
  677. var potentialRevisions = await this.lookupSaveRevisions((parsedRequest['public_path']).slice(1), segments[1]);
  678. console.log('!!!!! - ', potentialRevisions);
  679. if (potentialRevisions.length > 0) {
  680. result['save_name'] = segments[1];
  681. if (segments.length > 2) {
  682. var requestedRevision = parseInt(segments[2]);
  683. if (requestedRevision) {
  684. if (potentialRevisions.indexOf(requestedRevision) > -1) {
  685. result['save_revision'] = requestedRevision;
  686. result['explicit_revision'] = true;
  687. result['application_path'] = parsedRequest['public_path'];
  688. }
  689. }
  690. }
  691. if (result['explicit_revision'] == undefined) {
  692. result['explicit_revision'] = false;
  693. potentialRevisions.sort();
  694. result['save_revision'] = potentialRevisions.pop();
  695. result['application_path'] = parsedRequest['public_path'];
  696. }
  697. }
  698. }
  699. }
  700. return result;
  701. }
  702. async loadSaveObject(loadInfo) {
  703. //let objName = loadInfo[ 'save_name' ] +'/'+ "savestate_" + loadInfo[ 'save_revision' ];
  704. let objName = "savestate_" + loadInfo['application_path'] + '/' + loadInfo['save_name'] + '_vwf_json';
  705. let objNameRev = "savestate_" + loadInfo['save_revision'] + loadInfo['application_path'] + '/' + loadInfo['save_name'] + '_vwf_json';
  706. // if(loadInfo[ 'save_revision' ]){
  707. // }
  708. let worldName = this.helpers.appPath //loadInfo[ 'application_path' ].slice(1);
  709. let saveObject = await _LCS_WORLD_USER.get('documents').get(worldName).get(objName).get('revs').get(objNameRev).once().then();
  710. let saveInfo = saveObject ? JSON.parse(saveObject.jsonState) : saveObject;
  711. return saveInfo;
  712. }
  713. // GetSaveInformation is a helper function that takes the application_path (/path/to/application).
  714. // It returns an array of all saves found for that
  715. // application (including separate entries for individual revisions of saves ).
  716. async getSaveInformation(application_path, userPUB) {
  717. var result = [];
  718. let user = _LCSDB.user(userPUB);
  719. var docName = application_path.slice(1);
  720. let potentialSaveNames = await user.get('documents').get(docName).once().then();
  721. if (potentialSaveNames) {
  722. for (const res of Object.keys(potentialSaveNames)) {
  723. if (res !== '_') {
  724. let el = await user.get('documents').path(docName).get(res).once().then();
  725. let revisionList = await this.lookupSaveRevisions(application_path.slice(1), el.filename);
  726. var latestsave = true;
  727. revisionList.sort();
  728. while (revisionList.length > 0) {
  729. var newEntry = {};
  730. newEntry['applicationpath'] = application_path;
  731. newEntry['savename'] = el.filename;
  732. newEntry['revision'] = revisionList.pop().toString();
  733. newEntry['latestsave'] = latestsave;
  734. if (latestsave) {
  735. newEntry['url'] = this.helpers.JoinPath(window.location.origin, application_path, "load", el.filename + "/");
  736. }
  737. else {
  738. newEntry['url'] = this.helpers.JoinPath(window.location.origin, application_path, "load", el.filename + "/", newEntry['revision'] + "/");
  739. }
  740. latestsave = false;
  741. result.push(newEntry);
  742. }
  743. }
  744. }
  745. }
  746. return result;
  747. }
  748. async getProtoWorldFiles(userPub, worldName, date) {
  749. let fileNamesAll = await _LCSDB.user(userPub).get('worlds').get(worldName).once().then();
  750. let worldFileNames = Object.keys(fileNamesAll).filter(el => (el !== '_') && (el !== 'owner') && (el !== 'parent') && (el !== 'featured') && (el !== 'published') && (el !== 'info_json'));
  751. let worldObj = {};
  752. for (var el in worldFileNames) {
  753. let fn = worldFileNames[el];
  754. let res = await _LCSDB.user(userPub).get('worlds').get(worldName).get(fn).once().then();
  755. var data = {
  756. 'file': res.file,
  757. 'modified': res.modified,
  758. 'created': res.created
  759. }
  760. if (!date) {
  761. data = {
  762. 'file': res.file
  763. }
  764. }
  765. worldObj[fn] = data;
  766. }
  767. console.log(worldObj);
  768. return worldObj
  769. }
  770. async cloneWorldPrototype(worldName, userName, newWorldName) {
  771. let userPub = await _LCSDB.get('users').get(userName).get('pub').once().then();
  772. //let worldProto = await _LCSDB.user(userPub).get('worlds').get(worldName).once().then();
  773. var worldID = window._app.helpers.GenerateInstanceID().toString();
  774. if (newWorldName) {
  775. worldID = newWorldName
  776. }
  777. //let modified = new Date().valueOf();
  778. console.log('clone: ' + worldName + 'to: ' + worldID);
  779. let newOwner = _LCSUSER.is.pub;
  780. let worldObj = {
  781. 'owner': newOwner,
  782. 'parent': userName + '/' + worldName,
  783. 'featured': true,
  784. 'published': true
  785. };
  786. let fileNamesAll = await _LCSDB.user(userPub).get('worlds').get(worldName).once().then();
  787. let worldFileNames = Object.keys(fileNamesAll).filter(el => (el !== '_') && (el !== 'owner') && (el !== 'parent') && (el !== 'featured') && (el !== 'published'));
  788. for (var el in worldFileNames) {
  789. let fn = worldFileNames[el];
  790. let res = await _LCSDB.user(userPub).get('worlds').get(worldName).get(fn).once().then();
  791. let data = {
  792. 'file': res.file,
  793. 'modified': res.modified
  794. }
  795. worldObj[fn] = data;
  796. }
  797. console.log(worldObj);
  798. Object.keys(worldObj).forEach(el => {
  799. _LCSUSER.get('worlds').get(worldID).get(el).put(worldObj[el]);
  800. })
  801. }
  802. async cloneWorldState(filename) {
  803. let myWorldProtos = await _LCSUSER.get('worlds').once().then();
  804. let userName = this.helpers.worldUser;
  805. let userPub = await _LCSDB.get('users').get(userName).get('pub').once().then();
  806. let protoUserRoot = this.helpers.getRoot(true).root;
  807. //let myName = _LCSUSER.is.alias;
  808. //let proto = Object.keys(myWorldProtos).filter(el => el == protoUserRoot);
  809. var protosKeys = [];
  810. if (myWorldProtos)
  811. protosKeys = Object.keys(myWorldProtos);
  812. if (protosKeys.includes(protoUserRoot)) {
  813. let userProtoFiles = await this.getProtoWorldFiles(userPub, protoUserRoot);
  814. let myProtoFiles = await this.getProtoWorldFiles(_LCSUSER.is.pub, protoUserRoot);
  815. let hashUP = await this.helpers.sha256(JSON.stringify(userProtoFiles));
  816. let hashMP = await this.helpers.sha256(JSON.stringify(myProtoFiles));
  817. if (hashUP == hashMP) {
  818. this.saveStateAsFile(filename);
  819. } else {
  820. let noty = new Noty({
  821. text: 'world prototype is modified.. could not clone world state',
  822. timeout: 2000,
  823. theme: 'mint',
  824. layout: 'bottomRight',
  825. type: 'error'
  826. });
  827. noty.show();
  828. }
  829. } else {
  830. await this.cloneWorldPrototype(protoUserRoot, userName, protoUserRoot);
  831. this.saveStateAsFile(filename);
  832. }
  833. }
  834. //TODO: refactor and config save
  835. saveStateAsFile(filename, otherProto) // invoke with the view as "this"
  836. {
  837. console.log("Saving: " + filename);
  838. //var clients = this.nodes["http://vwf.example.com/clients.vwf"];
  839. // Save State Information
  840. var state = vwf.getState();
  841. state.nodes[0].children = {};
  842. var timestamp = state["queue"].time;
  843. timestamp = Math.round(timestamp * 1000);
  844. var objectIsTypedArray = function (candidate) {
  845. var typedArrayTypes = [
  846. Int8Array,
  847. Uint8Array,
  848. // Uint8ClampedArray,
  849. Int16Array,
  850. Uint16Array,
  851. Int32Array,
  852. Uint32Array,
  853. Float32Array,
  854. Float64Array
  855. ];
  856. var isTypedArray = false;
  857. if (typeof candidate == "object" && candidate != null) {
  858. typedArrayTypes.forEach(function (typedArrayType) {
  859. isTypedArray = isTypedArray || candidate instanceof typedArrayType;
  860. });
  861. }
  862. return isTypedArray;
  863. };
  864. var transitTransformation = function (object) {
  865. return objectIsTypedArray(object) ?
  866. Array.prototype.slice.call(object) : object;
  867. };
  868. let jsonValuePure = require("vwf/utility").transform(
  869. state, transitTransformation
  870. );
  871. //remove all Ohm generated grammarsfrom state
  872. let jsonValue = _app.helpers.removeGrammarObj(jsonValuePure);
  873. var jsonState = JSON.stringify(jsonValue);
  874. let rootPath = this.helpers.getRoot(true);
  875. var inst = rootPath.inst;
  876. if (filename == '') filename = inst;
  877. //if (root.indexOf('.vwf') != -1) root = root.substring(0, root.lastIndexOf('/'));
  878. var root = rootPath.root;
  879. var json = jsonState;
  880. if (otherProto) {
  881. console.log('need to modify state...');
  882. json = this.helpers.replaceSubStringALL(jsonState, '/' + root + '/', '/' + otherProto + '/');//jsonState.replace(('/' + root + '/'), ('/' + otherProto +'/') );
  883. root = otherProto;
  884. console.log(json);
  885. }
  886. //var documents = _LCSUSER.get('documents');
  887. var saveRevision = new Date().valueOf();
  888. var stateForStore = {
  889. "root": root,
  890. "filename": filename,
  891. "inst": inst,
  892. "timestamp": timestamp,
  893. "extension": ".vwf.json",
  894. "jsonState": json,
  895. "publish": true
  896. };
  897. //let objName = loadInfo[ 'save_name' ] +'/'+ "savestate_" + loadInfo[ 'save_revision' ];
  898. // "savestate_" + loadInfo[ 'save_revision' ] + '/' + loadInfo[ 'save_name' ] + '_vwf_json'
  899. var docName = 'savestate_/' + root + '/' + filename + '_vwf_json';
  900. _LCSUSER.get('documents').get(root).get(docName).put(stateForStore, res => {
  901. if (res) {
  902. let noty = new Noty({
  903. text: 'Saved to ' + docName,
  904. timeout: 2000,
  905. theme: 'mint',
  906. layout: 'bottomRight',
  907. type: 'success'
  908. });
  909. noty.show();
  910. }
  911. });
  912. _LCSUSER.get('worlds').get(root).get('info_json').once(res => {
  913. if (res) {
  914. let modified = saveRevision;
  915. let newOwner = _LCSUSER.is.pub;
  916. let userName = _LCSUSER.is.alias;
  917. let obj = {
  918. 'parent': userName + '/' + root,
  919. 'owner': newOwner,
  920. 'file': res.file,
  921. //'modified': modified,
  922. 'created': modified
  923. }
  924. let docInfoName = 'savestate_/' + root + '/' + filename + '_info_vwf_json';
  925. _LCSUSER.get('documents').get(root).get(docInfoName).not(res => {
  926. _LCSUSER.get('documents').get(root).get(docInfoName).put(obj);
  927. });
  928. _LCSUSER.get('documents').get(root).get(docInfoName).get('created').not(res => {
  929. _LCSUSER.get('documents').get(root).get(docInfoName).get('created').put(modified);
  930. });
  931. _LCSUSER.get('documents').get(root).get(docInfoName).get('modified').put(modified);
  932. }
  933. });
  934. var docNameRev = 'savestate_' + saveRevision.toString() + '/' + root + '/' + filename + '_vwf_json';
  935. _LCSUSER.get('documents').get(root).get(docName).get('revs').get(docNameRev).put(stateForStore)
  936. .path("revision").put(saveRevision);
  937. // Save Config Information
  938. var config = { "info": {}, "model": {}, "view": {} };
  939. // Save browser title
  940. config["info"]["title"] = document.title//$('title').html();
  941. // Save model drivers
  942. Object.keys(vwf_view.kernel.kernel.models).forEach(function (modelDriver) {
  943. if (modelDriver.indexOf('vwf/model/') != -1) config["model"][modelDriver] = "";
  944. });
  945. // If neither glge or threejs model drivers are defined, specify nodriver
  946. if (config["model"]["vwf/model/glge"] === undefined && config["model"]["vwf/model/threejs"] === undefined) config["model"]["nodriver"] = "";
  947. // Save view drivers and associated parameters, if any
  948. Object.keys(vwf_view.kernel.kernel.views).forEach(function (viewDriver) {
  949. if (viewDriver.indexOf('vwf/view/') != -1) {
  950. if (vwf_view.kernel.kernel.views[viewDriver].parameters) {
  951. config["view"][viewDriver] = vwf_view.kernel.kernel.views[viewDriver].parameters;
  952. }
  953. else config["view"][viewDriver] = "";
  954. }
  955. });
  956. //var jsonConfig = $.encoder.encodeForURL(JSON.stringify(config));
  957. var jsonConfig = JSON.stringify(config);
  958. let configStateForStore = {
  959. "root": root,
  960. "filename": filename,
  961. "inst": inst,
  962. "timestamp": timestamp,
  963. "extension": "config.vwf.json",
  964. "jsonState": jsonConfig
  965. };
  966. //let objName = loadInfo[ 'save_name' ] +'/'+ "savestate_" + loadInfo[ 'save_revision' ];
  967. // "savestate_" + loadInfo[ 'save_revision' ] + '/' + loadInfo[ 'save_name' ] + '_vwf_json'
  968. // let configName = 'savestate_/' + root + '/' + filename + '_config_vwf_json';
  969. // let documentSaveConfigState = _LCSUSER.get(configName).put(configStateForStore);
  970. // //documents.path(root).set(documentSaveConfigState);
  971. // let configNameRev = 'savestate_' + saveRevision.toString() + '/' + root + '/' + filename + '_config_vwf_json';
  972. // _LCSUSER.get(configNameRev).put(configStateForStore);
  973. // _LCSUSER.get(configNameRev).path("revision").put(saveRevision);
  974. //documentSaveConfigState.path('revs').set(documentSaveStateRevision);
  975. // Save config file to server
  976. // var xhrConfig = new XMLHttpRequest();
  977. // xhrConfig.open("POST", "/" + root + "/save/" + filename, true);
  978. // xhrConfig.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
  979. // xhrConfig.send("root=" + root + "/" + filename + "&filename=saveState&inst=" + inst + "&timestamp=" + timestamp + "&extension=.vwf.config.json" + "&jsonState=" + jsonConfig);
  980. }
  981. // LoadSavedState
  982. async loadSavedState(filename, applicationpath, revision) {
  983. console.log("Loading: " + filename);
  984. let userName = await _LCS_WORLD_USER.get('alias').once().then();
  985. if (revision) {
  986. window.location.pathname = '/' + userName + applicationpath + '/load/' + filename + '/' + revision + '/';
  987. }
  988. else { // applicationpath + "/" + inst + '/load/' + filename + '/';
  989. window.location.pathname = '/' + userName + applicationpath + '/load/' + filename + '/';
  990. }
  991. }
  992. hideUIControl() {
  993. var el = document.getElementById("ui-controls");
  994. if (el) {
  995. el.classList.remove("visible");
  996. el.classList.add("not-visible");
  997. }
  998. }
  999. showUIControl() {
  1000. var el = document.getElementById("ui-controls");
  1001. if (el) {
  1002. el.classList.remove("not-visible");
  1003. el.classList.add("visible");
  1004. }
  1005. }
  1006. hideProgressBar() {
  1007. var progressbar = document.getElementById("load-progressbar");
  1008. if (progressbar) {
  1009. progressbar.classList.remove("visible");
  1010. progressbar.classList.add("not-visible");
  1011. progressbar.classList.add("mdc-linear-progress--closed");
  1012. }
  1013. }
  1014. showProgressBar() {
  1015. let progressbar = document.getElementById("load-progressbar");
  1016. if (progressbar) {
  1017. progressbar.classList.add("visible");
  1018. progressbar.classList.add("mdc-linear-progress--indeterminate");
  1019. }
  1020. }
  1021. }
  1022. export { App }