video widget shows status for html5 videos; video widget bug fixes
[living-lab-site.git] / js / jquery.ui.nsvideo.js
1 /*
2  * jQuery UI NS-Video 1.0.0 beta
3  *
4  * Copyright 2011, Călin-Andrei Burloiu
5  * Dual licensed under the MIT or GPL Version 2 licenses.
6  * http://jquery.org/license
7  *
8  *
9  * Depends:
10  *   jquery.ui.core.js
11  *   jquery.ui.widget.js
12  */
13 (function( $, undefined ) {
14
15 $.widget( "ui.nsvideo", {
16         version: "1.0.0 beta",
17         options: {
18                 type: 'ns-html5',
19                 srcIndex: 0,
20                 width: 0,
21                 height: 0,
22                 minWidth: 0,
23                 maxWidth: 0,
24                 showState: true,
25                 refreshInterval: 0.1,   // seconds
26                 autoplay: false,
27                 initialDuration: "--:--"
28         },
29         
30         _create: function() {
31                 var widget = this;
32                 
33                 widget.element
34                         .addClass( "ui-widget ui-widget-content ui-corner-all" );
35                 
36                 widget.$videoContainer = $('<div class="ui-nsvideo-nsplugin"></div>')
37                         .appendTo(widget.element);
38                 widget.$progressContainer = $('<div class="ui-nsvideo-progress-container ui-widget-content ui-corner-top"></div>')
39                         .appendTo(widget.element);
40                 widget.$progress = $('<div class="ui-nsvideo-progress"></div>')
41                         .appendTo(widget.$progressContainer);
42                 widget.$loadedProgress = $('<div class="ui-nsvideo-loaded-progress"></div>')
43                         .appendTo(widget.$progress);
44                 widget.$controls = $('<div class="ui-nsvideo-controls ui-widget-content ui-corner-bottom"></div>')
45                         .appendTo(widget.element);
46                 
47                 widget.video();
48                 
49                 // Time progress slider with load progress also
50                 widget.$loadedProgress
51                         // TODO an object that inherits progressbar should be used in order to customize min value.
52                         .progressbar({
53                                 value: 0
54                         });
55                 widget.$progress
56                         .slider({
57                                         value: 0,
58                                         min: 0,
59                                         max: 1000,
60                                         slide: function(event, ui) {
61                                                 widget.videoPlugin('crtTime', [ui.value]);
62                                                 widget.videoPlugin('refreshTime');
63                                                 widget.videoPlugin('refreshState');
64                                         }
65                         });
66                 
67                 // Play / Pause
68                 $('<button class="ui-nsvideo-play ui-nsvideo-button ui-nsvideo-control-left">Play / Pause</button>')
69                         .appendTo(widget.$controls)
70                         .button({
71                                 text: false,
72                                 icons: { primary: "ui-icon-play" }
73                         })
74                         .click(function() {
75                                 widget.videoPlugin('togglePlay');
76                         });
77                 
78                 // Time information (current and total)
79                 widget.$time = $('<div class="ui-nsvideo-time ui-nsvideo-text ui-nsvideo-control-left">00:00 / ' + widget.options.initialDuration + '</div>')
80                         .appendTo(widget.$controls);
81                         
82                 // Full screen
83                 $('<button class="ui-nsvideo-fullscreen ui-nsvideo-button ui-nsvideo-control-right">Full Screen</button>')
84                         .appendTo(widget.$controls)
85                         .button({
86                                 text: false,
87                                 icons: { primary: "ui-icon-arrow-4-diag" }
88                         })
89                         .click(function() {
90                                 widget.videoPlugin('fullscreen');
91                         });
92
93                 // Video format buttonset
94                 if (typeof widget.options.src == 'object')
95                 {
96                         var $formats = $('<form><div class="ui-nsvideo-formats ui-nsvideo-control-right"></div></form>')
97                                 .appendTo(widget.$controls);
98                         $formats = $('.ui-nsvideo-formats', $formats[0]);
99                         $.each(widget.options.src, function(index, value) {
100                                 id = widget.element.attr('id') + '-format-' + index;
101                                 definition = value.res.substring(value.res.indexOf('x')+1)+'p';
102                                 $('<input type="radio" id="' + id + '" name="format" />')
103                                         .appendTo($formats)
104                                         .attr('checked', (index == widget.options.srcIndex))
105                                         .click(function() {
106                                                 widget.videoPlugin('pause');
107                                                 widget.srcIndex(index);
108                                         });
109                                 $('<label for="' + id + '">' + definition + '</label>')
110                                         .appendTo($formats);
111                         });
112                         
113                         $formats.buttonset();
114                 }
115                 
116                 // Volume
117                 $('<div class="ui-nsvideo-volume ui-nsvideo-control-right"></div>')
118                         .appendTo(widget.$controls)
119                         .slider({
120                                 range: "min",
121                                 min: 0,
122                                 max: 100,
123                                 slide: function(event, ui) {
124                                         widget.videoPlugin('volume', [ui.value]);
125                                 }
126                         });
127                 
128                 // Toggle Mute
129                 $('<button class="ui-nsvideo-mute ui-nsvideo-button ui-nsvideo-control-right">Mute</button>')
130                         .appendTo(widget.$controls)
131                         .button({
132                                 text: false,
133                                 icons: { primary: "ui-icon-volume-on" }
134                         })
135                         .click(function() {
136                                 widget.videoPlugin('toggleMute');
137                         });
138                         
139                 // Status information
140                 widget.$stateText = $('<div class="ui-nsvideo-text ui-nsvideo-control-right">...</div>')
141                         .appendTo(widget.$controls)
142                         .css('cursor', 'pointer')
143                         .click(function() {
144                                 widget.videoPlugin('refreshAll');
145                         });
146                 if (! widget.options.showState)
147                         widget.$stateText.hide();
148                 
149                 // Clear fix helper
150                 $('<div class="ui-helper-clearfix"></div>')
151                         .appendTo(widget.$controls);
152         },
153
154         _destroy: function() {
155                 
156         },
157
158         _setOption: function( key, value ) {
159                 // TODO
160                 if ( key === "TODO" ) {
161                         
162                 }
163
164                 this._super( "_setOption", key, value );
165         },
166         
167         _leadingZeros: function(number, length) {
168                 if (!length)
169                         length = 2;
170                 
171                 var str = '';
172                 
173                 if (isNaN(number))
174                 {
175                         for (var i=0; i<length; i++)
176                                 str += '-';
177                         return str;
178                 }
179                 
180                 str += number;
181                 while (str.length < length) 
182                 {
183                         str = '0' + str;
184                 }
185
186                 return str;
187         },
188         
189         video: function() {
190                 widget = this;
191                 
192                 // Select video source.
193                 // If src option is string, that's the source.
194                 // If src is an object, there is a list of associative arrays, each
195                 // one having the mandatory keys "src" and "res" (resolution).
196                 var src = widget.crtSrc();
197                 if (src == null)
198                         return widget;
199                 
200                 widget.$videoContainer.html('');
201                 
202                 var width = widget.options.width;
203                 var height = widget.options.height;
204                 
205                 // HTML5
206                 if (widget.options.type == 'ns-html5'
207                         || widget.options.type == 'html5')
208                 {
209                         widget.$video = $('<video id="' + widget.element.attr('id') + '-video" src="' + src + '"' + (width == 0 ? '' : ' width="' + width + '"') + (height == 0 ? '' : ' height="' + height + '"') + ' preload="auto"' + (widget.options.autoplay ? ' autoplay="autoplay"' : '') + '>'
210                                 +'Error: Your browser does not support HTML5 or the video format!'
211                         +'</video>')
212                                 .appendTo(widget.$videoContainer)
213                                 .bind({
214                                         ended: function() {
215                                                 widget.html5.pause();
216                                                 widget.html5.refreshState();
217                                         },
218                                         play: function() {
219                                                 widget.html5.play();
220                                                 widget.html5.refreshState();
221                                         },
222                                         pause: function() {
223                                                 widget.html5.pause();
224                                                 widget.html5.refreshState();
225                                         },
226                                         timeupdate: function() {
227                                                 widget.html5.refreshTime();
228                                         },
229                                         progress: function() {
230                                                 widget.html5.refreshLoadedProgress();
231                                         },
232                                         loadedmetadata: function() {
233                                                 widget.html5.refreshTime();
234                                                 widget.html5.refreshVolume();
235                                                 widget.html5.refreshState();
236                                                 widget._setWidgetWidth();
237                                         },
238                                         seeked: function() {
239                                                 widget.html5.play();
240                                                 widget.html5.refreshState();
241                                         },
242                                         volumechange: function() {
243                                                 widget.html5.refreshVolume();
244                                         },
245                                         
246                                         loadstart: function() {
247                                                 widget.html5.refreshState();
248                                         },
249                                         suspend: function() {
250                                                 widget.html5.refreshState();
251                                         },
252                                         abort: function() {
253                                                 widget.html5.refreshState();
254                                         },
255                                         error: function() {
256                                                 widget.html5.refreshState();
257                                         },
258                                         emptied: function() {
259                                                 widget.html5.refreshState();
260                                         },
261                                         stalled: function() {
262                                                 widget.html5.refreshState();
263                                         },
264                                         loadeddata: function() {
265                                                 widget.html5.refreshState();
266                                         },
267                                         waiting: function() {
268                                                 widget.html5.refreshState();
269                                         },
270                                         seeking: function() {
271                                                 widget.html5.refreshState();
272                                         },
273                                 });
274                 }
275                 // VLC
276                 else if (widget.options.type == 'ns-vlc'
277                         || widget.options.type == 'vlc')
278                 {
279                         var embedType;
280                         if (widget.options.type == 'ns-vlc')
281                                 embedType = 'application/x-ns-stream';
282                         else
283                                 embedType = 'application/x-vlc-plugin';
284                         
285                         if (navigator.appName == "Netscape")
286                         {
287                                 widget.$video = $('<embed type="' + embedType + '" name="vlcVideo" id="' + widget.element.attr('id') + '-video" autoplay="' + (widget.options.autoplay ? 'yes' : 'no') + '" loop="no" width="' + (width == 0 ? '640' : width) + '" height="' + (height == 0 ? '480' : height) + '" target="' + src + '" />')
288                                         .appendTo(widget.$videoContainer);
289                         }
290                         else
291                         {
292                                 widget.$video = $('<object classid="clsid:1800B8AF-4E33-43C0-AFC7-894433C13538" width="' + (width == 0 ? '640' : width) + '" height="' + (height == 0 ? '480' : height) + '" id="' + widget.element.attr('id') + '-video" name="vlcVideo" events="True" target="">'
293                                                 + '<param name="Src" value="' + src + '" />'
294                                                 + '<param name="ShowDisplay" value="True" />'
295                                                 + '<param name="Loop" value="False" />'
296                                                 + '<param name="AutoPlay" value="' + (widget.options.autoplay ? 'True' : 'False') + '" />'
297                                                 + '<param name="Toolbar" value="False" />'
298                                         + '</object>')
299                                         .appendTo(widget.$videoContainer);
300                         }
301                 }
302                 
303                 //  BUG: this if is because of a NS-VLC bug.
304                 if (widget.options.type != 'ns-vlc')
305                         widget.$video.css('position', 'relative');
306                 
307                 // Adjust video size for auto-resizing within ranges minWidth and
308                 // maxWidth.
309                 if ( (width == 0 || height == 0)
310                         && (widget.options.minWidth != 0 && widget.options.maxWidth != 0
311                                 || this.options.type.indexOf('vlc') != -1) )
312                 {
313                         widget.adjustVideoSizeFromMeta();
314                 }
315                 
316                 // Initialize video plugin
317                 widget.$video.ready(function() {
318                         widget.videoPlugin('init');
319                 });
320                 
321                 widget._setWidgetWidth();
322         },
323         
324         adjustVideoSizeFromMeta: function() {
325                 var widget = this;
326                 
327                 if (typeof widget.options.src == 'object'
328                         && (typeof widget.options.src[ widget.options.srcIndex ].res)
329                                 != 'undefined'
330                         && (typeof widget.options.src[ widget.options.srcIndex ].dar)
331                                 != 'undefined')
332                 {
333                         var resolution = widget.options.src[ widget.options.srcIndex ].res;
334                         var dar = widget.options.src[ widget.options.srcIndex ].dar;
335                         var darL = parseInt(
336                                 dar.substring(0, dar.indexOf(':')));
337                         var darR = parseInt(
338                                 dar.substring(dar.indexOf(':') + 1));
339                         var videoHeight = parseInt(
340                                 resolution.substring(resolution.indexOf('x') + 1));
341                         var videoWidth = Math.round(videoHeight * darL / darR);
342                         
343                         // Video width must be between minWidth and maxWidth pixels.
344                         if (widget.options.minWidth != 0 && widget.options.maxWidth != 0)
345                         {
346                                 if (videoWidth > widget.options.maxWidth)
347                                 {
348                                         videoHeight = Math.round(widget.options.maxWidth / videoWidth
349                                                 * videoHeight);
350                                         videoWidth = widget.options.maxWidth;
351                                 }
352                                 else if (videoWidth < widget.options.minWidth)
353                                 {
354                                         videoHeight = Math.round(widget.options.minWidth / videoWidth
355                                                 * videoHeight);
356                                         videoWidth = widget.options.minWidth;
357                                 }
358                         }
359                         
360                         widget.$video.css('width', videoWidth);
361                         widget.$video.css('height', videoHeight);
362                 }
363         },
364         
365         _setWidgetWidth: function() {
366                 if (widget.$video.width() < 640)
367                 {
368                         widget.element.css('width',
369                                                         640 + 8 + 'px');
370                         widget.$video.css('left', 
371                                 Math.round(widget.$videoContainer.width()/2 
372                                 - widget.$video.width()/2)
373                                 + 'px');
374                 }
375                 else
376                 {
377                         widget.element.css('width',
378                                                         widget.$video.width() + 8 + 'px');
379                         widget.$video.css('left', '0');
380                 }
381                 
382                 this._trigger('resize');
383         },
384         
385         setPlayButton: function() {
386                 $('button.ui-nsvideo-play', widget.element[0])
387                         .button('option', 'icons', { primary: "ui-icon-play" })
388                         .button('refresh');
389         },
390         setPauseButton: function() {
391                 $('button.ui-nsvideo-play', widget.element[0])
392                         .button('option', 'icons', { primary: "ui-icon-pause" })
393                         .button('refresh');
394         },
395         setMuteButton: function() {
396                 $('button.ui-nsvideo-mute', widget.element[0])
397                         .button('option', 'icons', { primary: "ui-icon-volume-off" })
398                         .button('refresh');
399         },
400         setUnmuteButton: function() {
401                 $('button.ui-nsvideo-mute', widget.element[0])
402                         .button('option', 'icons', { primary: "ui-icon-volume-on" })
403                         .button('refresh');
404         },
405         setTimeText: function(text) {
406                 //$('.ui-nsvideo-time', widget.element[0])
407                 this.$time
408                         .html(text);
409         },
410         setVolumeSlider: function(vol) {
411                 $('.ui-nsvideo-volume', widget.element[0])
412                         .slider('value', vol);
413         },
414         setProgressSlider: function(prog) {
415                 $('.ui-nsvideo-progress', widget.element[0])
416                         .slider('value', prog);
417         },
418         setLoadedProgressSlider: function(prog) {
419                 $('.ui-nsvideo-loaded-progress', widget.element[0])
420                         .progressbar('value', prog);
421         },
422         
423         videoPlugin: function(method, args) {
424                 if (typeof args == 'undefined')
425                         args = [];
426                 var videoPlugin = null;
427                 
428                 if (this.options.type.indexOf('html5') != -1)
429                 {
430                         videoPlugin = this.html5;
431                 }
432                 else if (this.options.type.indexOf('vlc') != -1)
433                 {
434                         videoPlugin = this.vlc;
435                 }
436                 
437                 if (videoPlugin)
438                         return videoPlugin[method].apply(this, args);
439                 
440                 return null;
441         },
442         
443         srcIndex: function(srcIndex) {
444                 var widget = this;
445                 
446                 if (typeof srcIndex == 'undefined')
447                         return widget.options.srcIndex;
448                 
449                 widget.options.srcIndex = srcIndex;
450                 
451                 // Refresh.
452                 widget.video();
453                 
454                 return widget;
455         },
456         
457         type: function(type) {
458                 var widget = this;
459                 
460                 if (typeof type == 'undefined')
461                         return widget.options.type;
462                 
463                 widget.videoPlugin('pause');
464                 if (widget.vlc.timerHandle)
465                         clearTimeout(widget.vlc.timerHandle);
466                 
467                 widget.options.type = type;
468                 widget.video();
469                 
470                 // Initialize video plugin
471                 widget.$video.ready(function() {
472                         widget.videoPlugin('init');
473                 });
474                 
475                 return widget;
476         },
477         
478         crtSrc: function() {
479                 var src;
480                 var widget = this;
481                 
482                 if (typeof widget.options.src == 'string')
483                         src = widget.options.src;
484                 else if (typeof widget.options.src == 'object')
485                 {
486                         if (typeof widget.options.srcIndex == 'undefined')
487                                 return null;
488                         
489                         if (typeof widget.options.src[ widget.options.srcIndex ].src
490                                 == 'undefined')
491                                 return null;
492                         
493                         src = widget.options.src[ widget.options.srcIndex ].src;
494                 }
495                 
496                 if (widget.options.type == 'ns-html5')
497                         src = 'tribe://' + src;
498                 
499                 return src;
500         },
501         
502         html5: {
503                 widget: this,
504                 //lastTime: null,
505                 
506                 ERR_STATES: {
507                         MEDIA_ERR_ABORTED: [1, "error: aborted"],
508                         MEDIA_ERR_NETWORK: [2, "network error"],
509                         MEDIA_ERR_DECODE: [3, "decode error"],
510                         MEDIA_ERR_SRC_NOT_SUPPORTED: [4, "error: source not supported"]
511                 },
512                 
513                 NETWORK_STATES: {
514                         NETWORK_EMPTY: [0, "no data from network"],
515                         NETWORK_IDLE: [1, ""],
516                         NETWORK_LOADING: [2, "loading..."],
517                         NETWORK_LOADED: [3, "loading completed"],
518                         NETWORK_NO_SOURCE: [4, "network: no source"]
519                 },
520                 
521                 READY_STATES: {
522                         HAVE_NOTHING: [0, "please wait..."],
523                         HAVE_METADATA: [1, ""],
524                         HAVE_CURRENT_DATA: [2, ""],
525                         HAVE_FUTURE_DATA: [3, ""],
526                         HAVE_ENOUGH_DATA: [4, "have enough data"]
527                 },
528                 
529                 init: function() {
530                         //widget.html5.refreshAll();
531                         
532                         widget.html5.refreshState();
533                         
534                         //if (widget.options.autoplay)
535                         //      widget.html5.play();
536                 },
537                 
538                 togglePlay: function() {
539                         if (widget.$video[0].paused)
540                         {
541                                 widget.html5.play();
542                         }
543                         else
544                         {
545                                 widget.html5.pause();
546                         }
547                 },
548
549                 play: function() {
550                         if (widget.$video[0].paused)
551                                 widget.$video[0].play();
552                         
553                         widget.setPauseButton();
554                         
555                         return widget;
556                 },
557                 
558                 pause: function() {
559                         if (!widget.$video[0].paused)
560                                 widget.$video[0].pause();
561                         
562                         widget.setPlayButton();
563
564                         return widget;
565                 },
566                 
567                 toggleMute: function() {
568                         if (!widget.$video[0].muted)
569                         {
570                                 widget.html5.mute();
571                         }
572                         else
573                         {
574                                 widget.html5.unmute();
575                         }
576                 },
577                   
578                 mute: function() {
579                         if (!widget.$video[0].muted)
580                                 widget.$video[0].muted = true;
581                         
582                         widget.setMuteButton();
583                         
584                         return widget;
585                 },
586                 
587                 unmute: function() {
588                         if (widget.$video[0].muted)
589                                 widget.$video[0].muted = false;
590                         
591                         widget.setUnmuteButton();
592                         
593                         return widget;
594                 },
595                 
596                 /**
597                 * Volume value is expressed in percents.
598                 */
599                 volume: function(vol) {
600                         if (typeof vol == 'undefined')
601                                 return Math.round(widget.$video[0].volume * 100);
602                         
603                         widget.html5.unmute();
604                         widget.$video[0].volume = vol / 100;
605                         
606                         return widget;
607                 },
608                 
609                 /**
610                  * Seek position is a value between 0 and 1000.
611                  */
612                 crtTime: function(pos) {
613                         // getter
614                         if (typeof pos == 'undefined')
615                         {
616                                 var crtTime = widget.$video[0].currentTime;
617                                 var totTime = widget.$video[0].duration;
618                                 if (isNaN(totTime) || totTime == 0)
619                                         return 0;
620                                 else
621                                         return Math.round(crtTime / totTime * 1000.0);
622                         }
623                         
624                         // setter
625                         widget.$video[0].currentTime = 
626                                 pos / 1000 * widget.$video[0].duration;
627                 },
628                   
629                 refreshAll: function() {
630                         widget.html5.refreshState();
631                         widget.html5.refreshVolume();
632                         widget.html5.refreshLoadedProgress();
633                         widget.html5.refreshTime();
634                 },
635                 
636                 refreshTime: function() {
637                         if (widget.$video[0].seeking)
638                                 return widget;
639                         
640                         var crtTime = widget.$video[0].currentTime;
641                         var totTime = widget.$video[0].duration;
642                         
643                         // Refresh only at refreshInterval seconds to save CPU time.
644                         var delta = crtTime - widget.html5.lastTime;
645                         if (typeof widget.html5.lastTime !== "undefined"
646                                 && delta >= 0 && delta < widget.options.refreshInterval)
647                                 return widget;
648                         widget.html5.lastTime = crtTime;
649                         
650                         // Current time string
651                         var crtH = Math.floor(crtTime / 3600);
652                         var crtM = Math.floor((crtTime / 60) % 60);
653                         var crtS = Math.floor(crtTime % 60);
654                         var strCrtTime = 
655                                 (crtH == 0 ? '' : (widget._leadingZeros(crtH) + ':'))
656                                 + widget._leadingZeros(crtM) + ':' + widget._leadingZeros(crtS);
657                                 
658                         // Total time string
659                         var totH = Math.floor(totTime / 3600);
660                         var totM = Math.floor((totTime / 60) % 60);
661                         var totS = Math.floor(totTime % 60);
662                         var strTotTime = 
663                                 (totH == 0 || isNaN(totH) ? '' : (widget._leadingZeros(totH) + ':'))
664                                 + widget._leadingZeros(totM) + ':' + widget._leadingZeros(totS);
665                         
666                         widget.setTimeText('' + strCrtTime + ' / ' + strTotTime);
667                         
668                         // Update time progress slider.
669                         widget.html5.refreshProgress();
670                         
671                         return widget;
672                 },
673                 
674                 _state: function(type, code) {
675                         var r;
676                         $.each(widget.html5[type + '_STATES'], function(index, value) {
677                                 if ('' + code == '' + value[0])
678                                 {
679                                         r = value;
680                                         return false;
681                                 }
682                         });
683                         
684                         return r;
685                 },
686                 
687                 refreshState: function() {
688                         var err = "";
689                         var normal = "";
690                         var network = "";
691                         var ready = "";
692                         var state = "";
693                         
694                         if (widget.$video[0].error)
695                                 err = widget.html5._state('ERR',
696                                                                                   widget.$video[0].error.code)[1];
697                           
698                         if (! widget.$video[0].paused)
699                                 normal = "playing...";
700                         else
701                         {
702                                 normal = "paused";
703                         }
704                         if (widget.$video[0].seeking)
705                                 normal = "seeking";
706                         if (widget.$video[0].ended)
707                                 normal = "ended";
708                         
709                         network = widget.html5._state('NETWORK',
710                                                                         widget.$video[0].networkState)[1]; 
711                         
712                         ready = widget.html5._state('READY',
713                                                                         widget.$video[0].readyState)[1];
714                         
715                         if (err !== "")
716                                 state = err;
717                         else
718                         {
719                                 state = normal;
720                                 
721                                 if (normal !== "" && (network !== "" || ready !== "") )
722                                         state += ' / ';
723                                 
724                                 if (network !== "")
725                                         state += network;
726                                 
727                                 if (network !== "" && ready !== "")
728                                         state += ' / ';
729                                 
730                                 if (ready !== "")
731                                         state += ready;
732                         }
733                         
734                         widget.$stateText
735                                 .html(state);
736                         
737                         return widget;
738                 },
739
740                 refreshVolume: function() {
741                         var vol;
742                         
743                         if (widget.$video[0].muted)
744                                 vol = 0;
745                         else
746                                 vol = Math.floor(widget.$video[0].volume * 100);
747                         
748                         widget.setVolumeSlider(vol);
749                         
750                         return widget;
751                 },
752                 
753                 refreshProgress: function() {
754                         widget.setProgressSlider(widget.html5.crtTime());
755                         
756                         return widget;
757                 },
758                 
759                 /**
760                 * Supported for Firefox 4.0 or later.
761                 */
762                 refreshLoadedProgress: function() {
763                         // Return if buffering status not available in browser.
764                         if (typeof widget.$video[0].buffered == 'undefined'
765                                 || widget.$video[0].buffered.length === 0)
766                                 return widget;
767                         
768                         var loadedTime = widget.$video[0].buffered.end(0);
769                         var totTime = widget.$video[0].duration;
770                         var percent;
771                         if (isNaN(totTime) || totTime == 0)
772                                 percent = 0
773                         else
774                                 percent = Math.floor(loadedTime / totTime * 100);
775                         
776                         widget.setLoadedProgressSlider(percent);
777                         
778                         return widget;
779                 },
780
781                 fullscreen: function() {
782                         alert('Your web browser does not support switching to full screen in HTML5 mode with this button. You can switch to full screen manually by right clicking on the video and choosing "Full Screen" from the popup menu.');
783                 }
784         },
785         
786         vlc: {
787                 widget: this,
788                 timerHandle: null,
789                 idleRefreshInterval: 1, // seconds
790                 
791                 STATES: {
792                         IDLE: [0, "idle"],
793                         OPENING: [1, "opening..."],
794                         BUFFERING: [2, "buffering..."],
795                         PLAYING: [3, "playing..."],
796                         PAUSED: [4, "paused"],
797                         STOPPING: [5, "stopping..."],
798                         ENDED: [6, "ended"],
799                         ERROR: [7, "error!"]
800                 },
801                 
802                 init: function() {
803                         if (widget.options.autoplay)
804                                 widget.vlc.play();
805                         widget.vlc.refreshAll();
806                 },
807                 
808                 togglePlay: function() {
809                         if (! widget.$video[0].playlist.isPlaying)
810                         {
811                                 widget.vlc.play();
812                         }
813                         else
814                         {
815                                 widget.vlc.pause();
816                         }
817                 },
818                 
819                 play: function() {
820                         if (! widget.$video[0].playlist.isPlaying)
821                                 widget.$video[0].playlist.play();
822                         
823                         widget.setPauseButton();
824                         
825                         // Schedule information refreshment at refreshInterval seconds.
826                         if (! widget.vlc.timerHandle)
827                                 widget.vlc.timerHandle = setTimeout(widget.vlc.refreshHandler, 
828                                                                                 widget.options.refreshInterval * 1000);
829                                 
830                         widget.vlc.refreshState();
831                         
832                         return widget;
833                 },
834                 
835                 pause: function() {
836                         if (widget.$video[0].playlist.isPlaying)
837                                 widget.$video[0].playlist.togglePause();
838                         
839                         widget.setPlayButton();
840                         
841                         // Cancel information refreshment scheduling.
842                         clearTimeout(widget.vlc.timerHandle);
843                         widget.vlc.timerHandle = null;
844                         
845                         widget.vlc.refreshState();
846
847                         return widget;
848                 },
849                 
850                 toggleMute: function() {
851                         if (! widget.$video[0].audio.mute)
852                         {
853                                 widget.vlc.mute();
854                         }
855                         else
856                         {
857                                 widget.vlc.unmute();
858                         }
859                 },
860                   
861                 mute: function() {
862                         if (! widget.$video[0].audio.mute)
863                                 widget.$video[0].audio.toggleMute();
864                         
865                         widget.setMuteButton();
866                         
867                         widget.vlc.refreshVolume();
868                         
869                         return widget;
870                 },
871                 
872                 unmute: function() {
873                         if (widget.$video[0].audio.mute)
874                                 widget.$video[0].audio.toggleMute();
875                         
876                         widget.setUnmuteButton();
877                         
878                         widget.vlc.refreshVolume();
879                         
880                         return widget;
881                 },
882                 
883                 /**
884                 * Volume value is expressed in percents.
885                 */
886                 volume: function(vol) {
887                         if (typeof vol == 'undefined')
888                                 return Math.round(widget.$video[0].audio.volume);
889                         
890                         widget.vlc.unmute();
891                         widget.$video[0].audio.volume = vol;
892                         
893                         return widget;
894                 },
895                 
896                 /**
897                  * Seek position is a value between 0 and 1000.
898                  */
899                 crtTime: function(pos) {
900                         // getter
901                         if (typeof pos == 'undefined')
902                         {
903                                 var crtTime = widget.$video[0].input.time;
904                                 var totTime = widget.$video[0].input.length;
905                                 if (isNaN(totTime) || totTime == 0)
906                                         return 0;
907                                 else
908                                         return Math.round(crtTime / totTime * 1000.0);
909                         }
910                         
911                         // setter
912                         widget.$video[0].input.time = 
913                                 pos / 1000 * widget.$video[0].input.length;
914                                 
915                         widget.vlc.refreshState();
916                 },
917                 
918                 /**
919                  * Timeout callback called at refreshInterval during playing in order
920                  * to refresh information.
921                  */
922                 refreshHandler: function() {
923                         if (widget.$video[0].input.state
924                                 == widget.vlc.STATES.PLAYING[0])
925                         {
926                                 widget.vlc.refreshTime();
927                                 widget.vlc.timerHandle = setTimeout(widget.vlc.refreshHandler, 
928                                                                                 widget.options.refreshInterval * 1000);
929                         }
930                         else
931                         {
932                                 if (widget.$video[0].input.state == widget.vlc.STATES.ENDED[0])
933                                 {
934                                         widget.vlc.pause();
935                                         try {
936                                                 widget.$video[0].playlist.stop();
937                                         } catch(e) {
938                                                 console.log('Exception: ' + e);
939                                         }
940                                 }
941                                 
942                                 widget.vlc.refreshTime();
943                                 widget.vlc.timerHandle = setTimeout(widget.vlc.refreshHandler, 
944                                                                                 widget.vlc.idleRefreshInterval * 1000);
945                         }
946                         
947                         widget.vlc.refreshState();
948                 },
949                 
950                 refreshAll: function() {
951                         widget.vlc.refreshState();
952                         widget.vlc.refreshVolume();
953                         widget.vlc.refreshLoadedProgress();
954                         
955                         try {
956                                 widget.vlc.refreshTime();
957                         } catch(e) {
958                                 console.log('Exception: ' + e);
959                                 widget.$time.html('00:00 / ' + widget.options.initialDuration);
960                         }
961                 },
962                 
963                 refreshTime: function() {
964                         // TODO while seeking (maybe not necessary for VLC)
965 //                      if (widget.$video[0].seeking)
966 //                              return widget;
967                         
968                         // Time values in seconds.
969                         var crtTime = widget.$video[0].input.time / 1000.0;
970                         var totTime = widget.$video[0].input.length / 1000.0;
971                         //var crtTime = widget.$video[0].input.position * totTime;
972                         
973                         // Current time string
974                         var crtH = Math.floor(crtTime / 3600);
975                         var crtM = Math.floor((crtTime / 60) % 60);
976                         var crtS = Math.floor(crtTime % 60);
977                         var strCrtTime = 
978                                 (crtH == 0 ? '' : (widget._leadingZeros(crtH) + ':'))
979                                 + widget._leadingZeros(crtM) + ':' + widget._leadingZeros(crtS);
980                                 
981                         // Total time string
982                         var totH = Math.floor(totTime / 3600);
983                         var totM = Math.floor((totTime / 60) % 60);
984                         var totS = Math.floor(totTime % 60);
985                         var strTotTime = 
986                                 (totH == 0 || isNaN(totH) ? '' : (widget._leadingZeros(totH) + ':'))
987                                 + widget._leadingZeros(totM) + ':' + widget._leadingZeros(totS);
988                         
989                         widget.setTimeText('' + strCrtTime + ' / ' + strTotTime);
990                         
991                         // Update time progress slider.
992                         widget.vlc.refreshProgress();
993                         
994                         return widget;
995                 },
996                 
997                 _state: function(code) {
998                         var r;
999                         $.each(widget.vlc.STATES, function(index, value) {
1000                                 if ('' + code == '' + value[0])
1001                                 {
1002                                         r = value;
1003                                         return false;
1004                                 }
1005                         });
1006                         
1007                         return r;
1008                 },
1009                 
1010                 refreshState: function() {
1011                         widget.$stateText
1012                                 .html(widget.vlc._state(widget.$video[0].input.state)[1]);
1013                                 
1014                         return widget;
1015                 },
1016
1017                 refreshVolume: function() {
1018                         var vol;
1019                         
1020                         if (widget.$video[0].audio.mute)
1021                                 vol = 0;
1022                         else
1023                                 vol = Math.floor(widget.$video[0].audio.volume);
1024                         
1025                         widget.setVolumeSlider(vol);
1026                         
1027                         return widget;
1028                 },
1029                 
1030                 refreshProgress: function() {
1031                         widget.setProgressSlider(widget.vlc.crtTime());
1032                         
1033                         return widget;
1034                 },
1035                 
1036                 /**
1037                 * Not supported for VLC.
1038                 */
1039                 refreshLoadedProgress: function() {
1040                         // TODO Currently not possible through VLC API.
1041                         
1042                         return widget;
1043                 },
1044
1045                 fullscreen: function() {
1046                         widget.$video[0].video.toggleFullscreen();
1047                 }
1048         }
1049 });
1050
1051 })( jQuery );