From cfa85894465dbf2d286e083d962babdf14641582 Mon Sep 17 00:00:00 2001
From: ulrich
Date: Sun, 04 Apr 2021 14:30:21 +0000
Subject: [PATCH] UI begonnen
---
ui/js/app-menu.js | 137 +++++++++
src/de/uhilger/mediaz/App.java | 1
src/de/uhilger/mediaz/api/StoreTestHandler.java | 10
ui/data/menu/untermenue-2.json | 27 +
ui/data/tpl/dlg-info.tpl | 8
ui/app.css | 119 ++++++++
ui/hamburger.css | 99 +++++++
ui/index.html | 79 +++++
src/de/uhilger/mediaz/api/AblageTestHandler.java | 4
ui/data/tpl/app-menu.tpl | 20 +
src/de/uhilger/mediaz/conf/Store.java | 12
src/mediaz_de_DE.properties | 2
src/de/uhilger/mediaz/Server.java | 7
ui/data/menu/hauptmenue.json | 32 ++
src/de/uhilger/mediaz/entity/Ablageort.java | 11
ui/app-menu.css | 36 ++
ui/js/app.js | 206 ++++++++++++++
ui/data/menu/untermenue-1.json | 27 +
18 files changed, 822 insertions(+), 15 deletions(-)
diff --git a/src/de/uhilger/mediaz/App.java b/src/de/uhilger/mediaz/App.java
index 63d8cbb..777c985 100644
--- a/src/de/uhilger/mediaz/App.java
+++ b/src/de/uhilger/mediaz/App.java
@@ -46,6 +46,7 @@
public static final String RB_AP_CONF = "appParamConf";
public static final String RB_AP_WWW_DATA = "appParamWWWData";
public static final String RB_AP_CTX = "appParamCtx";
+ public static final String RB_AP_UI = "appParamUi";
/**
* <p>Start-Methode dieser Anwendung</p>
diff --git a/src/de/uhilger/mediaz/Server.java b/src/de/uhilger/mediaz/Server.java
index c31c9fb..4bc3286 100644
--- a/src/de/uhilger/mediaz/Server.java
+++ b/src/de/uhilger/mediaz/Server.java
@@ -22,6 +22,7 @@
import de.uhilger.mediaz.api.FileHandler;
import de.uhilger.mediaz.api.StopServerHandler;
import de.uhilger.mediaz.api.StoreTestHandler;
+import java.io.File;
import java.io.IOException;
import java.util.logging.Logger;
import java.net.InetSocketAddress;
@@ -41,6 +42,7 @@
public static final String RB_SERVER_START_MSG = "msgServerStart";
public static final String RB_WEBROOT = "webroot";
+ public static final String RB_UI_ROOT = "uiroot";
public static final String RB_STOP_SERVER = "stopServer";
public static final String RB_ABLAGE_TEST = "testAblage";
public static final String RB_STORE_TEST = "testStore";
@@ -90,9 +92,14 @@
*/
public void start() throws IOException {
logger.log(Level.INFO, App.getRs(RB_SERVER_START_MSG), Integer.toString(port));
+
+ String ui = App.getInitParameter(App.getRs(App.RB_AP_UI));
+
+ File uiDir = new File(ui);
HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);
server.createContext(ctx + App.getRs(RB_WEBROOT), new FileHandler(App.getInitParameter(App.getRs(App.RB_AP_WWW_DATA))));
+ server.createContext(ctx + App.getRs(RB_UI_ROOT), new FileHandler(uiDir.getAbsolutePath()));
server.createContext(ctx + App.getRs(RB_STOP_SERVER), new StopServerHandler());
server.createContext(ctx + App.getRs(RB_ABLAGE_TEST), new AblageTestHandler());
server.createContext(ctx + App.getRs(RB_STORE_TEST), new StoreTestHandler());
diff --git a/src/de/uhilger/mediaz/api/AblageTestHandler.java b/src/de/uhilger/mediaz/api/AblageTestHandler.java
index 05e18e7..e43f4c4 100644
--- a/src/de/uhilger/mediaz/api/AblageTestHandler.java
+++ b/src/de/uhilger/mediaz/api/AblageTestHandler.java
@@ -8,7 +8,7 @@
import com.google.gson.Gson;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
-import de.uhilger.mediaz.entity.Ablage;
+import de.uhilger.mediaz.entity.Ablageort;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
@@ -21,7 +21,7 @@
@Override
public void handle(HttpExchange e) throws IOException {
- Ablage ablage = new Ablage();
+ Ablageort ablage = new Ablageort();
ablage.setName("Katalog");
ablage.setOrt("/home/ulrich/Videos");
diff --git a/src/de/uhilger/mediaz/api/StoreTestHandler.java b/src/de/uhilger/mediaz/api/StoreTestHandler.java
index 9735b8e..8e040a0 100644
--- a/src/de/uhilger/mediaz/api/StoreTestHandler.java
+++ b/src/de/uhilger/mediaz/api/StoreTestHandler.java
@@ -8,7 +8,7 @@
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import de.uhilger.mediaz.conf.Store;
-import de.uhilger.mediaz.entity.Ablage;
+import de.uhilger.mediaz.entity.Ablageort;
import de.uhilger.mediaz.entity.ConfigurationElement;
import java.io.File;
import java.io.IOException;
@@ -27,11 +27,11 @@
@Override
public void handle(HttpExchange e) throws IOException {
- Ablage ablage = new Ablage();
- ablage.setName("Katalog");
- ablage.setOrt("/home/ulrich/Videos");
+ Ablageort ort = new Ablageort();
+ ort.setName("Katalog");
+ ort.setOrt("/home/ulrich/Videos");
Store store = new Store();
- File file = store.writeToFile(ablage);
+ File file = store.writeToFile(ort);
try {
ConfigurationElement elem = store.readFromFile(file);
logger.log(Level.INFO, "Typ: {0}, Name: {1}",
diff --git a/src/de/uhilger/mediaz/conf/Store.java b/src/de/uhilger/mediaz/conf/Store.java
index 2fd46c7..546aeee 100644
--- a/src/de/uhilger/mediaz/conf/Store.java
+++ b/src/de/uhilger/mediaz/conf/Store.java
@@ -8,16 +8,14 @@
import com.google.gson.Gson;
import de.uhilger.mediaz.App;
import de.uhilger.mediaz.Server;
-import de.uhilger.mediaz.entity.Ablage;
+import de.uhilger.mediaz.entity.Ablageort;
import de.uhilger.mediaz.entity.ConfigurationElement;
import java.io.BufferedReader;
import java.io.File;
-import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
-import java.io.InputStreamReader;
import java.util.logging.Logger;
/**
@@ -29,7 +27,7 @@
private static final Logger logger = Logger.getLogger(Store.class.getName());
- private static final String typeAblage = "Ablage";
+ private static final String typeAblageort = "Ablageort";
/**
* Ein Objekt als JSON in eine Datei schreiben
@@ -76,10 +74,10 @@
String json = sb.toString();
Gson gson = new Gson();
switch(type) {
- case typeAblage:
- return gson.fromJson(json, Ablage.class);
+ case typeAblageort:
+ return gson.fromJson(json, Ablageort.class);
default:
- Ablage ablage = new Ablage();
+ Ablageort ablage = new Ablageort();
ablage.setName("Test");
return ablage;
}
diff --git a/src/de/uhilger/mediaz/entity/Ablage.java b/src/de/uhilger/mediaz/entity/Ablageort.java
similarity index 74%
rename from src/de/uhilger/mediaz/entity/Ablage.java
rename to src/de/uhilger/mediaz/entity/Ablageort.java
index 91ee34b..2ab0c85 100644
--- a/src/de/uhilger/mediaz/entity/Ablage.java
+++ b/src/de/uhilger/mediaz/entity/Ablageort.java
@@ -9,10 +9,11 @@
*
* @author ulrich
*/
-public class Ablage implements ConfigurationElement {
+public class Ablageort implements ConfigurationElement {
private String name;
private String ort;
+ private String url;
@Override
public String getName() {
@@ -30,5 +31,13 @@
public void setOrt(String ort) {
this.ort = ort;
}
+
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
}
diff --git a/src/mediaz_de_DE.properties b/src/mediaz_de_DE.properties
index d126562..5f0d59a 100644
--- a/src/mediaz_de_DE.properties
+++ b/src/mediaz_de_DE.properties
@@ -4,9 +4,11 @@
appParamConf=conf
appParamWWWData=www-data
appParamCtx=ctx
+appParamUi=ui
# API-Endpunkte
webroot=/
+uiroot=/ui
stopServer=/server/stop
testAblage=/test/ablage
testStore=/test/store
diff --git a/ui/app-menu.css b/ui/app-menu.css
new file mode 100644
index 0000000..aa3e116
--- /dev/null
+++ b/ui/app-menu.css
@@ -0,0 +1,36 @@
+
+.app-menu {
+ margin: 0;
+ padding: 0;
+}
+
+.app-menu-kopf {
+ text-align: center;
+}
+
+ul.app-menu {
+ list-style: none;
+}
+
+.app-menu-item-back {
+ margin-bottom: 0.3em;
+ cursor: pointer;
+}
+
+.app-menu-item {
+ text-align: right;
+ cursor: pointer;
+}
+
+.app-menu-item-submark {
+ color: transparent;
+ cursor: pointer;
+}
+
+/*
+ Das div-Element, das das Menue aufnimmt erhaelt
+ die Klasse app-menu-content
+*/
+.app-menu-content {
+ overflow: hidden;
+}
diff --git a/ui/app.css b/ui/app.css
new file mode 100644
index 0000000..5827ce8
--- /dev/null
+++ b/ui/app.css
@@ -0,0 +1,119 @@
+html, body {
+ margin: 0;
+ padding: 0;
+ height: 100%; /* Anmerkung 2 */
+ font-size: larger;
+ font-family: 'Roboto Condensed';
+}
+body {
+ min-height: 0; /* Anmerkung 1 */
+ display: flex;
+ flex-flow: column;
+}
+.inhalt {
+ display: flex;
+ flex-flow: row;
+ height: 100%; /* Anmerkung 2 */
+ min-height: 0; /* Anmerkung 1 */
+ background-color: #ededed;
+ overflow: hidden;
+}
+.nord {
+ background-color: black;
+ display: flex;
+ flex-flow: row;
+ height: 2em;
+ align-items: center;
+}
+.sued {
+ height: 1.5em;
+ overflow: hidden;
+ transition: all 0.3s ease-in;
+ background-color: lightgray;
+}
+.west {
+ flex-grow: 0;
+ flex-shrink: 0;
+ flex-basis: 4em;
+ background-color: white;
+ transition: all 0.3s ease-in;
+ overflow: hidden;
+ white-space: nowrap;
+}
+.ost {
+ flex-grow: 0;
+ flex-shrink: 0;
+ flex-basis: 6em;
+ transition: all 0.3s ease-in;
+ background-color: antiquewhite;
+ overflow: hidden;
+}
+.zentrum-behaelter {
+ display: flex;
+ flex-flow: column;
+ /* background-color: #eaeaea; */
+ width: 100%;
+}
+
+.zentrum {
+ width: 100%;
+ height: 100%;
+ overflow-x: hidden;
+ overflow-y: auto;
+ -webkit-overflow-scrolling: touch;
+}
+
+.zentraler-inhalt {
+ padding: 0.5em;
+}
+
+/*
+ Anmerkungen:
+ 1.) min.height: 0 fuer body und inhalt ist gegen einen Bug, vgl.
+ http://stackoverflow.com/questions/33859811/understanding-flexbox-and-overflowauto
+ 2.) height 100% fuer html, body und inhalt sorgt dafuer, dass sich alles
+ immer ueber das gesamte Browserfenster ausdehnt.
+*/
+
+.app-titel {
+ margin-left: 0.6em;
+ color: white;
+}
+
+.pointer-cursor {
+ cursor: pointer;
+}
+
+.dialog {
+ position: relative;
+ /* height: 0.1em; */
+ transition: all 0.3s ease-in;
+}
+
+.dlg-behaelter {
+ line-height: 1.6;
+ padding: 0.4em;
+}
+
+.dlg-info {
+ background-color: #dcf2fb; // blau
+ padding: 0.4em;
+}
+
+/*
+ Close Button
+
+ <div>
+ <span class="close-btn">✖</span>
+ </div>
+*/
+
+.close-btn {
+ position: absolute;
+ top: 0px;
+ right: 0.4em;
+ margin: 0;
+ padding: 0;
+ font-size: 1.3em;
+ color: #b8b8b8;
+}
diff --git a/ui/data/menu/hauptmenue.json b/ui/data/menu/hauptmenue.json
new file mode 100644
index 0000000..b0685f0
--- /dev/null
+++ b/ui/data/menu/hauptmenue.json
@@ -0,0 +1,32 @@
+{
+ "menue": {
+ "menuetitel": "Hauptmenü",
+ "wurzel": true,
+ "vorgaenger": {
+ "vtitel": "",
+ "vverweis": ""
+ },
+ "inhalt": [
+ {
+ "titel": "Seite umschalten",
+ "umenue": false,
+ "funktion": "app.seitenleiste_umschalten"
+ },
+ {
+ "titel": "Fuss umschalten",
+ "umenue": false,
+ "funktion": "app.fusszeile_umschalten"
+ },
+ {
+ "titel": "mehr",
+ "umenue": true,
+ "verweis": "untermenue-1.json"
+ },
+ {
+ "titel": "Info",
+ "umenue": false,
+ "funktion": "app.info_dialog_zeigen"
+ }
+ ]
+ }
+}
diff --git a/ui/data/menu/untermenue-1.json b/ui/data/menu/untermenue-1.json
new file mode 100644
index 0000000..24f547c
--- /dev/null
+++ b/ui/data/menu/untermenue-1.json
@@ -0,0 +1,27 @@
+{
+ "menue": {
+ "menuetitel": "Untermenü 1",
+ "wurzel": false,
+ "vorgaenger": {
+ "vtitel": "Hauptmenü",
+ "vverweis": "hauptmenue.json"
+ },
+ "inhalt": [
+ {
+ "titel": "Benachrichtigung 1",
+ "umenue": false,
+ "funktion": "app.message_1"
+ },
+ {
+ "titel": "noch mehr",
+ "umenue": true,
+ "verweis": "untermenue-2.json"
+ },
+ {
+ "titel": "Benachrichtigung 2",
+ "umenue": false,
+ "funktion": "app.message_2"
+ }
+ ]
+ }
+}
diff --git a/ui/data/menu/untermenue-2.json b/ui/data/menu/untermenue-2.json
new file mode 100644
index 0000000..00c0268
--- /dev/null
+++ b/ui/data/menu/untermenue-2.json
@@ -0,0 +1,27 @@
+{
+ "menue": {
+ "menuetitel": "Untermenü 2",
+ "wurzel": false,
+ "vorgaenger": {
+ "vtitel": "Untermenü 1",
+ "vverweis": "untermenue-1.json"
+ },
+ "inhalt": [
+ {
+ "titel": "Funktion U2.1",
+ "umenue": false,
+ "funktion": "app.message_3('U2.1')"
+ },
+ {
+ "titel": "Funktion U2.2",
+ "umenue": false,
+ "funktion": "app.message_3('U2.2')"
+ },
+ {
+ "titel": "Funktion U2.3",
+ "umenue": false,
+ "funktion": "app.message_3('U2.3')"
+ }
+ ]
+ }
+}
diff --git a/ui/data/tpl/app-menu.tpl b/ui/data/tpl/app-menu.tpl
new file mode 100644
index 0000000..de9b1e4
--- /dev/null
+++ b/ui/data/tpl/app-menu.tpl
@@ -0,0 +1,20 @@
+{{#menue}}
+ <p class="app-menu-kopf">
+ {{menuetitel}}
+ </p>
+ <ul class="app-menu">
+ {{^wurzel}}
+ <li class="app-menu-item-back bitem" data-verweis="{{vorgaenger.vverweis}}">
+ ❮ {{vorgaenger.vtitel}}</i>
+ </li>
+ {{/wurzel}}
+ {{#inhalt}}
+ {{#umenue}}
+ <li class="app-menu-item smenu" data-verweis="{{verweis}}">{{titel}} ❯</li>
+ {{/umenue}}
+ {{^umenue}}
+ <li class="app-menu-item mitem" data-verweis="{{funktion}}" >{{titel}} <span class="app-menu-item-submark">❯</span></i></li>
+ {{/umenue}}
+ {{/inhalt}}
+ </ul>
+{{/menue}}
\ No newline at end of file
diff --git a/ui/data/tpl/dlg-info.tpl b/ui/data/tpl/dlg-info.tpl
new file mode 100644
index 0000000..692ace2
--- /dev/null
+++ b/ui/data/tpl/dlg-info.tpl
@@ -0,0 +1,8 @@
+<div class="dlg-info">
+ <span class="close-btn pointer-cursor">✖</span>
+ <div class="dlg-behaelter">
+ <div class="dlg-info-app-titel">app-vorlage</div>
+ <div class="dlg-info-app-info">Eine Vorlage für Apps von <a href='https://uhilger.de'>Ulrich Hilger</a>.</div>
+ <div class="dlg-info-app-info">Weitere Infos im <a href='/gitblit/docs/web!app-vorlage.git'>Code-Repository</a>.</div>
+ </div>
+</div>
diff --git a/ui/hamburger.css b/ui/hamburger.css
new file mode 100644
index 0000000..c311070
--- /dev/null
+++ b/ui/hamburger.css
@@ -0,0 +1,99 @@
+/*!
+ * entnommen aus
+ *
+ * Hamburgers
+ * @description Tasty CSS-animated hamburgers
+ * @author Jonathan Suh @jonsuh
+ * @site https://jonsuh.com/hamburgers
+ * @link https://github.com/jonsuh/hamburgers
+ */
+
+.hamburger {
+ display: inline-block;
+ cursor: pointer;
+ transition-property: opacity, filter;
+ transition-duration: 0.15s;
+ transition-timing-function: linear;
+ font: inherit;
+ color: inherit;
+ text-transform: none;
+ background-color: transparent;
+ border: 0;
+ margin: 0;
+ overflow: visible;
+}
+
+.hamburger:hover {
+ opacity: 0.7;
+}
+
+.hamburger-box {
+ width: 40px;
+ height: 24px;
+ display: inline-block;
+ position: relative;
+}
+
+.hamburger-inner {
+ display: block;
+ top: 50%;
+ margin: 0;
+}
+
+.hamburger-inner, .hamburger-inner::before, .hamburger-inner::after {
+ width: 30px;
+ height: 4px;
+ background-color: white; /* #000; */
+ border-radius: 4px;
+ position: absolute;
+ transition-property: transform;
+ transition-duration: 0.15s;
+ transition-timing-function: ease;
+}
+
+.hamburger-inner::before, .hamburger-inner::after {
+ content: "";
+ display: block;
+}
+
+.hamburger-inner::before {
+ top: -10px;
+}
+
+.hamburger-inner::after {
+ bottom: -10px;
+}
+
+/*
+ * Elastic
+ */
+.hamburger--elastic .hamburger-inner {
+ top: 2px;
+ transition-duration: 0.275s;
+ transition-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55);
+}
+
+.hamburger--elastic .hamburger-inner::before {
+ top: 10px;
+ transition: opacity 0.125s 0.275s ease;
+}
+
+.hamburger--elastic .hamburger-inner::after {
+ top: 20px;
+ transition: transform 0.275s cubic-bezier(0.68, -0.55, 0.265, 1.55);
+}
+
+.hamburger--elastic.is-active .hamburger-inner {
+ transform: translate3d(0, 10px, 0) rotate(135deg);
+ transition-delay: 0.075s;
+}
+
+.hamburger--elastic.is-active .hamburger-inner::before {
+ transition-delay: 0s;
+ opacity: 0;
+}
+
+.hamburger--elastic.is-active .hamburger-inner::after {
+ transform: translate3d(0, -20px, 0) rotate(-270deg);
+ transition-delay: 0.075s;
+}
diff --git a/ui/index.html b/ui/index.html
new file mode 100644
index 0000000..d206386
--- /dev/null
+++ b/ui/index.html
@@ -0,0 +1,79 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>App-Vorlage</title>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <meta name="apple-mobile-web-app-capable" content="yes" />
+ <link href="https://fonts.googleapis.com/css?family=Roboto+Condensed" rel="stylesheet">
+ <link rel="stylesheet" type="text/css" href="app-menu.css">
+ <link rel="stylesheet" type="text/css" href="hamburger.css">
+ <link rel="stylesheet" type="text/css" href="app.css">
+ </head>
+ <body>
+ <!-- Kopfzeile -->
+ <div class="nord">
+ <div id="nav-menu">
+ <div id="nav-toggle" class="hamburger hamburger--elastic">
+ <div class="hamburger-box">
+ <div class="hamburger-inner"></div>
+ </div>
+ </div>
+ </div>
+ <div class="app-titel">
+ <span id="app-titel">App-Vorlage</span>
+ </div>
+ </div>
+ <div class="inhalt">
+ <!-- westliche Seitenleiste -->
+ <div class="west">
+ westliche Seitenleiste
+ </div>
+ <div class="zentrum-behaelter">
+ <!-- Einblendbereich -->
+ <div class="dialog"></div>
+ <!-- zentraler Inhaltsbereich -->
+ <div class="zentrum">
+ <div class="zentraler-inhalt">
+ <p>
+ Hier kann beliebiger Inhalt erscheinen.
+ </p>
+ <p>
+ Wenn dessen Darstellung mehr
+ Platz benötigt als das Anzeigegerät bietet wird ein
+ Rollbalken eingeblendet. Beim Rollen zu anfangs nicht sichtbaren
+ Teilen des Inhalts bleiben die den Inhaltsbereich
+ umschließenden Elemente sichtbar.
+ </p>
+ <p>
+ Ein Klick auf das Hamburger-Piktogramm oben links bzw. dessen
+ Antippen blendet ein Menü ein von dem aus weitere Funktionen
+ ausgelöst werden können.
+ </p>
+ </div>
+ </div>
+ </div>
+ <!-- oestliche Seitenleiste -->
+ <div class="ost ost-open">
+ östliche Seitenleiste
+ </div>
+ </div>
+ <!-- Fusszeile -->
+ <div class="sued sued-open">
+ Fußzeile
+ </div>
+ <!-- Skripte -->
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/2.3.0/mustache.min.js"></script>
+ <script src="js/app-menu.js"></script>
+ <!-- <script src="js/vorlagen.js"></script> -->
+ <script src="js/app.js"></script>
+ <script>
+ var app;
+ document.addEventListener('DOMContentLoaded', function () {
+ app = new AppVorlage();
+ app.init();
+ });
+ </script>
+ </body>
+</html>
+
diff --git a/ui/js/app-menu.js b/ui/js/app-menu.js
new file mode 100644
index 0000000..6a3b11e
--- /dev/null
+++ b/ui/js/app-menu.js
@@ -0,0 +1,137 @@
+function AppMenu() {
+ var self = this;
+ var _app_menu_selector;
+ var _app_menu_mbreite;
+ var _app_menu_url_prefix = "";
+ var _app_menu_template;
+
+ /*
+ * die nachfolgenden Funktionen steuern das ein- und
+ * ausblenden des menues
+ */
+ this.init = function (url_prefix, mdesc, mtpl, mselector, mbreite) {
+ self._app_menu_selector = mselector;
+ self._app_menu_mbreite = mbreite;
+ var menu = document.querySelector(self._app_menu_selector);
+ menu.style.flexBasis = '0em';
+ self._app_menu_url_prefix = url_prefix;
+ /*
+ Die Menue-Vorlage wird einmal zu Beginn geladen und
+ waehrend dem Programmlauf immer wieder neu zum Rendern
+ einer dynamisch gelandenen Menuebeschreibung verwendet
+ */
+ var request = new XMLHttpRequest();
+ request.open("GET", mtpl);
+ request.addEventListener('load', function(event) {
+ if (request.status >= 200 && request.status < 300) {
+ self._app_menu_template = request.responseText;
+ Mustache.parse(self._app_menu_template); // optional, speeds up future uses
+ self.app_menu_laden(mdesc);
+ } else {
+ console.warn(request.statusText, request.responseText);
+ }
+ });
+ request.send();
+ };
+
+ this.app_menu_do_toggle = function(elem) {
+ self.toggle();
+ };
+
+ this.toggle = function() {
+ var menuDiv = document.querySelector(self._app_menu_selector);
+ if(menuDiv.classList.contains('app-menu-open')) {
+ menuDiv.classList.remove('app-menu-open');
+ menuDiv.style.flexBasis = '0em';
+ } else {
+ menuDiv.classList.add('app-menu-open');
+ menuDiv.style.flexBasis = self._app_menu_mbreite;
+ }
+ };
+
+ /*
+ * ab hier Steuerung des Menueinhalts
+ */
+
+
+ /*
+ * Menuebeschreibung als JSON-Datei laden
+ * mdesc: der URL einer JSON-Datei mit einer Menuebeschreibung
+ * richtung: z.Zt. unbenutzt: Animationsrichtung
+ */
+ this.app_menu_laden = function(mdesc, richtung) {
+ var xmlhttp = new XMLHttpRequest();
+ var url = self._app_menu_url_prefix + mdesc;
+ xmlhttp.onreadystatechange = function() {
+ if (this.readyState == 4 && this.status == 200) {
+ self.app_menu_bauen(JSON.parse(this.responseText), richtung);
+ }
+ };
+ xmlhttp.open("GET", url, true);
+ xmlhttp.send();
+ };
+
+ /*
+ Aus einer Menuebeschreibung im JSON-Format mit Hilfe
+ von Mustache und der zu Beginn geladenen HTML-Vorlage
+ ein div-Element zusammenbauen, das als Menue eingeblendet
+ werden kann und dem Element _app_menu_selector hinzufuegen
+ */
+ this.app_menu_bauen = function(menuejs, richtung) {
+
+ // neues Menue als div-Element zusammensetzen
+ var menuDiv = document.createElement("div");
+ menuDiv.classList.add('app-menu-content');
+ menuDiv.style.position = 'relative';
+ menuDiv.innerHTML = Mustache.render(self._app_menu_template, menuejs);
+
+ // altes Menue loeschen
+ self.app_menu_remove_event_listener_multi('.smenu', 'click', self.app_menu_klick_herunter);
+ self.app_menu_remove_event_listener_multi('.bitem', 'click', self.app_menu_klick_herauf);
+ self.app_menu_remove_event_listener_multi('.mitem', 'click', self.app_menu_ausfuehren);
+ var menu = document.querySelector(self._app_menu_selector);
+ menu.innerHTML = '';
+
+ // neues Menue hinzufuegen
+ menu.append(menuDiv);
+ self.app_menu_add_event_listener_multi('.smenu', 'click', self.app_menu_klick_herunter);
+ self.app_menu_add_event_listener_multi('.bitem', 'click', self.app_menu_klick_herauf);
+ self.app_menu_add_event_listener_multi('.mitem', 'click', self.app_menu_ausfuehren);
+
+ menuDiv = document.querySelector('.app-menu-content');
+ menuDiv.classList.add('slidein-from-right');
+ };
+
+ this.app_menu_klick_herunter = function() {
+ self.app_menu_laden(this.getAttribute('data-verweis'), 'herunter');
+ };
+
+ this.app_menu_klick_herauf = function() {
+ self.app_menu_laden(this.getAttribute('data-verweis'), 'herauf');
+ };
+
+ this.app_menu_ausfuehren = function() {
+ var functionName = this.getAttribute('data-verweis');
+ eval(functionName + "(this)");
+ };
+
+ /* --- Helferlein ---*/
+ /*
+ sel - '.smenu'
+ evt - 'click' fuer onclick
+ func - der verweis auf die funktion
+ */
+ this.app_menu_remove_event_listener_multi = function(sel, evt, func) {
+ var elem = document.querySelectorAll(sel);
+ for (var index = 0; index < elem.length; index++) {
+ elem[index].removeEventListener(evt, func);
+ }
+ };
+
+ this.app_menu_add_event_listener_multi = function(sel, evt, func) {
+ var elem = document.querySelectorAll(sel);
+ for (var index = 0; index < elem.length; index++) {
+ elem[index].addEventListener(evt, func);
+ }
+ };
+}
diff --git a/ui/js/app.js b/ui/js/app.js
new file mode 100644
index 0000000..4eaffef
--- /dev/null
+++ b/ui/js/app.js
@@ -0,0 +1,206 @@
+function AppVorlage() {
+ var self = this;
+ var appMenu;
+ // var vorlagen;
+ var cache; // mustache templates
+
+
+ this.init = function() {
+ //self.vorlagen = new Vorlagen();
+ self.cache = new Array();
+ self.appMenu = new AppMenu();
+ self.appMenu.init(
+ "data/menu/",
+ "hauptmenue.json",
+ "data/tpl/app-menu.tpl",
+ ".west",
+ "8em");
+
+ document.querySelector('.hamburger').addEventListener('click', function(e) {
+ self.menue_umschalten();
+ });
+
+ };
+
+ this.menue_umschalten = function() {
+ var ham = document.querySelector(".hamburger");
+ ham.classList.toggle("is-active"); // hamburger-icon umschalten
+ self.appMenu.toggle(); // menue oeffnen/schliessen
+ };
+
+ this.info_dialog_zeigen = function() {
+ self.dialog_laden_und_zeigen('data/tpl/dlg-info.tpl', '');
+ self.menue_umschalten();
+ };
+
+ this.seitenleiste_umschalten = function() {
+ var ostDiv = document.querySelector('.ost');
+ if(ostDiv.classList.contains('ost-open')) {
+ ostDiv.classList.remove('ost-open');
+ ostDiv.style.flexBasis = '0em';
+ } else {
+ ostDiv.classList.add('ost-open');
+ ostDiv.style.flexBasis = '6em';
+ }
+ self.menue_umschalten();
+ };
+
+ this.fusszeile_umschalten = function() {
+ var suedDiv = document.querySelector('.sued');
+ if(suedDiv.classList.contains('sued-open')) {
+ suedDiv.classList.remove('sued-open');
+ suedDiv.style.height = '0';
+ } else {
+ suedDiv.classList.add('sued-open');
+ suedDiv.style.height = '1.5em';
+ }
+ self.menue_umschalten();
+ };
+
+ this.menu_message = function(msg) {
+ self.meldung_mit_timeout(msg, 1500);
+ var suedDiv = document.querySelector('.sued');
+ if(suedDiv.classList.contains('sued-open')) {
+ } else {
+ suedDiv.classList.add('sued-open');
+ suedDiv.style.height = '1.5em';
+ }
+ self.menue_umschalten();
+ };
+
+ this.message_1 = function() {
+ self.menu_message('Eine Mitteilung.');
+ };
+
+ this.message_2 = function() {
+ self.menu_message('Was wir schon immer sagen wollten.');
+ };
+
+ this.message_3 = function(text) {
+ self.menu_message(text);
+ };
+
+ this.meldung_mit_timeout = function(meldung, timeout) {
+ var s = document.querySelector('.sued');
+ s.textContent = meldung;
+ setTimeout(function() {
+ s.textContent = 'Bereit.';
+ setTimeout(function() {
+ var suedDiv = document.querySelector('.sued');
+ if(suedDiv.classList.contains('sued-open')) {
+ suedDiv.classList.remove('sued-open');
+ suedDiv.style.height = '0';
+ }
+ }, 500);
+ }, timeout);
+ };
+
+ /* Dialog-Funktionen */
+
+ /*
+ Einen Dialog aus Vorlagen erzeugen
+
+ vurl - URL zur Dialogvorlage
+ msgTpl - URL mit einer Vorlage eines Mitteilungstextes (optional)
+ */
+ this.dialog_laden_und_zeigen = function(vurl, msgTpl) {
+ if(msgTpl !== '') {
+ fetch(msgTpl)
+ .then(data => {
+ // Handle data
+ self.dialog_zeigen(vurl, data);
+ }).catch(error => {
+ // Handle error
+ });
+ } else {
+ self.dialog_zeigen(vurl, '');
+ }
+ };
+
+ this.dialog_zeigen = function(vurl, inhalt) {
+ var dlg = document.querySelector(".dialog");
+ self.html_erzeugen(
+ vurl,
+ inhalt,
+ function(html) {
+ //dlg.html(html);
+ dlg.style.height = '5em';
+ dlg.innerHTML = html;
+ document.querySelector('.close-btn').addEventListener('click', self.dialog_schliessen);
+ //dlg.slideDown(300);
+ });
+ };
+
+ self.dialog_schliessen = function() {
+ document.querySelector('.close-btn').removeEventListener('click', self.dialog_schliessen);
+ //$('.dialog').slideUp(300);
+ var dlg = document.querySelector('.dialog');
+ //dlg.style.display = "none";
+ dlg.style.height = '0';
+ dlg.innerHTML = '';
+ };
+
+ /* Vorlagen */
+
+ /*
+ Das HTML erzeugen, das entsteht, wenn eine Vorlage mit Inhalt
+ gefüllt wird
+
+ Das Füllen erfolgt asynchron, d.h. der Programmlauf geht nach dem
+ Aufruf weiter ohne auf das Laden und Füllen der Vorlage zu warten.
+ Das fertige HTML wird der Callback-Funktion übergeben
+ sobald die Vorlage geladen und gefüllt ist, unabhängig davon, wo der
+ Programmlauf zu diesem Zeitpunkt mittlerweile ist.
+
+ vurl - URL zur Vorlagendatei
+ inhalt - die JSON-Struktur, deren Inhalt in die
+ Vorlage gefüllt werden soll
+ cb - Callback-Funktion, die gerufen wird, wenn die Vorlage gefüllt ist.
+ Dieser Callback-Funktion wird das fertige HTML übergeben
+ */
+ this.html_erzeugen = function(vurl, inhalt, cb) {
+ var vorlage = self.cache[vurl];
+ if(vorlage === undefined) {
+ self.vorlage_laden_und_fuellen(vurl, inhalt, cb);
+ } else {
+ self.vorlage_fuellen(vurl, inhalt, cb);
+ }
+ };
+
+ this.vorlage_fuellen = function(vurl, inhalt, cb) {
+ cb(Mustache.render(self.cache[vurl], inhalt));
+ };
+
+ /*
+ Eine Vorlage vom Server in den lokalen Speicher laden
+ vurl - der URL unter dem die Vorlage zu finden ist
+ inhalt - die JSON-Struktur, deren Inhalt in die
+ Vorlage gefüllt werden soll
+ cb - callback: Diese Funktion wird gerufen, wenn die Vorlage mit dem
+ Inhalt gefüllt ist
+ */
+ this.vorlage_laden_und_fuellen = function(vurl, inhalt, cb) {
+ /*
+ $.ajax({
+ url: vurl,
+ type: "GET",
+ dataType : "text"
+ }).done(function( vorlage ) {
+ self.cache[vurl] = vorlage;
+ self.vorlage_fuellen(vurl, inhalt, cb);
+ });
+ */
+ var xmlhttp = new XMLHttpRequest();
+ xmlhttp.onreadystatechange = function() {
+ if (this.readyState == 4 && this.status == 200) {
+ self.cache[vurl] = this.responseText;
+ self.vorlage_fuellen(vurl, inhalt, cb);
+ }
+ };
+ xmlhttp.open("GET", vurl, true);
+ xmlhttp.send();
+ };
+
+
+}
+
--
Gitblit v1.9.3