зеркало из https://github.com/mozilla/popcorn-js.git
[#423] one baseplayer to rule them all.
[#423] updates to initial baseplayer code to work better with custom players [#423] a fix to media function calls; added return statement [#423] pulling initial work into proper repo location, fixing tests [423] finished youtube player and tests for youtube player and baseplayer
This commit is contained in:
Родитель
78b5b4393e
Коммит
a3fdc5d53f
|
@ -15,6 +15,7 @@
|
|||
<script>
|
||||
// Helper function to get elements
|
||||
function element(id) {
|
||||
|
||||
return document.getElementById(id);
|
||||
}
|
||||
|
||||
|
@ -22,7 +23,7 @@
|
|||
var paused = true,
|
||||
popcorn;
|
||||
|
||||
popcorn = Popcorn( Popcorn.youtube( 'video', 'http://www.youtube.com/watch?v=9oar9glUCL0', { width: 400, controls: 0, annotations: 3 } ) );
|
||||
popcorn = Popcorn.youtube( '#video', 'http://www.youtube.com/watch?v=9oar9glUCL0' );
|
||||
|
||||
popcorn = popcorn
|
||||
.footnote({
|
||||
|
@ -94,19 +95,18 @@
|
|||
element( 'player-time' ).innerHTML = popcorn.currentTime();
|
||||
})
|
||||
// Update button labels
|
||||
.listen( 'playing' , function() {
|
||||
.listen( 'play' , function() {
|
||||
paused = false;
|
||||
element( 'btn-play-pause' ).innerHTML = 'Pause';
|
||||
})
|
||||
.listen('pause', function() {
|
||||
paused = true;
|
||||
element( 'btn-play-pause' ).innerHTML = 'Play';
|
||||
})
|
||||
popcorn.mute()
|
||||
.play();
|
||||
});
|
||||
|
||||
// Setup UI after loaded
|
||||
popcorn.listen( 'load', function() {
|
||||
|
||||
element( 'player-status' ).innerHTML = 'Ready';
|
||||
element( 'player_vol' ).innerHTML = popcorn.volume();
|
||||
|
||||
|
@ -121,6 +121,7 @@
|
|||
|
||||
// Seek
|
||||
element('btn-seek').addEventListener('click', function() {
|
||||
|
||||
popcorn.currentTime( 30 );
|
||||
}, false);
|
||||
|
||||
|
@ -131,7 +132,7 @@
|
|||
}, false);
|
||||
|
||||
element('btn-mute').addEventListener('click', function() {
|
||||
popcorn.mute();
|
||||
popcorn.mute( !popcorn.media.muted );
|
||||
}, false);
|
||||
});
|
||||
}, false );
|
||||
|
|
|
@ -1,558 +1,187 @@
|
|||
// Popcorn Youtube Player Wrapper
|
||||
// A global callback for youtube... that makes me angry
|
||||
var onYouTubePlayerReady = function( containerId ) {
|
||||
|
||||
var onYouTubePlayerReady;
|
||||
onYouTubePlayerReady[ containerId ] && onYouTubePlayerReady[ containerId ]();
|
||||
};
|
||||
onYouTubePlayerReady.stateChangeEventHandler = {};
|
||||
|
||||
( function( Popcorn ) {
|
||||
/**
|
||||
* Youtube wrapper for popcorn.
|
||||
* This plug-in adds capability for Popcorn.js to deal with Youtube
|
||||
* videos. This plug-in also doesn't use Popcorn's plugin() API and
|
||||
* instead hacks directly into Popcorn's core.
|
||||
*
|
||||
* To use this plug-in, onYouTubePlayerReady() event handler needs to be
|
||||
* called by the Youtube video player, before videos can be registered.
|
||||
* Once videos are registered, calls to them can be made the same way as
|
||||
* regular Popcorn objects. Also note that enablejsapi=1 needs to be added
|
||||
* to the embed code, in order for Youtube's JavaScript API to work.
|
||||
*
|
||||
* Note that there are a few methods, properties and events that are not
|
||||
* supported. See the bottom of this plug-in for a complete list.
|
||||
*/
|
||||
Popcorn.player( "youtube", {
|
||||
_setup: function( options ) {
|
||||
|
||||
// Intended
|
||||
var undef;
|
||||
var media = this,
|
||||
player = {},
|
||||
youtubeObject,
|
||||
container = document.createElement( "div" ),
|
||||
currentTime = 0,
|
||||
seekTime = 0,
|
||||
seeking = false,
|
||||
dataLoaded = false;
|
||||
|
||||
// Config parameters
|
||||
// 33 ms per update is suitable for 30 fps
|
||||
// 0.05 sec tolerance between old and new times to determine if currentTime has been set programatically
|
||||
// 250 ms progress interval as specified by WHATWG
|
||||
var timeupdateInterval = 33,
|
||||
timeCheckInterval = 0.5,
|
||||
progressInterval = 250;
|
||||
container.id = media.id + Popcorn.guid();
|
||||
|
||||
// Ready State Constants
|
||||
var READY_STATE_HAVE_NOTHING = 0,
|
||||
READY_STATE_HAVE_METADATA = 1,
|
||||
READY_STATE_HAVE_CURRENT_DATA = 2,
|
||||
READY_STATE_HAVE_FUTURE_DATA = 3,
|
||||
READY_STATE_HAVE_ENOUGH_DATA = 4;
|
||||
media.appendChild( container );
|
||||
|
||||
// Youtube State Constants
|
||||
var YOUTUBE_STATE_UNSTARTED = -1,
|
||||
YOUTUBE_STATE_ENDED = 0,
|
||||
YOUTUBE_STATE_PLAYING = 1,
|
||||
YOUTUBE_STATE_PAUSED = 2,
|
||||
YOUTUBE_STATE_BUFFERING = 3,
|
||||
YOUTUBE_STATE_CUED = 5;
|
||||
var youtubeInit = function() {
|
||||
|
||||
var urlRegex = /^.*[\/=](.{11})/;
|
||||
// expose a callback to this scope, that is called from the global callback youtube calls
|
||||
onYouTubePlayerReady[ container.id ] = function() {
|
||||
|
||||
// Collection of all Youtube players
|
||||
var registry = {},
|
||||
loadedPlayers = {};
|
||||
youtubeObject = document.getElementById( container.id );
|
||||
|
||||
var abs = Math.abs;
|
||||
// more youtube callback nonsense
|
||||
onYouTubePlayerReady.stateChangeEventHandler[ container.id ] = function( state ) {
|
||||
|
||||
Popcorn.getScript( "http://ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js" );
|
||||
// playing is state 1
|
||||
// paused is state 2
|
||||
if ( state === 1 ) {
|
||||
|
||||
// Extract the id from a web url
|
||||
function extractIdFromUrl( url ) {
|
||||
if ( !url ) {
|
||||
media.paused && media.play();
|
||||
// youtube fires paused events while seeking
|
||||
// this is the only way to get seeking events
|
||||
} else if ( state === 2 ) {
|
||||
|
||||
// silly logic forced on me by the youtube API
|
||||
// calling youtube.seekTo triggers multiple events
|
||||
// with the second events getCurrentTime being the old time
|
||||
if ( seeking && seekTime === currentTime && seekTime !== youtubeObject.getCurrentTime() ) {
|
||||
|
||||
seeking = false;
|
||||
youtubeObject.seekTo( currentTime );
|
||||
return;
|
||||
}
|
||||
|
||||
var matches = urlRegex.exec( url );
|
||||
currentTime = youtubeObject.getCurrentTime();
|
||||
media.dispatchEvent( "timeupdate" );
|
||||
!media.paused && media.pause();
|
||||
}
|
||||
};
|
||||
|
||||
// Return id, which comes after first equals sign
|
||||
return matches ? matches[1] : "";
|
||||
// youtube requires callbacks to be a string to a function path from the global scope
|
||||
youtubeObject.addEventListener( "onStateChange", "onYouTubePlayerReady.stateChangeEventHandler." + container.id );
|
||||
|
||||
var timeupdate = function() {
|
||||
|
||||
currentTime = youtubeObject.getCurrentTime();
|
||||
media.dispatchEvent( "timeupdate" );
|
||||
timeout = setTimeout( timeupdate, 10 );
|
||||
};
|
||||
|
||||
media.play = function() {
|
||||
|
||||
media.paused = false;
|
||||
media.dispatchEvent( "play" );
|
||||
|
||||
if ( dataLoaded ) {
|
||||
|
||||
media.dispatchEvent( "loadeddata" );
|
||||
dataLoaded = false;
|
||||
}
|
||||
|
||||
// Extract the id from a player url
|
||||
function extractIdFromUri( url ) {
|
||||
if ( !url ) {
|
||||
return;
|
||||
media.dispatchEvent( "playing" );
|
||||
timeupdate();
|
||||
youtubeObject.playVideo();
|
||||
};
|
||||
|
||||
media.pause = function() {
|
||||
|
||||
if ( !media.paused ) {
|
||||
|
||||
media.paused = true;
|
||||
media.dispatchEvent( "pause" );
|
||||
youtubeObject.pauseVideo();
|
||||
}
|
||||
};
|
||||
|
||||
media.__defineSetter__( "currentTime", function( val ) {
|
||||
|
||||
// make sure val is a number
|
||||
currentTime = seekTime = +val;
|
||||
seeking = true;
|
||||
media.dispatchEvent( "seeked" );
|
||||
media.dispatchEvent( "timeupdate" );
|
||||
youtubeObject.seekTo( currentTime );
|
||||
return currentTime;
|
||||
});
|
||||
|
||||
media.__defineGetter__( "currentTime", function() {
|
||||
|
||||
return currentTime;
|
||||
});
|
||||
|
||||
media.__defineSetter__( "muted", function( val ) {
|
||||
|
||||
if ( youtubeObject.isMuted() !== val ) {
|
||||
|
||||
if ( val ) {
|
||||
|
||||
youtubeObject.mute();
|
||||
} else {
|
||||
|
||||
youtubeObject.unMute();
|
||||
}
|
||||
|
||||
var matches = urlRegex.exec( url );
|
||||
|
||||
// Return id, which comes after first equals sign
|
||||
return matches ? matches[1] : "";
|
||||
media.dispatchEvent( "volumechange" );
|
||||
}
|
||||
|
||||
function getPlayerAddress( vidId, playerId ) {
|
||||
if( !vidId ) {
|
||||
return;
|
||||
return youtubeObject.isMuted();
|
||||
});
|
||||
|
||||
media.__defineGetter__( "muted", function() {
|
||||
|
||||
return youtubeObject.isMuted();
|
||||
});
|
||||
|
||||
media.__defineSetter__( "volume", function( val ) {
|
||||
|
||||
if ( youtubeObject.getVolume() !== val ) {
|
||||
|
||||
youtubeObject.setVolume( val );
|
||||
media.dispatchEvent( "volumechange" );
|
||||
}
|
||||
|
||||
return "http://www.youtube.com/e/" + id;
|
||||
return youtubeObject.getVolume();
|
||||
});
|
||||
|
||||
media.__defineGetter__( "volume", function() {
|
||||
|
||||
return youtubeObject.getVolume();
|
||||
});
|
||||
|
||||
media.readyState = 4;
|
||||
media.dispatchEvent( 'load' );
|
||||
dataLoaded = true;
|
||||
media.duration = youtubeObject.getDuration();
|
||||
media.dispatchEvent( 'durationchange' );
|
||||
|
||||
if ( !media.paused ) {
|
||||
|
||||
media.play();
|
||||
}
|
||||
|
||||
function makeSWF( url, container ) {
|
||||
var bounds,
|
||||
params,
|
||||
flashvars,
|
||||
attributes,
|
||||
self = this;
|
||||
media.paused && media.dispatchEvent( 'loadeddata' );
|
||||
};
|
||||
|
||||
options.controls = +options.controls === 0 || +options.controls === 1 ? options.controls : 1;
|
||||
options.annotations = +options.annotations === 1 || +options.annotations === 3 ? options.annotations : 1;
|
||||
|
||||
var flashvars = {
|
||||
playerapiid: container.id,
|
||||
controls: options.controls,
|
||||
iv_load_policy: options.annotations
|
||||
};
|
||||
|
||||
swfobject.embedSWF( "http://www.youtube.com/e/" + /^.*[\/=](.{11})/.exec( media.src )[ 1 ] + "?enablejsapi=1&playerapiid=" + container.id + "&version=3",
|
||||
container.id, media.offsetWidth, media.offsetHeight, "8", null,
|
||||
flashvars, {wmode: "transparent", allowScriptAccess: "always"}, {id: container.id} );
|
||||
};
|
||||
|
||||
if ( !window.swfobject ) {
|
||||
setTimeout( function() {
|
||||
makeSWF.call( self, url, container );
|
||||
}, 1 );
|
||||
return;
|
||||
}
|
||||
|
||||
bounds = container.getBoundingClientRect();
|
||||
|
||||
// Determine width/height/etc based on container
|
||||
this.width = container.style.width || 460;
|
||||
this.height = container.style.height || 350;
|
||||
|
||||
// Just in case we got the attributes as strings. We'll need to do math with these later
|
||||
this.width = parseFloat(this.width);
|
||||
this.height = parseFloat(this.height);
|
||||
|
||||
// For calculating position relative to video (like subtitles)
|
||||
this.offsetWidth = this.width;
|
||||
this.offsetHeight = this.height;
|
||||
this.offsetLeft = bounds.left;
|
||||
this.offsetTop = bounds.top;
|
||||
|
||||
this.offsetParent = container.offsetParent;
|
||||
|
||||
flashvars = {
|
||||
playerapiid: this.playerId,
|
||||
controls: this.controls,
|
||||
iv_load_policy: this.iv_load_policy
|
||||
};
|
||||
|
||||
params = {
|
||||
allowscriptaccess: 'always',
|
||||
allowfullscreen: 'true',
|
||||
// This is so we can overlay html on top of Flash
|
||||
wmode: 'transparent'
|
||||
|
||||
};
|
||||
|
||||
attributes = {
|
||||
id: this.playerId
|
||||
};
|
||||
|
||||
swfobject.embedSWF( "http://www.youtube.com/e/" + this.vidId +"?enablejsapi=1&playerapiid=" + this.playerId + "&version=3",
|
||||
this.playerId, this.width, this.height, "8", null, flashvars, params, attributes );
|
||||
}
|
||||
|
||||
// Called when a player is loaded
|
||||
// Playerid must match the element id
|
||||
onYouTubePlayerReady = function ( playerId ) {
|
||||
var vid = registry[playerId];
|
||||
|
||||
loadedPlayers[playerId] = 1;
|
||||
|
||||
// Video hadn't loaded yet when ctor was called
|
||||
vid.video = document.getElementById( playerId );
|
||||
vid.duration = vid.video.getDuration();
|
||||
|
||||
|
||||
// Issue load event
|
||||
vid.dispatchEvent( 'load' );
|
||||
vid.dispatchEvent( "durationchange" );
|
||||
};
|
||||
|
||||
Popcorn.youtube = function( elementId, url, options ) {
|
||||
return new Popcorn.youtube.init( elementId, url, options );
|
||||
};
|
||||
|
||||
Popcorn.youtube.init = function( elementId, url, options ) {
|
||||
if ( !elementId ) {
|
||||
throw "Element id is invalid.";
|
||||
} else if ( /file/.test( location.protocol ) ) {
|
||||
throw "This must be run from a web server.";
|
||||
}
|
||||
|
||||
options = options || {};
|
||||
|
||||
var self = this;
|
||||
|
||||
this.playerId = elementId + Popcorn.guid();
|
||||
this.readyState = READY_STATE_HAVE_NOTHING;
|
||||
this._eventListeners = {};
|
||||
this.loadStarted = false;
|
||||
this.loadedData = false;
|
||||
this.fullyLoaded = false;
|
||||
this.paused = false;
|
||||
|
||||
// If supplied as number, append 'px' on end
|
||||
// If suppliied as '###' or '###px', convert to number and append 'px' back on end
|
||||
options.width = options.width && (+options.width)+"px";
|
||||
options.height = options.height && (+options.height)+"px";
|
||||
|
||||
// show controls on video. Integer value - 1 is for show, 0 is for hide
|
||||
this.controls = +options.controls === 0 || +options.controls === 1 ? options.controls : 1;
|
||||
|
||||
// show video annotations, 1 is show, 3 is hide
|
||||
this.iv_load_policy = +options.annotations === 1 || +options.annotations === 3 ? options.annotations : 1;
|
||||
|
||||
this._target = document.getElementById( elementId );
|
||||
this._container = document.createElement( "div" );
|
||||
this._container.id = this.playerId;
|
||||
this._container.style.height = this._target.style.height = options.height || this._target.style.height || "350px";
|
||||
this._container.style.width = this._target.style.width = options.width || this._target.style.width || "460px";
|
||||
this._target.appendChild( this._container );
|
||||
this.parentNode = this._target.parentNode;
|
||||
|
||||
this.offsetHeight = +this._target.offsetHeight;
|
||||
this.offsetWidth = +this._target.offsetWidth;
|
||||
|
||||
this.currentTime = this.previousCurrentTime = 0;
|
||||
this.volume = this.previousVolume = this.preMuteVol = 1;
|
||||
this.duration = 0;
|
||||
|
||||
this.vidId = extractIdFromUrl( url ) || extractIdFromUri( url );
|
||||
|
||||
if ( !this.vidId ) {
|
||||
throw "Could not find video id";
|
||||
}
|
||||
|
||||
this.addEventListener( "load", function() {
|
||||
// For calculating position relative to video (like subtitles)
|
||||
this.offsetWidth = this.video.offsetWidth;
|
||||
this.offsetHeight = this.video.offsetHeight;
|
||||
this.offsetParent = this.video.offsetParent;
|
||||
|
||||
// Set up stuff that requires the API to be loaded
|
||||
this.registerYoutubeEventHandlers();
|
||||
this.registerInternalEventHandlers();
|
||||
});
|
||||
|
||||
(function() {
|
||||
var hasBeenCalled = 0;
|
||||
|
||||
self.addEventListener( "playing", function() {
|
||||
if (hasBeenCalled) {
|
||||
return;
|
||||
}
|
||||
|
||||
hasBeenCalled = 1;
|
||||
self.duration = self.video.getDuration();
|
||||
self.dispatchEvent( "durationchange" );
|
||||
|
||||
});
|
||||
})();
|
||||
|
||||
if ( loadedPlayers[this.playerId] ) {
|
||||
this.video = registry[this.playerId].video;
|
||||
|
||||
this.vidId = this.vidId || extractIdFromUrl( this._container.getAttribute( "src" ) ) || extractIdFromUri( this._container.getAttribute( "src" ) );
|
||||
|
||||
if (this.vidId !== registry[this.playerId].vidId ) {
|
||||
this.video.cueVideoById( this.vidId );
|
||||
Popcorn.getScript( "http://ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js", youtubeInit );
|
||||
} else {
|
||||
// Same video, new ctor. Force a seek to the beginning
|
||||
this.previousCurrentTime = 1;
|
||||
|
||||
youtubeInit();
|
||||
}
|
||||
|
||||
this.dispatchEvent( 'load' );
|
||||
} else if ( this._container ) {
|
||||
makeSWF.call( this, url, this._container );
|
||||
} else {
|
||||
// Container not yet loaded, get it on DOMDontentLoad
|
||||
document.addEventListener( "DOMContentLoaded", function() {
|
||||
self._container = document.getElementById( elementId );
|
||||
|
||||
if ( !self._container ) {
|
||||
throw "Could not find container!";
|
||||
}
|
||||
|
||||
makeSWF.call( self, url, self._container );
|
||||
}, false);
|
||||
}
|
||||
|
||||
registry[this.playerId] = this;
|
||||
};
|
||||
// end Popcorn.youtube.init
|
||||
|
||||
Popcorn.extend( Popcorn.youtube.init.prototype, {
|
||||
|
||||
// For internal use only.
|
||||
// Register handlers to YouTube events.
|
||||
registerYoutubeEventHandlers: function() {
|
||||
var youcorn = this,
|
||||
stateChangeHandler = 'Popcorn.youtube.stateChangeEventHandler',
|
||||
errorHandler = 'Popcorn.youtube.errorEventHandler';
|
||||
|
||||
this.video.addEventListener( 'onStateChange', stateChangeHandler );
|
||||
this.video.addEventListener( 'onError', errorHandler );
|
||||
|
||||
/**
|
||||
* Since Flash can only call named functions, they are declared
|
||||
* separately here.
|
||||
*/
|
||||
Popcorn.youtube.stateChangeEventHandler = function( state ) {
|
||||
// In case ctor has been called many times for many ctors
|
||||
// Only use latest ctor call for each player id
|
||||
var self = registry[youcorn.playerId];
|
||||
|
||||
if ( state === YOUTUBE_STATE_UNSTARTED ) {
|
||||
self.readyState = READY_STATE_HAVE_METADATA;
|
||||
self.dispatchEvent( 'loadedmetadata' );
|
||||
} else if ( state === YOUTUBE_STATE_ENDED ) {
|
||||
self.dispatchEvent( 'ended' );
|
||||
} else if ( state === YOUTUBE_STATE_PLAYING ) {
|
||||
// Being able to play means current data is loaded.
|
||||
if ( !this.loadedData ) {
|
||||
this.loadedData = true;
|
||||
self.dispatchEvent( 'loadeddata' );
|
||||
}
|
||||
|
||||
self.readyState = READY_STATE_HAVE_CURRENT_DATA;
|
||||
self.dispatchEvent( 'playing' );
|
||||
} else if ( state === YOUTUBE_STATE_PAUSED ) {
|
||||
self.dispatchEvent( 'pause' );
|
||||
} else if ( state === YOUTUBE_STATE_BUFFERING ) {
|
||||
self.dispatchEvent( 'waiting' );
|
||||
} else if ( state === YOUTUBE_STATE_CUED ) {
|
||||
// not handled
|
||||
Popcorn.nop();
|
||||
}
|
||||
};
|
||||
|
||||
Popcorn.youtube.errorEventHandler = function( state ) {
|
||||
youcorn.dispatchEvent( 'error' );
|
||||
};
|
||||
},
|
||||
|
||||
// For internal use only.
|
||||
// Start current time and loading progress syncing intervals.
|
||||
registerInternalEventHandlers: function() {
|
||||
this.addEventListener( 'playing', function() {
|
||||
this.startTimeUpdater();
|
||||
});
|
||||
this.addEventListener( 'loadedmetadata', function() {
|
||||
this.startProgressUpdater();
|
||||
});
|
||||
},
|
||||
|
||||
play: function() {
|
||||
// In case called before video is loaded, defer acting
|
||||
if ( !loadedPlayers[this.playerId] ) {
|
||||
this.addEventListener( "load", function() {
|
||||
this.play();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.dispatchEvent( 'play' );
|
||||
this.video.playVideo();
|
||||
},
|
||||
|
||||
pause: function() {
|
||||
// In case called before video is loaded, defer acting
|
||||
if ( !loadedPlayers[this.playerId] ) {
|
||||
this.addEventListener( "load", this.pause );
|
||||
return;
|
||||
}
|
||||
|
||||
this.video.pauseVideo();
|
||||
// pause event is raised by Youtube.
|
||||
},
|
||||
|
||||
load: function() {
|
||||
// In case called before video is loaded, defer acting
|
||||
if ( !loadedPlayers[this.playerId] ) {
|
||||
this.addEventListener( "load", function() {
|
||||
this.load();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.video.playVideo();
|
||||
this.video.pauseVideo();
|
||||
},
|
||||
|
||||
seekTo: function( time ) {
|
||||
var playing = this.video.getPlayerState() == YOUTUBE_STATE_PLAYING;
|
||||
this.video.seekTo( time, true );
|
||||
|
||||
// Prevent Youtube's behaviour to start playing video after seeking.
|
||||
if ( !playing ) {
|
||||
this.video.paused = true;
|
||||
this.video.pauseVideo();
|
||||
} else {
|
||||
this.video.paused = false;
|
||||
}
|
||||
|
||||
// Data need to be loaded again.
|
||||
if ( !this.fullyLoaded ) {
|
||||
this.loadedData = false;
|
||||
}
|
||||
|
||||
// Raise event.
|
||||
this.dispatchEvent( 'seeked' );
|
||||
},
|
||||
|
||||
// Mute is toggleable
|
||||
mute: function() {
|
||||
// In case called before video is loaded, defer acting
|
||||
if ( !loadedPlayers[this.playerId] ) {
|
||||
this.addEventListener( "load", this.mute );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( this.volume !== 0 ) {
|
||||
this.preMuteVol = this.volume;
|
||||
this.setVolume( 0 );
|
||||
} else {
|
||||
this.setVolume( this.preMuteVol );
|
||||
}
|
||||
},
|
||||
|
||||
// Expects beteween 0 and 1
|
||||
setVolume: function( vol ) {
|
||||
this.volume = this.previousVolume = vol;
|
||||
this.video.setVolume( vol * 100 );
|
||||
this.dispatchEvent( 'volumechange' );
|
||||
},
|
||||
|
||||
addEventListener: function( evt, func ) {
|
||||
var evtName = evt.type || evt;
|
||||
|
||||
if ( !this._eventListeners[evtName] ) {
|
||||
this._eventListeners[evtName] = [];
|
||||
}
|
||||
|
||||
this._eventListeners[evtName].push( func );
|
||||
},
|
||||
|
||||
/**
|
||||
* Notify event listeners about an event.
|
||||
*/
|
||||
dispatchEvent: function( name ) {
|
||||
var evtName = name.type || name;
|
||||
if ( !this._eventListeners[evtName] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
|
||||
Popcorn.forEach( this._eventListeners[evtName], function( evt ) {
|
||||
evt.call( self, null );
|
||||
});
|
||||
},
|
||||
|
||||
/* Unsupported methods. */
|
||||
|
||||
defaultPlaybackRate: function( arg ) {
|
||||
},
|
||||
|
||||
playbackRate: function( arg ) {
|
||||
},
|
||||
|
||||
getBoundingClientRect: function() {
|
||||
var b,
|
||||
self = this;
|
||||
|
||||
if ( this.video ) {
|
||||
b = this.video.getBoundingClientRect();
|
||||
|
||||
return {
|
||||
bottom: b.bottom,
|
||||
left: b.left,
|
||||
right: b.right,
|
||||
top: b.top,
|
||||
|
||||
// These not guaranteed to be in there
|
||||
width: b.width || ( b.right - b.left ),
|
||||
height: b.height || ( b.bottom - b.top )
|
||||
};
|
||||
} else {
|
||||
b = self._container.getBoundingClientRect();
|
||||
|
||||
// Update bottom, right for expected values once the container loads
|
||||
return {
|
||||
left: b.left,
|
||||
top: b.top,
|
||||
width: self._target.offsetWidth,
|
||||
height: self._target.offsetHeight,
|
||||
bottom: b.top + self._target.offsetWidth,
|
||||
right: b.left + self._target.offsetHeight
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
startTimeUpdater: function() {
|
||||
var state = typeof this.video.getPlayerState != "function" ? this.readyState : this.video.getPlayerState(),
|
||||
self = this,
|
||||
seeked = 0;
|
||||
|
||||
if ( abs( this.currentTime - this.previousCurrentTime ) > timeCheckInterval ) {
|
||||
// Has programatically set the currentTime
|
||||
this.previousCurrentTime = this.currentTime - timeCheckInterval;
|
||||
this.seekTo( this.currentTime );
|
||||
seeked = 1;
|
||||
} else {
|
||||
this.previousCurrentTime = this.currentTime;
|
||||
this.currentTime = typeof this.video.getCurrentTime != "function" ? this.currentTime : this.video.getCurrentTime();
|
||||
}
|
||||
|
||||
if ( this.volume !== this.previousVolume ) {
|
||||
this.setVolume( this.volume );
|
||||
}
|
||||
|
||||
if ( state !== YOUTUBE_STATE_ENDED && state !== YOUTUBE_STATE_PAUSED || seeked ) {
|
||||
this.dispatchEvent( 'timeupdate' );
|
||||
}
|
||||
|
||||
if( state !== YOUTUBE_STATE_ENDED ) {
|
||||
setTimeout( function() {
|
||||
self.startTimeUpdater.call(self);
|
||||
}, timeupdateInterval);
|
||||
}
|
||||
},
|
||||
|
||||
startProgressUpdater: function() {
|
||||
var bytesLoaded = this.video.getVideoBytesLoaded(),
|
||||
bytesToLoad = this.video.getVideoBytesTotal(),
|
||||
self = this;
|
||||
|
||||
// do nothing if size is not yet determined
|
||||
if ( bytesToLoad === 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// raise an event if load has just started
|
||||
if ( !this.loadStarted ) {
|
||||
this.loadStarted = true;
|
||||
this.dispatchEvent( 'loadstart' );
|
||||
}
|
||||
|
||||
// fully loaded
|
||||
if ( bytesLoaded >= bytesToLoad ) {
|
||||
this.fullyLoaded = true;
|
||||
this.readyState = READY_STATE_HAVE_ENOUGH_DATA;
|
||||
this.dispatchEvent( 'canplaythrough' );
|
||||
return;
|
||||
}
|
||||
|
||||
this.dispatchEvent( 'progress' );
|
||||
|
||||
setTimeout( function() {
|
||||
self.startProgressUpdater.call( self );
|
||||
}, progressInterval);
|
||||
}
|
||||
}); // end Popcorn.extend
|
||||
|
||||
/* Unsupported properties and events. */
|
||||
|
||||
/**
|
||||
* Unsupported events are:
|
||||
* * suspend
|
||||
* * abort
|
||||
* * emptied
|
||||
* * stalled
|
||||
* * canplay
|
||||
* * seeking
|
||||
* * ratechange
|
||||
*/
|
||||
|
||||
})( Popcorn );
|
||||
|
||||
|
|
|
@ -1,158 +1,288 @@
|
|||
test( "Popcorn YouTube Plugin Event Tests", function() {
|
||||
test("Update Timer", function () {
|
||||
|
||||
QUnit.reset();
|
||||
|
||||
var p2 = Popcorn.youtube( '#video2', 'http://www.youtube.com/watch?v=9oar9glUCL0' ),
|
||||
expects = 12,
|
||||
count = 0,
|
||||
execCount = 0,
|
||||
// These make sure events are only fired once
|
||||
// any second call will produce a failed test
|
||||
forwardStart = false,
|
||||
forwardEnd = false,
|
||||
backwardStart = false,
|
||||
backwardEnd = false,
|
||||
wrapperRunning = { one: false, two: false, };
|
||||
|
||||
function plus() {
|
||||
if ( ++count == expects ) {
|
||||
if ( ++count === expects ) {
|
||||
// clean up added events after tests
|
||||
Popcorn.removePlugin( "forwards" );
|
||||
Popcorn.removePlugin( "backwards" );
|
||||
Popcorn.removePlugin( "wrapper" );
|
||||
p2.removePlugin( "exec" );
|
||||
start();
|
||||
}
|
||||
}
|
||||
|
||||
QUnit.reset();
|
||||
|
||||
// events must be fired in this order
|
||||
var expectedEvents = [
|
||||
'play',
|
||||
'loadeddata',
|
||||
'playing',
|
||||
'volumechange',
|
||||
'pause',
|
||||
'play',
|
||||
'playing',
|
||||
'seeked',
|
||||
'volumechange',
|
||||
'volumechange',
|
||||
'playing',
|
||||
'pause',
|
||||
'ended'
|
||||
];
|
||||
|
||||
var popcorn = Popcorn( Popcorn.youtube( 'video', "http://www.youtube.com/e/ac7KhViaVqc" ) ),
|
||||
count = 0,
|
||||
eventCount = 0,
|
||||
added = [],
|
||||
set1Executed = false,
|
||||
set2Executed = false,
|
||||
set3Executed = false,
|
||||
expects = expectedEvents.length + 6,
|
||||
listeners = [];
|
||||
|
||||
// These tests come close to 10 seconds on chrome, increasing to 15
|
||||
stop( 15000 );
|
||||
expect(expects);
|
||||
|
||||
popcorn.volume(1); // is muted later
|
||||
Popcorn.plugin( "forwards", function () {
|
||||
return {
|
||||
start: function ( event, options ) {
|
||||
|
||||
// check time sync
|
||||
popcorn.exec(2, function() {
|
||||
ok( popcorn.currentTime() >= 2, "Check time synchronization." );
|
||||
if ( !options.startFired ) {
|
||||
|
||||
options.startFired = true;
|
||||
forwardStart = !forwardStart;
|
||||
ok( forwardStart, "forward's start fired" );
|
||||
plus();
|
||||
ok( popcorn.video.paused == false, "Video is not paused" );
|
||||
plus();
|
||||
});
|
||||
popcorn.exec(49, function() {
|
||||
ok( popcorn.currentTime() >= 49, "Check time synchronization." );
|
||||
plus();
|
||||
});
|
||||
popcorn.exec(40, function() {
|
||||
ok( false, "This should not be run." );
|
||||
});
|
||||
}
|
||||
},
|
||||
end: function ( event, options ) {
|
||||
|
||||
if ( !options.endFired ) {
|
||||
|
||||
// register each events
|
||||
for ( var i in expectedEvents ) {
|
||||
(function( event ) {
|
||||
// skip same listeners already added
|
||||
for ( var i in added ) {
|
||||
if ( added[i] == event ) {
|
||||
return;
|
||||
options.endFired = true;
|
||||
forwardEnd = !forwardEnd;
|
||||
p2.currentTime(1).play();
|
||||
ok( forwardEnd, "forward's end fired" );
|
||||
plus();
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
listeners.push( {
|
||||
evt: event,
|
||||
fn: popcorn.listen( event, function() {
|
||||
eventCount++;
|
||||
var expected = expectedEvents.shift();
|
||||
if ( expected == event ) {
|
||||
ok( true, "Event: "+event + " is fired." );
|
||||
p2.forwards({
|
||||
start: 3,
|
||||
end: 4
|
||||
});
|
||||
|
||||
Popcorn.plugin( "backwards", function () {
|
||||
return {
|
||||
start: function ( event, options ) {
|
||||
|
||||
if ( !options.startFired ) {
|
||||
|
||||
options.startFired = true;
|
||||
backwardStart = !backwardStart;
|
||||
p2.currentTime(0).play();
|
||||
ok( true, "backward's start fired" );
|
||||
plus();
|
||||
}
|
||||
},
|
||||
end: function ( event, options ) {
|
||||
|
||||
if ( !options.endFired ) {
|
||||
|
||||
options.endFired = true;
|
||||
backwardEnd = !backwardEnd;
|
||||
ok( backwardEnd, "backward's end fired" );
|
||||
plus();
|
||||
p2.currentTime( 5 ).play();
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
p2.backwards({
|
||||
start: 1,
|
||||
end: 2
|
||||
});
|
||||
|
||||
Popcorn.plugin( "wrapper", {
|
||||
start: function ( event, options ) {
|
||||
|
||||
wrapperRunning[ options.wrapper ] = true;
|
||||
},
|
||||
end: function ( event, options ) {
|
||||
|
||||
wrapperRunning[ options.wrapper ] = false;
|
||||
}
|
||||
});
|
||||
|
||||
// second instance of wrapper is wrapping the first
|
||||
p2.wrapper({
|
||||
start: 6,
|
||||
end: 7,
|
||||
wrapper: "one"
|
||||
})
|
||||
.wrapper({
|
||||
start: 5,
|
||||
end: 8,
|
||||
wrapper: "two"
|
||||
})
|
||||
// checking wrapper 2's start
|
||||
.exec( 5, function() {
|
||||
|
||||
if ( execCount === 0 ) {
|
||||
|
||||
execCount++;
|
||||
ok( wrapperRunning.two, "wrapper two is running at second 5" );
|
||||
plus();
|
||||
ok( !wrapperRunning.one, "wrapper one is stopped at second 5" );
|
||||
plus();
|
||||
} else {
|
||||
ok( false, event + " is fired unexpectedly, expecting: " + expected );
|
||||
}
|
||||
})
|
||||
});
|
||||
added.push( event );
|
||||
})( expectedEvents[i] );
|
||||
// checking wrapper 1's start
|
||||
.exec( 6, function() {
|
||||
|
||||
if ( execCount === 1 ) {
|
||||
|
||||
execCount++;
|
||||
ok( wrapperRunning.two, "wrapper two is running at second 6" );
|
||||
plus();
|
||||
ok( wrapperRunning.one, "wrapper one is running at second 6" );
|
||||
plus();
|
||||
}
|
||||
})
|
||||
// checking wrapper 1's end
|
||||
.exec( 7, function() {
|
||||
|
||||
// Cleanup
|
||||
listeners.push( popcorn.listen( "ended", function() {
|
||||
Popcorn.forEach( listeners, function ( obj ) {
|
||||
popcorn.unlisten( obj.evt, obj.fn );
|
||||
});
|
||||
}));
|
||||
if ( execCount === 2 ) {
|
||||
|
||||
// operations set1
|
||||
popcorn.listen( 'playing', function() {
|
||||
// prevent double calling
|
||||
if ( set1Executed ) {
|
||||
return;
|
||||
execCount++;
|
||||
ok( wrapperRunning.two, "wrapper two is running at second 7" );
|
||||
plus();
|
||||
ok( !wrapperRunning.one, "wrapper one is stopped at second 7" );
|
||||
plus();
|
||||
}
|
||||
})
|
||||
// checking wrapper 2's end
|
||||
.exec( 8, function() {
|
||||
|
||||
// toggle volume 1 second after playing
|
||||
setTimeout(function() {
|
||||
popcorn.volume(0.5);
|
||||
}, 1000);
|
||||
if ( execCount === 3 ) {
|
||||
|
||||
// pause 3 seconds after playing
|
||||
setTimeout(function() {
|
||||
popcorn.pause();
|
||||
}, 3000);
|
||||
|
||||
set1Executed = true;
|
||||
execCount++;
|
||||
ok( !wrapperRunning.two, "wrapper two is stopped at second 9" );
|
||||
plus();
|
||||
ok( !wrapperRunning.one, "wrapper one is stopped at second 9" );
|
||||
plus();
|
||||
}
|
||||
});
|
||||
|
||||
// begin the test
|
||||
popcorn.play();
|
||||
p2.currentTime(3).play();
|
||||
|
||||
// operations set2
|
||||
popcorn.listen( 'pause', function() {
|
||||
if ( set2Executed ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// continue to play
|
||||
setTimeout(function() {
|
||||
popcorn.play();
|
||||
}, 500);
|
||||
|
||||
// seek to the end
|
||||
setTimeout(function() {
|
||||
popcorn.currentTime(48);
|
||||
}, 1000);
|
||||
|
||||
set2Executed = true;
|
||||
});
|
||||
|
||||
// operations set3
|
||||
popcorn.listen( 'seeked', function() {
|
||||
if ( set3Executed ) {
|
||||
return;
|
||||
test("Plugin Factory", function () {
|
||||
|
||||
QUnit.reset();
|
||||
|
||||
var popped = Popcorn.youtube( '#video2', 'http://www.youtube.com/watch?v=9oar9glUCL0' ),
|
||||
methods = "load play pause currentTime mute volume roundTime exec removePlugin",
|
||||
expects = 34, // 15*2+2+2. executor/complicator each do 15
|
||||
count = 0;
|
||||
|
||||
function plus() {
|
||||
if ( ++count == expects ) {
|
||||
Popcorn.removePlugin("executor");
|
||||
Popcorn.removePlugin("complicator");
|
||||
start();
|
||||
}
|
||||
}
|
||||
|
||||
popcorn.volume(0.5);
|
||||
expect( expects );
|
||||
stop( 15000 );
|
||||
|
||||
popcorn.mute();
|
||||
equals( popcorn.volume(), 0, "Muted" );
|
||||
Popcorn.plugin("executor", function () {
|
||||
|
||||
return {
|
||||
|
||||
start: function () {
|
||||
var self = this;
|
||||
|
||||
// These ensure that a popcorn instance is the value of `this` inside a plugin definition
|
||||
|
||||
methods.split(/\s+/g).forEach(function (k,v) {
|
||||
ok( k in self, "executor instance has method: " + k );
|
||||
|
||||
plus();
|
||||
});
|
||||
|
||||
ok( "media" in this, "executor instance has `media` property" );
|
||||
plus();
|
||||
ok( Object.prototype.toString.call(popped.media) === "[object Object]", "video property is a HTML DIV element" );
|
||||
plus();
|
||||
|
||||
popcorn.mute();
|
||||
ok( popcorn.volume() !== 0, "Not Muted" );
|
||||
ok( "data" in this, "executor instance has `data` property" );
|
||||
plus();
|
||||
ok( Object.prototype.toString.call(popped.data) === "[object Object]", "data property is an object" );
|
||||
plus();
|
||||
|
||||
equals( popcorn.volume(), 0.5, "Back to volume of 1" );
|
||||
ok( "trackEvents" in this.data, "executor instance has `trackEvents` property" );
|
||||
plus();
|
||||
ok( Object.prototype.toString.call(popped.data.trackEvents) === "[object Object]", "executor trackEvents property is an object" )
|
||||
plus();
|
||||
},
|
||||
end: function () {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
set3Executed = true;
|
||||
});
|
||||
|
||||
ok( "executor" in popped, "executor plugin is now available to instance" );
|
||||
plus();
|
||||
equals( Popcorn.registry.length, 1, "One item in the registry");
|
||||
plus();
|
||||
|
||||
popped.executor({
|
||||
start: 1,
|
||||
end: 2
|
||||
});
|
||||
|
||||
Popcorn.plugin("complicator", {
|
||||
|
||||
start: function ( event ) {
|
||||
|
||||
var self = this;
|
||||
|
||||
// These ensure that a popcorn instance is the value of `this` inside a plugin definition
|
||||
|
||||
methods.split(/\s+/g).forEach(function (k,v) {
|
||||
ok( k in self, "complicator instance has method: " + k );
|
||||
|
||||
plus();
|
||||
});
|
||||
|
||||
ok( "media" in this, "complicator instance has `media` property" );
|
||||
plus();
|
||||
ok( Object.prototype.toString.call(popped.media) === "[object Object]", "video property is a HTMLVideoElement" );
|
||||
plus();
|
||||
|
||||
ok( "data" in this, "complicator instance has `data` property" );
|
||||
plus();
|
||||
ok( Object.prototype.toString.call(popped.data) === "[object Object]", "complicator data property is an object" );
|
||||
plus();
|
||||
|
||||
ok( "trackEvents" in this.data, " complicatorinstance has `trackEvents` property" );
|
||||
plus();
|
||||
ok( Object.prototype.toString.call(popped.data.trackEvents) === "[object Object]", "complicator trackEvents property is an object" )
|
||||
plus();
|
||||
},
|
||||
end: function () {
|
||||
|
||||
//start();
|
||||
|
||||
},
|
||||
timeupdate: function () {
|
||||
}
|
||||
});
|
||||
|
||||
ok( "complicator" in popped, "complicator plugin is now available to instance" );
|
||||
plus();
|
||||
equals( Popcorn.registry.length, 2, "Two items in the registry");
|
||||
plus();
|
||||
|
||||
popped.complicator({
|
||||
start: 4,
|
||||
end: 5
|
||||
});
|
||||
|
||||
popped.currentTime(0).play();
|
||||
|
||||
});
|
||||
|
||||
test( "Popcorn YouTube Plugin Url and Duration Tests", function() {
|
||||
|
@ -166,19 +296,19 @@ test( "Popcorn YouTube Plugin Url and Duration Tests", function() {
|
|||
|
||||
var count = 0,
|
||||
expects = 3,
|
||||
popcorn = Popcorn( Popcorn.youtube( 'video2', 'http://www.youtube.com/watch?v=9oar9glUCL0' ) );
|
||||
popcorn = Popcorn.youtube( '#video2', 'http://www.youtube.com/watch?v=9oar9glUCL0' );
|
||||
|
||||
expect( expects );
|
||||
stop( 10000 );
|
||||
|
||||
equals( popcorn.video.vidId, '9oar9glUCL0', 'Video id set' );
|
||||
equals( popcorn.media.id, 'video2', 'Video id set' );
|
||||
plus();
|
||||
|
||||
equals( popcorn.duration(), 0, 'Duration starts as 0');
|
||||
plus();
|
||||
|
||||
popcorn.listen( "durationchange", function() {
|
||||
notEqual( 0, popcorn.duration(), "Duration has been changed from 0" );
|
||||
notEqual( popcorn.duration(), 0, "Duration has been changed from 0" );
|
||||
plus();
|
||||
|
||||
popcorn.pause();
|
||||
|
@ -194,47 +324,53 @@ test( "Popcorn YouTube Plugin Url Regex Test", function() {
|
|||
var urlTests = [
|
||||
{ name: 'standard',
|
||||
url: 'http://www.youtube.com/watch?v=9oar9glUCL0',
|
||||
expected: '9oar9glUCL0',
|
||||
expected: 'http://www.youtube.com/watch?v=9oar9glUCL0',
|
||||
},
|
||||
{ name: 'share url',
|
||||
url: 'http://youtu.be/9oar9glUCL0',
|
||||
expected: '9oar9glUCL0',
|
||||
expected: 'http://youtu.be/9oar9glUCL0',
|
||||
},
|
||||
{ name: 'long embed',
|
||||
url: 'http://www.youtube.com/embed/9oar9glUCL0',
|
||||
expected: '9oar9glUCL0',
|
||||
expected: 'http://www.youtube.com/embed/9oar9glUCL0',
|
||||
},
|
||||
{ name: 'short embed 1 (e)',
|
||||
url: 'http://www.youtube.com/e/9oar9glUCL0',
|
||||
expected: '9oar9glUCL0',
|
||||
expected: 'http://www.youtube.com/e/9oar9glUCL0',
|
||||
},
|
||||
{ name: 'short embed 2 (v)',
|
||||
url: 'http://www.youtube.com/v/9oar9glUCL0',
|
||||
expected: '9oar9glUCL0',
|
||||
expected: 'http://www.youtube.com/v/9oar9glUCL0',
|
||||
},
|
||||
{ name: 'contains underscore',
|
||||
url: 'http://www.youtube.com/v/GP53b__h4ew',
|
||||
expected: 'GP53b__h4ew',
|
||||
expected: 'http://www.youtube.com/v/GP53b__h4ew',
|
||||
},
|
||||
];
|
||||
|
||||
expect( urlTests.length );
|
||||
var count = 0,
|
||||
expects = urlTests.length;
|
||||
|
||||
expect( expects );
|
||||
stop( 10000 );
|
||||
|
||||
for ( var t in urlTests ) {
|
||||
Popcorn.forEach( urlTests, function( valuse, key ) {
|
||||
|
||||
var urlTest = urlTests[t],
|
||||
popcorn = Popcorn( Popcorn.youtube( 'video3', urlTest.url ) );
|
||||
var urlTest = urlTests[ key ],
|
||||
popcorn = Popcorn.youtube( '#video3', urlTest.url );
|
||||
|
||||
equals( popcorn.video.vidId, urlTest.expected, 'Video id is correct for ' + urlTest.name + ': ' + urlTest.url );
|
||||
popcorn.listen( "loadeddata", function() {
|
||||
|
||||
equals( popcorn.media.src, urlTest.expected, 'Video id is correct for ' + urlTest.name + ': ' + urlTest.url );
|
||||
popcorn.pause();
|
||||
|
||||
// Get rid of the youtube object inside the video3, to keep things simple
|
||||
var div = document.getElementById('video3');
|
||||
div.removeChild(div.firstChild);
|
||||
}
|
||||
count++;
|
||||
if ( count === expects ) {
|
||||
|
||||
start();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test( "Controls and Annotations toggling", function() {
|
||||
|
@ -243,24 +379,25 @@ test( "Controls and Annotations toggling", function() {
|
|||
|
||||
expect( 6 );
|
||||
|
||||
var popcorn = Popcorn( Popcorn.youtube( "video", "http://www.youtube.com/watch?v=9oar9glUCL0" ) ),
|
||||
var popcorn = Popcorn.youtube( "#video", "http://www.youtube.com/watch?v=9oar9glUCL0" ),
|
||||
targetDiv = document.getElementById( "video" );
|
||||
testTarget = targetDiv.querySelector( "object" ).querySelector( "param:nth-of-type( 4 )" );
|
||||
testTarget = targetDiv.querySelector( "object" ).querySelector( "param[name=flashvars]" );
|
||||
|
||||
ok( /controls=1/.test( testTarget.value ), "controls are defaulted to 1 ( displayed )" );
|
||||
ok( /iv_load_policy=1/.test( testTarget.value ), "annotations ( iv_load_policy ) are defaulted to ( enabled )" );
|
||||
|
||||
targetDiv.innerHTML = "";
|
||||
|
||||
popcorn = Popcorn( Popcorn.youtube( "video", "http://www.youtube.com/watch?v=9oar9glUCL0", { controls: 1, annotations: 1 } ) );
|
||||
testTarget = targetDiv.querySelector( "object" ).querySelector( "param:nth-of-type( 4 )" );
|
||||
popcorn = Popcorn.youtube( "#video", "http://www.youtube.com/watch?v=9oar9glUCL0", { controls: 1, annotations: 1 } );
|
||||
|
||||
testTarget = targetDiv.querySelector( "object" ).querySelector( "param[name=flashvars]" );
|
||||
ok( /controls=1/.test( testTarget.value ), "controls is set to 1 ( displayed )" );
|
||||
ok( /iv_load_policy=1/.test( testTarget.value ), "annotations ( iv_load_policy ) is set to 1 ( enabled )" );
|
||||
|
||||
targetDiv.innerHTML = "";
|
||||
|
||||
popcorn = Popcorn( Popcorn.youtube( "video", "http://www.youtube.com/watch?v=9oar9glUCL0", { controls: 0, annotations: 3 } ) );
|
||||
testTarget = targetDiv.querySelector( "object" ).querySelector( "param:nth-of-type( 4 )" );
|
||||
popcorn = Popcorn.youtube( "#video", "http://www.youtube.com/watch?v=9oar9glUCL0", { controls: 0, annotations: 3 } );
|
||||
testTarget = targetDiv.querySelector( "object" ).querySelector( "param[name=flashvars]" );
|
||||
ok( /controls=0/.test( testTarget.value ), "controls is set to 0 ( hidden )" );
|
||||
ok( /iv_load_policy=3/.test( testTarget.value ), "annotations ( iv_load_policy ) is set to 3 ( hidden )" );
|
||||
|
||||
|
|
159
popcorn.js
159
popcorn.js
|
@ -1488,6 +1488,165 @@
|
|||
return parser;
|
||||
};
|
||||
|
||||
Popcorn.player = function( name, player ) {
|
||||
|
||||
player = player || {};
|
||||
|
||||
var playerFn = function( target, src, options ) {
|
||||
|
||||
options = options || {};
|
||||
|
||||
// List of events
|
||||
var date = new Date() / 1000,
|
||||
baselineTime = date,
|
||||
currentTime = 0,
|
||||
timeout,
|
||||
events = {},
|
||||
|
||||
// The container div of the resource
|
||||
container = document.getElementById( rIdExp.exec( target ) && rIdExp.exec( target )[ 2 ] ) || document.createElement( "div" ),
|
||||
basePlayer = {},
|
||||
popcorn;
|
||||
|
||||
// copies a div into the media object
|
||||
for( var val in container ) {
|
||||
|
||||
if ( typeof container[ val ] === "object" ) {
|
||||
|
||||
basePlayer[ val ] = container[ val ];
|
||||
} else if ( typeof container[ val ] === "function" ) {
|
||||
|
||||
basePlayer[ val ] = ( function( value ) {
|
||||
|
||||
return function() {
|
||||
|
||||
return container[ value ].apply( container, arguments );
|
||||
};
|
||||
}( val ));
|
||||
} else {
|
||||
|
||||
basePlayer.__defineGetter__( val, ( function( value ) {
|
||||
|
||||
return function() {
|
||||
|
||||
return container[ value ];
|
||||
};
|
||||
}( val )));
|
||||
}
|
||||
}
|
||||
|
||||
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" );
|
||||
};
|
||||
|
||||
basePlayer.__defineSetter__( "currentTime", function( val ) {
|
||||
|
||||
// make sure val is a number
|
||||
currentTime = +val;
|
||||
basePlayer.dispatchEvent( "timeupdate" );
|
||||
return currentTime;
|
||||
});
|
||||
|
||||
basePlayer.__defineGetter__( "currentTime", function() {
|
||||
|
||||
return currentTime;
|
||||
});
|
||||
|
||||
// Adds an event listener to the object
|
||||
basePlayer.addEventListener = function( evtName, fn ) {
|
||||
|
||||
if ( !events[ evtName ] ) {
|
||||
|
||||
events[ evtName ] = [];
|
||||
}
|
||||
|
||||
events[ evtName ].push( fn );
|
||||
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 );
|
||||
}
|
||||
}
|
||||
|
||||
Popcorn.forEach( events[ eventName ], function( val ) {
|
||||
|
||||
val.call( self, evt, self );
|
||||
});
|
||||
};
|
||||
|
||||
// Attempt to get src from playerFn parameter
|
||||
basePlayer.src = src || "";
|
||||
basePlayer.readyState = 0;
|
||||
basePlayer.duration = 0;
|
||||
basePlayer.paused = true;
|
||||
basePlayer.ended = 0;
|
||||
|
||||
// basePlayer has no concept of sound
|
||||
basePlayer.volume = 1;
|
||||
basePlayer.muted = 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( 'load' );
|
||||
}
|
||||
|
||||
popcorn = new Popcorn.p.init( basePlayer, options );
|
||||
|
||||
return popcorn;
|
||||
};
|
||||
|
||||
Popcorn[ name ] = Popcorn[ name ] || playerFn;
|
||||
};
|
||||
|
||||
// Cache references to reused RegExps
|
||||
var rparams = /\?/,
|
||||
|
|
|
@ -3368,7 +3368,192 @@ test("dataType: XML Response", function() {
|
|||
|
||||
});
|
||||
|
||||
module( "Popcorn Player" );
|
||||
|
||||
test( "Base player methods", function() {
|
||||
|
||||
var expects = 2;
|
||||
|
||||
expect( expects );
|
||||
|
||||
stop( 10000 );
|
||||
|
||||
ok( Popcorn.player, "Popcorn.player function exists" );
|
||||
|
||||
Popcorn.player( "newplayer" );
|
||||
ok( Popcorn.newplayer, "Popcorn.player registers new players" );
|
||||
|
||||
start();
|
||||
});
|
||||
|
||||
test( "Base player functionality", function() {
|
||||
|
||||
Popcorn.player( "baseplayer" );
|
||||
|
||||
//QUnit.reset();
|
||||
|
||||
var p2 = Popcorn.baseplayer( '#video' ),
|
||||
expects = 12,
|
||||
count = 0,
|
||||
execCount = 0,
|
||||
// These make sure events are only fired once
|
||||
// any second call will produce a failed test
|
||||
forwardStart = false,
|
||||
forwardEnd = false,
|
||||
backwardStart = false,
|
||||
backwardEnd = false,
|
||||
wrapperRunning = { one: false, two: false, };
|
||||
|
||||
function plus() {
|
||||
if ( ++count === expects ) {
|
||||
// clean up added events after tests
|
||||
Popcorn.removePlugin( "forwards" );
|
||||
Popcorn.removePlugin( "backwards" );
|
||||
Popcorn.removePlugin( "wrapper" );
|
||||
p2.removePlugin( "exec" );
|
||||
start();
|
||||
}
|
||||
}
|
||||
|
||||
// These tests come close to 10 seconds on chrome, increasing to 15
|
||||
stop( 15000 );
|
||||
|
||||
Popcorn.plugin( "forwards", function () {
|
||||
return {
|
||||
start: function ( event, options ) {
|
||||
|
||||
if ( !options.startFired ) {
|
||||
|
||||
options.startFired = true;
|
||||
forwardStart = !forwardStart;
|
||||
ok( forwardStart, "forward's start fired" );
|
||||
plus();
|
||||
}
|
||||
},
|
||||
end: function ( event, options ) {
|
||||
|
||||
if ( !options.endFired ) {
|
||||
|
||||
options.endFired = true;
|
||||
forwardEnd = !forwardEnd;
|
||||
p2.currentTime(1).play();
|
||||
ok( forwardEnd, "forward's end fired" );
|
||||
plus();
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
p2.forwards({
|
||||
start: 3,
|
||||
end: 4
|
||||
});
|
||||
|
||||
Popcorn.plugin( "backwards", function () {
|
||||
return {
|
||||
start: function ( event, options ) {
|
||||
|
||||
if ( !options.startFired ) {
|
||||
|
||||
options.startFired = true;
|
||||
backwardStart = !backwardStart;
|
||||
p2.currentTime(0).play();
|
||||
ok( true, "backward's start fired" );
|
||||
plus();
|
||||
}
|
||||
},
|
||||
end: function ( event, options ) {
|
||||
|
||||
if ( !options.endFired ) {
|
||||
|
||||
options.endFired = true;
|
||||
backwardEnd = !backwardEnd;
|
||||
ok( backwardEnd, "backward's end fired" );
|
||||
plus();
|
||||
p2.currentTime( 5 ).play();
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
p2.backwards({
|
||||
start: 1,
|
||||
end: 2
|
||||
});
|
||||
|
||||
Popcorn.plugin( "wrapper", {
|
||||
start: function ( event, options ) {
|
||||
|
||||
wrapperRunning[ options.wrapper ] = true;
|
||||
},
|
||||
end: function ( event, options ) {
|
||||
|
||||
wrapperRunning[ options.wrapper ] = false;
|
||||
}
|
||||
});
|
||||
|
||||
// second instance of wrapper is wrapping the first
|
||||
p2.wrapper({
|
||||
start: 6,
|
||||
end: 7,
|
||||
wrapper: "one"
|
||||
})
|
||||
.wrapper({
|
||||
start: 5,
|
||||
end: 8,
|
||||
wrapper: "two"
|
||||
})
|
||||
// checking wrapper 2's start
|
||||
.exec( 5, function() {
|
||||
|
||||
if ( execCount === 0 ) {
|
||||
|
||||
execCount++;
|
||||
ok( wrapperRunning.two, "wrapper two is running at second 5" );
|
||||
plus();
|
||||
ok( !wrapperRunning.one, "wrapper one is stopped at second 5" );
|
||||
plus();
|
||||
}
|
||||
})
|
||||
// checking wrapper 1's start
|
||||
.exec( 6, function() {
|
||||
|
||||
if ( execCount === 1 ) {
|
||||
|
||||
execCount++;
|
||||
ok( wrapperRunning.two, "wrapper two is running at second 6" );
|
||||
plus();
|
||||
ok( wrapperRunning.one, "wrapper one is running at second 6" );
|
||||
plus();
|
||||
}
|
||||
})
|
||||
// checking wrapper 1's end
|
||||
.exec( 7, function() {
|
||||
|
||||
if ( execCount === 2 ) {
|
||||
|
||||
execCount++;
|
||||
ok( wrapperRunning.two, "wrapper two is running at second 7" );
|
||||
plus();
|
||||
ok( !wrapperRunning.one, "wrapper one is stopped at second 7" );
|
||||
plus();
|
||||
}
|
||||
})
|
||||
// checking wrapper 2's end
|
||||
.exec( 8, function() {
|
||||
|
||||
if ( execCount === 3 ) {
|
||||
|
||||
execCount++;
|
||||
ok( !wrapperRunning.two, "wrapper two is stopped at second 9" );
|
||||
plus();
|
||||
ok( !wrapperRunning.one, "wrapper one is stopped at second 9" );
|
||||
plus();
|
||||
}
|
||||
});
|
||||
|
||||
p2.currentTime(3).play();
|
||||
});
|
||||
|
||||
module("Popcorn Parser");
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче