repo: janusweb
action: commit
revision: 
path_from: 
revision_from: 2243ed40415d2060cb957b15a37ac0124819822a:
path_to: 
revision_to: 
git.thebackupbox.net
janusweb
git clone git://git.thebackupbox.net/janusweb
commit 2243ed40415d2060cb957b15a37ac0124819822a
Author: James Baicoianu 
Date:   Wed May 9 12:41:11 2018 -0700

    Major UI configurability improvements

diff --git a/scripts/ui/main.js b/scripts/ui/main.js
index 30d44cd5ca9f7d2dce1da8c039a001adb952a859..
index ..c771fab7ee08315b753f11f603868c1d9d894f2f 100644
--- a/scripts/ui/main.js
+++ b/scripts/ui/main.js
@@ -1,37 +1,41 @@
 elation.require(['utils.template', 'elements.elements', 'elements.ui.all', 'elements.collection.all'], function() {
-  elation.component.add('janusweb.ui.main', function() {
-    this.init = function() {
+  elation.elements.define('janus.ui.app', class extends elation.elements.base {
+    init() {
+      super.init();
       this.apps = {};
-      this.addclass('janusweb_ui_main');
-/*
-      var template = document.getElementById('janus-ui');
-      if (template) {
-        this.container.innerHTML = template.innerHTML;
-        elation.component.init();
-      }
-*/
-      var datapath = elation.config.get('janusweb.datapath', '/media/janusweb');
-      var configurl = this.args.config || datapath + '/assets/webui/default.json'
-      this.loadConfig(configurl).then((uicfg) => {
-        console.log('UI config', uicfg);
-        this.container.innerHTML = elation.template.get('janusweb.ui');
+      this.applist = [];
+      this.components = [];
+      this.defineAttributes({
+        name: { type: 'string' },
+        src: { type: 'string' },
+        toplevel: { type: 'boolean', default: false },
+        removable: { type: 'boolean', default: false }
       });
     }
-    this.loadConfig = function(url) {
+    create() {
+    }
+    loadConfig(url) {
+      if (!url) url = this.src;
       return new Promise((resolve, reject) => {
         console.log('Loading UI config:', url);
         fetch(url)
           .then((r) => r.json())
+          .catch((e) => {
+            console.error('Failed to load UI config:', this);
+            reject();
+          })
           .then((json) => this.processJSON(url, json))
-          .then(resolve, reject);
+          .then(() => { this.dispatchEvent({type: 'appload'}); })
+          .then(resolve, reject)
       });
     }
-    this.processJSON = function(url, json) {
+    processJSON(url, json) {
       var urlparts = url.split('/');
       urlparts.pop();
       var baseurl = urlparts.join('/');
       return new Promise((resolve, reject) => {
         //console.log('process the json', json);
+        this.components = json.components;

         this.loadIncludes(json.includes, baseurl)
           .then(() => this.loadApps(json.apps, baseurl))
@@ -40,7 +44,7 @@ elation.require(['utils.template', 'elements.elements', 'elements.ui.all', 'elem
           .then(() => resolve(json));
       });
     }
-    this.loadIncludes = function(includes, baseurl) {
+    loadIncludes(includes, baseurl) {
       var promises = [];
       if (includes) {
         for (var i = 0; i < includes.length; i++) {
@@ -52,20 +56,40 @@ elation.require(['utils.template', 'elements.elements', 'elements.ui.all', 'elem
         return new Promise((resolve, reject) => resolve());
       }
     }
-    this.loadApps = function(apps, baseurl) {
+    loadApp(name, url, toplevel, removable) {
+      let appargs = {
+        name: name,
+        src: url
+      };
+      if (toplevel) {
+        appargs.toplevel = 1;
+      }
+      if (removable === undefined) removable = true;
+      appargs.removable = (removable ? 1 : false);
+
+      let app = elation.elements.create('janus-ui-app', appargs);
+      this.apps[name] = app;
+      this.applist.push(app);
+          //console.log(' - load app', k, apps[k], baseurl);
+      app.addEventListener('appload', (ev) => { this.dispatchEvent({type: 'appload'}); });
+
+      this.appendChild(app);
+      return app.loadConfig();
+    }
+    loadApps(apps, baseurl) {
       var promises = [];
+      if (!baseurl) baseurl = '';
       if (apps) {
         for (var k in apps) {
-          this.apps[k] = apps[k];
-          //console.log(' - load app', k, apps[k], baseurl);
-          promises.push(this.loadConfig(baseurl + '/' + apps[k]));
+          let promise = this.loadApp(k, baseurl + '/' + apps[k]);
+          promises.push(promise);
         }
         return Promise.all(promises);
       } else {
         return new Promise((resolve, reject) => resolve());
       }
     }
-    this.loadScriptsAndCSS = function(scripts, css, baseurl) {
+    loadScriptsAndCSS(scripts, css, baseurl) {
       //console.log('get all these scripts and css', scripts, css);
       var promises = [];
       if (scripts) {
@@ -86,7 +110,7 @@ elation.require(['utils.template', 'elements.elements', 'elements.ui.all', 'elem
       }
       return Promise.all(promises);
     }
-    this.loadTemplates = function(templates, baseurl) {
+    loadTemplates(templates, baseurl) {
       //console.log('get all these templates', templates, baseurl);
       var promises = [];
       if (templates) {
@@ -97,7 +121,7 @@ elation.require(['utils.template', 'elements.elements', 'elements.ui.all', 'elem
       }
       return Promise.all(promises);
     }
-    this.loadTemplate = function(name, url) {
+    loadTemplate(name, url) {
       //console.log('load template:', name, url);
       return new Promise((resolve, reject) => {
         fetch(url).then((r) => r.text().then((t) => { 
@@ -106,7 +130,7 @@ elation.require(['utils.template', 'elements.elements', 'elements.ui.all', 'elem
         }));
       });
     }
-    this.resolveFullURL = function(url, baseurl) {
+    resolveFullURL(url, baseurl) {
       var baseparts = baseurl.split('/');
       var urlparts = url.split('/');
       if (urlparts[0][0] == '.') {
@@ -121,5 +145,124 @@ elation.require(['utils.template', 'elements.elements', 'elements.ui.all', 'elem
       }
       return url;
     }
-  }, elation.ui.base);
+  });
+  elation.elements.define('janus.ui.main', class extends elation.elements.janus.ui.app {
+    init() {
+      super.init();
+      this.defineAttributes({
+        config: { type: 'string' },
+        client: { type: 'object' },
+        editing: { type: 'boolean', set: this.updateEditMode }
+      });
+
+      this.handleDragOver = this.handleDragOver.bind(this);
+      this.handleDrop = this.handleDrop.bind(this);
+    }
+    create() {
+      var datapath = elation.config.get('janusweb.datapath', '/media/janusweb');
+      var configurl = this.config || datapath + '/assets/webui/default.json'
+      this.installedapps = elation.elements.create('collection.localindexed', {index: 'name', storagekey: 'janusweb.ui.installedapps'});
+      this.installedapps.load();
+      this.container = elation.elements.create('div', {
+        append: this,
+        class: 'janus-ui-container'
+      });
+      var promises = [];
+      promises.push(this.loadApp('default', configurl, true, false));
+      var installed = this.installedapps.items;
+      for (var i = 0; i < installed.length; i++) {
+        promises.push(this.loadApp(installed[i].name, installed[i].url, true).catch(error => { return error }));
+      }
+
+      Promise.all(promises).then((uicfg) => {
+        console.log('UI config', uicfg);
+        this.container.innerHTML = elation.template.get('janusweb.ui');
+      });
+    }
+    updateEditMode(editmode) {
+      if (editmode) {
+        if (!this.applayout) {
+          this.applayout = elation.elements.create('janus-ui-applayout', { append: this });
+        }
+        this.addEventListener('dragover', this.handleDragOver);
+        this.addEventListener('drop', this.handleDrop);
+        this.applayout.show();
+      } else {
+        this.removeEventListener('dragover', this.handleDragOver);
+        this.removeEventListener('drop', this.handleDrop);
+        this.applayout.hide();
+      }
+    }
+    openWindow(component, args) {
+      if (!args) args = {};
+      if (!args.append) args.append = this;
+      var win = elation.elements.create('ui-window', args);
+      win.setcontent('<' + component + '>');
+      return win;
+    }
+    installApp(name, url) {
+      this.installedapps.add({name: name, url: url});
+      return this.loadApp(name, url);
+    }
+    uninstallApp(name) {
+      var app = this.installedapps.remove({name: name});
+
+      return;
+    }
+
+    handleDragOver(ev) {
+      if (this.applayout) {
+        this.applayout.setTarget(ev.target);
+      }
+      ev.preventDefault();
+    }
+    handleDrop(ev) {
+      var component = ev.dataTransfer.getData('x-janus/x-component');
+      if (component) {
+        //this.applayout.target.innerHTML += '<' + component + '>';
+        var el = elation.elements.create(component, {
+          preview: 1,
+          append: this.applayout.target
+        });
+      }
+    }
+  });
+  elation.elements.define('janus.ui.applayout', class extends elation.elements.base {
+    init() {
+    }
+    create() {
+      this.taglabel = elation.elements.create('ui-label', {
+        append: this
+      });
+      var region_top = elation.elements.create('div', {
+        append: this
+      });
+    }
+    getParentComponent(el, attr) {
+      var p = el;
+      while (p && !(p instanceof elation.elements.base && (attr !== undefined && p[attr]))) {
+        p = p.parentNode;
+      }
+      return p;
+    }
+    setTarget(target) {
+      var component = this.getParentComponent(target, 'editable');
+      if (this.target !== component) {
+        if (component) {
+          //console.log('set target!', component);
+          this.taglabel.setLabel(component.nodeName.toLowerCase());
+          var dim = component.getBoundingClientRect();
+
+          this.style.left = dim.x + 'px';
+          this.style.top = dim.y + 'px';
+          this.style.width = dim.width + 'px';
+          this.style.height = dim.height + 'px';
+          this.show();
+        } else {
+          this.hide();
+        }
+        this.target = component;
+      }
+    }
+  });
 });

-----END OF PAGE-----