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