Persoenliche Mediazentrale
ulrich
2021-04-12 1c5fa42ec09b357f922d882739b80b6630fd83f7
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);
9e14ef 259       self.addEvtListener('#stop-btn', 'click', function() {
U 260         self.kommando('stop');
261       });
262       self.addEvtListener('#pause-btn', 'click', function() {
263         self.kommando('pause');
264       });
265       self.addEvtListener('#weiter-btn', 'click', function() {
266         self.kommando('weiter');
267       });
268       
269 /*
270     <button class="ctrl-btn ctrl-item" id="hier-btn" title="hier spielen"><i class="icon-tablet"></i></button>
271  */      
272       
095119 273       self.addEvtListener('#weg-btn', 'click', self.titelWeg);
U 274       self.addEvtListener('#leeren-btn', 'click', self.alleTitelEntfernen);
748b6f 275       self.media_liste();
U 276     });
277   };
278   
279   /* Titel einer Abspielliste */
280   
281   this.titel_liste = function() {
282     self.reset_top_buttons();
283     var plname = document.querySelector('#playlist').value;
78d707 284     document.querySelector('.bereich-name').textContent = 'Abspielliste ' + plname;
U 285     var bb = document.querySelector('.breadcrumb-behaelter');
286     bb.textContent = "";
748b6f 287     self.http_get('../api/alist/' + plname, function (responseText) {
78d707 288       self.vorlage_laden_und_fuellen("data/tpl/titel_liste.txt", JSON.parse(responseText), function (html) {
748b6f 289         document.querySelector(".zentraler-inhalt").innerHTML = html;
U 290         self.addEvtListener('.entity-eintrag', 'click', function (event) {
291           var t = event.target;
292           self.removeClassMulti('selected');
293           t.classList.add('selected');
294         });
295       });
296     });
297   };  
298   
b56bb3 299   /* ------------- Media-Steuerung ------------------------- */
U 300   
301   this.play = function() {
658c14 302     var bereichName = document.querySelector('.bereich-name').textContent;
U 303     if(bereichName === '') {
304       var elem = document.querySelector(".selected");
305       //var titelName = elem.textContent;
306       var titelName = elem.attributes.dateiName.nodeValue;
307       var album = elem.attributes.album.nodeValue;
308       var interpret = elem.attributes.interpret.nodeValue;
309       var anzName = elem.attributes.titelAnzName.nodeValue;
310       var titel;
311       if(self.mediaPfad.endsWith('/')) {
312         titel = new Titel(titelName, self.mediaPfad, self.ortPfad, interpret, anzName, album);
313       } else {
314         titel = new Titel(titelName, self.mediaPfad + '/', self.ortPfad, interpret, anzName, album);
315       }
1c5fa4 316       var playername = document.querySelector('#abspieler').value;
U 317       /*      
318       HTTP POST /mz/api/strg/abspieler/play mit Titel im Body
658c14 319       
1c5fa4 320       url: /media/test/M/Muenchener-Freiheit/01-Ohne-Dich-schlaf-ich-heut-Nacht-nicht-ein.mp3      
658c14 321       */
U 322       console.log('plname: ' + playername + ' url: ' + titel.katalogUrl + titel.pfad + titel.name);
1c5fa4 323       self.http_post('../api/strg/' + playername, JSON.stringify(titel), function(responseText) {
658c14 324         self.meldung_mit_timeout(responseText, 1500);
1c5fa4 325       });      
658c14 326     } else {
U 327       var abs = document.querySelector('#abspieler').value;
328       var lst = document.querySelector('#playlist').value;
329       console.log(
330         "play playlist.value: " + document.querySelector('#playlist').value + 
331         ", abspieler.value: " + document.querySelector('#abspieler').value);
332       self.http_get('../api/strg/' + abs + '/play/liste/' + lst, function(responseText) {
333         self.meldung_mit_timeout(responseText, 1500);
334       });
335     }
095119 336   };
U 337   
9e14ef 338   this.kommando = function(kommando) {
U 339     var abs = document.querySelector('#abspieler').value;
340     self.http_get('../api/strg/' + abs + '/' + kommando, function(responseText) {
341       self.meldung_mit_timeout(responseText, 1500);
342     });
343   };
344     
095119 345   /* ------------- Verwaltungsfunktionen Abspielliste -------------------- */
U 346   
347   self.alleTitelEntfernen = function() {
348     var plname = document.querySelector('#playlist').value;
349     self.http_delete('../api/alist/' + plname + '/alle', '', function(responseText) {
350       // DELETE    http://localhost:9090/mz/api/alist/liste1/0
351       //self.meldung_mit_timeout(responseText, 1500);
352       self.titel_liste();
353     });
354   };
355   
356   this.titelDazu = function() {
357     var elem = document.querySelector(".selected");
358     //var titelName = elem.textContent;
359     var titelName = elem.attributes.dateiName.nodeValue;
360     var album = elem.attributes.album.nodeValue;
361     var interpret = elem.attributes.interpret.nodeValue;
362     var anzName = elem.attributes.titelAnzName.nodeValue;
363     var titel;
364     if(self.mediaPfad.endsWith('/')) {
365       titel = new Titel(titelName, self.mediaPfad, self.ortPfad, interpret, anzName, album);
366     } else {
367       titel = new Titel(titelName, self.mediaPfad + '/', self.ortPfad, interpret, anzName, album);
368     }
369     var plname = document.querySelector('#playlist').value;
370     self.http_put('../api/alist/' + plname, JSON.stringify(titel), function(responseText) {
371       //self.meldung_mit_timeout(responseText, 1500);
372     });
373   };  
374   
375   this.titelWeg = function() {
376     var elem = document.querySelector(".selected");
377     var parentElem = elem.parentNode;
378     //console.log("elem: " + elem.nodeName + ", parent: " + parentElem.nodeName + ", len: " + parentElem.childNodes.length);
379     var liElems = parentElem.getElementsByTagName(elem.nodeName); // nur die LI Elemente
380     //console.log("liElems.anz: " + liElems.length);
381     var gefunden = false;
382     for(var i = 0; i < liElems.length && !gefunden; i++) {
383       //console.log(liElems.item(i).textContent);
384       if(liElems.item(i).classList.contains("selected")) {
385         gefunden = true;
386         var index = i;
387         //console.log(elem.textContent + ' hat Index ' + i);
388       }
389     }
390     // /mz/api/alist/[pl-name]/[nr] 
391     var plname = document.querySelector('#playlist').value;
392     self.http_delete('../api/alist/' + plname + '/' + index,'', function(responseText) {
393       // DELETE    http://localhost:9090/mz/api/alist/liste1/0
394       //self.meldung_mit_timeout(responseText, 1500);
395       self.titel_liste();
396     });
397     
b56bb3 398   };
U 399   
748b6f 400   /* ------------- Helfer fuer Entitaets-Formulare ----------------------- */
14638b 401   
U 402   /*
403    * url: '../api/store/Ablageort/liste/'
78d707 404    * tpl: "data/tpl/ablageort_liste.txt"
14638b 405    * storeUrl: '../api/store/Ablageort/'
U 406    * formFunc: "self.ablageort_form"
407    * cb: etwas wie
408    *   function(responseText){
409    *     var ablageort = JSON.parse(responseText);
410    *     self.ablageort_form(ablageort);
411    *   });
412    */
50e53e 413   this.entitaet_liste = function(bname, listUrl, tpl, storeUrl, formFunc, cb) {
14638b 414     self.reset_top_buttons();
50e53e 415     document.querySelector('.bereich-name').textContent = bname;
78d707 416     var bb = document.querySelector('.breadcrumb-behaelter');
U 417     bb.textContent = "";
14638b 418     self.http_get(listUrl, function (responseText) {
U 419       self.vorlage_laden_und_fuellen(tpl, JSON.parse(responseText), function (html) {
420         document.querySelector(".zentraler-inhalt").innerHTML = html;
421         self.addEvtListener('.entity-eintrag', 'click', function (event) {
422           var t = event.target;
423           self.http_get(storeUrl + t.textContent, cb);
424         });
425         //self.addEvtListener('#neu-btn', 'click', function (event) {
426         self.addEvtListener('#top-neu-btn', 'click', function(event) {
427           eval(formFunc + "(this)");
428         });        
429       });
430     });
431   };  
748b6f 432       
48f8f9 433   /*
c7030d 434    * dat: gefuelltes Datenobjekt bei Aenderung
U 435    * key: der alte schluesselbegriff bei Aenderung (z.B. al.name)
78d707 436    * tpl: "data/tpl/form_abspielliste.txt"
c7030d 437    * url: '../api/store/Abspielliste/'
U 438    * selector: '#abspielliste-name'
439    * cbOk: etwas wie
440    *    function() {
441    *       self.abspielliste_auswahl_fuellen();
442    *       self.abspielliste_liste();
443    *     });
444    * delSelector: '#abspielliste-name'
445    * cbDel: etwas wie
446    *    function() {
447    *       self.abspielliste_auswahl_fuellen();
448    *       self.abspielliste_liste();
449    *     });
450    */
451
50e53e 452   this.entitaet_form = function(bname, dat, key, tpl, url, selector, cb) {
U 453     document.querySelector('.bereich-name').textContent = bname;
c7030d 454     self.vorlage_laden_und_fuellen(tpl, dat, function (html) {
U 455       document.querySelector(".zentraler-inhalt").innerHTML = html;
456       const form = document.querySelector('form');      
457       form.addEventListener('submit', function(event) {
458         self.handle_submit(event, key, url, selector, cb);
459       });
460       self.addEvtListener('#cancel-btn', 'click', cb);
461       self.addEvtListener('#loeschen-btn', 'click', function(event) {
462         event.preventDefault();
463         self.handle_del_btn(selector, url, cb);
464       });
465     });
466   };
467   
468   /*
48f8f9 469    * existingKey: wenn die Entitaet existiert und geandert werden soll
U 470    *                 leer, wenn neue Entitaet 
471    */
472   this.handle_submit = function(event, existingKey, putUrl, keySelector, cb) {
473     event.preventDefault();
474     const data = new FormData(event.target);
475     const value = Object.fromEntries(data.entries());
095119 476     //console.log({ value });
U 477     //console.log(JSON.stringify(value));
48f8f9 478     var daten = JSON.stringify(value);
U 479     var formkey = document.querySelector(keySelector).value;
480     formkey = formkey.replace(' ', '').replace(/[\W]+/g, '');
481     if(typeof existingKey === "undefined" ||  existingKey.length < 1) {
482       // neu
483       self.http_put(putUrl + formkey, daten, function (responseText) {
484         if(typeof(cb) !== 'function') {
485           // ..
486         } else {
487           cb();
488         }
489       });
490     } else {
491       // aendern
492       self.http_put(putUrl + existingKey, daten, function (responseText) {
493         if(typeof(cb) !== 'function') {
494           // ..
495         } else {
496           cb();
497         }
498       });
499     }
500   };
501   
502   this.handle_del_btn = function(selectorKey, delUrl, cb) {
503     var pkey = document.querySelector(selectorKey).value;
504     var dlgdata = {"del-elem": pkey};
78d707 505     self.dialog_laden_und_zeigen('data/tpl/dlg-loeschen.txt', dlgdata, function() {
48f8f9 506       self.addEvtListener('#nein-btn', 'click', self.dialog_schliessen);
U 507       self.addEvtListener('#ja-btn', 'click', function(event) {
095119 508         //console.log("loeschen geklickt.");
48f8f9 509         self.http_delete(delUrl + pkey, '', function (responseText) {
U 510           self.dialog_schliessen();
511           if(typeof(cb) !== 'function') {
512             // ..
513           } else {
514             cb();
515           }
516         });
517       });
518     });    
519   };  
748b6f 520   
U 521   /* ------------------ sonstige Helfer ----------------------- */
522       
7c22a2 523   this.addEvtListener = function(selector, eventName, func) {
U 524     document.querySelectorAll(selector).forEach(elem => { elem.addEventListener(eventName, func); });
525   };
526   
527   this.removeClassMulti = function(selector) {
528     document.querySelectorAll('.' + selector).forEach(elem => { elem.classList.remove(selector); });
529   };
530
f45e20 531   /* --------------------- asynchroner HTTP Client ----------------- */
faab2d 532   
f074f6 533   this.http_get = function (u, cb) {
b379f5 534     self.http_call('GET', u, null, cb);
U 535   };
f074f6 536
U 537   this.http_post = function (u, data, cb) {
b379f5 538     self.http_call('POST', u, data, cb);
U 539   };
540
90f5d4 541   this.http_put = function (u, data, cb) {
U 542     self.http_call('PUT', u, data, cb);
543   };
544   
5b7356 545   this.http_delete = function (u, data, cb) {
5f7e0b 546     // console.log("delete " + u);
5b7356 547     self.http_call('DELETE', u, data, cb);
U 548   };
549   
f074f6 550   this.http_call = function (method, u, data, scallback) {
b379f5 551     var xhr = new XMLHttpRequest();
U 552     var url = u;
f074f6 553     xhr.onreadystatechange = function () {
b379f5 554       if (this.readyState === 4 && this.status === 200) {
U 555         scallback(this.responseText);
556       }
557     };
558     xhr.open(method, url);
f074f6 559     if (method === 'GET') {
b379f5 560       xhr.send();
2597cd 561     } else if (method === 'POST' || method === 'PUT' || method === 'DELETE') {
b379f5 562       xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
U 563       xhr.send(data);
564     }
565   };
8239d1 566   
f45e20 567   /* ------------------------ aus App-Vorlage -------------------  */
cfa858 568
f074f6 569   this.menue_umschalten = function () {
cfa858 570     var ham = document.querySelector(".hamburger");
U 571     ham.classList.toggle("is-active"); // hamburger-icon umschalten
572     self.appMenu.toggle(); // menue oeffnen/schliessen
573   };
574
f074f6 575   this.info_dialog_zeigen = function () {
78d707 576     self.dialog_laden_und_zeigen('data/tpl/dlg-info.txt', '');
cfa858 577     self.menue_umschalten();
U 578   };
579
f074f6 580   this.seitenleiste_umschalten = function () {
cfa858 581     var ostDiv = document.querySelector('.ost');
f074f6 582     if (ostDiv.classList.contains('ost-open')) {
cfa858 583       ostDiv.classList.remove('ost-open');
f074f6 584       ostDiv.style.flexBasis = '0em';
cfa858 585     } else {
f074f6 586       ostDiv.classList.add('ost-open');
U 587       ostDiv.style.flexBasis = '6em';
cfa858 588     }
U 589     self.menue_umschalten();
590   };
591
f074f6 592   this.fusszeile_umschalten = function () {
cfa858 593     var suedDiv = document.querySelector('.sued');
f074f6 594     if (suedDiv.classList.contains('sued-open')) {
cfa858 595       suedDiv.classList.remove('sued-open');
f074f6 596       suedDiv.style.height = '0';
cfa858 597     } else {
U 598       suedDiv.classList.add('sued-open');
f074f6 599       suedDiv.style.height = '1.5em';
cfa858 600     }
U 601     self.menue_umschalten();
602   };
603
f074f6 604   this.menu_message = function (msg) {
cfa858 605     self.meldung_mit_timeout(msg, 1500);
U 606     var suedDiv = document.querySelector('.sued');
f074f6 607     if (suedDiv.classList.contains('sued-open')) {
cfa858 608     } else {
U 609       suedDiv.classList.add('sued-open');
f074f6 610       suedDiv.style.height = '1.5em';
cfa858 611     }
U 612     self.menue_umschalten();
613   };
614
f074f6 615   this.message_1 = function () {
cfa858 616     self.menu_message('Eine Mitteilung.');
U 617   };
618
f074f6 619   this.message_2 = function () {
cfa858 620     self.menu_message('Was wir schon immer sagen wollten.');
U 621   };
622
f074f6 623   this.message_3 = function (text) {
cfa858 624     self.menu_message(text);
U 625   };
626
f074f6 627   this.meldung_mit_timeout = function (meldung, timeout) {
cfa858 628     var s = document.querySelector('.sued');
a43e1a 629     s.classList.add('sued-open');
U 630     s.style.height = '1.5em';
cfa858 631     s.textContent = meldung;
f074f6 632     setTimeout(function () {
cfa858 633       s.textContent = 'Bereit.';
f074f6 634       setTimeout(function () {
cfa858 635         var suedDiv = document.querySelector('.sued');
f074f6 636         if (suedDiv.classList.contains('sued-open')) {
U 637           suedDiv.classList.remove('sued-open');
638           suedDiv.style.height = '0';
cfa858 639         }
U 640       }, 500);
641     }, timeout);
642   };
8239d1 643   
f45e20 644   /* --------------------- Dialog-Funktionen ------------------------ */
cfa858 645
U 646   /*
f074f6 647    Einen Dialog aus Vorlagen erzeugen
U 648    
649    vurl - URL zur Dialogvorlage
650    msgTpl - URL mit einer Vorlage eines Mitteilungstextes (optional)
651    */
5b7356 652   this.dialog_laden_und_zeigen = function (vurl, msgTpl, cb) {
U 653     var vorlage = self.cache[vurl];
654     if(vorlage === undefined) {
655       self.http_get(vurl, function(antwort) {
656         self.cache[vurl] = antwort;
657         self.dialog_zeigen(vurl, msgTpl, cb);
f074f6 658       });
cfa858 659     } else {
5b7356 660       self.dialog_zeigen(vurl, msgTpl, cb);
cfa858 661     }
U 662   };
663
5b7356 664   this.dialog_zeigen = function (vurl, inhalt, cb) {
U 665     var dlg = document.querySelector(".dialog");
2597cd 666     self.html_erzeugen(vurl, inhalt, function (html) {
U 667       dlg.style.height = '7em';
668       dlg.innerHTML = html;
669       document.querySelector('.close-btn').addEventListener('click', self.dialog_schliessen);
670       if(typeof(cb) !== 'function') {
671         // ..
672       } else {
673         cb();
674       }
675     });
5b7356 676   };
2597cd 677   
f45e20 678   this.dialog_schliessen = function () {
cfa858 679     document.querySelector('.close-btn').removeEventListener('click', self.dialog_schliessen);
U 680     var dlg = document.querySelector('.dialog');
681     dlg.style.height = '0';
682     dlg.innerHTML = '';
683   };
684
f45e20 685   /* ---------------------   Vorlagen   ---------------------- */
cfa858 686
U 687   /*
f074f6 688    Das HTML erzeugen, das entsteht, wenn eine Vorlage mit Inhalt
U 689    gefüllt wird
690    
691    Das Füllen erfolgt asynchron, d.h. der Programmlauf geht nach dem
692    Aufruf weiter ohne auf das Laden und Füllen der Vorlage zu warten.
693    Das fertige HTML wird der Callback-Funktion übergeben
694    sobald die Vorlage geladen und gefüllt ist, unabhängig davon, wo der
695    Programmlauf zu diesem Zeitpunkt mittlerweile ist.
696    
697    vurl - URL zur Vorlagendatei
698    inhalt - die JSON-Struktur, deren Inhalt in die
699    Vorlage gefüllt werden soll
700    cb - Callback-Funktion, die gerufen wird, wenn die Vorlage gefüllt ist.
701    Dieser Callback-Funktion wird das fertige HTML übergeben
702    */
703   this.html_erzeugen = function (vurl, inhalt, cb) {
cfa858 704     var vorlage = self.cache[vurl];
f074f6 705     if (vorlage === undefined) {
cfa858 706       self.vorlage_laden_und_fuellen(vurl, inhalt, cb);
U 707     } else {
708       self.vorlage_fuellen(vurl, inhalt, cb);
709     }
710   };
711
f074f6 712   this.vorlage_fuellen = function (vurl, inhalt, cb) {
cfa858 713     cb(Mustache.render(self.cache[vurl], inhalt));
U 714   };
715
716   /*
f074f6 717    Eine Vorlage vom Server in den lokalen Speicher laden
U 718    vurl - der URL unter dem die Vorlage zu finden ist
719    inhalt - die JSON-Struktur, deren Inhalt in die
720    Vorlage gefüllt werden soll
721    cb - callback: Diese Funktion wird gerufen, wenn die Vorlage mit dem
722    Inhalt gefüllt ist
723    */
724   this.vorlage_laden_und_fuellen = function (vurl, inhalt, cb) {
cfa858 725     var xmlhttp = new XMLHttpRequest();
f074f6 726     xmlhttp.onreadystatechange = function () {
cfa858 727       if (this.readyState == 4 && this.status == 200) {
U 728         self.cache[vurl] = this.responseText;
729         self.vorlage_fuellen(vurl, inhalt, cb);
730       }
731     };
732     xmlhttp.open("GET", vurl, true);
733     xmlhttp.send();
734   };
735
736
737 }
738
f45e20 739 /* ----------- Objekte ---------------- */
U 740
741 function Ablageort(n, o, u) {
742   this.name = n;
743   this.ort = o;
744   this.url = u;
745 }
746
747 function Einstellung(k, v) {
748   this.key = k;
749   this.value = v;
750 }
751
752 function Abspieler(n, u) {
753   this.name = n;
754   this.url = u;
8d7d35 755 }
U 756
757 function Abspielliste(n) {
758   this.name = n;
e60cff 759 }
U 760
245ac1 761 function Titel(n, p, u, i, t, a) {
e60cff 762   this.katalogUrl = u;
2bdd78 763   this.pfad = p;
e60cff 764   this.name = n;
245ac1 765   this.interpret = i;
U 766   this.titelAnzName = t;
767   this.album  = a;
d6b78c 768 }