Persoenliche Mediazentrale
ulrich
2021-04-30 ea73fab1d832c5e9b815042c6a6a61634125977e
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;
1c3232 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   };
1c3232 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'; 
960359 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
960359 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") {
960359 77               self.http_get('api/store/Ablageort/' + t.textContent, function(responseText) {
d027b5 78                 var ablageort = JSON.parse(responseText);
U 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       });
3deb92 111       //var url = '/tango' + self.ortPfad + self.mediaPfad;
U 112       var url = '.' + self.ortPfad + self.mediaPfad;
3271f1 113       if(!url.endsWith('/')) {
U 114         url = url + '/';
115       }
116       self.http_get(url, function(responseText) {
2af7d6 117         self.html_erzeugen("data/tpl/katalog_inhalt_liste.txt", JSON.parse(responseText), function (html) {
86bbf7 118           document.querySelector(".zentraler-inhalt").innerHTML = html;
U 119           self.addEvtListener('.entity-eintrag', 'click', function (event) {
120             var t = event.target;
3271f1 121             var tx = t.textContent;
86bbf7 122             if(t.classList.contains("entity-typ-folder")) {
3271f1 123               if(self.mediaPfad.endsWith('/')) {
U 124                 self.mediaPfad = self.mediaPfad + tx;                
125               } else {
126                 self.mediaPfad = self.mediaPfad + '/' + tx;
127               }
86bbf7 128               self.media_liste();
U 129             } else {
37eadf 130               if(t.classList.contains('selected')) {
78d707 131                 t.classList.add('added-to-playlist');
095119 132                 self.titelDazu();
37eadf 133               } else {
U 134                 self.removeClassMulti('selected');
135                 t.classList.add('selected');
136               }
e60cff 137               //self.selTitel = new Titel(t.textContent, self.ortPfad);       
86bbf7 138             }
U 139           });
8d7d35 140           self.addEvtListener('#top-up-btn', 'click', function(event) {
86bbf7 141             if(self.mediaPfad === '/') {
U 142               self.ortPfad = '/';              
143             } else {
144               var pos = self.mediaPfad.lastIndexOf('/');
3271f1 145               var parent;
U 146               if(pos > 1) {
147                 parent = self.mediaPfad.substring(0, pos);
148               } else {
149                 parent = '/';
150               }
86bbf7 151               self.mediaPfad = parent;
U 152             }
153             self.media_liste();
8d7d35 154           });
86bbf7 155         });
U 156       });
157     }
e60cff 158   };
245ac1 159   
39ebae 160   this.ein_aus_btn = function() {
U 161     self.addEvtListener('#ein-aus-btn', 'click', function (event) {
162       var geraetName = event.target.attributes.gname.nodeValue;
163       var nameElem = event.target.parentNode.querySelector('.schalt-geraet-name');
164       if(nameElem.classList.contains('schalt-geraet-true')) {
165         // ausschalten
960359 166         self.http_get('api/gstrg/geraet/' + geraetName + "/aus", function(responseText) {
39ebae 167           // console.log(responseText);
U 168           self.geraet_schalt_liste();
169         });
170       } else {
171         // einschalten            
960359 172         self.http_get('api/gstrg/geraet/' + geraetName + "/ein", function(responseText) {
39ebae 173           // console.log(responseText);
U 174           self.geraet_schalt_liste();
175         });
176       }
177     });
178   };
179   
3e5a56 180   this.geraet_schalt_liste = function() {
39ebae 181     /*self.entitaet_liste('Geräte schalten','../api/store/Geraet/listealles/', 
U 182       "data/tpl/geraet_schalt_liste.txt", '../api/store/Geraet/', 
183       "self.form_geraet_status", function(responseText) {*/
960359 184     self.entitaet_liste('Geräte schalten','api/store/Geraet/listealles/', 
U 185       "data/tpl/geraet_schalt_liste.txt", 'api/store/Geraet/', 
39ebae 186       "", function(responseText) {
U 187         //var geraet = JSON.parse(responseText);
188         //self.geraet_status_form(geraet);
189       }, self.ein_aus_btn);      
3e5a56 190   };
U 191   
3929b0 192   this.geraet_liste = function() {
960359 193     self.entitaet_liste('Geräte','api/store/Geraet/liste/', 
U 194       "data/tpl/geraet_liste.txt", 'api/store/Geraet/', 
3929b0 195       "self.geraet_form", function(responseText) {
U 196         var geraet = JSON.parse(responseText);
197         self.geraet_form(geraet);
198       });
199   };
200
a43e1a 201   this.ablageort_liste = function() {
960359 202     self.entitaet_liste('Kataloge','api/store/Ablageort/liste/', 
U 203       "data/tpl/ablageort_liste.txt", 'api/store/Ablageort/', 
e44ed0 204       "self.ablageort_form", function(responseText) {
U 205         var ablageort = JSON.parse(responseText);
206         self.ablageort_form(ablageort);
207       });
cf6509 208   };
U 209
210   this.prefs_liste = function() {
960359 211     self.entitaet_liste('Einstellungen','api/store/Einstellung/liste/', 
U 212       "data/tpl/einstellung_liste.txt", 'api/store/Einstellung/', 
e44ed0 213       "self.prefs_form", function(responseText) {
U 214         var einstellung = JSON.parse(responseText);
215         self.prefs_form(einstellung);
216       });
cf6509 217   };
U 218
3d4bca 219   this.abspieler_liste = function() {
960359 220     self.entitaet_liste('Abspieler','api/store/Abspieler/liste/', 
U 221       "data/tpl/abspieler_liste.txt", 'api/store/Abspieler/', 
e44ed0 222       "self.abspieler_form", function(responseText) {
U 223         var abspieler = JSON.parse(responseText);
224         self.abspieler_form(abspieler);
225       });
8d7d35 226   };
U 227   
d027b5 228   this.livestream_liste = function() {
960359 229     self.entitaet_liste('Livestreams','api/store/Livestream/liste/', 
U 230       "data/tpl/livestream_liste.txt", 'api/store/Livestream/', 
d027b5 231       "self.livestream_form", function(responseText) {
U 232         var livestream = JSON.parse(responseText);
233         self.livestream_form(livestream);
234       });
235   };
236   
8d7d35 237   this.abspielliste_liste = function() {
960359 238     self.entitaet_liste('Abspielliste','api/store/Abspielliste/liste/', 
U 239       "data/tpl/abspielliste_liste.txt", 'api/store/Abspielliste/', 
e44ed0 240       "self.abspielliste_form", function(responseText) {
095119 241         //console.log("responseTest: '" + responseText + "'");
e44ed0 242         var abspielliste = JSON.parse(responseText);
U 243         self.abspielliste_form(abspielliste);
244       });
8d7d35 245   };
U 246   
c7030d 247   /* -------------------- Entitaets-Formulare ------------------ */  
8d7d35 248   
U 249   this.abspielliste_form = function(al) {
50e53e 250     self.entitaet_form('Abspielliste', al, al.name,
960359 251       "data/tpl/form_abspielliste.txt", 'api/store/Abspielliste/',
960317 252       '#abspielliste-name', 'name', function(event) {
095119 253           if(event !== undefined) {
U 254             event.preventDefault();
255           }
c7030d 256           self.abspielliste_auswahl_fuellen();
U 257           self.abspielliste_liste();
258     });
3d4bca 259   };
71def1 260   
3d4bca 261   this.abspieler_form = function(pl) {
50e53e 262     self.entitaet_form('Abspieler', pl, pl.key,
960359 263       "data/tpl/form_abspieler.txt", 'api/store/Abspieler/',
960317 264       '#abspieler-name', 'name', function() { 
c7030d 265           self.abspieler_auswahl_fuellen();
U 266           self.abspieler_liste();
d027b5 267     });
U 268   };
269
270   this.livestream_form = function(ls) {
271     self.entitaet_form('Livestream', ls, ls.name,
960359 272       "data/tpl/form_livestream.txt", 'api/store/Livestream/',
960317 273       '#livestream-name', 'name', function() { 
d027b5 274           self.livestream_liste();
3929b0 275     });
U 276   };
277
278   this.geraet_form = function(ge) {
279     self.entitaet_form('Gerät', ge, ge.name,
960359 280       "data/tpl/form_geraet.txt", 'api/store/Geraet/',
960317 281       '#geraet-name', 'name', function() { 
3929b0 282           self.geraet_liste();
3e5a56 283     });
U 284   };
285
286   this.geraet_status_form = function(ge) {
287     self.entitaet_form('Gerät', ge, ge.name,
960359 288       "data/tpl/form_geraet_status.txt", 'api/store/Geraet/',
960317 289       '#geraet-name', 'name', function() { 
3e5a56 290           self.geraet_schalt_liste();
c7030d 291     });
71def1 292   };
U 293
cf6509 294   this.prefs_form = function(k) {
78d707 295     self.entitaet_form('Einstellung', k, k.key,
960359 296       "data/tpl/form_einstellung.txt", 'api/store/Einstellung/',
960317 297       '#einstellung-key', 'key', function() { 
c7030d 298           self.prefs_liste();
U 299     });
a43e1a 300   };
cfa858 301
90f5d4 302   /* 
U 303    * Ablageort-Formular anzeigen
304    * 
305    * {"name":"Katalog 2","ort":"/home/ulrich/Videos","url":"/media/kat2"}: 
306    * 
307    * @param {type} ablageort  der Ablageort, der bearbeitet werden soll, leer fuer neuen Ort
308    * @returns {undefined} kein Rueckgabewert
309    */
310   this.ablageort_form = function(ort) {
78d707 311     self.entitaet_form('Katalog', ort, ort.name,
960359 312       "data/tpl/form_ablageort.txt", 'api/store/Ablageort/',
a48ca3 313       '#ablageort-name', 'name', function() { 
c7030d 314         self.ablageort_liste();
U 315     });
b379f5 316   };
48f8f9 317   
748b6f 318   /* ------------------------------- UI-Dynamik ----------------------- */
48f8f9 319   
748b6f 320   self.reset_top_buttons = function() {
2af7d6 321     self.html_erzeugen("data/tpl/top_btns.txt", '', function (html) {
748b6f 322       document.querySelector(".top-btns").innerHTML = html;
U 323     });
324   };
325   
326   this.abspieler_auswahl_fuellen = function() {
960359 327     self.http_get('api/store/Abspieler/liste/', function (responseText) {
2af7d6 328       self.html_erzeugen("data/tpl/abs_sel.txt", JSON.parse(responseText), function (html) {
748b6f 329         document.querySelector(".abs-sel").innerHTML = html;
U 330       });    
331     });
332   };
333
334   this.abspielliste_auswahl_fuellen = function() {
960359 335     self.http_get('api/store/Abspielliste/', function (responseText) {
2af7d6 336       self.html_erzeugen("data/tpl/pl_sel.txt", JSON.parse(responseText), function (html) {
748b6f 337         document.querySelector(".pl-sel").innerHTML = html;
b56bb3 338         self.addEvtListener('#playlist', 'change', function() {
U 339           self.titel_liste();
340         });
748b6f 341       });    
U 342     });
343   };
344   
345   /* Unterer Einblendbereich */
346   
347   this.dialog_unten_zeigen = function() {
2af7d6 348     self.html_erzeugen("data/tpl/ctrl.txt", "", function (html) {
748b6f 349       var dlg = document.querySelector(".dialog-unten");
a07e7e 350       //dlg.style.height = '10em';
748b6f 351       dlg.innerHTML = html;
U 352       self.abspieler_auswahl_fuellen();
353       self.abspielliste_auswahl_fuellen();
095119 354       self.addEvtListener('#dazu-btn', 'click', self.titelDazu);
b56bb3 355       self.addEvtListener('#play-btn', 'click', self.play);
9e14ef 356       self.addEvtListener('#stop-btn', 'click', function() {
U 357         self.kommando('stop');
358       });
359       self.addEvtListener('#pause-btn', 'click', function() {
360         self.kommando('pause');
361       });
c6fdc4 362       self.addEvtListener('#weiter-btn', 'click', self.weiter);
fe0cf7 363
U 364       self.addEvtListener('#hier-btn', 'click', self.hier_spielen);
9e14ef 365       
U 366 /*
367     <button class="ctrl-btn ctrl-item" id="hier-btn" title="hier spielen"><i class="icon-tablet"></i></button>
368  */      
369       
095119 370       self.addEvtListener('#weg-btn', 'click', self.titelWeg);
U 371       self.addEvtListener('#leeren-btn', 'click', self.alleTitelEntfernen);
f6ea0c 372       
U 373       self.addEvtListener('#media-btn', 'click', self.media_liste);
374       self.addEvtListener('#plst-btn', 'click', self.titel_liste);
938b6b 375       self.addEvtListener('#live-btn', 'click', self.livestream_selection);
f6ea0c 376       self.addEvtListener('#switch-btn', 'click', self.geraet_schalt_liste);
U 377     
748b6f 378       self.media_liste();
U 379     });
380   };
381   
382   /* Titel einer Abspielliste */
383   
384   this.titel_liste = function() {
385     self.reset_top_buttons();
386     var plname = document.querySelector('#playlist').value;
78d707 387     document.querySelector('.bereich-name').textContent = 'Abspielliste ' + plname;
U 388     var bb = document.querySelector('.breadcrumb-behaelter');
389     bb.textContent = "";
960359 390     self.http_get('api/alist/' + plname, function (responseText) {
2af7d6 391       self.html_erzeugen("data/tpl/titel_liste.txt", JSON.parse(responseText), function (html) {
748b6f 392         document.querySelector(".zentraler-inhalt").innerHTML = html;
U 393         self.addEvtListener('.entity-eintrag', 'click', function (event) {
394           var t = event.target;
395           self.removeClassMulti('selected');
396           t.classList.add('selected');
397         });
398       });
399     });
400   };  
401   
b56bb3 402   /* ------------- Media-Steuerung ------------------------- */
U 403   
404   this.play = function() {
658c14 405     var bereichName = document.querySelector('.bereich-name').textContent;
U 406     if(bereichName === '') {
4f2589 407       var titel = self.titelErmitteln(document.querySelector(".selected"));
1c5fa4 408       var playername = document.querySelector('#abspieler').value;
658c14 409       console.log('plname: ' + playername + ' url: ' + titel.katalogUrl + titel.pfad + titel.name);
960359 410       self.http_post('api/strg/' + playername + '/play/titel', JSON.stringify(titel), function(responseText) {
658c14 411         self.meldung_mit_timeout(responseText, 1500);
d027b5 412       });   
U 413     } else if(bereichName === 'Livestream-Auswahl') {
414       var streamName = document.querySelector(".selected").textContent;
415       var playername = document.querySelector('#abspieler').value;
416       var stream = new Livestream(streamName, '-');
960359 417       self.http_post('api/strg/' + playername + '/play/stream', JSON.stringify(stream), function(responseText) {
d027b5 418         self.meldung_mit_timeout(responseText, 1500);
U 419       });   
658c14 420     } else {
U 421       var abs = document.querySelector('#abspieler').value;
422       var lst = document.querySelector('#playlist').value;
423       console.log(
424         "play playlist.value: " + document.querySelector('#playlist').value + 
425         ", abspieler.value: " + document.querySelector('#abspieler').value);
960359 426       self.http_get('api/strg/' + abs + '/play/liste/' + lst, function(responseText) {
658c14 427         self.meldung_mit_timeout(responseText, 1500);
U 428       });
429     }
095119 430   };
U 431   
c6fdc4 432   this.weiter = function() {
U 433     var bereichName = document.querySelector('.bereich-name').textContent;
434     if(bereichName === '') {
435       var titel = self.titelErmitteln(document.querySelector(".selected"));
436       var playername = document.querySelector('#abspieler').value;
437       console.log('plname: ' + playername + ' url: ' + titel.katalogUrl + titel.pfad + titel.name);
960359 438       self.http_post('api/strg/' + playername + '/weiter/titel', JSON.stringify(titel), function(responseText) {
c6fdc4 439         self.meldung_mit_timeout(responseText, 1500);
U 440       });   
441     }
442   };
443   
9e14ef 444   this.kommando = function(kommando) {
U 445     var abs = document.querySelector('#abspieler').value;
960359 446     self.http_get('api/strg/' + abs + '/' + kommando, function(responseText) {
9e14ef 447       self.meldung_mit_timeout(responseText, 1500);
U 448     });
449   };
fe0cf7 450   
U 451   this.hier_spielen = function() {
452     var url;
453     // den Host noch vom Server abrufen und den nachfolgenden Code ersetzen
883f91 454     var host = 'http://' + window.location.host + '/tango';
fe0cf7 455     console.log('host: ' + host);
U 456     var bereichName = document.querySelector('.bereich-name').textContent;
457     if(bereichName === '') {
458       var titel = self.titelErmitteln(document.querySelector(".selected"));
459       //var playername = document.querySelector('#abspieler').value;
460       console.log(' url: ' + titel.katalogUrl + titel.pfad + titel.name);
461       //self.http_post('../api/strg/' + playername + '/titel', JSON.stringify(titel), function(responseText) {
462       //  self.meldung_mit_timeout(responseText, 1500);
463       //}); 
464       url = host + titel.katalogUrl + titel.pfad + titel.name;
465       window.open(url);
466     } else if(bereichName === 'Livestream-Auswahl') {
467       var streamName = document.querySelector(".selected").textContent;
468       // hier den Stream-URL abrufen
469       //GET /mz/api/store/[typname]/[name]
960359 470       self.http_get('api/store/Livestream/' + streamName, function(responseText) {
fe0cf7 471         var stream = JSON.parse(responseText);        
U 472         url = stream.url;
473         window.open(url);
474       });
475     } else {
476       var lst = document.querySelector('#playlist').value;
477       console.log(
478         "play playlist.value: " + document.querySelector('#playlist').value + 
479         ", abspieler.value: " + document.querySelector('#abspieler').value);
480       // hier noch URL fuer Stream der Abspielliste abrufen
481       // Es muss auch noch die Funktion auf dem Server gabut werden, die 
482       // eine Abspielliste als Stream liefert
483       url = 'Stream fuer Abspielliste ' + lst + ' noch nicht gebaut.';
484     }
485     console.log('url: ' + url);
486     //window.open(url);
487   };  
9e14ef 488     
095119 489   /* ------------- Verwaltungsfunktionen Abspielliste -------------------- */
U 490   
491   self.alleTitelEntfernen = function() {
492     var plname = document.querySelector('#playlist').value;
960359 493     self.http_delete('api/alist/' + plname + '/alle', '', function(responseText) {
095119 494       // DELETE    http://localhost:9090/mz/api/alist/liste1/0
U 495       //self.meldung_mit_timeout(responseText, 1500);
496       self.titel_liste();
497     });
498   };
499   
500   this.titelDazu = function() {
4f2589 501     var titel = self.titelErmitteln(document.querySelector(".selected"));
095119 502     //var titelName = elem.textContent;
4f2589 503     /*
095119 504     var titelName = elem.attributes.dateiName.nodeValue;
U 505     var album = elem.attributes.album.nodeValue;
506     var interpret = elem.attributes.interpret.nodeValue;
507     var anzName = elem.attributes.titelAnzName.nodeValue;
508     var titel;
509     if(self.mediaPfad.endsWith('/')) {
510       titel = new Titel(titelName, self.mediaPfad, self.ortPfad, interpret, anzName, album);
511     } else {
512       titel = new Titel(titelName, self.mediaPfad + '/', self.ortPfad, interpret, anzName, album);
513     }
4f2589 514     */
095119 515     var plname = document.querySelector('#playlist').value;
960359 516     self.http_put('api/alist/' + plname, JSON.stringify(titel), function(responseText) {
095119 517       //self.meldung_mit_timeout(responseText, 1500);
U 518     });
519   };  
520   
521   this.titelWeg = function() {
522     var elem = document.querySelector(".selected");
523     var parentElem = elem.parentNode;
524     //console.log("elem: " + elem.nodeName + ", parent: " + parentElem.nodeName + ", len: " + parentElem.childNodes.length);
525     var liElems = parentElem.getElementsByTagName(elem.nodeName); // nur die LI Elemente
526     //console.log("liElems.anz: " + liElems.length);
527     var gefunden = false;
528     for(var i = 0; i < liElems.length && !gefunden; i++) {
529       //console.log(liElems.item(i).textContent);
530       if(liElems.item(i).classList.contains("selected")) {
531         gefunden = true;
532         var index = i;
533         //console.log(elem.textContent + ' hat Index ' + i);
534       }
535     }
536     // /mz/api/alist/[pl-name]/[nr] 
537     var plname = document.querySelector('#playlist').value;
960359 538     self.http_delete('api/alist/' + plname + '/' + index,'', function(responseText) {
095119 539       // DELETE    http://localhost:9090/mz/api/alist/liste1/0
U 540       //self.meldung_mit_timeout(responseText, 1500);
541       self.titel_liste();
542     });
543     
b56bb3 544   };
U 545   
748b6f 546   /* ------------- Helfer fuer Entitaets-Formulare ----------------------- */
14638b 547   
U 548   /*
549    * url: '../api/store/Ablageort/liste/'
78d707 550    * tpl: "data/tpl/ablageort_liste.txt"
14638b 551    * storeUrl: '../api/store/Ablageort/'
U 552    * formFunc: "self.ablageort_form"
553    * cb: etwas wie
554    *   function(responseText){
555    *     var ablageort = JSON.parse(responseText);
556    *     self.ablageort_form(ablageort);
557    *   });
39ebae 558    */  
849ee2 559   this.entitaet_liste = function(bname, listUrl, tpl, storeUrl, formFunc, cb, customListCode) {
14638b 560     self.reset_top_buttons();
50e53e 561     document.querySelector('.bereich-name').textContent = bname;
78d707 562     var bb = document.querySelector('.breadcrumb-behaelter');
U 563     bb.textContent = "";
14638b 564     self.http_get(listUrl, function (responseText) {
2af7d6 565       self.html_erzeugen(tpl, JSON.parse(responseText), function (html) {
14638b 566         document.querySelector(".zentraler-inhalt").innerHTML = html;
U 567         self.addEvtListener('.entity-eintrag', 'click', function (event) {
568           var t = event.target;
569           self.http_get(storeUrl + t.textContent, cb);
570         });
571         //self.addEvtListener('#neu-btn', 'click', function (event) {
572         self.addEvtListener('#top-neu-btn', 'click', function(event) {
573           eval(formFunc + "(this)");
849ee2 574         });                
U 575         if(typeof(customListCode) !== 'function') {
576           // ..
577         } else {
578           customListCode();
579         }        
14638b 580       });
U 581     });
582   };  
748b6f 583       
48f8f9 584   /*
c7030d 585    * dat: gefuelltes Datenobjekt bei Aenderung
U 586    * key: der alte schluesselbegriff bei Aenderung (z.B. al.name)
78d707 587    * tpl: "data/tpl/form_abspielliste.txt"
c7030d 588    * url: '../api/store/Abspielliste/'
U 589    * selector: '#abspielliste-name'
590    * cbOk: etwas wie
591    *    function() {
592    *       self.abspielliste_auswahl_fuellen();
593    *       self.abspielliste_liste();
594    *     });
595    * delSelector: '#abspielliste-name'
596    * cbDel: etwas wie
597    *    function() {
598    *       self.abspielliste_auswahl_fuellen();
599    *       self.abspielliste_liste();
600    *     });
601    */
602
960317 603   this.entitaet_form = function(bname, dat, key, tpl, url, selector, keyname, cb) {
50e53e 604     document.querySelector('.bereich-name').textContent = bname;
2af7d6 605     self.html_erzeugen(tpl, dat, function (html) {
c7030d 606       document.querySelector(".zentraler-inhalt").innerHTML = html;
U 607       const form = document.querySelector('form');      
608       form.addEventListener('submit', function(event) {
960317 609         self.handle_submit(event, key, url, selector, keyname, cb);
c7030d 610       });
U 611       self.addEvtListener('#cancel-btn', 'click', cb);
612       self.addEvtListener('#loeschen-btn', 'click', function(event) {
613         event.preventDefault();
614         self.handle_del_btn(selector, url, cb);
615       });
616     });
617   };
618   
619   /*
48f8f9 620    * existingKey: wenn die Entitaet existiert und geandert werden soll
U 621    *                 leer, wenn neue Entitaet 
622    */
960317 623   this.handle_submit = function(event, existingKey, putUrl, keySelector, keyname, cb) {
48f8f9 624     event.preventDefault();
U 625     const data = new FormData(event.target);
626     const value = Object.fromEntries(data.entries());
627     var formkey = document.querySelector(keySelector).value;
960317 628     formkey = formkey.replace(' ', '');
U 629     formkey = formkey.replace(/[\W]+/g, '');
630     value[keyname] = formkey;
631     var daten = JSON.stringify(value);
48f8f9 632     if(typeof existingKey === "undefined" ||  existingKey.length < 1) {
U 633       // neu
634       self.http_put(putUrl + formkey, daten, function (responseText) {
635         if(typeof(cb) !== 'function') {
636           // ..
637         } else {
638           cb();
639         }
640       });
641     } else {
642       // aendern
643       self.http_put(putUrl + existingKey, daten, function (responseText) {
644         if(typeof(cb) !== 'function') {
645           // ..
646         } else {
647           cb();
648         }
649       });
650     }
651   };
652   
653   this.handle_del_btn = function(selectorKey, delUrl, cb) {
654     var pkey = document.querySelector(selectorKey).value;
655     var dlgdata = {"del-elem": pkey};
78d707 656     self.dialog_laden_und_zeigen('data/tpl/dlg-loeschen.txt', dlgdata, function() {
48f8f9 657       self.addEvtListener('#nein-btn', 'click', self.dialog_schliessen);
U 658       self.addEvtListener('#ja-btn', 'click', function(event) {
095119 659         //console.log("loeschen geklickt.");
48f8f9 660         self.http_delete(delUrl + pkey, '', function (responseText) {
U 661           self.dialog_schliessen();
662           if(typeof(cb) !== 'function') {
663             // ..
664           } else {
665             cb();
666           }
667         });
668       });
669     });    
670   };  
748b6f 671   
U 672   /* ------------------ sonstige Helfer ----------------------- */
673       
7c22a2 674   this.addEvtListener = function(selector, eventName, func) {
U 675     document.querySelectorAll(selector).forEach(elem => { elem.addEventListener(eventName, func); });
676   };
677   
678   this.removeClassMulti = function(selector) {
679     document.querySelectorAll('.' + selector).forEach(elem => { elem.classList.remove(selector); });
680   };
4f2589 681   
U 682   self.titelErmitteln = function(elem) {
683     var titelName = elem.attributes.dateiName.nodeValue;
684     var album = elem.attributes.album.nodeValue;
685     var interpret = elem.attributes.interpret.nodeValue;
686     var anzName = elem.attributes.titelAnzName.nodeValue;
687     var titel;
688     if(self.mediaPfad.endsWith('/')) {
689       titel = new Titel(titelName, self.mediaPfad, self.ortPfad, interpret, anzName, album);
690     } else {
691       titel = new Titel(titelName, self.mediaPfad + '/', self.ortPfad, interpret, anzName, album);
692     }
693     return titel;
694   };
7c22a2 695
f45e20 696   /* --------------------- asynchroner HTTP Client ----------------- */
faab2d 697   
f074f6 698   this.http_get = function (u, cb) {
b379f5 699     self.http_call('GET', u, null, cb);
U 700   };
f074f6 701
U 702   this.http_post = function (u, data, cb) {
b379f5 703     self.http_call('POST', u, data, cb);
U 704   };
705
90f5d4 706   this.http_put = function (u, data, cb) {
U 707     self.http_call('PUT', u, data, cb);
708   };
709   
5b7356 710   this.http_delete = function (u, data, cb) {
5f7e0b 711     // console.log("delete " + u);
5b7356 712     self.http_call('DELETE', u, data, cb);
U 713   };
714   
ea73fa 715   this.http_call = function (method, callurl, data, scallback) {
b379f5 716     var xhr = new XMLHttpRequest();
f074f6 717     xhr.onreadystatechange = function () {
b379f5 718       if (this.readyState === 4 && this.status === 200) {
U 719         scallback(this.responseText);
720       }
721     };
ea73fa 722     xhr.open(method, callurl);
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 }