* add browse dialog for file input (this needs some authentification ... but
[vlc.git] / share / http / functions.js
1 /*****************************************************************************
2  * functions.js: VLC media player web interface
3  *****************************************************************************
4  * Copyright (C) 2005 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 function loadXMLDoc( url, callback )
25 {
26   // branch for native XMLHttpRequest object
27   if ( window.XMLHttpRequest )
28   {
29     req = new XMLHttpRequest();
30     req.onreadystatechange = callback;
31     req.open( "GET", url, true );
32     req.send( null );
33   // branch for IE/Windows ActiveX version
34   }
35   else if ( window.ActiveXObject )
36   {
37     req = new ActiveXObject( "Microsoft.XMLHTTP" );
38     if ( req )
39     {
40       req.onreadystatechange = callback;
41       req.open( "GET", url, true );
42       req.send();
43     }
44   }
45 }
46
47 function format_time( s )
48 {
49     hours = Math.floor(s/3600);
50     minutes = Math.floor((s/60)%60);
51     seconds = s%60;
52     if( hours < 10 ) hours = "0"+hours;
53     if( minutes < 10 ) minutes = "0"+minutes;
54     if( seconds < 10 ) seconds = "0"+seconds;
55     return hours+":"+minutes+":"+seconds;
56 }
57
58
59 function in_play()
60 {
61     input = value('input_mrl');
62     if( value('sout_mrl') != '' )
63         input += ' '+value('sout_mrl');
64     url = 'requests/status.xml?command=in_play&input='+escape(input);
65     loadXMLDoc( url, parse_status );
66     setTimeout( 'update_playlist()', 1000 );
67 }
68 function in_enqueue()
69 {
70     input = value('input_mrl');
71     if( value('sout_mrl') != '' )
72         input += ' '+value('sout_mrl');
73     url = 'requests/status.xml?command=in_enqueue&input='+escape(input);
74     loadXMLDoc( url, parse_status );
75     setTimeout( 'update_playlist()', 1000 );
76 }
77 function pl_play( id )
78 {
79     loadXMLDoc( 'requests/status.xml?command=pl_play&id='+id, parse_status );
80     setTimeout( 'update_playlist()', 1000 );
81 }
82 function pl_pause()
83 {
84     loadXMLDoc( 'requests/status.xml?command=pl_pause', parse_status );
85 }
86 function pl_stop()
87 {
88     loadXMLDoc( 'requests/status.xml?command=pl_stop', parse_status );
89     setTimeout( 'update_playlist()', 1000 );
90 }
91 function pl_next()
92 {
93     loadXMLDoc( 'requests/status.xml?command=pl_next', parse_status );
94     setTimeout( 'update_playlist()', 1000 );
95 }
96 function pl_previous()
97 {
98     loadXMLDoc( 'requests/status.xml?command=pl_previous', parse_status );
99     setTimeout( 'update_playlist()', 1000 );
100 }
101 function pl_delete( id )
102 {
103     loadXMLDoc( 'requests/status.xml?command=pl_delete&id='+id, parse_status );
104     setTimeout( 'update_playlist()', 1000 );
105 }
106 function pl_empty()
107 {
108     loadXMLDoc( 'requests/status.xml?command=pl_empty', parse_status );
109     setTimeout( 'update_playlist()', 1000 );
110 }
111 function volume_down()
112 {
113     loadXMLDoc( 'requests/status.xml?command=volume&val=-20', parse_status );
114 }
115 function volume_up()
116 {
117     loadXMLDoc( 'requests/status.xml?command=volume&val=%2B20', parse_status );
118 }
119 function fullscreen()
120 {
121     loadXMLDoc( 'requests/status.xml?command=fullscreen', parse_status );
122 }
123 function update_status()
124 {
125     loadXMLDoc( 'requests/status.xml', parse_status );
126 }
127 function update_playlist()
128 {
129     loadXMLDoc( 'requests/playlist.xml', parse_playlist );
130 }
131
132 function parse_status()
133 {
134     if( req.readyState == 4 )
135     {
136         if( req.status == 200 )
137         {
138             status = req.responseXML.documentElement;
139             document.getElementById( 'time' ).textContent = format_time( status.getElementsByTagName( 'time' )[0].firstChild.data );
140             document.getElementById( 'length' ).textContent = format_time( status.getElementsByTagName( 'length' )[0].firstChild.data );
141             if( status.getElementsByTagName( 'volume' ).length != 0 )
142             document.getElementById( 'volume' ).textContent = Math.floor(status.getElementsByTagName( 'volume' )[0].firstChild.data/5.12)+"%";
143             document.getElementById( 'state' ).textContent = status.getElementsByTagName( 'state' )[0].firstChild.data;
144             if( status.getElementsByTagName( 'state' )[0].firstChild.data == "playing" )
145             {
146                 document.getElementById( 'btn_pause_img' ).setAttribute( 'src', 'images/pause.png' );
147             }
148             else
149             {
150                 document.getElementById( 'btn_pause_img' ).setAttribute( 'src', 'images/play.png' );
151             }
152         }
153         else
154         {
155             /*alert( 'Error! HTTP server replied: ' + req.status );*/
156         }
157     }
158 }
159
160 function parse_playlist()
161 {
162     if( req.readyState == 4 )
163     {
164         if( req.status == 200 )
165         {
166             answer = req.responseXML.documentElement;
167             playtree = document.getElementById( 'playtree' );
168             /* pos = playtree; */
169             pos = document.createElement( "div" );
170             pos_top = pos;
171             elt = answer.firstChild;
172             while( elt )
173             {
174                 if( elt.nodeName == "node" )
175                 {
176                     pos.appendChild( document.createElement( "div" ) );
177                     pos.lastChild.setAttribute( 'class', 'pl_node' );
178                     pos.lastChild.setAttribute( 'onclick', 'pl_play('+elt.getAttribute( 'id' )+');' );
179                     pos.lastChild.setAttribute( 'id', 'pl_'+elt.getAttribute( 'id' ) );
180                     pos.lastChild.appendChild( document.createTextNode( elt.getAttribute( 'name' ) ) );
181                 }
182                 else if( elt.nodeName == "leaf" )
183                 {
184                     pos.appendChild( document.createElement( "a" ) );
185                     pl = pos.lastChild;
186                     pl.setAttribute( 'class', 'pl_leaf' );
187                     pl.setAttribute( 'href', 'javascript:pl_play('+elt.getAttribute( 'id' )+');' );
188                     pl.setAttribute( 'id', 'pl_'+elt.getAttribute( 'id' ) );
189                     if( elt.getAttribute( 'current' ) == 'current' )
190                     {
191                         pl.setAttribute( 'style', 'font-weight: bold;' );
192                         document.getElementById( 'nowplaying' ).textContent
193                             = elt.getAttribute( 'name' );
194                     }
195                     pl.setAttribute( 'title', elt.getAttribute( 'uri' ));
196                     pl.appendChild( document.createTextNode( elt.getAttribute( 'name' ) ) );
197                 }
198                 if( elt.firstChild )
199                 {
200                     elt = elt.firstChild;
201                     pos = pos.lastChild;
202                 }
203                 else if( elt.nextSibling )
204                 {
205                     elt = elt.nextSibling;
206                     pos = pos;
207                 }
208                 else
209                 {
210                     elt = elt.parentNode.nextSibling;
211                     pos = pos.parentNode;
212                 }
213             }
214             while( playtree.hasChildNodes() )
215                 playtree.removeChild( playtree.firstChild );
216             playtree.appendChild( pos_top );
217         }
218         else
219         {
220             /*alert( 'Error! HTTP server replied: ' + req.status );*/
221         }
222     }
223 }
224
225 function set_css( item, element, value )
226 {
227     for( j = 0; j<document.styleSheets.length; j++ )
228     {
229         cssRules = document.styleSheets[j].cssRules;
230         for( i = 0; i<cssRules.length; i++)
231         {
232             if( cssRules[i].selectorText == item )
233             {
234                 cssRules[i].style.setProperty( element, value, null );
235                 return;
236             }
237         }
238     }
239 }
240
241 function get_css( item, element )
242 {
243     for( j = 0; j<document.styleSheets.length; j++ )
244     {
245         cssRules = document.styleSheets[j].cssRules;
246         for( i = 0; i<cssRules.length; i++)
247         {
248             if( cssRules[i].selectorText == item )
249             {
250                 return cssRules[i].style.getPropertyValue( element );
251             }
252         }
253     }
254 }
255
256 function toggle_btn_text()
257 {
258     if( get_css( '.btn_text', 'display' ) == 'none' )
259     {
260         set_css( '.btn_text', 'display', 'block' );
261     }
262     else
263     {
264         set_css( '.btn_text', 'display', 'none' );
265     }
266 }
267
268 function toggle_show_sout_helper()
269 {
270     element = document.getElementById( "sout_helper" );
271         if( element.style.display == 'block' )
272     {
273         element.style.display = 'none';
274         document.getElementById( "sout_helper_toggle" ).value = 'Full sout interface';
275     }
276     else
277     {
278         element.style.display = 'block';
279         document.getElementById( "sout_helper_toggle" ).value = 'Hide sout interface';
280     }
281 }
282
283 function toggle_show( id )
284 {
285     element = document.getElementById( id );
286     if( element.style.display == 'block' || element.style.display == '' )
287     {
288         element.style.display = 'none';
289     }
290     else
291     {
292         element.style.display = 'block';
293     }
294 }
295
296 function show( id )
297 {
298     document.getElementById( id ).style.display = 'block';
299 }
300 function hide( id )
301 {
302     document.getElementById( id ).style.display = 'none';
303 }
304
305 function hide_input( )
306 {
307     document.getElementById( 'input_file' ).style.display = 'none';
308     document.getElementById( 'input_disc' ).style.display = 'none';
309     document.getElementById( 'input_network' ).style.display = 'none';
310 }
311
312 function checked( id )
313 {
314     return document.getElementById( id ).checked;
315 }
316 function value( id )
317 {
318     return document.getElementById( id ).value;
319 }
320 function radio_value( name )
321 {
322     radio = document.getElementsByName( name );
323     for( i = 0; i<radio.length; i++ )
324     {
325         if( radio[i].checked )
326         {
327             return radio[i].value;
328         }
329     }
330     return "";
331 }
332 function disable( id )
333 {/* FIXME */
334     return document.getElementById( id ).value;
335 }
336
337 function check_and_replace_int( id, val )
338 {
339     var objRegExp = /^\d\d*$/;
340     if( value( id ) != ''
341         && ( !objRegExp.test( value( id ) )
342              || parseInt( value( id ) ) < 1 ) )
343         document.getElementById( id ).value = val;
344
345 }
346
347 function disable( id )
348 {
349     document.getElementById( id ).disabled = true;
350 }
351
352 function enable( id )
353 {
354     document.getElementById( id ).disabled = false;
355 }
356
357 function button_over( element )
358 {
359     element.style.border = "1px solid black";
360 }
361
362 function button_out( element )
363 {
364     element.style.border = "1px none black";
365 }
366
367 /* update the input MRL using data from the input file helper */
368 /* FIXME ... subs support */
369 function update_input_file()
370 {
371     mrl = document.getElementById( 'input_mrl' );
372
373     mrl.value = value( 'input_file_filename' );
374 }
375
376 /* update the input MRL using data from the input disc helper */
377 function update_input_disc()
378 {
379     mrl = document.getElementById( 'input_mrl' );
380     type = radio_value( "input_disc_type" );
381     device = value( "input_disc_dev" );
382
383     check_and_replace_int( 'input_disc_title', 0 );
384     check_and_replace_int( 'input_disc_chapter', 0 );
385     check_and_replace_int( 'input_disc_subtrack', '' );
386     check_and_replace_int( 'input_disc_audiotrack', 0 );
387
388     title = value( 'input_disc_title' );
389     chapter = value( 'input_disc_chapter' );
390     subs = value( 'input_disc_subtrack' );
391     audio = value( 'input_disc_audiotrack' );
392
393     mrl.value = "";
394
395     if( type == "dvd" )
396     {
397         mrl.value += "dvd://";
398     }
399     else if( type == "dvdsimple" )
400     {
401         mrl.value += "dvdsimple://";
402     }
403     else if( type == "vcd" )
404     {
405         mrl.value += "vcd://";
406     }
407     else if( type == "cdda" )
408     {
409         mrl.value += "cdda://";
410     }
411
412     mrl.value += device;
413
414     if( title )
415     {
416         mrl.value += "@"+title;
417         if( chapter && type != "cdda" )
418             mrl.value += ":"+chapter;
419     }
420
421     if( type != "cdda" )
422     {
423         if( subs != '' )
424             mrl.value += " :sub-track="+subs;
425         if( audio != '' )
426             mrl.value += " :audio-track="+audio;
427     }
428
429 }
430
431 /* update the input MRL using data from the input network helper */
432 function update_input_net()
433 {
434     mrl = document.getElementById( 'input_mrl' );
435     type = radio_value( "input_net_type" );
436     
437     check_and_replace_int( 'input_net_udp_port', 1234 );
438     check_and_replace_int( 'input_net_udpmcast_port', 1234 );
439
440     mrl.value = "";
441
442     if( type == "udp" )
443     {
444         mrl.value += "udp://";
445         if( checked( 'input_net_udp_forceipv6' ) )
446             mrl.value += "[::]";
447         if( value( 'input_net_udp_port' ) )
448             mrl.value += ":"+value( 'input_net_udp_port' );
449     }
450     else if( type == "udpmcast" )
451     {
452         mrl.value += "udp://@"+value( 'input_net_udpmcast_address');
453         if( value( 'input_net_udpmcast_port' ) )
454             mrl.value += ":"+value( 'input_net_udpmcast_port' );
455     }
456     else if( type == "http" )
457     {
458         url = value( 'input_net_http_url' );
459         if( url.substring(0,7) != "http://"
460             && url.substring(0,8) != "https://"
461             && url.substring(0,6) != "ftp://"
462             && url.substring(0,6) != "mms://"
463             && url.substring(0,7) != "mmsh://" )
464             mrl.value += "http://";
465         mrl.value += url;
466     }
467     else if( type == "rtsp" )
468     {
469         url = value( 'input_net_rtsp_url' );
470         if( url.substring(0,7) != "rtsp://" )
471             mrl.value += "rtsp://";
472         mrl.value += url;
473     }
474
475     if( checked( "input_net_timeshift" ) )
476         mrl.value += " :access-filter=timeshift";
477 }
478
479 /* update the sout MRL using data from the sout_helper */
480 function update_sout()
481 {
482     mrl = document.getElementById( 'sout_mrl' );
483     mrl.value = "";
484
485     check_and_replace_int( 'sout_http_port', 8080 );
486     check_and_replace_int( 'sout_mmsh_port', 8080 );
487     check_and_replace_int( 'sout_rtp_port', 1234 );
488     check_and_replace_int( 'sout_udp_port', 1234 );
489     check_and_replace_int( 'sout_ttl', 1 );
490
491     if( checked( 'sout_soverlay' ) )
492     {
493         disable( 'sout_scodec' );
494         disable( 'sout_sub' );
495     }
496     else
497     {
498         enable( 'sout_scodec' );
499         enable( 'sout_sub' );
500     }
501
502     transcode =  checked( 'sout_vcodec_s' ) || checked( 'sout_acodec_s' )
503                  || checked( 'sout_sub' ) || checked( 'sout_soverlay' );
504
505     if( transcode )
506     {
507         mrl.value += ":sout=#transcode{";
508         alot = false; /* alot == at least one transcode */
509         if( checked( 'sout_vcodec_s' ) )
510         {
511             mrl.value += "vcodec="+value( 'sout_vcodec' )+",vb="+value( 'sout_vb' )+",scale="+value( 'sout_scale' );
512             alot = true;
513         }
514         if( checked( 'sout_acodec_s' ) )
515         {
516             if( alot ) mrl.value += ",";
517             mrl.value += "acodec="+value( 'sout_acodec' )+",ab="+value( 'sout_ab' );
518             if( value( 'sout_channels' ) )
519                 mrl.value += ",channels="+value( 'sout_channels' );
520             alot = true;
521         }
522         if( checked( 'sout_soverlay' ) )
523         {
524             if( alot ) mrl.value += ",";
525             mrl.value += "soverlay";
526             alot = true;
527         }
528         else if( checked( 'sout_sub' ) )
529         {
530             if( alot ) mrl.value += ",";
531             mrl.value += "scodec="+value( 'sout_scodec' );
532             alot = true;
533         }
534         mrl.value += "}";
535     }
536
537     output = checked( 'sout_display' ) + checked( 'sout_file' )
538              + checked( 'sout_http' ) + checked( 'sout_mmsh' )
539              + checked( 'sout_rtp' )  + checked( 'sout_udp' );
540
541     if( output )
542     {
543         if( transcode )
544             mrl.value += ":";
545         else
546             mrl.value += ":sout=#";
547         aloo = false; /* aloo == at least one output */
548         mux = radio_value( 'sout_mux' );
549         ttl = parseInt( value( 'sout_ttl' ) );
550         if( output > 1 ) mrl.value += "duplicate{";
551         if( checked( 'sout_display' ) )
552         {
553             if( output > 1 ) mrl.value += "dst="
554             mrl.value += "display";
555             aloo = true;
556         }
557         if( checked( 'sout_file' ) )
558         {
559             if( aloo ) mrl.value += ",";
560             if( output > 1 ) mrl.value += "dst="
561             mrl.value += "std{access=file,mux="+mux+",dst="+value( 'sout_file_filename' )+"}";
562             aloo = true;
563         }
564         if( checked( 'sout_http' ) )
565         {
566             if( aloo ) mrl.value += ",";
567             if( output > 1 ) mrl.value += "dst="
568             mrl.value += "std{access=http,mux="+mux+",dst="+value( 'sout_http_addr' );
569             if( value( 'sout_http_port' ) )
570                 mrl.value += ":"+value( 'sout_http_port' );
571             mrl.value += "}";
572             aloo = true;
573         }
574         if( checked( 'sout_mmsh' ) )
575         {
576             if( aloo ) mrl.value += ",";
577             if( output > 1 ) mrl.value += "dst="
578             mrl.value += "std{access=mmsh,mux="+mux+",dst="+value( 'sout_mmsh_addr' );
579             if( value( 'sout_mmsh_port' ) )
580                 mrl.value += ":"+value( 'sout_mmsh_port' );
581             mrl.value += "}";
582             aloo = true;
583         }
584         if( checked( 'sout_rtp' ) )
585         {
586             if( aloo ) mrl.value += ",";
587             if( output > 1 ) mrl.value += "dst="
588             mrl.value += "std{access=rtp";
589             if( ttl ) mrl.value += "{ttl="+ttl+"}";
590             mrl.value += ",mux="+mux+",dst="+value( 'sout_rtp_addr' );
591             if( value( 'sout_rtp_port' ) )
592                 mrl.value += ":"+value( 'sout_rtp_port' );
593             if( checked( 'sout_sap' ) )
594             {
595                 mrl.value += ",sap";
596                 if( value( 'sout_sap_group' ) != '' )
597                 {
598                     mrl.value += ",group=\""+value( 'sout_sap_group' )+"\"";
599                 }
600                 mrl.value += ",name=\""+value( 'sout_sap_name' )+"\"";
601             }
602             mrl.value += "}";
603             aloo = true;
604         }
605         if( checked( 'sout_udp' ) )
606         {
607             if( aloo ) mrl.value += ",";
608             if( output > 1 ) mrl.value += "dst="
609             mrl.value += "std{access=udp";
610             if( ttl ) mrl.value += "{ttl="+ttl+"}";
611             mrl.value += ",mux="+mux+",dst="+value( 'sout_udp_addr' );
612             if( value('sout_udp_port' ) )
613                 mrl.value += ":"+value( 'sout_udp_port' );
614             if( checked( 'sout_sap' ) )
615             {
616                 mrl.value += ",sap";
617                 if( value( 'sout_sap_group' ) != '' )
618                 {
619                     mrl.value += ",group=\""+value( 'sout_sap_group' )+"\"";
620                 }
621                 mrl.value += ",name=\""+value( 'sout_sap_name' )+"\"";
622             }
623             mrl.value += "}";
624             aloo = true;
625         }
626         if( output > 1 ) mrl.value += "}";
627     }
628
629     if( ( transcode || output ) && checked( 'sout_all' ) )
630         mrl.value += " :sout-all";
631 }
632
633 /* reset sout mrl value */
634 function reset_sout()
635 {
636     document.getElementById('sout_mrl').value = value('sout_old_mrl');
637 }
638
639 function save_sout()
640 {
641     document.getElementById('sout_old_mrl').value = value('sout_mrl');
642 }
643
644 function loop_refresh_status()
645 {
646     setTimeout( 'loop_refresh_status()', 1000 );
647     update_status();
648 }
649 function loop_refresh_playlist()
650 {
651  /*   setTimeout( 'loop_refresh_playlist()', 10000 ); */
652     update_playlist();
653 }
654 function loop_refresh()
655 {
656     setTimeout('loop_refresh_status()',0);
657     setTimeout('loop_refresh_playlist()',0);
658 }
659
660 function browse( dest )
661 {
662     document.getElementById( 'browse_dest' ).value = dest;
663     browse_dir( document.getElementById( 'browse_lastdir' ).value );
664     show( 'browse' );
665 }
666 function browse_dir( dir )
667 {
668     document.getElementById( 'browse_lastdir' ).value = dir;
669     loadXMLDoc( 'requests/browse.xml?dir='+dir, parse_browse_dir );
670 }
671 function browse_path( p )
672 {
673     document.getElementById( document.getElementById( 'browse_dest' ).value ).value = p;
674     hide( 'browse' );
675     document.getElementById( document.getElementById( 'browse_dest' ).value ).focus();
676 }
677 function parse_browse_dir( )
678 {
679     if( req.readyState == 4 )
680     {
681         if( req.status == 200 )
682         {
683             answer = req.responseXML.documentElement;
684             browser = document.getElementById( 'browser' );
685             pos = document.createElement( "div" );
686             elt = answer.firstChild;
687             while( elt )
688             {
689                 if( elt.nodeName == "element" )
690                 {
691                     pos.appendChild( document.createElement( "a" ) );
692                     pos.lastChild.setAttribute( 'class', 'browser' );
693                     if( elt.getAttribute( 'type' ) == 'directory' )
694                     {
695                         pos.lastChild.setAttribute( 'href', 'javascript:browse_dir(\''+elt.getAttribute( 'path' )+'\');');
696                     }
697                     else
698                     {
699                         pos.lastChild.setAttribute( 'href', 'javascript:browse_path(\''+elt.getAttribute( 'path' )+'\');' );
700                     }
701                     pos.lastChild.appendChild( document.createTextNode( elt.getAttribute( 'name' ) ) );
702                 }
703                 elt = elt.nextSibling;
704             }
705             while( browser.hasChildNodes() )
706                 browser.removeChild( browser.firstChild );
707             browser.appendChild( pos );
708         }
709         else
710         {
711             /*alert( 'Error! HTTP server replied: ' + req.status );*/
712         }
713     }
714 }