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