Mercurial > hg > env-test-daniele
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/johndyer-mediaelement-13fa20a/src/js/mep-player.js Wed Mar 06 15:45:48 2013 +0000 @@ -0,0 +1,486 @@ +(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.$); \ No newline at end of file