Persoenliche Mediazentrale
ulrich
2021-04-26 c6fdc483f9e98ae0e461fd58a012584f21c40582
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",
0866ae 21             "6em");
f45e20 22
U 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);
d027b5 33     self.addEvtListener('#mi-live', 'click', self.livestream_liste);
3929b0 34     self.addEvtListener('#mi-devices', 'click', self.geraet_liste);
a27c68 35     self.addEvtListener('#mi-switch', 'click', self.geraet_schalt_liste);
f45e20 36     
U 37     self.fusszeile_umschalten();
38     self.seitenleiste_umschalten();
39     self.dialog_unten_zeigen();
40   };
41   
8d7d35 42   /* ---------------- Entitaets-Listen ----------------- */
d027b5 43   
U 44   this.livestream_selection = function() {
45     document.querySelector('.breadcrumb-behaelter').textContent = '';
46     document.querySelector('.bereich-name').textContent = 'Livestream-Auswahl'; 
47     self.http_get('../api/store/Livestream/liste/', function(responseText) {
2af7d6 48       self.html_erzeugen("data/tpl/livestream_liste.txt", JSON.parse(responseText), function (html) {
d027b5 49         document.querySelector(".zentraler-inhalt").innerHTML = html;
U 50         self.addEvtListener('.entity-eintrag', 'click', function (event) {
51           var t = event.target;
52           self.removeClassMulti('selected');
53           t.classList.add('selected');          
54         });
55       });
56     });
57   };
8d7d35 58
86bbf7 59   // auf der obersten Ebene werden die Kataloge angezeigt,
U 60   // darunter der Inhalt des aktuellen Pfades
61   this.media_liste = function() {
78d707 62     self.reset_top_buttons(); 
095119 63     //console.log("ortPfad: " + self.ortPfad + ", mediaPfad: " + self.mediaPfad);
78d707 64     document.querySelector('.bereich-name').textContent = '';    
86bbf7 65     if(self.ortPfad === '/') {
78d707 66       var bb = document.querySelector('.breadcrumb-behaelter');
U 67       bb.textContent = "Kataloge";
86bbf7 68       // Kataloge listen
f45e20 69       self.http_get('../api/store/Ablageort/liste/', function (responseText) {
8d7d35 70         //document.querySelector('#top-up-btn').removeEventListener('click', self.media_liste_herauf);
2af7d6 71         self.html_erzeugen("data/tpl/katalog_root_liste.txt", JSON.parse(responseText), function (html) {
86bbf7 72           document.querySelector(".zentraler-inhalt").innerHTML = html;
U 73           self.addEvtListener('.entity-eintrag', 'click', function (event) {
74             var t = event.target;
78d707 75             self.katName = t.textContent;
d027b5 76             if(self.katName !== "Livestreams") {
U 77               self.http_get('../api/store/Ablageort/' + t.textContent, function(responseText) {
78                 var ablageort = JSON.parse(responseText);
79                 self.ortPfad = ablageort.url;
80                 self.media_liste();
81               });
82             } else {
83               self.livestream_selection();
84             }
86bbf7 85           });
U 86         });
87       });
88     } else {
78d707 89       var bb = document.querySelector('.breadcrumb-behaelter');
U 90       var brPfad = self.katName + self.mediaPfad;
91       var breadcrumbs = brPfad.split('/');
92       var brLinks = "";
93       var brLinkPfad = "";
94       for(var index = 0; index < breadcrumbs.length; index++) {
95         // <a class="breadcrumb-link" href="#">breadcrumbs[index]</a>
96         if(index === 0) {
275b21 97           brLinkPfad = '';
78d707 98         } else {
U 99           brLinkPfad = brLinkPfad + '/' + breadcrumbs[index];
100         }
101         brLinks = brLinks + "<a brlink='" + brLinkPfad + "' class='breadcrumb-link' href='#'>" + breadcrumbs[index] + "</a>";
102         //console.log('   breadcrumbs[' + index + ']: ' + breadcrumbs[index]);
103       }
104       bb.innerHTML = brLinks;
105       self.addEvtListener('.breadcrumb-link', 'click', function(event) {
095119 106         //console.log(event.target.attributes.brlink.nodeValue);
78d707 107         var neuerPfad = event.target.attributes.brlink.nodeValue;
U 108         self.mediaPfad = neuerPfad;
109         self.media_liste();
110       });
3271f1 111       var url = '..' + self.ortPfad + self.mediaPfad;
U 112       if(!url.endsWith('/')) {
113         url = url + '/';
114       }
115       self.http_get(url, function(responseText) {
2af7d6 116         self.html_erzeugen("data/tpl/katalog_inhalt_liste.txt", JSON.parse(responseText), function (html) {
86bbf7 117           document.querySelector(".zentraler-inhalt").innerHTML = html;
U 118           self.addEvtListener('.entity-eintrag', 'click', function (event) {
119             var t = event.target;
3271f1 120             var tx = t.textContent;
86bbf7 121             if(t.classList.contains("entity-typ-folder")) {
3271f1 122               if(self.mediaPfad.endsWith('/')) {
U 123                 self.mediaPfad = self.mediaPfad + tx;                
124               } else {
125                 self.mediaPfad = self.mediaPfad + '/' + tx;
126               }
86bbf7 127               self.media_liste();
U 128             } else {
37eadf 129               if(t.classList.contains('selected')) {
78d707 130                 t.classList.add('added-to-playlist');
095119 131                 self.titelDazu();
37eadf 132               } else {
U 133                 self.removeClassMulti('selected');
134                 t.classList.add('selected');
135               }
e60cff 136               //self.selTitel = new Titel(t.textContent, self.ortPfad);       
86bbf7 137             }
U 138           });
8d7d35 139           self.addEvtListener('#top-up-btn', 'click', function(event) {
86bbf7 140             if(self.mediaPfad === '/') {
U 141               self.ortPfad = '/';              
142             } else {
143               var pos = self.mediaPfad.lastIndexOf('/');
3271f1 144               var parent;
U 145               if(pos > 1) {
146                 parent = self.mediaPfad.substring(0, pos);
147               } else {
148                 parent = '/';
149               }
86bbf7 150               self.mediaPfad = parent;
U 151             }
152             self.media_liste();
8d7d35 153           });
86bbf7 154         });
U 155       });
156     }
e60cff 157   };
245ac1 158   
39ebae 159   this.ein_aus_btn = function() {
U 160     self.addEvtListener('#ein-aus-btn', 'click', function (event) {
161       var geraetName = event.target.attributes.gname.nodeValue;
162       var nameElem = event.target.parentNode.querySelector('.schalt-geraet-name');
163       if(nameElem.classList.contains('schalt-geraet-true')) {
164         // ausschalten
165         self.http_get('../api/gstrg/geraet/' + geraetName + "/aus", function(responseText) {
166           // console.log(responseText);
167           self.geraet_schalt_liste();
168         });
169       } else {
170         // einschalten            
171         self.http_get('../api/gstrg/geraet/' + geraetName + "/ein", function(responseText) {
172           // console.log(responseText);
173           self.geraet_schalt_liste();
174         });
175       }
176     });
177   };
178   
3e5a56 179   this.geraet_schalt_liste = function() {
39ebae 180     /*self.entitaet_liste('Geräte schalten','../api/store/Geraet/listealles/', 
U 181       "data/tpl/geraet_schalt_liste.txt", '../api/store/Geraet/', 
182       "self.form_geraet_status", function(responseText) {*/
a29f5c 183     self.entitaet_liste('Geräte schalten','../api/store/Geraet/listealles/', 
3e5a56 184       "data/tpl/geraet_schalt_liste.txt", '../api/store/Geraet/', 
39ebae 185       "", function(responseText) {
U 186         //var geraet = JSON.parse(responseText);
187         //self.geraet_status_form(geraet);
188       }, self.ein_aus_btn);      
3e5a56 189   };
U 190   
3929b0 191   this.geraet_liste = function() {
U 192     self.entitaet_liste('Geräte','../api/store/Geraet/liste/', 
193       "data/tpl/geraet_liste.txt", '../api/store/Geraet/', 
194       "self.geraet_form", function(responseText) {
195         var geraet = JSON.parse(responseText);
196         self.geraet_form(geraet);
197       });
198   };
199
a43e1a 200   this.ablageort_liste = function() {
78d707 201     self.entitaet_liste('Kataloge','../api/store/Ablageort/liste/', 
U 202       "data/tpl/ablageort_liste.txt", '../api/store/Ablageort/', 
e44ed0 203       "self.ablageort_form", function(responseText) {
U 204         var ablageort = JSON.parse(responseText);
205         self.ablageort_form(ablageort);
206       });
cf6509 207   };
U 208
209   this.prefs_liste = function() {
50e53e 210     self.entitaet_liste('Einstellungen','../api/store/Einstellung/liste/', 
78d707 211       "data/tpl/einstellung_liste.txt", '../api/store/Einstellung/', 
e44ed0 212       "self.prefs_form", function(responseText) {
U 213         var einstellung = JSON.parse(responseText);
214         self.prefs_form(einstellung);
215       });
cf6509 216   };
U 217
3d4bca 218   this.abspieler_liste = function() {
50e53e 219     self.entitaet_liste('Abspieler','../api/store/Abspieler/liste/', 
78d707 220       "data/tpl/abspieler_liste.txt", '../api/store/Abspieler/', 
e44ed0 221       "self.abspieler_form", function(responseText) {
U 222         var abspieler = JSON.parse(responseText);
223         self.abspieler_form(abspieler);
224       });
8d7d35 225   };
U 226   
d027b5 227   this.livestream_liste = function() {
7d3118 228     self.entitaet_liste('Livestreams','../api/store/Livestream/liste/', 
d027b5 229       "data/tpl/livestream_liste.txt", '../api/store/Livestream/', 
U 230       "self.livestream_form", function(responseText) {
231         var livestream = JSON.parse(responseText);
232         self.livestream_form(livestream);
233       });
234   };
235   
8d7d35 236   this.abspielliste_liste = function() {
50e53e 237     self.entitaet_liste('Abspielliste','../api/store/Abspielliste/liste/', 
78d707 238       "data/tpl/abspielliste_liste.txt", '../api/store/Abspielliste/', 
e44ed0 239       "self.abspielliste_form", function(responseText) {
095119 240         //console.log("responseTest: '" + responseText + "'");
e44ed0 241         var abspielliste = JSON.parse(responseText);
U 242         self.abspielliste_form(abspielliste);
243       });
8d7d35 244   };
U 245   
c7030d 246   /* -------------------- Entitaets-Formulare ------------------ */  
8d7d35 247   
U 248   this.abspielliste_form = function(al) {
50e53e 249     self.entitaet_form('Abspielliste', al, al.name,
78d707 250       "data/tpl/form_abspielliste.txt", '../api/store/Abspielliste/',
960317 251       '#abspielliste-name', 'name', function(event) {
095119 252           if(event !== undefined) {
U 253             event.preventDefault();
254           }
c7030d 255           self.abspielliste_auswahl_fuellen();
U 256           self.abspielliste_liste();
257     });
3d4bca 258   };
71def1 259   
3d4bca 260   this.abspieler_form = function(pl) {
50e53e 261     self.entitaet_form('Abspieler', pl, pl.key,
78d707 262       "data/tpl/form_abspieler.txt", '../api/store/Abspieler/',
960317 263       '#abspieler-name', 'name', function() { 
c7030d 264           self.abspieler_auswahl_fuellen();
U 265           self.abspieler_liste();
d027b5 266     });
U 267   };
268
269   this.livestream_form = function(ls) {
270     self.entitaet_form('Livestream', ls, ls.name,
271       "data/tpl/form_livestream.txt", '../api/store/Livestream/',
960317 272       '#livestream-name', 'name', function() { 
d027b5 273           self.livestream_liste();
3929b0 274     });
U 275   };
276
277   this.geraet_form = function(ge) {
278     self.entitaet_form('Gerät', ge, ge.name,
279       "data/tpl/form_geraet.txt", '../api/store/Geraet/',
960317 280       '#geraet-name', 'name', function() { 
3929b0 281           self.geraet_liste();
3e5a56 282     });
U 283   };
284
285   this.geraet_status_form = function(ge) {
286     self.entitaet_form('Gerät', ge, ge.name,
287       "data/tpl/form_geraet_status.txt", '../api/store/Geraet/',
960317 288       '#geraet-name', 'name', function() { 
3e5a56 289           self.geraet_schalt_liste();
c7030d 290     });
71def1 291   };
U 292
cf6509 293   this.prefs_form = function(k) {
78d707 294     self.entitaet_form('Einstellung', k, k.key,
U 295       "data/tpl/form_einstellung.txt", '../api/store/Einstellung/',
960317 296       '#einstellung-key', 'key', function() { 
c7030d 297           self.prefs_liste();
U 298     });
a43e1a 299   };
cfa858 300
90f5d4 301   /* 
U 302    * Ablageort-Formular anzeigen
303    * 
304    * {"name":"Katalog 2","ort":"/home/ulrich/Videos","url":"/media/kat2"}: 
305    * 
306    * @param {type} ablageort  der Ablageort, der bearbeitet werden soll, leer fuer neuen Ort
307    * @returns {undefined} kein Rueckgabewert
308    */
309   this.ablageort_form = function(ort) {
78d707 310     self.entitaet_form('Katalog', ort, ort.name,
U 311       "data/tpl/form_ablageort.txt", '../api/store/Ablageort/',
a48ca3 312       '#ablageort-name', 'name', function() { 
c7030d 313         self.ablageort_liste();
U 314     });
b379f5 315   };
48f8f9 316   
748b6f 317   /* ------------------------------- UI-Dynamik ----------------------- */
48f8f9 318   
748b6f 319   self.reset_top_buttons = function() {
2af7d6 320     self.html_erzeugen("data/tpl/top_btns.txt", '', function (html) {
748b6f 321       document.querySelector(".top-btns").innerHTML = html;
U 322     });
323   };
324   
325   this.abspieler_auswahl_fuellen = function() {
b56bb3 326     self.http_get('../api/store/Abspieler/liste/', function (responseText) {
2af7d6 327       self.html_erzeugen("data/tpl/abs_sel.txt", JSON.parse(responseText), function (html) {
748b6f 328         document.querySelector(".abs-sel").innerHTML = html;
U 329       });    
330     });
331   };
332
333   this.abspielliste_auswahl_fuellen = function() {
334     self.http_get('../api/store/Abspielliste/', function (responseText) {
2af7d6 335       self.html_erzeugen("data/tpl/pl_sel.txt", JSON.parse(responseText), function (html) {
748b6f 336         document.querySelector(".pl-sel").innerHTML = html;
b56bb3 337         self.addEvtListener('#playlist', 'change', function() {
U 338           self.titel_liste();
339         });
748b6f 340       });    
U 341     });
342   };
343   
344   /* Unterer Einblendbereich */
345   
346   this.dialog_unten_zeigen = function() {
2af7d6 347     self.html_erzeugen("data/tpl/ctrl.txt", "", function (html) {
748b6f 348       var dlg = document.querySelector(".dialog-unten");
a07e7e 349       //dlg.style.height = '10em';
748b6f 350       dlg.innerHTML = html;
U 351       self.abspieler_auswahl_fuellen();
352       self.abspielliste_auswahl_fuellen();
095119 353       self.addEvtListener('#dazu-btn', 'click', self.titelDazu);
b56bb3 354       self.addEvtListener('#play-btn', 'click', self.play);
9e14ef 355       self.addEvtListener('#stop-btn', 'click', function() {
U 356         self.kommando('stop');
357       });
358       self.addEvtListener('#pause-btn', 'click', function() {
359         self.kommando('pause');
360       });
c6fdc4 361       self.addEvtListener('#weiter-btn', 'click', self.weiter);
fe0cf7 362
U 363       self.addEvtListener('#hier-btn', 'click', self.hier_spielen);
9e14ef 364       
U 365 /*
366     <button class="ctrl-btn ctrl-item" id="hier-btn" title="hier spielen"><i class="icon-tablet"></i></button>
367  */      
368       
095119 369       self.addEvtListener('#weg-btn', 'click', self.titelWeg);
U 370       self.addEvtListener('#leeren-btn', 'click', self.alleTitelEntfernen);
f6ea0c 371       
U 372       self.addEvtListener('#media-btn', 'click', self.media_liste);
373       self.addEvtListener('#plst-btn', 'click', self.titel_liste);
938b6b 374       self.addEvtListener('#live-btn', 'click', self.livestream_selection);
f6ea0c 375       self.addEvtListener('#switch-btn', 'click', self.geraet_schalt_liste);
U 376     
748b6f 377       self.media_liste();
U 378     });
379   };
380   
381   /* Titel einer Abspielliste */
382   
383   this.titel_liste = function() {
384     self.reset_top_buttons();
385     var plname = document.querySelector('#playlist').value;
78d707 386     document.querySelector('.bereich-name').textContent = 'Abspielliste ' + plname;
U 387     var bb = document.querySelector('.breadcrumb-behaelter');
388     bb.textContent = "";
748b6f 389     self.http_get('../api/alist/' + plname, function (responseText) {
2af7d6 390       self.html_erzeugen("data/tpl/titel_liste.txt", JSON.parse(responseText), function (html) {
748b6f 391         document.querySelector(".zentraler-inhalt").innerHTML = html;
U 392         self.addEvtListener('.entity-eintrag', 'click', function (event) {
393           var t = event.target;
394           self.removeClassMulti('selected');
395           t.classList.add('selected');
396         });
397       });
398     });
399   };  
400   
b56bb3 401   /* ------------- Media-Steuerung ------------------------- */
U 402   
403   this.play = function() {
658c14 404     var bereichName = document.querySelector('.bereich-name').textContent;
U 405     if(bereichName === '') {
4f2589 406       var titel = self.titelErmitteln(document.querySelector(".selected"));
1c5fa4 407       var playername = document.querySelector('#abspieler').value;
658c14 408       console.log('plname: ' + playername + ' url: ' + titel.katalogUrl + titel.pfad + titel.name);
c6fdc4 409       self.http_post('../api/strg/' + playername + '/play/titel', JSON.stringify(titel), function(responseText) {
658c14 410         self.meldung_mit_timeout(responseText, 1500);
d027b5 411       });   
U 412     } else if(bereichName === 'Livestream-Auswahl') {
413       var streamName = document.querySelector(".selected").textContent;
414       var playername = document.querySelector('#abspieler').value;
415       var stream = new Livestream(streamName, '-');
c6fdc4 416       self.http_post('../api/strg/' + playername + '/play/stream', JSON.stringify(stream), function(responseText) {
d027b5 417         self.meldung_mit_timeout(responseText, 1500);
U 418       });   
658c14 419     } else {
U 420       var abs = document.querySelector('#abspieler').value;
421       var lst = document.querySelector('#playlist').value;
422       console.log(
423         "play playlist.value: " + document.querySelector('#playlist').value + 
424         ", abspieler.value: " + document.querySelector('#abspieler').value);
425       self.http_get('../api/strg/' + abs + '/play/liste/' + lst, function(responseText) {
426         self.meldung_mit_timeout(responseText, 1500);
427       });
428     }
095119 429   };
U 430   
c6fdc4 431   this.weiter = function() {
U 432     var bereichName = document.querySelector('.bereich-name').textContent;
433     if(bereichName === '') {
434       var titel = self.titelErmitteln(document.querySelector(".selected"));
435       var playername = document.querySelector('#abspieler').value;
436       console.log('plname: ' + playername + ' url: ' + titel.katalogUrl + titel.pfad + titel.name);
437       self.http_post('../api/strg/' + playername + '/weiter/titel', JSON.stringify(titel), function(responseText) {
438         self.meldung_mit_timeout(responseText, 1500);
439       });   
440     }
441   };
442   
9e14ef 443   this.kommando = function(kommando) {
U 444     var abs = document.querySelector('#abspieler').value;
445     self.http_get('../api/strg/' + abs + '/' + kommando, function(responseText) {
446       self.meldung_mit_timeout(responseText, 1500);
447     });
448   };
fe0cf7 449   
U 450   this.hier_spielen = function() {
451     var url;
452     // den Host noch vom Server abrufen und den nachfolgenden Code ersetzen
883f91 453     var host = 'http://' + window.location.host + '/tango';
fe0cf7 454     console.log('host: ' + host);
U 455     var bereichName = document.querySelector('.bereich-name').textContent;
456     if(bereichName === '') {
457       var titel = self.titelErmitteln(document.querySelector(".selected"));
458       //var playername = document.querySelector('#abspieler').value;
459       console.log(' url: ' + titel.katalogUrl + titel.pfad + titel.name);
460       //self.http_post('../api/strg/' + playername + '/titel', JSON.stringify(titel), function(responseText) {
461       //  self.meldung_mit_timeout(responseText, 1500);
462       //}); 
463       url = host + titel.katalogUrl + titel.pfad + titel.name;
464       window.open(url);
465     } else if(bereichName === 'Livestream-Auswahl') {
466       var streamName = document.querySelector(".selected").textContent;
467       // hier den Stream-URL abrufen
468       //GET /mz/api/store/[typname]/[name]
469       self.http_get('../api/store/Livestream/' + streamName, function(responseText) {
470         var stream = JSON.parse(responseText);        
471         url = stream.url;
472         window.open(url);
473       });
474     } else {
475       var lst = document.querySelector('#playlist').value;
476       console.log(
477         "play playlist.value: " + document.querySelector('#playlist').value + 
478         ", abspieler.value: " + document.querySelector('#abspieler').value);
479       // hier noch URL fuer Stream der Abspielliste abrufen
480       // Es muss auch noch die Funktion auf dem Server gabut werden, die 
481       // eine Abspielliste als Stream liefert
482       url = 'Stream fuer Abspielliste ' + lst + ' noch nicht gebaut.';
483     }
484     console.log('url: ' + url);
485     //window.open(url);
486   };  
9e14ef 487     
095119 488   /* ------------- Verwaltungsfunktionen Abspielliste -------------------- */
U 489   
490   self.alleTitelEntfernen = function() {
491     var plname = document.querySelector('#playlist').value;
492     self.http_delete('../api/alist/' + plname + '/alle', '', function(responseText) {
493       // DELETE    http://localhost:9090/mz/api/alist/liste1/0
494       //self.meldung_mit_timeout(responseText, 1500);
495       self.titel_liste();
496     });
497   };
498   
499   this.titelDazu = function() {
4f2589 500     var titel = self.titelErmitteln(document.querySelector(".selected"));
095119 501     //var titelName = elem.textContent;
4f2589 502     /*
095119 503     var titelName = elem.attributes.dateiName.nodeValue;
U 504     var album = elem.attributes.album.nodeValue;
505     var interpret = elem.attributes.interpret.nodeValue;
506     var anzName = elem.attributes.titelAnzName.nodeValue;
507     var titel;
508     if(self.mediaPfad.endsWith('/')) {
509       titel = new Titel(titelName, self.mediaPfad, self.ortPfad, interpret, anzName, album);
510     } else {
511       titel = new Titel(titelName, self.mediaPfad + '/', self.ortPfad, interpret, anzName, album);
512     }
4f2589 513     */
095119 514     var plname = document.querySelector('#playlist').value;
U 515     self.http_put('../api/alist/' + plname, JSON.stringify(titel), function(responseText) {
516       //self.meldung_mit_timeout(responseText, 1500);
517     });
518   };  
519   
520   this.titelWeg = function() {
521     var elem = document.querySelector(".selected");
522     var parentElem = elem.parentNode;
523     //console.log("elem: " + elem.nodeName + ", parent: " + parentElem.nodeName + ", len: " + parentElem.childNodes.length);
524     var liElems = parentElem.getElementsByTagName(elem.nodeName); // nur die LI Elemente
525     //console.log("liElems.anz: " + liElems.length);
526     var gefunden = false;
527     for(var i = 0; i < liElems.length && !gefunden; i++) {
528       //console.log(liElems.item(i).textContent);
529       if(liElems.item(i).classList.contains("selected")) {
530         gefunden = true;
531         var index = i;
532         //console.log(elem.textContent + ' hat Index ' + i);
533       }
534     }
535     // /mz/api/alist/[pl-name]/[nr] 
536     var plname = document.querySelector('#playlist').value;
537     self.http_delete('../api/alist/' + plname + '/' + index,'', function(responseText) {
538       // DELETE    http://localhost:9090/mz/api/alist/liste1/0
539       //self.meldung_mit_timeout(responseText, 1500);
540       self.titel_liste();
541     });
542     
b56bb3 543   };
U 544   
748b6f 545   /* ------------- Helfer fuer Entitaets-Formulare ----------------------- */
14638b 546   
U 547   /*
548    * url: '../api/store/Ablageort/liste/'
78d707 549    * tpl: "data/tpl/ablageort_liste.txt"
14638b 550    * storeUrl: '../api/store/Ablageort/'
U 551    * formFunc: "self.ablageort_form"
552    * cb: etwas wie
553    *   function(responseText){
554    *     var ablageort = JSON.parse(responseText);
555    *     self.ablageort_form(ablageort);
556    *   });
39ebae 557    */  
849ee2 558   this.entitaet_liste = function(bname, listUrl, tpl, storeUrl, formFunc, cb, customListCode) {
14638b 559     self.reset_top_buttons();
50e53e 560     document.querySelector('.bereich-name').textContent = bname;
78d707 561     var bb = document.querySelector('.breadcrumb-behaelter');
U 562     bb.textContent = "";
14638b 563     self.http_get(listUrl, function (responseText) {
2af7d6 564       self.html_erzeugen(tpl, JSON.parse(responseText), function (html) {
14638b 565         document.querySelector(".zentraler-inhalt").innerHTML = html;
U 566         self.addEvtListener('.entity-eintrag', 'click', function (event) {
567           var t = event.target;
568           self.http_get(storeUrl + t.textContent, cb);
569         });
570         //self.addEvtListener('#neu-btn', 'click', function (event) {
571         self.addEvtListener('#top-neu-btn', 'click', function(event) {
572           eval(formFunc + "(this)");
849ee2 573         });                
U 574         if(typeof(customListCode) !== 'function') {
575           // ..
576         } else {
577           customListCode();
578         }        
14638b 579       });
U 580     });
581   };  
748b6f 582       
48f8f9 583   /*
c7030d 584    * dat: gefuelltes Datenobjekt bei Aenderung
U 585    * key: der alte schluesselbegriff bei Aenderung (z.B. al.name)
78d707 586    * tpl: "data/tpl/form_abspielliste.txt"
c7030d 587    * url: '../api/store/Abspielliste/'
U 588    * selector: '#abspielliste-name'
589    * cbOk: etwas wie
590    *    function() {
591    *       self.abspielliste_auswahl_fuellen();
592    *       self.abspielliste_liste();
593    *     });
594    * delSelector: '#abspielliste-name'
595    * cbDel: etwas wie
596    *    function() {
597    *       self.abspielliste_auswahl_fuellen();
598    *       self.abspielliste_liste();
599    *     });
600    */
601
960317 602   this.entitaet_form = function(bname, dat, key, tpl, url, selector, keyname, cb) {
50e53e 603     document.querySelector('.bereich-name').textContent = bname;
2af7d6 604     self.html_erzeugen(tpl, dat, function (html) {
c7030d 605       document.querySelector(".zentraler-inhalt").innerHTML = html;
U 606       const form = document.querySelector('form');      
607       form.addEventListener('submit', function(event) {
960317 608         self.handle_submit(event, key, url, selector, keyname, cb);
c7030d 609       });
U 610       self.addEvtListener('#cancel-btn', 'click', cb);
611       self.addEvtListener('#loeschen-btn', 'click', function(event) {
612         event.preventDefault();
613         self.handle_del_btn(selector, url, cb);
614       });
615     });
616   };
617   
618   /*
48f8f9 619    * existingKey: wenn die Entitaet existiert und geandert werden soll
U 620    *                 leer, wenn neue Entitaet 
621    */
960317 622   this.handle_submit = function(event, existingKey, putUrl, keySelector, keyname, cb) {
48f8f9 623     event.preventDefault();
U 624     const data = new FormData(event.target);
625     const value = Object.fromEntries(data.entries());
626     var formkey = document.querySelector(keySelector).value;
960317 627     formkey = formkey.replace(' ', '');
U 628     formkey = formkey.replace(/[\W]+/g, '');
629     value[keyname] = formkey;
630     var daten = JSON.stringify(value);
48f8f9 631     if(typeof existingKey === "undefined" ||  existingKey.length < 1) {
U 632       // neu
633       self.http_put(putUrl + formkey, daten, function (responseText) {
634         if(typeof(cb) !== 'function') {
635           // ..
636         } else {
637           cb();
638         }
639       });
640     } else {
641       // aendern
642       self.http_put(putUrl + existingKey, daten, function (responseText) {
643         if(typeof(cb) !== 'function') {
644           // ..
645         } else {
646           cb();
647         }
648       });
649     }
650   };
651   
652   this.handle_del_btn = function(selectorKey, delUrl, cb) {
653     var pkey = document.querySelector(selectorKey).value;
654     var dlgdata = {"del-elem": pkey};
78d707 655     self.dialog_laden_und_zeigen('data/tpl/dlg-loeschen.txt', dlgdata, function() {
48f8f9 656       self.addEvtListener('#nein-btn', 'click', self.dialog_schliessen);
U 657       self.addEvtListener('#ja-btn', 'click', function(event) {
095119 658         //console.log("loeschen geklickt.");
48f8f9 659         self.http_delete(delUrl + pkey, '', function (responseText) {
U 660           self.dialog_schliessen();
661           if(typeof(cb) !== 'function') {
662             // ..
663           } else {
664             cb();
665           }
666         });
667       });
668     });    
669   };  
748b6f 670   
U 671   /* ------------------ sonstige Helfer ----------------------- */
672       
7c22a2 673   this.addEvtListener = function(selector, eventName, func) {
U 674     document.querySelectorAll(selector).forEach(elem => { elem.addEventListener(eventName, func); });
675   };
676   
677   this.removeClassMulti = function(selector) {
678     document.querySelectorAll('.' + selector).forEach(elem => { elem.classList.remove(selector); });
679   };
4f2589 680   
U 681   self.titelErmitteln = function(elem) {
682     var titelName = elem.attributes.dateiName.nodeValue;
683     var album = elem.attributes.album.nodeValue;
684     var interpret = elem.attributes.interpret.nodeValue;
685     var anzName = elem.attributes.titelAnzName.nodeValue;
686     var titel;
687     if(self.mediaPfad.endsWith('/')) {
688       titel = new Titel(titelName, self.mediaPfad, self.ortPfad, interpret, anzName, album);
689     } else {
690       titel = new Titel(titelName, self.mediaPfad + '/', self.ortPfad, interpret, anzName, album);
691     }
692     return titel;
693   };
7c22a2 694
f45e20 695   /* --------------------- asynchroner HTTP Client ----------------- */
faab2d 696   
f074f6 697   this.http_get = function (u, cb) {
b379f5 698     self.http_call('GET', u, null, cb);
U 699   };
f074f6 700
U 701   this.http_post = function (u, data, cb) {
b379f5 702     self.http_call('POST', u, data, cb);
U 703   };
704
90f5d4 705   this.http_put = function (u, data, cb) {
U 706     self.http_call('PUT', u, data, cb);
707   };
708   
5b7356 709   this.http_delete = function (u, data, cb) {
5f7e0b 710     // console.log("delete " + u);
5b7356 711     self.http_call('DELETE', u, data, cb);
U 712   };
713   
f074f6 714   this.http_call = function (method, u, data, scallback) {
b379f5 715     var xhr = new XMLHttpRequest();
U 716     var url = u;
f074f6 717     xhr.onreadystatechange = function () {
b379f5 718       if (this.readyState === 4 && this.status === 200) {
U 719         scallback(this.responseText);
720       }
721     };
722     xhr.open(method, url);
f074f6 723     if (method === 'GET') {
b379f5 724       xhr.send();
2597cd 725     } else if (method === 'POST' || method === 'PUT' || method === 'DELETE') {
b379f5 726       xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
U 727       xhr.send(data);
728     }
729   };
8239d1 730   
f45e20 731   /* ------------------------ aus App-Vorlage -------------------  */
cfa858 732
f074f6 733   this.menue_umschalten = function () {
cfa858 734     var ham = document.querySelector(".hamburger");
U 735     ham.classList.toggle("is-active"); // hamburger-icon umschalten
736     self.appMenu.toggle(); // menue oeffnen/schliessen
737   };
738
f074f6 739   this.info_dialog_zeigen = function () {
78d707 740     self.dialog_laden_und_zeigen('data/tpl/dlg-info.txt', '');
cfa858 741     self.menue_umschalten();
U 742   };
743
f074f6 744   this.seitenleiste_umschalten = function () {
cfa858 745     var ostDiv = document.querySelector('.ost');
f074f6 746     if (ostDiv.classList.contains('ost-open')) {
cfa858 747       ostDiv.classList.remove('ost-open');
f074f6 748       ostDiv.style.flexBasis = '0em';
cfa858 749     } else {
f074f6 750       ostDiv.classList.add('ost-open');
U 751       ostDiv.style.flexBasis = '6em';
cfa858 752     }
U 753     self.menue_umschalten();
754   };
755
f074f6 756   this.fusszeile_umschalten = function () {
cfa858 757     var suedDiv = document.querySelector('.sued');
f074f6 758     if (suedDiv.classList.contains('sued-open')) {
cfa858 759       suedDiv.classList.remove('sued-open');
f074f6 760       suedDiv.style.height = '0';
cfa858 761     } else {
U 762       suedDiv.classList.add('sued-open');
f074f6 763       suedDiv.style.height = '1.5em';
cfa858 764     }
U 765     self.menue_umschalten();
766   };
767
f074f6 768   this.menu_message = function (msg) {
cfa858 769     self.meldung_mit_timeout(msg, 1500);
U 770     var suedDiv = document.querySelector('.sued');
f074f6 771     if (suedDiv.classList.contains('sued-open')) {
cfa858 772     } else {
U 773       suedDiv.classList.add('sued-open');
f074f6 774       suedDiv.style.height = '1.5em';
cfa858 775     }
U 776     self.menue_umschalten();
777   };
778
f074f6 779   this.message_1 = function () {
cfa858 780     self.menu_message('Eine Mitteilung.');
U 781   };
782
f074f6 783   this.message_2 = function () {
cfa858 784     self.menu_message('Was wir schon immer sagen wollten.');
U 785   };
786
f074f6 787   this.message_3 = function (text) {
cfa858 788     self.menu_message(text);
U 789   };
790
f074f6 791   this.meldung_mit_timeout = function (meldung, timeout) {
cfa858 792     var s = document.querySelector('.sued');
a43e1a 793     s.classList.add('sued-open');
U 794     s.style.height = '1.5em';
cfa858 795     s.textContent = meldung;
f074f6 796     setTimeout(function () {
cfa858 797       s.textContent = 'Bereit.';
f074f6 798       setTimeout(function () {
cfa858 799         var suedDiv = document.querySelector('.sued');
f074f6 800         if (suedDiv.classList.contains('sued-open')) {
U 801           suedDiv.classList.remove('sued-open');
802           suedDiv.style.height = '0';
cfa858 803         }
U 804       }, 500);
805     }, timeout);
806   };
8239d1 807   
f45e20 808   /* --------------------- Dialog-Funktionen ------------------------ */
cfa858 809
U 810   /*
f074f6 811    Einen Dialog aus Vorlagen erzeugen
U 812    
813    vurl - URL zur Dialogvorlage
814    msgTpl - URL mit einer Vorlage eines Mitteilungstextes (optional)
815    */
5b7356 816   this.dialog_laden_und_zeigen = function (vurl, msgTpl, cb) {
U 817     var vorlage = self.cache[vurl];
818     if(vorlage === undefined) {
819       self.http_get(vurl, function(antwort) {
820         self.cache[vurl] = antwort;
821         self.dialog_zeigen(vurl, msgTpl, cb);
f074f6 822       });
cfa858 823     } else {
5b7356 824       self.dialog_zeigen(vurl, msgTpl, cb);
cfa858 825     }
U 826   };
827
5b7356 828   this.dialog_zeigen = function (vurl, inhalt, cb) {
U 829     var dlg = document.querySelector(".dialog");
2597cd 830     self.html_erzeugen(vurl, inhalt, function (html) {
U 831       dlg.style.height = '7em';
832       dlg.innerHTML = html;
833       document.querySelector('.close-btn').addEventListener('click', self.dialog_schliessen);
834       if(typeof(cb) !== 'function') {
835         // ..
836       } else {
837         cb();
838       }
839     });
5b7356 840   };
2597cd 841   
f45e20 842   this.dialog_schliessen = function () {
cfa858 843     document.querySelector('.close-btn').removeEventListener('click', self.dialog_schliessen);
U 844     var dlg = document.querySelector('.dialog');
845     dlg.style.height = '0';
846     dlg.innerHTML = '';
847   };
848
f45e20 849   /* ---------------------   Vorlagen   ---------------------- */
cfa858 850
U 851   /*
f074f6 852    Das HTML erzeugen, das entsteht, wenn eine Vorlage mit Inhalt
U 853    gefüllt wird
854    
855    Das Füllen erfolgt asynchron, d.h. der Programmlauf geht nach dem
856    Aufruf weiter ohne auf das Laden und Füllen der Vorlage zu warten.
857    Das fertige HTML wird der Callback-Funktion übergeben
858    sobald die Vorlage geladen und gefüllt ist, unabhängig davon, wo der
859    Programmlauf zu diesem Zeitpunkt mittlerweile ist.
860    
861    vurl - URL zur Vorlagendatei
862    inhalt - die JSON-Struktur, deren Inhalt in die
863    Vorlage gefüllt werden soll
864    cb - Callback-Funktion, die gerufen wird, wenn die Vorlage gefüllt ist.
865    Dieser Callback-Funktion wird das fertige HTML übergeben
866    */
867   this.html_erzeugen = function (vurl, inhalt, cb) {
cfa858 868     var vorlage = self.cache[vurl];
f074f6 869     if (vorlage === undefined) {
cfa858 870       self.vorlage_laden_und_fuellen(vurl, inhalt, cb);
U 871     } else {
872       self.vorlage_fuellen(vurl, inhalt, cb);
873     }
874   };
875
f074f6 876   this.vorlage_fuellen = function (vurl, inhalt, cb) {
cfa858 877     cb(Mustache.render(self.cache[vurl], inhalt));
U 878   };
879
880   /*
f074f6 881    Eine Vorlage vom Server in den lokalen Speicher laden
U 882    vurl - der URL unter dem die Vorlage zu finden ist
883    inhalt - die JSON-Struktur, deren Inhalt in die
884    Vorlage gefüllt werden soll
885    cb - callback: Diese Funktion wird gerufen, wenn die Vorlage mit dem
886    Inhalt gefüllt ist
887    */
888   this.vorlage_laden_und_fuellen = function (vurl, inhalt, cb) {
cfa858 889     var xmlhttp = new XMLHttpRequest();
f074f6 890     xmlhttp.onreadystatechange = function () {
cfa858 891       if (this.readyState == 4 && this.status == 200) {
U 892         self.cache[vurl] = this.responseText;
893         self.vorlage_fuellen(vurl, inhalt, cb);
894       }
895     };
896     xmlhttp.open("GET", vurl, true);
897     xmlhttp.send();
898   };
899
900
901 }
902
f45e20 903 /* ----------- Objekte ---------------- */
U 904
905 function Ablageort(n, o, u) {
906   this.name = n;
907   this.ort = o;
908   this.url = u;
909 }
910
911 function Einstellung(k, v) {
912   this.key = k;
913   this.value = v;
914 }
915
916 function Abspieler(n, u) {
917   this.name = n;
918   this.url = u;
8d7d35 919 }
U 920
d027b5 921 function Livestream(n, u) {
U 922   this.name = n;
923   this.url = u;
924 }
925
8d7d35 926 function Abspielliste(n) {
U 927   this.name = n;
e60cff 928 }
U 929
245ac1 930 function Titel(n, p, u, i, t, a) {
e60cff 931   this.katalogUrl = u;
2bdd78 932   this.pfad = p;
e60cff 933   this.name = n;
245ac1 934   this.interpret = i;
U 935   this.titelAnzName = t;
936   this.album  = a;
3929b0 937 }
U 938
a29f5c 939 function Geraet(n, e, a, s, st) {
3929b0 940   this.name = n;
U 941   this.einUrl = e;
942   this.ausUrl = a;
943   this.statusUrl = s;
a29f5c 944   this.status = st;
d6b78c 945 }