]> git.sesse.net Git - vlc/blob - share/http/js/functions.js
http-interface: - changed logic so playlist is only downloaded initially and on modif...
[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 function playlist_populated()
283 {
284     if( document.getElementById( 'playtree' ) != null && document.getElementById( 'playtree' ).childElementCount > 0 )
285     {
286         return true;
287     }
288     return false;
289 }
290
291 /**********************************************************************
292  * Interface actions
293  *********************************************************************/
294 /* input actions */
295 function in_play()
296 {
297     var input = value('input_mrl');
298     if( value('sout_mrl') != '' )
299         input += ' '+value('sout_mrl');
300     var url = 'requests/status.xml?command=in_play&input='+encodeURIComponent( addslashes(escapebackslashes(input)) );
301     loadXMLDoc( url, parse_status );
302     setTimeout( 'update_playlist()', 1000 );
303 }
304 function in_enqueue()
305 {
306     var input = value('input_mrl');
307     if( value('sout_mrl') != '' )
308         input += ' '+value('sout_mrl');
309     var url = 'requests/status.xml?command=in_enqueue&input='+encodeURIComponent( addslashes(escapebackslashes(input)) );
310     loadXMLDoc( url, parse_status );
311     setTimeout( 'update_playlist()', 1000 );
312 }
313
314 /* playlist actions */
315 function pl_play( id )
316 {
317     loadXMLDoc( 'requests/status.xml?command=pl_play&id='+id, parse_status );
318     pl_cur_id = id;
319     setTimeout( 'update_playlist()', 1000 );
320 }
321 function pl_pause()
322 {
323     loadXMLDoc( 'requests/status.xml?command=pl_pause&id='+pl_cur_id, parse_status );
324 }
325 function pl_stop()
326 {
327     loadXMLDoc( 'requests/status.xml?command=pl_stop', parse_status );
328     setTimeout( 'update_playlist()', 1000 );
329 }
330 function pl_next()
331 {
332     loadXMLDoc( 'requests/status.xml?command=pl_next', parse_status );
333     setTimeout( 'update_playlist()', 1000 );
334 }
335 function pl_previous()
336 {
337     loadXMLDoc( 'requests/status.xml?command=pl_previous', parse_status );
338     setTimeout( 'update_playlist()', 1000 );
339 }
340 function pl_delete( id )
341 {
342     loadXMLDoc( 'requests/status.xml?command=pl_delete&id='+id, parse_status );
343     setTimeout( 'update_playlist(true)', 1000 );
344 }
345 function pl_empty()
346 {
347     loadXMLDoc( 'requests/status.xml?command=pl_empty', parse_status );
348     setTimeout( 'update_playlist(true)', 1000 );
349 }
350 function pl_sort( sort, order )
351 {
352     loadXMLDoc( 'requests/status.xml?command=pl_sort&id='+order+'&val='+sort, parse_status );
353     setTimeout( 'update_playlist(true)', 1000 );
354 }
355 function pl_shuffle()
356 {
357     loadXMLDoc( 'requests/status.xml?command=pl_random', parse_status );
358     setTimeout( 'update_playlist(true)', 1000 );
359 }
360 function pl_loop()
361 {
362     loadXMLDoc( 'requests/status.xml?command=pl_loop', parse_status );
363 }
364 function pl_repeat()
365 {
366     loadXMLDoc( 'requests/status.xml?command=pl_repeat', parse_status );
367 }
368 function pl_sd( value )
369 {
370     loadXMLDoc( 'requests/status.xml?command=pl_sd&val='+value, parse_status );
371 }
372
373 /* misc actions */
374 function volume_down()
375 {
376     loadXMLDoc( 'requests/status.xml?command=volume&val=-20', parse_status );
377 }
378 function volume_up()
379 {
380     loadXMLDoc( 'requests/status.xml?command=volume&val=%2B20', parse_status );
381 }
382 function seek( pos )
383 {
384     loadXMLDoc( 'requests/status.xml?command=seek&val='+pos, parse_status );
385 }
386 function fullscreen()
387 {
388     loadXMLDoc( 'requests/status.xml?command=fullscreen', parse_status );
389 }
390 function snapshot()
391 {
392     loadXMLDoc( 'requests/status.xml?command=snapshot', parse_status );
393 }
394 function hotkey( str )
395 {
396     /* Use hotkey name (without the "key-" part) as the argument to simulate a hotkey press */
397     loadXMLDoc( 'requests/status.xml?command=key&val='+str, parse_status );
398 }
399 function update_status()
400 {
401     if( req == null || req.readyState == 0 || req.readyState == 4 )
402     {
403         loadXMLDoc( 'requests/status.xml', parse_status );
404     }
405 }
406 function update_playlist(force_refresh)
407 {
408     if( force_refresh || !playlist_populated() )
409     {
410         loadXMLDoc( 'requests/playlist.xml', parse_playlist );
411     }
412     else
413     {
414         loadXMLDoc( 'requests/status.xml', update_playlist_view );
415     }
416 }
417
418 /**********************************************************************
419  * Parse xml replies to XMLHttpRequests
420  *********************************************************************/
421 /* parse request/status.xml */
422 function parse_status()
423 {
424     if( req.readyState == 4 )
425     {
426         if( req.status == 200 )
427         {
428             var status = req.responseXML.documentElement;
429             var timetag = status.getElementsByTagName( 'time' );
430             if( timetag.length > 0 )
431             {
432                 var new_time = timetag[0].firstChild.data;
433             }
434             else
435             {
436                 new_time = old_time;
437             }
438             var lengthtag = status.getElementsByTagName( 'length' );
439             var length;
440             if( lengthtag.length > 0 )
441             {
442                 length = lengthtag[0].firstChild.data;
443             }
444             else
445             {
446                 length = 0;
447             }
448             var slider_position;
449             positiontag = status.getElementsByTagName( 'position' );
450             if( length < 100 && positiontag.length > 0 )
451             {
452                 slider_position = ( positiontag[0].firstChild.data * 4 ) + "px";
453             }
454             else if( length > 0 )
455             {
456                 /* this is more precise if length > 100 */
457                 slider_position = Math.floor( ( new_time * 400 ) / length ) + "px";
458             }
459             else
460             {
461                 slider_position = 0;
462             }
463             if( old_time > new_time )
464                 setTimeout('update_playlist()',50);
465             old_time = new_time;
466             set_text( 'time', format_time( new_time ) );
467             set_text( 'length', format_time( length ) );
468             if( status.getElementsByTagName( 'volume' ).length != 0 )
469                 set_text( 'volume', Math.floor(status.getElementsByTagName( 'volume' )[0].firstChild.data/5.12)+'%' );
470             var statetag = status.getElementsByTagName( 'state' );
471             if( statetag.length > 0 )
472             {
473                 set_text( 'state', statetag[0].firstChild.data );
474             }
475             else
476             {
477                 set_text( 'state', '(?)' );
478             }
479             if( slider_mouse_down == 0 )
480             {
481                 document.getElementById( 'main_slider_point' ).style.left = slider_position;
482             }
483             var statustag = status.getElementsByTagName( 'state' );
484             if( statustag.length > 0 ? statustag[0].firstChild.data == "playing" : 0 )
485             {
486                 document.getElementById( 'btn_pause_img' ).setAttribute( 'src', 'images/pause.png' );
487                 document.getElementById( 'btn_pause_img' ).setAttribute( 'alt', 'Pause' );
488                 document.getElementById( 'btn_pause' ).setAttribute( 'title', 'Pause' );
489             }
490             else
491             {
492                 document.getElementById( 'btn_pause_img' ).setAttribute( 'src', 'images/play.png' );
493                 document.getElementById( 'btn_pause_img' ).setAttribute( 'alt', 'Play' );
494                 document.getElementById( 'btn_pause' ).setAttribute( 'title', 'Play' );
495             }
496
497             var randomtag = status.getElementsByTagName( 'random' );
498             if( randomtag.length > 0 ? randomtag[0].firstChild.data == "1" : 0)
499                 setclass( document.getElementById( 'btn_shuffle'), 'on' );
500             else
501                 setclass( document.getElementById( 'btn_shuffle'), 'off' );
502                
503             var looptag = status.getElementsByTagName( 'loop' );
504             if( looptag.length > 0 ? looptag[0].firstChild.data == "1" : 0)
505                 setclass( document.getElementById( 'btn_loop'), 'on' );
506             else
507                 setclass( document.getElementById( 'btn_loop'), 'off' );
508
509             var repeattag = status.getElementsByTagName( 'repeat' );
510             if( repeattag.length > 0 ? repeattag[0].firstChild.data == "1" : 0 )
511                 setclass( document.getElementById( 'btn_repeat'), 'on' );
512             else
513                 setclass( document.getElementById( 'btn_repeat'), 'off' );
514
515             var tree = document.createElement( "ul" );
516             var categories = status.getElementsByTagName( 'category' );
517             var i;
518             for( i = 0; i < categories.length; i++ )
519             {
520                 var item = document.createElement( "li" );
521                 item.appendChild( document.createTextNode( categories[i].getAttribute( 'name' ) ) );
522                 var subtree = document.createElement( "dl" );
523                 var infos = categories[i].getElementsByTagName( 'info' );
524                 var j;
525                 for( j = 0; j < infos.length; j++ )
526                 {
527                     var subitem = document.createElement( "dt" );
528                     subitem.appendChild( document.createTextNode( infos[j].getAttribute( 'name' ) ) );
529                     subtree.appendChild( subitem );
530                     if( infos[j].hasChildNodes() )
531                     {
532                         var subitem = document.createElement( "dd" );
533                         subitem.appendChild( document.createTextNode( infos[j].firstChild.data ) );
534                         subtree.appendChild( subitem );
535                     }
536                 }
537                 item.appendChild( subtree );
538                 tree.appendChild( item );
539             }
540             var infotree = document.getElementById('infotree' );
541             clear_children( infotree );
542             infotree.appendChild( tree );
543             
544         }
545         else
546         {
547             /*alert( 'Error! HTTP server replied: ' + req.status );*/
548         }
549     }
550 }
551
552 /* parse playlist.xml */
553 function parse_playlist()
554 {
555     if( req.readyState == 4 )
556     {
557         if( req.status == 200 )
558         {
559             var answer = req.responseXML.documentElement;
560             var playtree = document.getElementById( 'playtree' );
561             var pos = document.createElement( "div" );
562             pos.style.height = document.body.clientHeight - 100 + "px";
563             pos.style.overflow = "auto";
564             var pos_top = pos;
565             var elt = answer.firstChild;
566             
567             pl_cur_id = 0;  /* changed to the current id is there actually
568                              * is a current id */
569             while( elt )
570             {
571                 if( elt.nodeName == "node" )
572                 {
573                     if( pos.hasChildNodes() )
574                         pos.appendChild( document.createElement( "br" ) );
575                     var nda = document.createElement( 'a' );
576                     nda.setAttribute( 'href', 'javascript:toggle_show_node(\''+elt.getAttribute( 'id' )+'\');' );
577                     var ndai = document.createElement( 'img' );
578                     ndai.setAttribute( 'src', 'images/minus.png' );
579                     ndai.setAttribute( 'alt', '[-]' );
580                     ndai.setAttribute( 'id', 'pl_img_'+elt.getAttribute( 'id' ) );
581                     nda.appendChild( ndai );
582                     pos.appendChild( nda );
583                     pos.appendChild( document.createTextNode( ' ' + elt.getAttribute( 'name' ) ) );
584
585                     if( elt.getAttribute( 'ro' ) == 'rw' )
586                     {
587                         pos.appendChild( document.createTextNode( ' ' ) );
588                         var del = document.createElement( "a" );
589                         del.setAttribute( 'href', 'javascript:pl_delete('+elt.getAttribute( 'id' )+')' );
590                             var delimg = document.createElement( "img" );
591                             delimg.setAttribute( 'src', 'images/delete_small.png' );
592                             delimg.setAttribute( 'alt', '(delete)' );
593                         del.appendChild( delimg );
594                         pos.appendChild( del );
595                     }
596
597                     var nd = document.createElement( "div" );
598                     setclass( nd, 'pl_node' );
599                     nd.setAttribute( 'id', 'pl_'+elt.getAttribute( 'id' ) );
600                     pos.appendChild( nd );
601                 }
602                 else if( elt.nodeName == "leaf" )
603                 {
604                     if( pos.hasChildNodes() )
605                     pos.appendChild( document.createElement( "br" ) );
606                     var pl = document.createElement( "a" );
607                     setclass( pl, 'pl_leaf' );
608                     pl.setAttribute( 'href', 'javascript:pl_play('+elt.getAttribute( 'id' )+');' );
609                     pl.setAttribute( 'id', 'pl_'+elt.getAttribute( 'id' ) );
610                     if( elt.getAttribute( 'current' ) == 'current' )
611                     {
612                         pl.style.fontWeight = 'bold';
613                         var nowplaying = document.getElementById( 'nowplaying' );
614                         clear_children( nowplaying );
615                         nowplaying.appendChild( document.createTextNode( elt.getAttribute( 'name' ) ) );
616                         pl.appendChild( document.createTextNode( '' ));
617                         pl_cur_id = elt.getAttribute( 'id' );
618                     }
619                     pl.setAttribute( 'title', elt.getAttribute( 'uri' ));
620                     pl.appendChild( document.createTextNode( elt.getAttribute( 'name' ) ) );
621                     var duration = elt.getAttribute( 'duration' );
622                     if( duration > 0 )
623                         pl.appendChild( document.createTextNode( " (" + format_time( elt.getAttribute( 'duration' ) / 1000000 ) + ")" ) );
624                     pos.appendChild( pl );
625
626                     if( elt.getAttribute( 'ro' ) == 'rw' )
627                     {
628                         pos.appendChild( document.createTextNode( ' ' ) );
629                         var del = document.createElement( "a" );
630                         del.setAttribute( 'href', 'javascript:pl_delete('+elt.getAttribute( 'id' )+')' );
631                             var delimg = document.createElement( "img" );
632                             delimg.setAttribute( 'src', 'images/delete_small.png' );
633                             delimg.setAttribute( 'alt', '(delete)' );
634                         del.appendChild( delimg );
635                         pos.appendChild( del );
636                     }
637                 }
638                 if( elt.firstChild )
639                 {
640                     elt = elt.firstChild;
641                     pos = pos.lastChild;
642                 }
643                 else if( elt.nextSibling )
644                 {
645                     elt = elt.nextSibling;
646                     pos = pos;
647                 }
648                 else
649                 {
650                     while( ! elt.parentNode.nextSibling )
651                     {
652                         elt = elt.parentNode;
653                         if( ! elt.parentNode ) break;
654                         pos = pos.parentNode;
655                     }
656                     if( ! elt.parentNode ) break;
657                     elt = elt.parentNode.nextSibling;
658                     pos = pos.parentNode;
659                 }
660             }
661             clear_children( playtree );
662             playtree.appendChild( pos_top );
663         }
664         else
665         {
666             /*alert( 'Error! HTTP server replied: ' + req.status );*/
667         }
668     }
669 }
670
671 /* parse browse.xml */
672 function parse_browse_dir( )
673 {
674     if( req.readyState == 4 )
675     {
676         if( req.status == 200 )
677         {
678             var answer = req.responseXML.documentElement;
679             if( !answer ) return;
680             var browser = document.getElementById( 'browser' );
681             var pos = document.createElement( "div" );
682             var elt = answer.firstChild;
683             while( elt )
684             {
685                 if( elt.nodeName == "element" )
686                 {
687                     var item = document.createElement( "a" );
688                     setclass( item, 'browser' );
689                     if( elt.getAttribute( 'type' ) == 'directory' )
690                     {
691                         item.setAttribute( 'href', 'javascript:browse_dir(\''+addslashes(escapebackslashes(elt.getAttribute( 'path' )))+'\');');
692                     }
693                     else
694                     {
695                         item.setAttribute( 'href', 'javascript:browse_path(\''+addslashes(escapebackslashes(elt.getAttribute( 'path' )))+'\');' );
696                     }
697                     item.appendChild( document.createTextNode( elt.getAttribute( 'name' ) ) );
698                     pos.appendChild( item );
699                     if( elt.getAttribute( 'type' ) == 'directory' )
700                     {
701                         pos.appendChild( document.createTextNode( ' ' ) );
702                         var item = document.createElement( "a" );
703                         setclass( item, 'browser' );
704                         item.setAttribute( 'href', 'javascript:browse_path(\''+addslashes(escapebackslashes(elt.getAttribute( 'path' )))+'\');');
705                         item.appendChild( document.createTextNode( '(select)' ) );
706                         pos.appendChild( item );
707                     }
708                     pos.appendChild( document.createElement( "br" ) );
709                 }
710                 elt = elt.nextSibling;
711             }
712             clear_children( browser );
713             browser.appendChild( pos );
714         }
715         else
716         {
717             /*alert( 'Error! HTTP server replied: ' + req.status );*/
718         }
719     }
720 }
721
722 /* updates playlist to display active entry */
723 function update_playlist_view ()
724 {
725     if( req.readyState == 4 ) {
726         if( req.status == 200 ) {
727             var status = req.responseXML.documentElement;
728             var title = status.getElementsByTagName( 'title' );
729             if( title.length > 0 ) {
730                 title = title[0].firstChild.data;
731
732                 //update now-playing..
733                 var nowplaying = document.getElementById( 'nowplaying' );
734                 clear_children( nowplaying );
735                 nowplaying.appendChild( document.createTextNode( title ) );
736
737                 //update playlist..
738                 var playtree = document.getElementById( 'playtree' );
739                 if( playtree.hasChildNodes() )
740                 {
741                     var root = playtree.firstChild;  //root div
742                     if( root.hasChildNodes() )
743                     {
744                         for( var i = 0; i < root.childNodes.length - 1; i++ )
745                         {
746                             if ( root.childNodes[i].className == "pl_node" && root.childNodes[i].hasChildNodes() )
747                             {
748                                 var node = root.childNodes[i];  //pl_node
749                                 if( node.className == "pl_node" && node.hasChildNodes() )
750                                 {
751                                     for( var j = 0; j < node.childNodes.length - 1; j++ )
752                                     {
753                                         if( node.childNodes[j].className == "pl_leaf" )
754                                         {
755                                             var leaf = node.childNodes[j];  //pl_leaf
756                                             var pl_title = leaf.textContent.substring( 0, leaf.textContent.length - leaf.text.length )
757                                             //if( leaf.style.fontWeight == "bold" && pl_title.substring(0, 2) == "* " )  //handle leaf currently identified as playing..
758                                             //{
759                                             //    pl_title = pl_title.substring(2);
760                                             //}
761                                             if( pl_title == title )
762                                             {
763                                                 leaf.style.fontWeight = "bold";
764                                             }
765                                             else
766                                             {
767                                                 leaf.style.fontWeight = "";
768                                             }
769                                         }
770                                     }
771                                 }
772                             }
773                         }
774                     }
775                 }
776             }
777         }
778     }
779 }
780
781 /**********************************************************************
782  * Input dialog functions
783  *********************************************************************/
784 function hide_input( )
785 {
786     document.getElementById( 'input_file' ).style.display = 'none';
787     document.getElementById( 'input_disc' ).style.display = 'none';
788     document.getElementById( 'input_network' ).style.display = 'none';
789     document.getElementById( 'input_fake' ).style.display = 'none';
790 }
791
792 /* update the input MRL using data from the input file helper */
793 /* FIXME ... subs support */
794 function update_input_file()
795 {
796     var mrl = document.getElementById( 'input_mrl' );
797
798     mrl.value = value( 'input_file_filename' );
799 }
800
801 /* update the input MRL using data from the input disc helper */
802 function update_input_disc()
803 {
804     var mrl     = document.getElementById( 'input_mrl' );
805     var type    = radio_value( "input_disc_type" );
806     var device  = value( "input_disc_dev" );
807
808     var title   = check_and_replace_int( 'input_disc_title', 0 );
809     var chapter = check_and_replace_int( 'input_disc_chapter', 0 );
810     var subs    = check_and_replace_int( 'input_disc_subtrack', '' );
811     var audio   = check_and_replace_int( 'input_disc_audiotrack', 0 );
812
813     mrl.value = "";
814
815     if( type == "dvd" )
816     {
817         mrl.value += "dvd://";
818     }
819     else if( type == "dvdsimple" )
820     {
821         mrl.value += "dvdsimple://";
822     }
823     else if( type == "vcd" )
824     {
825         mrl.value += "vcd://";
826     }
827     else if( type == "cdda" )
828     {
829         mrl.value += "cdda://";
830     }
831
832     mrl.value += device;
833
834     if( title )
835     {
836         mrl.value += "@"+title;
837         if( chapter && type != "cdda" )
838             mrl.value += ":"+chapter;
839     }
840
841     if( type != "cdda" )
842     {
843         if( subs != '' )
844             mrl.value += " :sub-track="+subs;
845         if( audio != '' )
846             mrl.value += " :audio-track="+audio;
847     }
848
849 }
850
851 /* update the input MRL using data from the input network helper */
852 function update_input_net()
853 {
854     var mrl = document.getElementById( 'input_mrl' );
855     var type = radio_value( "input_net_type" );
856     
857     check_and_replace_int( 'input_net_udp_port', 1234 );
858     check_and_replace_int( 'input_net_udpmcast_port', 1234 );
859
860     mrl.value = "";
861
862     if( type == "udp" )
863     {
864         mrl.value += "udp://";
865         if( checked( 'input_net_udp_forceipv6' ) )
866             mrl.value += "[::]";
867         if( value( 'input_net_udp_port' ) )
868             mrl.value += ":"+value( 'input_net_udp_port' );
869     }
870     else if( type == "udpmcast" )
871     {
872         mrl.value += "udp://@"+value( 'input_net_udpmcast_address');
873         if( value( 'input_net_udpmcast_port' ) )
874             mrl.value += ":"+value( 'input_net_udpmcast_port' );
875     }
876     else if( type == "http" )
877     {
878         var url = value( 'input_net_http_url' );
879         if( url.substring(0,7) != "http://"
880             && url.substring(0,8) != "https://"
881             && url.substring(0,6) != "ftp://"
882             && url.substring(0,6) != "mms://"
883             && url.substring(0,7) != "mmsh://" )
884             mrl.value += "http://";
885         mrl.value += url;
886     }
887     else if( type == "rtsp" )
888     {
889         var url = value( 'input_net_rtsp_url' );
890         if( url.substring(0,7) != "rtsp://" )
891             mrl.value += "rtsp://";
892         mrl.value += url;
893     }
894
895     if( checked( "input_net_timeshift" ) )
896         mrl.value += " :access-filter=timeshift";
897 }
898
899 /* update the input MRL using data from the input fake helper */
900 function update_input_fake()
901 {
902     var mrl = document.getElementById( 'input_mrl' );
903
904     mrl.value = "fake://";
905     mrl.value += " :fake-file=" + value( "input_fake_filename" );
906
907     if( value( "input_fake_width" ) )
908         mrl.value += " :fake-width=" + value( "input_fake_width" );
909     if( value( "input_fake_height" ) )
910         mrl.value += " :fake-height=" + value( "input_fake_height" );
911     if( value( "input_fake_ar" ) )
912         mrl.value += " :fake-ar=" + value( "input_fake_ar" );
913 }
914
915 /**********************************************************************
916  * Sout dialog functions
917  *********************************************************************/
918 /* toggle show the full sout interface */
919 function toggle_show_sout_helper()
920 {
921     var element = document.getElementById( "sout_helper" );
922     if( element.style.display == 'block' )
923     {
924         element.style.display = 'none';
925         document.getElementById( "sout_helper_toggle" ).value = 'Full sout interface';
926     }
927     else
928     {
929         element.style.display = 'block';
930         document.getElementById( "sout_helper_toggle" ).value = 'Hide sout interface';
931     }
932 }
933
934 /* update the sout MRL using data from the sout_helper */
935 function update_sout()
936 {
937     var mrl = document.getElementById( 'sout_mrl' );
938     mrl.value = "";
939
940     check_and_replace_int( 'sout_http_port', 8080 );
941     check_and_replace_int( 'sout_mmsh_port', 8080 );
942     check_and_replace_int( 'sout_rtp_port', 1234 );
943     check_and_replace_int( 'sout_udp_port', 1234 );
944     check_and_replace_int( 'sout_ttl', 1 );
945
946     if( checked( 'sout_soverlay' ) )
947     {
948         disable( 'sout_scodec' );
949         disable( 'sout_sub' );
950     }
951     else
952     {
953         enable( 'sout_scodec' );
954         enable( 'sout_sub' );
955     }
956
957     var transcode =  checked( 'sout_vcodec_s' ) || checked( 'sout_acodec_s' )
958                   || checked( 'sout_sub' )      || checked( 'sout_soverlay' );
959
960     if( transcode )
961     {
962         mrl.value += ":sout=#transcode{";
963         var alot = false; /* alot == at least one transcode */
964         if( checked( 'sout_vcodec_s' ) )
965         {
966             mrl.value += "vcodec="+value( 'sout_vcodec' )+",vb="+value( 'sout_vb' )+",scale="+value( 'sout_scale' );
967             alot = true;
968         }
969         if( checked( 'sout_acodec_s' ) )
970         {
971             if( alot ) mrl.value += ",";
972             mrl.value += "acodec="+value( 'sout_acodec' )+",ab="+value( 'sout_ab' );
973             if( value( 'sout_channels' ) )
974                 mrl.value += ",channels="+value( 'sout_channels' );
975             alot = true;
976         }
977         if( checked( 'sout_soverlay' ) )
978         {
979             if( alot ) mrl.value += ",";
980             mrl.value += "soverlay";
981             alot = true;
982         }
983         else if( checked( 'sout_sub' ) )
984         {
985             if( alot ) mrl.value += ",";
986             mrl.value += "scodec="+value( 'sout_scodec' );
987             alot = true;
988         }
989         mrl.value += value( 'sout_transcode_extra' );
990             
991         mrl.value += "}";
992     }
993
994     var output = checked( 'sout_display' ) + checked( 'sout_file' )
995                + checked( 'sout_http' )    + checked( 'sout_mmsh' )
996                + checked( 'sout_rtp' )     + checked( 'sout_udp' );
997
998     if( output )
999     {
1000         if( transcode )
1001             mrl.value += ":";
1002         else
1003             mrl.value += ":sout=#";
1004         var aloo = false; /* aloo == at least one output */
1005         var mux = radio_value( 'sout_mux' );
1006         var ttl = parseInt( value( 'sout_ttl' ) );
1007         if( output > 1 ) mrl.value += "duplicate{";
1008         if( checked( 'sout_display' ) )
1009         {
1010             if( output > 1 ) mrl.value += "dst="
1011             mrl.value += "display";
1012             aloo = true;
1013         }
1014         if( checked( 'sout_file' ) )
1015         {
1016             if( aloo ) mrl.value += ",";
1017             if( output > 1 ) mrl.value += "dst="
1018             mrl.value += "std{access=file,mux="+mux+",dst="+value( 'sout_file_filename' )+"}";
1019             aloo = true;
1020         }
1021         if( checked( 'sout_http' ) )
1022         {
1023             if( aloo ) mrl.value += ",";
1024             if( output > 1 ) mrl.value += "dst="
1025             mrl.value += "std{access=http,mux="+mux+",dst="+value( 'sout_http_addr' );
1026             if( value( 'sout_http_port' ) )
1027                 mrl.value += ":"+value( 'sout_http_port' );
1028             mrl.value += "}";
1029             aloo = true;
1030         }
1031         if( checked( 'sout_mmsh' ) )
1032         {
1033             if( aloo ) mrl.value += ",";
1034             if( output > 1 ) mrl.value += "dst="
1035             mrl.value += "std{access=mmsh,mux="+mux+",dst="+value( 'sout_mmsh_addr' );
1036             if( value( 'sout_mmsh_port' ) )
1037                 mrl.value += ":"+value( 'sout_mmsh_port' );
1038             mrl.value += "}";
1039             aloo = true;
1040         }
1041         if( checked( 'sout_rtp' ) )
1042         {
1043             if( aloo ) mrl.value += ",";
1044             if( output > 1 ) mrl.value += "dst="
1045             mrl.value += "std{access=rtp";
1046             if( ttl ) mrl.value += "{ttl="+ttl+"}";
1047             mrl.value += ",mux="+mux+",dst="+value( 'sout_rtp_addr' );
1048             if( value( 'sout_rtp_port' ) )
1049                 mrl.value += ":"+value( 'sout_rtp_port' );
1050             if( checked( 'sout_sap' ) )
1051             {
1052                 mrl.value += ",sap";
1053                 if( value( 'sout_sap_group' ) != '' )
1054                 {
1055                     mrl.value += ",group=\""+value( 'sout_sap_group' )+"\"";
1056                 }
1057                 mrl.value += ",name=\""+value( 'sout_sap_name' )+"\"";
1058             }
1059             mrl.value += "}";
1060             aloo = true;
1061         }
1062         if( checked( 'sout_udp' ) )
1063         {
1064             if( aloo ) mrl.value += ",";
1065             if( output > 1 ) mrl.value += "dst="
1066             mrl.value += "std{access=udp";
1067             if( ttl ) mrl.value += "{ttl="+ttl+"}";
1068             mrl.value += ",mux="+mux+",dst="+value( 'sout_udp_addr' );
1069             if( value('sout_udp_port' ) )
1070                 mrl.value += ":"+value( 'sout_udp_port' );
1071             if( checked( 'sout_sap' ) )
1072             {
1073                 mrl.value += ",sap";
1074                 if( value( 'sout_sap_group' ) != '' )
1075                 {
1076                     mrl.value += ",group=\""+value( 'sout_sap_group' )+"\"";
1077                 }
1078                 mrl.value += ",name=\""+value( 'sout_sap_name' )+"\"";
1079             }
1080             mrl.value += "}";
1081             aloo = true;
1082         }
1083         if( output > 1 ) mrl.value += "}";
1084     }
1085
1086     if( ( transcode || output ) && checked( 'sout_all' ) )
1087         mrl.value += " :sout-all";
1088 }
1089
1090 /* reset sout mrl value */
1091 function reset_sout()
1092 {
1093     document.getElementById('sout_mrl').value = value('sout_old_mrl');
1094 }
1095
1096 /* save sout mrl value */
1097 function save_sout()
1098 {
1099     document.getElementById('sout_old_mrl').value = value('sout_mrl');
1100 }
1101
1102 /**********************************************************************
1103  * Browser dialog functions
1104  *********************************************************************/
1105 /* only browse() should be called directly */
1106 function browse( dest )
1107 {
1108     document.getElementById( 'browse_dest' ).value = dest;
1109     document.getElementById( 'browse_lastdir' ).value;
1110     browse_dir( document.getElementById( 'browse_lastdir' ).value );
1111     show( 'browse' );
1112 }
1113 function browse_dir( dir )
1114 {
1115     document.getElementById( 'browse_lastdir' ).value = dir;
1116     loadXMLDoc( 'requests/browse.xml?dir='+encodeURIComponent(dir), parse_browse_dir );
1117 }
1118 function browse_path( p )
1119 {
1120     document.getElementById( value( 'browse_dest' ) ).value = p;
1121     hide( 'browse' );
1122     document.getElementById( value( 'browse_dest' ) ).focus();
1123 }
1124 function refresh_albumart( force )
1125 {
1126     if( albumart_id != pl_cur_id || force )
1127     {
1128         var now = new Date();
1129         var albumart = document.getElementById( 'albumart' );
1130         albumart.src = '/art?timestamp=' + now.getTime();
1131         albumart_id = pl_cur_id;
1132     }
1133 }
1134 /**********************************************************************
1135  * Periodically update stuff in the interface
1136  *********************************************************************/
1137 function loop_refresh_status()
1138 {
1139     setTimeout( 'loop_refresh_status()', 1000 );
1140     update_status();
1141 }
1142 function loop_refresh_playlist()
1143 {
1144     /* setTimeout( 'loop_refresh_playlist()', 10000 ); */
1145     update_playlist();
1146 }
1147 function loop_refresh_albumart()
1148 {
1149     setTimeout( 'loop_refresh_albumart()', 1000 );
1150     refresh_albumart( false );
1151 }
1152 function loop_refresh()
1153 {
1154     setTimeout( 'loop_refresh_status()', 1 );
1155     setTimeout( 'loop_refresh_playlist()', 1 );
1156     setTimeout( 'loop_refresh_albumart()', 1 );
1157 }
1158