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 var id = widget.element.attr('id') + '-format-' + index;
101 var 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() {
387 if (widget.$video.width() < 640)
389 widget.element.css('width',
391 widget.$video.css('left',
392 Math.round(widget.$videoContainer.width()/2
393 - widget.$video.width()/2)
398 widget.element.css('width',
399 widget.$video.width() + 8 + 'px');
400 widget.$video.css('left', '0');
403 this._trigger('resize');
406 setPlayButton: function() {
407 $('button.ui-nsvideo-play', widget.element[0])
408 .button('option', 'icons', { primary: "ui-icon-play" })
411 setPauseButton: function() {
412 $('button.ui-nsvideo-play', widget.element[0])
413 .button('option', 'icons', { primary: "ui-icon-pause" })
416 setMuteButton: function() {
417 $('button.ui-nsvideo-mute', widget.element[0])
418 .button('option', 'icons', { primary: "ui-icon-volume-off" })
421 setUnmuteButton: function() {
422 $('button.ui-nsvideo-mute', widget.element[0])
423 .button('option', 'icons', { primary: "ui-icon-volume-on" })
426 setTimeText: function(text) {
427 //$('.ui-nsvideo-time', widget.element[0])
431 setVolumeSlider: function(vol) {
432 $('.ui-nsvideo-volume', widget.element[0])
433 .slider('value', vol);
435 setProgressSlider: function(prog) {
436 $('.ui-nsvideo-progress', widget.element[0])
437 .slider('value', prog);
439 setLoadedProgressSlider: function(prog) {
440 $('.ui-nsvideo-loaded-progress', widget.element[0])
441 .progressbar('value', prog);
444 videoPlugin: function(method, args) {
445 if (typeof args == 'undefined')
447 var videoPlugin = null;
449 if (this.options.type.indexOf('html5') != -1)
451 videoPlugin = this.html5;
453 else if (this.options.type.indexOf('vlc') != -1)
455 videoPlugin = this.vlc;
459 return videoPlugin[method].apply(this, args);
464 srcIndex: function(srcIndex) {
467 if (typeof srcIndex == 'undefined')
468 return widget.options.srcIndex;
470 widget.options.srcIndex = srcIndex;
478 type: function(type) {
481 if (typeof type == 'undefined')
482 return widget.options.type;
484 widget.videoPlugin('pause');
485 if (widget.vlc.timerHandle)
486 clearTimeout(widget.vlc.timerHandle);
488 widget.options.type = type;
491 // Initialize video plugin
492 widget.$video.ready(function() {
493 widget.videoPlugin('init');
503 if (typeof widget.options.src == 'string')
504 src = widget.options.src;
505 else if (typeof widget.options.src == 'object')
507 if (typeof widget.options.srcIndex == 'undefined')
510 if (typeof widget.options.src[ widget.options.srcIndex ].src
514 src = widget.options.src[ widget.options.srcIndex ].src;
517 if (widget.options.type == 'ns-html5')
518 src = 'tribe://' + src;
528 MEDIA_ERR_ABORTED: [1, "error: aborted"],
529 MEDIA_ERR_NETWORK: [2, "network error"],
530 MEDIA_ERR_DECODE: [3, "decode error"],
531 MEDIA_ERR_SRC_NOT_SUPPORTED: [4, "error: source not supported"]
535 NETWORK_EMPTY: [0, "no data from network"],
536 NETWORK_IDLE: [1, ""],
537 NETWORK_LOADING: [2, "loading..."],
538 NETWORK_LOADED: [3, "loading completed"],
539 NETWORK_NO_SOURCE: [4, "network: no source"]
543 HAVE_NOTHING: [0, "please wait..."],
544 HAVE_METADATA: [1, ""],
545 HAVE_CURRENT_DATA: [2, ""],
546 HAVE_FUTURE_DATA: [3, ""],
547 HAVE_ENOUGH_DATA: [4, "have enough data"]
551 //widget.html5.refreshAll();
553 widget.html5.refreshState();
555 //if (widget.options.autoplay)
556 // widget.html5.play();
559 togglePlay: function() {
560 if (widget.$video[0].paused)
566 widget.html5.pause();
571 if (widget.$video[0].paused)
572 widget.$video[0].play();
574 widget.setPauseButton();
580 if (!widget.$video[0].paused)
581 widget.$video[0].pause();
583 widget.setPlayButton();
588 toggleMute: function() {
589 if (!widget.$video[0].muted)
595 widget.html5.unmute();
600 if (!widget.$video[0].muted)
601 widget.$video[0].muted = true;
603 widget.setMuteButton();
609 if (widget.$video[0].muted)
610 widget.$video[0].muted = false;
612 widget.setUnmuteButton();
618 * Volume value is expressed in percents.
620 volume: function(vol) {
621 if (typeof vol == 'undefined')
622 return Math.round(widget.$video[0].volume * 100);
624 widget.html5.unmute();
625 widget.$video[0].volume = vol / 100;
631 * Seek position is a value between 0 and 1000.
633 crtTime: function(pos) {
635 if (typeof pos == 'undefined')
637 var crtTime = widget.$video[0].currentTime;
638 var totTime = widget.$video[0].duration;
639 if (isNaN(totTime) || totTime == 0)
642 return Math.round(crtTime / totTime * 1000.0);
646 widget.$video[0].currentTime =
647 pos / 1000 * widget.$video[0].duration;
650 refreshAll: function() {
651 widget.html5.refreshState();
652 widget.html5.refreshVolume();
653 widget.html5.refreshLoadedProgress();
654 widget.html5.refreshTime();
657 refreshTime: function() {
658 if (widget.$video[0].seeking)
661 var crtTime = widget.$video[0].currentTime;
662 var totTime = widget.$video[0].duration;
664 // Refresh only at refreshInterval seconds to save CPU time.
665 var delta = crtTime - widget.html5.lastTime;
666 if (typeof widget.html5.lastTime !== "undefined"
667 && delta >= 0 && delta < widget.options.refreshInterval)
669 widget.html5.lastTime = crtTime;
671 // Current time string
672 var crtH = Math.floor(crtTime / 3600);
673 var crtM = Math.floor((crtTime / 60) % 60);
674 var crtS = Math.floor(crtTime % 60);
676 (crtH == 0 ? '' : (widget._leadingZeros(crtH) + ':'))
677 + widget._leadingZeros(crtM) + ':' + widget._leadingZeros(crtS);
680 var totH = Math.floor(totTime / 3600);
681 var totM = Math.floor((totTime / 60) % 60);
682 var totS = Math.floor(totTime % 60);
684 (totH == 0 || isNaN(totH) ? '' : (widget._leadingZeros(totH) + ':'))
685 + widget._leadingZeros(totM) + ':' + widget._leadingZeros(totS);
687 widget.setTimeText('' + strCrtTime + ' / ' + strTotTime);
689 // Update time progress slider.
690 widget.html5.refreshProgress();
695 _state: function(type, code) {
697 $.each(widget.html5[type + '_STATES'], function(index, value) {
698 if ('' + code == '' + value[0])
708 refreshState: function() {
717 if (widget.$video[0].error)
718 err = widget.html5._state('ERR',
719 widget.$video[0].error.code)[1];
721 if (! widget.$video[0].paused)
722 normal = "playing...";
727 if (widget.$video[0].seeking)
729 if (widget.$video[0].ended)
732 network = widget.html5._state('NETWORK',
733 widget.$video[0].networkState)[1];
735 ready = widget.html5._state('READY',
736 widget.$video[0].readyState)[1];
744 if (normal !== "" && (network !== "" || ready !== "") )
750 if (network !== "" && ready !== "")
763 refreshVolume: function() {
766 if (widget.$video[0].muted)
769 vol = Math.floor(widget.$video[0].volume * 100);
771 widget.setVolumeSlider(vol);
776 refreshProgress: function() {
777 widget.setProgressSlider(widget.html5.crtTime());
783 * Supported for Firefox 4.0 or later.
785 refreshLoadedProgress: function() {
786 // Return if buffering status not available in browser.
787 if (typeof widget.$video[0].buffered == 'undefined'
788 || widget.$video[0].buffered.length === 0)
791 var loadedTime = widget.$video[0].buffered.end(0);
792 var totTime = widget.$video[0].duration;
794 if (isNaN(totTime) || totTime == 0)
797 percent = Math.floor(loadedTime / totTime * 100);
799 widget.setLoadedProgressSlider(percent);
804 fullscreen: function() {
805 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.');
812 idleRefreshInterval: 1, // seconds
816 OPENING: [1, "opening..."],
817 BUFFERING: [2, "buffering..."],
818 PLAYING: [3, "playing..."],
819 PAUSED: [4, "paused"],
820 STOPPING: [5, "stopping..."],
826 if (widget.options.autoplay)
828 widget.vlc.refreshAll();
831 togglePlay: function() {
832 if (! widget.$video[0].playlist.isPlaying)
843 if(typeof widget.$video[0].playlist.isPlaying == 'undefined')
846 widget.$installContainer.hide();
848 if (! widget.$video[0].playlist.isPlaying)
849 widget.$video[0].playlist.play();
851 widget.setPauseButton();
853 // Schedule information refreshment at refreshInterval seconds.
854 if (! widget.vlc.timerHandle)
855 widget.vlc.timerHandle = setTimeout(widget.vlc.refreshHandler,
856 widget.options.refreshInterval * 1000);
858 widget.vlc.refreshState();
864 if (typeof widget.$video[0].playlist === 'undefined')
867 if (widget.$video[0].playlist.isPlaying)
868 widget.$video[0].playlist.togglePause();
870 widget.setPlayButton();
872 // Cancel information refreshment scheduling.
873 clearTimeout(widget.vlc.timerHandle);
874 widget.vlc.timerHandle = null;
876 widget.vlc.refreshState();
881 toggleMute: function() {
882 if (! widget.$video[0].audio.mute)
893 if (! widget.$video[0].audio.mute)
894 widget.$video[0].audio.toggleMute();
896 widget.setMuteButton();
898 widget.vlc.refreshVolume();
904 if (widget.$video[0].audio.mute)
905 widget.$video[0].audio.toggleMute();
907 widget.setUnmuteButton();
909 widget.vlc.refreshVolume();
915 * Volume value is expressed in percents.
917 volume: function(vol) {
918 if (typeof vol == 'undefined')
919 return Math.round(widget.$video[0].audio.volume);
922 widget.$video[0].audio.volume = vol;
928 * Seek position is a value between 0 and 1000.
930 crtTime: function(pos) {
932 if (typeof pos == 'undefined')
934 var crtTime = widget.$video[0].input.time;
935 var totTime = widget.$video[0].input.length;
936 if (isNaN(totTime) || totTime == 0)
939 return Math.round(crtTime / totTime * 1000.0);
943 widget.$video[0].input.time =
944 pos / 1000 * widget.$video[0].input.length;
946 widget.vlc.refreshState();
950 * Timeout callback called at refreshInterval during playing in order
951 * to refresh information.
953 refreshHandler: function() {
954 if (widget.$video[0].input.state
955 == widget.vlc.STATES.PLAYING[0])
957 widget.vlc.refreshTime();
958 widget.vlc.timerHandle = setTimeout(widget.vlc.refreshHandler,
959 widget.options.refreshInterval * 1000);
963 if (widget.$video[0].input.state == widget.vlc.STATES.ENDED[0])
967 widget.$video[0].playlist.stop();
969 console.log('Exception: ' + e);
973 widget.vlc.refreshTime();
974 widget.vlc.timerHandle = setTimeout(widget.vlc.refreshHandler,
975 widget.vlc.idleRefreshInterval * 1000);
978 widget.vlc.refreshState();
981 refreshAll: function() {
982 widget.vlc.refreshState();
983 widget.vlc.refreshVolume();
984 widget.vlc.refreshLoadedProgress();
987 widget.vlc.refreshTime();
989 console.log('Exception: ' + e);
990 widget.$time.html('00:00 / ' + widget.options.initialDuration);
994 refreshTime: function() {
995 // TODO while seeking (maybe not necessary for VLC)
996 // if (widget.$video[0].seeking)
999 // Time values in seconds.
1000 var crtTime = widget.$video[0].input.time / 1000.0;
1001 var totTime = widget.$video[0].input.length / 1000.0;
1002 //var crtTime = widget.$video[0].input.position * totTime;
1004 // Current time string
1005 var crtH = Math.floor(crtTime / 3600);
1006 var crtM = Math.floor((crtTime / 60) % 60);
1007 var crtS = Math.floor(crtTime % 60);
1009 (crtH == 0 ? '' : (widget._leadingZeros(crtH) + ':'))
1010 + widget._leadingZeros(crtM) + ':' + widget._leadingZeros(crtS);
1012 // Total time string
1013 var totH = Math.floor(totTime / 3600);
1014 var totM = Math.floor((totTime / 60) % 60);
1015 var totS = Math.floor(totTime % 60);
1017 (totH == 0 || isNaN(totH) ? '' : (widget._leadingZeros(totH) + ':'))
1018 + widget._leadingZeros(totM) + ':' + widget._leadingZeros(totS);
1020 widget.setTimeText('' + strCrtTime + ' / ' + strTotTime);
1022 // Update time progress slider.
1023 widget.vlc.refreshProgress();
1028 _state: function(code) {
1030 $.each(widget.vlc.STATES, function(index, value) {
1031 if ('' + code == '' + value[0])
1041 refreshState: function() {
1043 .html(widget.vlc._state(widget.$video[0].input.state)[1]);
1048 refreshVolume: function() {
1051 if (widget.$video[0].audio.mute)
1054 vol = Math.floor(widget.$video[0].audio.volume);
1056 widget.setVolumeSlider(vol);
1061 refreshProgress: function() {
1062 widget.setProgressSlider(widget.vlc.crtTime());
1068 * Not supported for VLC.
1070 refreshLoadedProgress: function() {
1071 // TODO Currently not possible through VLC API.
1076 fullscreen: function() {
1077 widget.$video[0].video.toggleFullscreen();