Nikolay Suslov пре 6 година
родитељ
комит
9a86d51d47
2 измењених фајлова са 568 додато и 312 уклоњено
  1. 326 136
      public/app.js
  2. 242 176
      public/web/world-app.js

+ 326 - 136
public/app.js

@@ -23,7 +23,7 @@ class App {
 
       this.helpers = new Helpers;
       this.initUser();
-      
+
       //client routes
       page('/', this.HandleIndex);
       page('/setup', this.HandleSetupIndex);
@@ -188,7 +188,7 @@ class App {
 
   async loadWorldsDefaults(replace) {
 
-   //load to DB default worlds
+    //load to DB default worlds
 
     let worldsResponse = await fetch('/world-files', { method: 'get' });
     let worldFiles = await worldsResponse.json();
@@ -239,31 +239,31 @@ class App {
 
     console.log(worldsObj);
 
-    if(replace){
+    if (replace) {
 
-    Object.entries(worldsObj).forEach(el => {
+      Object.entries(worldsObj).forEach(el => {
 
-      let worldName = el[0];
-      let files = el[1];
-      Object.entries(files).forEach(file => {
+        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]);
+          _LCSDB.user().get('worlds').get(worldName).get(file[0]).put(file[1]);
 
+        })
       })
-    })
-  } else {
-    //force replace all default worlds
+    } else {
+      //force replace all default worlds
 
       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]).not(res=>{
+
+          _LCSDB.user().get('worlds').get(worldName).get(file[0]).not(res => {
             _LCSDB.user().get('worlds').get(worldName).get(file[0]).put(file[1]);
           })
-  
+
         })
       })
 
