Newer
Older
solitude / src / js / src / main / app.js
@sylow-castle sylow-castle on 30 Jul 9 KB [update] add Simple Logger App
window.addEventListener('load', main(document));

function main(document) {
  graftSolitude(window);
  webconfig(window);
  var mysolitude;
  if (true || (typeof solitude.style) === 'undefined') {
    mysolitude = readyWeb();
  }

  let Que;
  initializeQue();

  //アプリケーション毎に初期化操作
  initializeSolitudeApps(mysolitude.apps)

  Que("li.tab").list().forEach(function (val, index, _list) {
    val.addEventListener("click", function () {
      selectTab(index);
    });
  })
  append(location.href);

  const leftMenu = Que("#left-menu").get();
  const menuToggle = Que("#menu-toggle").get();
  menuToggle.addEventListener("click", function () {
    leftMenu.classList.remove("initial")
    toggleMenuItem(leftMenu);
    Que("#left-menu li span").list().forEach(function (val) {
      toggleMenuItem(val);
    })
    toggleMenuItem(Que("#main").get());

  })

  // 以下、宣言
  function toggleMenuItem(element) {
    if (element.classList.contains("expanded")) {
      element.classList.add("shrinked");
      element.classList.remove("expanded");
    } else {
      element.classList.add("expanded");
      element.classList.remove("shrinked");
    }
  }

  function selectTab(index) {
    let tab, content;
    Que("#main-pane li.tab").list().forEach(function (val) {
      val.classList.remove("selected");
      if (val.dataset.soloAppId == index) {
        tab = val;
      }
    });

    Que("#main-pane div.main-article").list().forEach(function (val) {
      val.classList.remove("selected");
      if (val.dataset.soloAppId == index) {
        content = val;
      }
    });

    tab.classList.add("selected");
    content.classList.add("selected");
  }

  function initializeQue() {
    //Queはglobalではない。親スコープで宣言済み。
    Que = newQue;
    Query.prototype = {
        get list() {return asList},
        get get() {return asNode},
        get que() {return reQue},
    }

    function Query(selector, base) {
      if (base instanceof Query) {
        this.origin = base.get();
      } else {
        this.origin = base || document;
      }
      this.raw = this.origin.querySelectorAll(selector);
      /*
      this.list = asList;
      this.get = asNode;
      this.que = reQue;
      */
    }

    function asList() {
      return this.raw;
    }

    function asNode() {
      if (this.raw.length > 0) {
        return this.raw[0];
      }
    }

    function newQue(selector, base) {
      return new Query(selector, base);
    }

    function reQue(selector) {
      return new Query(selector, this.get())
    }

  }

  function append(kontent) {
    let logs = Que("#logarea").get().value
    logs += kontent + "\n";
    Que("#logarea").get().value = logs;
  }

  /**
   * solitude web版でのsolitudeオブジェクトの組み立てを行う
   */
  function readyWeb() {
    //先にwebconfigを起動している前提
    const solitude = window.solitude;
    solitude.apps = {};
    Object.assign(solitude.apps, window.tmp.solitudeapps);
    addProperty(solitude);
    return solitude;

    /*
    function buildWebSolitude(_global) {
        const solitude = {};
        solitude.write = {}
        solitude.write.local = (_path, contents) => { append(contents) };
        return solitude;
    }
    */
    function addProperty(app) {
      //solitude本体を設定しているような表現をしたいための回りくどい書き方
      Object.defineProperty(app, 'style', {
        get() { return 'web' }
      });

      app.write = {}
      app.write.local = writeLocal;
      app.read = {};
      app.read.local = readLocal;

      return app;

      function writeLocal(path, _contents) {
        solitude.io.fs.download(path, cotents, "text/plain");
      }

      function readLocal(path) {
        // stringで解決するPromiseを返したい。
        return fetch(path).then(
          (response) => {
            if (response.ok) {
              return response.text();
            } else {
              throw new Error(`HTTP error, status = ${response.status}`);
            }
          }, (cause) => {
            const error = new Error('ファイルのオープンに失敗しました。causeプロパティで原因となったオブジェクトを返します。');
            error.cause = cause;
            throw error;
          }
        );
      }
    }
  }

  function initializeSolitudeApps(apps) {
    appsProto = {
      useMenu: true,
      useTab: true,
      autorun: false,
    };

    const appFuncs = [IframeApp, StaticPageApp, BatchApp];
    appFuncs.forEach(appFunc => {
      appFunc.prototype = appsProto;
    });

    let app;
    let appInstanceID = 0;
    for (const [name, conf] of Object.entries(apps)) {
      const appType = conf.type || "undef";
      switch (appType) {
        case "main":
        case "undef":
          app = null;
          //do nothing                            
          break;
        case "staticpage":
          app = new StaticPageApp(name, conf);
          break;
        case "iframe":
          app = new IframeApp(name, conf);
          break;
        case "batch":
          app = new BatchApp(name, conf);
          break;
        default:
          //something wrong;
          break;
      }
      if(app) {
        app.instanceID = ++appInstanceID;
        app.load();
        solitude.log.info("loaded " + name +", instanceID " + app.instanceID )
      } 

    }


    //巻き上げを期待しているためclass構文ではない。
    function IframeApp(name, conf) {
      this.name = name;
      this.conf = conf;
      this.load = loadApp;
      this.template = "#app-type-iframe";
      this.callback = iframeCallback;
    }

    function StaticPageApp(name, conf) {
      this.name = name;
      this.conf = conf;
      this.load = loadApp;
      this.template = "#app-type-staticpage";
      this.callback = staticpageCallback;
    }

    function BatchApp(name, conf) {
      this.name = name;
      this.conf = conf;
      this.load = loadBatchApp;
      this.template = "#app-type-batch";
      this.callback = batchCallback;
    }

    function loadApp() {
      addNewMenu(this.name);
      const mount = Que("#left-menu ol");
      const app = this;
      mount.que("li.init").get().addEventListener("click", function () {

       if(isInitialized(app.instanceID)) {
         return;
       }

        addNewTab(app);
        addNewContent(app);  
        solitude.log.info(app.name + " is initialized");
      });
      mount.que("li.init").get().classList.remove("init");    
    }

    function isInitialized(appInstanceID) {
      //アプリのタブが出ているかでロード済みかを判断
      let result = false;
      Que("#main-tabs li").list().forEach(elem => {
        if(appInstanceID == elem.dataset.soloAppId) {
          result = true;
        }
      });
      return result; 

    }

    function loadBatchApp() {
      addNewContent(this);
    }

    function addNewMenu(name) {
      const mount = Que("#left-menu ol").get();
      let menu = Que("#app-menu").get();
      menu = menu.content.cloneNode(true);

      const span = Que("span", menu).get();
      // template では初期状態はexpanded
      if (Que("#left-menu.shrinked").list().length > 0) {
        const cls = span.classList;
        cls.remove("expanded");
        cls.add("shrinked");
      }
      span.textContent = name;
      mount.appendChild(menu);
    }

    function addNewTab(app) {
      const mount = Que("#main-tabs").get();
      let tab = Que("#app-tab").get()
      tab = tab.content.cloneNode(true);
      const li = Que("li", tab).get();
      li.textContent = app.name;
      li.dataset.soloAppId = app.instanceID;
      const index = app.instanceID;
      mount.appendChild(tab);

      li.addEventListener("click", () => {
        selectTab(index);
      });
    }

    function addNewContent(app) {
      const appNode = (Que(app.template).get()).content.cloneNode(true);
      const base = Que("div.main-article", appNode);
      base.get().classList.remove("undefined");
      base.get().dataset.soloAppId = app.instanceID;
      base.que("div.app-container").get().classList.add(app.name);
      app.callback(app, base);

      const mount = Que("#app-pane").get();
      mount.appendChild(base.get());

    }

    function iframeCallback(app, base) {
      const iframe = Que("div.app-container > iframe", base).get();
      iframe.setAttribute("id", app.name);
      iframe.setAttribute("title", app.name);
      iframe.setAttribute("src", app.conf.src);
    }

    function staticpageCallback(app, base) {
      mysolitude.read.local(app.conf.src).then((text) => {
        const parser = new DOMParser();
        const doc = parser.parseFromString(text, "text/html");
        const content = Que("div.app-body", doc).get().cloneNode(true);
        Que("div.app-container", base).get().appendChild(content);

        //文書内のJavascriptコードを実行する。簡単な実装。
        //これだとon.loadイベントとかがキックされなくなってしまう気がする。
        const removes = [];
        Que("script", content).list().forEach(element => {
          const script = document.createElement("script")
          script.innerHTML = element.innerHTML;
          for (const attr of element.attributes) {
            script.setAttribute(attr.name, attr.value);
          }
          element.before(script)
          removes.push(element);
        });

        for (const script of removes) {
          script.remove();
        }

      });
    }

    function batchCallback(app, base) {
      const content = document.createElement("script")
      content.setAttribute("src", app.conf.src);
      Que("div.app-container", base).get().appendChild(content);
    }
  }
}