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 if (typeof $.fn.nsinstall == 'function')
205 widget.$installContainer = $('<div class="container-install-in-widget"></div>')
206 .appendTo(widget.$videoContainer);
207 widget.$installContainer
209 type: widget.options.type,
210 hideIfAlreadyInstalled: true
212 if (widget.$installContainer.nsinstall('option', 'error')
213 == 'already installed')
214 widget.$installContainer.hide();
217 var width = widget.options.width;
218 var height = widget.options.height;
221 if (widget.options.type == 'ns-html5'
222 || widget.options.type == 'html5')
224 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"' : '') + '>'
225 +'Error: Your browser does not support HTML5 or the video format!'
227 .appendTo(widget.$videoContainer)
230 widget.html5.pause();
231 widget.html5.refreshState();
235 widget.html5.refreshState();
238 widget.html5.pause();
239 widget.html5.refreshState();
241 timeupdate: function() {
242 widget.html5.refreshTime();
244 progress: function() {
245 widget.html5.refreshLoadedProgress();
247 loadedmetadata: function() {
248 widget.html5.refreshTime();
249 widget.html5.refreshVolume();
250 widget.html5.refreshState();
251 widget._setWidgetWidth();
253 if (widget.$video[0].error != 3
254 && widget.$video[0].error != 4)
255 widget.$installContainer.hide();
259 widget.html5.refreshState();
261 volumechange: function() {
262 widget.html5.refreshVolume();
265 loadstart: function() {
266 widget.html5.refreshState();
268 suspend: function() {
269 widget.html5.refreshState();
272 widget.html5.refreshState();
275 widget.html5.refreshState();
277 emptied: function() {
278 widget.html5.refreshState();
280 stalled: function() {
281 widget.html5.refreshState();
283 loadeddata: function() {
284 widget.html5.refreshState();
286 waiting: function() {
287 widget.html5.refreshState();
289 seeking: function() {
290 widget.html5.refreshState();
295 else if (widget.options.type == 'ns-vlc'
296 || widget.options.type == 'vlc')
299 if (widget.options.type == 'ns-vlc')
300 embedType = 'application/x-ns-stream';
302 embedType = 'application/x-vlc-plugin';
304 if (navigator.appName == "Netscape")
306 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 + '" />')
307 .appendTo(widget.$videoContainer);
311 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="">'
312 + '<param name="Src" value="' + src + '" />'
313 + '<param name="ShowDisplay" value="True" />'
314 + '<param name="Loop" value="False" />'
315 + '<param name="AutoPlay" value="' + (widget.options.autoplay ? 'True' : 'False') + '" />'
316 + '<param name="Toolbar" value="False" />'
318 .appendTo(widget.$videoContainer);
322 // BUG: this if is because of a NS-VLC bug.
323 if (widget.options.type != 'ns-vlc')
324 widget.$video.css('position', 'relative');
326 // Adjust video size for auto-resizing within ranges minWidth and
328 if ( (width == 0 || height == 0)
329 && (widget.options.minWidth != 0 && widget.options.maxWidth != 0
330 || this.options.type.indexOf('vlc') != -1) )
332 widget.adjustVideoSizeFromMeta();
335 // Initialize video plugin
336 widget.$video.ready(function() {
337 widget.videoPlugin('init');
340 widget._setWidgetWidth();
343 adjustVideoSizeFromMeta: function() {
346 if (typeof widget.options.src == 'object'
347 && (typeof widget.options.src[ widget.options.srcIndex ].res)
349 && (typeof widget.options.src[ widget.options.srcIndex ].dar)
352 var resolution = widget.options.src[ widget.options.srcIndex ].res;
353 var dar = widget.options.src[ widget.options.srcIndex ].dar;
355 dar.substring(0, dar.indexOf(':')));
357 dar.substring(dar.indexOf(':') + 1));
358 var videoHeight = parseInt(
359 resolution.substring(resolution.indexOf('x') + 1));
360 var videoWidth = Math.round(videoHeight * darL / darR);
362 // Video width must be between minWidth and maxWidth pixels.
363 if (widget.options.minWidth != 0 && widget.options.maxWidth != 0)
365 if (videoWidth > widget.options.maxWidth)
367 videoHeight = Math.round(widget.options.maxWidth / videoWidth
369 videoWidth = widget.options.maxWidth;
371 else if (videoWidth < widget.options.minWidth)
373 videoHeight = Math.round(widget.options.minWidth / videoWidth
375 videoWidth = widget.options.minWidth;
379 widget.$video.css('width', videoWidth);
380 widget.$video.css('height', videoHeight);
384 _setWidgetWidth: function() {
385 if (widget.$video.width() < 640)
387 widget.element.css('width',
389 widget.$video.css('left',
390 Math.round(widget.$videoContainer.width()/2
391 - widget.$video.width()/2)
396 widget.element.css('width',
397 widget.$video.width() + 8 + 'px');
398 widget.$video.css('left', '0');
401 this._trigger('resize');
404 setPlayButton: function() {
405 $('button.ui-nsvideo-play', widget.element[0])
406 .button('option', 'icons', { primary: "ui-icon-play" })
409 setPauseButton: function() {
410 $('button.ui-nsvideo-play', widget.element[0])
411 .button('option', 'icons', { primary: "ui-icon-pause" })
414 setMuteButton: function() {
415 $('button.ui-nsvideo-mute', widget.element[0])
416 .button('option', 'icons', { primary: "ui-icon-volume-off" })
419 setUnmuteButton: function() {
420 $('button.ui-nsvideo-mute', widget.element[0])
421 .button('option', 'icons', { primary: "ui-icon-volume-on" })
424 setTimeText: function(text) {
425 //$('.ui-nsvideo-time', widget.element[0])
429 setVolumeSlider: function(vol) {
430 $('.ui-nsvideo-volume', widget.element[0])
431 .slider('value', vol);
433 setProgressSlider: function(prog) {
434 $('.ui-nsvideo-progress', widget.element[0])
435 .slider('value', prog);
437 setLoadedProgressSlider: function(prog) {
438 $('.ui-nsvideo-loaded-progress', widget.element[0])
439 .progressbar('value', prog);
442 videoPlugin: function(method, args) {
443 if (typeof args == 'undefined')
445 var videoPlugin = null;
447 if (this.options.type.indexOf('html5') != -1)
449 videoPlugin = this.html5;
451 else if (this.options.type.indexOf('vlc') != -1)
453 videoPlugin = this.vlc;
457 return videoPlugin[method].apply(this, args);
462 srcIndex: function(srcIndex) {
465 if (typeof srcIndex == 'undefined')
466 return widget.options.srcIndex;
468 widget.options.srcIndex = srcIndex;
476 type: function(type) {
479 if (typeof type == 'undefined')
480 return widget.options.type;
482 widget.videoPlugin('pause');
483 if (widget.vlc.timerHandle)
484 clearTimeout(widget.vlc.timerHandle);
486 widget.options.type = type;
489 // Initialize video plugin
490 widget.$video.ready(function() {
491 widget.videoPlugin('init');
501 if (typeof widget.options.src == 'string')
502 src = widget.options.src;
503 else if (typeof widget.options.src == 'object')
505 if (typeof widget.options.srcIndex == 'undefined')
508 if (typeof widget.options.src[ widget.options.srcIndex ].src
512 src = widget.options.src[ widget.options.srcIndex ].src;
515 if (widget.options.type == 'ns-html5')
516 src = 'tribe://' + src;
526 MEDIA_ERR_ABORTED: [1, "error: aborted"],
527 MEDIA_ERR_NETWORK: [2, "network error"],
528 MEDIA_ERR_DECODE: [3, "decode error"],
529 MEDIA_ERR_SRC_NOT_SUPPORTED: [4, "error: source not supported"]
533 NETWORK_EMPTY: [0, "no data from network"],
534 NETWORK_IDLE: [1, ""],
535 NETWORK_LOADING: [2, "loading..."],
536 NETWORK_LOADED: [3, "loading completed"],
537 NETWORK_NO_SOURCE: [4, "network: no source"]
541 HAVE_NOTHING: [0, "please wait..."],
542 HAVE_METADATA: [1, ""],
543 HAVE_CURRENT_DATA: [2, ""],
544 HAVE_FUTURE_DATA: [3, ""],
545 HAVE_ENOUGH_DATA: [4, "have enough data"]
549 //widget.html5.refreshAll();
551 widget.html5.refreshState();
553 //if (widget.options.autoplay)
554 // widget.html5.play();
557 togglePlay: function() {
558 if (widget.$video[0].paused)
564 widget.html5.pause();
569 if (widget.$video[0].paused)
570 widget.$video[0].play();
572 widget.setPauseButton();
578 if (!widget.$video[0].paused)
579 widget.$video[0].pause();
581 widget.setPlayButton();
586 toggleMute: function() {
587 if (!widget.$video[0].muted)
593 widget.html5.unmute();
598 if (!widget.$video[0].muted)
599 widget.$video[0].muted = true;
601 widget.setMuteButton();
607 if (widget.$video[0].muted)
608 widget.$video[0].muted = false;
610 widget.setUnmuteButton();
616 * Volume value is expressed in percents.
618 volume: function(vol) {
619 if (typeof vol == 'undefined')
620 return Math.round(widget.$video[0].volume * 100);
622 widget.html5.unmute();
623 widget.$video[0].volume = vol / 100;
629 * Seek position is a value between 0 and 1000.
631 crtTime: function(pos) {
633 if (typeof pos == 'undefined')
635 var crtTime = widget.$video[0].currentTime;
636 var totTime = widget.$video[0].duration;
637 if (isNaN(totTime) || totTime == 0)
640 return Math.round(crtTime / totTime * 1000.0);
644 widget.$video[0].currentTime =
645 pos / 1000 * widget.$video[0].duration;
648 refreshAll: function() {
649 widget.html5.refreshState();
650 widget.html5.refreshVolume();
651 widget.html5.refreshLoadedProgress();
652 widget.html5.refreshTime();
655 refreshTime: function() {
656 if (widget.$video[0].seeking)
659 var crtTime = widget.$video[0].currentTime;
660 var totTime = widget.$video[0].duration;
662 // Refresh only at refreshInterval seconds to save CPU time.
663 var delta = crtTime - widget.html5.lastTime;
664 if (typeof widget.html5.lastTime !== "undefined"
665 && delta >= 0 && delta < widget.options.refreshInterval)
667 widget.html5.lastTime = crtTime;
669 // Current time string
670 var crtH = Math.floor(crtTime / 3600);
671 var crtM = Math.floor((crtTime / 60) % 60);
672 var crtS = Math.floor(crtTime % 60);
674 (crtH == 0 ? '' : (widget._leadingZeros(crtH) + ':'))
675 + widget._leadingZeros(crtM) + ':' + widget._leadingZeros(crtS);
678 var totH = Math.floor(totTime / 3600);
679 var totM = Math.floor((totTime / 60) % 60);
680 var totS = Math.floor(totTime % 60);
682 (totH == 0 || isNaN(totH) ? '' : (widget._leadingZeros(totH) + ':'))
683 + widget._leadingZeros(totM) + ':' + widget._leadingZeros(totS);
685 widget.setTimeText('' + strCrtTime + ' / ' + strTotTime);
687 // Update time progress slider.
688 widget.html5.refreshProgress();
693 _state: function(type, code) {
695 $.each(widget.html5[type + '_STATES'], function(index, value) {
696 if ('' + code == '' + value[0])
706 refreshState: function() {
713 if (widget.$video[0].error)
714 err = widget.html5._state('ERR',
715 widget.$video[0].error.code)[1];
717 if (! widget.$video[0].paused)
718 normal = "playing...";
723 if (widget.$video[0].seeking)
725 if (widget.$video[0].ended)
728 network = widget.html5._state('NETWORK',
729 widget.$video[0].networkState)[1];
731 ready = widget.html5._state('READY',
732 widget.$video[0].readyState)[1];
740 if (normal !== "" && (network !== "" || ready !== "") )
746 if (network !== "" && ready !== "")
759 refreshVolume: function() {
762 if (widget.$video[0].muted)
765 vol = Math.floor(widget.$video[0].volume * 100);
767 widget.setVolumeSlider(vol);
772 refreshProgress: function() {
773 widget.setProgressSlider(widget.html5.crtTime());
779 * Supported for Firefox 4.0 or later.
781 refreshLoadedProgress: function() {
782 // Return if buffering status not available in browser.
783 if (typeof widget.$video[0].buffered == 'undefined'
784 || widget.$video[0].buffered.length === 0)
787 var loadedTime = widget.$video[0].buffered.end(0);
788 var totTime = widget.$video[0].duration;
790 if (isNaN(totTime) || totTime == 0)
793 percent = Math.floor(loadedTime / totTime * 100);
795 widget.setLoadedProgressSlider(percent);
800 fullscreen: function() {
801 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.');
808 idleRefreshInterval: 1, // seconds
812 OPENING: [1, "opening..."],
813 BUFFERING: [2, "buffering..."],
814 PLAYING: [3, "playing..."],
815 PAUSED: [4, "paused"],
816 STOPPING: [5, "stopping..."],
822 if (widget.options.autoplay)
824 widget.vlc.refreshAll();
827 togglePlay: function() {
828 if (! widget.$video[0].playlist.isPlaying)
839 if(typeof widget.$video[0].playlist.isPlaying == 'undefined')
842 widget.$installContainer.hide();
844 if (! widget.$video[0].playlist.isPlaying)
845 widget.$video[0].playlist.play();
847 widget.setPauseButton();
849 // Schedule information refreshment at refreshInterval seconds.
850 if (! widget.vlc.timerHandle)
851 widget.vlc.timerHandle = setTimeout(widget.vlc.refreshHandler,
852 widget.options.refreshInterval * 1000);
854 widget.vlc.refreshState();
860 if (typeof widget.$video[0].playlist === 'undefined')
863 if (widget.$video[0].playlist.isPlaying)
864 widget.$video[0].playlist.togglePause();
866 widget.setPlayButton();
868 // Cancel information refreshment scheduling.
869 clearTimeout(widget.vlc.timerHandle);
870 widget.vlc.timerHandle = null;
872 widget.vlc.refreshState();
877 toggleMute: function() {
878 if (! widget.$video[0].audio.mute)
889 if (! widget.$video[0].audio.mute)
890 widget.$video[0].audio.toggleMute();
892 widget.setMuteButton();
894 widget.vlc.refreshVolume();
900 if (widget.$video[0].audio.mute)
901 widget.$video[0].audio.toggleMute();
903 widget.setUnmuteButton();
905 widget.vlc.refreshVolume();
911 * Volume value is expressed in percents.
913 volume: function(vol) {
914 if (typeof vol == 'undefined')
915 return Math.round(widget.$video[0].audio.volume);
918 widget.$video[0].audio.volume = vol;
924 * Seek position is a value between 0 and 1000.
926 crtTime: function(pos) {
928 if (typeof pos == 'undefined')
930 var crtTime = widget.$video[0].input.time;
931 var totTime = widget.$video[0].input.length;
932 if (isNaN(totTime) || totTime == 0)
935 return Math.round(crtTime / totTime * 1000.0);
939 widget.$video[0].input.time =
940 pos / 1000 * widget.$video[0].input.length;
942 widget.vlc.refreshState();
946 * Timeout callback called at refreshInterval during playing in order
947 * to refresh information.
949 refreshHandler: function() {
950 if (widget.$video[0].input.state
951 == widget.vlc.STATES.PLAYING[0])
953 widget.vlc.refreshTime();
954 widget.vlc.timerHandle = setTimeout(widget.vlc.refreshHandler,
955 widget.options.refreshInterval * 1000);
959 if (widget.$video[0].input.state == widget.vlc.STATES.ENDED[0])
963 widget.$video[0].playlist.stop();
965 console.log('Exception: ' + e);
969 widget.vlc.refreshTime();
970 widget.vlc.timerHandle = setTimeout(widget.vlc.refreshHandler,
971 widget.vlc.idleRefreshInterval * 1000);
974 widget.vlc.refreshState();
977 refreshAll: function() {
978 widget.vlc.refreshState();
979 widget.vlc.refreshVolume();
980 widget.vlc.refreshLoadedProgress();
983 widget.vlc.refreshTime();
985 console.log('Exception: ' + e);
986 widget.$time.html('00:00 / ' + widget.options.initialDuration);
990 refreshTime: function() {
991 // TODO while seeking (maybe not necessary for VLC)
992 // if (widget.$video[0].seeking)
995 // Time values in seconds.
996 var crtTime = widget.$video[0].input.time / 1000.0;
997 var totTime = widget.$video[0].input.length / 1000.0;
998 //var crtTime = widget.$video[0].input.position * totTime;
1000 // Current time string
1001 var crtH = Math.floor(crtTime / 3600);
1002 var crtM = Math.floor((crtTime / 60) % 60);
1003 var crtS = Math.floor(crtTime % 60);
1005 (crtH == 0 ? '' : (widget._leadingZeros(crtH) + ':'))
1006 + widget._leadingZeros(crtM) + ':' + widget._leadingZeros(crtS);
1008 // Total time string
1009 var totH = Math.floor(totTime / 3600);
1010 var totM = Math.floor((totTime / 60) % 60);
1011 var totS = Math.floor(totTime % 60);
1013 (totH == 0 || isNaN(totH) ? '' : (widget._leadingZeros(totH) + ':'))
1014 + widget._leadingZeros(totM) + ':' + widget._leadingZeros(totS);
1016 widget.setTimeText('' + strCrtTime + ' / ' + strTotTime);
1018 // Update time progress slider.
1019 widget.vlc.refreshProgress();
1024 _state: function(code) {
1026 $.each(widget.vlc.STATES, function(index, value) {
1027 if ('' + code == '' + value[0])
1037 refreshState: function() {
1039 .html(widget.vlc._state(widget.$video[0].input.state)[1]);
1044 refreshVolume: function() {
1047 if (widget.$video[0].audio.mute)
1050 vol = Math.floor(widget.$video[0].audio.volume);
1052 widget.setVolumeSlider(vol);
1057 refreshProgress: function() {
1058 widget.setProgressSlider(widget.vlc.crtTime());
1064 * Not supported for VLC.
1066 refreshLoadedProgress: function() {
1067 // TODO Currently not possible through VLC API.
1072 fullscreen: function() {
1073 widget.$video[0].video.toggleFullscreen();