Mercurial > hg > env-test-daniele
comparison johndyer-mediaelement-13fa20a/src/js/mep-player.js @ 0:032bc65ebafc
added core components
author | George Fazekas <gyorgy.fazekas@eecs.qmul.ac.uk> |
---|---|
date | Wed, 06 Mar 2013 15:45:48 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:032bc65ebafc |
---|---|
1 (function ($) { | |
2 | |
3 // default player values | |
4 mejs.MepDefaults = { | |
5 // url to poster (to fix iOS 3.x) | |
6 poster: '', | |
7 // default if the <video width> is not specified | |
8 defaultVideoWidth: 480, | |
9 // default if the <video height> is not specified | |
10 defaultVideoHeight: 270, | |
11 // if set, overrides <video width> | |
12 videoWidth: -1, | |
13 // if set, overrides <video height> | |
14 videoHeight: -1, | |
15 // width of audio player | |
16 audioWidth: 400, | |
17 // height of audio player | |
18 audioHeight: 30, | |
19 // initial volume when the player starts (overrided by user cookie) | |
20 startVolume: 0.8, | |
21 // useful for <audio> player loops | |
22 loop: false, | |
23 // resize to media dimensions | |
24 enableAutosize: true, | |
25 // forces the hour marker (##:00:00) | |
26 alwaysShowHours: false, | |
27 // Hide controls when playing and mouse is not over the video | |
28 alwaysShowControls: false, | |
29 // force iPad's native controls | |
30 iPadUseNativeControls: true, | |
31 // features to show | |
32 features: ['playpause','current','progress','duration','tracks','volume','fullscreen'] | |
33 }; | |
34 | |
35 mejs.mepIndex = 0; | |
36 | |
37 // wraps a MediaElement object in player controls | |
38 mejs.MediaElementPlayer = function(node, o) { | |
39 // enforce object, even without "new" (via John Resig) | |
40 if ( !(this instanceof mejs.MediaElementPlayer) ) { | |
41 return new mejs.MediaElementPlayer(node, o); | |
42 } | |
43 | |
44 var | |
45 t = this, | |
46 mf = mejs.MediaFeatures; | |
47 | |
48 // create options | |
49 t.options = $.extend({},mejs.MepDefaults,o); | |
50 | |
51 // these will be reset after the MediaElement.success fires | |
52 t.$media = t.$node = $(node); | |
53 t.node = t.media = t.$media[0]; | |
54 | |
55 // check for existing player | |
56 if (typeof t.node.player != 'undefined') { | |
57 return t.node.player; | |
58 } else { | |
59 // attach player to DOM node for reference | |
60 t.node.player = t; | |
61 } | |
62 | |
63 t.init(); | |
64 | |
65 return t; | |
66 }; | |
67 | |
68 // actual player | |
69 mejs.MediaElementPlayer.prototype = { | |
70 init: function() { | |
71 | |
72 var | |
73 t = this, | |
74 mf = mejs.MediaFeatures, | |
75 // options for MediaElement (shim) | |
76 meOptions = $.extend(true, {}, t.options, { | |
77 success: function(media, domNode) { t.meReady(media, domNode); }, | |
78 error: function(e) { t.handleError(e);} | |
79 }); | |
80 | |
81 t.isVideo = (t.media.tagName.toLowerCase() !== 'audio' && !t.options.isVideo); | |
82 | |
83 // use native controls in iPad, iPhone, and Android | |
84 if ((mf.isiPad && t.options.iPadUseNativeControls) || mf.isiPhone) { | |
85 // add controls and stop | |
86 t.$media.attr('controls', 'controls'); | |
87 | |
88 // attempt to fix iOS 3 bug | |
89 t.$media.removeAttr('poster'); | |
90 | |
91 // override Apple's autoplay override for iPads | |
92 if (mf.isiPad && t.media.getAttribute('autoplay') !== null) { | |
93 t.media.load(); | |
94 t.media.play(); | |
95 } | |
96 | |
97 } else if (mf.isAndroid && t.isVideo) { | |
98 | |
99 // leave default player | |
100 | |
101 } else { | |
102 | |
103 // DESKTOP: use MediaElementPlayer controls | |
104 | |
105 // remove native controls | |
106 t.$media.removeAttr('controls'); | |
107 | |
108 // unique ID | |
109 t.id = 'mep_' + mejs.mepIndex++; | |
110 | |
111 // build container | |
112 t.container = | |
113 $('<div id="' + t.id + '" class="mejs-container">'+ | |
114 '<div class="mejs-inner">'+ | |
115 '<div class="mejs-mediaelement"></div>'+ | |
116 '<div class="mejs-layers"></div>'+ | |
117 '<div class="mejs-controls"></div>'+ | |
118 '<div class="mejs-clear"></div>'+ | |
119 '</div>' + | |
120 '</div>') | |
121 .addClass(t.$media[0].className) | |
122 .insertBefore(t.$media); | |
123 | |
124 // move the <video/video> tag into the right spot | |
125 t.container.find('.mejs-mediaelement').append(t.$media); | |
126 | |
127 // find parts | |
128 t.controls = t.container.find('.mejs-controls'); | |
129 t.layers = t.container.find('.mejs-layers'); | |
130 | |
131 // determine the size | |
132 if (t.isVideo) { | |
133 // priority = videoWidth (forced), width attribute, defaultVideoWidth | |
134 t.width = (t.options.videoWidth > 0) ? t.options.videoWidth : (t.$media[0].getAttribute('width') !== null) ? t.$media.attr('width') : t.options.defaultVideoWidth; | |
135 t.height = (t.options.videoHeight > 0) ? t.options.videoHeight : (t.$media[0].getAttribute('height') !== null) ? t.$media.attr('height') : t.options.defaultVideoHeight; | |
136 } else { | |
137 t.width = t.options.audioWidth; | |
138 t.height = t.options.audioHeight; | |
139 } | |
140 | |
141 // set the size, while we wait for the plugins to load below | |
142 t.setPlayerSize(t.width, t.height); | |
143 | |
144 // create MediaElementShim | |
145 meOptions.pluginWidth = t.height; | |
146 meOptions.pluginHeight = t.width; | |
147 } | |
148 | |
149 // create MediaElement shim | |
150 mejs.MediaElement(t.$media[0], meOptions); | |
151 }, | |
152 | |
153 // Sets up all controls and events | |
154 meReady: function(media, domNode) { | |
155 | |
156 | |
157 var t = this, | |
158 mf = mejs.MediaFeatures, | |
159 f, | |
160 feature; | |
161 | |
162 // make sure it can't create itself again if a plugin reloads | |
163 if (t.created) | |
164 return; | |
165 else | |
166 t.created = true; | |
167 | |
168 t.media = media; | |
169 t.domNode = domNode; | |
170 | |
171 if (!mf.isiPhone && !mf.isAndroid && !(mf.isiPad && t.options.iPadUseNativeControls)) { | |
172 | |
173 // two built in features | |
174 t.buildposter(t, t.controls, t.layers, t.media); | |
175 t.buildoverlays(t, t.controls, t.layers, t.media); | |
176 | |
177 // grab for use by features | |
178 t.findTracks(); | |
179 | |
180 // add user-defined features/controls | |
181 for (f in t.options.features) { | |
182 feature = t.options.features[f]; | |
183 if (t['build' + feature]) { | |
184 //try { | |
185 t['build' + feature](t, t.controls, t.layers, t.media); | |
186 //} catch (e) { | |
187 // TODO: report control error | |
188 //throw e; | |
189 //console.log('error building ' + feature); | |
190 //console.log(e); | |
191 //} | |
192 } | |
193 } | |
194 | |
195 t.container.trigger('controlsready'); | |
196 | |
197 // reset all layers and controls | |
198 t.setPlayerSize(t.width, t.height); | |
199 t.setControlsSize(); | |
200 | |
201 | |
202 // controls fade | |
203 if (t.isVideo) { | |
204 // show/hide controls | |
205 t.container | |
206 .bind('mouseenter', function () { | |
207 if (!t.options.alwaysShowControls) { | |
208 t.controls.css('visibility','visible'); | |
209 t.controls.stop(true, true).fadeIn(200); | |
210 } | |
211 }) | |
212 .bind('mouseleave', function () { | |
213 if (!t.media.paused && !t.options.alwaysShowControls) { | |
214 t.controls.stop(true, true).fadeOut(200, function() { | |
215 $(this).css('visibility','hidden'); | |
216 $(this).css('display','block'); | |
217 }); | |
218 } | |
219 }); | |
220 | |
221 // check for autoplay | |
222 if (t.domNode.getAttribute('autoplay') !== null && !t.options.alwaysShowControls) { | |
223 t.controls.css('visibility','hidden'); | |
224 } | |
225 | |
226 // resizer | |
227 if (t.options.enableAutosize) { | |
228 t.media.addEventListener('loadedmetadata', function(e) { | |
229 // if the <video height> was not set and the options.videoHeight was not set | |
230 // then resize to the real dimensions | |
231 if (t.options.videoHeight <= 0 && t.domNode.getAttribute('height') === null && !isNaN(e.target.videoHeight)) { | |
232 t.setPlayerSize(e.target.videoWidth, e.target.videoHeight); | |
233 t.setControlsSize(); | |
234 t.media.setVideoSize(e.target.videoWidth, e.target.videoHeight); | |
235 } | |
236 }, false); | |
237 } | |
238 } | |
239 | |
240 // ended for all | |
241 t.media.addEventListener('ended', function (e) { | |
242 t.media.setCurrentTime(0); | |
243 t.media.pause(); | |
244 | |
245 if (t.setProgressRail) | |
246 t.setProgressRail(); | |
247 if (t.setCurrentRail) | |
248 t.setCurrentRail(); | |
249 | |
250 if (t.options.loop) { | |
251 t.media.play(); | |
252 } else if (!t.options.alwaysShowControls) { | |
253 t.controls.css('visibility','visible'); | |
254 } | |
255 }, true); | |
256 | |
257 // resize on the first play | |
258 t.media.addEventListener('loadedmetadata', function(e) { | |
259 if (t.updateDuration) { | |
260 t.updateDuration(); | |
261 } | |
262 if (t.updateCurrent) { | |
263 t.updateCurrent(); | |
264 } | |
265 | |
266 t.setControlsSize(); | |
267 }, true); | |
268 | |
269 | |
270 // webkit has trouble doing this without a delay | |
271 setTimeout(function () { | |
272 t.setControlsSize(); | |
273 t.setPlayerSize(t.width, t.height); | |
274 }, 50); | |
275 | |
276 } | |
277 | |
278 | |
279 if (t.options.success) { | |
280 t.options.success(t.media, t.domNode); | |
281 } | |
282 }, | |
283 | |
284 handleError: function(e) { | |
285 // Tell user that the file cannot be played | |
286 if (this.options.error) { | |
287 this.options.error(e); | |
288 } | |
289 }, | |
290 | |
291 setPlayerSize: function(width,height) { | |
292 var t = this; | |
293 | |
294 // ie9 appears to need this (jQuery bug?) | |
295 t.width = parseInt(width, 10); | |
296 t.height = parseInt(height, 10); | |
297 | |
298 t.container | |
299 .width(t.width) | |
300 .height(t.height); | |
301 | |
302 t.layers.children('.mejs-layer') | |
303 .width(t.width) | |
304 .height(t.height); | |
305 }, | |
306 | |
307 setControlsSize: function() { | |
308 var t = this, | |
309 usedWidth = 0, | |
310 railWidth = 0, | |
311 rail = t.controls.find('.mejs-time-rail'), | |
312 total = t.controls.find('.mejs-time-total'), | |
313 current = t.controls.find('.mejs-time-current'), | |
314 loaded = t.controls.find('.mejs-time-loaded'); | |
315 others = rail.siblings(); | |
316 | |
317 // find the size of all the other controls besides the rail | |
318 others.each(function() { | |
319 if ($(this).css('position') != 'absolute') { | |
320 usedWidth += $(this).outerWidth(true); | |
321 } | |
322 }); | |
323 // fit the rail into the remaining space | |
324 railWidth = t.controls.width() - usedWidth - (rail.outerWidth(true) - rail.outerWidth(false)); | |
325 | |
326 // outer area | |
327 rail.width(railWidth); | |
328 // dark space | |
329 total.width(railWidth - (total.outerWidth(true) - total.width())); | |
330 | |
331 if (t.setProgressRail) | |
332 t.setProgressRail(); | |
333 if (t.setCurrentRail) | |
334 t.setCurrentRail(); | |
335 }, | |
336 | |
337 | |
338 buildposter: function(player, controls, layers, media) { | |
339 var poster = | |
340 $('<div class="mejs-poster mejs-layer">'+ | |
341 '<img />'+ | |
342 '</div>') | |
343 .appendTo(layers), | |
344 posterUrl = player.$media.attr('poster'), | |
345 posterImg = poster.find('img').width(player.width).height(player.height); | |
346 | |
347 // prioriy goes to option (this is useful if you need to support iOS 3.x (iOS completely fails with poster) | |
348 if (player.options.poster != '') { | |
349 posterImg.attr('src',player.options.poster); | |
350 // second, try the real poster | |
351 } else if (posterUrl !== '' && posterUrl != null) { | |
352 posterImg.attr('src',posterUrl); | |
353 } else { | |
354 poster.remove(); | |
355 } | |
356 | |
357 media.addEventListener('play',function() { | |
358 poster.hide(); | |
359 }, false); | |
360 }, | |
361 | |
362 buildoverlays: function(player, controls, layers, media) { | |
363 if (!player.isVideo) | |
364 return; | |
365 | |
366 var | |
367 loading = | |
368 $('<div class="mejs-overlay mejs-layer">'+ | |
369 '<div class="mejs-overlay-loading"><span></span></div>'+ | |
370 '</div>') | |
371 .hide() // start out hidden | |
372 .appendTo(layers), | |
373 error = | |
374 $('<div class="mejs-overlay mejs-layer">'+ | |
375 '<div class="mejs-overlay-error"></div>'+ | |
376 '</div>') | |
377 .hide() // start out hidden | |
378 .appendTo(layers), | |
379 | |
380 // this needs to come last so it's on top | |
381 bigPlay = | |
382 $('<div class="mejs-overlay mejs-layer mejs-overlay-play">'+ | |
383 '<div class="mejs-overlay-button"></div>'+ | |
384 '</div>') | |
385 .appendTo(layers) | |
386 .click(function() { | |
387 if (media.paused) { | |
388 media.play(); | |
389 } else { | |
390 media.pause(); | |
391 } | |
392 }); | |
393 | |
394 | |
395 // show/hide big play button | |
396 media.addEventListener('play',function() { | |
397 bigPlay.hide(); | |
398 error.hide(); | |
399 }, false); | |
400 media.addEventListener('pause',function() { | |
401 bigPlay.show(); | |
402 }, false); | |
403 | |
404 // show/hide loading | |
405 media.addEventListener('loadstart',function() { | |
406 // for some reason Chrome is firing this event | |
407 if (mejs.MediaFeatures.isChrome && media.getAttribute && media.getAttribute('preload') === 'none') | |
408 return; | |
409 | |
410 loading.show(); | |
411 }, false); | |
412 media.addEventListener('canplay',function() { | |
413 loading.hide(); | |
414 }, false); | |
415 | |
416 // error handling | |
417 media.addEventListener('error',function() { | |
418 loading.hide(); | |
419 error.show(); | |
420 error.find('mejs-overlay-error').html("Error loading this resource"); | |
421 }, false); | |
422 }, | |
423 | |
424 findTracks: function() { | |
425 var t = this, | |
426 tracktags = t.$media.find('track'); | |
427 | |
428 // store for use by plugins | |
429 t.tracks = []; | |
430 tracktags.each(function() { | |
431 t.tracks.push({ | |
432 srclang: $(this).attr('srclang').toLowerCase(), | |
433 src: $(this).attr('src'), | |
434 kind: $(this).attr('kind'), | |
435 entries: [], | |
436 isLoaded: false | |
437 }); | |
438 }); | |
439 }, | |
440 changeSkin: function(className) { | |
441 this.container[0].className = 'mejs-container ' + className; | |
442 this.setPlayerSize(); | |
443 this.setControlsSize(); | |
444 }, | |
445 play: function() { | |
446 this.media.play(); | |
447 }, | |
448 pause: function() { | |
449 this.media.pause(); | |
450 }, | |
451 load: function() { | |
452 this.media.load(); | |
453 }, | |
454 setMuted: function(muted) { | |
455 this.media.setMuted(muted); | |
456 }, | |
457 setCurrentTime: function(time) { | |
458 this.media.setCurrentTime(time); | |
459 }, | |
460 getCurrentTime: function() { | |
461 return this.media.currentTime; | |
462 }, | |
463 setVolume: function(volume) { | |
464 this.media.setVolume(volume); | |
465 }, | |
466 getVolume: function() { | |
467 return this.media.volume; | |
468 }, | |
469 setSrc: function(src) { | |
470 this.media.setSrc(src); | |
471 } | |
472 }; | |
473 | |
474 // turn into jQuery plugin | |
475 if (typeof jQuery != 'undefined') { | |
476 jQuery.fn.mediaelementplayer = function (options) { | |
477 return this.each(function () { | |
478 new mejs.MediaElementPlayer(this, options); | |
479 }); | |
480 }; | |
481 } | |
482 | |
483 // push out to window | |
484 window.MediaElementPlayer = mejs.MediaElementPlayer; | |
485 | |
486 })(mejs.$); |