2 * jQuery UI NS-Video 1.0.0 beta
4 * Copyright 2011, Călin-Andrei Burloiu
5 * Dual licensed under the MIT or GPL Version 2 licenses.
6 * http://jquery.org/license
13 (function( $, undefined ) {
15 $.widget( "ui.nsvideo", {
16 version: "1.0.0 beta",
25 refreshInterval: 0.1, // seconds
27 initialDuration: "--:--"
34 .addClass( "ui-widget ui-widget-content ui-corner-all" );
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);
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.
60 slide: function(event, ui) {
61 widget.videoPlugin('crtTime', [ui.value]);
62 widget.videoPlugin('refreshTime');
63 widget.videoPlugin('refreshState');
68 $('<button class="ui-nsvideo-play ui-nsvideo-button ui-nsvideo-control-left">Play / Pause</button>')
69 .appendTo(widget.$controls)
72 icons: { primary: "ui-icon-play" }
75 widget.videoPlugin('togglePlay');
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);
83 $('<button class="ui-nsvideo-fullscreen ui-nsvideo-button ui-nsvideo-control-right">Full Screen</button>')
84 .appendTo(widget.$controls)
87 icons: { primary: "ui-icon-arrow-4-diag" }
90 widget.videoPlugin('fullscreen');
93 // Video format buttonset
94 if (typeof widget.options.src == 'object')
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" />')
104 .attr('checked', (index == widget.options.srcIndex))
106 widget.videoPlugin('pause');
107 widget.srcIndex(index);
109 $('<label for="' + id + '">' + definition + '</label>')
113 $formats.buttonset();
117 $('<div class="ui-nsvideo-volume ui-nsvideo-control-right"></div>')
118 .appendTo(widget.$controls)
123 slide: function(event, ui) {
124 widget.videoPlugin('volume', [ui.value]);
129 $('<button class="ui-nsvideo-mute ui-nsvideo-button ui-nsvideo-control-right">Mute</button>')
130 .appendTo(widget.$controls)
133 icons: { primary: "ui-icon-volume-on" }
136 widget.videoPlugin('toggleMute');
139 // Status information
140 widget.$stateText = $('<div class="ui-nsvideo-text ui-nsvideo-control-right">...</div>')
141 .appendTo(widget.$controls)
142 .css('cursor', 'pointer')
144 widget.videoPlugin('refreshAll');
146 if (! widget.options.showState)
147 widget.$stateText.hide();
150 $('<div class="ui-helper-clearfix"></div>')
151 .appendTo(widget.$controls);
154 _destroy: function() {
158 _setOption: function( key, value ) {
160 if ( key === "TODO" ) {
164 this._super( "_setOption", key, value );
167 _leadingZeros: function(number, length) {
175 for (var i=0; i<length; i++)
181 while (str.length < length)
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();
200 widget.$videoContainer.html('');
202 // Install buttons or not supported message if required
203 var $installContainer = $('<div class="container-install-in-widget"></div>')
204 .appendTo(widget.$videoContainer);
207 type: widget.options.type,
208 hideIfAlreadyInstalled: true
211 var width = widget.options.width;
212 var height = widget.options.height;
215 if (widget.options.type == 'ns-html5'
216 || widget.options.type == 'html5')
218 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"' : '') + '>'
219 +'Error: Your browser does not support HTML5 or the video format!'
221 .appendTo(widget.$videoContainer)
224 widget.html5.pause();
225 widget.html5.refreshState();
229 widget.html5.refreshState();
232 widget.html5.pause();
233 widget.html5.refreshState();
235 timeupdate: function() {
236 widget.html5.refreshTime();
238 progress: function() {
239 widget.html5.refreshLoadedProgress();
241 loadedmetadata: function() {
242 widget.html5.refreshTime();
243 widget.html5.refreshVolume();
244 widget.html5.refreshState();
245 widget._setWidgetWidth();
249 widget.html5.refreshState();
251 volumechange: function() {
252 widget.html5.refreshVolume();
255 loadstart: function() {
256 widget.html5.refreshState();
258 suspend: function() {
259 widget.html5.refreshState();
262 widget.html5.refreshState();
265 widget.html5.refreshState();
267 emptied: function() {
268 widget.html5.refreshState();
270 stalled: function() {
271 widget.html5.refreshState();
273 loadeddata: function() {
274 widget.html5.refreshState();
276 waiting: function() {
277 widget.html5.refreshState();
279 seeking: function() {
280 widget.html5.refreshState();
285 else if (widget.options.type == 'ns-vlc'
286 || widget.options.type == 'vlc')
289 if (widget.options.type == 'ns-vlc')
290 embedType = 'application/x-ns-stream';
292 embedType = 'application/x-vlc-plugin';
294 if (navigator.appName == "Netscape")
296 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 + '" />')
297 .appendTo(widget.$videoContainer);
301 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="">'
302 + '<param name="Src" value="' + src + '" />'
303 + '<param name="ShowDisplay" value="True" />'
304 + '<param name="Loop" value="False" />'
305 + '<param name="AutoPlay" value="' + (widget.options.autoplay ? 'True' : 'False') + '" />'
306 + '<param name="Toolbar" value="False" />'
308 .appendTo(widget.$videoContainer);
312 // BUG: this if is because of a NS-VLC bug.
313 if (widget.options.type != 'ns-vlc')
314 widget.$video.css('position', 'relative');
316 // Adjust video size for auto-resizing within ranges minWidth and
318 if ( (width == 0 || height == 0)
319 && (widget.options.minWidth != 0 && widget.options.maxWidth != 0
320 || this.options.type.indexOf('vlc') != -1) )
322 widget.adjustVideoSizeFromMeta();
325 // Initialize video plugin
326 widget.$video.ready(function() {
327 widget.videoPlugin('init');
330 widget._setWidgetWidth();
333 adjustVideoSizeFromMeta: function() {
336 if (typeof widget.options.src == 'object'
337 && (typeof widget.options.src[ widget.options.srcIndex ].res)
339 && (typeof widget.options.src[ widget.options.srcIndex ].dar)
342 var resolution = widget.options.src[ widget.options.srcIndex ].res;
343 var dar = widget.options.src[ widget.options.srcIndex ].dar;
345 dar.substring(0, dar.indexOf(':')));
347 dar.substring(dar.indexOf(':') + 1));
348 var videoHeight = parseInt(
349 resolution.substring(resolution.indexOf('x') + 1));
350 var videoWidth = Math.round(videoHeight * darL / darR);
352 // Video width must be between minWidth and maxWidth pixels.
353 if (widget.options.minWidth != 0 && widget.options.maxWidth != 0)
355 if (videoWidth > widget.options.maxWidth)
357 videoHeight = Math.round(widget.options.maxWidth / videoWidth
359 videoWidth = widget.options.maxWidth;
361 else if (videoWidth < widget.options.minWidth)
363 videoHeight = Math.round(widget.options.minWidth / videoWidth
365 videoWidth = widget.options.minWidth;
369 widget.$video.css('width', videoWidth);
370 widget.$video.css('height', videoHeight);
374 _setWidgetWidth: function() {
375 if (widget.$video.width() < 640)
377 widget.element.css('width',
379 widget.$video.css('left',
380 Math.round(widget.$videoContainer.width()/2
381 - widget.$video.width()/2)
386 widget.element.css('width',
387 widget.$video.width() + 8 + 'px');
388 widget.$video.css('left', '0');
391 this._trigger('resize');
394 setPlayButton: function() {
395 $('button.ui-nsvideo-play', widget.element[0])
396 .button('option', 'icons', { primary: "ui-icon-play" })
399 setPauseButton: function() {
400 $('button.ui-nsvideo-play', widget.element[0])
401 .button('option', 'icons', { primary: "ui-icon-pause" })
404 setMuteButton: function() {
405 $('button.ui-nsvideo-mute', widget.element[0])
406 .button('option', 'icons', { primary: "ui-icon-volume-off" })
409 setUnmuteButton: function() {
410 $('button.ui-nsvideo-mute', widget.element[0])
411 .button('option', 'icons', { primary: "ui-icon-volume-on" })
414 setTimeText: function(text) {
415 //$('.ui-nsvideo-time', widget.element[0])
419 setVolumeSlider: function(vol) {
420 $('.ui-nsvideo-volume', widget.element[0])
421 .slider('value', vol);
423 setProgressSlider: function(prog) {
424 $('.ui-nsvideo-progress', widget.element[0])
425 .slider('value', prog);
427 setLoadedProgressSlider: function(prog) {
428 $('.ui-nsvideo-loaded-progress', widget.element[0])
429 .progressbar('value', prog);
432 videoPlugin: function(method, args) {
433 if (typeof args == 'undefined')
435 var videoPlugin = null;
437 if (this.options.type.indexOf('html5') != -1)
439 videoPlugin = this.html5;
441 else if (this.options.type.indexOf('vlc') != -1)
443 videoPlugin = this.vlc;
447 return videoPlugin[method].apply(this, args);
452 srcIndex: function(srcIndex) {
455 if (typeof srcIndex == 'undefined')
456 return widget.options.srcIndex;
458 widget.options.srcIndex = srcIndex;
466 type: function(type) {
469 if (typeof type == 'undefined')
470 return widget.options.type;
472 widget.videoPlugin('pause');
473 if (widget.vlc.timerHandle)
474 clearTimeout(widget.vlc.timerHandle);
476 widget.options.type = type;
479 // Initialize video plugin
480 widget.$video.ready(function() {
481 widget.videoPlugin('init');
491 if (typeof widget.options.src == 'string')
492 src = widget.options.src;
493 else if (typeof widget.options.src == 'object')
495 if (typeof widget.options.srcIndex == 'undefined')
498 if (typeof widget.options.src[ widget.options.srcIndex ].src
502 src = widget.options.src[ widget.options.srcIndex ].src;
505 if (widget.options.type == 'ns-html5')
506 src = 'tribe://' + src;
516 MEDIA_ERR_ABORTED: [1, "error: aborted"],
517 MEDIA_ERR_NETWORK: [2, "network error"],
518 MEDIA_ERR_DECODE: [3, "decode error"],
519 MEDIA_ERR_SRC_NOT_SUPPORTED: [4, "error: source not supported"]
523 NETWORK_EMPTY: [0, "no data from network"],
524 NETWORK_IDLE: [1, ""],
525 NETWORK_LOADING: [2, "loading..."],
526 NETWORK_LOADED: [3, "loading completed"],
527 NETWORK_NO_SOURCE: [4, "network: no source"]
531 HAVE_NOTHING: [0, "please wait..."],
532 HAVE_METADATA: [1, ""],
533 HAVE_CURRENT_DATA: [2, ""],
534 HAVE_FUTURE_DATA: [3, ""],
535 HAVE_ENOUGH_DATA: [4, "have enough data"]
539 //widget.html5.refreshAll();
541 widget.html5.refreshState();
543 //if (widget.options.autoplay)
544 // widget.html5.play();
547 togglePlay: function() {
548 if (widget.$video[0].paused)
554 widget.html5.pause();
559 if (widget.$video[0].paused)
560 widget.$video[0].play();
562 widget.setPauseButton();
568 if (!widget.$video[0].paused)
569 widget.$video[0].pause();
571 widget.setPlayButton();
576 toggleMute: function() {
577 if (!widget.$video[0].muted)
583 widget.html5.unmute();
588 if (!widget.$video[0].muted)
589 widget.$video[0].muted = true;
591 widget.setMuteButton();
597 if (widget.$video[0].muted)
598 widget.$video[0].muted = false;
600 widget.setUnmuteButton();
606 * Volume value is expressed in percents.
608 volume: function(vol) {
609 if (typeof vol == 'undefined')
610 return Math.round(widget.$video[0].volume * 100);
612 widget.html5.unmute();
613 widget.$video[0].volume = vol / 100;
619 * Seek position is a value between 0 and 1000.
621 crtTime: function(pos) {
623 if (typeof pos == 'undefined')
625 var crtTime = widget.$video[0].currentTime;
626 var totTime = widget.$video[0].duration;
627 if (isNaN(totTime) || totTime == 0)
630 return Math.round(crtTime / totTime * 1000.0);
634 widget.$video[0].currentTime =
635 pos / 1000 * widget.$video[0].duration;
638 refreshAll: function() {
639 widget.html5.refreshState();
640 widget.html5.refreshVolume();
641 widget.html5.refreshLoadedProgress();
642 widget.html5.refreshTime();
645 refreshTime: function() {
646 if (widget.$video[0].seeking)
649 var crtTime = widget.$video[0].currentTime;
650 var totTime = widget.$video[0].duration;
652 // Refresh only at refreshInterval seconds to save CPU time.
653 var delta = crtTime - widget.html5.lastTime;
654 if (typeof widget.html5.lastTime !== "undefined"
655 && delta >= 0 && delta < widget.options.refreshInterval)
657 widget.html5.lastTime = crtTime;
659 // Current time string
660 var crtH = Math.floor(crtTime / 3600);
661 var crtM = Math.floor((crtTime / 60) % 60);
662 var crtS = Math.floor(crtTime % 60);
664 (crtH == 0 ? '' : (widget._leadingZeros(crtH) + ':'))
665 + widget._leadingZeros(crtM) + ':' + widget._leadingZeros(crtS);
668 var totH = Math.floor(totTime / 3600);
669 var totM = Math.floor((totTime / 60) % 60);
670 var totS = Math.floor(totTime % 60);
672 (totH == 0 || isNaN(totH) ? '' : (widget._leadingZeros(totH) + ':'))
673 + widget._leadingZeros(totM) + ':' + widget._leadingZeros(totS);
675 widget.setTimeText('' + strCrtTime + ' / ' + strTotTime);
677 // Update time progress slider.
678 widget.html5.refreshProgress();
683 _state: function(type, code) {
685 $.each(widget.html5[type + '_STATES'], function(index, value) {
686 if ('' + code == '' + value[0])
696 refreshState: function() {
703 if (widget.$video[0].error)
704 err = widget.html5._state('ERR',
705 widget.$video[0].error.code)[1];
707 if (! widget.$video[0].paused)
708 normal = "playing...";
713 if (widget.$video[0].seeking)
715 if (widget.$video[0].ended)
718 network = widget.html5._state('NETWORK',
719 widget.$video[0].networkState)[1];
721 ready = widget.html5._state('READY',
722 widget.$video[0].readyState)[1];
730 if (normal !== "" && (network !== "" || ready !== "") )
736 if (network !== "" && ready !== "")
749 refreshVolume: function() {
752 if (widget.$video[0].muted)
755 vol = Math.floor(widget.$video[0].volume * 100);
757 widget.setVolumeSlider(vol);
762 refreshProgress: function() {
763 widget.setProgressSlider(widget.html5.crtTime());
769 * Supported for Firefox 4.0 or later.
771 refreshLoadedProgress: function() {
772 // Return if buffering status not available in browser.
773 if (typeof widget.$video[0].buffered == 'undefined'
774 || widget.$video[0].buffered.length === 0)
777 var loadedTime = widget.$video[0].buffered.end(0);
778 var totTime = widget.$video[0].duration;
780 if (isNaN(totTime) || totTime == 0)
783 percent = Math.floor(loadedTime / totTime * 100);
785 widget.setLoadedProgressSlider(percent);
790 fullscreen: function() {
791 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.');
798 idleRefreshInterval: 1, // seconds
802 OPENING: [1, "opening..."],
803 BUFFERING: [2, "buffering..."],
804 PLAYING: [3, "playing..."],
805 PAUSED: [4, "paused"],
806 STOPPING: [5, "stopping..."],
812 if (widget.options.autoplay)
814 widget.vlc.refreshAll();
817 togglePlay: function() {
818 if (! widget.$video[0].playlist.isPlaying)
829 if (! widget.$video[0].playlist.isPlaying)
830 widget.$video[0].playlist.play();
832 widget.setPauseButton();
834 // Schedule information refreshment at refreshInterval seconds.
835 if (! widget.vlc.timerHandle)
836 widget.vlc.timerHandle = setTimeout(widget.vlc.refreshHandler,
837 widget.options.refreshInterval * 1000);
839 widget.vlc.refreshState();
845 if (typeof widget.$video[0].playlist === 'undefined')
848 if (widget.$video[0].playlist.isPlaying)
849 widget.$video[0].playlist.togglePause();
851 widget.setPlayButton();
853 // Cancel information refreshment scheduling.
854 clearTimeout(widget.vlc.timerHandle);
855 widget.vlc.timerHandle = null;
857 widget.vlc.refreshState();
862 toggleMute: function() {
863 if (! widget.$video[0].audio.mute)
874 if (! widget.$video[0].audio.mute)
875 widget.$video[0].audio.toggleMute();
877 widget.setMuteButton();
879 widget.vlc.refreshVolume();
885 if (widget.$video[0].audio.mute)
886 widget.$video[0].audio.toggleMute();
888 widget.setUnmuteButton();
890 widget.vlc.refreshVolume();
896 * Volume value is expressed in percents.
898 volume: function(vol) {
899 if (typeof vol == 'undefined')
900 return Math.round(widget.$video[0].audio.volume);
903 widget.$video[0].audio.volume = vol;
909 * Seek position is a value between 0 and 1000.
911 crtTime: function(pos) {
913 if (typeof pos == 'undefined')
915 var crtTime = widget.$video[0].input.time;
916 var totTime = widget.$video[0].input.length;
917 if (isNaN(totTime) || totTime == 0)
920 return Math.round(crtTime / totTime * 1000.0);
924 widget.$video[0].input.time =
925 pos / 1000 * widget.$video[0].input.length;
927 widget.vlc.refreshState();
931 * Timeout callback called at refreshInterval during playing in order
932 * to refresh information.
934 refreshHandler: function() {
935 if (widget.$video[0].input.state
936 == widget.vlc.STATES.PLAYING[0])
938 widget.vlc.refreshTime();
939 widget.vlc.timerHandle = setTimeout(widget.vlc.refreshHandler,
940 widget.options.refreshInterval * 1000);
944 if (widget.$video[0].input.state == widget.vlc.STATES.ENDED[0])
948 widget.$video[0].playlist.stop();
950 console.log('Exception: ' + e);
954 widget.vlc.refreshTime();
955 widget.vlc.timerHandle = setTimeout(widget.vlc.refreshHandler,
956 widget.vlc.idleRefreshInterval * 1000);
959 widget.vlc.refreshState();
962 refreshAll: function() {
963 widget.vlc.refreshState();
964 widget.vlc.refreshVolume();
965 widget.vlc.refreshLoadedProgress();
968 widget.vlc.refreshTime();
970 console.log('Exception: ' + e);
971 widget.$time.html('00:00 / ' + widget.options.initialDuration);
975 refreshTime: function() {
976 // TODO while seeking (maybe not necessary for VLC)
977 // if (widget.$video[0].seeking)
980 // Time values in seconds.
981 var crtTime = widget.$video[0].input.time / 1000.0;
982 var totTime = widget.$video[0].input.length / 1000.0;
983 //var crtTime = widget.$video[0].input.position * totTime;
985 // Current time string
986 var crtH = Math.floor(crtTime / 3600);
987 var crtM = Math.floor((crtTime / 60) % 60);
988 var crtS = Math.floor(crtTime % 60);
990 (crtH == 0 ? '' : (widget._leadingZeros(crtH) + ':'))
991 + widget._leadingZeros(crtM) + ':' + widget._leadingZeros(crtS);
994 var totH = Math.floor(totTime / 3600);
995 var totM = Math.floor((totTime / 60) % 60);
996 var totS = Math.floor(totTime % 60);
998 (totH == 0 || isNaN(totH) ? '' : (widget._leadingZeros(totH) + ':'))
999 + widget._leadingZeros(totM) + ':' + widget._leadingZeros(totS);
1001 widget.setTimeText('' + strCrtTime + ' / ' + strTotTime);
1003 // Update time progress slider.
1004 widget.vlc.refreshProgress();
1009 _state: function(code) {
1011 $.each(widget.vlc.STATES, function(index, value) {
1012 if ('' + code == '' + value[0])
1022 refreshState: function() {
1024 .html(widget.vlc._state(widget.$video[0].input.state)[1]);
1029 refreshVolume: function() {
1032 if (widget.$video[0].audio.mute)
1035 vol = Math.floor(widget.$video[0].audio.volume);
1037 widget.setVolumeSlider(vol);
1042 refreshProgress: function() {
1043 widget.setProgressSlider(widget.vlc.crtTime());
1049 * Not supported for VLC.
1051 refreshLoadedProgress: function() {
1052 // TODO Currently not possible through VLC API.
1057 fullscreen: function() {
1058 widget.$video[0].video.toggleFullscreen();