Persoenliche Mediazentrale
ulrich
2021-04-09 c5c07f023e1e43a82ed471e990fcd85d13455c28
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() {
197     self.http_get('../api/store/Abspieler/', function (responseText) {
198       self.vorlage_laden_und_fuellen("data/tpl/abs_sel.tpl", JSON.parse(responseText), function (html) {
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;
208       });    
209     });
210   };
211   
e44ed0 212   this.addSelectedTitel = function() {
U 213     var elem = document.querySelector(".selected");
214     var titelName = elem.textContent;
215     var album = elem.attributes.album.nodeValue;
216     var interpret = elem.attributes.interpret.nodeValue;
217     var anzName = elem.attributes.titelAnzName.nodeValue;
218     var titel;
219     if(self.mediaPfad.endsWith('/')) {
220       titel = new Titel(titelName, self.mediaPfad, self.ortPfad, interpret, anzName, album);
221     } else {
222       titel = new Titel(titelName, self.mediaPfad + '/', self.ortPfad, interpret, anzName, album);
223     }
224     var plname = document.querySelector('#playlist').value;
225     self.http_put('../api/alist/' + plname, JSON.stringify(titel), function(responseText) {
226       self.meldung_mit_timeout(responseText, 1500);
227     });
228   };  
229   
748b6f 230   /* Unterer Einblendbereich */
U 231   
232   this.dialog_unten_zeigen = function() {
233     self.vorlage_laden_und_fuellen("data/tpl/ctrl.tpl", "", function (html) {
234       var dlg = document.querySelector(".dialog-unten");
235       dlg.style.height = '4.5em';
236       dlg.innerHTML = html;
237       self.abspieler_auswahl_fuellen();
238       self.abspielliste_auswahl_fuellen();
239       self.addEvtListener('#dazu-btn', 'click', self.addSelectedTitel);
240       self.media_liste();
241     });
242   };
243   
244   /* Titel einer Abspielliste */
245   
246   this.titel_liste = function() {
247     self.reset_top_buttons();
248     var plname = document.querySelector('#playlist').value;
249     self.http_get('../api/alist/' + plname, function (responseText) {
250       self.vorlage_laden_und_fuellen("data/tpl/titel_liste.tpl", JSON.parse(responseText), function (html) {
251         document.querySelector(".zentraler-inhalt").innerHTML = html;
252         self.addEvtListener('.entity-eintrag', 'click', function (event) {
253           var t = event.target;
254           self.removeClassMulti('selected');
255           t.classList.add('selected');
256         });
257       });
258     });
259   };  
260   
261   /* ------------- Helfer fuer Entitaets-Formulare ----------------------- */
14638b 262   
U 263   /*
264    * url: '../api/store/Ablageort/liste/'
265    * tpl: "data/tpl/ablageort_liste.tpl"
266    * storeUrl: '../api/store/Ablageort/'
267    * formFunc: "self.ablageort_form"
268    * cb: etwas wie
269    *   function(responseText){
270    *     var ablageort = JSON.parse(responseText);
271    *     self.ablageort_form(ablageort);
272    *   });
273    */
274   this.entitaet_liste = function(listUrl, tpl, storeUrl, formFunc, cb) {
275     self.reset_top_buttons();
276     self.http_get(listUrl, function (responseText) {
277       self.vorlage_laden_und_fuellen(tpl, JSON.parse(responseText), function (html) {
278         document.querySelector(".zentraler-inhalt").innerHTML = html;
279         self.addEvtListener('.entity-eintrag', 'click', function (event) {
280           var t = event.target;
281           self.http_get(storeUrl + t.textContent, cb);
282         });
283         //self.addEvtListener('#neu-btn', 'click', function (event) {
284         self.addEvtListener('#top-neu-btn', 'click', function(event) {
285           eval(formFunc + "(this)");
286         });        
287       });
288     });
289   };  
748b6f 290       
48f8f9 291   /*
c7030d 292    * dat: gefuelltes Datenobjekt bei Aenderung
U 293    * key: der alte schluesselbegriff bei Aenderung (z.B. al.name)
294    * tpl: "data/tpl/form_abspielliste.tpl"
295    * url: '../api/store/Abspielliste/'
296    * selector: '#abspielliste-name'
297    * cbOk: etwas wie
298    *    function() {
299    *       self.abspielliste_auswahl_fuellen();
300    *       self.abspielliste_liste();
301    *     });
302    * delSelector: '#abspielliste-name'
303    * cbDel: etwas wie
304    *    function() {
305    *       self.abspielliste_auswahl_fuellen();
306    *       self.abspielliste_liste();
307    *     });
308    */
309
310   this.entitaet_form = function(dat, key, tpl, url, selector, cb) {
311     self.vorlage_laden_und_fuellen(tpl, dat, function (html) {
312       document.querySelector(".zentraler-inhalt").innerHTML = html;
313       const form = document.querySelector('form');      
314       form.addEventListener('submit', function(event) {
315         self.handle_submit(event, key, url, selector, cb);
316       });
317       self.addEvtListener('#cancel-btn', 'click', cb);
318       self.addEvtListener('#loeschen-btn', 'click', function(event) {
319         event.preventDefault();
320         self.handle_del_btn(selector, url, cb);
321       });
322     });
323   };
324   
325   /*
48f8f9 326    * existingKey: wenn die Entitaet existiert und geandert werden soll
U 327    *                 leer, wenn neue Entitaet 
328    */
329   this.handle_submit = function(event, existingKey, putUrl, keySelector, cb) {
330     event.preventDefault();
331     const data = new FormData(event.target);
332     const value = Object.fromEntries(data.entries());
333     console.log({ value });
334     console.log(JSON.stringify(value));
335     var daten = JSON.stringify(value);
336     var formkey = document.querySelector(keySelector).value;
337     formkey = formkey.replace(' ', '').replace(/[\W]+/g, '');
338     if(typeof existingKey === "undefined" ||  existingKey.length < 1) {
339       // neu
340       self.http_put(putUrl + formkey, daten, function (responseText) {
341         if(typeof(cb) !== 'function') {
342           // ..
343         } else {
344           cb();
345         }
346       });
347     } else {
348       // aendern
349       self.http_put(putUrl + existingKey, daten, function (responseText) {
350         if(typeof(cb) !== 'function') {
351           // ..
352         } else {
353           cb();
354         }
355       });
356     }
357   };
358   
359   this.handle_del_btn = function(selectorKey, delUrl, cb) {
360     var pkey = document.querySelector(selectorKey).value;
361     var dlgdata = {"del-elem": pkey};
362     self.dialog_laden_und_zeigen('data/tpl/dlg-loeschen.tpl', dlgdata, function() {
363       self.addEvtListener('#nein-btn', 'click', self.dialog_schliessen);
364       self.addEvtListener('#ja-btn', 'click', function(event) {
365         console.log("loeschen geklickt.");
366         self.http_delete(delUrl + pkey, '', function (responseText) {
367           self.dialog_schliessen();
368           if(typeof(cb) !== 'function') {
369             // ..
370           } else {
371             cb();
372           }
373         });
374       });
375     });    
376   };  
748b6f 377   
U 378   /* ------------------ sonstige Helfer ----------------------- */
379       
7c22a2 380   this.addEvtListener = function(selector, eventName, func) {
U 381     document.querySelectorAll(selector).forEach(elem => { elem.addEventListener(eventName, func); });
382   };
383   
384   this.removeClassMulti = function(selector) {
385     document.querySelectorAll('.' + selector).forEach(elem => { elem.classList.remove(selector); });
386   };
387
f45e20 388   /* --------------------- asynchroner HTTP Client ----------------- */
faab2d 389   
f074f6 390   this.http_get = function (u, cb) {
b379f5 391     self.http_call('GET', u, null, cb);
U 392   };
f074f6 393
U 394   this.http_post = function (u, data, cb) {
b379f5 395     self.http_call('POST', u, data, cb);
U 396   };
397
90f5d4 398   this.http_put = function (u, data, cb) {
U 399     self.http_call('PUT', u, data, cb);
400   };
401   
5b7356 402   this.http_delete = function (u, data, cb) {
2597cd 403     console.log("delete " + u);
5b7356 404     self.http_call('DELETE', u, data, cb);
U 405   };
406   
f074f6 407   this.http_call = function (method, u, data, scallback) {
b379f5 408     var xhr = new XMLHttpRequest();
U 409     var url = u;
f074f6 410     xhr.onreadystatechange = function () {
b379f5 411       if (this.readyState === 4 && this.status === 200) {
U 412         scallback(this.responseText);
413       }
414     };
415     xhr.open(method, url);
f074f6 416     if (method === 'GET') {
b379f5 417       xhr.send();
2597cd 418     } else if (method === 'POST' || method === 'PUT' || method === 'DELETE') {
b379f5 419       xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
U 420       xhr.send(data);
421     }
422   };
8239d1 423   
f45e20 424   /* ------------------------ aus App-Vorlage -------------------  */
cfa858 425
f074f6 426   this.menue_umschalten = function () {
cfa858 427     var ham = document.querySelector(".hamburger");
U 428     ham.classList.toggle("is-active"); // hamburger-icon umschalten
429     self.appMenu.toggle(); // menue oeffnen/schliessen
430   };
431
f074f6 432   this.info_dialog_zeigen = function () {
cfa858 433     self.dialog_laden_und_zeigen('data/tpl/dlg-info.tpl', '');
U 434     self.menue_umschalten();
435   };
436
f074f6 437   this.seitenleiste_umschalten = function () {
cfa858 438     var ostDiv = document.querySelector('.ost');
f074f6 439     if (ostDiv.classList.contains('ost-open')) {
cfa858 440       ostDiv.classList.remove('ost-open');
f074f6 441       ostDiv.style.flexBasis = '0em';
cfa858 442     } else {
f074f6 443       ostDiv.classList.add('ost-open');
U 444       ostDiv.style.flexBasis = '6em';
cfa858 445     }
U 446     self.menue_umschalten();
447   };
448
f074f6 449   this.fusszeile_umschalten = function () {
cfa858 450     var suedDiv = document.querySelector('.sued');
f074f6 451     if (suedDiv.classList.contains('sued-open')) {
cfa858 452       suedDiv.classList.remove('sued-open');
f074f6 453       suedDiv.style.height = '0';
cfa858 454     } else {
U 455       suedDiv.classList.add('sued-open');
f074f6 456       suedDiv.style.height = '1.5em';
cfa858 457     }
U 458     self.menue_umschalten();
459   };
460
f074f6 461   this.menu_message = function (msg) {
cfa858 462     self.meldung_mit_timeout(msg, 1500);
U 463     var suedDiv = document.querySelector('.sued');
f074f6 464     if (suedDiv.classList.contains('sued-open')) {
cfa858 465     } else {
U 466       suedDiv.classList.add('sued-open');
f074f6 467       suedDiv.style.height = '1.5em';
cfa858 468     }
U 469     self.menue_umschalten();
470   };
471
f074f6 472   this.message_1 = function () {
cfa858 473     self.menu_message('Eine Mitteilung.');
U 474   };
475
f074f6 476   this.message_2 = function () {
cfa858 477     self.menu_message('Was wir schon immer sagen wollten.');
U 478   };
479
f074f6 480   this.message_3 = function (text) {
cfa858 481     self.menu_message(text);
U 482   };
483
f074f6 484   this.meldung_mit_timeout = function (meldung, timeout) {
cfa858 485     var s = document.querySelector('.sued');
a43e1a 486     s.classList.add('sued-open');
U 487     s.style.height = '1.5em';
cfa858 488     s.textContent = meldung;
f074f6 489     setTimeout(function () {
cfa858 490       s.textContent = 'Bereit.';
f074f6 491       setTimeout(function () {
cfa858 492         var suedDiv = document.querySelector('.sued');
f074f6 493         if (suedDiv.classList.contains('sued-open')) {
U 494           suedDiv.classList.remove('sued-open');
495           suedDiv.style.height = '0';
cfa858 496         }
U 497       }, 500);
498     }, timeout);
499   };
8239d1 500   
f45e20 501   /* --------------------- Dialog-Funktionen ------------------------ */
cfa858 502
U 503   /*
f074f6 504    Einen Dialog aus Vorlagen erzeugen
U 505    
506    vurl - URL zur Dialogvorlage
507    msgTpl - URL mit einer Vorlage eines Mitteilungstextes (optional)
508    */
5b7356 509   this.dialog_laden_und_zeigen = function (vurl, msgTpl, cb) {
U 510     var vorlage = self.cache[vurl];
511     if(vorlage === undefined) {
512       self.http_get(vurl, function(antwort) {
513         self.cache[vurl] = antwort;
514         self.dialog_zeigen(vurl, msgTpl, cb);
f074f6 515       });
cfa858 516     } else {
5b7356 517       self.dialog_zeigen(vurl, msgTpl, cb);
cfa858 518     }
U 519   };
520
5b7356 521   this.dialog_zeigen = function (vurl, inhalt, cb) {
U 522     var dlg = document.querySelector(".dialog");
2597cd 523     self.html_erzeugen(vurl, inhalt, function (html) {
U 524       dlg.style.height = '7em';
525       dlg.innerHTML = html;
526       document.querySelector('.close-btn').addEventListener('click', self.dialog_schliessen);
527       if(typeof(cb) !== 'function') {
528         // ..
529       } else {
530         cb();
531       }
532     });
5b7356 533   };
2597cd 534   
f45e20 535   this.dialog_schliessen = function () {
cfa858 536     document.querySelector('.close-btn').removeEventListener('click', self.dialog_schliessen);
U 537     var dlg = document.querySelector('.dialog');
538     dlg.style.height = '0';
539     dlg.innerHTML = '';
540   };
541
f45e20 542   /* ---------------------   Vorlagen   ---------------------- */
cfa858 543
U 544   /*
f074f6 545    Das HTML erzeugen, das entsteht, wenn eine Vorlage mit Inhalt
U 546    gefüllt wird
547    
548    Das Füllen erfolgt asynchron, d.h. der Programmlauf geht nach dem
549    Aufruf weiter ohne auf das Laden und Füllen der Vorlage zu warten.
550    Das fertige HTML wird der Callback-Funktion übergeben
551    sobald die Vorlage geladen und gefüllt ist, unabhängig davon, wo der
552    Programmlauf zu diesem Zeitpunkt mittlerweile ist.
553    
554    vurl - URL zur Vorlagendatei
555    inhalt - die JSON-Struktur, deren Inhalt in die
556    Vorlage gefüllt werden soll
557    cb - Callback-Funktion, die gerufen wird, wenn die Vorlage gefüllt ist.
558    Dieser Callback-Funktion wird das fertige HTML übergeben
559    */
560   this.html_erzeugen = function (vurl, inhalt, cb) {
cfa858 561     var vorlage = self.cache[vurl];
f074f6 562     if (vorlage === undefined) {
cfa858 563       self.vorlage_laden_und_fuellen(vurl, inhalt, cb);
U 564     } else {
565       self.vorlage_fuellen(vurl, inhalt, cb);
566     }
567   };
568
f074f6 569   this.vorlage_fuellen = function (vurl, inhalt, cb) {
cfa858 570     cb(Mustache.render(self.cache[vurl], inhalt));
U 571   };
572
573   /*
f074f6 574    Eine Vorlage vom Server in den lokalen Speicher laden
U 575    vurl - der URL unter dem die Vorlage zu finden ist
576    inhalt - die JSON-Struktur, deren Inhalt in die
577    Vorlage gefüllt werden soll
578    cb - callback: Diese Funktion wird gerufen, wenn die Vorlage mit dem
579    Inhalt gefüllt ist
580    */
581   this.vorlage_laden_und_fuellen = function (vurl, inhalt, cb) {
cfa858 582     var xmlhttp = new XMLHttpRequest();
f074f6 583     xmlhttp.onreadystatechange = function () {
cfa858 584       if (this.readyState == 4 && this.status == 200) {
U 585         self.cache[vurl] = this.responseText;
586         self.vorlage_fuellen(vurl, inhalt, cb);
587       }
588     };
589     xmlhttp.open("GET", vurl, true);
590     xmlhttp.send();
591   };
592
593
594 }
595
f45e20 596 /* ----------- Objekte ---------------- */
U 597
598 function Ablageort(n, o, u) {
599   this.name = n;
600   this.ort = o;
601   this.url = u;
602 }
603
604 function Einstellung(k, v) {
605   this.key = k;
606   this.value = v;
607 }
608
609 function Abspieler(n, u) {
610   this.name = n;
611   this.url = u;
8d7d35 612 }
U 613
614 function Abspielliste(n) {
615   this.name = n;
e60cff 616 }
U 617
245ac1 618 function Titel(n, p, u, i, t, a) {
e60cff 619   this.katalogUrl = u;
2bdd78 620   this.pfad = p;
e60cff 621   this.name = n;
245ac1 622   this.interpret = i;
U 623   this.titelAnzName = t;
624   this.album  = a;
d6b78c 625 }