Mercurial > hg > env-test-daniele
view johndyer-mediaelement-13fa20a/src/js/mep-player.js @ 25:4a4bd554b4c1 tip
Closing this sub branch.
author | Daniele Barchiesi <daniele.barchiesi@eecs.qmul.ac.uk> |
---|---|
date | Mon, 25 Mar 2013 14:02:54 +0000 |
parents | 032bc65ebafc |
children |
line wrap: on
line source
(function ($) { // default player values mejs.MepDefaults = { // url to poster (to fix iOS 3.x) poster: '', // default if the <video width> is not specified defaultVideoWidth: 480, // default if the <video height> is not specified defaultVideoHeight: 270, // if set, overrides <video width> videoWidth: -1, // if set, overrides <video height> videoHeight: -1, // width of audio player audioWidth: 400, // height of audio player audioHeight: 30, // initial volume when the player starts (overrided by user cookie) startVolume: 0.8, // useful for <audio> player loops loop: false, // resize to media dimensions enableAutosize: true, // forces the hour marker (##:00:00) alwaysShowHours: false, // Hide controls when playing and mouse is not over the video alwaysShowControls: false, // force iPad's native controls iPadUseNativeControls: true, // features to show features: ['playpause','current','progress','duration','tracks','volume','fullscreen'] }; mejs.mepIndex = 0; // wraps a MediaElement object in player controls mejs.MediaElementPlayer = function(node, o) { // enforce object, even without "new" (via John Resig) if ( !(this instanceof mejs.MediaElementPlayer) ) { return new mejs.MediaElementPlayer(node, o); } var t = this, mf = mejs.MediaFeatures; // create options t.options = $.extend({},mejs.MepDefaults,o); // these will be reset after the MediaElement.success fires t.$media = t.$node = $(node); t.node = t.media = t.$media[0]; // check for existing player if (typeof t.node.player != 'undefined') { return t.node.player; } else { // attach player to DOM node for reference t.node.player = t; } t.init(); return t; }; // actual player mejs.MediaElementPlayer.prototype = { init: function() { var t = this, mf = mejs.MediaFeatures, // options for MediaElement (shim) meOptions = $.extend(true, {}, t.options, { success: function(media, domNode) { t.meReady(media, domNode); }, error: function(e) { t.handleError(e);} }); t.isVideo = (t.media.tagName.toLowerCase() !== 'audio' && !t.options.isVideo); // use native controls in iPad, iPhone, and Android if ((mf.isiPad && t.options.iPadUseNativeControls) || mf.isiPhone) { // add controls and stop t.$media.attr('controls', 'controls'); // attempt to fix iOS 3 bug t.$media.removeAttr('poster'); // override Apple's autoplay override for iPads if (mf.isiPad && t.media.getAttribute('autoplay') !== null) { t.media.load(); t.media.play(); } } else if (mf.isAndroid && t.isVideo) { // leave default player } else { // DESKTOP: use MediaElementPlayer controls // remove native controls t.$media.removeAttr('controls'); // unique ID t.id = 'mep_' + mejs.mepIndex++; // build container t.container = $('<div id="' + t.id + '" class="mejs-container">'+ '<div class="mejs-inner">'+ '<div class="mejs-mediaelement"></div>'+ '<div class="mejs-layers"></div>'+ '<div class="mejs-controls"></div>'+ '<div class="mejs-clear"></div>'+ '</div>' + '</div>') .addClass(t.$media[0].className) .insertBefore(t.$media); // move the <video/video> tag into the right spot t.container.find('.mejs-mediaelement').append(t.$media); // find parts t.controls = t.container.find('.mejs-controls'); t.layers = t.container.find('.mejs-layers'); // determine the size if (t.isVideo) { // priority = videoWidth (forced), width attribute, defaultVideoWidth t.width = (t.options.videoWidth > 0) ? t.options.videoWidth : (t.$media[0].getAttribute('width') !== null) ? t.$media.attr('width') : t.options.defaultVideoWidth; t.height = (t.options.videoHeight > 0) ? t.options.videoHeight : (t.$media[0].getAttribute('height') !== null) ? t.$media.attr('height') : t.options.defaultVideoHeight; } else { t.width = t.options.audioWidth; t.height = t.options.audioHeight; } // set the size, while we wait for the plugins to load below t.setPlayerSize(t.width, t.height); // create MediaElementShim meOptions.pluginWidth = t.height; meOptions.pluginHeight = t.width; } // create MediaElement shim mejs.MediaElement(t.$media[0], meOptions); }, // Sets up all controls and events meReady: function(media, domNode) { var t = this, mf = mejs.MediaFeatures, f, feature; // make sure it can't create itself again if a plugin reloads if (t.created) return; else t.created = true; t.media = media; t.domNode = domNode; if (!mf.isiPhone && !mf.isAndroid && !(mf.isiPad && t.options.iPadUseNativeControls)) { // two built in features t.buildposter(t, t.controls, t.layers, t.media); t.buildoverlays(t, t.controls, t.layers, t.media); // grab for use by features t.findTracks(); // add user-defined features/controls for (f in t.options.features) { feature = t.options.features[f]; if (t['build' + feature]) { //try { t['build' + feature](t, t.controls, t.layers, t.media); //} catch (e) { // TODO: report control error //throw e; //console.log('error building ' + feature); //console.log(e); //} } } t.container.trigger('controlsready'); // reset all layers and controls t.setPlayerSize(t.width, t.height); t.setControlsSize(); // controls fade if (t.isVideo) { // show/hide controls t.container .bind('mouseenter', function () { if (!t.options.alwaysShowControls) { t.controls.css('visibility','visible'); t.controls.stop(true, true).fadeIn(200); } }) .bind('mouseleave', function () { if (!t.media.paused && !t.options.alwaysShowControls) { t.controls.stop(true, true).fadeOut(200, function() { $(this).css('visibility','hidden'); $(this).css('display','block'); }); } }); // check for autoplay if (t.domNode.getAttribute('autoplay') !== null && !t.options.alwaysShowControls) { t.controls.css('visibility','hidden'); } // resizer if (t.options.enableAutosize) { t.media.addEventListener('loadedmetadata', function(e) { // if the <video height> was not set and the options.videoHeight was not set // then resize to the real dimensions if (t.options.videoHeight <= 0 && t.domNode.getAttribute('height') === null && !isNaN(e.target.videoHeight)) { t.setPlayerSize(e.target.videoWidth, e.target.videoHeight); t.setControlsSize(); t.media.setVideoSize(e.target.videoWidth, e.target.videoHeight); } }, false); } } // ended for all t.media.addEventListener('ended', function (e) { t.media.setCurrentTime(0); t.media.pause(); if (t.setProgressRail) t.setProgressRail(); if (t.setCurrentRail) t.setCurrentRail(); if (t.options.loop) { t.media.play(); } else if (!t.options.alwaysShowControls) { t.controls.css('visibility','visible'); } }, true); // resize on the first play t.media.addEventListener('loadedmetadata', function(e) { if (t.updateDuration) { t.updateDuration(); } if (t.updateCurrent) { t.updateCurrent(); } t.setControlsSize(); }, true); // webkit has trouble doing this without a delay setTimeout(function () { t.setControlsSize(); t.setPlayerSize(t.width, t.height); }, 50); } if (t.options.success) { t.options.success(t.media, t.domNode); } }, handleError: function(e) { // Tell user that the file cannot be played if (this.options.error) { this.options.error(e); } }, setPlayerSize: function(width,height) { var t = this; // ie9 appears to need this (jQuery bug?) t.width = parseInt(width, 10); t.height = parseInt(height, 10); t.container .width(t.width) .height(t.height); t.layers.children('.mejs-layer') .width(t.width) .height(t.height); }, setControlsSize: function() { var t = this, usedWidth = 0, railWidth = 0, rail = t.controls.find('.mejs-time-rail'), total = t.controls.find('.mejs-time-total'), current = t.controls.find('.mejs-time-current'), loaded = t.controls.find('.mejs-time-loaded'); others = rail.siblings(); // find the size of all the other controls besides the rail others.each(function() { if ($(this).css('position') != 'absolute') { usedWidth += $(this).outerWidth(true); } }); // fit the rail into the remaining space railWidth = t.controls.width() - usedWidth - (rail.outerWidth(true) - rail.outerWidth(false)); // outer area rail.width(railWidth); // dark space total.width(railWidth - (total.outerWidth(true) - total.width())); if (t.setProgressRail) t.setProgressRail(); if (t.setCurrentRail) t.setCurrentRail(); }, buildposter: function(player, controls, layers, media) { var poster = $('<div class="mejs-poster mejs-layer">'+ '<img />'+ '</div>') .appendTo(layers), posterUrl = player.$media.attr('poster'), posterImg = poster.find('img').width(player.width).height(player.height); // prioriy goes to option (this is useful if you need to support iOS 3.x (iOS completely fails with poster) if (player.options.poster != '') { posterImg.attr('src',player.options.poster); // second, try the real poster } else if (posterUrl !== '' && posterUrl != null) { posterImg.attr('src',posterUrl); } else { poster.remove(); } media.addEventListener('play',function() { poster.hide(); }, false); }, buildoverlays: function(player, controls, layers, media) { if (!player.isVideo) return; var loading = $('<div class="mejs-overlay mejs-layer">'+ '<div class="mejs-overlay-loading"><span></span></div>'+ '</div>') .hide() // start out hidden .appendTo(layers), error = $('<div class="mejs-overlay mejs-layer">'+ '<div class="mejs-overlay-error"></div>'+ '</div>') .hide() // start out hidden .appendTo(layers), // this needs to come last so it's on top bigPlay = $('<div class="mejs-overlay mejs-layer mejs-overlay-play">'+ '<div class="mejs-overlay-button"></div>'+ '</div>') .appendTo(layers) .click(function() { if (media.paused) { media.play(); } else { media.pause(); } }); // show/hide big play button media.addEventListener('play',function() { bigPlay.hide(); error.hide(); }, false); media.addEventListener('pause',function() { bigPlay.show(); }, false); // show/hide loading media.addEventListener('loadstart',function() { // for some reason Chrome is firing this event if (mejs.MediaFeatures.isChrome && media.getAttribute && media.getAttribute('preload') === 'none') return; loading.show(); }, false); media.addEventListener('canplay',function() { loading.hide(); }, false); // error handling media.addEventListener('error',function() { loading.hide(); error.show(); error.find('mejs-overlay-error').html("Error loading this resource"); }, false); }, findTracks: function() { var t = this, tracktags = t.$media.find('track'); // store for use by plugins t.tracks = []; tracktags.each(function() { t.tracks.push({ srclang: $(this).attr('srclang').toLowerCase(), src: $(this).attr('src'), kind: $(this).attr('kind'), entries: [], isLoaded: false }); }); }, changeSkin: function(className) { this.container[0].className = 'mejs-container ' + className; this.setPlayerSize(); this.setControlsSize(); }, play: function() { this.media.play(); }, pause: function() { this.media.pause(); }, load: function() { this.media.load(); }, setMuted: function(muted) { this.media.setMuted(muted); }, setCurrentTime: function(time) { this.media.setCurrentTime(time); }, getCurrentTime: function() { return this.media.currentTime; }, setVolume: function(volume) { this.media.setVolume(volume); }, getVolume: function() { return this.media.volume; }, setSrc: function(src) { this.media.setSrc(src); } }; // turn into jQuery plugin if (typeof jQuery != 'undefined') { jQuery.fn.mediaelementplayer = function (options) { return this.each(function () { new mejs.MediaElementPlayer(this, options); }); }; } // push out to window window.MediaElementPlayer = mejs.MediaElementPlayer; })(mejs.$);