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 var width = widget.options.width;
203 var height = widget.options.height;
206 if (widget.options.type == 'ns-html5'
207 || widget.options.type == 'html5')
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!'
212 .appendTo(widget.$videoContainer)
215 widget.html5.pause();
216 widget.html5.refreshState();
220 widget.html5.refreshState();
223 widget.html5.pause();
224 widget.html5.refreshState();
226 timeupdate: function() {
227 widget.html5.refreshTime();
229 progress: function() {
230 widget.html5.refreshLoadedProgress();
232 loadedmetadata: function() {
233 widget.html5.refreshTime();
234 widget.html5.refreshVolume();
235 widget.html5.refreshState();
236 widget._setWidgetWidth();
240 widget.html5.refreshState();
242 volumechange: function() {
243 widget.html5.refreshVolume();
246 loadstart: function() {
247 widget.html5.refreshState();
249 suspend: function() {
250 widget.html5.refreshState();
253 widget.html5.refreshState();
256 widget.html5.refreshState();
258 emptied: function() {
259 widget.html5.refreshState();
261 stalled: function() {
262 widget.html5.refreshState();
264 loadeddata: function() {
265 widget.html5.refreshState();
267 waiting: function() {
268 widget.html5.refreshState();
270 seeking: function() {
271 widget.html5.refreshState();
276 else if (widget.options.type == 'ns-vlc'
277 || widget.options.type == 'vlc')
280 if (widget.options.type == 'ns-vlc')
281 embedType = 'application/x-ns-stream';
283 embedType = 'application/x-vlc-plugin';
285 if (navigator.appName == "Netscape")
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);
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" />'
299 .appendTo(widget.$videoContainer);
303 // BUG: this if is because of a NS-VLC bug.
304 if (widget.options.type != 'ns-vlc')
305 widget.$video.css('position', 'relative');
307 // Adjust video size for auto-resizing within ranges minWidth and
309 if ( (width == 0 || height == 0)
310 && (widget.options.minWidth != 0 && widget.options.maxWidth != 0
311 || this.options.type.indexOf('vlc') != -1) )
313 widget.adjustVideoSizeFromMeta();
316 // Initialize video plugin
317 widget.$video.ready(function() {
318 widget.videoPlugin('init');
321 widget._setWidgetWidth();
324 adjustVideoSizeFromMeta: function() {
327 if (typeof widget.options.src == 'object'
328 && (typeof widget.options.src[ widget.options.srcIndex ].res)
330 && (typeof widget.options.src[ widget.options.srcIndex ].dar)
333 var resolution = widget.options.src[ widget.options.srcIndex ].res;
334 var dar = widget.options.src[ widget.options.srcIndex ].dar;
336 dar.substring(0, dar.indexOf(':')));
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);
343 // Video width must be between minWidth and maxWidth pixels.
344 if (widget.options.minWidth != 0 && widget.options.maxWidth != 0)
346 if (videoWidth > widget.options.maxWidth)
348 videoHeight = Math.round(widget.options.maxWidth / videoWidth
350 videoWidth = widget.options.maxWidth;
352 else if (videoWidth < widget.options.minWidth)
354 videoHeight = Math.round(widget.options.minWidth / videoWidth
356 videoWidth = widget.options.minWidth;
360 widget.$video.css('width', videoWidth);
361 widget.$video.css('height', videoHeight);
365 _setWidgetWidth: function() {
366 if (widget.$video.width() < 640)
368 widget.element.css('width',
370 widget.$video.css('left',
371 Math.round(widget.$videoContainer.width()/2
372 - widget.$video.width()/2)
377 widget.element.css('width',
378 widget.$video.width() + 8 + 'px');
379 widget.$video.css('left', '0');
382 this._trigger('resize');
385 setPlayButton: function() {
386 $('button.ui-nsvideo-play', widget.element[0])
387 .button('option', 'icons', { primary: "ui-icon-play" })
390 setPauseButton: function() {
391 $('button.ui-nsvideo-play', widget.element[0])
392 .button('option', 'icons', { primary: "ui-icon-pause" })
395 setMuteButton: function() {
396 $('button.ui-nsvideo-mute', widget.element[0])
397 .button('option', 'icons', { primary: "ui-icon-volume-off" })
400 setUnmuteButton: function() {
401 $('button.ui-nsvideo-mute', widget.element[0])
402 .button('option', 'icons', { primary: "ui-icon-volume-on" })
405 setTimeText: function(text) {
406 //$('.ui-nsvideo-time', widget.element[0])
410 setVolumeSlider: function(vol) {
411 $('.ui-nsvideo-volume', widget.element[0])
412 .slider('value', vol);
414 setProgressSlider: function(prog) {
415 $('.ui-nsvideo-progress', widget.element[0])
416 .slider('value', prog);
418 setLoadedProgressSlider: function(prog) {
419 $('.ui-nsvideo-loaded-progress', widget.element[0])
420 .progressbar('value', prog);
423 videoPlugin: function(method, args) {
424 if (typeof args == 'undefined')
426 var videoPlugin = null;
428 if (this.options.type.indexOf('html5') != -1)
430 videoPlugin = this.html5;
432 else if (this.options.type.indexOf('vlc') != -1)
434 videoPlugin = this.vlc;
438 return videoPlugin[method].apply(this, args);
443 srcIndex: function(srcIndex) {
446 if (typeof srcIndex == 'undefined')
447 return widget.options.srcIndex;
449 widget.options.srcIndex = srcIndex;
457 type: function(type) {
460 if (typeof type == 'undefined')
461 return widget.options.type;
463 widget.videoPlugin('pause');
464 if (widget.vlc.timerHandle)
465 clearTimeout(widget.vlc.timerHandle);
467 widget.options.type = type;
470 // Initialize video plugin
471 widget.$video.ready(function() {
472 widget.videoPlugin('init');
482 if (typeof widget.options.src == 'string')
483 src = widget.options.src;
484 else if (typeof widget.options.src == 'object')
486 if (typeof widget.options.srcIndex == 'undefined')
489 if (typeof widget.options.src[ widget.options.srcIndex ].src
493 src = widget.options.src[ widget.options.srcIndex ].src;
496 if (widget.options.type == 'ns-html5')
497 src = 'tribe://' + src;
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"]
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"]
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"]
530 //widget.html5.refreshAll();
532 widget.html5.refreshState();
534 //if (widget.options.autoplay)
535 // widget.html5.play();
538 togglePlay: function() {
539 if (widget.$video[0].paused)
545 widget.html5.pause();
550 if (widget.$video[0].paused)
551 widget.$video[0].play();
553 widget.setPauseButton();
559 if (!widget.$video[0].paused)
560 widget.$video[0].pause();
562 widget.setPlayButton();
567 toggleMute: function() {
568 if (!widget.$video[0].muted)
574 widget.html5.unmute();
579 if (!widget.$video[0].muted)
580 widget.$video[0].muted = true;
582 widget.setMuteButton();
588 if (widget.$video[0].muted)
589 widget.$video[0].muted = false;
591 widget.setUnmuteButton();
597 * Volume value is expressed in percents.
599 volume: function(vol) {
600 if (typeof vol == 'undefined')
601 return Math.round(widget.$video[0].volume * 100);
603 widget.html5.unmute();
604 widget.$video[0].volume = vol / 100;
610 * Seek position is a value between 0 and 1000.
612 crtTime: function(pos) {
614 if (typeof pos == 'undefined')
616 var crtTime = widget.$video[0].currentTime;
617 var totTime = widget.$video[0].duration;
618 if (isNaN(totTime) || totTime == 0)
621 return Math.round(crtTime / totTime * 1000.0);
625 widget.$video[0].currentTime =
626 pos / 1000 * widget.$video[0].duration;
629 refreshAll: function() {
630 widget.html5.refreshState();
631 widget.html5.refreshVolume();
632 widget.html5.refreshLoadedProgress();
633 widget.html5.refreshTime();
636 refreshTime: function() {
637 if (widget.$video[0].seeking)
640 var crtTime = widget.$video[0].currentTime;
641 var totTime = widget.$video[0].duration;
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)
648 widget.html5.lastTime = crtTime;
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);
655 (crtH == 0 ? '' : (widget._leadingZeros(crtH) + ':'))
656 + widget._leadingZeros(crtM) + ':' + widget._leadingZeros(crtS);
659 var totH = Math.floor(totTime / 3600);
660 var totM = Math.floor((totTime / 60) % 60);
661 var totS = Math.floor(totTime % 60);
663 (totH == 0 || isNaN(totH) ? '' : (widget._leadingZeros(totH) + ':'))
664 + widget._leadingZeros(totM) + ':' + widget._leadingZeros(totS);
666 widget.setTimeText('' + strCrtTime + ' / ' + strTotTime);
668 // Update time progress slider.
669 widget.html5.refreshProgress();
674 _state: function(type, code) {
676 $.each(widget.html5[type + '_STATES'], function(index, value) {
677 if ('' + code == '' + value[0])
687 refreshState: function() {
694 if (widget.$video[0].error)
695 err = widget.html5._state('ERR',
696 widget.$video[0].error.code)[1];
698 if (! widget.$video[0].paused)
699 normal = "playing...";
704 if (widget.$video[0].seeking)
706 if (widget.$video[0].ended)
709 network = widget.html5._state('NETWORK',
710 widget.$video[0].networkState)[1];
712 ready = widget.html5._state('READY',
713 widget.$video[0].readyState)[1];
721 if (normal !== "" && (network !== "" || ready !== "") )
727 if (network !== "" && ready !== "")
740 refreshVolume: function() {
743 if (widget.$video[0].muted)
746 vol = Math.floor(widget.$video[0].volume * 100);
748 widget.setVolumeSlider(vol);
753 refreshProgress: function() {
754 widget.setProgressSlider(widget.html5.crtTime());
760 * Supported for Firefox 4.0 or later.
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)
768 var loadedTime = widget.$video[0].buffered.end(0);
769 var totTime = widget.$video[0].duration;
771 if (isNaN(totTime) || totTime == 0)
774 percent = Math.floor(loadedTime / totTime * 100);
776 widget.setLoadedProgressSlider(percent);
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.');
789 idleRefreshInterval: 1, // seconds
793 OPENING: [1, "opening..."],
794 BUFFERING: [2, "buffering..."],
795 PLAYING: [3, "playing..."],
796 PAUSED: [4, "paused"],
797 STOPPING: [5, "stopping..."],
803 if (widget.options.autoplay)
805 widget.vlc.refreshAll();
808 togglePlay: function() {
809 if (! widget.$video[0].playlist.isPlaying)
820 if (! widget.$video[0].playlist.isPlaying)
821 widget.$video[0].playlist.play();
823 widget.setPauseButton();
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);
830 widget.vlc.refreshState();
836 if (widget.$video[0].playlist.isPlaying)
837 widget.$video[0].playlist.togglePause();
839 widget.setPlayButton();
841 // Cancel information refreshment scheduling.
842 clearTimeout(widget.vlc.timerHandle);
843 widget.vlc.timerHandle = null;
845 widget.vlc.refreshState();
850 toggleMute: function() {
851 if (! widget.$video[0].audio.mute)
862 if (! widget.$video[0].audio.mute)
863 widget.$video[0].audio.toggleMute();
865 widget.setMuteButton();
867 widget.vlc.refreshVolume();
873 if (widget.$video[0].audio.mute)
874 widget.$video[0].audio.toggleMute();
876 widget.setUnmuteButton();
878 widget.vlc.refreshVolume();
884 * Volume value is expressed in percents.
886 volume: function(vol) {
887 if (typeof vol == 'undefined')
888 return Math.round(widget.$video[0].audio.volume);
891 widget.$video[0].audio.volume = vol;
897 * Seek position is a value between 0 and 1000.
899 crtTime: function(pos) {
901 if (typeof pos == 'undefined')
903 var crtTime = widget.$video[0].input.time;
904 var totTime = widget.$video[0].input.length;
905 if (isNaN(totTime) || totTime == 0)
908 return Math.round(crtTime / totTime * 1000.0);
912 widget.$video[0].input.time =
913 pos / 1000 * widget.$video[0].input.length;
915 widget.vlc.refreshState();
919 * Timeout callback called at refreshInterval during playing in order
920 * to refresh information.
922 refreshHandler: function() {
923 if (widget.$video[0].input.state
924 == widget.vlc.STATES.PLAYING[0])
926 widget.vlc.refreshTime();
927 widget.vlc.timerHandle = setTimeout(widget.vlc.refreshHandler,
928 widget.options.refreshInterval * 1000);
932 if (widget.$video[0].input.state == widget.vlc.STATES.ENDED[0])
936 widget.$video[0].playlist.stop();
938 console.log('Exception: ' + e);
942 widget.vlc.refreshTime();
943 widget.vlc.timerHandle = setTimeout(widget.vlc.refreshHandler,
944 widget.vlc.idleRefreshInterval * 1000);
947 widget.vlc.refreshState();
950 refreshAll: function() {
951 widget.vlc.refreshState();
952 widget.vlc.refreshVolume();
953 widget.vlc.refreshLoadedProgress();
956 widget.vlc.refreshTime();
958 console.log('Exception: ' + e);
959 widget.$time.html('00:00 / ' + widget.options.initialDuration);
963 refreshTime: function() {
964 // TODO while seeking (maybe not necessary for VLC)
965 // if (widget.$video[0].seeking)
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;
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);
978 (crtH == 0 ? '' : (widget._leadingZeros(crtH) + ':'))
979 + widget._leadingZeros(crtM) + ':' + widget._leadingZeros(crtS);
982 var totH = Math.floor(totTime / 3600);
983 var totM = Math.floor((totTime / 60) % 60);
984 var totS = Math.floor(totTime % 60);
986 (totH == 0 || isNaN(totH) ? '' : (widget._leadingZeros(totH) + ':'))
987 + widget._leadingZeros(totM) + ':' + widget._leadingZeros(totS);
989 widget.setTimeText('' + strCrtTime + ' / ' + strTotTime);
991 // Update time progress slider.
992 widget.vlc.refreshProgress();
997 _state: function(code) {
999 $.each(widget.vlc.STATES, function(index, value) {
1000 if ('' + code == '' + value[0])
1010 refreshState: function() {
1012 .html(widget.vlc._state(widget.$video[0].input.state)[1]);
1017 refreshVolume: function() {
1020 if (widget.$video[0].audio.mute)
1023 vol = Math.floor(widget.$video[0].audio.volume);
1025 widget.setVolumeSlider(vol);
1030 refreshProgress: function() {
1031 widget.setProgressSlider(widget.vlc.crtTime());
1037 * Not supported for VLC.
1039 refreshLoadedProgress: function() {
1040 // TODO Currently not possible through VLC API.
1045 fullscreen: function() {
1046 widget.$video[0].video.toggleFullscreen();