Багфиксы. Обработка особенностей поведения mopidy и mpd. Обработка пустых и малознача...
[mpd-lua.git] / mpd.js
1 urlbase="/cgi-bin/mpd.cgi?"
2 minScrollHeight=200
3
4 currentState=""
5
6 function GetFilename(url)
7 {
8    if (url)
9    return url.split('/').pop().split('#')[0].split('?')[0];
10 }
11
12 function EscapeStr(str) {
13   res = str.replace(/'/g,"\\'");
14   return res;
15 }
16
17 function SetSize() {
18   var w = window,
19       d = document,
20       e = d.documentElement,
21       g = d.getElementsByTagName('body')[0],
22       body_h = g.clientHeight,
23       window_h = w.innerHeight|| e.clientHeight|| g.clientHeight,
24       items = d.getElementById('items'),
25       current_h = items.clientHeight,
26       new_h=(window_h-body_h)+current_h;
27       if (new_h>minScrollHeight) {
28         items.style.height=new_h+"px";
29       }
30 }
31
32 function toHHMMSS(seconds) {
33     var hours = Math.floor(seconds / 3600);
34     seconds -= hours*3600;
35     var minutes = Math.floor(seconds / 60);
36     seconds -= minutes*60;
37
38     if (hours   < 10) {hours   = "0"+hours;}
39     if (minutes < 10) {minutes = "0"+minutes;}
40     if (seconds < 10) {seconds = "0"+seconds;}
41     if (hours == 0) {
42      return minutes+':'+seconds;
43     } else {
44      return hours+':'+minutes+':'+seconds;
45     }
46 }
47
48 function RefreshTime() {
49   if (currentSeconds) {
50     currentTime = toHHMMSS(currentSeconds)
51     if (trackSeconds) {
52       trackTime = toHHMMSS(trackSeconds)
53       nowPlayingTime = currentTime+"/"+trackTime
54     } else {
55       nowPlayingTime = currentTime
56     }
57   } else {
58     nowPlayingTime = "-:--/-:--"
59   }
60   document.getElementById('nowplaying_tracklen').innerHTML=nowPlayingTime;
61 }
62
63 function PeriodicRefreshTime() {
64   if (currentState == "play") {
65     nowTime = Date.now()
66     delta = (nowTime - updateTime)/1000
67     currentSeconds = updateSeconds + Math.round(delta)
68     if (trackSeconds && (currentSeconds > trackSeconds)) {
69       currentSeconds = trackSeconds
70     }
71     RefreshTime()
72   }
73 }
74
75 function RefreshPageStatus() {
76
77   var req = new XMLHttpRequest();
78
79   req.onreadystatechange = function () {
80     if (this.readyState != 4 || this.status != 200) return;
81     var returnedData = JSON.parse(this.responseText);
82     trackName = GetFilename(returnedData['current_playing']);
83     try {
84       var trackName=decodeURI(trackName).replace(/%2C/g,",")
85     }  
86     catch(e) {
87       console.log(trackName)
88     }
89     trackNo = returnedData['song'];
90     trackId = returnedData['songid']
91     currentState = returnedData['state'];
92     document.title='MPD Player: '+trackName;
93     if (trackNo) {
94       var nowPlayingTrackNo=String(1+Number(trackNo))
95     } else {
96       var nowPlayingTrackNo="--"
97     }
98     nowPlayingTrack = nowPlayingTrackNo + '/' + returnedData['playlistlength'];
99     nowPlayingName = trackName;
100     playingTime = returnedData['time']
101     if (playingTime) {
102       var splits = playingTime.split(":")
103       updateTime = Date.now()
104       currentSeconds = Number(splits[0])
105       updateSeconds = currentSeconds
106       trackSeconds = Number(splits[1])
107     } else {
108       currentSeconds = null
109     }
110     if (currentState=='stop') {
111       nowPlayingName = '<font color="gray">' + nowPlayingName+ '</font>'
112     }
113     document.getElementById('nowplaying_trackno').innerHTML=nowPlayingTrack;
114     document.getElementById('nowplaying_trackname').innerHTML=nowPlayingName;
115     RefreshTime()
116     if (currentState=="play") {
117       document.getElementById('playpausebutton').innerHTML="<span onclick=\"Command('pause')\"><img class=\"button\" title=\"Pause\" src=\"images/pause.svg\"></span>";
118     } else {
119       document.getElementById('playpausebutton').innerHTML="<span onclick=\"Command('play')\"><img class=\"button\" title=\"Play\" src=\"images/play-button.svg\"></span>";
120     }
121     if (currentState=="stop") {
122       document.getElementById('stopbutton').innerHTML="<span><img class=\"button\" src=\"images/stopoff.svg\"></span>";
123     } else {
124       document.getElementById('stopbutton').innerHTML="<span onclick=\"Command('stop')\"><img class=\"button\" title=\"Stop\" src=\"images/stop.svg\"></span>";
125     }
126     if (returnedData["repeat"]=="1") {
127       document.getElementById('repeatstate').innerHTML="<img class=\"button\" title=\"Replay is on\" src=\"images/replay.svg\"></a>";
128     } else {
129       document.getElementById('repeatstate').innerHTML="<img class=\"button\" title=\"Replay is off\" src=\"images/replayoff.svg\"></a>";
130     }
131     document.getElementById('volume_total').innerHTML="<div id=\"volume_actual\" style=\"width:"+returnedData["volume"]+"%\">";
132
133     var items = document.getElementById('items');
134     var table = items.getElementsByClassName('track');
135     var current_track="track_"+trackId;
136     for (var i = 0; i < table.length; i++) {
137       if (table[i].id==current_track) {
138         table[i].classList.add("itemActive");
139       } else {
140         table[i].classList.remove("itemActive")
141       }
142     }
143         
144   };
145
146   req.open("GET", urlbase+"status", true);
147   req.send();
148
149 }
150
151 function RefreshPlaylist() {
152
153 var req = new XMLHttpRequest();
154
155 req.onreadystatechange = function () {
156   if (this.readyState != 4 || this.status != 200) return;
157   var returnedData = JSON.parse(this.responseText);
158
159   var playlistMenuText = "<table>\
160   <tr>\
161   <td><span onclick=\"EditPlayList()\"><img class=\"medium-button\" title=\"Files\" src=\"images/folder.svg\"></span><td>\
162   <td><span onclick=\"LoadPlayList()\"><img class=\"medium-button\" title=\"Lists\" src=\"images/list.svg\"></span><td>\
163   <td><span onclick=\"SavePlayList()\"><img class=\"medium-button\" title=\"Save current list\" src=\"images/download.svg\"></span><td>\
164   <td><span onclick=\"return confirm('Clear current playlist, are you sure?') ? PlaylistCommand('clear') : false;\" ><img class=\"medium-button\" title=\"Clear current list\"  src=\"images/cancel.svg\"></span><td>\
165   </tr>\
166   </table>";
167
168   var itemsText="<table>\
169   <tr id=\"items_heading\">\
170   <td></td class=\"track_number\"><td class=\"file\">Title</td><td class=\"medium-button\" colspan=\"3\">Controls</td>\
171   </tr>";
172
173   var even = 0;
174   for (var key in returnedData) {
175        var rec=returnedData[key];
176        var name=GetFilename(rec["title"]);
177        try {
178          var name=decodeURI(name).replace(/%2C/g,",")
179        }  
180        catch(e) {
181          console.log(name)
182        }
183        var id=rec["id"];
184
185        if (even) { 
186          evText="itemEven"; 
187        } else { 
188          evText="itemOdd";
189        };
190
191        even = ! even;
192
193        itemsText = itemsText + "<tr id=\"track_"+id+"\" class=\"track "+evText+"\">\
194   <td class=\"track_number\">\
195   <a name=\""+id+"\">"+(Number(key)+1)+"</a></td>\
196   </td>\
197   <td class=\"file\">\
198   <span class=\"link\" onclick=\"PlaylistCommand('playitem',"+id+")\">"+name+"</span>\
199   </td>\
200   <td class=\"move\">\
201   <span onclick=\"PlaylistCommand('moveup',"+id+")\"><img class=\"small-button\" title=\"Move up\" src=\"images/up-arrow.svg\"></span>\
202   </td>\
203   <td class=\"move\">\
204   <span onclick=\"PlaylistCommand('movedown',"+id+")\"><img class=\"small-button\" title=\"Move down\" src=\"images/down-arrow.svg\"></span>\
205   </td>\
206   <td class=\"remove\">\
207   <span onclick=\"PlaylistCommand('remove',"+id+")\"><img class=\"small-button\" title=\"Remove\" src=\"images/cancel.svg\"></span>\
208   </td>\
209   </tr>"; 
210   }
211
212   itemsText = itemsText + "</table>";
213
214   document.getElementById('items').innerHTML=itemsText;
215   document.getElementById('playlist_menu_top').innerHTML=playlistMenuText;
216   document.getElementById('playlist_menu_bottom').innerHTML=playlistMenuText;
217 };
218
219 req.open("GET", urlbase+"playlist", true);
220 req.send();
221
222 }
223
224 function EditPlayList(dir) {
225
226 var req = new XMLHttpRequest();
227
228 req.onreadystatechange = function () {
229   if (this.readyState != 4 || this.status != 200) return;
230   var returnedData = JSON.parse(this.responseText);
231
232   var playlistMenuText = "<table>\
233   <tr>\
234   <td><span onclick=\"RefreshPageContent()\"><img class=\"medium-button\" title=\"Home\" src=\"images/list.svg\"></span><td>\
235   <td><span onclick=\"return confirm('Add all to the list, are you sure?') ? PlaylistEditCommand('add','"+EscapeStr(dir)+"') : false;\" ><img class=\"medium-button\" title=\"Add all\" src=\"images/plus.svg\"></span><td>\
236   </tr>\
237   </table>";
238
239   var itemsText= "<table>\
240   <tr id=\"items_heading\">\
241   <td></td class=\"track_number\"><td class=\"file\">Title</td><td colspan=\"2\">Controls</td>\
242   </tr>";
243
244   var even = 0;
245   if (dir) {
246     var lastSlash=dir.lastIndexOf("/");
247     if (lastSlash>0) {
248       var upperLevel=dir.slice(0,lastSlash);
249     } else {
250       var upperLevel="";
251     }
252     even = ! even;
253     var itemsText = itemsText + "<tr class=\"itemOdd\">\
254   <td class=\"track_number\"></td>\
255   <td class=\"file\"><span class=\"link\" onclick=\"EditPlayList('"+upperLevel+"')\">..</span></td>\
256   <td></td><td></td>";
257   } 
258
259   var i = 0;
260   for (var key in returnedData) {
261        var rec=returnedData[key];
262        var type=rec["type"];
263        var name=rec["name"];
264        var lastSlash=name.lastIndexOf("/");
265        if (lastSlash>0) {
266          var tailName=name.slice(lastSlash+1);
267        } else {
268          var tailName=name
269        };
270        try {
271          var tailName=decodeURI(tailName).replace(/%2C/g,",")
272        }  
273        catch(e) {
274          console.log(tailName)
275        }
276
277        if (type == "directory" || type == "file") {
278          if (even) { 
279            evText="itemEven"; 
280          } else { 
281            evText="itemOdd";
282          };
283
284          i = i + 1;
285          even = ! even;
286
287          itemsText = itemsText + "<tr class=\""+evText+"\">\
288            <td class=\"track_number\">\
289            <a name=\""+i+"\"></a></td>\
290            </td>";
291       
292          if (type == "directory") {
293            itemsText = itemsText + "<td class=\"file\">\
294              <span class=\"link\" onclick=\"EditPlayList('"+EscapeStr(name)+"')\">"+tailName+"</span></td><td>\
295              <span onclick=\"PlaylistEditCommand('add','"+EscapeStr(name)+"')\"><img class=\"small-button\" title=\"Add\" src=\"images/plus.svg\"></span></td>";
296          }; 
297
298          if (type == "file") {
299            itemsText = itemsText + "<td class=\"file\">\
300              <span class=\"link\" onclick=\"PlaylistEditCommand('add','"+EscapeStr(name)+"')\">"+tailName+"</span></td><td>\
301              <span onclick=\"PlaylistEditCommand('add','"+EscapeStr(name)+"')\"><img class=\"small-button\" title=\"Add\" src=\"images/plus.svg\"></span></td>";
302          };
303
304          itemsText = itemsText + "</tr>";
305
306        }       
307        
308   }
309
310   var itemsText = itemsText+"</table>";
311   document.getElementById('items').innerHTML=itemsText;
312   document.getElementById('playlist_menu_top').innerHTML=playlistMenuText;
313   document.getElementById('playlist_menu_bottom').innerHTML=playlistMenuText;
314 };
315
316 if (!dir) { dir = ''; };
317
318 req.open("GET", urlbase+"lists|edit|"+dir, true);
319 req.send();
320
321 }
322
323 function LoadPlayList() {
324
325 var req = new XMLHttpRequest();
326
327 req.onreadystatechange = function () {
328   if (this.readyState != 4 || this.status != 200) return;
329   var returnedData = JSON.parse(this.responseText);
330   playlistMenuText="<table>\
331     <tr>\
332       <td><span onclick=\"RefreshPageContent()\"><img class=\"medium-button\" title=\"Home\" src=\"images/list.svg\"></span><td>\
333       <td><span onclick=\"confirm('Clear current playlist, are you sure?') ? PlaylistCommandRefStatus('clear') : false;\" ><img class=\"medium-button\" title=\"Clear all\" src=\"images/cancel.svg\"></span><td>\
334     </tr>\
335     </table>";
336   itemsText="<table>\
337     <tr id=\"items_heading\">\
338       <td class=\"track_number\"></td><td class=\"file\">Name</td><td>Controls</td>\
339     </tr>";
340
341   var even = 0;
342   for (var key in returnedData) {
343        var name=returnedData[key];
344
345        if (even) { 
346          evText="itemEven"; 
347        } else { 
348          evText="itemOdd";
349        };
350
351        even = ! even;
352
353        itemsText = itemsText + "<tr class=\""+evText+"\">\
354       <td class=\"track_number\"><a name=\"0\"></a></td>\
355       <td class=\"file\"><span class=\"link\" onclick=\"PlaylistEditCommandRefFull('load','"+EscapeStr(name)+"')\">"+name+"</td>\
356       <td class=\"controls\"><span onclick=\"confirm('Delete playlist "+name+", are you sure?') ? DelPlayList('"+EscapeStr(name)+"') : false;\"><img class=\"small-button\" title=\"Delete\" src=\"images/minus.svg\"></span></td>\
357     </tr>";
358   }
359
360   itemsText=itemsText+"</table>";
361   document.getElementById('items').innerHTML=itemsText;
362   document.getElementById('playlist_menu_top').innerHTML=playlistMenuText;
363   document.getElementById('playlist_menu_bottom').innerHTML=playlistMenuText;
364 };
365
366 req.open("GET", urlbase+"lists|load", true);
367 req.send();
368
369 }
370
371 function SavePlayList() {
372
373 var name=window.prompt('List name','');
374
375 var req = new XMLHttpRequest();
376
377 req.onreadystatechange = function () {
378   if (this.readyState != 4 || this.status != 200) return;
379   if (this.responseText != 'OK') {
380     window.alert(this.responseText);
381   }
382 };
383
384 req.open("GET", urlbase+"lists|save|"+name, true);
385 req.send();
386
387 }
388
389 function DelPlayList(item) {
390
391 var req = new XMLHttpRequest();
392
393 req.onreadystatechange = function () {
394   if (this.readyState != 4 || this.status != 200) return;
395   LoadPlayList();
396   RefreshPageStatus();
397 };
398
399 req.open("GET", urlbase+"lists|delete|"+item, true);
400 req.send();
401
402 }
403
404 function RefreshPageContent() {
405   RefreshPlaylist();
406   RefreshPageStatus();
407 }
408
409 function Command(cmd) {
410
411 var req = new XMLHttpRequest();
412
413 req.onreadystatechange = function () {
414   if (this.readyState != 4 || this.status != 200) return;
415   RefreshPageStatus();
416 };
417
418 req.open("GET", urlbase+cmd, true);
419 req.send();
420
421 }
422
423 function PlaylistCommand(cmd,item) {
424
425 var req = new XMLHttpRequest();
426
427 req.onreadystatechange = function () {
428   if (this.readyState != 4 || this.status != 200) return;
429   RefreshPageContent();
430 };
431
432 req.open("GET", urlbase+"cpl|"+cmd+"|"+item, true);
433 req.send();
434
435 }
436
437 function PlaylistCommandRefStatus(cmd,item) {
438
439 var req = new XMLHttpRequest();
440
441 req.onreadystatechange = function () {
442   if (this.readyState != 4 || this.status != 200) return;
443   RefreshPageStatus();
444 };
445
446 req.open("GET", urlbase+"cpl|"+cmd+"|"+item, true);
447 req.send();
448
449 }
450
451 function PlaylistEditCommand(cmd,item) {
452
453 var req = new XMLHttpRequest();
454
455 req.onreadystatechange = function () {
456   if (this.readyState != 4 || this.status != 200) return;
457   RefreshPageStatus();
458 };
459
460 req.open("GET", urlbase+"lists|"+cmd+"|"+item, true);
461 req.send();
462
463 }
464
465 function PlaylistEditCommandRefFull(cmd,item) {
466
467 var req = new XMLHttpRequest();
468
469 req.onreadystatechange = function () {
470   if (this.readyState != 4 || this.status != 200) return;
471   RefreshPageContent();
472 };
473
474 req.open("GET", urlbase+"lists|"+cmd+"|"+item, true);
475 req.send();
476
477 }
478
479 function subscribe_status() {
480   var xhr = new XMLHttpRequest();
481
482   xhr.onreadystatechange = function() {
483     if (this.readyState != 4) return;
484     if (this.status == 200) {
485       RefreshPageStatus()
486       setTimeout(subscribe_status,1000)
487     } else {
488       setTimeout(subscribe_status,15000)
489     }
490   }
491   xhr.open("GET", urlbase+"idle", true);
492   xhr.send();
493 }
494
495 setTimeout(subscribe_status,5000)
496 setInterval(PeriodicRefreshTime, 1000);