Fixed bug with click to jump in playlist
[stereo.git] / DAAPLib / src / util / command / stereo.html
blobe55ec7c84ce67677a9859e8177b122765fdd84db
1 <?xml version="1.0" encoding="utf-8" ?>
2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
3 "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
4 <html xmlns="http://www.w3.org/1999/xhtml"
5 xmlns:svg="http://www.w3.org/2000/svg">
6 <head>
7 <title>Stereo</title>
8 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
9 <script type="text/javascript" charset="utf-8">
10 //<![CDATA[
11 function getPlaybackStatusUpdate() {
12 var request = new XMLHttpRequest();
13 request.onreadystatechange = function() {
14 if (request.readyState == 4) {
15 parseUpdateResponse(request);
18 if (lastUpdate && lastUpdate.revision) {
19 request.open("GET", "/ctrl-int/1/playstatusupdate?revision-number="+lastUpdate.revision, true);
21 else {
22 request.open("GET", "/ctrl-int/1/playstatusupdate", true);
24 //XHR binary charset opt by Marcus Granado 2006 [http://mgran.blogspot.com]
25 request.overrideMimeType('text/plain; charset=x-user-defined');
26 request.send(null);
29 function getPlaylistUpdate() {
30 var request = new XMLHttpRequest();
31 request.onreadystatechange = function() {
32 if (request.readyState == 4) {
33 parsePlaylistResponse(request);
36 request.open("GET", "/ctrl-int/1/playlist", true);
37 //XHR binary charset opt by Marcus Granado 2006 [http://mgran.blogspot.com]
38 request.overrideMimeType('text/plain; charset=x-user-defined');
39 request.send(null);
42 function getAlbumArt(revision) {
43 document.getElementById("albumart").src = "/ctrl-int/1/nowplayingartwork?revision="+revision;
46 function getPlaylists() {
47 var request = new XMLHttpRequest();
48 request.onreadystatechange = function() {
49 if (request.readyState == 4) {
50 var text = request.responseText;
52 var tree = new Node();
53 parseNode(tree, text, 0);
54 tree = tree.children[0];
56 var playlists = new Array();
58 for (node in tree.children) {
59 if (tree.children[node].name = "mlcl") {
60 var list = tree.children[node];
61 for (item in list.children) {
62 playlists.push(new Container(list.children[item]));
67 var parent = document.getElementById("browse-playlists");
68 while (parent.childNodes.length > 0) {
69 parent.removeChild(parent.firstChild);
72 for (p in playlists) {
73 var item = document.createElement("DIV");
74 item.className="item";
76 item.appendChild(createNode("title", playlists[p].name));
77 item.appendChild(createNode("count", playlists[p].items));
79 parent.appendChild(item);
83 request.open("GET", "/databases/1/containers", true);
84 //XHR binary charset opt by Marcus Granado 2006 [http://mgran.blogspot.com]
85 request.overrideMimeType('text/plain; charset=x-user-defined');
86 request.send(null);
89 function getArtists() {
90 var request = new XMLHttpRequest();
91 request.onreadystatechange = function() {
92 if (request.readyState == 4) {
93 var text = request.responseText;
95 var tree = new Node();
96 parseNode(tree, text, 0);
97 tree = tree.children[0];
99 var artists = new Array();
101 for (node in tree.children) {
102 if (tree.children[node].name = "abar") {
103 var list = tree.children[node];
104 for (item in list.children) {
105 artists.push(list.children[item].value);
110 var parent = document.getElementById("browse-artists");
111 while (parent.childNodes.length > 0) {
112 parent.removeChild(parent.firstChild);
115 for (a in artists) {
116 var item = document.createElement("DIV");
117 item.className="item";
119 item.appendChild(createNode("title", artists[a]));
121 parent.appendChild(item);
125 request.open("GET", "/databases/1/browse/artists", true);
126 //XHR binary charset opt by Marcus Granado 2006 [http://mgran.blogspot.com]
127 request.overrideMimeType('text/plain; charset=x-user-defined');
128 request.send(null);
131 function next() {
132 var request = new XMLHttpRequest();
133 request.open("GET", "/ctrl-int/1/nextitem", true);
134 request.send(null);
137 function prev() {
138 var request = new XMLHttpRequest();
139 request.open("GET", "/ctrl-int/1/previtem", true);
140 request.send(null);
143 function play() {
144 var request = new XMLHttpRequest();
145 request.open("GET", "/ctrl-int/1/playpause", true);
146 request.send(null);
149 function pause() {
150 var request = new XMLHttpRequest();
151 request.open("GET", "/ctrl-int/1/pause", true);
152 request.send(null);
155 function jump(index) {
156 var request = new XMLHttpRequest();
157 request.open("GET", "/ctrl-int/1/cue?command=play&index="+index);
158 request.send(null);
161 function Node(name, length) {
162 this.name = name;
163 this.length = length;
164 this.value = "";
165 this.children = new Array();
166 this.print = function (indent) {
167 sysout(indent + this.name + ": " + this.value + " (" + this.length + ")");
168 for (var i = 0; i < this.children.length; i++) {
169 this.children[i].print(indent+" ");
174 function parseUpdateResponse(response) {
175 var text = response.responseText;
177 var name = text.substring(0, 4);
178 var length = parseInt(text, 4);
180 var tree = new Node(name, length);
182 for (var i = 8; i < text.length;) {
183 i += parseNode(tree, text, i);
186 var up = new PlayStatusUpdate(tree);
188 updateStatus(up);
190 if (!lastUpdate || up.revision != lastUpdate.revision) {
191 getPlaylistUpdate();
192 getAlbumArt(up.revision);
193 if ((!lastUpdate && up.status == 4)
194 || (lastUpdate.status == 2 && up.status == 4)) {
195 document.body.className = "playing";
199 lastUpdate = up;
200 getPlaybackStatusUpdate();
203 function parsePlaylistResponse(response) {
204 var text = response.responseText;
206 var name = text.substring(0, 4);
207 var length = parseInt(text, 4);
209 var tree = new Node(name, length);
211 for (var i = 8; i < text.length;) {
212 i += parseNode(tree, text, i);
215 var playlist = new Playlist(tree);
217 updatePlaylist(playlist);
221 function parseNode(parent, text, index, debug) {
222 var name = text.substring(index, index + 4);
223 var length = parseInt(text, index + 4);
224 var node = new Node(name, length);
226 if (debug) sysout(name + " " + length);
228 if (name == "mlit" && parent.name == "abar") {
229 //this is a hack caused by apple reusing mlit (list)
230 //as a string container in abar records
231 node.value = parseString(text, index+8, length);
233 else {
234 switch (types[name]) {
235 case byteVal:
236 node.value = parseByte(text, index + 8);
237 break;
238 case shortVal:
239 node.value = parseShort(text, index + 8);
240 break;
241 case intVal:
242 node.value = parseInt(text, index + 8);
243 break;
244 case longVal:
245 node.value = parseLong(text, index + 8);
246 break;
247 case longlongVal:
248 node.value = parseLongLong(text, index + 8);
249 break;
250 case stringVal:
251 node.value = parseString(text, index + 8, length);
252 break;
253 case dateVal:
254 node.value = parseDate(text, index + 8);
255 break;
256 case version:
257 node.value = parseVersion(text, index + 8);
258 break;
259 case list:
260 for ( var i = 0; i < length;) {
261 i += parseNode(node, text, 8 + index + i);
263 break;
267 parent.children.push(node);
268 return 8 + length;
271 function parseByte(text, index) {
272 return text.charCodeAt(index);
275 function parseShort(text, index) {
276 var i = text.charCodeAt(index) & 255;
277 i <<= 8;
278 i += text.charCodeAt(index + 1) & 255;
279 return i;
282 function parseInt(text, index) {
283 var i = text.charCodeAt(index) & 255;
284 i <<= 8;
285 i += text.charCodeAt(index + 1) & 255;
286 i <<= 8;
287 i += text.charCodeAt(index + 2) & 255;
288 i <<= 8;
289 i += text.charCodeAt(index + 3) & 255;
290 return i;
293 function parseLong(text, index) {
294 var i = text.charCodeAt(index) & 255;
295 i <<= 8;
296 i += text.charCodeAt(index + 1) & 255;
297 i <<= 8;
298 i += text.charCodeAt(index + 2) & 255;
299 i <<= 8;
300 i += text.charCodeAt(index + 3) & 255;
301 i <<= 8;
302 i += text.charCodeAt(index + 4) & 255;
303 i <<= 8;
304 i += text.charCodeAt(index + 5) & 255;
305 i <<= 8;
306 i += text.charCodeAt(index + 6) & 255;
307 i <<= 8;
308 i += text.charCodeAt(index + 7) & 255;
309 return i;
312 function parseLongLong(text, index) {
313 var i = new Array();
314 i.push(parseInt(text, index));
315 i.push(parseInt(text, index + 4));
316 i.push(parseInt(text, index + 8));
317 i.push(parseInt(text, index + 12));
318 return i;
321 function parseString(text, index, length) {
322 //needs to decode utf-8. This is slow :-(
324 var string = "";
326 for ( var i = 0; i < length;) {
327 var c = text.charCodeAt(index + i) & 255;
328 if (c < 128) {
329 string += String.fromCharCode(c);
330 i++;
331 } else if (c > 191 && c < 224) {
332 var c2 = text.charCodeAt(index + i + 1) & 255;
333 string += String.fromCharCode(((c & 31)) << 6 | (c2 & 63));
334 i += 2;
335 } else {
336 var c2 = text.charCodeAt(index + i + 1) & 255;
337 var c3 = text.charCodeAt(index + i + 2) & 255;
338 string += String.fromCharCode(((c & 15)) << 12 | (c2 & 63) << 6
339 | (c3 & 63));
340 i += 3;
344 return string;
347 function parseDate(text, index) {
348 return parseInt(text, index);
351 function parseVersion(text, index) {
352 var i = text.charCodeAt(index) & 255;
353 i += ".";
354 i += text.charCodeAt(index + 1) & 255;
355 i += ".";
356 i += text.charCodeAt(index + 2) & 255;
357 i += ".";
358 i += text.charCodeAt(index + 3) & 255;
359 return i;
362 function sysout(text) {
363 var p = document.createElement("PRE");
364 p.className = "debug";
365 p.appendChild(document.createTextNode(text));
366 document.body.appendChild(p);
369 var lastUpdate = 0;
371 function PlayStatusUpdate(tree) {
373 this.album = "";
374 this.title = "";
375 this.artist = "";
376 this.genre = "";
377 this.id = 0;
378 this.status = 0;
379 this.revision = 0;
381 for (child in tree.children) {
382 var node = tree.children[child];
383 switch (node.name) {
384 case "caps":
385 this.status = node.value;
386 break;
387 case "cmsr":
388 this.revision = node.value;
389 break;
390 case "cann":
391 this.title = node.value;
392 break;
393 case "cana":
394 this.artist = node.value;
395 break;
396 case "canl":
397 this.album = node.value;
398 break;
399 case "cang":
400 this.genre = node.value;
401 break;
402 case "canp":
403 this.id = node.value[3];
404 break;
409 function Container(tree) {
410 this.id = 0;
411 this.persistantId = 0;
412 this.name = "";
413 this.base = false;
414 this.parent = 0;
415 this.items = 0;
417 for (child in tree.children) {
418 var node = tree.children[child];
419 switch (node.name) {
420 case "miid":
421 this.id = node.value;
422 break;
423 case "mper":
424 this.persistantId = node.value;
425 break;
426 case "minm":
427 this.name = node.value;
428 break;
429 case "abpl":
430 this.base = node.value;
431 break;
432 case "mpco":
433 this.parent = node.value;
434 break;
435 case "mimc":
436 this.items = node.value;
437 break;
442 function Playlist(tree) {
444 this.tracks = new Array();
446 var list = 0;
448 for (child in tree.children) {
449 var node = tree.children[child];
450 switch (node.name) {
451 case "mlcl":
452 list = node;
453 break;
457 if (!list)
458 return;
460 for (child in list.children) {
461 var node = list.children[child];
462 switch (node.name) {
463 case "mlit":
464 this.tracks.push(new Track(node));
465 break;
470 function Track(tree) {
472 this.album = "";
473 this.title = "";
474 this.artist = "";
475 this.genre = "";
476 this.id = 0;
478 for (child in tree.children) {
479 var node = tree.children[child];
480 switch (node.name) {
481 case "asal":
482 this.album = node.value;
483 break;
484 case "minm":
485 this.title = node.value;
486 break;
487 case "asar":
488 this.artist = node.value;
489 break;
490 case "asag":
491 this.genre = node.value;
492 break;
493 case "miid":
494 this.id = node.value;
495 break;
500 function updateStatus(update) {
502 if (update.status == 2) {
503 document.getElementById("state").className = "stopped";
505 document.getElementById("play").className = "";
506 document.getElementById("pause").className = "hidden";
507 } else if (update.status == 3) {
508 document.getElementById("state").className = "paused";
509 setText(document.getElementById("title"), update.title);
510 setText(document.getElementById("artist"), update.artist);
511 setText(document.getElementById("album"), update.album);
512 setText(document.getElementById("genre"), update.genre);
514 document.getElementById("play").className = "";
515 document.getElementById("pause").className = "hidden";
516 } else if (update.status == 4) {
517 document.getElementById("state").className = "playing";
518 setText(document.getElementById("title"), update.title);
519 setText(document.getElementById("artist"), update.artist);
520 setText(document.getElementById("album"), update.album);
521 setText(document.getElementById("genre"), update.genre);
523 document.getElementById("play").className = "hidden";
524 document.getElementById("pause").className = "";
528 function setText(node, text) {
529 while (node.childNodes.length > 0) {
530 node.removeChild(node.firstChild);
532 node.appendChild(document.createTextNode(text));
535 function updatePlaylist(playlist) {
537 var list = document.getElementById("playlist");
538 list.innerHTML = "";
540 var current = lastUpdate.id;
542 for (i in playlist.tracks) {
543 var track = playlist.tracks[i];
545 var node = document.createElement("DIV");
546 node.className = "item";
548 node.appendChild(createNode("album", track.album));
549 node.appendChild(createNode("title", track.title));
550 node.appendChild(createNode("artist", track.artist));
551 node.appendChild(createNode("genre", track.genre));
552 node.appendChild(createNode("id", track.id));
554 if (current && track.id == current) {
555 node.className += " current";
556 current = 0;
558 while (list.childNodes.length > 2) {
559 list.removeChild(list.childNodes[0]);
563 list.appendChild(node);
567 function createNode(type, value) {
568 var node = document.createElement("DIV");
569 node.className = type;
570 node.appendChild(document.createTextNode(value));
571 return node;
574 function registerControls() {
576 document.getElementById("back").onclick = function() {
577 document.body.className = "main";
579 document.getElementById("playing").onclick = function() {
580 document.body.className = "playing";
583 document.body.className = "main";
585 document.getElementById("previous").onclick = function() {
586 prev();
588 document.getElementById("play").onclick = function() {
589 play();
591 document.getElementById("pause").onclick = function() {
592 pause();
594 document.getElementById("next").onclick = function() {
595 next();
598 document.getElementById("playlist").onclick = function(e) {
599 var node = e.target;
600 while (!node.className.match("item")) {
601 node = node.parentNode;
602 if (node == document.body)
603 return;
605 var playlist = document.getElementById("playlist");
606 var offset = -1;
607 for (child in playlist.childNodes) {
608 if (playlist.childNodes[child].className
609 && playlist.childNodes[child].className
610 .match("current")) {
611 offset = child;
614 if (offset == -1)
615 return;
616 for (child in playlist.childNodes) {
617 if (playlist.childNodes[child] == node) {
618 jump(child - offset);
619 break;
624 replacePrev();
625 replacePause();
626 replacePlay();
627 replaceNext();
629 document.getElementById("browse-menu-playlists").onclick = function() {
630 getPlaylists();
631 document.getElementById("browse").className = "playlists";
633 document.getElementById("browse-menu-artists").onclick = function() {
634 getArtists();
635 document.getElementById("browse").className = "artists";
637 document.getElementById("browse-menu-albums").onclick = function() {
638 getAlbums();
639 document.getElementById("browse").className = "albums";
641 document.getElementById("browse-menu-search").onclick = function() {
642 getSearch();
643 document.getElementById("browse").className = "search";
645 getPlaylists();
646 document.getElementById("browse").className = "playlists";
649 function replacePrev() {
650 var prev = document.getElementById("previous");
651 prev.removeChild(prev.firstChild);
653 var ns = 'http://www.w3.org/2000/svg';
654 var svg = document.createElementNS(ns, 'svg');
655 svg.setAttribute('viewPort', '0, 0, 33, 30');
656 svg.setAttribute('width', '33');
657 svg.setAttribute('height', '30');
659 var bar = document.createElementNS(ns, 'path');
660 bar.setAttribute('d', 'm 0,3 4,0 0,24 -4,0 z');
661 bar.setAttribute('style', 'fill: #222;');
662 svg.appendChild(bar);
664 var la = document.createElementNS(ns, 'path');
665 la.setAttribute('d', 'm 7,15 10,11 0,-22 z');
667 .setAttribute('style',
668 'fill: #222;stroke: #222; stroke-width:0.2em;stroke-linejoin:round;');
669 svg.appendChild(la);
671 var ra = document.createElementNS(ns, 'path');
672 ra.setAttribute('d', 'm 20,15 10,11 0,-22 z');
674 .setAttribute('style',
675 'fill: #222;stroke: #222; stroke-width:0.2em;stroke-linejoin:round;');
676 svg.appendChild(ra);
678 prev.appendChild(svg);
681 function replacePlay() {
682 var play = document.getElementById("play");
683 play.removeChild(play.firstChild);
685 var ns = 'http://www.w3.org/2000/svg';
686 var svg = document.createElementNS(ns, 'svg');
687 svg.setAttribute('viewPort', '0, 0, 30, 30');
688 svg.setAttribute('width', '30');
689 svg.setAttribute('height', '30');
691 var p = document.createElementNS(ns, 'path');
692 p.setAttribute('d', 'm 5,3 22,12 -22,12 z');
694 .setAttribute('style',
695 'fill: #222;stroke: #222; stroke-width:0.2em;stroke-linejoin:round;');
696 svg.appendChild(p);
698 play.appendChild(svg);
701 function replacePause() {
702 var pause = document.getElementById("pause");
703 pause.removeChild(pause.firstChild);
705 var ns = 'http://www.w3.org/2000/svg';
706 var svg = document.createElementNS(ns, 'svg');
707 svg.setAttribute('viewPort', '0, 0, 17, 30');
708 svg.setAttribute('width', '30');
709 svg.setAttribute('height', '30');
711 var l = document.createElementNS(ns, 'path');
712 l.setAttribute('d', 'm 0,2 6,0 0,26 -6,0 z');
713 l.setAttribute('style', 'fill: #222;');
714 svg.appendChild(l);
716 var r = document.createElementNS(ns, 'path');
717 r.setAttribute('d', 'm 11,2 6,0 0,26 -6,0 z');
718 r.setAttribute('style', 'fill: #222;');
719 svg.appendChild(r);
721 pause.appendChild(svg);
724 function replaceNext() {
725 var next = document.getElementById("next");
726 next.removeChild(next.firstChild);
728 var ns = 'http://www.w3.org/2000/svg';
729 var svg = document.createElementNS(ns, 'svg');
730 svg.setAttribute('viewPort', '0, 0, 33, 30');
731 svg.setAttribute('width', '33');
732 svg.setAttribute('height', '30');
734 var la = document.createElementNS(ns, 'path');
735 la.setAttribute('d', 'm 1,4 10,11 -10,11 z');
737 .setAttribute('style',
738 'fill: #222;stroke: #222; stroke-width:0.2em;stroke-linejoin:round;');
739 svg.appendChild(la);
741 var ra = document.createElementNS(ns, 'path');
742 ra.setAttribute('d', 'm 15,4 10,11 -10,11 z');
744 .setAttribute('style',
745 'fill: #222;stroke: #222; stroke-width:0.2em;stroke-linejoin:round;');
746 svg.appendChild(ra);
748 var bar = document.createElementNS(ns, 'path');
749 bar.setAttribute('d', 'm 27,3 4,0 0,24 -4,0 z');
750 bar.setAttribute('style', 'fill: #222;');
751 svg.appendChild(bar);
753 next.appendChild(svg);
755 //]]>
756 </script>
758 <script type="text/javascript" charset="utf-8">
759 //<![CDATA[
760 var byteVal = 1;
761 var shortVal = 3;
762 var intVal = 5;
763 var longVal = 7;
764 var longlongVal = 8;
765 var stringVal = 9;
766 var dateVal = 10; //4-byte int
767 var version = 11; //4 single bytes or two ints
768 var list = 12;
769 var composite = 13;
770 var types = new Array();
771 types["cmst"] = list;
772 types["mstt"] = intVal;
773 types["cmsr"] = intVal;
774 types["caps"] = byteVal;
775 types["cash"] = byteVal;
776 types["carp"] = byteVal;
777 types["caas"] = intVal;
778 types["caar"] = intVal;
779 types["canp"] = longlongVal;
780 types["cann"] = stringVal;
781 types["cana"] = stringVal;
782 types["canl"] = stringVal;
783 types["cang"] = stringVal;
784 types["asai"] = longVal;
785 types["cmmk"] = intVal;
786 types["cant"] = intVal;
787 types["cast"] = intVal;
789 types["muty"] = intVal;
790 types["mtco"] = intVal;
791 types["mrco"] = intVal;
792 types["mlcl"] = list;
793 types["mlit"] = list;
794 types["minm"] = stringVal;
795 types["asgn"] = stringVal;
796 types["asal"] = stringVal;
797 types["asar"] = stringVal;
798 types["mikd"] = byteVal;
799 types["assp"] = intVal;
800 types["assr"] = intVal;
801 types["asst"] = intVal;
802 types["astm"] = intVal;
803 types["asai"] = longVal;
804 types["miid"] = intVal;
806 types["aply"] = list;
807 types["abpl"] = byteVal;
808 types["mpco"] = intVal;
809 types["mimc"] = intVal;
810 types["mper"] = longVal;
812 types["abro"] = list;
813 types["abar"] = list;
814 //]]>
815 </script>
817 <style type="text/css">
818 html, body {
819 margin: 0;
821 body {
822 font-family: "Geneva", sans-serif;
824 #navigation {
825 position: fixed;
826 top: 0;
827 height: 4ex;
828 left: 0;
829 right: 0;
830 background-color: #222;
831 color: white;
833 #navigation div {
834 font-size: 80%;
835 padding: 1ex 1em;
837 .playing #navigation {
838 background-color: #222;
839 color: white;
841 .main #navigation {
842 background-color: #ccc;
843 color: black;
845 .playing #playing {
846 display: none;
848 .main #back {
849 display: none;
851 #back {
852 float: left;
854 #playing {
855 float: right;
857 .content {
858 margin-top: 4ex;
859 margin-bottom: 8ex;
861 #playlist {
862 margin-top: 20ex;
864 #state {
865 position: fixed;
866 top: 4ex;
867 height: 12ex;
868 left: 0;
869 right: 0;
870 padding: 2ex 1em;
871 padding-bottom: 2ex;
872 border-bottom: solid 2px #ccc;
873 background: white;
874 overflow: auto;
876 #state div {
877 font-size: 120%;
879 #state .artist {
880 color: black;
882 #state .album,#state .genre {
883 font-size: 80%;
885 .item {
886 border-bottom: solid 1px #ccc;
887 padding: 1ex 1em;
888 display: block;
890 .album,.artist,.title {
891 display: block;
893 .album, .artist, .genre {
894 color: #888;
895 font-size: 0.7em;
897 .title {
898 font-weight: bold;
900 .id {
901 display: none;
903 .current {
904 background-color: #eee;
906 #albumart {
907 height: 10ex;
908 float: left;
909 margin-right: 1em;
911 #control {
912 position: fixed;
913 bottom: 0;
914 height: 4ex;
915 padding: 2ex 0;
916 width: 100%;
917 text-align: center;
918 opacity: 0.8;
919 background-color: #ccc;
921 #control div {
922 display: inline;
924 #control .hidden {
925 display: none;
927 #control #volume {
928 display: none;
930 #play,#pause {
931 padding: 2ex 3em;
933 .main #current {
934 display: none;
936 .playing #browse {
937 display: none;
939 #browse-menu {
940 position: fixed;
941 left: 0;
942 right: 0;
943 bottom: 0;
944 height: 4ex;
945 padding: 2ex 0;
946 background-color: #222;
947 color: white;
948 text-align: center;
950 #browse-menu div {
951 display: inline;
953 .debug {
954 display: block;
956 #browse .content {
957 display: none;
959 .playlists #browse-playlists {
960 display: block;
962 .artists #browse-artists {
963 display: block;
966 </style>
968 </head>
969 <body onload="registerControls(); getPlaybackStatusUpdate();">
970 <div id="navigation">
971 <div id="back">Back</div>
972 <div id="playing">Now Playing</div>
973 </div>
974 <div id="current">
975 <div id="state">
976 <img id="albumart" src="" alt="" />
977 <div id="title" class="title"></div>
978 <div id="album" class="album"></div>
979 <div id="artist" class="artist"></div>
980 <div id="genre" class="genre"></div>
981 </div>
982 <div id="control">
983 <div id="previous">previous</div>
984 <div id="play">play</div>
985 <div id="pause">pause</div>
986 <div id="next">next</div>
987 <div id="volume">volume</div>
988 </div>
989 <div id="playlist" class="content">
990 </div>
991 </div>
992 <div id="browse">
993 <div id="browse-menu">
994 <div id="browse-menu-playlists">Playlists</div>
995 <div id="browse-menu-artists">Artists</div>
996 <div id="browse-menu-albums">Albums</div>
997 <div id="browse-menu-search">Search</div>
998 </div>
999 <div id="browse-playlists" class="content"></div>
1000 <div id="browse-artists" class="content"></div>
1001 <div id="browse-albums" class="content"></div>
1002 <div id="browse-search" class="content"></div>
1003 </div>
1004 </body>
1005 </html>