Persoenliche Mediazentrale
ulrich
2022-05-11 392dc977ae7d13188f07db1d6d865a964bb96858
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);
392dc9 377
U 378       self.addEvtListener('#voldn-btn', 'click', function() {
379         self.kommando('voldn');
380       });
381       self.addEvtListener('#volup-btn', 'click', function() {
382         self.kommando('volup');
383       });
384       
748b6f 385       self.media_liste();
U 386     });
387   };
388   
389   /* Titel einer Abspielliste */
390   
391   this.titel_liste = function() {
392     self.reset_top_buttons();
393     var plname = document.querySelector('#playlist').value;
78d707 394     document.querySelector('.bereich-name').textContent = 'Abspielliste ' + plname;
U 395     var bb = document.querySelector('.breadcrumb-behaelter');
396     bb.textContent = "";
960359 397     self.http_get('api/alist/' + plname, function (responseText) {
2af7d6 398       self.html_erzeugen("data/tpl/titel_liste.txt", JSON.parse(responseText), function (html) {
748b6f 399         document.querySelector(".zentraler-inhalt").innerHTML = html;
U 400         self.addEvtListener('.entity-eintrag', 'click', function (event) {
401           var t = event.target;
402           self.removeClassMulti('selected');
403           t.classList.add('selected');
404         });
405       });
406     });
407   };  
408   
b56bb3 409   /* ------------- Media-Steuerung ------------------------- */
U 410   
411   this.play = function() {
658c14 412     var bereichName = document.querySelector('.bereich-name').textContent;
U 413     if(bereichName === '') {
4f2589 414       var titel = self.titelErmitteln(document.querySelector(".selected"));
1c5fa4 415       var playername = document.querySelector('#abspieler').value;
658c14 416       console.log('plname: ' + playername + ' url: ' + titel.katalogUrl + titel.pfad + titel.name);
960359 417       self.http_post('api/strg/' + playername + '/play/titel', JSON.stringify(titel), function(responseText) {
658c14 418         self.meldung_mit_timeout(responseText, 1500);
d027b5 419       });   
U 420     } else if(bereichName === 'Livestream-Auswahl') {
421       var streamName = document.querySelector(".selected").textContent;
422       var playername = document.querySelector('#abspieler').value;
423       var stream = new Livestream(streamName, '-');
960359 424       self.http_post('api/strg/' + playername + '/play/stream', JSON.stringify(stream), function(responseText) {
d027b5 425         self.meldung_mit_timeout(responseText, 1500);
U 426       });   
658c14 427     } else {
U 428       var abs = document.querySelector('#abspieler').value;
429       var lst = document.querySelector('#playlist').value;
430       console.log(
431         "play playlist.value: " + document.querySelector('#playlist').value + 
432         ", abspieler.value: " + document.querySelector('#abspieler').value);
960359 433       self.http_get('api/strg/' + abs + '/play/liste/' + lst, function(responseText) {
658c14 434         self.meldung_mit_timeout(responseText, 1500);
U 435       });
436     }
095119 437   };
U 438   
c6fdc4 439   this.weiter = function() {
U 440     var bereichName = document.querySelector('.bereich-name').textContent;
441     if(bereichName === '') {
442       var titel = self.titelErmitteln(document.querySelector(".selected"));
443       var playername = document.querySelector('#abspieler').value;
444       console.log('plname: ' + playername + ' url: ' + titel.katalogUrl + titel.pfad + titel.name);
960359 445       self.http_post('api/strg/' + playername + '/weiter/titel', JSON.stringify(titel), function(responseText) {
c6fdc4 446         self.meldung_mit_timeout(responseText, 1500);
U 447       });   
448     }
449   };
450   
9e14ef 451   this.kommando = function(kommando) {
U 452     var abs = document.querySelector('#abspieler').value;
960359 453     self.http_get('api/strg/' + abs + '/' + kommando, function(responseText) {
9e14ef 454       self.meldung_mit_timeout(responseText, 1500);
U 455     });
456   };
fe0cf7 457   
U 458   this.hier_spielen = function() {
459     var url;
460     // den Host noch vom Server abrufen und den nachfolgenden Code ersetzen
883f91 461     var host = 'http://' + window.location.host + '/tango';
fe0cf7 462     console.log('host: ' + host);
U 463     var bereichName = document.querySelector('.bereich-name').textContent;
464     if(bereichName === '') {
465       var titel = self.titelErmitteln(document.querySelector(".selected"));
466       //var playername = document.querySelector('#abspieler').value;
467       console.log(' url: ' + titel.katalogUrl + titel.pfad + titel.name);
468       //self.http_post('../api/strg/' + playername + '/titel', JSON.stringify(titel), function(responseText) {
469       //  self.meldung_mit_timeout(responseText, 1500);
470       //}); 
471       url = host + titel.katalogUrl + titel.pfad + titel.name;
472       window.open(url);
473     } else if(bereichName === 'Livestream-Auswahl') {
474       var streamName = document.querySelector(".selected").textContent;
475       // hier den Stream-URL abrufen
476       //GET /mz/api/store/[typname]/[name]
960359 477       self.http_get('api/store/Livestream/' + streamName, function(responseText) {
fe0cf7 478         var stream = JSON.parse(responseText);        
U 479         url = stream.url;
480         window.open(url);
481       });
482     } else {
483       var lst = document.querySelector('#playlist').value;
484       console.log(
485         "play playlist.value: " + document.querySelector('#playlist').value + 
486         ", abspieler.value: " + document.querySelector('#abspieler').value);
487       // hier noch URL fuer Stream der Abspielliste abrufen
488       // Es muss auch noch die Funktion auf dem Server gabut werden, die 
489       // eine Abspielliste als Stream liefert
490       url = 'Stream fuer Abspielliste ' + lst + ' noch nicht gebaut.';
491     }
492     console.log('url: ' + url);
493     //window.open(url);
494   };  
9e14ef 495     
095119 496   /* ------------- Verwaltungsfunktionen Abspielliste -------------------- */
U 497   
498   self.alleTitelEntfernen = function() {
499     var plname = document.querySelector('#playlist').value;
960359 500     self.http_delete('api/alist/' + plname + '/alle', '', function(responseText) {
095119 501       // DELETE    http://localhost:9090/mz/api/alist/liste1/0
U 502       //self.meldung_mit_timeout(responseText, 1500);
503       self.titel_liste();
504     });
505   };
506   
507   this.titelDazu = function() {
4f2589 508     var titel = self.titelErmitteln(document.querySelector(".selected"));
095119 509     //var titelName = elem.textContent;
4f2589 510     /*
095119 511     var titelName = elem.attributes.dateiName.nodeValue;
U 512     var album = elem.attributes.album.nodeValue;
513     var interpret = elem.attributes.interpret.nodeValue;
514     var anzName = elem.attributes.titelAnzName.nodeValue;
515     var titel;
516     if(self.mediaPfad.endsWith('/')) {
517       titel = new Titel(titelName, self.mediaPfad, self.ortPfad, interpret, anzName, album);
518     } else {
519       titel = new Titel(titelName, self.mediaPfad + '/', self.ortPfad, interpret, anzName, album);
520     }
4f2589 521     */
095119 522     var plname = document.querySelector('#playlist').value;
960359 523     self.http_put('api/alist/' + plname, JSON.stringify(titel), function(responseText) {
095119 524       //self.meldung_mit_timeout(responseText, 1500);
U 525     });
526   };  
527   
528   this.titelWeg = function() {
529     var elem = document.querySelector(".selected");
530     var parentElem = elem.parentNode;
531     //console.log("elem: " + elem.nodeName + ", parent: " + parentElem.nodeName + ", len: " + parentElem.childNodes.length);
532     var liElems = parentElem.getElementsByTagName(elem.nodeName); // nur die LI Elemente
533     //console.log("liElems.anz: " + liElems.length);
534     var gefunden = false;
535     for(var i = 0; i < liElems.length && !gefunden; i++) {
536       //console.log(liElems.item(i).textContent);
537       if(liElems.item(i).classList.contains("selected")) {
538         gefunden = true;
539         var index = i;
540         //console.log(elem.textContent + ' hat Index ' + i);
541       }
542     }
543     // /mz/api/alist/[pl-name]/[nr] 
544     var plname = document.querySelector('#playlist').value;
960359 545     self.http_delete('api/alist/' + plname + '/' + index,'', function(responseText) {
095119 546       // DELETE    http://localhost:9090/mz/api/alist/liste1/0
U 547       //self.meldung_mit_timeout(responseText, 1500);
548       self.titel_liste();
549     });
550     
b56bb3 551   };
U 552   
748b6f 553   /* ------------- Helfer fuer Entitaets-Formulare ----------------------- */
14638b 554   
U 555   /*
556    * url: '../api/store/Ablageort/liste/'
78d707 557    * tpl: "data/tpl/ablageort_liste.txt"
14638b 558    * storeUrl: '../api/store/Ablageort/'
U 559    * formFunc: "self.ablageort_form"
560    * cb: etwas wie
561    *   function(responseText){
562    *     var ablageort = JSON.parse(responseText);
563    *     self.ablageort_form(ablageort);
564    *   });
39ebae 565    */  
849ee2 566   this.entitaet_liste = function(bname, listUrl, tpl, storeUrl, formFunc, cb, customListCode) {
14638b 567     self.reset_top_buttons();
50e53e 568     document.querySelector('.bereich-name').textContent = bname;
78d707 569     var bb = document.querySelector('.breadcrumb-behaelter');
U 570     bb.textContent = "";
14638b 571     self.http_get(listUrl, function (responseText) {
2af7d6 572       self.html_erzeugen(tpl, JSON.parse(responseText), function (html) {
14638b 573         document.querySelector(".zentraler-inhalt").innerHTML = html;
U 574         self.addEvtListener('.entity-eintrag', 'click', function (event) {
575           var t = event.target;
576           self.http_get(storeUrl + t.textContent, cb);
577         });
578         //self.addEvtListener('#neu-btn', 'click', function (event) {
579         self.addEvtListener('#top-neu-btn', 'click', function(event) {
580           eval(formFunc + "(this)");
849ee2 581         });                
U 582         if(typeof(customListCode) !== 'function') {
583           // ..
584         } else {
585           customListCode();
586         }        
14638b 587       });
U 588     });
589   };  
748b6f 590       
48f8f9 591   /*
c7030d 592    * dat: gefuelltes Datenobjekt bei Aenderung
U 593    * key: der alte schluesselbegriff bei Aenderung (z.B. al.name)
78d707 594    * tpl: "data/tpl/form_abspielliste.txt"
c7030d 595    * url: '../api/store/Abspielliste/'
U 596    * selector: '#abspielliste-name'
597    * cbOk: etwas wie
598    *    function() {
599    *       self.abspielliste_auswahl_fuellen();
600    *       self.abspielliste_liste();
601    *     });
602    * delSelector: '#abspielliste-name'
603    * cbDel: etwas wie
604    *    function() {
605    *       self.abspielliste_auswahl_fuellen();
606    *       self.abspielliste_liste();
607    *     });
608    */
609
960317 610   this.entitaet_form = function(bname, dat, key, tpl, url, selector, keyname, cb) {
50e53e 611     document.querySelector('.bereich-name').textContent = bname;
2af7d6 612     self.html_erzeugen(tpl, dat, function (html) {
c7030d 613       document.querySelector(".zentraler-inhalt").innerHTML = html;
U 614       const form = document.querySelector('form');      
615       form.addEventListener('submit', function(event) {
960317 616         self.handle_submit(event, key, url, selector, keyname, cb);
c7030d 617       });
U 618       self.addEvtListener('#cancel-btn', 'click', cb);
619       self.addEvtListener('#loeschen-btn', 'click', function(event) {
620         event.preventDefault();
621         self.handle_del_btn(selector, url, cb);
622       });
623     });
624   };
625   
626   /*
48f8f9 627    * existingKey: wenn die Entitaet existiert und geandert werden soll
U 628    *                 leer, wenn neue Entitaet 
629    */
960317 630   this.handle_submit = function(event, existingKey, putUrl, keySelector, keyname, cb) {
48f8f9 631     event.preventDefault();
U 632     const data = new FormData(event.target);
633     const value = Object.fromEntries(data.entries());
634     var formkey = document.querySelector(keySelector).value;
960317 635     formkey = formkey.replace(' ', '');
U 636     formkey = formkey.replace(/[\W]+/g, '');
637     value[keyname] = formkey;
638     var daten = JSON.stringify(value);
48f8f9 639     if(typeof existingKey === "undefined" ||  existingKey.length < 1) {
U 640       // neu
641       self.http_put(putUrl + formkey, daten, function (responseText) {
642         if(typeof(cb) !== 'function') {
643           // ..
644         } else {
645           cb();
646         }
647       });
648     } else {
649       // aendern
650       self.http_put(putUrl + existingKey, daten, function (responseText) {
651         if(typeof(cb) !== 'function') {
652           // ..
653         } else {
654           cb();
655         }
656       });
657     }
658   };
659   
660   this.handle_del_btn = function(selectorKey, delUrl, cb) {
661     var pkey = document.querySelector(selectorKey).value;
662     var dlgdata = {"del-elem": pkey};
78d707 663     self.dialog_laden_und_zeigen('data/tpl/dlg-loeschen.txt', dlgdata, function() {
48f8f9 664       self.addEvtListener('#nein-btn', 'click', self.dialog_schliessen);
U 665       self.addEvtListener('#ja-btn', 'click', function(event) {
095119 666         //console.log("loeschen geklickt.");
48f8f9 667         self.http_delete(delUrl + pkey, '', function (responseText) {
U 668           self.dialog_schliessen();
669           if(typeof(cb) !== 'function') {
670             // ..
671           } else {
672             cb();
673           }
674         });
675       });
676     });    
677   };  
748b6f 678   
U 679   /* ------------------ sonstige Helfer ----------------------- */
680       
7c22a2 681   this.addEvtListener = function(selector, eventName, func) {
U 682     document.querySelectorAll(selector).forEach(elem => { elem.addEventListener(eventName, func); });
683   };
684   
685   this.removeClassMulti = function(selector) {
686     document.querySelectorAll('.' + selector).forEach(elem => { elem.classList.remove(selector); });
687   };
4f2589 688   
U 689   self.titelErmitteln = function(elem) {
690     var titelName = elem.attributes.dateiName.nodeValue;
691     var album = elem.attributes.album.nodeValue;
692     var interpret = elem.attributes.interpret.nodeValue;
693     var anzName = elem.attributes.titelAnzName.nodeValue;
694     var titel;
695     if(self.mediaPfad.endsWith('/')) {
696       titel = new Titel(titelName, self.mediaPfad, self.ortPfad, interpret, anzName, album);
697     } else {
698       titel = new Titel(titelName, self.mediaPfad + '/', self.ortPfad, interpret, anzName, album);
699     }
700     return titel;
701   };
7c22a2 702
f45e20 703   /* --------------------- asynchroner HTTP Client ----------------- */
faab2d 704   
f074f6 705   this.http_get = function (u, cb) {
b379f5 706     self.http_call('GET', u, null, cb);
U 707   };
f074f6 708
U 709   this.http_post = function (u, data, cb) {
b379f5 710     self.http_call('POST', u, data, cb);
U 711   };
712
90f5d4 713   this.http_put = function (u, data, cb) {
U 714     self.http_call('PUT', u, data, cb);
715   };
716   
5b7356 717   this.http_delete = function (u, data, cb) {
5f7e0b 718     // console.log("delete " + u);
5b7356 719     self.http_call('DELETE', u, data, cb);
U 720   };
721   
ea73fa 722   this.http_call = function (method, callurl, data, scallback) {
b379f5 723     var xhr = new XMLHttpRequest();
f074f6 724     xhr.onreadystatechange = function () {
b379f5 725       if (this.readyState === 4 && this.status === 200) {
U 726         scallback(this.responseText);
727       }
728     };
ea73fa 729     xhr.open(method, callurl);
f074f6 730     if (method === 'GET') {
b379f5 731       xhr.send();
2597cd 732     } else if (method === 'POST' || method === 'PUT' || method === 'DELETE') {
b379f5 733       xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
U 734       xhr.send(data);
735     }
736   };
8239d1 737   
f45e20 738   /* ------------------------ aus App-Vorlage -------------------  */
cfa858 739
f074f6 740   this.menue_umschalten = function () {
cfa858 741     var ham = document.querySelector(".hamburger");
U 742     ham.classList.toggle("is-active"); // hamburger-icon umschalten
743     self.appMenu.toggle(); // menue oeffnen/schliessen
744   };
745
f074f6 746   this.info_dialog_zeigen = function () {
78d707 747     self.dialog_laden_und_zeigen('data/tpl/dlg-info.txt', '');
cfa858 748     self.menue_umschalten();
U 749   };
750
f074f6 751   this.seitenleiste_umschalten = function () {
cfa858 752     var ostDiv = document.querySelector('.ost');
f074f6 753     if (ostDiv.classList.contains('ost-open')) {
cfa858 754       ostDiv.classList.remove('ost-open');
f074f6 755       ostDiv.style.flexBasis = '0em';
cfa858 756     } else {
f074f6 757       ostDiv.classList.add('ost-open');
U 758       ostDiv.style.flexBasis = '6em';
cfa858 759     }
U 760     self.menue_umschalten();
761   };
762
f074f6 763   this.fusszeile_umschalten = function () {
cfa858 764     var suedDiv = document.querySelector('.sued');
f074f6 765     if (suedDiv.classList.contains('sued-open')) {
cfa858 766       suedDiv.classList.remove('sued-open');
f074f6 767       suedDiv.style.height = '0';
cfa858 768     } else {
U 769       suedDiv.classList.add('sued-open');
f074f6 770       suedDiv.style.height = '1.5em';
cfa858 771     }
U 772     self.menue_umschalten();
773   };
774
f074f6 775   this.menu_message = function (msg) {
cfa858 776     self.meldung_mit_timeout(msg, 1500);
U 777     var suedDiv = document.querySelector('.sued');
f074f6 778     if (suedDiv.classList.contains('sued-open')) {
cfa858 779     } else {
U 780       suedDiv.classList.add('sued-open');
f074f6 781       suedDiv.style.height = '1.5em';
cfa858 782     }
U 783     self.menue_umschalten();
784   };
785
f074f6 786   this.message_1 = function () {
cfa858 787     self.menu_message('Eine Mitteilung.');
U 788   };
789
f074f6 790   this.message_2 = function () {
cfa858 791     self.menu_message('Was wir schon immer sagen wollten.');
U 792   };
793
f074f6 794   this.message_3 = function (text) {
cfa858 795     self.menu_message(text);
U 796   };
797
f074f6 798   this.meldung_mit_timeout = function (meldung, timeout) {
cfa858 799     var s = document.querySelector('.sued');
a43e1a 800     s.classList.add('sued-open');
U 801     s.style.height = '1.5em';
cfa858 802     s.textContent = meldung;
f074f6 803     setTimeout(function () {
cfa858 804       s.textContent = 'Bereit.';
f074f6 805       setTimeout(function () {
cfa858 806         var suedDiv = document.querySelector('.sued');
f074f6 807         if (suedDiv.classList.contains('sued-open')) {
U 808           suedDiv.classList.remove('sued-open');
809           suedDiv.style.height = '0';
cfa858 810         }
U 811       }, 500);
812     }, timeout);
813   };
8239d1 814   
f45e20 815   /* --------------------- Dialog-Funktionen ------------------------ */
cfa858 816
U 817   /*
f074f6 818    Einen Dialog aus Vorlagen erzeugen
U 819    
820    vurl - URL zur Dialogvorlage
821    msgTpl - URL mit einer Vorlage eines Mitteilungstextes (optional)
822    */
5b7356 823   this.dialog_laden_und_zeigen = function (vurl, msgTpl, cb) {
U 824     var vorlage = self.cache[vurl];
825     if(vorlage === undefined) {
826       self.http_get(vurl, function(antwort) {
827         self.cache[vurl] = antwort;
828         self.dialog_zeigen(vurl, msgTpl, cb);
f074f6 829       });
cfa858 830     } else {
5b7356 831       self.dialog_zeigen(vurl, msgTpl, cb);
cfa858 832     }
U 833   };
834
5b7356 835   this.dialog_zeigen = function (vurl, inhalt, cb) {
U 836     var dlg = document.querySelector(".dialog");
2597cd 837     self.html_erzeugen(vurl, inhalt, function (html) {
U 838       dlg.style.height = '7em';
839       dlg.innerHTML = html;
840       document.querySelector('.close-btn').addEventListener('click', self.dialog_schliessen);
841       if(typeof(cb) !== 'function') {
842         // ..
843       } else {
844         cb();
845       }
846     });
5b7356 847   };
2597cd 848   
f45e20 849   this.dialog_schliessen = function () {
cfa858 850     document.querySelector('.close-btn').removeEventListener('click', self.dialog_schliessen);
U 851     var dlg = document.querySelector('.dialog');
852     dlg.style.height = '0';
853     dlg.innerHTML = '';
854   };
855
f45e20 856   /* ---------------------   Vorlagen   ---------------------- */
cfa858 857
U 858   /*
f074f6 859    Das HTML erzeugen, das entsteht, wenn eine Vorlage mit Inhalt
U 860    gefüllt wird
861    
862    Das Füllen erfolgt asynchron, d.h. der Programmlauf geht nach dem
863    Aufruf weiter ohne auf das Laden und Füllen der Vorlage zu warten.
864    Das fertige HTML wird der Callback-Funktion übergeben
865    sobald die Vorlage geladen und gefüllt ist, unabhängig davon, wo der
866    Programmlauf zu diesem Zeitpunkt mittlerweile ist.
867    
868    vurl - URL zur Vorlagendatei
869    inhalt - die JSON-Struktur, deren Inhalt in die
870    Vorlage gefüllt werden soll
871    cb - Callback-Funktion, die gerufen wird, wenn die Vorlage gefüllt ist.
872    Dieser Callback-Funktion wird das fertige HTML übergeben
873    */
874   this.html_erzeugen = function (vurl, inhalt, cb) {
cfa858 875     var vorlage = self.cache[vurl];
f074f6 876     if (vorlage === undefined) {
cfa858 877       self.vorlage_laden_und_fuellen(vurl, inhalt, cb);
U 878     } else {
879       self.vorlage_fuellen(vurl, inhalt, cb);
880     }
881   };
882
f074f6 883   this.vorlage_fuellen = function (vurl, inhalt, cb) {
cfa858 884     cb(Mustache.render(self.cache[vurl], inhalt));
U 885   };
886
887   /*
f074f6 888    Eine Vorlage vom Server in den lokalen Speicher laden
U 889    vurl - der URL unter dem die Vorlage zu finden ist
890    inhalt - die JSON-Struktur, deren Inhalt in die
891    Vorlage gefüllt werden soll
892    cb - callback: Diese Funktion wird gerufen, wenn die Vorlage mit dem
893    Inhalt gefüllt ist
894    */
895   this.vorlage_laden_und_fuellen = function (vurl, inhalt, cb) {
cfa858 896     var xmlhttp = new XMLHttpRequest();
f074f6 897     xmlhttp.onreadystatechange = function () {
cfa858 898       if (this.readyState == 4 && this.status == 200) {
U 899         self.cache[vurl] = this.responseText;
900         self.vorlage_fuellen(vurl, inhalt, cb);
901       }
902     };
903     xmlhttp.open("GET", vurl, true);
904     xmlhttp.send();
905   };
906
907
908 }
909
f45e20 910 /* ----------- Objekte ---------------- */
U 911
912 function Ablageort(n, o, u) {
913   this.name = n;
914   this.ort = o;
915   this.url = u;
916 }
917
918 function Einstellung(k, v) {
919   this.key = k;
920   this.value = v;
921 }
922
923 function Abspieler(n, u) {
924   this.name = n;
925   this.url = u;
8d7d35 926 }
U 927
d027b5 928 function Livestream(n, u) {
U 929   this.name = n;
930   this.url = u;
931 }
932
8d7d35 933 function Abspielliste(n) {
U 934   this.name = n;
e60cff 935 }
U 936
245ac1 937 function Titel(n, p, u, i, t, a) {
e60cff 938   this.katalogUrl = u;
2bdd78 939   this.pfad = p;
e60cff 940   this.name = n;
245ac1 941   this.interpret = i;
U 942   this.titelAnzName = t;
943   this.album  = a;
3929b0 944 }
U 945
a29f5c 946 function Geraet(n, e, a, s, st) {
3929b0 947   this.name = n;
U 948   this.einUrl = e;
949   this.ausUrl = a;
950   this.statusUrl = s;
a29f5c 951   this.status = st;
d6b78c 952 }