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