a jQuery UI plugin named nsvideo, that plays videos using html5, was implemented...
[living-lab-site.git] / js / jquery.ui.nsvideo.js
1 /*
2  * jQuery UI NS-Video @VERSION
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
14 function padWithZeros(number, length)
15 {
16                 if (!length)
17                         length = 2;
18                 
19                 var str = '';
20                 
21                 if (isNaN(number))
22                 {
23                         for (var i=0; i<length; i++)
24                                 str += '-';
25                         return str;
26                 }
27                 
28                 str += number;
29                 while (str.length < length) 
30                 {
31                         str = '0' + str;
32                 }
33
34                 return str;
35         }
36
37 (function( $, undefined ) {
38
39 $.widget( "ui.nsvideo", {
40         version: "@VERSION",
41         options: {
42                 type: 'ns-html5',
43                 //src: 'http://10.38.128.248/devel/data/torrents/rtt01a_600p.ogv.tstream' // TODO no default src
44                 //src: 'http://10.38.128.248/devel/data/torrents/IndependentaRomaniei_240p.ogv.tstream' // TODO no default src
45         },
46
47         min: 0,
48
49         _create: function() {
50                 var widget = this;
51                 
52                 widget.element
53                         .addClass( "ui-widget ui-widget-content ui-corner-all" );
54                 
55                 widget.$nsPlugin = $('<div class="ui-nsvideo-nsplugin"></div>')
56                         .appendTo(widget.element);
57                 widget.$progressContainer = $('<div class="ui-nsvideo-progress-container ui-widget-content ui-corner-top"></div>')
58                         .appendTo(widget.element);
59                 widget.$progress = $('<div class="ui-nsvideo-progress"></div>')
60                         .appendTo(widget.$progressContainer);
61                 widget.$loadedProgress = $('<div class="ui-nsvideo-loaded-progress"></div>')
62                         .appendTo(widget.$progress);
63                 widget.$controls = $('<div class="ui-nsvideo-controls ui-widget-content ui-corner-bottom"></div>')
64                         .appendTo(widget.element);
65                 
66                 widget.setVideo();
67                 
68                 // Time progress slider with load progress also
69                 widget.$loadedProgress
70                         // TODO an object that inherits progressbar should be used in order to customize min value.
71                         .progressbar({
72                                 value: 0
73                         });
74                 widget.$progress
75                         .slider({
76                                         value: 0,
77                                         min: 0,
78                                         max: 1000, //Math.floor(widget.$video[0].duration),
79                                         slide: function(event, ui) {
80                                                 widget.$video[0].currentTime = 
81                                                         ui.value / 1000 * widget.$video[0].duration;
82                                         }
83                         });
84                 
85                 // Play / Pause
86                 $('<button class="ui-nsvideo-play ui-nsvideo-control-left">Play / Pause</button>')
87                         .appendTo(widget.$controls)
88                         .button({
89                                 text: false,
90                                 icons: { primary: "ui-icon-play" }
91                         })
92                         .click(function() {
93                                 widget.togglePlay();
94                         });
95                 
96                 // Time information (current and total)
97                 $('<div class="ui-nsvideo-time ui-nsvideo-control-left">--:-- / --:--</div>')
98                         .appendTo(widget.$controls);
99
100                 // Video definition buttonset
101                 if (typeof widget.options.src == 'object')
102                 {
103                         var $definitions = $('<form><div class="ui-nsvideo-definitions ui-nsvideo-control-right"></div></form>')
104                                 .appendTo(widget.$controls);
105                         $definitions = $('.ui-nsvideo-definitions', $definitions[0]);
106                         $.each(widget.options.src, function(index, value) {
107                                 id = widget.element.attr('id') + '-def-' + index;
108                                 $('<input type="radio" id="' + id + '" name="definition" />')
109                                         .appendTo($definitions)
110                                         .attr('checked', (index == widget.options.definition))
111                                         .click(function() {
112                                                 widget.setDefinition(index);
113                                         });
114                                 $('<label for="' + id + '">' + index + '</label>')
115                                         .appendTo($definitions);
116                         });
117                         
118                         $definitions.buttonset();
119                 }
120                 
121                 // Volume
122                 $('<div class="ui-nsvideo-volume ui-nsvideo-control-right"></div>')
123                         .appendTo(widget.$controls)
124                         .slider({
125                                 range: "min",
126                                 min: 0,
127                                 max: 100,
128                                 slide: function(event, ui) {
129                                         widget.unmuteHtml5();
130                                         widget.$video[0].volume = ui.value / 100;
131                                 }
132                         });
133                 
134                 // Toggle Mute
135                 $('<button class="ui-nsvideo-mute ui-nsvideo-control-right">Mute</button>')
136                         .appendTo(widget.$controls)
137                         .button({
138                                 text: false,
139                                 icons: { primary: "ui-icon-volume-on" }
140                         })
141                         .click(function() {
142                                 widget.toggleMute();
143                         });
144                         
145                 // Clear fix helper
146                 $('<div class="ui-helper-clearfix"></div>')
147                         .appendTo(widget.$controls);
148         },
149
150         _destroy: function() {
151                 
152         },
153
154         _setOption: function( key, value ) {
155                 if ( key === "TODO" ) {
156                         
157                 }
158
159                 this._super( "_setOption", key, value );
160         },
161         
162         setVideo: function() {
163                 widget = this;
164                 
165                 // Select video source.
166                 // If src option is string, that's the source.
167                 // If src is an object, properties are definitions and values are
168                 // sources.
169                 var src = widget.getCrtSrc();
170                 if (src == null)
171                         return widget;
172                 
173                 widget.$nsPlugin.html('');
174                 
175                 if (widget.options.type == 'ns-html5'
176                         || widget.options.type == 'html5')
177                 {
178                         widget.$video = $('<video id="vid" src="' + src + '" width="800" height="450" preload="auto">'
179                                 +'Error: Your browser does not support HTML5 or the video format!'
180                         +'</video>')
181                                 .appendTo(widget.$nsPlugin)
182                                 .bind({
183                                         ended: function() {
184                                                 widget.pauseHtml5();
185                                         },
186                                         play: function() {
187                                                 widget.playHtml5();
188                                         },
189                                         pause: function() {
190                                                 widget.pauseHtml5();
191                                         },
192                                         timeupdate: function() {
193                                                 widget.refreshTimeHtml5();
194                                         },
195                                         progress: function() {
196                                                 widget.refreshLoadedProgressHtml5();
197                                         },
198                                         loadedmetadata: function() {
199                                                 widget.refreshTimeHtml5();
200                                                 widget.refreshVolumeHtml5();
201                                         },
202                                         seeked: function() {
203                                                 widget.playHtml5();
204                                         },
205                                         volumechange: function() {
206                                                 widget.refreshVolumeHtml5();
207                                         }
208                                 });
209                 }
210         },
211         
212         playHtml5: function() {
213                 if (this.$video[0].paused)
214                         this.$video[0].play();
215                 
216                 $('button.ui-nsvideo-play', this.element[0])
217                         .button('option', 'icons', { primary: "ui-icon-pause" })
218                         .button('refresh');
219                 
220                 return this;
221         },
222         
223         pauseHtml5: function() {
224                 if (!this.$video[0].paused)
225                         this.$video[0].pause();
226                 
227                 $('button.ui-nsvideo-play', this.element[0])
228                         .button('option', 'icons', { primary: "ui-icon-play" })
229                         .button('refresh');
230                 
231                 return this;
232         },
233         
234         refreshTimeHtml5: function() {
235                 var widget = this;
236                 
237                 if (widget.$video[0].seeking)
238                         return widget;
239                 
240                 var crtTime = widget.$video[0].currentTime;
241                 var totTime = widget.$video[0].duration;
242                 
243                 // Refresh only at 0.1 s to save CPU time.
244                 var delta = crtTime - widget.lastTime;
245                 if (typeof widget.lastTime != "undefined" && delta >= 0 && delta < 0.1)
246                         return widget;
247                 widget.lastTime = crtTime;
248                 
249                 // Current time string
250                 var crtH = Math.floor(crtTime / 3600);
251                 var crtM = Math.floor((crtTime / 60) % 60);
252                 var crtS = Math.floor(crtTime % 60);
253                 var strCrtTime = 
254                         (crtH == 0 ? '' : (padWithZeros(crtH) + ':'))
255                         + padWithZeros(crtM) + ':' + padWithZeros(crtS);
256                         
257                 // Total time string
258                 var totH = Math.floor(totTime / 3600);
259                 var totM = Math.floor((totTime / 60) % 60);
260                 var totS = Math.floor(totTime % 60);
261                 var strTotTime = 
262                         (totH == 0 || isNaN(totH) ? '' : (padWithZeros(totH) + ':'))
263                         + padWithZeros(totM) + ':' + padWithZeros(totS);
264                 
265                 $('.ui-nsvideo-time', widget.element[0])
266                         .html('' + strCrtTime + ' / ' + strTotTime);
267                 
268                 // Update time progress slider.
269                 widget.refreshProgressHtml5();
270                 
271                 return widget;
272         },
273         
274         muteHtml5: function() {
275                 if (!this.$video[0].muted)
276                         this.$video[0].muted = true;
277                 
278                 $('button.ui-nsvideo-mute', this.element[0])
279                         .button('option', 'icons', { primary: "ui-icon-volume-off" })
280                         .button('refresh');
281                 
282                 return this;
283         },
284         
285         unmuteHtml5: function() {
286                 if (this.$video[0].muted)
287                         this.$video[0].muted = false;
288                 
289                 $('button.ui-nsvideo-mute', this.element[0])
290                         .button('option', 'icons', { primary: "ui-icon-volume-on" })
291                         .button('refresh');
292                 
293                 return this;
294         },
295         
296         refreshVolumeHtml5: function() {
297                 var vol;
298                 
299                 if (this.$video[0].muted)
300                         vol = 0;
301                 else
302                         vol = Math.floor(this.$video[0].volume * 100);
303                 
304                 $('.ui-nsvideo-volume', this.element[0])
305                         .slider('value', vol);
306                 
307                 return this;
308         },
309         
310         refreshProgressHtml5: function() {
311                 var crtTime = this.$video[0].currentTime;
312                 var totTime = this.$video[0].duration;
313                 var permilia;
314                 if (isNaN(totTime) || totTime == 0)
315                         permilia = 0
316                 else
317                         permilia = Math.floor(crtTime / totTime * 1000);
318                 
319                 $('.ui-nsvideo-progress', this.element[0])
320                         .slider('value', permilia);
321                 
322                 return this;
323         },
324         
325         /**
326          * Supported for Firefox 4.0 or later.
327          */
328         refreshLoadedProgressHtml5: function() {
329                 // Return if buffering status not available in browser.
330                 if (typeof this.$video[0].buffered == 'undefined'
331                         || this.$video[0].buffered.length === 0)
332                         return this;
333                 
334                 var loadedTime = this.$video[0].buffered.end(0);
335                 var totTime = this.$video[0].duration;
336                 var percent;
337                 if (isNaN(totTime) || totTime == 0)
338                         percent = 0
339                 else
340                         percent = Math.floor(loadedTime / totTime * 100);
341                 
342                 $('.ui-nsvideo-loaded-progress', this.element[0])
343                         .progressbar('value', percent);
344                 
345                 return this;
346         },
347         
348         togglePlay: function() {
349                 if (this.options.type.indexOf('html5') != -1)
350                 {
351                         if (this.$video[0].paused)
352                         {
353                                 this.playHtml5();
354                         }
355                         else
356                         {
357                                 this.pauseHtml5();
358                         }
359                 }
360                 else if (this.options.type == 'ns-vlc')
361                 {
362                         
363                 }
364                 
365                 return this;
366         },
367         
368         toggleMute: function() {
369                 if (this.options.type.indexOf('html5') != -1)
370                 {
371                         if (!this.$video[0].muted)
372                         {
373                                 this.muteHtml5();
374                         }
375                         else
376                         {
377                                 this.unmuteHtml5();
378                         }
379                 }
380                 else if (this.options.type == 'ns-vlc')
381                 {
382                         
383                 }
384                 
385                 return this;
386         },
387         
388         getCrtSrc: function() {
389                 var src;
390                 var widget = this;
391                 
392                 if (typeof widget.options.src == 'string')
393                         src = widget.options.src;
394                 else if (typeof widget.options.src == 'object')
395                 {
396                         if (typeof widget.options.definition == 'undefined')
397                                 return null;
398                         
399                         if (typeof widget.options.src[ widget.options.definition ]
400                                 == 'undefined')
401                                 return null;
402                         
403                         src = widget.options.src[ widget.options.definition ];
404                 }
405                 
406                 if (widget.options.type == 'ns-html5')
407                         src = 'tribe://' + src;
408                 
409                 return src;
410         },
411         
412         setDefinition: function(definition) {
413                 if (this.options.type.indexOf('html5') != -1)
414                 {
415                         this.options.definition = definition;
416                         this.setVideo();
417                 }
418                 
419                 return this;
420         }
421 });
422
423 })( jQuery );