Persoenliche Mediazentrale
undisclosed
2023-01-29 183d3e0c669aa8882c994c8ef756e1ac1d61fd7d
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.audioCtx = new (window.AudioContext || window.webkitAudioContext)();
624         const track = self.audioCtx.createMediaElementSource(self.audioElem);
625         track.connect(self.audioCtx.destination);
626         self.audioElem.play();
627         self.playState = 'play';        
628       }
629     });
f9dd4f 630   };
U 631   
a6081c 632   this.getTitelFromJSONObj = function(titelObj) {
f9dd4f 633     //const audioElements = document.querySelectorAll('.entity-eintrag');
U 634     const host = 'http://' + window.location.host + '/tango';
a6081c 635     const titel = host + titelObj.katalogUrl + titelObj.pfad + titelObj.name;
f9dd4f 636     return titel;
U 637   };
638   
792b21 639   this.gehe_zu_dialog_zeigen = function () {
U 640     self.dialog_laden_und_zeigen('data/tpl/gehe-zu.txt', '', function(){
641       const form = document.querySelector('form');      
642       form.addEventListener('submit', function(event) {
643         // hier gehe zu realisieren
644         event.preventDefault();
645         const data = new FormData(event.target);
646         const value = Object.fromEntries(data.entries());
647         var daten = JSON.stringify(value);
648         console.log('gehe zu mit ' + daten);
649         var sekunden = (value['std'] * 3600) + (value['min'] * 60) + (value['sek'] * 1);
650         if(value['richtung'] === 'zurueck') {
651           sekunden *= -1;
652         }
653         console.log('sekunden: ' + sekunden);
654         self.dialog_schliessen();
655         // HTTP GET /mz/api/strg/abspieler/seek/[sekunden]
656         self.kommando('seek/' + sekunden);
657       });
658       self.addEvtListener('#cancel-btn', 'click', function(event) {
659         self.dialog_schliessen();
660       });
661       self.menue_umschalten();    
662     });
663   };
9e14ef 664     
095119 665   /* ------------- Verwaltungsfunktionen Abspielliste -------------------- */
U 666   
667   self.alleTitelEntfernen = function() {
668     var plname = document.querySelector('#playlist').value;
960359 669     self.http_delete('api/alist/' + plname + '/alle', '', function(responseText) {
095119 670       // DELETE    http://localhost:9090/mz/api/alist/liste1/0
U 671       //self.meldung_mit_timeout(responseText, 1500);
672       self.titel_liste();
673     });
674   };
675   
1bf4f6 676   /*
U 677    * {
678    *  "katalogUrl":"/media",
679    *  "pfad":"/Musik/B/Bay-City-Rollers/Original-Album-Classics/3/",
680    *  "name":"3-37-Love-Is.mp3",
681    *  "interpret":"Bay City Rollers",
682    *  "titelAnzName":"Love Is",
683    *  "album":"Original Album Classics"
684    * }
685    * @returns {undefined}
686    */
095119 687   this.titelDazu = function() {
4f2589 688     var titel = self.titelErmitteln(document.querySelector(".selected"));
095119 689     //var titelName = elem.textContent;
4f2589 690     /*
095119 691     var titelName = elem.attributes.dateiName.nodeValue;
U 692     var album = elem.attributes.album.nodeValue;
693     var interpret = elem.attributes.interpret.nodeValue;
694     var anzName = elem.attributes.titelAnzName.nodeValue;
695     var titel;
696     if(self.mediaPfad.endsWith('/')) {
697       titel = new Titel(titelName, self.mediaPfad, self.ortPfad, interpret, anzName, album);
698     } else {
699       titel = new Titel(titelName, self.mediaPfad + '/', self.ortPfad, interpret, anzName, album);
700     }
4f2589 701     */
095119 702     var plname = document.querySelector('#playlist').value;
960359 703     self.http_put('api/alist/' + plname, JSON.stringify(titel), function(responseText) {
095119 704       //self.meldung_mit_timeout(responseText, 1500);
U 705     });
706   };  
707   
708   this.titelWeg = function() {
1bf4f6 709     //var elem = document.querySelector(".selected");
U 710     //var parentElem = elem.parentNode;
095119 711     //console.log("elem: " + elem.nodeName + ", parent: " + parentElem.nodeName + ", len: " + parentElem.childNodes.length);
1bf4f6 712     //var liElems = parentElem.getElementsByTagName(elem.nodeName); // nur die LI Elemente
095119 713     //console.log("liElems.anz: " + liElems.length);
1bf4f6 714     //var gefunden = false;
U 715     //for(var i = 0; i < liElems.length && !gefunden; i++) {
095119 716       //console.log(liElems.item(i).textContent);
1bf4f6 717       //if(liElems.item(i).classList.contains("selected")) {
U 718         //gefunden = true;
719         //var index = i;
095119 720         //console.log(elem.textContent + ' hat Index ' + i);
1bf4f6 721       //}
U 722     //}
723     
724     const index = self.getIndexBySelector("selected");
095119 725     // /mz/api/alist/[pl-name]/[nr] 
U 726     var plname = document.querySelector('#playlist').value;
960359 727     self.http_delete('api/alist/' + plname + '/' + index,'', function(responseText) {
095119 728       // DELETE    http://localhost:9090/mz/api/alist/liste1/0
U 729       //self.meldung_mit_timeout(responseText, 1500);
730       self.titel_liste();
731     });
732     
b56bb3 733   };
U 734   
1bf4f6 735   this.getIndexBySelector = function(selector) {
U 736     var qSel = '.' + selector;
737     var elem = document.querySelector(qSel);
738     var parentElem = elem.parentNode;
739     //console.log("elem: " + elem.nodeName + ", parent: " + parentElem.nodeName + ", len: " + parentElem.childNodes.length);
740     var liElems = parentElem.getElementsByTagName(elem.nodeName); // nur die LI Elemente
741     //console.log("liElems.anz: " + liElems.length);
742     var gefunden = false;
743     var index = -1;
744     for(var i = 0; i < liElems.length && !gefunden; i++) {
745       //console.log(liElems.item(i).textContent);
746       if(liElems.item(i).classList.contains(selector)) {
747         gefunden = true;
748         index = i;
749         //console.log(elem.textContent + ' hat Index ' + i);
750       }
751     }
752     return index;
753   };
754   
748b6f 755   /* ------------- Helfer fuer Entitaets-Formulare ----------------------- */
14638b 756   
U 757   /*
758    * url: '../api/store/Ablageort/liste/'
78d707 759    * tpl: "data/tpl/ablageort_liste.txt"
14638b 760    * storeUrl: '../api/store/Ablageort/'
U 761    * formFunc: "self.ablageort_form"
762    * cb: etwas wie
763    *   function(responseText){
764    *     var ablageort = JSON.parse(responseText);
765    *     self.ablageort_form(ablageort);
766    *   });
39ebae 767    */  
849ee2 768   this.entitaet_liste = function(bname, listUrl, tpl, storeUrl, formFunc, cb, customListCode) {
14638b 769     self.reset_top_buttons();
50e53e 770     document.querySelector('.bereich-name').textContent = bname;
78d707 771     var bb = document.querySelector('.breadcrumb-behaelter');
U 772     bb.textContent = "";
14638b 773     self.http_get(listUrl, function (responseText) {
2af7d6 774       self.html_erzeugen(tpl, JSON.parse(responseText), function (html) {
14638b 775         document.querySelector(".zentraler-inhalt").innerHTML = html;
U 776         self.addEvtListener('.entity-eintrag', 'click', function (event) {
777           var t = event.target;
778           self.http_get(storeUrl + t.textContent, cb);
779         });
780         //self.addEvtListener('#neu-btn', 'click', function (event) {
781         self.addEvtListener('#top-neu-btn', 'click', function(event) {
782           eval(formFunc + "(this)");
849ee2 783         });                
U 784         if(typeof(customListCode) !== 'function') {
785           // ..
786         } else {
787           customListCode();
788         }        
14638b 789       });
U 790     });
791   };  
748b6f 792       
48f8f9 793   /*
c7030d 794    * dat: gefuelltes Datenobjekt bei Aenderung
U 795    * key: der alte schluesselbegriff bei Aenderung (z.B. al.name)
78d707 796    * tpl: "data/tpl/form_abspielliste.txt"
c7030d 797    * url: '../api/store/Abspielliste/'
U 798    * selector: '#abspielliste-name'
799    * cbOk: etwas wie
800    *    function() {
801    *       self.abspielliste_auswahl_fuellen();
802    *       self.abspielliste_liste();
803    *     });
804    * delSelector: '#abspielliste-name'
805    * cbDel: etwas wie
806    *    function() {
807    *       self.abspielliste_auswahl_fuellen();
808    *       self.abspielliste_liste();
809    *     });
810    */
811
960317 812   this.entitaet_form = function(bname, dat, key, tpl, url, selector, keyname, cb) {
50e53e 813     document.querySelector('.bereich-name').textContent = bname;
2af7d6 814     self.html_erzeugen(tpl, dat, function (html) {
c7030d 815       document.querySelector(".zentraler-inhalt").innerHTML = html;
U 816       const form = document.querySelector('form');      
817       form.addEventListener('submit', function(event) {
960317 818         self.handle_submit(event, key, url, selector, keyname, cb);
c7030d 819       });
U 820       self.addEvtListener('#cancel-btn', 'click', cb);
821       self.addEvtListener('#loeschen-btn', 'click', function(event) {
822         event.preventDefault();
823         self.handle_del_btn(selector, url, cb);
824       });
825     });
826   };
827   
828   /*
48f8f9 829    * existingKey: wenn die Entitaet existiert und geandert werden soll
U 830    *                 leer, wenn neue Entitaet 
831    */
960317 832   this.handle_submit = function(event, existingKey, putUrl, keySelector, keyname, cb) {
48f8f9 833     event.preventDefault();
U 834     const data = new FormData(event.target);
835     const value = Object.fromEntries(data.entries());
836     var formkey = document.querySelector(keySelector).value;
960317 837     formkey = formkey.replace(' ', '');
U 838     formkey = formkey.replace(/[\W]+/g, '');
839     value[keyname] = formkey;
840     var daten = JSON.stringify(value);
48f8f9 841     if(typeof existingKey === "undefined" ||  existingKey.length < 1) {
U 842       // neu
843       self.http_put(putUrl + formkey, daten, function (responseText) {
844         if(typeof(cb) !== 'function') {
845           // ..
846         } else {
847           cb();
848         }
849       });
850     } else {
851       // aendern
852       self.http_put(putUrl + existingKey, daten, function (responseText) {
853         if(typeof(cb) !== 'function') {
854           // ..
855         } else {
856           cb();
857         }
858       });
859     }
860   };
861   
862   this.handle_del_btn = function(selectorKey, delUrl, cb) {
863     var pkey = document.querySelector(selectorKey).value;
864     var dlgdata = {"del-elem": pkey};
78d707 865     self.dialog_laden_und_zeigen('data/tpl/dlg-loeschen.txt', dlgdata, function() {
48f8f9 866       self.addEvtListener('#nein-btn', 'click', self.dialog_schliessen);
U 867       self.addEvtListener('#ja-btn', 'click', function(event) {
095119 868         //console.log("loeschen geklickt.");
48f8f9 869         self.http_delete(delUrl + pkey, '', function (responseText) {
U 870           self.dialog_schliessen();
871           if(typeof(cb) !== 'function') {
872             // ..
873           } else {
874             cb();
875           }
876         });
877       });
878     });    
879   };  
748b6f 880   
U 881   /* ------------------ sonstige Helfer ----------------------- */
882       
7c22a2 883   this.addEvtListener = function(selector, eventName, func) {
U 884     document.querySelectorAll(selector).forEach(elem => { elem.addEventListener(eventName, func); });
885   };
886   
887   this.removeClassMulti = function(selector) {
888     document.querySelectorAll('.' + selector).forEach(elem => { elem.classList.remove(selector); });
889   };
4f2589 890   
U 891   self.titelErmitteln = function(elem) {
892     var titelName = elem.attributes.dateiName.nodeValue;
893     var album = elem.attributes.album.nodeValue;
894     var interpret = elem.attributes.interpret.nodeValue;
895     var anzName = elem.attributes.titelAnzName.nodeValue;
896     var titel;
897     if(self.mediaPfad.endsWith('/')) {
898       titel = new Titel(titelName, self.mediaPfad, self.ortPfad, interpret, anzName, album);
899     } else {
900       titel = new Titel(titelName, self.mediaPfad + '/', self.ortPfad, interpret, anzName, album);
901     }
902     return titel;
903   };
7c22a2 904
f45e20 905   /* --------------------- asynchroner HTTP Client ----------------- */
faab2d 906   
f074f6 907   this.http_get = function (u, cb) {
b379f5 908     self.http_call('GET', u, null, cb);
U 909   };
f074f6 910
U 911   this.http_post = function (u, data, cb) {
b379f5 912     self.http_call('POST', u, data, cb);
U 913   };
914
90f5d4 915   this.http_put = function (u, data, cb) {
U 916     self.http_call('PUT', u, data, cb);
917   };
918   
5b7356 919   this.http_delete = function (u, data, cb) {
5f7e0b 920     // console.log("delete " + u);
5b7356 921     self.http_call('DELETE', u, data, cb);
U 922   };
923   
ea73fa 924   this.http_call = function (method, callurl, data, scallback) {
b379f5 925     var xhr = new XMLHttpRequest();
f074f6 926     xhr.onreadystatechange = function () {
b379f5 927       if (this.readyState === 4 && this.status === 200) {
U 928         scallback(this.responseText);
929       }
930     };
ea73fa 931     xhr.open(method, callurl);
f074f6 932     if (method === 'GET') {
b379f5 933       xhr.send();
2597cd 934     } else if (method === 'POST' || method === 'PUT' || method === 'DELETE') {
b379f5 935       xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
U 936       xhr.send(data);
937     }
938   };
8239d1 939   
f45e20 940   /* ------------------------ aus App-Vorlage -------------------  */
cfa858 941
f074f6 942   this.menue_umschalten = function () {
cfa858 943     var ham = document.querySelector(".hamburger");
U 944     ham.classList.toggle("is-active"); // hamburger-icon umschalten
945     self.appMenu.toggle(); // menue oeffnen/schliessen
946   };
947
f074f6 948   this.info_dialog_zeigen = function () {
78d707 949     self.dialog_laden_und_zeigen('data/tpl/dlg-info.txt', '');
cfa858 950     self.menue_umschalten();
U 951   };
952
f074f6 953   this.seitenleiste_umschalten = function () {
cfa858 954     var ostDiv = document.querySelector('.ost');
f074f6 955     if (ostDiv.classList.contains('ost-open')) {
cfa858 956       ostDiv.classList.remove('ost-open');
f074f6 957       ostDiv.style.flexBasis = '0em';
cfa858 958     } else {
f074f6 959       ostDiv.classList.add('ost-open');
U 960       ostDiv.style.flexBasis = '6em';
cfa858 961     }
U 962     self.menue_umschalten();
963   };
964
f074f6 965   this.fusszeile_umschalten = function () {
cfa858 966     var suedDiv = document.querySelector('.sued');
f074f6 967     if (suedDiv.classList.contains('sued-open')) {
cfa858 968       suedDiv.classList.remove('sued-open');
f074f6 969       suedDiv.style.height = '0';
cfa858 970     } else {
U 971       suedDiv.classList.add('sued-open');
f074f6 972       suedDiv.style.height = '1.5em';
cfa858 973     }
U 974     self.menue_umschalten();
975   };
976
f074f6 977   this.menu_message = function (msg) {
cfa858 978     self.meldung_mit_timeout(msg, 1500);
U 979     var suedDiv = document.querySelector('.sued');
f074f6 980     if (suedDiv.classList.contains('sued-open')) {
cfa858 981     } else {
U 982       suedDiv.classList.add('sued-open');
f074f6 983       suedDiv.style.height = '1.5em';
cfa858 984     }
U 985     self.menue_umschalten();
986   };
987
f074f6 988   this.message_1 = function () {
cfa858 989     self.menu_message('Eine Mitteilung.');
U 990   };
991
f074f6 992   this.message_2 = function () {
cfa858 993     self.menu_message('Was wir schon immer sagen wollten.');
U 994   };
995
f074f6 996   this.message_3 = function (text) {
cfa858 997     self.menu_message(text);
U 998   };
999
f074f6 1000   this.meldung_mit_timeout = function (meldung, timeout) {
cfa858 1001     var s = document.querySelector('.sued');
a43e1a 1002     s.classList.add('sued-open');
U 1003     s.style.height = '1.5em';
cfa858 1004     s.textContent = meldung;
f074f6 1005     setTimeout(function () {
cfa858 1006       s.textContent = 'Bereit.';
f074f6 1007       setTimeout(function () {
cfa858 1008         var suedDiv = document.querySelector('.sued');
f074f6 1009         if (suedDiv.classList.contains('sued-open')) {
U 1010           suedDiv.classList.remove('sued-open');
1011           suedDiv.style.height = '0';
cfa858 1012         }
U 1013       }, 500);
1014     }, timeout);
1015   };
8239d1 1016   
f45e20 1017   /* --------------------- Dialog-Funktionen ------------------------ */
cfa858 1018
U 1019   /*
f074f6 1020    Einen Dialog aus Vorlagen erzeugen
U 1021    
1022    vurl - URL zur Dialogvorlage
1023    msgTpl - URL mit einer Vorlage eines Mitteilungstextes (optional)
1024    */
5b7356 1025   this.dialog_laden_und_zeigen = function (vurl, msgTpl, cb) {
U 1026     var vorlage = self.cache[vurl];
1027     if(vorlage === undefined) {
1028       self.http_get(vurl, function(antwort) {
1029         self.cache[vurl] = antwort;
1030         self.dialog_zeigen(vurl, msgTpl, cb);
f074f6 1031       });
cfa858 1032     } else {
5b7356 1033       self.dialog_zeigen(vurl, msgTpl, cb);
cfa858 1034     }
U 1035   };
1036
5b7356 1037   this.dialog_zeigen = function (vurl, inhalt, cb) {
U 1038     var dlg = document.querySelector(".dialog");
2597cd 1039     self.html_erzeugen(vurl, inhalt, function (html) {
U 1040       dlg.style.height = '7em';
1041       dlg.innerHTML = html;
1042       document.querySelector('.close-btn').addEventListener('click', self.dialog_schliessen);
1043       if(typeof(cb) !== 'function') {
1044         // ..
1045       } else {
1046         cb();
1047       }
1048     });
5b7356 1049   };
2597cd 1050   
f45e20 1051   this.dialog_schliessen = function () {
cfa858 1052     document.querySelector('.close-btn').removeEventListener('click', self.dialog_schliessen);
U 1053     var dlg = document.querySelector('.dialog');
1054     dlg.style.height = '0';
1055     dlg.innerHTML = '';
1056   };
1057
f45e20 1058   /* ---------------------   Vorlagen   ---------------------- */
cfa858 1059
U 1060   /*
f074f6 1061    Das HTML erzeugen, das entsteht, wenn eine Vorlage mit Inhalt
U 1062    gefüllt wird
1063    
1064    Das Füllen erfolgt asynchron, d.h. der Programmlauf geht nach dem
1065    Aufruf weiter ohne auf das Laden und Füllen der Vorlage zu warten.
1066    Das fertige HTML wird der Callback-Funktion übergeben
1067    sobald die Vorlage geladen und gefüllt ist, unabhängig davon, wo der
1068    Programmlauf zu diesem Zeitpunkt mittlerweile ist.
1069    
1070    vurl - URL zur Vorlagendatei
1071    inhalt - die JSON-Struktur, deren Inhalt in die
1072    Vorlage gefüllt werden soll
1073    cb - Callback-Funktion, die gerufen wird, wenn die Vorlage gefüllt ist.
1074    Dieser Callback-Funktion wird das fertige HTML übergeben
1075    */
1076   this.html_erzeugen = function (vurl, inhalt, cb) {
cfa858 1077     var vorlage = self.cache[vurl];
f074f6 1078     if (vorlage === undefined) {
cfa858 1079       self.vorlage_laden_und_fuellen(vurl, inhalt, cb);
U 1080     } else {
1081       self.vorlage_fuellen(vurl, inhalt, cb);
1082     }
1083   };
1084
f074f6 1085   this.vorlage_fuellen = function (vurl, inhalt, cb) {
cfa858 1086     cb(Mustache.render(self.cache[vurl], inhalt));
U 1087   };
1088
1089   /*
f074f6 1090    Eine Vorlage vom Server in den lokalen Speicher laden
U 1091    vurl - der URL unter dem die Vorlage zu finden ist
1092    inhalt - die JSON-Struktur, deren Inhalt in die
1093    Vorlage gefüllt werden soll
1094    cb - callback: Diese Funktion wird gerufen, wenn die Vorlage mit dem
1095    Inhalt gefüllt ist
1096    */
1097   this.vorlage_laden_und_fuellen = function (vurl, inhalt, cb) {
cfa858 1098     var xmlhttp = new XMLHttpRequest();
f074f6 1099     xmlhttp.onreadystatechange = function () {
cfa858 1100       if (this.readyState == 4 && this.status == 200) {
U 1101         self.cache[vurl] = this.responseText;
1102         self.vorlage_fuellen(vurl, inhalt, cb);
1103       }
1104     };
1105     xmlhttp.open("GET", vurl, true);
1106     xmlhttp.send();
1107   };
1108
1109
1110 }
1111
f45e20 1112 /* ----------- Objekte ---------------- */
U 1113
1114 function Ablageort(n, o, u) {
1115   this.name = n;
1116   this.ort = o;
1117   this.url = u;
1118 }
1119
1120 function Einstellung(k, v) {
1121   this.key = k;
1122   this.value = v;
1123 }
1124
1125 function Abspieler(n, u) {
1126   this.name = n;
1127   this.url = u;
8d7d35 1128 }
U 1129
d027b5 1130 function Livestream(n, u) {
U 1131   this.name = n;
1132   this.url = u;
1133 }
1134
8d7d35 1135 function Abspielliste(n) {
U 1136   this.name = n;
e60cff 1137 }
U 1138
245ac1 1139 function Titel(n, p, u, i, t, a) {
e60cff 1140   this.katalogUrl = u;
2bdd78 1141   this.pfad = p;
e60cff 1142   this.name = n;
245ac1 1143   this.interpret = i;
U 1144   this.titelAnzName = t;
1145   this.album  = a;
3929b0 1146 }
U 1147
a29f5c 1148 function Geraet(n, e, a, s, st) {
3929b0 1149   this.name = n;
U 1150   this.einUrl = e;
1151   this.ausUrl = a;
1152   this.statusUrl = s;
a29f5c 1153   this.status = st;
d6b78c 1154 }