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