2 Pitchfork Music Player Daemon Client
3 Copyright (C) 2007 Roger Bystrøm
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; version 2 of the License.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License along
15 with this program; if not, write to the Free Software Foundation, Inc.,
16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 var position_txt_node = null;
20 var bitrate_txt_node = null;
21 var ALBUMART_URL = "metadata.php?pic=";
22 var OUTPUT_ID = "output_";
24 var DIR_SEPARATOR = "/";
26 var STATUS_DEFAULT_TIMEOUT = 10;
28 /* todo: put these in an object, no reason for them to be lying around here */
29 var last_update_cmd_id;
30 var problem_update_delay = 5;
35 var playing = new Object();
39 playing.pl_version = 0;
46 playing.image = ""; //
48 playing.random = 0; // random on/off
49 playing.repeat = 0; // repeat on/off
50 playing.xfade = 0; // crossfade seconds
51 playing.update = false; // dbupdate
52 playing.show_node = null; // node that is supposed to have playing status
53 playing.is_stream = false; // true if the playing song is a stream
54 playing.error = false;
55 playing.error_time = 0;
59 playing.TIME_ELAPSED = 0;
60 playing.TIME_REMAINING = 1;
62 playing.time_dtype = playing.TIME_ELAPSED;
64 /* various elements found around the document */
65 playing.disp_info = null;
66 playing.pp_button = null;
67 playing.disp_artist = null;
68 playing.disp_title = null;
69 playing.disp_album = null;
70 playing.disp_year = null;
71 playing.albumart = null;
73 var last_pl_selected = true;
75 var output_toggle_id = null;
77 var pl_overlay_id = -1;
78 var pl_overlay_write = null;
80 var status_bar = null;
82 var send_command_rm_status = false;
83 var pl_entry_clone = null;
85 var settings_time = 10;
87 var need_info_arr = null;
89 var init_failed = false;
91 var playlist_add_popup = null;
92 var playlist_save_popup = null;
94 var pagination = new Object();
95 pagination.max = 0; // max pr. page
96 pagination.page = 0; // what page we currently are on
97 pagination.pages = 0; // number of pages being advertised
98 pagination.need_update = false; // indicates wheter we need an update
99 pagination.list = null; // reference to the displaying area
100 pagination.container = null; // reference to the container
102 function init_player() {
104 status_bar = new StatusBar();
109 update_delay = update_delay * 1000;
111 else window.update_delay = 1000;
113 possliderid = setup_slider(document.getElementById('posslider'), position_adjust, LANG.POSITION);
114 set_slider_pos(possliderid, 0);
116 volsliderid = setup_slider(document.getElementById('volslider'), volume_adjust, LANG.VOLUME);
117 set_slider_pos(volsliderid, 0);
119 playlist_element = document.getElementById('playlist')
121 var pltmp_id = setup_node_move(playlist_element, playlist_move, playlist_get_move_txt);
122 add_move_doubleclick(pltmp_id, playlist_dblclick);
123 set_selection_change_handler(pltmp_id, pl_selection_changed);
125 pl_overlay_id = setup_overlay(playlist_element, new Array(10, 10, 300, 300 ), pl_overlay_open_cb, pl_overlay_close_cb);
126 pl_overlay_write = get_overlay_write_area(pl_overlay_id);
128 playing.disp_info = document.getElementById('disp_info');
129 playing.disp_artist = document.getElementById('disp_artist');
130 playing.disp_title = document.getElementById('disp_title');
131 playing.disp_album = document.getElementById('disp_album');
132 playing.disp_year = document.getElementById('disp_year');
133 playing.albumart = document.getElementById("albumart");
134 playing.pp_button = document.getElementById("pp_button");
136 pagination.list = document.getElementById('pagination_list');
137 pagination.container = document.getElementById('pagination');
138 pagination.max = pagination_max; // nice :S
140 var tmp = setting_get("time_dtype");
143 setting_set("time_dtype", playing.time_dtype);
146 if(tmp==playing.TIME_ELAPSED)
147 playing.time_dtype = playing.TIME_ELAPSED;
148 else if(tmp==playing.TIME_REMAINING)
149 playing.time_dtype = playing.TIME_REMAINING;
160 if(typeof(window.metadata_init)=='function')
162 if(typeof(window.recommend_init)=='function')
168 debug(LANG.E_INIT +": " + e.message, true);
172 /* arg-list: command to send, command to call when result is return, show status message when working,
173 don't request status update with this sendcommand,
174 post data if we should use that, if it should form-urlencoded content type should be set */
175 function send_command(command, result_callback, status_msg, nostatus, do_post, form_urlencoded) {
179 var http = new XMLHttpRequest();
180 var url = "command.php?" + command;
183 url+="&plchanges=" + playing.pl_version;
184 if(pagination.max>0) {
185 url+="&pmax=" + pagination.max + "&page=" + pagination.page;
186 //debug("pmax: " + pagination.max + ", page: " + pagination.page);
190 if(send_command_rm_status) {
192 send_command_rm_status = false;
195 http.onreadystatechange = function() {
196 if(http.readyState==4) {
198 if(http.responseText&&(resp = evaluate_json(http.responseText))) {
199 if(resp['connection']&&resp['connection']=="failed") {
200 last_update = get_time();
201 show_status_bar(LANG.E_CONNECT);
202 send_command_rm_status = true;
204 result_callback("failed");
211 show_status_bar(LANG.E_INVALID_RESPONSE);
212 send_command_rm_status = true;
213 last_update = get_time();
215 result_callback("failed");
221 if(http.status==200) {
222 var res = resp['result'];
223 var stat = resp["status"];
224 var plchanges = resp["plchanges"];
225 var has_plchanges = plchanges && stat && playing.pl_version != stat.playlist;
227 if(res&&result_callback) {
228 result_callback(res);
232 current_status_handler(stat, has_plchanges);
233 last_update = get_time();
237 playing.pl_version = stat.playlist;
238 plchanges_handler3(plchanges, stat.playlistlength);
239 /* the currently playing song might have changed if it's a stream */
240 if(playing.is_stream) {
245 /* don't remove if there's no message or a timer */
246 if(status_msg&&!status_bar.timer)
250 if(result_callback) {
251 result_callback("server operation failed");
253 show_status_bar(LANG.NO_RESPONSE); // maybe add a 10 second delay here and reconnect!
254 send_command_rm_status = true;
260 show_status_bar(status_msg, true);
265 http.open("POST", url);
266 if(form_urlencoded) {
267 http.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
272 http.open("GET", url);
277 function update_callback(res) {
278 var time = update_delay;
280 time = problem_update_delay*update_delay;
281 last_update_cmd_id = setTimeout(need_update, time);
284 function reschedule_update_now() {
285 clearTimeout(last_update_cmd_id);
286 // make sure it get's run immidiately
287 last_update_cmd_id = setTimeout(need_update, 0, -update_delay);
290 function need_update(delay) {
293 var now = get_time();
294 if(now>last_update+update_delay+delay-20) { // giving it a little slack
295 send_command("ping", update_callback);
298 delay = last_update+update_delay-now;
302 last_update_cmd_id = setTimeout(need_update, delay);
307 function handle_fatal_error(txt) {
308 show_status_bar(txt);
309 clearTimeout(last_update_cmd_id);
312 /* current status handler, info is stats
313 * if has_plchanges it is assumed the data also carries plchanges
314 * which will handle resizing of the playlist */
315 function current_status_handler(info, has_plchanges) {
317 // something is wrong
318 set_slider_pos(possliderid, 0);
322 var tmp = info.playlistlength;
323 if((tmp = parseInt(tmp))>=0) {
324 if(playing.pl_size!=tmp) {
325 playing.pl_size = tmp;
326 if(pagination.max>0) {
327 // make sure size fits inside what we have as number of pages
328 var s = pagination.max * (pagination.pages);
329 if(tmp < s-pagination.max || tmp>=s) {
330 pagination_update_list(tmp);
333 playlist_resize(tmp);
336 else if(!has_plchanges) { // if it doesn't carry plchanges we have to do it here
337 playlist_resize(tmp);
342 tmp = info.updating_db;
345 if(playing.update!=tmp) {
346 playing.update = tmp;
347 show_status_bar(LANG.WAIT_UPDATING_DB, true);
350 else if(playing.update) {
351 playing.update = false;
355 var state = info["state"];
356 var volume = info[ "volume"];
357 if(volume!=null&&volume!=playing.volume) {
358 set_slider_pos(volsliderid, volume);
359 playing.volume = volume;
362 playing.repeat = info.repeat;
363 playing.random = info.random;
364 playing.xfade = info.xfade;
368 var pos = info["time"].split(":");
369 set_slider_pos(possliderid, (pos[0]*100)/pos[1]);
370 playing.length = pos[1];
372 /* complex, but seems to be somewhat better in firefox */
373 var tmp = info.bitrate;
374 if(tmp&&playing.bitrate!=tmp) {
375 var disp_info = playing.disp_info
376 if(bitrate_txt_node==null) {
377 bitrate_txt_node = document.createTextNode(LANG.BITRATE);
378 disp_info.appendChild(bitrate_txt_node);
379 if(disp_info.normalize)
380 disp_info.normalize();
381 bitrate_txt_node = disp_info.firstChild.splitText(LANG.BITRATE.length);
383 var rep = document.createTextNode(tmp);
384 disp_info.replaceChild(rep, bitrate_txt_node);
385 bitrate_txt_node = rep;
386 playing.bitrate = tmp;
389 var postxt = get_slider_txt(possliderid);
391 if(position_txt_node==null) {
392 position_txt_node = postxt.firstChild.splitText(LANG.POSITION.length);
394 var rep = create_txt(format_playing_time(pos[0], pos[1]));
395 postxt.replaceChild(rep, position_txt_node);
396 position_txt_node = rep;
399 else if(playing.state!="stop") { // state == stop and last state wasn't stop
400 set_slider_pos(possliderid, 0);
401 var disp_info = playing.disp_info;
402 var rep = document.createTextNode("0");
403 var postxt = get_slider_txt(possliderid);
404 if(bitrate_txt_node&&bitrate_txt_node!=null) {
405 disp_info.replaceChild(rep, bitrate_txt_node);
406 bitrate_txt_node = rep;
409 rep = document.createTextNode("");
410 if(position_txt_node&&position_txt_node!=null) {
411 postxt.replaceChild(rep, position_txt_node);
412 position_txt_node = rep;
415 if(state!=playing.state) {
416 playing.state = state;
417 var bt = playing.pp_button;
419 bt.src = IMAGE.BUTTON_PAUSE;
420 if(typeof(window.streaming_try_autoplay)=='function')
421 streaming_try_autoplay();
424 bt.src = IMAGE.BUTTON_PLAY;
425 if(typeof(window.streaming_try_autostop)=='function')
426 streaming_try_autostop();
430 if(typeof info.error != 'undefined' || playing.error) {
431 if(playing.error && get_time() - 10000 > playing.error_time) {
432 playing.error = false;
436 else if(typeof info.error != 'undefined' && playing.error != info.error) {
437 playing.error = info.error;
438 playing.error_time = get_time();
439 show_status_bar("MPD error: " + playing.error);
443 var c_play = info.songid;
444 if(typeof(c_play)=='undefined') {
448 select_playing_song();
450 else if(c_play!=playing.id) {
452 playing.pos = parseInt(info.song);
453 select_playing_song();
454 if(pagination_is_following()) {
455 playlist_scroll_to_playing();
457 /* this is last because it needs the id */
460 else if(playing.pos!=info.song) {
461 playing.pos = parseInt(info.song);
465 function format_playing_time(pos, total) {
466 if(playing.time_dtype==playing.TIME_REMAINING) {
467 return convert_minsec(pos-total) + "/" + convert_minsec(total);
469 else { // dtype == playing.TIME_ELAPSED || something wrong
470 return convert_minsec(pos) + "/" + convert_minsec(total);
474 function request_song_info() {
477 remove_children(playing.disp_artist);
478 remove_children(playing.disp_title);
479 remove_children(playing.disp_album);
480 if(playing.disp_year)
481 remove_children(playing.disp_year);
483 remove_children(playing.albumart);
491 playing.is_stream = false;
496 send_command("currentsong", update_current_song, false, true);
500 function update_current_song(info) {
501 var artist = info[ "Artist"];
502 var title = info["Title"];
503 var album = info[ "Album"];
504 var year = (info["Year"] == null) ? info["Date"] : info["Year"];
505 var a = playing.disp_artist;
506 var t = playing.disp_title;
507 var alb = playing.disp_album;
508 var y = playing.disp_year;
509 var new_thumb = false;
511 if(typeof(title)=='undefined')
513 if(typeof(album)=='undefined')
515 if(typeof(artist)=='undefined')
517 if(typeof(year)=='undefined')
522 if(artist!=playing.artist) {
523 playing.artist = artist;
526 a.appendChild(create_txt(artist));
528 if(playing.album != album) {
529 playing.album = album;
531 remove_children(alb);
532 alb.appendChild(create_txt(album));
534 if((playing.year != year) && y) {
538 y.appendChild(create_txt(year));
541 if(typeof(info['file'])!='undefined') {
542 var f = info['file'];
543 if(f&&f.indexOf("http://")==0)
544 playing.is_stream = true;
545 else playing.is_stream = false;
549 playing.title = title;
551 if(title==null||title=="") {
552 title = info["file"];
554 title = title.substring(title.lastIndexOf(DIR_SEPARATOR)+1);
556 t.appendChild(create_txt(title));
558 set_playing_title(artist, title);
560 if(new_thumb&&typeof(window.request_thumbnail) == 'function') {
561 setTimeout(request_thumbnail, 1);
565 function set_playing_title(artist, title) {
566 if(typeof(artist)=='undefined'||artist==null)
568 if(typeof(title)=='undefined'||title==null)
571 var wt = "Pitchfork MPD Client";
572 if(artist.length||title.length) {
580 function volume_adjust(vol) {
581 send_command("volume=" + parseInt(vol));
584 function position_adjust(pos) {
585 send_command("position=" + parseInt((pos* parseInt(playing.length))/100) + "&id=" + playing.id);
588 function convert_minsec(sec) {
589 var min = parseInt(sec/60);
590 var s = Math.abs(sec%60);
591 return (sec<0&&min==0?"-" + min:min) + ":" + (s<10?"0" + s:s);
594 function buttons_init() {
597 var elem = document.getElementById('pp_button');
598 elem.src = IMAGE.BUTTON_PLAY;
599 add_listener(elem, "click", send_play_pause);
600 if(window.stop_button) {
601 elem = document.getElementById('stop_button');
602 elem.style.display = "";
603 elem.src = IMAGE.BUTTON_STOP;
604 add_listener(elem, "click", send_stop_cmd);
605 elem.parentNode.style.marginLeft = "-15px";
608 elem = document.getElementById("next_button");
609 elem.src = IMAGE.BUTTON_NEXT;
610 add_listener(elem, "click", send_next_song);
611 elem = document.getElementById("previous_button");
612 elem.src = IMAGE.BUTTON_PREVIOUS;
613 add_listener(elem, "click", send_previous_song);
615 /* left menu buttons */
616 elem = document.getElementById("open_directory_button");
617 elem.src = IMAGE.MENU_ITEM_DIRECTORY;
618 add_listener(elem, "click", open_pl_overlay);
619 elem = document.getElementById("crop_items_button");
620 elem.src = IMAGE.MENU_ITEM_CROP;
621 add_listener(elem, "click", remove_songs_event);
622 elem = document.getElementById("remove_items_button");
623 elem.src = IMAGE.MENU_ITEM_REMOVE;
624 add_listener(elem, "click", remove_songs_event);
626 /* server settings */
627 elem = document.getElementById("settings_header");
628 add_listener(elem, "click", open_close_settings);
629 add_listener(elem, "mousedown", stop_event);
632 elem = document.getElementById("playlist_add");
633 add_listener(elem, "click", playlist_add_button);
634 elem = document.getElementById("playlist_save");
635 add_listener(elem, "click", playlist_save_button);
638 elem = document.getElementById("status_bar_img");
639 elem.src = IMAGE.WORKING;
641 /* streaming if applicable */
642 elem = document.getElementById("streaming_open");
644 add_listener(elem, "click", streaming_open);
648 elem = get_slider_txt(possliderid);
650 add_listener(elem, "click", change_pos_dtype);
654 elem = document.getElementById("pagination");
656 add_listener(elem, "click", pagination_change_page);
657 add_listener(elem, "mousemove", pagination_scroll_view);
658 add_listener(elem, "mouseout", pagination_scroll_stop);
660 elem = document.getElementById("pagination_jump_current");
662 elem.src = IMAGE.JUMP_CURRENT;
663 add_listener(elem, "click", playlist_scroll_to_playing);
664 elem.title = LANG.JUMP_CURRENT;
666 elem = document.getElementById("pagination_follow_current");
668 add_listener(elem, "click", pagination_toggle_following);
671 elem = document.getElementById("playlist_search_btn");
673 add_listener(elem, "click", plsearch_open);
676 // set it to nothing selected
677 pl_selection_changed(false);
680 function send_play_pause(e) {
683 if(playing.state=="stop") {
686 act+="&id=" + playing.id;
688 send_command("act=" + act);
690 function send_stop_cmd(e) {
692 send_command("act=stop");
694 function send_next_song(e) {
696 send_command("act=next");
698 function send_previous_song(e) {
700 send_command("act=previous");
703 function send_update_db_cmd(e) {
705 send_command("updatedb");
708 function send_clear_error() {
709 send_command("clearerror", false, false, true);
712 function remove_songs_event(e) {
713 var inv = 'crop_items_button'==e.target.id;
714 var sel = get_pl_selection_range(inv);
716 /* nothing selected if we just removed it,
717 * or at least in theory */
718 pl_selection_changed(false);
723 send_command("remove=" + encodeURIComponent(sel), remove_songs_cb, LANG.WAIT_REMOVING);
726 function remove_songs_cb(response) {
728 show_status_bar(LANG.E_REMOVE);
729 hide_status_bar(STATUS_DEFAULT_TIMEOUT);
733 function open_close_settings(e) {
734 var sc = document.getElementById('settings_container');
736 if(sc.style.display == "block") { /* not perfect but I think there's enough vars at the top */
737 sc.firstChild.style.display = "none";
738 remove_listener(sc, "mousedown", stop_event);
739 remove_listener(sc, "click", settings_click_handler);
740 setTimeout(open_close_settings_timer, settings_time, sc, false, new Array(0, 200));
744 var dst_height = sc.scrollHeight; // || innerHeight
745 sc.style.height = "50px";
746 sc.style.overflow = "hidden";
747 remove_children(sc.firstChild);
748 sc.firstChild.style.display = "none";
749 sc.firstChild.appendChild(document.createTextNode("Loading.."));
750 sc.style.display = "block";
751 add_listener(sc, "mousedown", stop_event);
752 add_listener(sc, "click", settings_click_handler);
753 setTimeout(open_close_settings_timer, settings_time, sc, true, new Array(0, 200));
754 send_command("outputs", open_settings_cb);
758 function open_close_settings_timer(sc, isOpening, heights) {
762 else heights[1] -=ad;
764 if(heights[0]<heights[1]) {
765 sc.style.height = (isOpening?heights[0]:heights[1]) + "px";
766 setTimeout(open_close_settings_timer, settings_time, sc, isOpening, heights);
770 //sc.style.overflow = "auto";
771 sc.firstChild.style.display = "block";
772 sc.style.height = heights[1] + "px";
775 sc.style.display = "none";
776 sc.style.height = heights[0] + "px";
782 function create_settings_status_image(stat) {
783 var img = create_node("img");
784 img.className = "server_settings";
785 if(stat==1||stat=="1") {
786 img.src = IMAGE.SERVER_SETTINGS_ENABLED;
789 img.src = IMAGE.SERVER_SETTINGS_DISABLED;
794 function open_settings_cb(response) {
795 var txt = document.getElementById('settings_content');
796 remove_children(txt);
797 var img = create_node("img");
798 img.className = "server_settings";
800 function add_entry(id, stat, text, no_img) {
801 var span = create_node("span", id);
802 span.className = "server_settings";
804 var im = create_settings_status_image(stat);
806 span.appendChild(im);
808 span.appendChild(create_txt(" " + text));
809 txt.appendChild(span);
810 txt.appendChild(create_node("br"));
814 add_entry("repeat_toggle", playing.repeat, LANG.REPEAT);
815 add_entry("random_toggle", playing.random, LANG.RANDOM);
816 var xfade = add_entry("xfade_entry", playing.xfade, LANG.XFADE, true);
817 var xe = create_node("img");
819 xe.className = "server_settings";
820 xe.src = IMAGE.SERVER_SETTINGS_XFADE_DOWN;
821 xfade.appendChild(xe);
822 var i_right = xe.cloneNode(true);
823 i_right.name = "right";
824 i_right.src = IMAGE.SERVER_SETTINGS_XFADE_UP;
825 xe = create_node("span", "xfade_adjust_txt", " " + playing.xfade + " ");
826 xfade.appendChild(xe);
827 xfade.appendChild(i_right);
829 var tmp = create_node("hr");
830 tmp.className = "server_settings";
831 txt.appendChild(tmp);
832 txt.appendChild(create_txt("Outputs:"));
833 txt.appendChild(create_node("br"));
835 var outputs = response['outputs'];
836 for(var i in outputs) {
837 var id = outputs[i]["outputid"];
838 var enabled = outputs[i]["outputenabled"];
839 var s = add_entry(OUTPUT_ID + id, enabled, outputs[i]["outputname"]);
840 s.setAttribute("outputenabled", enabled);
845 txt.appendChild(create_txt(LANG.E_NO_OUTPUTS));
849 function settings_click_handler(e) {
850 for(var n = e.target; n.parentNode; n=n.parentNode) {
852 if(n.id.indexOf(OUTPUT_ID)==0&&n.id.indexOf("img")<0) {
856 else if(n.id=="repeat_toggle") {
860 else if(n.id=="random_toggle") {
864 else if(n.id=="xfade_entry") {
867 else if(n.id=="settings_container") {
875 function toggle_repeat(e) {
876 send_command("repeat=" + (parseInt(playing.repeat)==0?1:0), toggle_repeat_cb);
878 function toggle_random(e) {
879 send_command("random=" + (parseInt(playing.random)==0?1:0), toggle_random_cb);
881 function toggle_output(e) {
883 output_toggle_id = target.id;
884 id = target.id.substring(OUTPUT_ID.length);
886 if(target.getAttribute("outputenabled")==1)
890 send_command(cmd, output_change_cb);
893 function xfade_adjust(node, ev) {
894 if(!ev.target.name) {
897 var name = ev.target.name;
898 if(name!="left"&&name!="right") {
901 var xfade= parseInt(playing.xfade) + ("left"==name?-1:+1);
904 send_command("xfade=" + xfade);
905 var x = document.getElementById("xfade_adjust_txt");
907 x.firstChild.nodeValue = " " + xfade + " ";
911 function toggle_repeat_cb(response) {
912 var n = document.getElementById("repeat_toggle_img");
913 var img = create_settings_status_image(response);
914 replace_node(img, n);
915 img.id = "repeat_toggle_img";
917 function toggle_random_cb(response) {
918 var n = document.getElementById("random_toggle_img");
919 var img = create_settings_status_image(response);
920 replace_node(img, n);
921 img.id = "random_toggle_img";
923 function output_change_cb(response) {
924 if(output_toggle_id==null)
926 var n = document.getElementById(output_toggle_id);
929 var o_img = document.getElementById(output_toggle_id + "_img");
930 n.setAttribute("outputenabled", response);
931 var img = create_settings_status_image(response);
933 replace_node(img, o_img);
934 output_toggle_id = null;
938 function send_play_pos(pos) {
939 send_command("act=play&pos=" + pos);
942 function open_pl_overlay(e) {
943 if(open_overlay_idx<0) {
944 open_overlay_fixed(pl_overlay_id);
947 close_overlay(pl_overlay_id);
951 function StatusBar(txt) {
952 this.txt = document.getElementById('status_bar_txt');
953 this.img = document.getElementById('status_bar_img');
954 this.main = document.getElementById('status_bar');
958 /* status bar (could be put in toolkit though */
959 function show_status_bar(txt, working) {
960 txt = create_txt(txt);
961 remove_children(status_bar.txt);
962 status_bar.txt.appendChild(txt);
964 status_bar.img.setAttribute("working", "yeah");
967 if(status_bar.img.hasAttribute("working"))
968 status_bar.img.removeAttribute("working");
970 status_bar.main.style.display = "block";
971 /* to prevent it from disappearing again if it is showing */
972 if(status_bar.timer) {
973 clearTimeout(status_bar.timer);
974 status_bar.timer = false;
978 /* hides status-bar after optional number of seconds */
979 function hide_status_bar(time) {
980 if(typeof(time)!='undefined'&&time&&time>0) {
981 status_bar.timer = setTimeout(hide_status_bar, time*1000, false);
984 remove_children(status_bar.txt);
985 if(browser_is_opera()) {
986 opera_quirk_set_display_none(status_bar.main);
989 status_bar.main.style.display = "none";
991 status_bar.timer = false;
995 function setup_keys() {
997 keyboard_register_listener(send_play_pause, "k", KEYBOARD_NO_KEY, true);
998 keyboard_register_listener(send_previous_song, "j", KEYBOARD_NO_KEY, true);
999 keyboard_register_listener(send_next_song, "l", KEYBOARD_NO_KEY, true);
1000 keyboard_register_listener(quickadd_focus, "s", KEYBOARD_CTRL_KEY|KEYBOARD_SHIFT_KEY, true);
1001 /* for browsers where ctrl+shift does something else */
1002 keyboard_register_listener(quickadd_focus, "s", KEYBOARD_CTRL_KEY|KEYBOARD_ALT_KEY, true);
1003 keyboard_register_listener(playlist_scroll_to_playing, " " , KEYBOARD_NO_KEY, true);
1005 var qa = document.getElementById('quickadd');
1006 qa.setAttribute("autocomplete", "off");
1007 add_listener(qa, "keydown", quickadd_keydown_handler); // stop it from getting to the keylisteners!
1008 add_listener(qa, "keyup", quickadd_keyup_handler);
1009 add_listener(qa, "focus", quickadd_focus);
1010 add_listener(qa, "blur", quickadd_blur);
1011 qa.title = LANG.QUICK_ADD + " [Ctrl+Shift+S]";
1014 function change_pos_dtype() {
1015 playing.time_dtype++;
1016 if(playing.time_dtype>=playing.TIME_END) {
1017 playing.time_dtype = 0;
1019 setting_set("time_dtype", playing.time_dtype);