1 /*****************************************************************************
2 * functions.js: VLC media player web interface
3 *****************************************************************************
4 * Copyright (C) 2005-2006 the VideoLAN team
7 * Authors: Antoine Cellerier <dionoea -at- videolan -dot- org>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
22 *****************************************************************************/
24 /* global variables */
28 /**********************************************************************
30 *********************************************************************/
32 /* XMLHttpRequest wrapper */
33 function loadXMLDoc( url, callback )
35 // branch for native XMLHttpRequest object
36 if ( window.XMLHttpRequest )
38 req = new XMLHttpRequest();
39 req.onreadystatechange = callback;
40 req.open( "GET", url, true );
42 // branch for IE/Windows ActiveX version
44 else if ( window.ActiveXObject )
46 req = new ActiveXObject( "Microsoft.XMLHTTP" );
49 req.onreadystatechange = callback;
50 req.open( "GET", url, true );
56 /* fomat time in second as hh:mm:ss */
57 function format_time( s )
59 hours = Math.floor(s/3600);
60 minutes = Math.floor((s/60)%60);
62 if( hours < 10 ) hours = "0"+hours;
63 if( minutes < 10 ) minutes = "0"+minutes;
64 if( seconds < 10 ) seconds = "0"+seconds;
65 return hours+":"+minutes+":"+seconds;
68 /* delete all a tag's children and add a text child node */
69 function set_text( id, val )
71 elt = document.getElementById( id );
72 while( elt.hasChildNodes() )
73 elt.removeChild( elt.firstChild );
74 elt.appendChild( document.createTextNode( val ) );
77 /* set item's 'element' attribute to value */
78 function set_css( item, element, value )
80 for( j = 0; j<document.styleSheets.length; j++ )
82 cssRules = document.styleSheets[j].cssRules;
83 for( i = 0; i<cssRules.length; i++)
85 if( cssRules[i].selectorText == item )
87 cssRules[i].style.setProperty( element, value, null );
94 /* get item's 'element' attribute */
95 function get_css( item, element )
97 for( j = 0; j<document.styleSheets.length; j++ )
99 cssRules = document.styleSheets[j].cssRules;
100 for( i = 0; i<cssRules.length; i++)
102 if( cssRules[i].selectorText == item )
104 return cssRules[i].style.getPropertyValue( element );
110 function toggle_show( id )
112 element = document.getElementById( id );
113 if( element.style.display == 'block' || element.style.display == '' )
115 element.style.display = 'none';
119 element.style.display = 'block';
122 function toggle_show_node( id )
124 element = document.getElementById( 'pl_'+id );
125 img = document.getElementById( 'pl_img_'+id );
126 if( element.style.display == 'block' || element.style.display == '' )
128 element.style.display = 'none';
129 img.setAttribute( 'src', 'images/plus.png' );
130 img.setAttribute( 'alt', '[+]' );
134 element.style.display = 'block';
135 img.setAttribute( 'src', 'images/minus.png' );
136 img.setAttribute( 'alt', '[-]' );
140 function show( id ){ document.getElementById( id ).style.display = 'block'; }
142 function hide( id ){ document.getElementById( id ).style.display = 'none'; }
144 function checked( id ){ return document.getElementById( id ).checked; }
146 function value( id ){ return document.getElementById( id ).value; }
148 function radio_value( name )
150 radio = document.getElementsByName( name );
151 for( i = 0; i<radio.length; i++ )
153 if( radio[i].checked )
155 return radio[i].value;
161 function check_and_replace_int( id, val )
163 var objRegExp = /^\d\d*$/;
164 if( value( id ) != ''
165 && ( !objRegExp.test( value( id ) )
166 || parseInt( value( id ) ) < 1 ) )
167 document.getElementById( id ).value = val;
170 function addslashes( str ){ return str.replace(/\'/g, '\\\''); }
172 function disable( id ){ document.getElementById( id ).disabled = true; }
174 function enable( id ){ document.getElementById( id ).disabled = false; }
176 function button_over( element ){ element.style.border = "1px solid #000"; }
178 function button_out( element ){ element.style.border = "1px solid #fff"; }
180 /* toggle show help under the buttons */
181 function toggle_btn_text()
183 if( get_css( '.btn_text', 'display' ) == 'none' )
185 set_css( '.btn_text', 'display', 'block' );
189 set_css( '.btn_text', 'display', 'none' );
193 /**********************************************************************
195 *********************************************************************/
199 input_iesuxx = value('input_mrl');
200 if( value('sout_mrl') != '' )
201 input_iesuxx += ' '+value('sout_mrl');
202 url = 'requests/status.xml?command=in_play&input='+escape(input_iesuxx);
203 loadXMLDoc( url, parse_status );
204 setTimeout( 'update_playlist()', 1000 );
206 function in_enqueue()
208 input_iesuxx = value('input_mrl');
209 if( value('sout_mrl') != '' )
210 input_iesuxx += ' '+value('sout_mrl');
211 url = 'requests/status.xml?command=in_enqueue&input='+escape(input_iesuxx);
212 loadXMLDoc( url, parse_status );
213 setTimeout( 'update_playlist()', 1000 );
216 /* playlist actions */
217 function pl_play( id )
219 loadXMLDoc( 'requests/status.xml?command=pl_play&id='+id, parse_status );
220 setTimeout( 'update_playlist()', 1000 );
224 loadXMLDoc( 'requests/status.xml?command=pl_pause', parse_status );
228 loadXMLDoc( 'requests/status.xml?command=pl_stop', parse_status );
229 setTimeout( 'update_playlist()', 1000 );
233 loadXMLDoc( 'requests/status.xml?command=pl_next', parse_status );
234 setTimeout( 'update_playlist()', 1000 );
236 function pl_previous()
238 loadXMLDoc( 'requests/status.xml?command=pl_previous', parse_status );
239 setTimeout( 'update_playlist()', 1000 );
241 function pl_delete( id )
243 loadXMLDoc( 'requests/status.xml?command=pl_delete&id='+id, parse_status );
244 setTimeout( 'update_playlist()', 1000 );
248 loadXMLDoc( 'requests/status.xml?command=pl_empty', parse_status );
249 setTimeout( 'update_playlist()', 1000 );
254 loadXMLDoc( 'requests/status.xml?command=pl_sort', parse_status );
255 setTimeout( 'update_playlist()', 1000 );
257 function pl_shuffle()
259 loadXMLDoc( 'requests/status.xml?command=pl_random', parse_status );
260 setTimeout( 'update_playlist()', 1000 );
264 loadXMLDoc( 'requests/status.xml?command=pl_loop', parse_status );
268 loadXMLDoc( 'requests/status.xml?command=pl_repeat', parse_status );
272 function volume_down()
274 loadXMLDoc( 'requests/status.xml?command=volume&val=-20', parse_status );
278 loadXMLDoc( 'requests/status.xml?command=volume&val=%2B20', parse_status );
280 function fullscreen()
282 loadXMLDoc( 'requests/status.xml?command=fullscreen', parse_status );
284 function update_status()
286 loadXMLDoc( 'requests/status.xml', parse_status );
288 function update_playlist()
290 loadXMLDoc( 'requests/playlist.xml', parse_playlist );
293 /**********************************************************************
294 * Parse xml replies to XMLHttpRequests
295 *********************************************************************/
296 /* parse request/status.xml */
297 function parse_status()
299 if( req.readyState == 4 )
301 if( req.status == 200 )
303 status_iesuxx = req.responseXML.documentElement;
304 new_time = status_iesuxx.getElementsByTagName( 'time' )[0].firstChild.data;
305 if( old_time > new_time )
306 setTimeout('update_playlist()',50);
308 set_text( 'time', format_time( new_time ) );
309 set_text( 'length', format_time( status_iesuxx.getElementsByTagName( 'length' )[0].firstChild.data ) );
310 if( status_iesuxx.getElementsByTagName( 'volume' ).length != 0 )
311 set_text( 'volume', Math.floor(status_iesuxx.getElementsByTagName( 'volume' )[0].firstChild.data/5.12)+'%' );
312 set_text( 'state', status_iesuxx.getElementsByTagName( 'state' )[0].firstChild.data );
313 if( status_iesuxx.getElementsByTagName( 'state' )[0].firstChild.data == "playing" )
315 document.getElementById( 'btn_pause_img' ).setAttribute( 'src', 'images/pause.png' );
319 document.getElementById( 'btn_pause_img' ).setAttribute( 'src', 'images/play.png' );
324 /*alert( 'Error! HTTP server replied: ' + req.status );*/
329 /* parse playlist.xml */
330 function parse_playlist()
332 if( req.readyState == 4 )
334 if( req.status == 200 )
336 answer = req.responseXML.documentElement;
337 playtree_iesuxx = document.getElementById( 'playtree' );
338 pos = document.createElement( "div" );
340 elt = answer.firstChild;
343 if( elt.nodeName == "node" )
345 if( pos.hasChildNodes() )
346 pos.appendChild( document.createElement( "br" ) );
347 nda = document.createElement( 'a' );
348 pos.appendChild( nda );
349 nda.setAttribute( 'href', 'javascript:toggle_show_node(\''+elt.getAttribute( 'id' )+'\');' );
350 ndai = document.createElement( 'img' );
351 nda.appendChild( ndai );
352 ndai.setAttribute( 'src', 'images/minus.png' );
353 ndai.setAttribute( 'alt', '[-]' );
354 ndai.setAttribute( 'id', 'pl_img_'+elt.getAttribute( 'id' ) );
355 pos.appendChild( document.createTextNode( ' ' + elt.getAttribute( 'name' ) ) );
356 nd = document.createElement( "div" );
357 pos.appendChild( nd );
358 nd.setAttribute( 'class', 'pl_node' );
359 nd.setAttribute( 'id', 'pl_'+elt.getAttribute( 'id' ) );
361 else if( elt.nodeName == "leaf" )
363 if( pos.hasChildNodes() )
364 pos.appendChild( document.createElement( "br" ) );
365 pl = document.createElement( "a" );
366 pos.appendChild( pl );
367 pl.setAttribute( 'class', 'pl_leaf' );
368 pl.setAttribute( 'href', 'javascript:pl_play('+elt.getAttribute( 'id' )+');' );
369 pl.setAttribute( 'id', 'pl_'+elt.getAttribute( 'id' ) );
370 if( elt.getAttribute( 'current' ) == 'current' )
372 pl.setAttribute( 'style', 'font-weight: bold;' );
373 document.getElementById( 'nowplaying' ).textContent
374 = elt.getAttribute( 'name' );
376 pl.setAttribute( 'title', elt.getAttribute( 'uri' ));
377 pl.appendChild( document.createTextNode( elt.getAttribute( 'name' ) ) );
378 pos.appendChild( document.createTextNode( ' ' ) );
379 del = document.createElement( "a" );
380 pos.appendChild( del );
381 del.setAttribute( 'href', 'javascript:pl_delete('+elt.getAttribute( 'id' )+')' );
382 del.appendChild( document.createElement( "img" ) );
384 del.setAttribute( 'src', 'images/delete_small.png' );
385 del.setAttribute( 'alt', '(delete)' );
389 elt = elt.firstChild;
392 else if( elt.nextSibling )
394 elt = elt.nextSibling;
399 elt = elt.parentNode.nextSibling;
400 pos = pos.parentNode;
403 while( playtree_iesuxx.hasChildNodes() )
404 playtree_iesuxx.removeChild( playtree_iesuxx.firstChild );
405 playtree_iesuxx.appendChild( pos_top );
409 /*alert( 'Error! HTTP server replied: ' + req.status );*/
414 /* parse browse.xml */
415 function parse_browse_dir( )
417 if( req.readyState == 4 )
419 if( req.status == 200 )
421 answer = req.responseXML.documentElement;
422 browser_iesuxx = document.getElementById( 'browser' );
423 pos = document.createElement( "div" );
424 elt = answer.firstChild;
427 if( elt.nodeName == "element" )
429 pos.appendChild( document.createElement( "a" ) );
430 pos.lastChild.setAttribute( 'class', 'browser' );
431 if( elt.getAttribute( 'type' ) == 'directory' )
433 pos.lastChild.setAttribute( 'href', 'javascript:browse_dir(\''+addslashes(elt.getAttribute( 'path' ))+'\');');
437 pos.lastChild.setAttribute( 'href', 'javascript:browse_path(\''+addslashes(elt.getAttribute( 'path' ))+'\');' );
439 pos.lastChild.appendChild( document.createTextNode( elt.getAttribute( 'name' ) ) );
440 if( elt.getAttribute( 'type' ) == 'directory' )
442 pos.appendChild( document.createTextNode( ' ' ) );
443 pos.appendChild( document.createElement( "a" ) );
444 pos.lastChild.setAttribute( 'class', 'browser' );
445 pos.lastChild.setAttribute( 'href', 'javascript:browse_path(\''+addslashes(elt.getAttribute( 'path' ))+'\');');
446 pos.lastChild.appendChild( document.createTextNode( '(select)' ) );
448 pos.appendChild( document.createElement( "br" ) );
450 elt = elt.nextSibling;
452 while( browser_iesuxx.hasChildNodes() )
453 browser_iesuxx.removeChild( browser_iesuxx.firstChild );
454 browser_iesuxx.appendChild( pos );
458 /*alert( 'Error! HTTP server replied: ' + req.status );*/
463 /**********************************************************************
464 * Input dialog functions
465 *********************************************************************/
466 function hide_input( )
468 document.getElementById( 'input_file' ).style.display = 'none';
469 document.getElementById( 'input_disc' ).style.display = 'none';
470 document.getElementById( 'input_network' ).style.display = 'none';
473 /* update the input MRL using data from the input file helper */
474 /* FIXME ... subs support */
475 function update_input_file()
477 mrl = document.getElementById( 'input_mrl' );
479 mrl.value = value( 'input_file_filename' );
482 /* update the input MRL using data from the input disc helper */
483 function update_input_disc()
485 mrl = document.getElementById( 'input_mrl' );
486 type = radio_value( "input_disc_type" );
487 device = value( "input_disc_dev" );
489 check_and_replace_int( 'input_disc_title', 0 );
490 check_and_replace_int( 'input_disc_chapter', 0 );
491 check_and_replace_int( 'input_disc_subtrack', '' );
492 check_and_replace_int( 'input_disc_audiotrack', 0 );
494 title = value( 'input_disc_title' );
495 chapter = value( 'input_disc_chapter' );
496 subs = value( 'input_disc_subtrack' );
497 audio = value( 'input_disc_audiotrack' );
503 mrl.value += "dvd://";
505 else if( type == "dvdsimple" )
507 mrl.value += "dvdsimple://";
509 else if( type == "vcd" )
511 mrl.value += "vcd://";
513 else if( type == "cdda" )
515 mrl.value += "cdda://";
522 mrl.value += "@"+title;
523 if( chapter && type != "cdda" )
524 mrl.value += ":"+chapter;
530 mrl.value += " :sub-track="+subs;
532 mrl.value += " :audio-track="+audio;
537 /* update the input MRL using data from the input network helper */
538 function update_input_net()
540 mrl = document.getElementById( 'input_mrl' );
541 type = radio_value( "input_net_type" );
543 check_and_replace_int( 'input_net_udp_port', 1234 );
544 check_and_replace_int( 'input_net_udpmcast_port', 1234 );
550 mrl.value += "udp://";
551 if( checked( 'input_net_udp_forceipv6' ) )
553 if( value( 'input_net_udp_port' ) )
554 mrl.value += ":"+value( 'input_net_udp_port' );
556 else if( type == "udpmcast" )
558 mrl.value += "udp://@"+value( 'input_net_udpmcast_address');
559 if( value( 'input_net_udpmcast_port' ) )
560 mrl.value += ":"+value( 'input_net_udpmcast_port' );
562 else if( type == "http" )
564 url = value( 'input_net_http_url' );
565 if( url.substring(0,7) != "http://"
566 && url.substring(0,8) != "https://"
567 && url.substring(0,6) != "ftp://"
568 && url.substring(0,6) != "mms://"
569 && url.substring(0,7) != "mmsh://" )
570 mrl.value += "http://";
573 else if( type == "rtsp" )
575 url = value( 'input_net_rtsp_url' );
576 if( url.substring(0,7) != "rtsp://" )
577 mrl.value += "rtsp://";
581 if( checked( "input_net_timeshift" ) )
582 mrl.value += " :access-filter=timeshift";
585 /**********************************************************************
586 * Sout dialog functions
587 *********************************************************************/
588 /* toggle show the full sout interface */
589 function toggle_show_sout_helper()
591 element = document.getElementById( "sout_helper" );
592 if( element.style.display == 'block' )
594 element.style.display = 'none';
595 document.getElementById( "sout_helper_toggle" ).value = 'Full sout interface';
599 element.style.display = 'block';
600 document.getElementById( "sout_helper_toggle" ).value = 'Hide sout interface';
604 /* update the sout MRL using data from the sout_helper */
605 function update_sout()
607 mrl = document.getElementById( 'sout_mrl' );
610 check_and_replace_int( 'sout_http_port', 8080 );
611 check_and_replace_int( 'sout_mmsh_port', 8080 );
612 check_and_replace_int( 'sout_rtp_port', 1234 );
613 check_and_replace_int( 'sout_udp_port', 1234 );
614 check_and_replace_int( 'sout_ttl', 1 );
616 if( checked( 'sout_soverlay' ) )
618 disable( 'sout_scodec' );
619 disable( 'sout_sub' );
623 enable( 'sout_scodec' );
624 enable( 'sout_sub' );
627 transcode = checked( 'sout_vcodec_s' ) || checked( 'sout_acodec_s' )
628 || checked( 'sout_sub' ) || checked( 'sout_soverlay' );
632 mrl.value += ":sout=#transcode{";
633 alot = false; /* alot == at least one transcode */
634 if( checked( 'sout_vcodec_s' ) )
636 mrl.value += "vcodec="+value( 'sout_vcodec' )+",vb="+value( 'sout_vb' )+",scale="+value( 'sout_scale' );
639 if( checked( 'sout_acodec_s' ) )
641 if( alot ) mrl.value += ",";
642 mrl.value += "acodec="+value( 'sout_acodec' )+",ab="+value( 'sout_ab' );
643 if( value( 'sout_channels' ) )
644 mrl.value += ",channels="+value( 'sout_channels' );
647 if( checked( 'sout_soverlay' ) )
649 if( alot ) mrl.value += ",";
650 mrl.value += "soverlay";
653 else if( checked( 'sout_sub' ) )
655 if( alot ) mrl.value += ",";
656 mrl.value += "scodec="+value( 'sout_scodec' );
662 output = checked( 'sout_display' ) + checked( 'sout_file' )
663 + checked( 'sout_http' ) + checked( 'sout_mmsh' )
664 + checked( 'sout_rtp' ) + checked( 'sout_udp' );
671 mrl.value += ":sout=#";
672 aloo = false; /* aloo == at least one output */
673 mux = radio_value( 'sout_mux' );
674 ttl = parseInt( value( 'sout_ttl' ) );
675 if( output > 1 ) mrl.value += "duplicate{";
676 if( checked( 'sout_display' ) )
678 if( output > 1 ) mrl.value += "dst="
679 mrl.value += "display";
682 if( checked( 'sout_file' ) )
684 if( aloo ) mrl.value += ",";
685 if( output > 1 ) mrl.value += "dst="
686 mrl.value += "std{access=file,mux="+mux+",dst="+value( 'sout_file_filename' )+"}";
689 if( checked( 'sout_http' ) )
691 if( aloo ) mrl.value += ",";
692 if( output > 1 ) mrl.value += "dst="
693 mrl.value += "std{access=http,mux="+mux+",dst="+value( 'sout_http_addr' );
694 if( value( 'sout_http_port' ) )
695 mrl.value += ":"+value( 'sout_http_port' );
699 if( checked( 'sout_mmsh' ) )
701 if( aloo ) mrl.value += ",";
702 if( output > 1 ) mrl.value += "dst="
703 mrl.value += "std{access=mmsh,mux="+mux+",dst="+value( 'sout_mmsh_addr' );
704 if( value( 'sout_mmsh_port' ) )
705 mrl.value += ":"+value( 'sout_mmsh_port' );
709 if( checked( 'sout_rtp' ) )
711 if( aloo ) mrl.value += ",";
712 if( output > 1 ) mrl.value += "dst="
713 mrl.value += "std{access=rtp";
714 if( ttl ) mrl.value += "{ttl="+ttl+"}";
715 mrl.value += ",mux="+mux+",dst="+value( 'sout_rtp_addr' );
716 if( value( 'sout_rtp_port' ) )
717 mrl.value += ":"+value( 'sout_rtp_port' );
718 if( checked( 'sout_sap' ) )
721 if( value( 'sout_sap_group' ) != '' )
723 mrl.value += ",group=\""+value( 'sout_sap_group' )+"\"";
725 mrl.value += ",name=\""+value( 'sout_sap_name' )+"\"";
730 if( checked( 'sout_udp' ) )
732 if( aloo ) mrl.value += ",";
733 if( output > 1 ) mrl.value += "dst="
734 mrl.value += "std{access=udp";
735 if( ttl ) mrl.value += "{ttl="+ttl+"}";
736 mrl.value += ",mux="+mux+",dst="+value( 'sout_udp_addr' );
737 if( value('sout_udp_port' ) )
738 mrl.value += ":"+value( 'sout_udp_port' );
739 if( checked( 'sout_sap' ) )
742 if( value( 'sout_sap_group' ) != '' )
744 mrl.value += ",group=\""+value( 'sout_sap_group' )+"\"";
746 mrl.value += ",name=\""+value( 'sout_sap_name' )+"\"";
751 if( output > 1 ) mrl.value += "}";
754 if( ( transcode || output ) && checked( 'sout_all' ) )
755 mrl.value += " :sout-all";
758 /* reset sout mrl value */
759 function reset_sout()
761 document.getElementById('sout_mrl').value = value('sout_old_mrl');
764 /* save sout mrl value */
767 document.getElementById('sout_old_mrl').value = value('sout_mrl');
770 /**********************************************************************
771 * Browser dialog functions
772 *********************************************************************/
773 /* only browse() should be called directly */
774 function browse( dest )
776 document.getElementById( 'browse_dest' ).value = dest;
777 browse_dir( document.getElementById( 'browse_lastdir' ).value );
780 function browse_dir( dir )
782 document.getElementById( 'browse_lastdir' ).value = dir;
783 loadXMLDoc( 'requests/browse.xml?dir='+dir, parse_browse_dir );
785 function browse_path( p )
787 document.getElementById( document.getElementById( 'browse_dest' ).value ).value = p;
789 document.getElementById( document.getElementById( 'browse_dest' ).value ).focus();
792 /**********************************************************************
793 * Periodically update stuff in the interface
794 *********************************************************************/
795 function loop_refresh_status()
797 setTimeout( 'loop_refresh_status()', 1000 );
800 function loop_refresh_playlist()
802 /* setTimeout( 'loop_refresh_playlist()', 10000 ); */
805 function loop_refresh()
807 setTimeout('loop_refresh_status()',0);
808 setTimeout('loop_refresh_playlist()',0);