@@ -273,10 +273,10 @@ class App {
 
   async loadEmptyDefaultProto() {
 
-     //empty proto world
-     let userPub = _LCSUSER.is.pub;
-     let worldsObj = {};
-     let emptyWorld = {
+    //empty proto world
+    let userPub = _LCSUSER.is.pub;
+    let worldsObj = {};
+    let emptyWorld = {
 
       "index_vwf_yaml": YAML.stringify(
         {
@@ -285,33 +285,33 @@ class App {
 
       "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),
+      "assets_json": JSON.stringify({}),
+      "index_vwf_html": JSON.stringify("<!-- DEFAULT HTML -->"),
+      "appui_js": JSON.stringify("//appui in JS"),
+      "info_json": JSON.stringify({
         "info": {
-          "title": "Empty World"
-        },
-        "model": {
-          "vwf/model/aframe": null
-        },
-        "view": {
-          "vwf/view/aframe": null,
-          "vwf/view/editor-new": null
-        } 
-      }, 4),
-      "assets_json": JSON.stringify ({}),
-      "index_vwf_html": JSON.stringify ("<!-- DEFAULT HTML -->"),
-      "appui_js": JSON.stringify ("//appui in JS"),
-      "info_json": JSON.stringify ({ 
-        "info": { 
           "en": {
-              "title": "Empty World",
-              "imgUrl": "",
-              "text": "Empty World"
+            "title": "Empty World",
+            "imgUrl": "",
+            "text": "Empty World"
           },
           "ru": {
-              "title": "Новый Мир",
-              "imgUrl": "",
-              "text": "Новый Мир"
+            "title": "Новый Мир",
+            "imgUrl": "",
+            "text": "Новый Мир"
           }
-      }
+        }
       }, null, 4)
     }
 
@@ -322,7 +322,7 @@ class App {
       'published': true
     }
 
-    Object.keys(emptyWorld).forEach(el=>{
+    Object.keys(emptyWorld).forEach(el => {
       //let modified = new Date().valueOf();
       let created = new Date().valueOf();
       let obj = {
@@ -331,7 +331,7 @@ class App {
         'created': created
       }
       worldsObj['empty'][el] = obj;
-    }) 
+    })
 
     console.log(worldsObj);
 
@@ -363,13 +363,13 @@ class App {
 
   }
 
-  HandleWorldAbout(ctx){
+  HandleWorldAbout(ctx) {
 
     console.log("about world");
 
     let userAlias = ctx.params.user;
     let worldName = ctx.params.space;
-    let saveName =  ctx.params.savename;
+    let saveName = ctx.params.savename;
 
     window._app.hideProgressBar();
     window._app.hideUIControl();
@@ -378,7 +378,7 @@ class App {
 
     let worldApp = new WorldApp(userAlias, worldName, saveName);
     worldApp.initWorldGUI();
-    
+
   }
 
   HandleSetupIndex() {
@@ -390,7 +390,7 @@ class App {
     el.setAttribute("id", "admin");
     document.body.appendChild(el);
 
-   
+
     _LCSDB.on('auth',
       async function (ack) {
 
@@ -404,7 +404,7 @@ class App {
                 $type: "p",
                 class: "mdc-typography--headline5",
                 $text: "1. Set app system user PUB key"
-            },
+              },
               {
                 $type: "button",
                 class: "mdc-button mdc-button--raised",
@@ -415,11 +415,11 @@ class App {
                 }
               }
             ]
-      
+
           }
-      
+
           let adminComponents = [];
-      
+
 
           let defaultPub = await _LCSDB.get('lcs/app').get('pub').once().then();
           if (!defaultPub) {
@@ -435,7 +435,7 @@ class App {
                   $type: "p",
                   class: "mdc-typography--headline5",
                   $text: "3. Initialize empty World proto"
-              },
+                },
                 {
                   $type: "button",
                   id: "loadDefaults",
@@ -448,7 +448,7 @@ class App {
                 }
               ]
             }
-            
+
 
             let loadDefaults = {
               $cell: true,
@@ -458,7 +458,7 @@ class App {
                   $type: "p",
                   class: "mdc-typography--headline5",
                   $text: "4. Load Sample Worlds protos from server (optional)"
-              },
+                },
                 {
                   $type: "button",
                   id: "loadDefaults",
@@ -477,17 +477,17 @@ class App {
                 _cellWidgets.switch({
                   'id': 'forceReplace',
                   'init': function () {
-                      this._switch = new mdc.switchControl.MDCSwitch(this);
-                      this._replaceSwitch = this._switch;
-                      this._switch.checked = false;
+                    this._switch = new mdc.switchControl.MDCSwitch(this);
+                    this._replaceSwitch = this._switch;
+                    this._switch.checked = false;
                   }
-              }
-              ),
-              {
-                $type: 'label',
-                for: 'input-forceReplace',
-                $text: 'Force replace'
-              }
+                }
+                ),
+                {
+                  $type: 'label',
+                  for: 'input-forceReplace',
+                  $text: 'Force replace'
+                }
 
               ]
             }
@@ -499,7 +499,7 @@ class App {
                   $type: "p",
                   class: "mdc-typography--headline5",
                   $text: "3. Load VWF & A-Frame default components"
-              },
+                },
                 {
                   $type: "button",
                   class: "mdc-button mdc-button--raised",
@@ -535,39 +535,39 @@ class App {
 
     _LCSDB.on('auth',
       async function (ack) {
-        if(ack.pub){
+        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
-            }
-          ]
-        }
+    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]
-      })
+    document.querySelector("#userProfile").$cell({
+      $cell: true,
+      $type: "div",
+      $components: [userProfile]
+    })
   }
 
 
@@ -640,7 +640,7 @@ class App {
                       editor.getSession().setMode(mode);
                       editor.setOptions({
                         maxLines: Infinity
-                    });
+                      });
                     }
                   },
                   {
@@ -803,7 +803,7 @@ class App {
 
   }
 
-  
+
   async HandleParsableRequestWithID(ctx) {
 
     let app = window._app;
@@ -819,7 +819,7 @@ class App {
 
     let pathToParse = pathname.replace('/' + user, "");
 
-    app.helpers.Process(pathToParse).then(async function(parsedRequest) {
+    app.helpers.Process(pathToParse).then(async function (parsedRequest) {
 
       localStorage.setItem('lcs_app', JSON.stringify({ path: parsedRequest }));
       console.log(parsedRequest);
@@ -849,7 +849,7 @@ class App {
 
       // 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() 
+      window.location.pathname = pathname + '/' + app.helpers.GenerateInstanceID()
 
       //return true;
     } else {
@@ -974,8 +974,8 @@ class App {
 
     //let objName = loadInfo[ 'save_name' ] +'/'+ "savestate_" + loadInfo[ 'save_revision' ];
 
-    if(!loadInfo.save_name){
-        return undefined
+    if (!loadInfo.save_name) {
+      return undefined
     }
 
     let objName = "savestate_" + loadInfo['application_path'] + '/' + loadInfo['save_name'] + '_vwf_json';
@@ -1461,19 +1461,19 @@ class App {
         for (const el of Object.keys(worldFiles)) {
           if (el !== '_') {
             let doc = await _LCSUSER.get('worlds').get(worldName).get(el).once().then();
-            if(doc) {
-            if (doc.file) {
-              for (const fEl of Object.keys(doc)) {
-                if (fEl !== '_') {
-                  await _LCSUSER.get('worlds').get(worldName).get(el).get(fEl).put(null).then();
+            if (doc) {
+              if (doc.file) {
+                for (const fEl of Object.keys(doc)) {
+                  if (fEl !== '_') {
+                    await _LCSUSER.get('worlds').get(worldName).get(el).get(fEl).put(null).then();
+                  }
                 }
+                await _LCSUSER.get('worlds').get(worldName).get(el).put(null).then();
+              } else {
+                await _LCSUSER.get('worlds').get(worldName).get(el).put(null).then()
               }
-              await _LCSUSER.get('worlds').get(worldName).get(el).put(null).then();
-            } else {
-              await _LCSUSER.get('worlds').get(worldName).get(el).put(null).then()
             }
           }
-          }
         }
       }
 
@@ -1504,6 +1504,16 @@ class App {
       await this.deleteWorldState(worldName, stateEntryInfo);
       await this.deleteWorldState(worldName, stateEntry);
     }
+
+    let noty = new Noty({
+      text: "World Deleted!",
+      timeout: 2000,
+      theme: 'mint',
+      layout: 'bottomRight',
+      type: 'success'
+    });
+    noty.show();
+
   }
 
 
@@ -1514,50 +1524,230 @@ class App {
     let listData = {};
 
     for (var prop in jsonObj) {
-        var name = prop.split('/')[1];
-        if (parsed[name]) {
-            parsed[name][prop] = jsonObj[prop];
-        } else {
-            parsed[name] = {};
-            parsed[name][prop] = jsonObj[prop];
-        }
+      var name = prop.split('/')[1];
+      if (parsed[name]) {
+        parsed[name][prop] = jsonObj[prop];
+      } else {
+        parsed[name] = {};
+        parsed[name][prop] = jsonObj[prop];
+      }
 
     }
     //console.log(parsed);
     for (var prop in parsed) {
-        var name = prop;
-        let obj = Object.entries(parsed[prop]);
-        var lists = {};
-        obj.forEach(el => {
-            if (el[1].loadInfo['save_name']) {
-                let saveName = prop + '/load/' + el[1].loadInfo.save_name;
-                if (!lists[saveName])
-                    lists[saveName] = {};
-                lists[saveName][el[0]] = el[1]
-            } else {
-                if (!lists[name])
-                    lists[name] = {};
-                lists[name][el[0]] = el[1]
-            }
-        });
+      var name = prop;
+      let obj = Object.entries(parsed[prop]);
+      var lists = {};
+      obj.forEach(el => {
+        if (el[1].loadInfo['save_name']) {
+          let saveName = prop + '/load/' + el[1].loadInfo.save_name;
+          if (!lists[saveName])
+            lists[saveName] = {};
+          lists[saveName][el[0]] = el[1]
+        } else {
+          if (!lists[name])
+            lists[name] = {};
+          lists[name][el[0]] = el[1]
+        }
+      });
 
-        // console.log(lists);
+      // console.log(lists);
 
-        Object.entries(lists).forEach(list => {
+      Object.entries(lists).forEach(list => {
 
-          listData[list[0]] = list[1];
+        listData[list[0]] = list[1];
 
-            // let element = document.getElementById(list[0] + 'List');
-            // if (element) {
-            //     element._setListData(list[1]);
-            // }
-        })
+        // let element = document.getElementById(list[0] + 'List');
+        // if (element) {
+        //     element._setListData(list[1]);
+        // }
+      })
     }
 
     return listData
     // console.log(data)
-}
+  }
+
+
+  async getAllProtoWorldsInfoForUser(userAlias) {
+
+    let userPub = await _LCSDB.get('users').get(userAlias).get('pub').once().then();
+
+    var db = _LCSDB.user(userPub);
+
+    if (_LCSUSER.is) {
+      if (_LCSUSER.is.alias == userAlias)
+        db = _LCSUSER;
+    }
+
+    var worlds = {};
+
+    let worldDocs = await db.get('worlds').once().then();
+    if(worldDocs) {
+      let protos = Object.keys(worldDocs).filter(el => el !== '_');
+
+      if (protos) {
+        for (const el of protos) {
+          let info = await this.getWorldInfo(userAlias, el);
+         if (Object.keys(info).length !== 0)
+                worlds[el] = info;
+        }
+      }
+    }
+
+    return worlds
+
+    // await db.get('worlds').once(async (proto, index) => {
+    //   let protos = Object.keys(proto).filter(el => el !== '_');
+    //  // console.log(protos);
+
+    //   if (protos) {
+    //     for (const el of protos) {
+    //       let info = await this.getWorldInfo(userAlias, el);
+    //       worlds[el] = info;
+    //     }
+    //   }
+    // }).then();
+    // return worlds
+  }
+
+  async getSaveStates(userAlias, worldName) {
+
+    let userPub = await _LCSDB.get('users').get(userAlias).get('pub').once().then();
+
+    var db = _LCSDB.user(userPub);
+
+    if (_LCSUSER.is) {
+      if (_LCSUSER.is.alias == userAlias)
+        db = _LCSUSER;
+    }
+
+    var states = {};
+
+    let docs = await db.get('documents').get(worldName).once().then();
+    if(docs){
+    let saves = Object.keys(docs).filter(el => el.includes('_info_vwf_json'));
+    if (saves) {
+      for (const el of saves) {
+        let stateName = el.split('/')[2].replace('_info_vwf_json', "");
+        let info = await this.getStateInfo(userAlias, worldName, stateName);
+        if (Object.keys(info).length !== 0)
+          states[stateName] = info;
+      }
+    }
+  }
+      return states
+
+
+  //  return await db.get('documents').get(worldName).once(async (save, index) => {
+  //     let saves = Object.keys(save).filter(el => el.includes('_info_vwf_json'));
+  //     //console.log(saves);
+  //     if (saves) {
+  //       for (const el of saves) {
+  //         let stateName = el.split('/')[2].replace('_info_vwf_json', "");
+  //         let info = await this.getStateInfo(userAlias, worldName, stateName);
+  //         states[stateName] = info;
+  //       }
+  //     }
+  //     return states
+  //   }).then();
+  //   //return states
+  }
+
+  async getStateInfo(user, space, saveName) {
+
+    let userPub = await _LCSDB.get('users').get(user).get('pub').once().then();
+    var db = _LCSDB.user(userPub);
+
+    if (_LCSUSER.is) {
+      if (_LCSUSER.is.alias == user)
+        db = _LCSUSER;
+    }
 
+    var info = {};
+
+    let docName = 'savestate_/' + space + '/' + saveName + '_info_vwf_json';
+    let world = await db.get('documents').get(space).get(docName).once().then();
+    if (world) {
+      let doc = await db.get('documents').get(space).get(docName).once((res) => {
+
+        if (res && res !== 'null') {
+
+          if (res.file && res.file !== 'null') {
+
+            let worldDesc = JSON.parse(res.file);
+            let root = Object.keys(worldDesc)[0];
+            var appInfo = worldDesc[root]['en'];
+
+            let langID = localStorage.getItem('krestianstvo_locale');
+            if (langID) {
+              appInfo = worldDesc[root][langID]
+            }
+
+            info = {
+              'worldName': space + '/load/' + saveName,
+              'created': res.created ? res.created : res.modified,
+              'modified': res.modified,
+              'type': 'saveState',
+              'userAlias': user,
+              'info': appInfo
+            }
+          }
+        }
+      }).then();
+    }
+    return info
+  }
+
+
+  async getWorldInfo(user, space) {
+    //get space for user
+
+    let userPub = await _LCSDB.get('users').get(user).get('pub').once().then();
+    var db = _LCSDB.user(userPub);
+
+    if (_LCSUSER.is) {
+      if (_LCSUSER.is.alias == user)
+        db = _LCSUSER;
+    }
+
+    var info = {};
+
+
+    let world = await db.get('worlds').get(space).once().then();
+    if (world) {
+      let doc = await db.get('worlds').get(space).get('info_json').once((res) => {
+
+        if (res && res !== 'null') {
+
+          if (res.file && res.file !== 'null') {
+
+            let worldDesc = JSON.parse(res.file);
+            let root = Object.keys(worldDesc)[0];
+            var appInfo = worldDesc[root]['en'];
+
+            let langID = localStorage.getItem('krestianstvo_locale');
+            if (langID) {
+              appInfo = worldDesc[root][langID]
+            }
+
+            info = {
+              'worldName': space,
+              'created': res.created ? res.created : res.modified,
+              'modified': res.modified,
+              'type': 'proto',
+              'userAlias': user,
+              'info': appInfo
+            }
+
+          }
+        }
+      }).then();
+    }
+
+    return info
+
+  }
 
 
 }

+ 242 - 176
public/web/world-app.js

@@ -59,121 +59,189 @@ class WorldApp {
 
     }
 
-    async initWorldGUI() {
 
+    createWorldStatesGUI() {
         let self = this;
-        let user = this.userAlias;
-        let space = this.worldName;
-        let saveName = this.saveName;
-
-        let el = document.createElement("div");
-        el.setAttribute("id", "aboutWorld");
-        document.body.appendChild(el);
 
-        //get user
-        // let userID = await _LCSDB.get('~@' + user).once().then();
-        // let userPub = Object.keys(userID).filter(el => (el !== '_'))[0].slice(1);
-
-        let userPub = await _LCSDB.get('users').get(user).get('pub').once().then();
-        var db = _LCSDB.user(userPub);
-
-        if (_LCSUSER.is) {
-            if (_LCSUSER.is.alias == user)
-                db = _LCSUSER;
-        }
-
-        let worldCardGUI = {
+        let worldStatesGUI = {
             $cell: true,
-            id: 'worldCard',
+            id: "worldStatesGUI",
             $type: "div",
-            _worldInfo: {},
+            $components: [],
+            _states: {},
             _refresh: function (data) {
-                this._worldInfo = data
+                this._states = data
             },
-            _getWorldInfo: async function () {
-                //get space for user
-                let world = await db.get('worlds').get(space).once().then();
-                if (world) {
-                    await db.get('worlds').get(space).get('info_json').on((res) => {
+            $init: async function () {
+                //this._refresh();
+            },
+            _makeWorldCard: function (data) {
+                let cardID = data[1].userAlias + '_' + data[1].worldName + '_' + data[0];
+                let card = self.createWorldCard(cardID, 'min');
+                card._worldInfo = data[1];
+                card.$update();
+                return {
+                    $cell: true,
+                    $type: "div",
+                    class: "mdc-layout-grid__cell mdc-layout-grid__cell--span-4",
+                    $components: [
+                        card
+                        //self.createWorldCard(data[1].userAlias, data[1].worldName, data[0])
+                        //this._worldCardDef(appInfo)
+                    ]
+                }
+                //console.log(data);
+            },
+            $update: function () {
+                this.$components = [
+                    {
+                        $type: "div",
+                        class: "mdc-layout-grid",
+                        $components: [
+                            {
+                                $type: "div",
+                                class: "mdc-layout-grid__inner",
+                                $components: [
+                                    {
+                                        $type: "div",
+                                        class: "mdc-layout-grid__cell mdc-layout-grid__cell--span-12",
+                                        $components: [
+                                            {
+                                                $type: "H3",
+                                                $text: 'States'
+                                            }
+                                        ]
+                                    }
+                                ]
+                            },
+                            {
+                                $type: "div",
+                                class: "mdc-layout-grid__inner",
+                                $components: Object.entries(this._states)
+                                .filter(el =>Object.keys(el[1]).length !== 0)
+                                .sort(function (el1, el2) {
+                                    return parseInt(el2[1].created) - parseInt(el1[1].created)
+                                })
+                                .map(this._makeWorldCard)
+                            }
+                        ]
 
-                        if (res && res !== 'null') {
+                    },
 
-                            if (res.file && res.file !== 'null') {
 
-                                let worldDesc = JSON.parse(res.file);
-                                let root = Object.keys(worldDesc)[0];
-                                var appInfo = worldDesc[root]['en'];
+                ]
+            }
+        }
 
-                                let langID = localStorage.getItem('krestianstvo_locale');
-                                if (langID) {
-                                    appInfo = worldDesc[root][langID]
-                                }
+        return worldStatesGUI
+    }
 
-                                let info = {
-                                    'worldName': space,
-                                    'created': res.created ? res.created : res.modified,
-                                    'modified': res.modified,
-                                    'type': 'proto',
-                                    'userAlias': user,
-                                    'info': appInfo
+    createWorldCard(id, type) {
+        let self = this;
+
+        let onlineGUI = {
+            $cell: true,
+            id: "onlineGUI_" + id,
+            class: "online",
+            $type: "div",
+            _instances: {},
+            _worldListItem: function (m) {
+                return {
+                    $type: "li",
+                    class: "mdc-list-item",
+                    $components: [
+                        {
+                            $type: "span",
+                            class: "world-link mdc-list-item__text",
+                            $components: [
+                                {
+                                    $type: "span",
+                                    class: "mdc-list-item__primary-text",
+                                    $components: [
+                                        {
+                                            $type: "a",
+                                            $text: m[0],
+                                            target: "_blank",
+                                            href: window.location.protocol + "//" + window.location.host + "/" + m[1].user + m[0],
+                                            onclick: function (e) {
+                                                //self.refresh();
+                                            }
+                                        },
+                                    ]
+                                },
+                                {
+                                    $type: "span",
+                                    class: "mdc-list-item__secondary-text",
+                                    $text: self.language.t('users') + m[1].clients
                                 }
-                                this._refresh(info);
-                            }
+                            ]
                         }
-                    }).then();
-                } else {
-                    this._refresh();
+                    ]
                 }
             },
-            _getStateInfo: async function () {
-                //get space for user
-                let docName = 'savestate_/' + space + '/' + saveName + '_info_vwf_json';
-                let world = await db.get('documents').get(space).get(docName).once().then();
-                if (world) {
-                    db.get('documents').get(space).get(docName).on((res) => {
+            $components: [],
+            _refresh: function (data) {
+                if (data) {
+                    if (Object.entries(data).length !== 0) {
+                        if (this._worldInfo) {
+                            let insts = Object.entries(data).filter(el => el[0] == this._worldInfo.worldName);
+                            if (insts.length !== 0)
+                                this._instances = insts[0][1];
+                        }
+                    } else {
+                        this._instances = {}
+                    }
 
-                        if (res && res !== 'null') {
+                }
 
-                            if (res.file && res.file !== 'null') {
+            },
+            $init: function () {
+                this._refresh();
+            },
+            $update: function () {
+                if (this._instances) {
+                    let cardListData = Object.entries(this._instances).filter(el => el[1].user == this._worldInfo.userAlias);
+                    this.$components = [
+                        {
+                            $type: "hr",
+                            class: "mdc-list-divider"
+                        }
+                    ].concat(cardListData.map(this._worldListItem))
+                }
 
-                                let worldDesc = JSON.parse(res.file);
-                                let root = Object.keys(worldDesc)[0];
-                                var appInfo = worldDesc[root]['en'];
+            }
+        }
 
-                                let langID = localStorage.getItem('krestianstvo_locale');
-                                if (langID) {
-                                    appInfo = worldDesc[root][langID]
-                                }
 
-                                let info = {
-                                    'worldName': space + '/load/' + saveName,
-                                    'created': res.created ? res.created : res.modified,
-                                    'modified': res.modified,
-                                    'type': 'saveState',
-                                    'userAlias': user,
-                                    'info': appInfo
-                                }
-                                this._refresh(info);
-                            }
-                        }
-                    });
-                } else {
-                    this._refresh();
-                }
+        return {
+            $cell: true,
+            id: 'worldCard_' + id,
+            $type: "div",
+            _worldInfo: {},
+            _refresh: function (data) {
+                this._worldInfo = data
             },
-            $init: async function () {
+            // _getWorldInfo: async function () {
+            //     //get space for user
+            //     let info = await _app.getWorldInfo(user, space);
+            //     this._refresh(info);
+            // },
+            // _getStateInfo: async function () {
+            //     //get space for user
+            //     let info = await _app.getStateInfo(user, space, saveName);
+            //     this._refresh(info);
+            // },
+            $init: function () {
                 //get space for user
-                if (!saveName) {
-                    await this._getWorldInfo();
-                } else {
-                    await this._getStateInfo();
-                }
+                // if (!saveName) {
+                //     this._getWorldInfo();
+                // } else {
+                //     this._getStateInfo();
+                // }
             },
             $update: function () {
                 console.log(this._worldInfo);
                 this.$components = [this._updateCard()]
-
-
             },
             $components: [],
             _updateCard: function () {
@@ -181,7 +249,7 @@ class WorldApp {
                 let desc = this._worldInfo;
 
 
-                if (!desc) {
+                if (!desc || Object.keys(desc).length == 0) {
                     return {
                         $type: "h1",
                         class: "mdc-typography--headline4",
@@ -191,10 +259,39 @@ class WorldApp {
 
 
                 let userGUI = [];
+                let online = [];
+
                 let cardInfo = {
                     "title": ""
                 };
 
+
+                if (type == "full") {
+
+                } else {
+                    userGUI.push({
+                        $type: "a",
+                        class: "mdc-button mdc-button--compact mdc-card__action mdc-button--outlined",
+                        $text: "Details",
+                        onclick: function (e) {
+                            e.preventDefault();
+                            window.location.pathname = "/" + desc.userAlias + '/' + desc.worldName + '/about'
+                        }
+                    });
+                }
+
+                userGUI.push({
+                    $type: "a",
+                    class: "mdc-button mdc-button--compact mdc-card__action mdc-button--outlined",
+                    $text: self.language.t('start'),//"Start new",
+                    target: "_blank",
+                    href: "/" + desc.userAlias + '/' + desc.worldName,
+                    onclick: function (e) {
+                        //self.refresh();
+                    }
+                });
+
+
                 if (desc.type == 'saveState') {
                     cardInfo.title = desc.worldName.split('/')[2];
                 }
@@ -208,9 +305,12 @@ class WorldApp {
                             $type: "a",
                             class: "mdc-button mdc-button--compact mdc-card__action",
                             $text: "States",
-                            onclick: function (e) {
+                            onclick: async function (e) {
                                 //console.log('clone');
-                                self.showOnlySaveStates(desc.worldName, desc.userAlias);
+                                document.querySelector('#worldStatesGUI')._refresh({});
+                                let data = await _app.getSaveStates(desc.userAlias, desc.worldName);
+                                document.querySelector('#worldStatesGUI')._refresh(data);
+                                //self.showOnlySaveStates(desc.worldName, desc.userAlias);
                                 //self.refresh();
                             }
                         }
@@ -218,6 +318,9 @@ class WorldApp {
                 }
 
 
+
+                online.push(onlineGUI);
+
                 return {
                     $cell: true,
                     $type: "div",
@@ -280,16 +383,7 @@ class WorldApp {
                             $type: "section",
                             class: "mdc-card__actions",
                             $components: [
-                                {
-                                    $type: "a",
-                                    class: "mdc-button mdc-button--compact mdc-card__action mdc-button--outlined",
-                                    $text: self.language.t('start'),//"Start new",
-                                    target: "_blank",
-                                    href: "/" + desc.userAlias + '/' + desc.worldName,
-                                    onclick: function (e) {
-                                        //self.refresh();
-                                    }
-                                }
+
                             ].concat(userGUI)
                         },
 
@@ -300,85 +394,47 @@ class WorldApp {
                                 {
                                     $type: 'div',
                                     $text: 'online now: '
-                                },
-                                onlineGUI
-                            ]
+                                }
+                            ].concat(online)
                         }
                     ]
                 }
             }
         }
 
-        let onlineGUI = {
-            $cell: true,
-            id: "onlineGUI",
-            $type: "div",
-            _instances: {},
-            _worldListItem: function (m) {
-                return {
-                    $type: "li",
-                    class: "mdc-list-item",
-                    $components: [
-                        {
-                            $type: "span",
-                            class: "world-link mdc-list-item__text",
-                            $components: [
-                                {
-                                    $type: "span",
-                                    class: "mdc-list-item__primary-text",
-                                    $components: [
-                                        {
-                                            $type: "a",
-                                            $text: m[0],
-                                            target: "_blank",
-                                            href: window.location.protocol + "//" + window.location.host + "/" + m[1].user + m[0],
-                                            onclick: function (e) {
-                                                //self.refresh();
-                                            }
-                                        },
-                                    ]
-                                },
-                                {
-                                    $type: "span",
-                                    class: "mdc-list-item__secondary-text",
-                                    $text: self.language.t('users') + m[1].clients
-                                }
-                            ]
-                        }
-                    ]
-                }
-            },
-            $components: [],
-            _refresh: function (data) {
-                if (data) {
-                    if (Object.entries(data).length !== 0) {
-                        if (this._worldInfo) {
-                            let insts = Object.entries(data).filter(el => el[0] == this._worldInfo.worldName);
-                            if (insts.length !== 0)
-                                this._instances = insts[0][1];
-                        }
-                    } else {
-                        this._instances = {}
-                    }
+    }
 
-                }
+    async initWorldGUI() {
 
-            },
-            $init: function () {
-                this._refresh();
-            },
-            $update: function () {
-                if (this._instances) {
-                    let cardListData = Object.entries(this._instances).filter(el => el[1].user == this._worldInfo.userAlias);
-                    this.$components = [
-                        {
-                            $type: "hr",
-                            class: "mdc-list-divider"
-                        }
-                    ].concat(cardListData.map(this._worldListItem))
-                }
+        let self = this;
+        let user = this.userAlias;
+        let space = this.worldName;
+        let saveName = this.saveName;
 
-            }
+        let el = document.createElement("div");
+        el.setAttribute("id", "aboutWorld");
+        document.body.appendChild(el);
+
+        let cardID = user + '_' + space + '_' + (saveName ? saveName : "");
+        let worldCardGUI = this.createWorldCard(cardID, 'full');
+        let worldStatesGUI = [];
+
+        var info = {};
+
+        if (!saveName) {
+            info = await _app.getWorldInfo(user, space);
+        } else {
+            info = await _app.getStateInfo(user, space, saveName);
+        }
+        worldCardGUI._worldInfo = info;
+        worldCardGUI.$update();
+
+        if (!saveName) {
+            let statesData = await _app.getSaveStates(user, space);
+            let worldStates = this.createWorldStatesGUI();
+            worldStates._states = statesData;
+            worldStates.$update();
+            worldStatesGUI.push(worldStates);
         }
 
 
@@ -527,7 +583,6 @@ class WorldApp {
             }
         }
 
-
         _LCSDB.on('auth',
             async function (ack) {
                 if (_LCSUSER.is) {
@@ -573,7 +628,13 @@ class WorldApp {
                                     $components: [
                                         actionsGUI
                                     ]
-                                }
+                                },
+                                {
+                                    $type: "div",
+                                    class: "mdc-layout-grid__cell mdc-layout-grid__cell--span-12",
+                                    $components: [
+                                    ].concat(worldStatesGUI)
+                                },
                             ]
                         }
                     ]
@@ -589,9 +650,14 @@ class WorldApp {
         let parcedData = _app.parseAppInstancesData(data);
 
         //if (Object.entries(parcedData).length !== 0)
-        let onlineGUI = document.querySelector("#onlineGUI");
-        if (onlineGUI)
-            document.querySelector("#onlineGUI")._refresh(parcedData)
+        let onlineGUIs = document.querySelectorAll('.online');
+
+        onlineGUIs.forEach(function (item) {
+            item._refresh(parcedData)
+        });
+
+        // if (onlineGUI)
+        //     document.querySelector("#onlineGUI")._refresh(parcedData)