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