Persoenliche Mediazentrale
ulrich
2021-04-11 095119ef9193968d088911688d18e03abc6d3b3a
commit | author | age
b379f5 1 function Mediazentrale() {
cfa858 2   var self = this;
U 3   var appMenu;
4   var cache; // mustache templates
86bbf7 5   var ortPfad;
U 6   var mediaPfad;
e60cff 7   var katUrl;
U 8   var selTitel;
78d707 9   var katName;
f45e20 10
U 11   this.init = function () {
12     self.mediaPfad = '/';
13     self.ortPfad = '/';
14     self.cache = new Array();
15     self.appMenu = new AppMenu();
16     self.appMenu.init(
17             "data/menu/",
18             "hauptmenue.json",
78d707 19             "data/tpl/app-menu.txt",
f45e20 20             ".west",
U 21             "8em");
22
23     document.querySelector('.hamburger').addEventListener('click', function (e) {
24       self.menue_umschalten();
25     });
26     
27     self.addEvtListener('#mi-katalog', 'click', self.media_liste);
28     self.addEvtListener('#mi-orte', 'click', self.ablageort_liste);
29     self.addEvtListener('#mi-prefs', 'click', self.prefs_liste);
30     self.addEvtListener('#mi-player', 'click', self.abspieler_liste);
8d7d35 31     self.addEvtListener('#mi-listen', 'click', self.abspielliste_liste);
U 32     self.addEvtListener('#mi-list', 'click', self.titel_liste);
f45e20 33     
U 34     self.fusszeile_umschalten();
35     self.seitenleiste_umschalten();
36     self.dialog_unten_zeigen();
37   };
38   
8d7d35 39   /* ---------------- Entitaets-Listen ----------------- */
U 40
86bbf7 41   // auf der obersten Ebene werden die Kataloge angezeigt,
U 42   // darunter der Inhalt des aktuellen Pfades
43   this.media_liste = function() {
78d707 44     self.reset_top_buttons(); 
095119 45     //console.log("ortPfad: " + self.ortPfad + ", mediaPfad: " + self.mediaPfad);
78d707 46     document.querySelector('.bereich-name').textContent = '';    
86bbf7 47     if(self.ortPfad === '/') {
78d707 48       var bb = document.querySelector('.breadcrumb-behaelter');
U 49       bb.textContent = "Kataloge";
86bbf7 50       // Kataloge listen
f45e20 51       self.http_get('../api/store/Ablageort/liste/', function (responseText) {
8d7d35 52         //document.querySelector('#top-up-btn').removeEventListener('click', self.media_liste_herauf);
78d707 53         self.vorlage_laden_und_fuellen("data/tpl/katalog_root_liste.txt", JSON.parse(responseText), function (html) {
86bbf7 54           document.querySelector(".zentraler-inhalt").innerHTML = html;
U 55           self.addEvtListener('.entity-eintrag', 'click', function (event) {
56             var t = event.target;
78d707 57             self.katName = t.textContent;
86bbf7 58             self.http_get('../api/store/Ablageort/' + t.textContent, function(responseText) {
U 59               var ablageort = JSON.parse(responseText);
60               self.ortPfad = ablageort.url;
61               self.media_liste();
62             });
63           });
64         });
65       });
66     } else {
78d707 67       var bb = document.querySelector('.breadcrumb-behaelter');
U 68       var brPfad = self.katName + self.mediaPfad;
69       var breadcrumbs = brPfad.split('/');
70       var brLinks = "";
71       var brLinkPfad = "";
72       for(var index = 0; index < breadcrumbs.length; index++) {
73         // <a class="breadcrumb-link" href="#">breadcrumbs[index]</a>
74         if(index === 0) {
75           brLinkPfad = '/';
76         } else {
77           brLinkPfad = brLinkPfad + '/' + breadcrumbs[index];
78         }
79         brLinks = brLinks + "<a brlink='" + brLinkPfad + "' class='breadcrumb-link' href='#'>" + breadcrumbs[index] + "</a>";
80         //console.log('   breadcrumbs[' + index + ']: ' + breadcrumbs[index]);
81       }
82       bb.innerHTML = brLinks;
83       self.addEvtListener('.breadcrumb-link', 'click', function(event) {
095119 84         //console.log(event.target.attributes.brlink.nodeValue);
78d707 85         var neuerPfad = event.target.attributes.brlink.nodeValue;
U 86         self.mediaPfad = neuerPfad;
87         self.media_liste();
88       });
3271f1 89       var url = '..' + self.ortPfad + self.mediaPfad;
U 90       if(!url.endsWith('/')) {
91         url = url + '/';
92       }
93       self.http_get(url, function(responseText) {
78d707 94         self.vorlage_laden_und_fuellen("data/tpl/katalog_inhalt_liste.txt", JSON.parse(responseText), function (html) {
86bbf7 95           document.querySelector(".zentraler-inhalt").innerHTML = html;
U 96           self.addEvtListener('.entity-eintrag', 'click', function (event) {
97             var t = event.target;
3271f1 98             var tx = t.textContent;
86bbf7 99             if(t.classList.contains("entity-typ-folder")) {
3271f1 100               if(self.mediaPfad.endsWith('/')) {
U 101                 self.mediaPfad = self.mediaPfad + tx;                
102               } else {
103                 self.mediaPfad = self.mediaPfad + '/' + tx;
104               }
86bbf7 105               self.media_liste();
U 106             } else {
37eadf 107               if(t.classList.contains('selected')) {
78d707 108                 t.classList.add('added-to-playlist');
095119 109                 self.titelDazu();
37eadf 110               } else {
U 111                 self.removeClassMulti('selected');
112                 t.classList.add('selected');
113               }
e60cff 114               //self.selTitel = new Titel(t.textContent, self.ortPfad);       
86bbf7 115             }
U 116           });
8d7d35 117           self.addEvtListener('#top-up-btn', 'click', function(event) {
86bbf7 118             if(self.mediaPfad === '/') {
U 119               self.ortPfad = '/';              
120             } else {
121               var pos = self.mediaPfad.lastIndexOf('/');
3271f1 122               var parent;
U 123               if(pos > 1) {
124                 parent = self.mediaPfad.substring(0, pos);
125               } else {
126                 parent = '/';
127               }
86bbf7 128               self.mediaPfad = parent;
U 129             }
130             self.media_liste();
8d7d35 131           });
86bbf7 132         });
U 133       });
134     }
e60cff 135   };
245ac1 136   
a43e1a 137   this.ablageort_liste = function() {
78d707 138     self.entitaet_liste('Kataloge','../api/store/Ablageort/liste/', 
U 139       "data/tpl/ablageort_liste.txt", '../api/store/Ablageort/', 
e44ed0 140       "self.ablageort_form", function(responseText) {
U 141         var ablageort = JSON.parse(responseText);
142         self.ablageort_form(ablageort);
143       });
cf6509 144   };
U 145
146   this.prefs_liste = function() {
50e53e 147     self.entitaet_liste('Einstellungen','../api/store/Einstellung/liste/', 
78d707 148       "data/tpl/einstellung_liste.txt", '../api/store/Einstellung/', 
e44ed0 149       "self.prefs_form", function(responseText) {
U 150         var einstellung = JSON.parse(responseText);
151         self.prefs_form(einstellung);
152       });
cf6509 153   };
U 154
3d4bca 155   this.abspieler_liste = function() {
50e53e 156     self.entitaet_liste('Abspieler','../api/store/Abspieler/liste/', 
78d707 157       "data/tpl/abspieler_liste.txt", '../api/store/Abspieler/', 
e44ed0 158       "self.abspieler_form", function(responseText) {
U 159         var abspieler = JSON.parse(responseText);
160         self.abspieler_form(abspieler);
161       });
8d7d35 162   };
U 163   
164   this.abspielliste_liste = function() {
50e53e 165     self.entitaet_liste('Abspielliste','../api/store/Abspielliste/liste/', 
78d707 166       "data/tpl/abspielliste_liste.txt", '../api/store/Abspielliste/', 
e44ed0 167       "self.abspielliste_form", function(responseText) {
095119 168         //console.log("responseTest: '" + responseText + "'");
e44ed0 169         var abspielliste = JSON.parse(responseText);
U 170         self.abspielliste_form(abspielliste);
171       });
8d7d35 172   };
U 173   
c7030d 174   /* -------------------- Entitaets-Formulare ------------------ */  
8d7d35 175   
U 176   this.abspielliste_form = function(al) {
50e53e 177     self.entitaet_form('Abspielliste', al, al.name,
78d707 178       "data/tpl/form_abspielliste.txt", '../api/store/Abspielliste/',
9865bd 179       '#abspielliste-name', function(event) {
095119 180           if(event !== undefined) {
U 181             event.preventDefault();
182           }
c7030d 183           self.abspielliste_auswahl_fuellen();
U 184           self.abspielliste_liste();
185     });
3d4bca 186   };
71def1 187   
3d4bca 188   this.abspieler_form = function(pl) {
50e53e 189     self.entitaet_form('Abspieler', pl, pl.key,
78d707 190       "data/tpl/form_abspieler.txt", '../api/store/Abspieler/',
c7030d 191       '#abspieler-name', function() { 
U 192           self.abspieler_auswahl_fuellen();
193           self.abspieler_liste();
194     });
71def1 195   };
U 196
cf6509 197   this.prefs_form = function(k) {
78d707 198     self.entitaet_form('Einstellung', k, k.key,
U 199       "data/tpl/form_einstellung.txt", '../api/store/Einstellung/',
c7030d 200       '#einstellung-key', function() { 
U 201           self.prefs_liste();
202     });
a43e1a 203   };
cfa858 204
90f5d4 205   /* 
U 206    * Ablageort-Formular anzeigen
207    * 
208    * {"name":"Katalog 2","ort":"/home/ulrich/Videos","url":"/media/kat2"}: 
209    * 
210    * @param {type} ablageort  der Ablageort, der bearbeitet werden soll, leer fuer neuen Ort
211    * @returns {undefined} kein Rueckgabewert
212    */
213   this.ablageort_form = function(ort) {
78d707 214     self.entitaet_form('Katalog', ort, ort.name,
U 215       "data/tpl/form_ablageort.txt", '../api/store/Ablageort/',
c7030d 216       '#ablageort-name', function() { 
U 217         self.ablageort_liste();
218     });
b379f5 219   };
48f8f9 220   
748b6f 221   /* ------------------------------- UI-Dynamik ----------------------- */
48f8f9 222   
748b6f 223   self.reset_top_buttons = function() {
78d707 224     self.vorlage_laden_und_fuellen("data/tpl/top_btns.txt", '', function (html) {
748b6f 225       document.querySelector(".top-btns").innerHTML = html;
U 226     });
227   };
228   
229   this.abspieler_auswahl_fuellen = function() {
b56bb3 230     self.http_get('../api/store/Abspieler/liste/', function (responseText) {
78d707 231       self.vorlage_laden_und_fuellen("data/tpl/abs_sel.txt", JSON.parse(responseText), function (html) {
748b6f 232         document.querySelector(".abs-sel").innerHTML = html;
U 233       });    
234     });
235   };
236
237   this.abspielliste_auswahl_fuellen = function() {
238     self.http_get('../api/store/Abspielliste/', function (responseText) {
78d707 239       self.vorlage_laden_und_fuellen("data/tpl/pl_sel.txt", JSON.parse(responseText), function (html) {
748b6f 240         document.querySelector(".pl-sel").innerHTML = html;
b56bb3 241         self.addEvtListener('#playlist', 'change', function() {
U 242           self.titel_liste();
243         });
748b6f 244       });    
U 245     });
246   };
247   
248   /* Unterer Einblendbereich */
249   
250   this.dialog_unten_zeigen = function() {
78d707 251     self.vorlage_laden_und_fuellen("data/tpl/ctrl.txt", "", function (html) {
748b6f 252       var dlg = document.querySelector(".dialog-unten");
U 253       dlg.style.height = '4.5em';
254       dlg.innerHTML = html;
255       self.abspieler_auswahl_fuellen();
256       self.abspielliste_auswahl_fuellen();
095119 257       self.addEvtListener('#dazu-btn', 'click', self.titelDazu);
b56bb3 258       self.addEvtListener('#play-btn', 'click', self.play);
095119 259       self.addEvtListener('#weg-btn', 'click', self.titelWeg);
U 260       self.addEvtListener('#leeren-btn', 'click', self.alleTitelEntfernen);
748b6f 261       self.media_liste();
U 262     });
263   };
264   
265   /* Titel einer Abspielliste */
266   
267   this.titel_liste = function() {
268     self.reset_top_buttons();
269     var plname = document.querySelector('#playlist').value;
78d707 270     document.querySelector('.bereich-name').textContent = 'Abspielliste ' + plname;
U 271     var bb = document.querySelector('.breadcrumb-behaelter');
272     bb.textContent = "";
748b6f 273     self.http_get('../api/alist/' + plname, function (responseText) {
78d707 274       self.vorlage_laden_und_fuellen("data/tpl/titel_liste.txt", JSON.parse(responseText), function (html) {
748b6f 275         document.querySelector(".zentraler-inhalt").innerHTML = html;
U 276         self.addEvtListener('.entity-eintrag', 'click', function (event) {
277           var t = event.target;
278           self.removeClassMulti('selected');
279           t.classList.add('selected');
280         });
281       });
282     });
283   };  
284   
b56bb3 285   /* ------------- Media-Steuerung ------------------------- */
U 286   
287   this.play = function() {
288     var abs = document.querySelector('#abspieler').value;
289     var lst = document.querySelector('#playlist').value;
290     console.log(
291       "play playlist.value: " + document.querySelector('#playlist').value + 
292       ", abspieler.value: " + document.querySelector('#abspieler').value);
293     self.http_get('../api/strg/' + abs + '/play/liste/' + lst, function(responseText) {
294       self.meldung_mit_timeout(responseText, 1500);
295     });
095119 296   };
U 297   
298   /* ------------- Verwaltungsfunktionen Abspielliste -------------------- */
299   
300   self.alleTitelEntfernen = function() {
301     var plname = document.querySelector('#playlist').value;
302     self.http_delete('../api/alist/' + plname + '/alle', '', function(responseText) {
303       // DELETE    http://localhost:9090/mz/api/alist/liste1/0
304       //self.meldung_mit_timeout(responseText, 1500);
305       self.titel_liste();
306     });
307   };
308   
309   this.titelDazu = function() {
310     var elem = document.querySelector(".selected");
311     //var titelName = elem.textContent;
312     var titelName = elem.attributes.dateiName.nodeValue;
313     var album = elem.attributes.album.nodeValue;
314     var interpret = elem.attributes.interpret.nodeValue;
315     var anzName = elem.attributes.titelAnzName.nodeValue;
316     var titel;
317     if(self.mediaPfad.endsWith('/')) {
318       titel = new Titel(titelName, self.mediaPfad, self.ortPfad, interpret, anzName, album);
319     } else {
320       titel = new Titel(titelName, self.mediaPfad + '/', self.ortPfad, interpret, anzName, album);
321     }
322     var plname = document.querySelector('#playlist').value;
323     self.http_put('../api/alist/' + plname, JSON.stringify(titel), function(responseText) {
324       //self.meldung_mit_timeout(responseText, 1500);
325     });
326   };  
327   
328   this.titelWeg = function() {
329     var elem = document.querySelector(".selected");
330     var parentElem = elem.parentNode;
331     //console.log("elem: " + elem.nodeName + ", parent: " + parentElem.nodeName + ", len: " + parentElem.childNodes.length);
332     var liElems = parentElem.getElementsByTagName(elem.nodeName); // nur die LI Elemente
333     //console.log("liElems.anz: " + liElems.length);
334     var gefunden = false;
335     for(var i = 0; i < liElems.length && !gefunden; i++) {
336       //console.log(liElems.item(i).textContent);
337       if(liElems.item(i).classList.contains("selected")) {
338         gefunden = true;
339         var index = i;
340         //console.log(elem.textContent + ' hat Index ' + i);
341       }
342     }
343     // /mz/api/alist/[pl-name]/[nr] 
344     var plname = document.querySelector('#playlist').value;
345     self.http_delete('../api/alist/' + plname + '/' + index,'', function(responseText) {
346       // DELETE    http://localhost:9090/mz/api/alist/liste1/0
347       //self.meldung_mit_timeout(responseText, 1500);
348       self.titel_liste();
349     });
350     
b56bb3 351   };
U 352   
748b6f 353   /* ------------- Helfer fuer Entitaets-Formulare ----------------------- */
14638b 354   
U 355   /*
356    * url: '../api/store/Ablageort/liste/'
78d707 357    * tpl: "data/tpl/ablageort_liste.txt"
14638b 358    * storeUrl: '../api/store/Ablageort/'
U 359    * formFunc: "self.ablageort_form"
360    * cb: etwas wie
361    *   function(responseText){
362    *     var ablageort = JSON.parse(responseText);
363    *     self.ablageort_form(ablageort);
364    *   });
365    */
50e53e 366   this.entitaet_liste = function(bname, listUrl, tpl, storeUrl, formFunc, cb) {
14638b 367     self.reset_top_buttons();
50e53e 368     document.querySelector('.bereich-name').textContent = bname;
78d707 369     var bb = document.querySelector('.breadcrumb-behaelter');
U 370     bb.textContent = "";
14638b 371     self.http_get(listUrl, function (responseText) {
U 372       self.vorlage_laden_und_fuellen(tpl, JSON.parse(responseText), function (html) {
373         document.querySelector(".zentraler-inhalt").innerHTML = html;
374         self.addEvtListener('.entity-eintrag', 'click', function (event) {
375           var t = event.target;
376           self.http_get(storeUrl + t.textContent, cb);
377         });
378         //self.addEvtListener('#neu-btn', 'click', function (event) {
379         self.addEvtListener('#top-neu-btn', 'click', function(event) {
380           eval(formFunc + "(this)");
381         });        
382       });
383     });
384   };  
748b6f 385       
48f8f9 386   /*
c7030d 387    * dat: gefuelltes Datenobjekt bei Aenderung
U 388    * key: der alte schluesselbegriff bei Aenderung (z.B. al.name)
78d707 389    * tpl: "data/tpl/form_abspielliste.txt"
c7030d 390    * url: '../api/store/Abspielliste/'
U 391    * selector: '#abspielliste-name'
392    * cbOk: etwas wie
393    *    function() {
394    *       self.abspielliste_auswahl_fuellen();
395    *       self.abspielliste_liste();
396    *     });
397    * delSelector: '#abspielliste-name'
398    * cbDel: etwas wie
399    *    function() {
400    *       self.abspielliste_auswahl_fuellen();
401    *       self.abspielliste_liste();
402    *     });
403    */
404
50e53e 405   this.entitaet_form = function(bname, dat, key, tpl, url, selector, cb) {
U 406     document.querySelector('.bereich-name').textContent = bname;
c7030d 407     self.vorlage_laden_und_fuellen(tpl, dat, function (html) {
U 408       document.querySelector(".zentraler-inhalt").innerHTML = html;
409       const form = document.querySelector('form');      
410       form.addEventListener('submit', function(event) {
411         self.handle_submit(event, key, url, selector, cb);
412       });
413       self.addEvtListener('#cancel-btn', 'click', cb);
414       self.addEvtListener('#loeschen-btn', 'click', function(event) {
415         event.preventDefault();
416         self.handle_del_btn(selector, url, cb);
417       });
418     });
419   };
420   
421   /*
48f8f9 422    * existingKey: wenn die Entitaet existiert und geandert werden soll
U 423    *                 leer, wenn neue Entitaet 
424    */
425   this.handle_submit = function(event, existingKey, putUrl, keySelector, cb) {
426     event.preventDefault();
427     const data = new FormData(event.target);
428     const value = Object.fromEntries(data.entries());
095119 429     //console.log({ value });
U 430     //console.log(JSON.stringify(value));
48f8f9 431     var daten = JSON.stringify(value);
U 432     var formkey = document.querySelector(keySelector).value;
433     formkey = formkey.replace(' ', '').replace(/[\W]+/g, '');
434     if(typeof existingKey === "undefined" ||  existingKey.length < 1) {
435       // neu
436       self.http_put(putUrl + formkey, daten, function (responseText) {
437         if(typeof(cb) !== 'function') {
438           // ..
439         } else {
440           cb();
441         }
442       });
443     } else {
444       // aendern
445       self.http_put(putUrl + existingKey, daten, function (responseText) {
446         if(typeof(cb) !== 'function') {
447           // ..
448         } else {
449           cb();
450         }
451       });
452     }
453   };
454   
455   this.handle_del_btn = function(selectorKey, delUrl, cb) {
456     var pkey = document.querySelector(selectorKey).value;
457     var dlgdata = {"del-elem": pkey};
78d707 458     self.dialog_laden_und_zeigen('data/tpl/dlg-loeschen.txt', dlgdata, function() {
48f8f9 459       self.addEvtListener('#nein-btn', 'click', self.dialog_schliessen);
U 460       self.addEvtListener('#ja-btn', 'click', function(event) {
095119 461         //console.log("loeschen geklickt.");
48f8f9 462         self.http_delete(delUrl + pkey, '', function (responseText) {
U 463           self.dialog_schliessen();
464           if(typeof(cb) !== 'function') {
465             // ..
466           } else {
467             cb();
468           }
469         });
470       });
471     });    
472   };  
748b6f 473   
U 474   /* ------------------ sonstige Helfer ----------------------- */
475       
7c22a2 476   this.addEvtListener = function(selector, eventName, func) {
U 477     document.querySelectorAll(selector).forEach(elem => { elem.addEventListener(eventName, func); });
478   };
479   
480   this.removeClassMulti = function(selector) {
481     document.querySelectorAll('.' + selector).forEach(elem => { elem.classList.remove(selector); });
482   };
483
f45e20 484   /* --------------------- asynchroner HTTP Client ----------------- */
faab2d 485   
f074f6 486   this.http_get = function (u, cb) {
b379f5 487     self.http_call('GET', u, null, cb);
U 488   };
f074f6 489
U 490   this.http_post = function (u, data, cb) {
b379f5 491     self.http_call('POST', u, data, cb);
U 492   };
493
90f5d4 494   this.http_put = function (u, data, cb) {
U 495     self.http_call('PUT', u, data, cb);
496   };
497   
5b7356 498   this.http_delete = function (u, data, cb) {
5f7e0b 499     // console.log("delete " + u);
5b7356 500     self.http_call('DELETE', u, data, cb);
U 501   };
502   
f074f6 503   this.http_call = function (method, u, data, scallback) {
b379f5 504     var xhr = new XMLHttpRequest();
U 505     var url = u;
f074f6 506     xhr.onreadystatechange = function () {
b379f5 507       if (this.readyState === 4 && this.status === 200) {
U 508         scallback(this.responseText);
509       }
510     };
511     xhr.open(method, url);
f074f6 512     if (method === 'GET') {
b379f5 513       xhr.send();
2597cd 514     } else if (method === 'POST' || method === 'PUT' || method === 'DELETE') {
b379f5 515       xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
U 516       xhr.send(data);
517     }
518   };
8239d1 519   
f45e20 520   /* ------------------------ aus App-Vorlage -------------------  */
cfa858 521
f074f6 522   this.menue_umschalten = function () {
cfa858 523     var ham = document.querySelector(".hamburger");
U 524     ham.classList.toggle("is-active"); // hamburger-icon umschalten
525     self.appMenu.toggle(); // menue oeffnen/schliessen
526   };
527
f074f6 528   this.info_dialog_zeigen = function () {
78d707 529     self.dialog_laden_und_zeigen('data/tpl/dlg-info.txt', '');
cfa858 530     self.menue_umschalten();
U 531   };
532
f074f6 533   this.seitenleiste_umschalten = function () {
cfa858 534     var ostDiv = document.querySelector('.ost');
f074f6 535     if (ostDiv.classList.contains('ost-open')) {
cfa858 536       ostDiv.classList.remove('ost-open');
f074f6 537       ostDiv.style.flexBasis = '0em';
cfa858 538     } else {
f074f6 539       ostDiv.classList.add('ost-open');
U 540       ostDiv.style.flexBasis = '6em';
cfa858 541     }
U 542     self.menue_umschalten();
543   };
544
f074f6 545   this.fusszeile_umschalten = function () {
cfa858 546     var suedDiv = document.querySelector('.sued');
f074f6 547     if (suedDiv.classList.contains('sued-open')) {
cfa858 548       suedDiv.classList.remove('sued-open');
f074f6 549       suedDiv.style.height = '0';
cfa858 550     } else {
U 551       suedDiv.classList.add('sued-open');
f074f6 552       suedDiv.style.height = '1.5em';
cfa858 553     }
U 554     self.menue_umschalten();
555   };
556
f074f6 557   this.menu_message = function (msg) {
cfa858 558     self.meldung_mit_timeout(msg, 1500);
U 559     var suedDiv = document.querySelector('.sued');
f074f6 560     if (suedDiv.classList.contains('sued-open')) {
cfa858 561     } else {
U 562       suedDiv.classList.add('sued-open');
f074f6 563       suedDiv.style.height = '1.5em';
cfa858 564     }
U 565     self.menue_umschalten();
566   };
567
f074f6 568   this.message_1 = function () {
cfa858 569     self.menu_message('Eine Mitteilung.');
U 570   };
571
f074f6 572   this.message_2 = function () {
cfa858 573     self.menu_message('Was wir schon immer sagen wollten.');
U 574   };
575
f074f6 576   this.message_3 = function (text) {
cfa858 577     self.menu_message(text);
U 578   };
579
f074f6 580   this.meldung_mit_timeout = function (meldung, timeout) {
cfa858 581     var s = document.querySelector('.sued');
a43e1a 582     s.classList.add('sued-open');
U 583     s.style.height = '1.5em';
cfa858 584     s.textContent = meldung;
f074f6 585     setTimeout(function () {
cfa858 586       s.textContent = 'Bereit.';
f074f6 587       setTimeout(function () {
cfa858 588         var suedDiv = document.querySelector('.sued');
f074f6 589         if (suedDiv.classList.contains('sued-open')) {
U 590           suedDiv.classList.remove('sued-open');
591           suedDiv.style.height = '0';
cfa858 592         }
U 593       }, 500);
594     }, timeout);
595   };
8239d1 596   
f45e20 597   /* --------------------- Dialog-Funktionen ------------------------ */
cfa858 598
U 599   /*
f074f6 600    Einen Dialog aus Vorlagen erzeugen
U 601    
602    vurl - URL zur Dialogvorlage
603    msgTpl - URL mit einer Vorlage eines Mitteilungstextes (optional)
604    */
5b7356 605   this.dialog_laden_und_zeigen = function (vurl, msgTpl, cb) {
U 606     var vorlage = self.cache[vurl];
607     if(vorlage === undefined) {
608       self.http_get(vurl, function(antwort) {
609         self.cache[vurl] = antwort;
610         self.dialog_zeigen(vurl, msgTpl, cb);
f074f6 611       });
cfa858 612     } else {
5b7356 613       self.dialog_zeigen(vurl, msgTpl, cb);
cfa858 614     }
U 615   };
616
5b7356 617   this.dialog_zeigen = function (vurl, inhalt, cb) {
U 618     var dlg = document.querySelector(".dialog");
2597cd 619     self.html_erzeugen(vurl, inhalt, function (html) {
U 620       dlg.style.height = '7em';
621       dlg.innerHTML = html;
622       document.querySelector('.close-btn').addEventListener('click', self.dialog_schliessen);
623       if(typeof(cb) !== 'function') {
624         // ..
625       } else {
626         cb();
627       }
628     });
5b7356 629   };
2597cd 630   
f45e20 631   this.dialog_schliessen = function () {
cfa858 632     document.querySelector('.close-btn').removeEventListener('click', self.dialog_schliessen);
U 633     var dlg = document.querySelector('.dialog');
634     dlg.style.height = '0';
635     dlg.innerHTML = '';
636   };
637
f45e20 638   /* ---------------------   Vorlagen   ---------------------- */
cfa858 639
U 640   /*
f074f6 641    Das HTML erzeugen, das entsteht, wenn eine Vorlage mit Inhalt
U 642    gefüllt wird
643    
644    Das Füllen erfolgt asynchron, d.h. der Programmlauf geht nach dem
645    Aufruf weiter ohne auf das Laden und Füllen der Vorlage zu warten.
646    Das fertige HTML wird der Callback-Funktion übergeben
647    sobald die Vorlage geladen und gefüllt ist, unabhängig davon, wo der
648    Programmlauf zu diesem Zeitpunkt mittlerweile ist.
649    
650    vurl - URL zur Vorlagendatei
651    inhalt - die JSON-Struktur, deren Inhalt in die
652    Vorlage gefüllt werden soll
653    cb - Callback-Funktion, die gerufen wird, wenn die Vorlage gefüllt ist.
654    Dieser Callback-Funktion wird das fertige HTML übergeben
655    */
656   this.html_erzeugen = function (vurl, inhalt, cb) {
cfa858 657     var vorlage = self.cache[vurl];
f074f6 658     if (vorlage === undefined) {
cfa858 659       self.vorlage_laden_und_fuellen(vurl, inhalt, cb);
U 660     } else {
661       self.vorlage_fuellen(vurl, inhalt, cb);
662     }
663   };
664
f074f6 665   this.vorlage_fuellen = function (vurl, inhalt, cb) {
cfa858 666     cb(Mustache.render(self.cache[vurl], inhalt));
U 667   };
668
669   /*
f074f6 670    Eine Vorlage vom Server in den lokalen Speicher laden
U 671    vurl - der URL unter dem die Vorlage zu finden ist
672    inhalt - die JSON-Struktur, deren Inhalt in die
673    Vorlage gefüllt werden soll
674    cb - callback: Diese Funktion wird gerufen, wenn die Vorlage mit dem
675    Inhalt gefüllt ist
676    */
677   this.vorlage_laden_und_fuellen = function (vurl, inhalt, cb) {
cfa858 678     var xmlhttp = new XMLHttpRequest();
f074f6 679     xmlhttp.onreadystatechange = function () {
cfa858 680       if (this.readyState == 4 && this.status == 200) {
U 681         self.cache[vurl] = this.responseText;
682         self.vorlage_fuellen(vurl, inhalt, cb);
683       }
684     };
685     xmlhttp.open("GET", vurl, true);
686     xmlhttp.send();
687   };
688
689
690 }
691
f45e20 692 /* ----------- Objekte ---------------- */
U 693
694 function Ablageort(n, o, u) {
695   this.name = n;
696   this.ort = o;
697   this.url = u;
698 }
699
700 function Einstellung(k, v) {
701   this.key = k;
702   this.value = v;
703 }
704
705 function Abspieler(n, u) {
706   this.name = n;
707   this.url = u;
8d7d35 708 }
U 709
710 function Abspielliste(n) {
711   this.name = n;
e60cff 712 }
U 713
245ac1 714 function Titel(n, p, u, i, t, a) {
e60cff 715   this.katalogUrl = u;
2bdd78 716   this.pfad = p;
e60cff 717   this.name = n;
245ac1 718   this.interpret = i;
U 719   this.titelAnzName = t;
720   this.album  = a;
d6b78c 721 }