main, functions.js, slider_*.png : add position slider
[vlc.git] / share / http / js / functions.js
1 /*****************************************************************************
2  * functions.js: VLC media player web interface
3  *****************************************************************************
4  * Copyright (C) 2005-2006 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Antoine Cellerier <dionoea -at- videolan -dot- org>
8  *
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.
13  *
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.
18  *
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  *****************************************************************************/
23
24 /* global variables */
25
26 var old_time = 0;
27
28 /**********************************************************************
29  * Slider functions
30  *********************************************************************/
31  
32 var slider_mouse_down = 0;
33 var slider_dx = 0;
34
35 /* findPosX() from http://www.quirksmode.rg/js/indpos.html */
36 function findPosX(obj)
37 {
38     var curleft = 0;
39     if (obj.offsetParent)
40     {
41         while (obj.offsetParent)
42         {
43             curleft += obj.offsetLeft
44             obj = obj.offsetParent;
45         }
46     }
47     else if (obj.x)
48         curleft += obj.x;
49     return curleft;
50 }
51
52 function slider_seek( e, bar )
53 {
54     seek(Math.floor(( e.clientX + document.body.scrollLeft - findPosX( bar )) / 4)+"%25");
55 }
56 function slider_down( e, point )
57 {
58     slider_mouse_down = 1;
59     slider_dx = e.clientX - findPosX( point );
60 }
61 function slider_up( e, bar )
62 {
63     slider_mouse_down = 0;
64     /* slider_seek( e, bar ); */
65 }
66 function slider_move( e, bar )
67 {
68     if( slider_mouse_down == 1 )
69     {
70         var slider_position  = Math.floor( e.clientX - slider_dx + document.body.scrollLeft - findPosX( bar ));
71         document.getElementById( 'main_slider_point' ).style.left = slider_position+"px";
72         slider_seek( e, bar );
73     }
74 }
75
76 /**********************************************************************
77  * Misc utils
78  *********************************************************************/
79
80 /* XMLHttpRequest wrapper */
81 function loadXMLDoc( url, callback )
82 {
83   // branch for native XMLHttpRequest object
84   if ( window.XMLHttpRequest )
85   {
86     req = new XMLHttpRequest();
87     req.onreadystatechange = callback;
88     req.open( "GET", url, true );
89     req.send( null );
90   // branch for IE/Windows ActiveX version
91   }
92   else if ( window.ActiveXObject )
93   {
94     req = new ActiveXObject( "Microsoft.XMLHTTP" );
95     if ( req )
96     {
97       req.onreadystatechange = callback;
98       req.open( "GET", url, true );
99       req.send();
100     }
101   }
102 }
103
104 /* fomat time in second as hh:mm:ss */
105 function format_time( s )
106 {
107     var hours = Math.floor(s/3600);
108     var minutes = Math.floor((s/60)%60);
109     var seconds = Math.floor(s%60);
110     if( hours < 10 ) hours = "0"+hours;
111     if( minutes < 10 ) minutes = "0"+minutes;
112     if( seconds < 10 ) seconds = "0"+seconds;
113     return hours+":"+minutes+":"+seconds;
114 }
115
116 /* delete all a tag's children and add a text child node */
117 function set_text( id, val )
118 {
119     var elt = document.getElementById( id );
120     while( elt.hasChildNodes() )
121         elt.removeChild( elt.firstChild );
122     elt.appendChild( document.createTextNode( val ) );
123 }
124
125 /* set item's 'element' attribute to value */
126 function set_css( item, element, value )
127 {
128     for( var j = 0; j < document.styleSheets.length; j++ )
129     {
130         cssRules = document.styleSheets[j].cssRules;
131         for( var i = 0; i < cssRules.length; i++)
132         {
133             if( cssRules[i].selectorText == item )
134             {
135                 cssRules[i].style.setProperty( element, value, null );
136                 return;
137             }
138         }
139     }
140 }
141
142 /* get item's 'element' attribute */
143 function get_css( item, element )
144 {
145     for( var j = 0; j < document.styleSheets.length; j++ )
146     {
147         cssRules = document.styleSheets[j].cssRules;
148         for( var i = 0; i < cssRules.length; i++)
149         {
150             if( cssRules[i].selectorText == item )
151             {
152                 return cssRules[i].style.getPropertyValue( element );
153             }
154         }
155     }
156 }
157
158 function toggle_show( id )
159 {
160     var element = document.getElementById( id );
161     if( element.style.display == 'block' || element.style.display == '' )
162     {
163         element.style.display = 'none';
164     }
165     else
166     {
167         element.style.display = 'block';
168     }
169 }
170 function toggle_show_node( id )
171 {
172     var element = document.getElementById( 'pl_'+id );
173     var img = document.getElementById( 'pl_img_'+id );
174     if( element.style.display == 'block' || element.style.display == '' )
175     {
176         element.style.display = 'none';
177         img.setAttribute( 'src', 'images/plus.png' );
178         img.setAttribute( 'alt', '[+]' );
179     }
180     else
181     {
182         element.style.display = 'block';
183         img.setAttribute( 'src', 'images/minus.png' );
184         img.setAttribute( 'alt', '[-]' );
185     }
186 }
187
188 function show( id ){ document.getElementById( id ).style.display = 'block'; }
189
190 function hide( id ){ document.getElementById( id ).style.display = 'none'; }
191
192 function checked( id ){ return document.getElementById( id ).checked; }
193
194 function value( id ){ return document.getElementById( id ).value; }
195
196 function radio_value( name )
197 {
198     var radio = document.getElementsByName( name );
199     for( var i = 0; i < radio.length; i++ )
200     {
201         if( radio[i].checked )
202         {
203             return radio[i].value;
204         }
205     }
206     return "";
207 }
208
209 function check_and_replace_int( id, val )
210 {
211     var objRegExp = /^\d\d*$/;
212     if( value( id ) != ''
213         && ( !objRegExp.test( value( id ) )
214              || parseInt( value( id ) ) < 1 ) )
215         document.getElementById( id ).value = val;
216 }
217
218 function addslashes( str ){ return str.replace(/\'/g, '\\\''); }
219
220 function disable( id ){ document.getElementById( id ).disabled = true; }
221
222 function enable( id ){ document.getElementById( id ).disabled = false; }
223
224 function button_over( element ){ element.style.border = "1px solid #000"; }
225
226 function button_out( element ){ element.style.border = "1px solid #fff"; }
227
228 /* toggle show help under the buttons */
229 function toggle_btn_text()
230 {
231     if( get_css( '.btn_text', 'display' ) == 'none' )
232     {
233         set_css( '.btn_text', 'display', 'block' );
234     }
235     else
236     {
237         set_css( '.btn_text', 'display', 'none' );
238     }
239 }
240
241 /**********************************************************************
242  * Interface actions
243  *********************************************************************/
244 /* input actions */
245 function in_play()
246 {
247     var input = value('input_mrl');
248     if( value('sout_mrl') != '' )
249         input += ' '+value('sout_mrl');
250     var url = 'requests/status.xml?command=in_play&input='+escape( input );
251     loadXMLDoc( url, parse_status );
252     setTimeout( 'update_playlist()', 1000 );
253 }
254 function in_enqueue()
255 {
256     var input = value('input_mrl');
257     if( value('sout_mrl') != '' )
258         input += ' '+value('sout_mrl');
259     var url = 'requests/status.xml?command=in_enqueue&input='+escape( input );
260     loadXMLDoc( url, parse_status );
261     setTimeout( 'update_playlist()', 1000 );
262 }
263
264 /* playlist actions */
265 function pl_play( id )
266 {
267     loadXMLDoc( 'requests/status.xml?command=pl_play&id='+id, parse_status );
268     setTimeout( 'update_playlist()', 1000 );
269 }
270 function pl_pause()
271 {
272     loadXMLDoc( 'requests/status.xml?command=pl_pause', parse_status );
273 }
274 function pl_stop()
275 {
276     loadXMLDoc( 'requests/status.xml?command=pl_stop', parse_status );
277     setTimeout( 'update_playlist()', 1000 );
278 }
279 function pl_next()
280 {
281     loadXMLDoc( 'requests/status.xml?command=pl_next', parse_status );
282     setTimeout( 'update_playlist()', 1000 );
283 }
284 function pl_previous()
285 {
286     loadXMLDoc( 'requests/status.xml?command=pl_previous', parse_status );
287     setTimeout( 'update_playlist()', 1000 );
288 }
289 function pl_delete( id )
290 {
291     loadXMLDoc( 'requests/status.xml?command=pl_delete&id='+id, parse_status );
292     setTimeout( 'update_playlist()', 1000 );
293 }
294 function pl_empty()
295 {
296     loadXMLDoc( 'requests/status.xml?command=pl_empty', parse_status );
297     setTimeout( 'update_playlist()', 1000 );
298 }
299 function pl_sort()
300 {
301     /* FIXME */
302     loadXMLDoc( 'requests/status.xml?command=pl_sort', parse_status );
303     setTimeout( 'update_playlist()', 1000 );
304 }
305 function pl_shuffle()
306 {
307     loadXMLDoc( 'requests/status.xml?command=pl_random', parse_status );
308     setTimeout( 'update_playlist()', 1000 );
309 }
310 function pl_loop()
311 {
312     loadXMLDoc( 'requests/status.xml?command=pl_loop', parse_status );
313 }
314 function pl_repeat()
315 {
316     loadXMLDoc( 'requests/status.xml?command=pl_repeat', parse_status );
317 }
318
319 /* misc actions */
320 function volume_down()
321 {
322     loadXMLDoc( 'requests/status.xml?command=volume&val=-20', parse_status );
323 }
324 function volume_up()
325 {
326     loadXMLDoc( 'requests/status.xml?command=volume&val=%2B20', parse_status );
327 }
328 function seek( pos )
329 {
330     loadXMLDoc( 'requests/status.xml?command=seek&val='+pos, parse_status );
331 }
332 function fullscreen()
333 {
334     loadXMLDoc( 'requests/status.xml?command=fullscreen', parse_status );
335 }
336 function update_status()
337 {
338     loadXMLDoc( 'requests/status.xml', parse_status );
339 }
340 function update_playlist()
341 {
342     loadXMLDoc( 'requests/playlist.xml', parse_playlist );
343 }
344
345 /**********************************************************************
346  * Parse xml replies to XMLHttpRequests
347  *********************************************************************/
348 /* parse request/status.xml */
349 function parse_status()
350 {
351     if( req.readyState == 4 )
352     {
353         if( req.status == 200 )
354         {
355             var status = req.responseXML.documentElement;
356             var new_time = status.getElementsByTagName( 'time' )[0].firstChild.data;
357             var length = status.getElementsByTagName( 'length' )[0].firstChild.data;
358             var slider_position;
359             if( length < 100 )
360             {
361                 slider_position = ( status.getElementsByTagName( 'position' )[0]
362                            .firstChild.data * 4 ) + "px";
363             }
364             else
365             {
366                 /* this is more precise if length > 100 */
367                 slider_position = Math.floor( ( new_time * 400 ) / length ) + "px";
368             }
369             if( old_time > new_time )
370                 setTimeout('update_playlist()',50);
371             old_time = new_time;
372             set_text( 'time', format_time( new_time ) );
373             set_text( 'length', format_time( length ) );
374             if( status.getElementsByTagName( 'volume' ).length != 0 )
375                 set_text( 'volume', Math.floor(status.getElementsByTagName( 'volume' )[0].firstChild.data/5.12)+'%' );
376             set_text( 'state', status.getElementsByTagName( 'state' )[0].firstChild.data );
377             if( slider_mouse_down == 0 )
378             {
379                 document.getElementById( 'main_slider_point' ).style.left = slider_position;
380             }
381             if( status.getElementsByTagName( 'state' )[0].firstChild.data == "playing" )
382             {
383                 document.getElementById( 'btn_pause_img' ).setAttribute( 'src', 'images/pause.png' );
384                 document.getElementById( 'btn_pause_img' ).setAttribute( 'alt', 'Pause' );
385                 document.getElementById( 'btn_pause' ).setAttribute( 'title', 'Pause' );
386             }
387             else
388             {
389                 document.getElementById( 'btn_pause_img' ).setAttribute( 'src', 'images/play.png' );
390                 document.getElementById( 'btn_pause_img' ).setAttribute( 'alt', 'Play' );
391                 document.getElementById( 'btn_pause' ).setAttribute( 'title', 'Play' );
392             }
393         }
394         else
395         {
396             /*alert( 'Error! HTTP server replied: ' + req.status );*/
397         }
398     }
399 }
400
401 /* parse playlist.xml */
402 function parse_playlist()
403 {
404     if( req.readyState == 4 )
405     {
406         if( req.status == 200 )
407         {
408             var answer = req.responseXML.documentElement;
409             var playtree = document.getElementById( 'playtree' );
410             var pos = document.createElement( "div" );
411             var pos_top = pos;
412             var elt = answer.firstChild;
413             while( elt )
414             {
415                 if( elt.nodeName == "node" )
416                 {
417                     if( pos.hasChildNodes() )
418                         pos.appendChild( document.createElement( "br" ) );
419                     var nda = document.createElement( 'a' );
420                     pos.appendChild( nda );
421                     nda.setAttribute( 'href', 'javascript:toggle_show_node(\''+elt.getAttribute( 'id' )+'\');' );
422                     var ndai = document.createElement( 'img' );
423                     nda.appendChild( ndai );
424                     ndai.setAttribute( 'src', 'images/minus.png' );
425                     ndai.setAttribute( 'alt', '[-]' );
426                     ndai.setAttribute( 'id', 'pl_img_'+elt.getAttribute( 'id' ) );
427                     pos.appendChild( document.createTextNode( ' ' + elt.getAttribute( 'name' ) ) );
428                     var nd = document.createElement( "div" );
429                     pos.appendChild( nd );
430                     nd.setAttribute( 'class', 'pl_node' );
431                     nd.setAttribute( 'id', 'pl_'+elt.getAttribute( 'id' ) );
432                 }
433                 else if( elt.nodeName == "leaf" )
434                 {
435                     if( pos.hasChildNodes() )
436                     pos.appendChild( document.createElement( "br" ) );
437                     var pl = document.createElement( "a" );
438                     pos.appendChild( pl );
439                     pl.setAttribute( 'class', 'pl_leaf' );
440                     pl.setAttribute( 'href', 'javascript:pl_play('+elt.getAttribute( 'id' )+');' );
441                     pl.setAttribute( 'id', 'pl_'+elt.getAttribute( 'id' ) );
442                     if( elt.getAttribute( 'current' ) == 'current' )
443                     {
444                         pl.setAttribute( 'style', 'font-weight: bold;' );
445                         document.getElementById( 'nowplaying' ).textContent
446                             = elt.getAttribute( 'name' );
447                     }
448                     pl.setAttribute( 'title', elt.getAttribute( 'uri' ));
449                     pl.appendChild( document.createTextNode( elt.getAttribute( 'name' ) ) );
450                     pos.appendChild( document.createTextNode( ' ' ) );
451                     var del = document.createElement( "a" );
452                     pos.appendChild( del );
453                     del.setAttribute( 'href', 'javascript:pl_delete('+elt.getAttribute( 'id' )+')' );
454                     del.appendChild( document.createElement( "img" ) );
455                     del = del.lastChild;
456                     del.setAttribute( 'src', 'images/delete_small.png' );
457                     del.setAttribute( 'alt', '(delete)' );
458                 }
459                 if( elt.firstChild )
460                 {
461                     elt = elt.firstChild;
462                     pos = pos.lastChild;
463                 }
464                 else if( elt.nextSibling )
465                 {
466                     elt = elt.nextSibling;
467                     pos = pos;
468                 }
469                 else
470                 {
471                     elt = elt.parentNode.nextSibling;
472                     pos = pos.parentNode;
473                 }
474             }
475             while( playtree.hasChildNodes() )
476                 playtree.removeChild( playtree.firstChild );
477             playtree.appendChild( pos_top );
478         }
479         else
480         {
481             /*alert( 'Error! HTTP server replied: ' + req.status );*/
482         }
483     }
484 }
485
486 /* parse browse.xml */
487 function parse_browse_dir( )
488 {
489     if( req.readyState == 4 )
490     {
491         if( req.status == 200 )
492         {
493             var answer = req.responseXML.documentElement;
494             var browser = document.getElementById( 'browser' );
495             var pos = document.createElement( "div" );
496             var elt = answer.firstChild;
497             while( elt )
498             {
499                 if( elt.nodeName == "element" )
500                 {
501                     pos.appendChild( document.createElement( "a" ) );
502                     pos.lastChild.setAttribute( 'class', 'browser' );
503                     if( elt.getAttribute( 'type' ) == 'directory' )
504                     {
505                         pos.lastChild.setAttribute( 'href', 'javascript:browse_dir(\''+addslashes(elt.getAttribute( 'path' ))+'\');');
506                     }
507                     else
508                     {
509                         pos.lastChild.setAttribute( 'href', 'javascript:browse_path(\''+addslashes(elt.getAttribute( 'path' ))+'\');' );
510                     }
511                     pos.lastChild.appendChild( document.createTextNode( elt.getAttribute( 'name' ) ) );
512                     if( elt.getAttribute( 'type' ) == 'directory' )
513                     {
514                         pos.appendChild( document.createTextNode( ' ' ) );
515                         pos.appendChild( document.createElement( "a" ) );
516                         pos.lastChild.setAttribute( 'class', 'browser' );
517                         pos.lastChild.setAttribute( 'href', 'javascript:browse_path(\''+addslashes(elt.getAttribute( 'path' ))+'\');');
518                         pos.lastChild.appendChild( document.createTextNode( '(select)' ) );
519                     }
520                     pos.appendChild( document.createElement( "br" ) );
521                 }
522                 elt = elt.nextSibling;
523             }
524             while( browser.hasChildNodes() )
525                 browser.removeChild( browser.firstChild );
526             browser.appendChild( pos );
527         }
528         else
529         {
530             /*alert( 'Error! HTTP server replied: ' + req.status );*/
531         }
532     }
533 }
534
535 /**********************************************************************
536  * Input dialog functions
537  *********************************************************************/
538 function hide_input( )
539 {
540     document.getElementById( 'input_file' ).style.display = 'none';
541     document.getElementById( 'input_disc' ).style.display = 'none';
542     document.getElementById( 'input_network' ).style.display = 'none';
543 }
544
545 /* update the input MRL using data from the input file helper */
546 /* FIXME ... subs support */
547 function update_input_file()
548 {
549     var mrl = document.getElementById( 'input_mrl' );
550
551     mrl.value = value( 'input_file_filename' );
552 }
553
554 /* update the input MRL using data from the input disc helper */
555 function update_input_disc()
556 {
557     var mrl = document.getElementById( 'input_mrl' );
558     var type = radio_value( "input_disc_type" );
559     var device = value( "input_disc_dev" );
560
561     check_and_replace_int( 'input_disc_title', 0 );
562     check_and_replace_int( 'input_disc_chapter', 0 );
563     check_and_replace_int( 'input_disc_subtrack', '' );
564     check_and_replace_int( 'input_disc_audiotrack', 0 );
565
566     var title = value( 'input_disc_title' );
567     var chapter = value( 'input_disc_chapter' );
568     var subs = value( 'input_disc_subtrack' );
569     var audio = value( 'input_disc_audiotrack' );
570
571     mrl.value = "";
572
573     if( type == "dvd" )
574     {
575         mrl.value += "dvd://";
576     }
577     else if( type == "dvdsimple" )
578     {
579         mrl.value += "dvdsimple://";
580     }
581     else if( type == "vcd" )
582     {
583         mrl.value += "vcd://";
584     }
585     else if( type == "cdda" )
586     {
587         mrl.value += "cdda://";
588     }
589
590     mrl.value += device;
591
592     if( title )
593     {
594         mrl.value += "@"+title;
595         if( chapter && type != "cdda" )
596             mrl.value += ":"+chapter;
597     }
598
599     if( type != "cdda" )
600     {
601         if( subs != '' )
602             mrl.value += " :sub-track="+subs;
603         if( audio != '' )
604             mrl.value += " :audio-track="+audio;
605     }
606
607 }
608
609 /* update the input MRL using data from the input network helper */
610 function update_input_net()
611 {
612     var mrl = document.getElementById( 'input_mrl' );
613     var type = radio_value( "input_net_type" );
614     
615     check_and_replace_int( 'input_net_udp_port', 1234 );
616     check_and_replace_int( 'input_net_udpmcast_port', 1234 );
617
618     mrl.value = "";
619
620     if( type == "udp" )
621     {
622         mrl.value += "udp://";
623         if( checked( 'input_net_udp_forceipv6' ) )
624             mrl.value += "[::]";
625         if( value( 'input_net_udp_port' ) )
626             mrl.value += ":"+value( 'input_net_udp_port' );
627     }
628     else if( type == "udpmcast" )
629     {
630         mrl.value += "udp://@"+value( 'input_net_udpmcast_address');
631         if( value( 'input_net_udpmcast_port' ) )
632             mrl.value += ":"+value( 'input_net_udpmcast_port' );
633     }
634     else if( type == "http" )
635     {
636         var url = value( 'input_net_http_url' );
637         if( url.substring(0,7) != "http://"
638             && url.substring(0,8) != "https://"
639             && url.substring(0,6) != "ftp://"
640             && url.substring(0,6) != "mms://"
641             && url.substring(0,7) != "mmsh://" )
642             mrl.value += "http://";
643         mrl.value += url;
644     }
645     else if( type == "rtsp" )
646     {
647         var url = value( 'input_net_rtsp_url' );
648         if( url.substring(0,7) != "rtsp://" )
649             mrl.value += "rtsp://";
650         mrl.value += url;
651     }
652
653     if( checked( "input_net_timeshift" ) )
654         mrl.value += " :access-filter=timeshift";
655 }
656
657 /**********************************************************************
658  * Sout dialog functions
659  *********************************************************************/
660 /* toggle show the full sout interface */
661 function toggle_show_sout_helper()
662 {
663     var element = document.getElementById( "sout_helper" );
664     if( element.style.display == 'block' )
665     {
666         element.style.display = 'none';
667         document.getElementById( "sout_helper_toggle" ).value = 'Full sout interface';
668     }
669     else
670     {
671         element.style.display = 'block';
672         document.getElementById( "sout_helper_toggle" ).value = 'Hide sout interface';
673     }
674 }
675
676 /* update the sout MRL using data from the sout_helper */
677 function update_sout()
678 {
679     var mrl = document.getElementById( 'sout_mrl' );
680     mrl.value = "";
681
682     check_and_replace_int( 'sout_http_port', 8080 );
683     check_and_replace_int( 'sout_mmsh_port', 8080 );
684     check_and_replace_int( 'sout_rtp_port', 1234 );
685     check_and_replace_int( 'sout_udp_port', 1234 );
686     check_and_replace_int( 'sout_ttl', 1 );
687
688     if( checked( 'sout_soverlay' ) )
689     {
690         disable( 'sout_scodec' );
691         disable( 'sout_sub' );
692     }
693     else
694     {
695         enable( 'sout_scodec' );
696         enable( 'sout_sub' );
697     }
698
699     var transcode =  checked( 'sout_vcodec_s' ) || checked( 'sout_acodec_s' )
700                   || checked( 'sout_sub' )      || checked( 'sout_soverlay' );
701
702     if( transcode )
703     {
704         mrl.value += ":sout=#transcode{";
705         var alot = false; /* alot == at least one transcode */
706         if( checked( 'sout_vcodec_s' ) )
707         {
708             mrl.value += "vcodec="+value( 'sout_vcodec' )+",vb="+value( 'sout_vb' )+",scale="+value( 'sout_scale' );
709             alot = true;
710         }
711         if( checked( 'sout_acodec_s' ) )
712         {
713             if( alot ) mrl.value += ",";
714             mrl.value += "acodec="+value( 'sout_acodec' )+",ab="+value( 'sout_ab' );
715             if( value( 'sout_channels' ) )
716                 mrl.value += ",channels="+value( 'sout_channels' );
717             alot = true;
718         }
719         if( checked( 'sout_soverlay' ) )
720         {
721             if( alot ) mrl.value += ",";
722             mrl.value += "soverlay";
723             alot = true;
724         }
725         else if( checked( 'sout_sub' ) )
726         {
727             if( alot ) mrl.value += ",";
728             mrl.value += "scodec="+value( 'sout_scodec' );
729             alot = true;
730         }
731         mrl.value += "}";
732     }
733
734     var output = checked( 'sout_display' ) + checked( 'sout_file' )
735                + checked( 'sout_http' )    + checked( 'sout_mmsh' )
736                + checked( 'sout_rtp' )     + checked( 'sout_udp' );
737
738     if( output )
739     {
740         if( transcode )
741             mrl.value += ":";
742         else
743             mrl.value += ":sout=#";
744         var aloo = false; /* aloo == at least one output */
745         var mux = radio_value( 'sout_mux' );
746         var ttl = parseInt( value( 'sout_ttl' ) );
747         if( output > 1 ) mrl.value += "duplicate{";
748         if( checked( 'sout_display' ) )
749         {
750             if( output > 1 ) mrl.value += "dst="
751             mrl.value += "display";
752             aloo = true;
753         }
754         if( checked( 'sout_file' ) )
755         {
756             if( aloo ) mrl.value += ",";
757             if( output > 1 ) mrl.value += "dst="
758             mrl.value += "std{access=file,mux="+mux+",dst="+value( 'sout_file_filename' )+"}";
759             aloo = true;
760         }
761         if( checked( 'sout_http' ) )
762         {
763             if( aloo ) mrl.value += ",";
764             if( output > 1 ) mrl.value += "dst="
765             mrl.value += "std{access=http,mux="+mux+",dst="+value( 'sout_http_addr' );
766             if( value( 'sout_http_port' ) )
767                 mrl.value += ":"+value( 'sout_http_port' );
768             mrl.value += "}";
769             aloo = true;
770         }
771         if( checked( 'sout_mmsh' ) )
772         {
773             if( aloo ) mrl.value += ",";
774             if( output > 1 ) mrl.value += "dst="
775             mrl.value += "std{access=mmsh,mux="+mux+",dst="+value( 'sout_mmsh_addr' );
776             if( value( 'sout_mmsh_port' ) )
777                 mrl.value += ":"+value( 'sout_mmsh_port' );
778             mrl.value += "}";
779             aloo = true;
780         }
781         if( checked( 'sout_rtp' ) )
782         {
783             if( aloo ) mrl.value += ",";
784             if( output > 1 ) mrl.value += "dst="
785             mrl.value += "std{access=rtp";
786             if( ttl ) mrl.value += "{ttl="+ttl+"}";
787             mrl.value += ",mux="+mux+",dst="+value( 'sout_rtp_addr' );
788             if( value( 'sout_rtp_port' ) )
789                 mrl.value += ":"+value( 'sout_rtp_port' );
790             if( checked( 'sout_sap' ) )
791             {
792                 mrl.value += ",sap";
793                 if( value( 'sout_sap_group' ) != '' )
794                 {
795                     mrl.value += ",group=\""+value( 'sout_sap_group' )+"\"";
796                 }
797                 mrl.value += ",name=\""+value( 'sout_sap_name' )+"\"";
798             }
799             mrl.value += "}";
800             aloo = true;
801         }
802         if( checked( 'sout_udp' ) )
803         {
804             if( aloo ) mrl.value += ",";
805             if( output > 1 ) mrl.value += "dst="
806             mrl.value += "std{access=udp";
807             if( ttl ) mrl.value += "{ttl="+ttl+"}";
808             mrl.value += ",mux="+mux+",dst="+value( 'sout_udp_addr' );
809             if( value('sout_udp_port' ) )
810                 mrl.value += ":"+value( 'sout_udp_port' );
811             if( checked( 'sout_sap' ) )
812             {
813                 mrl.value += ",sap";
814                 if( value( 'sout_sap_group' ) != '' )
815                 {
816                     mrl.value += ",group=\""+value( 'sout_sap_group' )+"\"";
817                 }
818                 mrl.value += ",name=\""+value( 'sout_sap_name' )+"\"";
819             }
820             mrl.value += "}";
821             aloo = true;
822         }
823         if( output > 1 ) mrl.value += "}";
824     }
825
826     if( ( transcode || output ) && checked( 'sout_all' ) )
827         mrl.value += " :sout-all";
828 }
829
830 /* reset sout mrl value */
831 function reset_sout()
832 {
833     document.getElementById('sout_mrl').value = value('sout_old_mrl');
834 }
835
836 /* save sout mrl value */
837 function save_sout()
838 {
839     document.getElementById('sout_old_mrl').value = value('sout_mrl');
840 }
841
842 /**********************************************************************
843  * Browser dialog functions
844  *********************************************************************/
845 /* only browse() should be called directly */
846 function browse( dest )
847 {
848     document.getElementById( 'browse_dest' ).value = dest;
849     browse_dir( document.getElementById( 'browse_lastdir' ).value );
850     show( 'browse' );
851 }
852 function browse_dir( dir )
853 {
854     document.getElementById( 'browse_lastdir' ).value = dir;
855     loadXMLDoc( 'requests/browse.xml?dir='+dir, parse_browse_dir );
856 }
857 function browse_path( p )
858 {
859     document.getElementById( value( 'browse_dest' ) ).value = p;
860     hide( 'browse' );
861     document.getElementById( value( 'browse_dest' ) ).focus();
862 }
863
864 /**********************************************************************
865  * Periodically update stuff in the interface
866  *********************************************************************/
867 function loop_refresh_status()
868 {
869     setTimeout( 'loop_refresh_status()', 1000 );
870     update_status();
871 }
872 function loop_refresh_playlist()
873 {
874     /* setTimeout( 'loop_refresh_playlist()', 10000 ); */
875     update_playlist();
876 }
877 function loop_refresh()
878 {
879     setTimeout( 'loop_refresh_status()', 1 );
880     setTimeout( 'loop_refresh_playlist()', 1 );
881 }
882