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;
45 playing.image = ""; //
47 playing.random = 0; // random on/off
48 playing.repeat = 0; // repeat on/off
49 playing.xfade = 0; // crossfade seconds
50 playing.update = false; // dbupdate
51 playing.show_node = null; // node that is supposed to have playing status
52 playing.is_stream = false; // true if the playing song is a stream
53 playing.error = false;
54 playing.error_time = 0;
58 playing.TIME_ELAPSED = 0;
59 playing.TIME_REMAINING = 1;
61 playing.time_dtype = playing.TIME_ELAPSED;
63 /* various elements found around the document */
64 playing.disp_info = null;
65 playing.pp_button = null;
66 playing.disp_artist = null;
67 playing.disp_title = null;
68 playing.disp_album = null;
69 playing.albumart = null;
71 var last_pl_selected = true;
73 var output_toggle_id = null;
75 var pl_overlay_id = -1;
76 var pl_overlay_write = null;
78 var status_bar = null;
80 var send_command_rm_status = false;
81 var pl_entry_clone = null;
83 var settings_time = 10;
85 var need_info_arr = null;
87 var init_failed = false;
89 var playlist_add_popup = null;
90 var playlist_save_popup = null;
92 var pagination = new Object();
93 pagination.max = 0; // max pr. page
94 pagination.page = 0; // what page we currently are on
95 pagination.pages = 0; // number of pages being advertised
96 pagination.need_update = false; // indicates wheter we need an update
97 pagination.list = null; // reference to the displaying area
98 pagination.container = null; // reference to the container
100 function init_player() {
102 status_bar = new StatusBar();
107 update_delay = update_delay * 1000;
109 else window.update_delay = 1000;
111 possliderid = setup_slider(document.getElementById('posslider'), position_adjust, LANG.POSITION);
112 set_slider_pos(possliderid, 0);
114 volsliderid = setup_slider(document.getElementById('volslider'), volume_adjust, LANG.VOLUME);
115 set_slider_pos(volsliderid, 0);
117 playlist_element = document.getElementById('playlist')
119 var pltmp_id = setup_node_move(playlist_element, playlist_move, playlist_get_move_txt);
120 add_move_doubleclick(pltmp_id, playlist_dblclick);
121 set_selection_change_handler(pltmp_id, pl_selection_changed);
123 pl_overlay_id = setup_overlay(playlist_element, new Array(10, 10, 300, 300 ), pl_overlay_open_cb, pl_overlay_close_cb);
124 pl_overlay_write = get_overlay_write_area(pl_overlay_id);
126 playing.disp_info = document.getElementById('disp_info');
127 playing.disp_artist = document.getElementById('disp_artist');
128 playing.disp_title = document.getElementById('disp_title');
129 playing.disp_album = document.getElementById('disp_album');
130 playing.albumart = document.getElementById("albumart");
131 playing.pp_button = document.getElementById("pp_button");
133 pagination.list = document.getElementById('pagination_list');
134 pagination.container = document.getElementById('pagination');
135 pagination.max = pagination_max; // nice :S
137 var tmp = setting_get("time_dtype");
140 setting_set("time_dtype", playing.time_dtype);
143 if(tmp==playing.TIME_ELAPSED)
144 playing.time_dtype = playing.TIME_ELAPSED;
145 else if(tmp==playing.TIME_REMAINING)
146 playing.time_dtype = playing.TIME_REMAINING;
157 if(typeof(window.metadata_init)=='function')
159 if(typeof(window.recommend_init)=='function')
165 debug(LANG.E_INIT +": " + e.message, true);
169 /* arg-list: command to send, command to call when result is return, show status message when working,
170 don't request status update with this sendcommand,
171 post data if we should use that, if it should form-urlencoded content type should be set */
172 function send_command(command, result_callback, status_msg, nostatus, do_post, form_urlencoded) {
176 var http = new XMLHttpRequest();
177 var url = "command.php?" + command;
180 url+="&plchanges=" + playing.pl_version;
181 if(pagination.max>0) {
182 url+="&pmax=" + pagination.max + "&page=" + pagination.page;
183 //debug("pmax: " + pagination.max + ", page: " + pagination.page);
187 if(send_command_rm_status) {
189 send_command_rm_status = false;
192 http.onreadystatechange = function() {
193 if(http.readyState==4) {
195 if(http.responseText&&(resp = evaluate_json(http.responseText))) {
196 if(resp['connection']&&resp['connection']=="failed") {
197 last_update = get_time();
198 show_status_bar(LANG.E_CONNECT);
199 send_command_rm_status = true;
201 result_callback("failed");
208 show_status_bar(LANG.E_INVALID_RESPONSE);
209 send_command_rm_status = true;
210 last_update = get_time();
212 result_callback("failed");
218 if(http.status==200) {
219 var res = resp['result'];
220 var stat = resp["status"];
221 var plchanges = resp["plchanges"];
222 var has_plchanges = plchanges && stat && playing.pl_version != stat.playlist;
224 if(res&&result_callback) {
225 result_callback(res);
229 current_status_handler(stat, has_plchanges);
230 last_update = get_time();
234 playing.pl_version = stat.playlist;
235 plchanges_handler3(plchanges, stat.playlistlength);
236 /* the currently playing song might have changed if it's a stream */
237 if(playing.is_stream) {
242 /* don't remove if there's no message or a timer */
243 if(status_msg&&!status_bar.timer)
247 if(result_callback) {
248 result_callback("server operation failed");
250 show_status_bar(LANG.NO_RESPONSE); // maybe add a 10 second delay here and reconnect!
251 send_command_rm_status = true;
257 show_status_bar(status_msg, true);
262 http.open("POST", url);
263 if(form_urlencoded) {
264 http.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
269 http.open("GET", url);
274 function update_callback(res) {
275 var time = update_delay;
277 time = problem_update_delay*update_delay;
278 last_update_cmd_id = setTimeout(need_update, time);
281 function reschedule_update_now() {
282 clearTimeout(last_update_cmd_id);
283 // make sure it get's run immidiately
284 last_update_cmd_id = setTimeout(need_update, 0, -update_delay);
287 function need_update(delay) {
290 var now = get_time();
291 if(now>last_update+update_delay+delay-20) { // giving it a little slack
292 send_command("ping", update_callback);
295 delay = last_update+update_delay-now;
299 last_update_cmd_id = setTimeout(need_update, delay);
304 function handle_fatal_error(txt) {
305 show_status_bar(txt);
306 clearTimeout(last_update_cmd_id);
309 /* current status handler, info is stats
310 * if has_plchanges it is assumed the data also carries plchanges
311 * which will handle resizing of the playlist */
312 function current_status_handler(info, has_plchanges) {
314 // something is wrong
315 set_slider_pos(possliderid, 0);
319 var tmp = info.playlistlength;
320 if((tmp = parseInt(tmp))>=0) {
321 if(playing.pl_size!=tmp) {
322 playing.pl_size = tmp;
323 if(pagination.max>0) {
324 // make sure size fits inside what we have as number of pages
325 var s = pagination.max * (pagination.pages);
326 if(tmp < s-pagination.max || tmp>=s) {
327 pagination_update_list(tmp);
330 playlist_resize(tmp);
333 else if(!has_plchanges) { // if it doesn't carry plchanges we have to do it here
334 playlist_resize(tmp);
339 tmp = info.updating_db;
342 if(playing.update!=tmp) {
343 playing.update = tmp;
344 show_status_bar(LANG.WAIT_UPDATING_DB, true);
347 else if(playing.update) {
348 playing.update = false;
352 var state = info["state"];
353 var volume = info[ "volume"];
354 if(volume!=null&&volume!=playing.volume) {
355 set_slider_pos(volsliderid, volume);
356 playing.volume = volume;
359 playing.repeat = info.repeat;
360 playing.random = info.random;
361 playing.xfade = info.xfade;
365 var pos = info["time"].split(":");
366 set_slider_pos(possliderid, (pos[0]*100)/pos[1]);
367 playing.length = pos[1];
369 /* complex, but seems to be somewhat better in firefox */
370 var tmp = info.bitrate;
371 if(tmp&&playing.bitrate!=tmp) {
372 var disp_info = playing.disp_info
373 if(bitrate_txt_node==null) {
374 bitrate_txt_node = document.createTextNode(LANG.BITRATE);
375 disp_info.appendChild(bitrate_txt_node);
376 if(disp_info.normalize)
377 disp_info.normalize();
378 bitrate_txt_node = disp_info.firstChild.splitText(LANG.BITRATE.length);
380 var rep = document.createTextNode(tmp);
381 disp_info.replaceChild(rep, bitrate_txt_node);
382 bitrate_txt_node = rep;
383 playing.bitrate = tmp;
386 var postxt = get_slider_txt(possliderid);
388 if(position_txt_node==null) {
389 position_txt_node = postxt.firstChild.splitText(LANG.POSITION.length);
391 var rep = create_txt(format_playing_time(pos[0], pos[1]));
392 postxt.replaceChild(rep, position_txt_node);
393 position_txt_node = rep;
396 else if(playing.state!="stop") { // state == stop and last state wasn't stop
397 set_slider_pos(possliderid, 0);
398 var disp_info = playing.disp_info;
399 var rep = document.createTextNode("0");
400 var postxt = get_slider_txt(possliderid);
401 if(bitrate_txt_node&&bitrate_txt_node!=null) {
402 disp_info.replaceChild(rep, bitrate_txt_node);
403 bitrate_txt_node = rep;
406 rep = document.createTextNode("");
407 if(position_txt_node&&position_txt_node!=null) {
408 postxt.replaceChild(rep, position_txt_node);
409 position_txt_node = rep;
412 if(state!=playing.state) {
413 playing.state = state;
414 var bt = playing.pp_button;
416 bt.src = IMAGE.BUTTON_PAUSE;
417 if(typeof(window.streaming_try_autoplay)=='function')
418 streaming_try_autoplay();
421 bt.src = IMAGE.BUTTON_PLAY;
422 if(typeof(window.streaming_try_autostop)=='function')
423 streaming_try_autostop();
427 if(typeof info.error != 'undefined' || playing.error) {
428 if(playing.error && get_time() - 10000 > playing.error_time) {
429 playing.error = false;
433 else if(typeof info.error != 'undefined' && playing.error != info.error) {
434 playing.error = info.error;
435 playing.error_time = get_time();
436 show_status_bar("MPD error: " + playing.error);
440 var c_play = info.songid;
441 if(typeof(c_play)=='undefined') {
445 select_playing_song();
447 else if(c_play!=playing.id) {
449 playing.pos = parseInt(info.song);
450 select_playing_song();
451 if(pagination_is_following()) {
452 playlist_scroll_to_playing();
454 /* this is last because it needs the id */
457 else if(playing.pos!=info.song) {
458 playing.pos = parseInt(info.song);
462 function format_playing_time(pos, total) {
463 if(playing.time_dtype==playing.TIME_REMAINING) {
464 return convert_minsec(pos-total) + "/" + convert_minsec(total);
466 else { // dtype == playing.TIME_ELAPSED || something wrong
467 return convert_minsec(pos) + "/" + convert_minsec(total);
471 function request_song_info() {
474 remove_children(playing.disp_artist);
475 remove_children(playing.disp_title);
476 remove_children(playing.disp_album);
478 remove_children(playing.albumart);
485 playing.is_stream = false;
490 send_command("currentsong", update_current_song, false, true);
494 function update_current_song(info) {
495 var artist = info[ "Artist"];
496 var title = info["Title"];
497 var album = info[ "Album"];
498 var a = playing.disp_artist;
499 var t = playing.disp_title;
500 var alb = playing.disp_album;
501 var new_thumb = false;
503 if(typeof(title)=='undefined')
505 if(typeof(album)=='undefined')
507 if(typeof(artist)=='undefined')
510 if(artist!=playing.artist) {
511 playing.artist = artist;
514 a.appendChild(create_txt(artist));
516 if(playing.album != album) {
517 playing.album = album;
519 remove_children(alb);
520 alb.appendChild(create_txt(album));
523 if(typeof(info['file'])!='undefined') {
524 var f = info['file'];
525 if(f&&f.indexOf("http://")==0)
526 playing.is_stream = true;
527 else playing.is_stream = false;
531 playing.title = title;
533 if(title==null||title=="") {
534 title = info["file"];
536 title = title.substring(title.lastIndexOf(DIR_SEPARATOR)+1);
538 t.appendChild(create_txt(title));
540 set_playing_title(artist, title);
542 if(new_thumb&&typeof(window.request_thumbnail) == 'function') {
543 setTimeout(request_thumbnail, 1);
547 function set_playing_title(artist, title) {
548 if(typeof(artist)=='undefined'||artist==null)
550 if(typeof(title)=='undefined'||title==null)
553 var wt = "Pitchfork MPD Client";
554 if(artist.length||title.length) {
562 function volume_adjust(vol) {
563 send_command("volume=" + parseInt(vol));
566 function position_adjust(pos) {
567 send_command("position=" + parseInt((pos* parseInt(playing.length))/100) + "&id=" + playing.id);
570 function convert_minsec(sec) {
571 var min = parseInt(sec/60);
572 var s = Math.abs(sec%60);
573 return (sec<0&&min==0?"-" + min:min) + ":" + (s<10?"0" + s:s);
576 function buttons_init() {
579 var elem = document.getElementById('pp_button');
580 elem.src = IMAGE.BUTTON_PLAY;
581 add_listener(elem, "click", send_play_pause);
582 if(window.stop_button) {
583 elem = document.getElementById('stop_button');
584 elem.style.display = "";
585 elem.src = IMAGE.BUTTON_STOP;
586 add_listener(elem, "click", send_stop_cmd);
587 elem.parentNode.style.marginLeft = "-15px";
590 elem = document.getElementById("next_button");
591 elem.src = IMAGE.BUTTON_NEXT;
592 add_listener(elem, "click", send_next_song);
593 elem = document.getElementById("previous_button");
594 elem.src = IMAGE.BUTTON_PREVIOUS;
595 add_listener(elem, "click", send_previous_song);
597 /* left menu buttons */
598 elem = document.getElementById("open_directory_button");
599 elem.src = IMAGE.MENU_ITEM_DIRECTORY;
600 add_listener(elem, "click", open_pl_overlay);
601 elem = document.getElementById("crop_items_button");
602 elem.src = IMAGE.MENU_ITEM_CROP;
603 add_listener(elem, "click", remove_songs_event);
604 elem = document.getElementById("remove_items_button");
605 elem.src = IMAGE.MENU_ITEM_REMOVE;
606 add_listener(elem, "click", remove_songs_event);
608 /* server settings */
609 elem = document.getElementById("settings_header");
610 add_listener(elem, "click", open_close_settings);
611 add_listener(elem, "mousedown", stop_event);
614 elem = document.getElementById("playlist_add");
615 add_listener(elem, "click", playlist_add_button);
616 elem = document.getElementById("playlist_save");
617 add_listener(elem, "click", playlist_save_button);
620 elem = document.getElementById("status_bar_img");
621 elem.src = IMAGE.WORKING;
623 /* streaming if applicable */
624 elem = document.getElementById("streaming_open");
626 add_listener(elem, "click", streaming_open);
630 elem = get_slider_txt(possliderid);
632 add_listener(elem, "click", change_pos_dtype);
636 elem = document.getElementById("pagination");
638 add_listener(elem, "click", pagination_change_page);
639 add_listener(elem, "mousemove", pagination_scroll_view);
640 add_listener(elem, "mouseout", pagination_scroll_stop);
642 elem = document.getElementById("pagination_jump_current");
644 elem.src = IMAGE.JUMP_CURRENT;
645 add_listener(elem, "click", playlist_scroll_to_playing);
646 elem.title = LANG.JUMP_CURRENT;
648 elem = document.getElementById("pagination_follow_current");
650 add_listener(elem, "click", pagination_toggle_following);
653 elem = document.getElementById("playlist_search_btn");
655 add_listener(elem, "click", plsearch_open);
658 // set it to nothing selected
659 pl_selection_changed(false);
662 function send_play_pause(e) {
665 if(playing.state=="stop") {
668 act+="&id=" + playing.id;
670 send_command("act=" + act);
672 function send_stop_cmd(e) {
674 send_command("act=stop");
676 function send_next_song(e) {
678 send_command("act=next");
680 function send_previous_song(e) {
682 send_command("act=previous");
685 function send_update_db_cmd(e) {
687 send_command("updatedb");
690 function send_clear_error() {
691 send_command("clearerror", false, false, true);
694 function remove_songs_event(e) {
695 var inv = 'crop_items_button'==e.target.id;
696 var sel = get_pl_selection_range(inv);
698 /* nothing selected if we just removed it,
699 * or at least in theory */
700 pl_selection_changed(false);
705 send_command("remove=" + encodeURIComponent(sel), remove_songs_cb, LANG.WAIT_REMOVING);
708 function remove_songs_cb(response) {
710 show_status_bar(LANG.E_REMOVE);
711 hide_status_bar(STATUS_DEFAULT_TIMEOUT);
715 function open_close_settings(e) {
716 var sc = document.getElementById('settings_container');
718 if(sc.style.display == "block") { /* not perfect but I think there's enough vars at the top */
719 sc.firstChild.style.display = "none";
720 remove_listener(sc, "mousedown", stop_event);
721 remove_listener(sc, "click", settings_click_handler);
722 setTimeout(open_close_settings_timer, settings_time, sc, false, new Array(0, 200));
726 var dst_height = sc.scrollHeight; // || innerHeight
727 sc.style.height = "50px";
728 sc.style.overflow = "hidden";
729 remove_children(sc.firstChild);
730 sc.firstChild.style.display = "none";
731 sc.firstChild.appendChild(document.createTextNode("Loading.."));
732 sc.style.display = "block";
733 add_listener(sc, "mousedown", stop_event);
734 add_listener(sc, "click", settings_click_handler);
735 setTimeout(open_close_settings_timer, settings_time, sc, true, new Array(0, 200));
736 send_command("outputs", open_settings_cb);
740 function open_close_settings_timer(sc, isOpening, heights) {
744 else heights[1] -=ad;
746 if(heights[0]<heights[1]) {
747 sc.style.height = (isOpening?heights[0]:heights[1]) + "px";
748 setTimeout(open_close_settings_timer, settings_time, sc, isOpening, heights);
752 //sc.style.overflow = "auto";
753 sc.firstChild.style.display = "block";
754 sc.style.height = heights[1] + "px";
757 sc.style.display = "none";
758 sc.style.height = heights[0] + "px";
764 function create_settings_status_image(stat) {
765 var img = create_node("img");
766 img.className = "server_settings";
767 if(stat==1||stat=="1") {
768 img.src = IMAGE.SERVER_SETTINGS_ENABLED;
771 img.src = IMAGE.SERVER_SETTINGS_DISABLED;
776 function open_settings_cb(response) {
777 var txt = document.getElementById('settings_content');
778 remove_children(txt);
779 var img = create_node("img");
780 img.className = "server_settings";
782 function add_entry(id, stat, text, no_img) {
783 var span = create_node("span", id);
784 span.className = "server_settings";
786 var im = create_settings_status_image(stat);
788 span.appendChild(im);
790 span.appendChild(create_txt(" " + text));
791 txt.appendChild(span);
792 txt.appendChild(create_node("br"));
796 add_entry("repeat_toggle", playing.repeat, LANG.REPEAT);
797 add_entry("random_toggle", playing.random, LANG.RANDOM);
798 var xfade = add_entry("xfade_entry", playing.xfade, LANG.XFADE, true);
799 var xe = create_node("img");
801 xe.className = "server_settings";
802 xe.src = IMAGE.SERVER_SETTINGS_XFADE_DOWN;
803 xfade.appendChild(xe);
804 var i_right = xe.cloneNode(true);
805 i_right.name = "right";
806 i_right.src = IMAGE.SERVER_SETTINGS_XFADE_UP;
807 xe = create_node("span", "xfade_adjust_txt", " " + playing.xfade + " ");
808 xfade.appendChild(xe);
809 xfade.appendChild(i_right);
811 var tmp = create_node("hr");
812 tmp.className = "server_settings";
813 txt.appendChild(tmp);
814 txt.appendChild(create_txt("Outputs:"));
815 txt.appendChild(create_node("br"));
817 var outputs = response['outputs'];
818 for(var i in outputs) {
819 var id = outputs[i]["outputid"];
820 var enabled = outputs[i]["outputenabled"];
821 var s = add_entry(OUTPUT_ID + id, enabled, outputs[i]["outputname"]);
822 s.setAttribute("outputenabled", enabled);
827 txt.appendChild(create_txt(LANG.E_NO_OUTPUTS));
831 function settings_click_handler(e) {
832 for(var n = e.target; n.parentNode; n=n.parentNode) {
834 if(n.id.indexOf(OUTPUT_ID)==0&&n.id.indexOf("img")<0) {
838 else if(n.id=="repeat_toggle") {
842 else if(n.id=="random_toggle") {
846 else if(n.id=="xfade_entry") {
849 else if(n.id=="settings_container") {
857 function toggle_repeat(e) {
858 send_command("repeat=" + (parseInt(playing.repeat)==0?1:0), toggle_repeat_cb);
860 function toggle_random(e) {
861 send_command("random=" + (parseInt(playing.random)==0?1:0), toggle_random_cb);
863 function toggle_output(e) {
865 output_toggle_id = target.id;
866 id = target.id.substring(OUTPUT_ID.length);
868 if(target.getAttribute("outputenabled")==1)
872 send_command(cmd, output_change_cb);
875 function xfade_adjust(node, ev) {
876 if(!ev.target.name) {
879 var name = ev.target.name;
880 if(name!="left"&&name!="right") {
883 var xfade= parseInt(playing.xfade) + ("left"==name?-1:+1);
886 send_command("xfade=" + xfade);
887 var x = document.getElementById("xfade_adjust_txt");
889 x.firstChild.nodeValue = " " + xfade + " ";
893 function toggle_repeat_cb(response) {
894 var n = document.getElementById("repeat_toggle_img");
895 var img = create_settings_status_image(response);
896 replace_node(img, n);
897 img.id = "repeat_toggle_img";
899 function toggle_random_cb(response) {
900 var n = document.getElementById("random_toggle_img");
901 var img = create_settings_status_image(response);
902 replace_node(img, n);
903 img.id = "random_toggle_img";
905 function output_change_cb(response) {
906 if(output_toggle_id==null)
908 var n = document.getElementById(output_toggle_id);
911 var o_img = document.getElementById(output_toggle_id + "_img");
912 n.setAttribute("outputenabled", response);
913 var img = create_settings_status_image(response);
915 replace_node(img, o_img);
916 output_toggle_id = null;
920 function send_play_pos(pos) {
921 send_command("act=play&pos=" + pos);
924 function open_pl_overlay(e) {
925 if(open_overlay_idx<0) {
926 open_overlay_fixed(pl_overlay_id);
929 close_overlay(pl_overlay_id);
933 function StatusBar(txt) {
934 this.txt = document.getElementById('status_bar_txt');
935 this.img = document.getElementById('status_bar_img');
936 this.main = document.getElementById('status_bar');
940 /* status bar (could be put in toolkit though */
941 function show_status_bar(txt, working) {
942 txt = create_txt(txt);
943 remove_children(status_bar.txt);
944 status_bar.txt.appendChild(txt);
946 status_bar.img.setAttribute("working", "yeah");
949 if(status_bar.img.hasAttribute("working"))
950 status_bar.img.removeAttribute("working");
952 status_bar.main.style.display = "block";
953 /* to prevent it from disappearing again if it is showing */
954 if(status_bar.timer) {
955 clearTimeout(status_bar.timer);
956 status_bar.timer = false;
960 /* hides status-bar after optional number of seconds */
961 function hide_status_bar(time) {
962 if(typeof(time)!='undefined'&&time&&time>0) {
963 status_bar.timer = setTimeout(hide_status_bar, time*1000, false);
966 remove_children(status_bar.txt);
967 if(browser_is_opera()) {
968 opera_quirk_set_display_none(status_bar.main);
971 status_bar.main.style.display = "none";
973 status_bar.timer = false;
977 function setup_keys() {
979 keyboard_register_listener(send_play_pause, "k", KEYBOARD_NO_KEY, true);
980 keyboard_register_listener(send_previous_song, "j", KEYBOARD_NO_KEY, true);
981 keyboard_register_listener(send_next_song, "l", KEYBOARD_NO_KEY, true);
982 keyboard_register_listener(quickadd_focus, "s", KEYBOARD_CTRL_KEY|KEYBOARD_SHIFT_KEY, true);
983 /* for browsers where ctrl+shift does something else */
984 keyboard_register_listener(quickadd_focus, "s", KEYBOARD_CTRL_KEY|KEYBOARD_ALT_KEY, true);
985 keyboard_register_listener(playlist_scroll_to_playing, " " , KEYBOARD_NO_KEY, true);
987 var qa = document.getElementById('quickadd');
988 qa.setAttribute("autocomplete", "off");
989 add_listener(qa, "keydown", quickadd_keydown_handler); // stop it from getting to the keylisteners!
990 add_listener(qa, "keyup", quickadd_keyup_handler);
991 add_listener(qa, "focus", quickadd_focus);
992 add_listener(qa, "blur", quickadd_blur);
993 qa.title = LANG.QUICK_ADD + " [Ctrl+Shift+S]";
996 function change_pos_dtype() {
997 playing.time_dtype++;
998 if(playing.time_dtype>=playing.TIME_END) {
999 playing.time_dtype = 0;
1001 setting_set("time_dtype", playing.time_dtype);