ChengDa_English/SGGL/FineUIPro.Web/AttachFile/lib/popcorn.player.js

438 lines
12 KiB
JavaScript

(function( Popcorn ) {
// combines calls of two function calls into one
var combineFn = function( first, second ) {
first = first || Popcorn.nop;
second = second || Popcorn.nop;
return function() {
first.apply( this, arguments );
second.apply( this, arguments );
};
};
// ID string matching
var rIdExp = /^(#([\w\-\_\.]+))$/;
Popcorn.player = function( name, player ) {
// return early if a player already exists under this name
if ( Popcorn[ name ] ) {
return;
}
player = player || {};
var playerFn = function( target, src, options ) {
options = options || {};
// List of events
var date = new Date() / 1000,
baselineTime = date,
currentTime = 0,
readyState = 0,
volume = 1,
muted = false,
events = {},
// The container div of the resource
container = typeof target === "string" ? Popcorn.dom.find( target ) : target,
basePlayer = {},
timeout,
popcorn;
if ( !Object.prototype.__defineGetter__ ) {
basePlayer = container || document.createElement( "div" );
}
// copies a div into the media object
for( var val in container ) {
// don't copy properties if using container as baseplayer
if ( val in basePlayer ) {
continue;
}
if ( typeof container[ val ] === "object" ) {
basePlayer[ val ] = container[ val ];
} else if ( typeof container[ val ] === "function" ) {
basePlayer[ val ] = (function( value ) {
// this is a stupid ugly kludgy hack in honour of Safari
// in Safari a NodeList is a function, not an object
if ( "length" in container[ value ] && !container[ value ].call ) {
return container[ value ];
} else {
return function() {
return container[ value ].apply( container, arguments );
};
}
}( val ));
} else {
Popcorn.player.defineProperty( basePlayer, val, {
get: (function( value ) {
return function() {
return container[ value ];
};
}( val )),
set: Popcorn.nop,
configurable: true
});
}
}
var timeupdate = function() {
date = new Date() / 1000;
if ( !basePlayer.paused ) {
basePlayer.currentTime = basePlayer.currentTime + ( date - baselineTime );
basePlayer.dispatchEvent( "timeupdate" );
timeout = setTimeout( timeupdate, 10 );
}
baselineTime = date;
};
basePlayer.play = function() {
this.paused = false;
if ( basePlayer.readyState >= 4 ) {
baselineTime = new Date() / 1000;
basePlayer.dispatchEvent( "play" );
timeupdate();
}
};
basePlayer.pause = function() {
this.paused = true;
basePlayer.dispatchEvent( "pause" );
};
Popcorn.player.defineProperty( basePlayer, "currentTime", {
get: function() {
return currentTime;
},
set: function( val ) {
// make sure val is a number
currentTime = +val;
basePlayer.dispatchEvent( "timeupdate" );
return currentTime;
},
configurable: true
});
Popcorn.player.defineProperty( basePlayer, "volume", {
get: function() {
return volume;
},
set: function( val ) {
// make sure val is a number
volume = +val;
basePlayer.dispatchEvent( "volumechange" );
return volume;
},
configurable: true
});
Popcorn.player.defineProperty( basePlayer, "muted", {
get: function() {
return muted;
},
set: function( val ) {
// make sure val is a number
muted = +val;
basePlayer.dispatchEvent( "volumechange" );
return muted;
},
configurable: true
});
Popcorn.player.defineProperty( basePlayer, "readyState", {
get: function() {
return readyState;
},
set: function( val ) {
readyState = val;
return readyState;
},
configurable: true
});
// Adds an event listener to the object
basePlayer.addEventListener = function( evtName, fn ) {
if ( !events[ evtName ] ) {
events[ evtName ] = [];
}
events[ evtName ].push( fn );
return fn;
};
// Removes an event listener from the object
basePlayer.removeEventListener = function( evtName, fn ) {
var i,
listeners = events[ evtName ];
if ( !listeners ){
return;
}
// walk backwards so we can safely splice
for ( i = events[ evtName ].length - 1; i >= 0; i-- ) {
if( fn === listeners[ i ] ) {
listeners.splice(i, 1);
}
}
return fn;
};
// Can take event object or simple string
basePlayer.dispatchEvent = function( oEvent ) {
var evt,
self = this,
eventInterface,
eventName = oEvent.type;
// A string was passed, create event object
if ( !eventName ) {
eventName = oEvent;
eventInterface = Popcorn.events.getInterface( eventName );
if ( eventInterface ) {
evt = document.createEvent( eventInterface );
evt.initEvent( eventName, true, true, window, 1 );
}
}
if ( events[ eventName ] ) {
for ( var i = events[ eventName ].length - 1; i >= 0; i-- ) {
events[ eventName ][ i ].call( self, evt, self );
}
}
};
// Attempt to get src from playerFn parameter
basePlayer.src = src || "";
basePlayer.duration = 0;
basePlayer.paused = true;
basePlayer.ended = 0;
options && options.events && Popcorn.forEach( options.events, function( val, key ) {
basePlayer.addEventListener( key, val, false );
});
// true and undefined returns on canPlayType means we should attempt to use it,
// false means we cannot play this type
if ( player._canPlayType( container.nodeName, src ) !== false ) {
if ( player._setup ) {
player._setup.call( basePlayer, options );
} else {
// there is no setup, which means there is nothing to load
basePlayer.readyState = 4;
basePlayer.dispatchEvent( "loadedmetadata" );
basePlayer.dispatchEvent( "loadeddata" );
basePlayer.dispatchEvent( "canplaythrough" );
}
} else {
// Asynchronous so that users can catch this event
setTimeout( function() {
basePlayer.dispatchEvent( "error" );
}, 0 );
}
popcorn = new Popcorn.p.init( basePlayer, options );
if ( player._teardown ) {
popcorn.destroy = combineFn( popcorn.destroy, function() {
player._teardown.call( basePlayer, options );
});
}
return popcorn;
};
playerFn.canPlayType = player._canPlayType = player._canPlayType || Popcorn.nop;
Popcorn[ name ] = Popcorn.player.registry[ name ] = playerFn;
};
Popcorn.player.registry = {};
Popcorn.player.defineProperty = Object.defineProperty || function( object, description, options ) {
object.__defineGetter__( description, options.get || Popcorn.nop );
object.__defineSetter__( description, options.set || Popcorn.nop );
};
// player queue is to help players queue things like play and pause
// HTML5 video's play and pause are asynch, but do fire in sequence
// play() should really mean "requestPlay()" or "queuePlay()" and
// stash a callback that will play the media resource when it's ready to be played
Popcorn.player.playerQueue = function() {
var _queue = [],
_running = false;
return {
next: function() {
_running = false;
_queue.shift();
_queue[ 0 ] && _queue[ 0 ]();
},
add: function( callback ) {
_queue.push(function() {
_running = true;
callback && callback();
});
// if there is only one item on the queue, start it
!_running && _queue[ 0 ]();
}
};
};
// Popcorn.smart will attempt to find you a wrapper or player. If it can't do that,
// it will default to using an HTML5 video in the target.
Popcorn.smart = function( target, src, options ) {
var node = typeof target === "string" ? Popcorn.dom.find( target ) : target,
i, srci, j, media, mediaWrapper, popcorn, srcLength,
// We leave HTMLVideoElement and HTMLAudioElement wrappers out
// of the mix, since we'll default to HTML5 video if nothing
// else works. Waiting on #1254 before we add YouTube to this.
wrappers = "HTMLYouTubeVideoElement HTMLVimeoVideoElement HTMLSoundCloudAudioElement HTMLNullVideoElement".split(" ");
if ( !node ) {
Popcorn.error( "Specified target `" + target + "` was not found." );
return;
}
// If our src is not an array, create an array of one.
src = typeof src === "string" ? [ src ] : src;
// Loop through each src, and find the first playable.
for ( i = 0, srcLength = src.length; i < srcLength; i++ ) {
srci = src[ i ];
// See if we can use a wrapper directly, if not, try players.
for ( j = 0; j < wrappers.length; j++ ) {
mediaWrapper = Popcorn[ wrappers[ j ] ];
if ( mediaWrapper && mediaWrapper._canPlaySrc( srci ) === "probably" ) {
media = mediaWrapper( node );
popcorn = Popcorn( media, options );
// Set src, but not until after we return the media so the caller
// can get error events, if any.
setTimeout( function() {
media.src = srci;
}, 0 );
return popcorn;
}
}
// No wrapper can play this, check players.
for ( var key in Popcorn.player.registry ) {
if ( Popcorn.player.registry.hasOwnProperty( key ) ) {
if ( Popcorn.player.registry[ key ].canPlayType( node.nodeName, srci ) ) {
// Popcorn.smart( player, src, /* options */ )
return Popcorn[ key ]( node, srci, options );
}
}
}
}
// If we don't have any players or wrappers that can handle this,
// Default to using HTML5 video. Similar to the HTMLVideoElement
// wrapper, we put a video in the div passed to us via:
// Popcorn.smart( div, src, options )
var videoHTML,
videoElement,
videoID = Popcorn.guid( "popcorn-video-" ),
videoHTMLContainer = document.createElement( "div" );
videoHTMLContainer.style.width = "100%";
videoHTMLContainer.style.height = "100%";
// If we only have one source, do not bother with source elements.
// This means we don't have the IE9 hack,
// and we can properly listen to error events.
// That way an error event can be told to backup to Flash if it fails.
if ( src.length === 1 ) {
videoElement = document.createElement( "video" );
videoElement.id = videoID;
node.appendChild( videoElement );
setTimeout( function() {
// Hack to decode html characters like &amp; to &
var decodeDiv = document.createElement( "div" );
decodeDiv.innerHTML = src[ 0 ];
videoElement.src = decodeDiv.firstChild.nodeValue;
}, 0 );
return Popcorn( '#' + videoID, options );
}
node.appendChild( videoHTMLContainer );
// IE9 doesn't like dynamic creation of source elements on <video>
// so we do it in one shot via innerHTML.
videoHTML = '<video id="' + videoID + '" preload=auto autobuffer>';
for ( i = 0, srcLength = src.length; i < srcLength; i++ ) {
videoHTML += '<source src="' + src[ i ] + '">';
}
videoHTML += "</video>";
videoHTMLContainer.innerHTML = videoHTML;
if ( options && options.events && options.events.error ) {
node.addEventListener( "error", options.events.error, false );
}
return Popcorn( '#' + videoID, options );
};
})( Popcorn );