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