]> git.sesse.net Git - vlc/blob - share/http/js/functions.js
http-interface: - fixed large playlist loading failure
[vlc] / 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 /**********************************************************************
25  * Global variables
26  *********************************************************************/
27
28 var old_time = 0;
29 var pl_cur_id;
30 var albumart_id = -1;
31 var req = null;
32
33 /**********************************************************************
34  * Slider functions
35  *********************************************************************/
36  
37 var slider_mouse_down = 0;
38 var slider_dx = 0;
39
40 /* findPosX() from http://www.quirksmode.rg/js/indpos.html */
41 function findPosX(obj)
42 {
43     var curleft = 0;
44     if (obj.offsetParent)
45     {
46         while (obj.offsetParent)
47         {
48             curleft += obj.offsetLeft
49             obj = obj.offsetParent;
50         }
51     }
52     else if (obj.x)
53         curleft += obj.x;
54     return curleft;
55 }
56
57 function slider_seek( e, bar )
58 {
59     seek(Math.floor(( e.clientX + document.body.scrollLeft - findPosX( bar )) / 4)+"%25");
60 }
61 function slider_down( e, point )
62 {
63     slider_mouse_down = 1;
64     slider_dx = e.clientX - findPosX( point );
65 }
66 function slider_up( e, bar )
67 {
68     slider_mouse_down = 0;
69     /* slider_seek( e, bar ); */
70 }
71 function slider_move( e, bar )
72 {
73     if( slider_mouse_down == 1 )
74     {
75         var slider_position  = Math.floor( e.clientX - slider_dx + document.body.scrollLeft - findPosX( bar ));
76         document.getElementById( 'main_slider_point' ).style.left = slider_position+"px";
77         slider_seek( e, bar );
78     }
79 }
80
81 /**********************************************************************
82  * Misc utils
83  *********************************************************************/
84
85 /* XMLHttpRequest wrapper */
86 function loadXMLDoc( url, callback )
87 {
88   // branch for native XMLHttpRequest object
89   if ( window.XMLHttpRequest )
90   {
91     req = new XMLHttpRequest();
92     req.onreadystatechange = callback;
93     req.open( "GET", url, true );
94     req.send( null );
95   // branch for IE/Windows ActiveX version
96   }
97   else if ( window.ActiveXObject )
98   {
99     req = new ActiveXObject( "Microsoft.XMLHTTP" );
100     if ( req )
101     {
102       req.onreadystatechange = callback;
103       req.open( "GET", url, true );
104       req.send();
105     }
106   }
107 }
108
109 /* fomat time in second as hh:mm:ss */
110 function format_time( s )
111 {
112     var hours = Math.floor(s/3600);
113     var minutes = Math.floor((s/60)%60);
114     var seconds = Math.floor(s%60);
115     if( hours < 10 ) hours = "0"+hours;
116     if( minutes < 10 ) minutes = "0"+minutes;
117     if( seconds < 10 ) seconds = "0"+seconds;
118     return hours+":"+minutes+":"+seconds;
119 }
120
121 /* delete all a tag's children and add a text child node */
122 function set_text( id, val )
123 {
124     var elt = document.getElementById( id );
125     while( elt.hasChildNodes() )
126         elt.removeChild( elt.firstChild );
127     elt.appendChild( document.createTextNode( val ) );
128 }
129
130 /* set item's 'element' attribute to value */
131 function set_css( item, element, value )
132 {
133     for( var j = 0; j < document.styleSheets.length; j++ )
134     {
135         var cssRules = document.styleSheets[j].cssRules;
136         if( !cssRules ) cssRules = document.styleSheets[j].rules;
137         for( var i = 0; i < cssRules.length; i++)
138         {
139             if( cssRules[i].selectorText == item )
140             {
141                 if( cssRules[i].style.setProperty )
142                     cssRules[i].style.setProperty( element, value, null );
143                 else
144                     cssRules[i].style.setAttribute( toCamelCase( element ), value );
145                 return;
146             }
147         }
148     }
149 }
150
151 /* get item's 'element' attribute */
152 function get_css( item, element )
153 {
154     for( var j = 0; j < document.styleSheets.length; j++ )
155     {
156         var cssRules = document.styleSheets[j].cssRules;
157         if( !cssRules ) cssRules = document.styleSheets[j].rules;
158         for( var i = 0; i < cssRules.length; i++)
159         {
160             if( cssRules[i].selectorText == item )
161             {
162                 if( cssRules[i].style.getPropertyValue )
163                     return cssRules[i].style.getPropertyValue( element );
164                 else
165                     return cssRules[i].style.getAttribute( toCamelCase( element ) );
166             }
167         }
168     }
169 }
170
171 function toggle_show( id )
172 {
173     var element = document.getElementById( id );
174     if( element.style.display == 'block' || element.style.display == '' )
175     {
176         element.style.display = 'none';
177     }
178     else
179     {
180         element.style.display = 'block';
181     }
182 }
183 function toggle_show_node( id )
184 {
185     var element = document.getElementById( 'pl_'+id );
186     var img = document.getElementById( 'pl_img_'+id );
187     if( element.style.display == 'block' || element.style.display == '' )
188     {
189         element.style.display = 'none';
190         img.setAttribute( 'src', 'images/plus.png' );
191         img.setAttribute( 'alt', '[+]' );
192     }
193     else
194     {
195         element.style.display = 'block';
196         img.setAttribute( 'src', 'images/minus.png' );
197         img.setAttribute( 'alt', '[-]' );
198     }
199 }
200
201 function show( id ){ document.getElementById( id ).style.display = 'block'; }
202 function showinline( id ){ document.getElementById( id ).style.display = 'inline'; }
203
204 function hide( id ){ document.getElementById( id ).style.display = 'none'; }
205
206 function checked( id ){ return document.getElementById( id ).checked; }
207
208 function value( id ){ return document.getElementById( id ).value; }
209
210 function setclass( obj, value )
211 {
212     obj.setAttribute( 'class', value ); /* Firefox */
213     obj.setAttribute( 'className', value ); /* IE */
214 }
215
216 function radio_value( name )
217 {
218     var radio = document.getElementsByName( name );
219     for( var i = 0; i < radio.length; i++ )
220     {
221         if( radio[i].checked )
222         {
223             return radio[i].value;
224         }
225     }
226     return "";
227 }
228
229 function check_and_replace_int( id, val )
230 {
231     var objRegExp = /^\d+$/;
232     if( value( id ) != ''
233         && ( !objRegExp.test( value( id ) )
234              || parseInt( value( id ) ) < 1 ) )
235         return document.getElementById( id ).value = val;
236     return document.getElementById( id ).value;
237 }
238
239 function addslashes( str ){ return str.replace(/\'/g, '\\\''); }
240 function escapebackslashes( str ){ return str.replace(/\\/g, '\\\\'); }
241
242 function toCamelCase( str )
243 {
244     str = str.split( '-' );
245     var cml = str[0];
246     for( var i=1; i<str.length; i++)
247         cml += str[i].charAt(0).toUpperCase()+str[i].substring(1);
248     return cml;
249 }
250
251 function disable( id ){ document.getElementById( id ).disabled = true; }
252
253 function enable( id ){ document.getElementById( id ).disabled = false; }
254
255 function button_over( element ){ element.style.border = "1px solid #000"; }
256
257 function button_out( element ){ element.style.border = "1px solid #fff"; }
258 function button_out_menu( element ){ element.style.border = "1px solid transparent"; }
259
260 function show_menu( id ){ document.getElementById(id).style.display = 'block'; }
261 function hide_menu( id ){ document.getElementById(id).style.display = 'none'; }
262
263 /* toggle show help under the buttons */
264 function toggle_btn_text()
265 {
266     if( get_css( '.btn_text', 'display' ) == 'none' )
267     {
268         set_css( '.btn_text', 'display', 'block' );
269     }
270     else
271     {
272         set_css( '.btn_text', 'display', 'none' );
273     }
274 }
275
276 function clear_children( elt )
277 {   
278     if( elt )
279         while( elt.hasChildNodes() )
280             elt.removeChild( elt.firstChild );
281 }
282
283 /**********************************************************************
284  * Interface actions
285  *********************************************************************/
286 /* input actions */
287 function in_play()
288 {
289     var input = value('input_mrl');
290     if( value('sout_mrl') != '' )
291         input += ' '+value('sout_mrl');
292     var url = 'requests/status.xml?command=in_play&input='+encodeURIComponent( addslashes(escapebackslashes(input)) );
293     loadXMLDoc( url, parse_status );
294     setTimeout( 'update_playlist()', 1000 );
295 }
296 function in_enqueue()
297 {
298     var input = value('input_mrl');
299     if( value('sout_mrl') != '' )
300         input += ' '+value('sout_mrl');
301     var url = 'requests/status.xml?command=in_enqueue&input='+encodeURIComponent( addslashes(escapebackslashes(input)) );
302     loadXMLDoc( url, parse_status );
303     setTimeout( 'update_playlist()', 1000 );
304 }
305
306 /* playlist actions */
307 function pl_play( id )
308 {
309     loadXMLDoc( 'requests/status.xml?command=pl_play&id='+id, parse_status );
310     pl_cur_id = id;
311     setTimeout( 'update_playlist()', 1000 );
312 }
313 function pl_pause()
314 {
315     loadXMLDoc( 'requests/status.xml?command=pl_pause&id='+pl_cur_id, parse_status );
316 }
317 function pl_stop()
318 {
319     loadXMLDoc( 'requests/status.xml?command=pl_stop', parse_status );
320     setTimeout( 'update_playlist()', 1000 );
321 }
322 function pl_next()
323 {
324     loadXMLDoc( 'requests/status.xml?command=pl_next', parse_status );
325     setTimeout( 'update_playlist()', 1000 );
326 }
327 function pl_previous()
328 {
329     loadXMLDoc( 'requests/status.xml?command=pl_previous', parse_status );
330     setTimeout( 'update_playlist()', 1000 );
331 }
332 function pl_delete( id )
333 {
334     loadXMLDoc( 'requests/status.xml?command=pl_delete&id='+id, parse_status );
335     setTimeout( 'update_playlist()', 1000 );
336 }
337 function pl_empty()
338 {
339     loadXMLDoc( 'requests/status.xml?command=pl_empty', parse_status );
340     setTimeout( 'update_playlist()', 1000 );
341 }
342 function pl_sort( sort, order )
343 {
344     loadXMLDoc( 'requests/status.xml?command=pl_sort&id='+order+'&val='+sort, parse_status );
345     setTimeout( 'update_playlist()', 1000 );
346 }
347 function pl_shuffle()
348 {
349     loadXMLDoc( 'requests/status.xml?command=pl_random', parse_status );
350     setTimeout( 'update_playlist()', 1000 );
351 }
352 function pl_loop()
353 {
354     loadXMLDoc( 'requests/status.xml?command=pl_loop', parse_status );
355 }
356 function pl_repeat()
357 {
358     loadXMLDoc( 'requests/status.xml?command=pl_repeat', parse_status );
359 }
360 function pl_sd( value )
361 {
362     loadXMLDoc( 'requests/status.xml?command=pl_sd&val='+value, parse_status );
363 }
364
365 /* misc actions */
366 function volume_down()
367 {
368     loadXMLDoc( 'requests/status.xml?command=volume&val=-20', parse_status );
369 }
370 function volume_up()
371 {
372     loadXMLDoc( 'requests/status.xml?command=volume&val=%2B20', parse_status );
373 }
374 function seek( pos )
375 {
376     loadXMLDoc( 'requests/status.xml?command=seek&val='+pos, parse_status );
377 }
378 function fullscreen()
379 {
380     loadXMLDoc( 'requests/status.xml?command=fullscreen', parse_status );
381 }
382 function snapshot()
383 {
384     loadXMLDoc( 'requests/status.xml?command=snapshot', parse_status );
385 }
386 function hotkey( str )
387 {
388     /* Use hotkey name (without the "key-" part) as the argument to simulate a hotkey press */
389     loadXMLDoc( 'requests/status.xml?command=key&val='+str, parse_status );
390 }
391 function update_status()
392 {
393     if( req == null || req.readyState == 0 || req.readyState == 4 )
394     {
395         loadXMLDoc( 'requests/status.xml', parse_status );
396     }
397 }
398 function update_playlist()
399 {
400     loadXMLDoc( 'requests/playlist.xml', parse_playlist );
401 }
402
403 /**********************************************************************
404  * Parse xml replies to XMLHttpRequests
405  *********************************************************************/
406 /* parse request/status.xml */
407 function parse_status()
408 {
409     if( req.readyState == 4 )
410     {
411         if( req.status == 200 )
412         {
413             var status = req.responseXML.documentElement;
414             var timetag = status.getElementsByTagName( 'time' );
415             if( timetag.length > 0 )
416             {
417                 var new_time = timetag[0].firstChild.data;
418             }
419             else
420             {
421                 new_time = old_time;
422             }
423             var lengthtag = status.getElementsByTagName( 'length' );
424             var length;
425             if( lengthtag.length > 0 )
426             {
427                 length = lengthtag[0].firstChild.data;
428             }
429             else
430             {
431                 length = 0;
432             }
433             var slider_position;
434             positiontag = status.getElementsByTagName( 'position' );
435             if( length < 100 && positiontag.length > 0 )
436             {
437                 slider_position = ( positiontag[0].firstChild.data * 4 ) + "px";
438             }
439             else if( length > 0 )
440             {
441                 /* this is more precise if length > 100 */
442                 slider_position = Math.floor( ( new_time * 400 ) / length ) + "px";
443             }
444             else
445             {
446                 slider_position = 0;
447             }
448             if( old_time > new_time )
449                 setTimeout('update_playlist()',50);
450             old_time = new_time;
451             set_text( 'time', format_time( new_time ) );
452             set_text( 'length', format_time( length ) );
453             if( status.getElementsByTagName( 'volume' ).length != 0 )
454                 set_text( 'volume', Math.floor(status.getElementsByTagName( 'volume' )[0].firstChild.data/5.12)+'%' );
455             var statetag = status.getElementsByTagName( 'state' );
456             if( statetag.length > 0 )
457             {
458                 set_text( 'state', statetag[0].firstChild.data );
459             }
460             else
461             {
462                 set_text( 'state', '(?)' );
463             }
464             if( slider_mouse_down == 0 )
465             {
466                 document.getElementById( 'main_slider_point' ).style.left = slider_position;
467             }
468             var statustag = status.getElementsByTagName( 'state' );
469             if( statustag.length > 0 ? statustag[0].firstChild.data == "playing" : 0 )
470             {
471                 document.getElementById( 'btn_pause_img' ).setAttribute( 'src', 'images/pause.png' );
472                 document.getElementById( 'btn_pause_img' ).setAttribute( 'alt', 'Pause' );
473                 document.getElementById( 'btn_pause' ).setAttribute( 'title', 'Pause' );
474             }
475             else
476             {
477                 document.getElementById( 'btn_pause_img' ).setAttribute( 'src', 'images/play.png' );
478                 document.getElementById( 'btn_pause_img' ).setAttribute( 'alt', 'Play' );
479                 document.getElementById( 'btn_pause' ).setAttribute( 'title', 'Play' );
480             }
481
482             var randomtag = status.getElementsByTagName( 'random' );
483             if( randomtag.length > 0 ? randomtag[0].firstChild.data == "1" : 0)
484                 setclass( document.getElementById( 'btn_shuffle'), 'on' );
485             else
486                 setclass( document.getElementById( 'btn_shuffle'), 'off' );
487                
488             var looptag = status.getElementsByTagName( 'loop' );
489             if( looptag.length > 0 ? looptag[0].firstChild.data == "1" : 0)
490                 setclass( document.getElementById( 'btn_loop'), 'on' );
491             else
492                 setclass( document.getElementById( 'btn_loop'), 'off' );
493
494             var repeattag = status.getElementsByTagName( 'repeat' );
495             if( repeattag.length > 0 ? repeattag[0].firstChild.data == "1" : 0 )
496                 setclass( document.getElementById( 'btn_repeat'), 'on' );
497             else
498                 setclass( document.getElementById( 'btn_repeat'), 'off' );
499
500             var tree = document.createElement( "ul" );
501             var categories = status.getElementsByTagName( 'category' );
502             var i;
503             for( i = 0; i < categories.length; i++ )
504             {
505                 var item = document.createElement( "li" );
506                 item.appendChild( document.createTextNode( categories[i].getAttribute( 'name' ) ) );
507                 var subtree = document.createElement( "dl" );
508                 var infos = categories[i].getElementsByTagName( 'info' );
509                 var j;
510                 for( j = 0; j < infos.length; j++ )
511                 {
512                     var subitem = document.createElement( "dt" );
513                     subitem.appendChild( document.createTextNode( infos[j].getAttribute( 'name' ) ) );
514                     subtree.appendChild( subitem );
515                     if( infos[j].hasChildNodes() )
516                     {
517                         var subitem = document.createElement( "dd" );
518                         subitem.appendChild( document.createTextNode( infos[j].firstChild.data ) );
519                         subtree.appendChild( subitem );
520                     }
521                 }
522                 item.appendChild( subtree );
523                 tree.appendChild( item );
524             }
525             var infotree = document.getElementById('infotree' );
526             clear_children( infotree );
527             infotree.appendChild( tree );
528             
529         }
530         else
531         {
532             /*alert( 'Error! HTTP server replied: ' + req.status );*/
533         }
534     }
535 }
536
537 /* parse playlist.xml */
538 function parse_playlist()
539 {
540     if( req.readyState == 4 )
541     {
542         if( req.status == 200 )
543         {
544             var answer = req.responseXML.documentElement;
545             var playtree = document.getElementById( 'playtree' );
546             var pos = document.createElement( "div" );
547             pos.style.height = document.body.clientHeight - 100 + "px";
548             pos.style.overflow = "auto";
549             var pos_top = pos;
550             var elt = answer.firstChild;
551             
552             pl_cur_id = 0;  /* changed to the current id is there actually
553                              * is a current id */
554             while( elt )
555             {
556                 if( elt.nodeName == "node" )
557                 {
558                     if( pos.hasChildNodes() )
559                         pos.appendChild( document.createElement( "br" ) );
560                     var nda = document.createElement( 'a' );
561                     nda.setAttribute( 'href', 'javascript:toggle_show_node(\''+elt.getAttribute( 'id' )+'\');' );
562                     var ndai = document.createElement( 'img' );
563                     ndai.setAttribute( 'src', 'images/minus.png' );
564                     ndai.setAttribute( 'alt', '[-]' );
565                     ndai.setAttribute( 'id', 'pl_img_'+elt.getAttribute( 'id' ) );
566                     nda.appendChild( ndai );
567                     pos.appendChild( nda );
568                     pos.appendChild( document.createTextNode( ' ' + elt.getAttribute( 'name' ) ) );
569
570                     if( elt.getAttribute( 'ro' ) == 'rw' )
571                     {
572                         pos.appendChild( document.createTextNode( ' ' ) );
573                         var del = document.createElement( "a" );
574                         del.setAttribute( 'href', 'javascript:pl_delete('+elt.getAttribute( 'id' )+')' );
575                             var delimg = document.createElement( "img" );
576                             delimg.setAttribute( 'src', 'images/delete_small.png' );
577                             delimg.setAttribute( 'alt', '(delete)' );
578                         del.appendChild( delimg );
579                         pos.appendChild( del );
580                     }
581
582                     var nd = document.createElement( "div" );
583                     setclass( nd, 'pl_node' );
584                     nd.setAttribute( 'id', 'pl_'+elt.getAttribute( 'id' ) );
585                     pos.appendChild( nd );
586                 }
587                 else if( elt.nodeName == "leaf" )
588                 {
589                     if( pos.hasChildNodes() )
590                     pos.appendChild( document.createElement( "br" ) );
591                     var pl = document.createElement( "a" );
592                     setclass( pl, 'pl_leaf' );
593                     pl.setAttribute( 'href', 'javascript:pl_play('+elt.getAttribute( 'id' )+');' );
594                     pl.setAttribute( 'id', 'pl_'+elt.getAttribute( 'id' ) );
595                     if( elt.getAttribute( 'current' ) == 'current' )
596                     {
597                         pl.style.fontWeight = 'bold';
598                         var nowplaying = document.getElementById( 'nowplaying' );
599                         clear_children( nowplaying );
600                         nowplaying.appendChild( document.createTextNode( elt.getAttribute( 'name' ) ) );
601                         pl.appendChild( document.createTextNode( '* '));
602                         pl_cur_id = elt.getAttribute( 'id' );
603                     }
604                     pl.setAttribute( 'title', elt.getAttribute( 'uri' ));
605                     pl.appendChild( document.createTextNode( elt.getAttribute( 'name' ) ) );
606                     var duration = elt.getAttribute( 'duration' );
607                     if( duration > 0 )
608                         pl.appendChild( document.createTextNode( " (" + format_time( elt.getAttribute( 'duration' ) / 1000000 ) + ")" ) );
609                     pos.appendChild( pl );
610
611                     if( elt.getAttribute( 'ro' ) == 'rw' )
612                     {
613                         pos.appendChild( document.createTextNode( ' ' ) );
614                         var del = document.createElement( "a" );
615                         del.setAttribute( 'href', 'javascript:pl_delete('+elt.getAttribute( 'id' )+')' );
616                             var delimg = document.createElement( "img" );
617                             delimg.setAttribute( 'src', 'images/delete_small.png' );
618                             delimg.setAttribute( 'alt', '(delete)' );
619                         del.appendChild( delimg );
620                         pos.appendChild( del );
621                     }
622                 }
623                 if( elt.firstChild )
624                 {
625                     elt = elt.firstChild;
626                     pos = pos.lastChild;
627                 }
628                 else if( elt.nextSibling )
629                 {
630                     elt = elt.nextSibling;
631                     pos = pos;
632                 }
633                 else
634                 {
635                     while( ! elt.parentNode.nextSibling )
636                     {
637                         elt = elt.parentNode;
638                         if( ! elt.parentNode ) break;
639                         pos = pos.parentNode;
640                     }
641                     if( ! elt.parentNode ) break;
642                     elt = elt.parentNode.nextSibling;
643                     pos = pos.parentNode;
644                 }
645             }
646             clear_children( playtree );
647             playtree.appendChild( pos_top );
648         }
649         else
650         {
651             /*alert( 'Error! HTTP server replied: ' + req.status );*/
652         }
653     }
654 }
655
656 /* parse browse.xml */
657 function parse_browse_dir( )
658 {
659     if( req.readyState == 4 )
660     {
661         if( req.status == 200 )
662         {
663             var answer = req.responseXML.documentElement;
664             if( !answer ) return;
665             var browser = document.getElementById( 'browser' );
666             var pos = document.createElement( "div" );
667             var elt = answer.firstChild;
668             while( elt )
669             {
670                 if( elt.nodeName == "element" )
671                 {
672                     var item = document.createElement( "a" );
673                     setclass( item, 'browser' );
674                     if( elt.getAttribute( 'type' ) == 'directory' )
675                     {
676                         item.setAttribute( 'href', 'javascript:browse_dir(\''+addslashes(escapebackslashes(elt.getAttribute( 'path' )))+'\');');
677                     }
678                     else
679                     {
680                         item.setAttribute( 'href', 'javascript:browse_path(\''+addslashes(escapebackslashes(elt.getAttribute( 'path' )))+'\');' );
681                     }
682                     item.appendChild( document.createTextNode( elt.getAttribute( 'name' ) ) );
683                     pos.appendChild( item );
684                     if( elt.getAttribute( 'type' ) == 'directory' )
685                     {
686                         pos.appendChild( document.createTextNode( ' ' ) );
687                         var item = document.createElement( "a" );
688                         setclass( item, 'browser' );
689                         item.setAttribute( 'href', 'javascript:browse_path(\''+addslashes(escapebackslashes(elt.getAttribute( 'path' )))+'\');');
690                         item.appendChild( document.createTextNode( '(select)' ) );
691                         pos.appendChild( item );
692                     }
693                     pos.appendChild( document.createElement( "br" ) );
694                 }
695                 elt = elt.nextSibling;
696             }
697             clear_children( browser );
698             browser.appendChild( pos );
699         }
700         else
701         {
702             /*alert( 'Error! HTTP server replied: ' + req.status );*/
703         }
704     }
705 }
706
707 /**********************************************************************
708  * Input dialog functions
709  *********************************************************************/
710 function hide_input( )
711 {
712     document.getElementById( 'input_file' ).style.display = 'none';
713     document.getElementById( 'input_disc' ).style.display = 'none';
714     document.getElementById( 'input_network' ).style.display = 'none';
715     document.getElementById( 'input_fake' ).style.display = 'none';
716 }
717
718 /* update the input MRL using data from the input file helper */
719 /* FIXME ... subs support */
720 function update_input_file()
721 {
722     var mrl = document.getElementById( 'input_mrl' );
723
724     mrl.value = value( 'input_file_filename' );
725 }
726
727 /* update the input MRL using data from the input disc helper */
728 function update_input_disc()
729 {
730     var mrl     = document.getElementById( 'input_mrl' );
731     var type    = radio_value( "input_disc_type" );
732     var device  = value( "input_disc_dev" );
733
734     var title   = check_and_replace_int( 'input_disc_title', 0 );
735     var chapter = check_and_replace_int( 'input_disc_chapter', 0 );
736     var subs    = check_and_replace_int( 'input_disc_subtrack', '' );
737     var audio   = check_and_replace_int( 'input_disc_audiotrack', 0 );
738
739     mrl.value = "";
740
741     if( type == "dvd" )
742     {
743         mrl.value += "dvd://";
744     }
745     else if( type == "dvdsimple" )
746     {
747         mrl.value += "dvdsimple://";
748     }
749     else if( type == "vcd" )
750     {
751         mrl.value += "vcd://";
752     }
753     else if( type == "cdda" )
754     {
755         mrl.value += "cdda://";
756     }
757
758     mrl.value += device;
759
760     if( title )
761     {
762         mrl.value += "@"+title;
763         if( chapter && type != "cdda" )
764             mrl.value += ":"+chapter;
765     }
766
767     if( type != "cdda" )
768     {
769         if( subs != '' )
770             mrl.value += " :sub-track="+subs;
771         if( audio != '' )
772             mrl.value += " :audio-track="+audio;
773     }
774
775 }
776
777 /* update the input MRL using data from the input network helper */
778 function update_input_net()
779 {
780     var mrl = document.getElementById( 'input_mrl' );
781     var type = radio_value( "input_net_type" );
782     
783     check_and_replace_int( 'input_net_udp_port', 1234 );
784     check_and_replace_int( 'input_net_udpmcast_port', 1234 );
785
786     mrl.value = "";
787
788     if( type == "udp" )
789     {
790         mrl.value += "udp://";
791         if( checked( 'input_net_udp_forceipv6' ) )
792             mrl.value += "[::]";
793         if( value( 'input_net_udp_port' ) )
794             mrl.value += ":"+value( 'input_net_udp_port' );
795     }
796     else if( type == "udpmcast" )
797     {
798         mrl.value += "udp://@"+value( 'input_net_udpmcast_address');
799         if( value( 'input_net_udpmcast_port' ) )
800             mrl.value += ":"+value( 'input_net_udpmcast_port' );
801     }
802     else if( type == "http" )
803     {
804         var url = value( 'input_net_http_url' );
805         if( url.substring(0,7) != "http://"
806             && url.substring(0,8) != "https://"
807             && url.substring(0,6) != "ftp://"
808             && url.substring(0,6) != "mms://"
809             && url.substring(0,7) != "mmsh://" )
810             mrl.value += "http://";
811         mrl.value += url;
812     }
813     else if( type == "rtsp" )
814     {
815         var url = value( 'input_net_rtsp_url' );
816         if( url.substring(0,7) != "rtsp://" )
817             mrl.value += "rtsp://";
818         mrl.value += url;
819     }
820
821     if( checked( "input_net_timeshift" ) )
822         mrl.value += " :access-filter=timeshift";
823 }
824
825 /* update the input MRL using data from the input fake helper */
826 function update_input_fake()
827 {
828     var mrl = document.getElementById( 'input_mrl' );
829
830     mrl.value = "fake://";
831     mrl.value += " :fake-file=" + value( "input_fake_filename" );
832
833     if( value( "input_fake_width" ) )
834         mrl.value += " :fake-width=" + value( "input_fake_width" );
835     if( value( "input_fake_height" ) )
836         mrl.value += " :fake-height=" + value( "input_fake_height" );
837     if( value( "input_fake_ar" ) )
838         mrl.value += " :fake-ar=" + value( "input_fake_ar" );
839 }
840
841 /**********************************************************************
842  * Sout dialog functions
843  *********************************************************************/
844 /* toggle show the full sout interface */
845 function toggle_show_sout_helper()
846 {
847     var element = document.getElementById( "sout_helper" );
848     if( element.style.display == 'block' )
849     {
850         element.style.display = 'none';
851         document.getElementById( "sout_helper_toggle" ).value = 'Full sout interface';
852     }
853     else
854     {
855         element.style.display = 'block';
856         document.getElementById( "sout_helper_toggle" ).value = 'Hide sout interface';
857     }
858 }
859
860 /* update the sout MRL using data from the sout_helper */
861 function update_sout()
862 {
863     var mrl = document.getElementById( 'sout_mrl' );
864     mrl.value = "";
865
866     check_and_replace_int( 'sout_http_port', 8080 );
867     check_and_replace_int( 'sout_mmsh_port', 8080 );
868     check_and_replace_int( 'sout_rtp_port', 1234 );
869     check_and_replace_int( 'sout_udp_port', 1234 );
870     check_and_replace_int( 'sout_ttl', 1 );
871
872     if( checked( 'sout_soverlay' ) )
873     {
874         disable( 'sout_scodec' );
875         disable( 'sout_sub' );
876     }
877     else
878     {
879         enable( 'sout_scodec' );
880         enable( 'sout_sub' );
881     }
882
883     var transcode =  checked( 'sout_vcodec_s' ) || checked( 'sout_acodec_s' )
884                   || checked( 'sout_sub' )      || checked( 'sout_soverlay' );
885
886     if( transcode )
887     {
888         mrl.value += ":sout=#transcode{";
889         var alot = false; /* alot == at least one transcode */
890         if( checked( 'sout_vcodec_s' ) )
891         {
892             mrl.value += "vcodec="+value( 'sout_vcodec' )+",vb="+value( 'sout_vb' )+",scale="+value( 'sout_scale' );
893             alot = true;
894         }
895         if( checked( 'sout_acodec_s' ) )
896         {
897             if( alot ) mrl.value += ",";
898             mrl.value += "acodec="+value( 'sout_acodec' )+",ab="+value( 'sout_ab' );
899             if( value( 'sout_channels' ) )
900                 mrl.value += ",channels="+value( 'sout_channels' );
901             alot = true;
902         }
903         if( checked( 'sout_soverlay' ) )
904         {
905             if( alot ) mrl.value += ",";
906             mrl.value += "soverlay";
907             alot = true;
908         }
909         else if( checked( 'sout_sub' ) )
910         {
911             if( alot ) mrl.value += ",";
912             mrl.value += "scodec="+value( 'sout_scodec' );
913             alot = true;
914         }
915         mrl.value += value( 'sout_transcode_extra' );
916             
917         mrl.value += "}";
918     }
919
920     var output = checked( 'sout_display' ) + checked( 'sout_file' )
921                + checked( 'sout_http' )    + checked( 'sout_mmsh' )
922                + checked( 'sout_rtp' )     + checked( 'sout_udp' );
923
924     if( output )
925     {
926         if( transcode )
927             mrl.value += ":";
928         else
929             mrl.value += ":sout=#";
930         var aloo = false; /* aloo == at least one output */
931         var mux = radio_value( 'sout_mux' );
932         var ttl = parseInt( value( 'sout_ttl' ) );
933         if( output > 1 ) mrl.value += "duplicate{";
934         if( checked( 'sout_display' ) )
935         {
936             if( output > 1 ) mrl.value += "dst="
937             mrl.value += "display";
938             aloo = true;
939         }
940         if( checked( 'sout_file' ) )
941         {
942             if( aloo ) mrl.value += ",";
943             if( output > 1 ) mrl.value += "dst="
944             mrl.value += "std{access=file,mux="+mux+",dst="+value( 'sout_file_filename' )+"}";
945             aloo = true;
946         }
947         if( checked( 'sout_http' ) )
948         {
949             if( aloo ) mrl.value += ",";
950             if( output > 1 ) mrl.value += "dst="
951             mrl.value += "std{access=http,mux="+mux+",dst="+value( 'sout_http_addr' );
952             if( value( 'sout_http_port' ) )
953                 mrl.value += ":"+value( 'sout_http_port' );
954             mrl.value += "}";
955             aloo = true;
956         }
957         if( checked( 'sout_mmsh' ) )
958         {
959             if( aloo ) mrl.value += ",";
960             if( output > 1 ) mrl.value += "dst="
961             mrl.value += "std{access=mmsh,mux="+mux+",dst="+value( 'sout_mmsh_addr' );
962             if( value( 'sout_mmsh_port' ) )
963                 mrl.value += ":"+value( 'sout_mmsh_port' );
964             mrl.value += "}";
965             aloo = true;
966         }
967         if( checked( 'sout_rtp' ) )
968         {
969             if( aloo ) mrl.value += ",";
970             if( output > 1 ) mrl.value += "dst="
971             mrl.value += "std{access=rtp";
972             if( ttl ) mrl.value += "{ttl="+ttl+"}";
973             mrl.value += ",mux="+mux+",dst="+value( 'sout_rtp_addr' );
974             if( value( 'sout_rtp_port' ) )
975                 mrl.value += ":"+value( 'sout_rtp_port' );
976             if( checked( 'sout_sap' ) )
977             {
978                 mrl.value += ",sap";
979                 if( value( 'sout_sap_group' ) != '' )
980                 {
981                     mrl.value += ",group=\""+value( 'sout_sap_group' )+"\"";
982                 }
983                 mrl.value += ",name=\""+value( 'sout_sap_name' )+"\"";
984             }
985             mrl.value += "}";
986             aloo = true;
987         }
988         if( checked( 'sout_udp' ) )
989         {
990             if( aloo ) mrl.value += ",";
991             if( output > 1 ) mrl.value += "dst="
992             mrl.value += "std{access=udp";
993             if( ttl ) mrl.value += "{ttl="+ttl+"}";
994             mrl.value += ",mux="+mux+",dst="+value( 'sout_udp_addr' );
995             if( value('sout_udp_port' ) )
996                 mrl.value += ":"+value( 'sout_udp_port' );
997             if( checked( 'sout_sap' ) )
998             {
999                 mrl.value += ",sap";
1000                 if( value( 'sout_sap_group' ) != '' )
1001                 {
1002                     mrl.value += ",group=\""+value( 'sout_sap_group' )+"\"";
1003                 }
1004                 mrl.value += ",name=\""+value( 'sout_sap_name' )+"\"";
1005             }
1006             mrl.value += "}";
1007             aloo = true;
1008         }
1009         if( output > 1 ) mrl.value += "}";
1010     }
1011
1012     if( ( transcode || output ) && checked( 'sout_all' ) )
1013         mrl.value += " :sout-all";
1014 }
1015
1016 /* reset sout mrl value */
1017 function reset_sout()
1018 {
1019     document.getElementById('sout_mrl').value = value('sout_old_mrl');
1020 }
1021
1022 /* save sout mrl value */
1023 function save_sout()
1024 {
1025     document.getElementById('sout_old_mrl').value = value('sout_mrl');
1026 }
1027
1028 /**********************************************************************
1029  * Browser dialog functions
1030  *********************************************************************/
1031 /* only browse() should be called directly */
1032 function browse( dest )
1033 {
1034     document.getElementById( 'browse_dest' ).value = dest;
1035     document.getElementById( 'browse_lastdir' ).value;
1036     browse_dir( document.getElementById( 'browse_lastdir' ).value );
1037     show( 'browse' );
1038 }
1039 function browse_dir( dir )
1040 {
1041     document.getElementById( 'browse_lastdir' ).value = dir;
1042     loadXMLDoc( 'requests/browse.xml?dir='+encodeURIComponent(dir), parse_browse_dir );
1043 }
1044 function browse_path( p )
1045 {
1046     document.getElementById( value( 'browse_dest' ) ).value = p;
1047     hide( 'browse' );
1048     document.getElementById( value( 'browse_dest' ) ).focus();
1049 }
1050 function refresh_albumart( force )
1051 {
1052     if( albumart_id != pl_cur_id || force )
1053     {
1054         var now = new Date();
1055         var albumart = document.getElementById( 'albumart' );
1056         albumart.src = '/art?timestamp=' + now.getTime();
1057         albumart_id = pl_cur_id;
1058     }
1059 }
1060 /**********************************************************************
1061  * Periodically update stuff in the interface
1062  *********************************************************************/
1063 function loop_refresh_status()
1064 {
1065     setTimeout( 'loop_refresh_status()', 1000 );
1066     update_status();
1067 }
1068 function loop_refresh_playlist()
1069 {
1070     /* setTimeout( 'loop_refresh_playlist()', 10000 ); */
1071     update_playlist();
1072 }
1073 function loop_refresh_albumart()
1074 {
1075     setTimeout( 'loop_refresh_albumart()', 1000 );
1076     refresh_albumart( false );
1077 }
1078 function loop_refresh()
1079 {
1080     setTimeout( 'loop_refresh_status()', 1 );
1081     setTimeout( 'loop_refresh_playlist()', 1 );
1082     setTimeout( 'loop_refresh_albumart()', 1 );
1083 }
1084