var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.url = "";
$scope.urlDefaults = {
"youtube": "https://www.youtube.com/watch?v=jySfU10IQu4",
"vimeo": "http://vimeo.com/43382919",
"dailymotion": "http://www.dailymotion.com/video/x178ukc_le-but-en-or-de-ronaldo-contre-la-suede_sport"
};
$scope.logs = [];
$scope.showLogs = true;
$scope.playerForRecreation = { dispose: function(){
document.getElementById("theVideoRecreation").remove();
}
};
// Keep this for reinsertion
$scope.videoForRecreation = document.getElementById("theVideoRecreation").cloneNode();
techOrder = [
"dailymotion",
"vimeo",
"youtube",
"html5",
"flash"
];
// Init the first player for calling player.src() on
initPlayer = function(){
debug("init");
$scope.playerForSrc = videojs("theVideo", {
techOrder: techOrder,
src: $scope.url
}, function(){
});
};
setTimeout(initPlayer, 500);
// Log to console and web page
debug = function(text){
console.log(arguments);
$scope.logs.unshift({ time: new Date().toUTCString(), text: text});
console.debug(text);
};
debug("debug created");
// guess techOrder to use
determineTech = function(){
var uri = new URI($scope.url);
console.debug(uri);
switch(uri.host){
case "www.youtube.com":
debug("youtube tech");
return ["youtube"];
case "www.dailymotion.com":
debug("dailymotion tech");
return ["dailymotion"];
case "www.vimeo.com":
case "vimeo.com":
debug("vimeo tech");
return ["vimeo"];
default:
debug("default tech");
return [ "html5", "flash"]
}
};
// Update the players to use the given url
$scope.updateUrl = function(){
debug("Update with url: " + $scope.url);
$scope.playerForSrc.src($scope.url);
// Init other player
$scope.playerForRecreation.dispose();
debug("Dispose old player");
document.getElementById("recreationContainer").appendChild($scope.videoForRecreation.cloneNode());
$scope.playerForRecreation = videojs("theVideoRecreation", {
techOrder: determineTech(),
src: $scope.url
});
};
//
$scope.pickDefault = function(aDefault){
$scope.url = aDefault;
$scope.updateUrl();
};
});
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>Videojs tech tests</title>
<link data-require="video.js@*" data-semver="4.2.2" rel="stylesheet" href="http://vjs.zencdn.net/4.2.2/video-js.css" />
<script data-require="video.js@*" data-semver="4.2.2" src="http://vjs.zencdn.net/4.2.2/video.js"></script>
<script>
document.write('<base href="' + document.location + '" />');
</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js@1.2.x" src="http://code.angularjs.org/1.2.0/angular.js" data-semver="1.2.0"></script>
<script type="text/javascript" charset="utf-8" src="uri.min.js"></script>
<script type="text/javascript" charset="utf-8" src="videojs-dailymotion.js"></script>
<script type="text/javascript" charset="utf-8" src="videojs-vimeo.js"></script>
<script type="text/javascript" charset="utf-8" src="videojs-youtube.js"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<div class="floater">
<video class="video-js vjs-default-skin vjs-big-play-centered" id="theVideo" height="200px" width="100%" controls></video>
</div>
<div id="recreationContainer" class="floater">
<video class="video-js vjs-default-skin vjs-big-play-centered" id="theVideoRecreation" height="200px" width="100%" controls></video>
</div>
<form name="form">
<input class="wideInput" type="url" name="url" id="url" placeholder="Enter a youtube, dailymotion or vimeo medium URL here" ng-model="url" />
<button ng-click="updateUrl()">Update player</button>
</form>
<fieldset>
<legend>Defaults:</legend>
<button ng-repeat="(name,url) in urlDefaults" ng-click="pickDefault(url)"> {{name}}</button>
</fieldset>
<fieldset id="logContainer">
<legend>Logs:</legend>
<ul id="logOutput">
<li ng-repeat="log in logs" ng-class-odd="'odd'" ng-class-even="'even'" class="vjs-default-skin:hover">
[{{log.time}}] {{log.text}}
</li>
</ul>
</fieldset>
</body>
</html>
/* Put your css in here */
input,body,html,button,ul,li,fieldset{
padding: 0px;
margin: 0px;
/*border: 0px;*/
/*outline:0px;*/
}
.wideInput{
width: 80%;
}
.floater{
float:left;
width: 45%;
}
#logContainer {
height: 100px;
/*position: fixed;*/
/*bottom: 0px;*/
height: 100%;
width: 100%;
}
#logContainer ul{
overflow-y: auto;
overflow-x: hidden;
width: 100%;
height: 100%;
}
li,ul{
display:block;
width: 100%;
}
.even{
background-color: seagreen;
}
.odd{
background-color: rgb(204, 204, 204);
}
/**
* @fileoverview Dailymotion Media Controller - Wrapper for Dailymotion Media API
* @version https://github.com/benjipott/video.js-dailymotion/tree/b6c2410e693c235357adcf470a2c11a7be7215dc/src/media.dailymotion.js
*/
/**
* Dailymotion Media Controller - Wrapper for Dailymotion Media API
* @param {videojs.Player|Object} player
* @param {Object=} options
* @param {Function=} ready
* @constructor
*/
videojs.Dailymotion = videojs.MediaTechController.extend({
init: function (player, options, ready) {
videojs.MediaTechController.call(this, player, options, ready);
this.features.fullscreenResize = true;
this.player_ = player;
this.player_el_ = document.getElementById(this.player_.id());
// Copy the Javascript options if they exist
if (typeof options.source != 'undefined') {
for (var key in options.source) {
this.player_.options()[key] = options.source[key];
}
}
this.videoId = videojs.Dailymotion.parseVideoId(this.player_.options().src);
if (typeof this.videoId != 'undefined') {
// Show the Dailymotion poster only if we don't use Dailymotion poster (otherwise the controls pop, it's not nice)
if (!this.player_.options().dmControls) {
// Set the Dailymotion poster only if none is specified
if (typeof this.player_.poster() == 'undefined') {
this.player_.poster('https://api.dailymotion.com/video/' + this.videoId + '?fields=url');
}
// Cover the entire iframe to have the same poster than Dailymotion
// Doesn't exist right away because the DOM hasn't created it
var self = this;
setTimeout(function () {
self.player_.posterImage.el().style.backgroundSize = 'cover';
}, 50);
}
}
this.id_ = this.player_.id() + '_dailymotion_api';
this.el_ = videojs.Component.prototype.createEl('iframe', {
id: this.id_,
className: 'vjs-tech',
scrolling: 'no',
marginWidth: 0,
marginHeight: 0,
frameBorder: 0,
webkitAllowFullScreen: '',
mozallowfullscreen: '',
allowFullScreen: ''
});
this.player_el_.insertBefore(this.el_, this.player_el_.firstChild);
this.params = {
id: this.id_,
autoplay: (this.player_.options().autoplay) ? 1 : 0,
chromeless: (this.player_.options().dmControls) ? 1 : 0,
html: 1,
info: 1,
logo: 1,
controls: 'html',
wmode: 'opaque',
format: 'json',
url: this.player_.options().src
};
if (typeof this.params.list == 'undefined') {
delete this.params.list;
}
// Make autoplay work for iOS
if (this.player_.options().autoplay) {
this.player_.bigPlayButton.hide();
this.playOnReady = true;
}
// If we are not on a server, don't specify the origin (it will crash)
if (window.location.protocol != 'file:') {
this.params.origin = window.location.protocol + '//' + window.location.hostname;
}
this.el_.src = 'http://www.dailymotion.com/services/oembed?' + videojs.Dailymotion.makeQueryString(this.params);
if (videojs.Dailymotion.apiReady) {
this.loadApi();
} else {
// Add to the queue because the Dailymotion API is not ready
videojs.Dailymotion.loadingQueue.push(this);
// Load the Dailymotion API if it is the first Dailymotion video
if (!videojs.Dailymotion.apiLoading) {
var tag = document.createElement('script');
tag.src = 'http://api.dmcdn.net/all.js';
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
videojs.Dailymotion.apiLoading = true;
}
}
}
});
videojs.Dailymotion.prototype.params = [];
videojs.Dailymotion.prototype.dispose = function () {
if (this.el_) {
this.el_.parentNode.removeChild(this.el_);
}
/*if (this.dmPlayer) {
this.dmPlayer.destroy();
}*/
videojs.MediaTechController.prototype.dispose.call(this);
};
videojs.Dailymotion.prototype.src = function (src) {
this.dmPlayer.load(videojs.Dailymotion.parseVideoId(src));
};
videojs.Dailymotion.prototype.currentSrc = function () {
if (this.isReady_) {
return this.params.url;
}
else {
return null;
}
};
videojs.Dailymotion.prototype.play = function () {
if (this.isReady_) {
this.dmPlayer.play();
} else {
// We will play it when the API will be ready
this.playOnReady = true;
if (!this.player_.options.dmControls) {
// Keep the big play button until it plays for real
this.player_.bigPlayButton.show();
}
}
};
videojs.Dailymotion.prototype.ended = function () {
if (this.isReady_) {
var stateId = this.dmPlayer.getPlayerState();
return stateId == 0;
} else {
// We will play it when the API will be ready
return false;
}
};
videojs.Dailymotion.prototype.pause = function () {
this.dmPlayer.pause(!this.dmPlayer.paused);
};
videojs.Dailymotion.prototype.paused = function () {
return this.dmPlayer.paused;
};
videojs.Dailymotion.prototype.currentTime = function () {
return this.dmPlayer.currentTime;
};
videojs.Dailymotion.prototype.setCurrentTime = function (seconds) {
this.dmPlayer.seek(seconds, true);
this.player_.trigger('timeupdate');
};
videojs.Dailymotion.prototype._duration;
videojs.Dailymotion.prototype.duration = function () {
return this.dmPlayer.duration;
};
videojs.Dailymotion.prototype.buffered = function () {
/*var loadedBytes = this.dmPlayer.getVideoBytesLoaded();
var totalBytes = this.dmPlayer.getVideoBytesTotal();
if (!loadedBytes || !totalBytes) return 0;
var duration = this.dmPlayer.getDuration();
var secondsBuffered = (loadedBytes / totalBytes) * duration;
var secondsOffset = (this.dmPlayer.getVideoStartBytes() / totalBytes) * duration;
return videojs.createTimeRange(secondsOffset, secondsOffset + secondsBuffered); */
};
videojs.Dailymotion.prototype.volume = function () {
if (isNaN(this.volumeVal)) {
this.volumeVal = this.dmPlayer.volume;
}
return this.volumeVal;
};
videojs.Dailymotion.prototype.setVolume = function (percentAsDecimal) {
if (percentAsDecimal && percentAsDecimal != this.volumeVal) {
this.dmPlayer.volume = percentAsDecimal;
this.volumeVal = percentAsDecimal;
this.player_.trigger('volumechange');
}
};
videojs.Dailymotion.prototype.muted = function () {
return this.dmPlayer.muted;
};
videojs.Dailymotion.prototype.setMuted = function (muted) {
this.dmPlayer.muted = muted;
var self = this;
setTimeout(function () {
self.player_.trigger('volumechange');
}, 50);
};
videojs.Dailymotion.prototype.onReady = function () {
this.isReady_ = true;
this.player_.trigger('techready');
// Hide the poster when ready because Dailymotion has it's own
this.triggerReady();
this.player_.trigger('durationchange');
// Play right away if we clicked before ready
if (this.playOnReady) {
this.dmPlayer.play();
}
};
videojs.Dailymotion.isSupported = function () {
return true;
};
videojs.Dailymotion.prototype.supportsFullScreen = function () {
return false;
};
videojs.Dailymotion.canPlaySource = function (srcObj) {
return (srcObj.type == 'video/dailymotion');
};
// All videos created before Dailymotion API is loaded
videojs.Dailymotion.loadingQueue = [];
// Create the Dailymotion player
videojs.Dailymotion.prototype.loadApi = function () {
this.dmPlayer = new DM.player(this.id_, {
video: this.videoId,
width: this.options.width,
height: this.options.height,
params: this.params
});
this.setupTriggers();
this.dmPlayer.vjsTech = this;
};
videojs.Dailymotion.prototype.onStateChange = function (event) {
var state = event.type;
if (state != this.lastState) {
switch (state) {
case -1:
this.player_.trigger('durationchange');
break;
case 'apiready':
this.onReady();
break;
case 'ended':
if (!this.player_.options().dmControls) {
this.player_.bigPlayButton.show();
}
break;
case 'play':
case 'playing':
break;
case 'pause':
break;
case 'durationchange':
break;
case 'timeupdate':
// Hide the waiting spinner since YouTube has its own
this.player_.loadingSpinner.hide();
break;
case 'progress':
break;
}
this.lastState = state;
}
};
videojs.Dailymotion.makeQueryString = function (args) {
var array = [];
for (var key in args) {
if (args.hasOwnProperty(key)) {
array.push(encodeURIComponent(key) + '=' + encodeURIComponent(args[key]));
}
}
return array.join('&');
};
videojs.Dailymotion.parseVideoId = function (src) {
// Regex that parse the video ID for any Dailymotion URL
var regExp = /^.+dailymotion.com\/((video|hub)\/([^_]+))?[^#]*(#video=([^_&]+))?/;
var match = src.match(regExp);
return match ? match[5] || match[3] : null;
};
videojs.Dailymotion.parsePlaylist = function (src) {
// Check if we have a playlist
var regExp = /[?&]list=([^#\&\?]+)/;
var match = src.match(regExp);
if (match != null && match.length > 1) {
return match[1];
}
};
// Make video events trigger player events
// May seem verbose here, but makes other APIs possible.
videojs.Dailymotion.prototype.setupTriggers = function () {
for (var i = videojs.Dailymotion.Events.length - 1; i >= 0; i--) {
//videojs.on(this.dmPlayer, videojs.Dailymotion.Events[i], videojs.bind(this, this.eventHandler));
this.dmPlayer.addEventListener(videojs.Dailymotion.Events[i], videojs.bind(this, this.eventHandler));
}
};
// Triggers removed using this.off when disposed
videojs.Dailymotion.prototype.eventHandler = function (e) {
this.onStateChange(e);
this.trigger(e);
};
// List of all HTML5 events (various uses).
videojs.Dailymotion.Events = 'apiready,play,playing,pause,ended,canplay,canplaythrough,timeupdate,progress,seeking,seeked,volumechange,durationchange,fullscreenchange,error'.split(',');
// Called when Dailymotion API is ready to be used
window.dmAsyncInit = function () {
var dm;
while ((dm = videojs.Dailymotion.loadingQueue.shift())) {
dm.loadApi();
}
videojs.Dailymotion.loadingQueue = [];
videojs.Dailymotion.apiReady = true;
}
/**
* @fileoverview Vimeo Media Controller - Wrapper for Vimeo Media API
* @version https://github.com/eXon/videojs-vimeo/tree/0b13d8059fb4450e061737824e96a430e70637c6/src/media.vimeo.js
*/
var VimeoState = {
UNSTARTED: -1,
ENDED: 0,
PLAYING: 1,
PAUSED: 2,
BUFFERING: 3
};
/**
* Vimeo Media Controller - Wrapper for Vimeo Media API
* @param {videojs.Player|Object} player
* @param {Object=} options
* @param {Function=} ready
* @constructor
*/
videojs.Vimeo = videojs.MediaTechController.extend({
init: function(player, options, ready){
videojs.MediaTechController.call(this, player, options, ready);
this.player_ = player;
this.player_el_ = document.getElementById(this.player_.id());
// Disable lockShowing because we always use Vimeo controls
this.player_.controls(false);
// Regex that parse the video ID for any Vimeo URL
var regExp = /^.*(vimeo\.com\/)((channels\/[A-z]+\/)|(groups\/[A-z]+\/videos\/))?([0-9]+)/;
var match = player.options().src.match(regExp);
if (match){
this.videoId = match[5];
// TODO: Get the poster URL using Ajax
}
this.id_ = this.player_.id() + '_vimeo_api';
this.el_ = videojs.Component.prototype.createEl('iframe', {
id: this.id_,
className: 'vjs-tech',
scrolling: 'no',
marginWidth: 0,
marginHeight: 0,
frameBorder: 0,
webkitAllowFullScreen: '',
mozallowfullscreen: '',
allowFullScreen: ''
});
this.player_el_.insertBefore(this.el_, this.player_el_.firstChild);
var params = {
api: 1,
byline: 0,
portrait: 0,
show_title: 0,
show_byline: 0,
show_portait: 0,
fullscreen: 1,
player_id: this.id_,
autoplay: (this.player_.options().autoplay)?1:0,
loop: (this.player_.options().loop)?1:0
};
this.baseUrl = (document.location.protocol == 'https')? 'https://secure.vimeo.com/video/' : 'http://player.vimeo.com/video/';
this.vimeo = {};
this.vimeoInfo = {};
this.el_.vjsTech = this;
this.el_.onload = function() { this.vjsTech.onLoad(); };
this.el_.src = this.baseUrl + this.videoId + '?' + videojs.Vimeo.makeQueryString(params);
// Remove the big play button and the control bar, we use Vimeo controls
// Doesn't exist right away because the DOM hasn't created it
var self = this;
setTimeout(function(){
var bigPlayDom = self.player_.bigPlayButton.el();
bigPlayDom.parentNode.removeChild(bigPlayDom);
var controlBarDom = self.player_.controlBar.el();
controlBarDom.parentNode.removeChild(controlBarDom);
}, 50);
}
});
videojs.Vimeo.prototype.dispose = function(){
this.vimeo.api('unload');
delete this.vimeo;
this.el_.parentNode.removeChild(this.el_);
videojs.MediaTechController.prototype.dispose.call(this);
};
videojs.Vimeo.prototype.play = function(){ this.vimeo.api('play'); };
videojs.Vimeo.prototype.pause = function(){ this.vimeo.api('pause'); };
videojs.Vimeo.prototype.paused = function(){
return this.lastState !== VimeoState.PLAYING &&
this.lastState !== VimeoState.BUFFERING;
};
videojs.Vimeo.prototype.currentTime = function(){ return this.vimeoInfo.time || 0; };
videojs.Vimeo.prototype.setCurrentTime = function(seconds){
this.vimeo.api('seekTo', seconds);
this.player_.trigger('timeupdate');
};
videojs.Vimeo.prototype.duration = function(){ return this.vimeoInfo.duration || 0; };
videojs.Vimeo.prototype.buffered = function(){ return videojs.createTimeRange(0, this.vimeoInfo.buffered || 0); };
videojs.Vimeo.prototype.volume = function() { return (this.vimeoInfo.muted)? this.vimeoInfo.muteVolume : this.vimeoInfo.volume; };
videojs.Vimeo.prototype.setVolume = function(percentAsDecimal){
this.vimeo.api('setvolume', percentAsDecimal);
this.vimeo.vimeoInfo.volume = percentAsDecimal;
this.player_.trigger('volumechange');
};
videojs.Vimeo.prototype.muted = function() { return this.vimeoInfo.muted || false; };
videojs.Vimeo.prototype.setMuted = function(muted) {
if (muted) {
this.vimeoInfo.muteVolume = this.vimeoInfo.volume;
this.setVolume(0);
} else {
this.setVolume(this.vimeoInfo.muteVolume);
}
this.vimeoInfo.muted = muted;
this.player_.trigger('volumechange');
};
videojs.Vimeo.prototype.onReady = function(){
this.isReady_ = true;
this.player_.trigger('techready');
this.triggerReady();
this.player_.trigger('durationchange');
};
videojs.Vimeo.prototype.onLoad = function(){
this.vimeo = $f(this.el_);
this.vimeoInfo = {
state: VimeoState.UNSTARTED,
volume: 1,
muted: false,
muteVolume: 1,
time: 0,
duration: 0,
buffered: 0,
url: this.baseUrl + this.videoId,
error: null
};
this.vimeo.addEvent('ready', function(id){ document.getElementById(id).vjsTech.onReady(); });
this.vimeo.addEvent('loadProgress', function(data, id){ document.getElementById(id).vjsTech.onLoadProgress(data); });
this.vimeo.addEvent('playProgress', function(data, id){ document.getElementById(id).vjsTech.onPlayProgress(data); });
this.vimeo.addEvent('play', function(id){ document.getElementById(id).vjsTech.onPlay(); });
this.vimeo.addEvent('pause', function(id){ document.getElementById(id).vjsTech.onPause(); });
this.vimeo.addEvent('finish', function(id){ document.getElementById(id).vjsTech.onFinish(); });
this.vimeo.addEvent('seek', function(id){ document.getElementById(id).vjsTech.onSeek(data); });
};
videojs.Vimeo.prototype.onLoadProgress = function(data){
var durationUpdate = !this.vimeoInfo.duration;
this.vimeoInfo.duration = data.duration;
this.vimeoInfo.buffered = data.percent;
this.player_.trigger('progress');
if (durationUpdate) this.player_.trigger('durationchange');
};
videojs.Vimeo.prototype.onPlayProgress = function(data){
this.vimeoInfo.time = data.seconds;
this.player_.trigger('timeupdate');
};
videojs.Vimeo.prototype.onPlay = function(){
this.vimeoInfo.state = VimeoState.PLAYING;
this.player_.trigger('play');
};
videojs.Vimeo.prototype.onPause = function(){
this.vimeoInfo.state = VimeoState.PAUSED;
this.player_.trigger('pause');
};
videojs.Vimeo.prototype.onFinish = function(){
this.vimeoInfo.state = VimeoState.ENDED;
this.player_.trigger('ended');
};
videojs.Vimeo.prototype.onSeek = function(data){
this.vimeoInfo.time = data.seconds;
this.player_.trigger('timeupdate');
this.player_.trigger('seeked');
};
videojs.Vimeo.prototype.onError = function(error){
this.player_.error = error;
this.player_.trigger('error');
};
videojs.Vimeo.isSupported = function(){
return true;
};
videojs.Vimeo.prototype.supportsFullScreen = function() {
return false;
};
videojs.Vimeo.canPlaySource = function(srcObj){
return (srcObj.type == 'video/vimeo');
};
videojs.Vimeo.makeQueryString = function(args){
var array = [];
for (var key in args){
if (args.hasOwnProperty(key)){
array.push(encodeURIComponent(key) + '=' + encodeURIComponent(args[key]));
}
}
return array.join('&');
};
// Froogaloop API -------------------------------------------------------------
// From https://github.com/vimeo/player-api/blob/master/javascript/froogaloop.js
var Froogaloop = (function(){
// Define a local copy of Froogaloop
function Froogaloop(iframe) {
// The Froogaloop object is actually just the init constructor
return new Froogaloop.fn.init(iframe);
}
var eventCallbacks = {},
hasWindowEvent = false,
isReady = false,
slice = Array.prototype.slice,
playerDomain = '';
Froogaloop.fn = Froogaloop.prototype = {
element: null,
init: function(iframe) {
if (typeof iframe === "string") {
iframe = document.getElementById(iframe);
}
this.element = iframe;
// Register message event listeners
playerDomain = getDomainFromUrl(this.element.getAttribute('src'));
return this;
},
/*
* Calls a function to act upon the player.
*
* @param {string} method The name of the Javascript API method to call. Eg: 'play'.
* @param {Array|Function} valueOrCallback params Array of parameters to pass when calling an API method
* or callback function when the method returns a value.
*/
api: function(method, valueOrCallback) {
if (!this.element || !method) {
return false;
}
var self = this,
element = self.element,
target_id = element.id !== '' ? element.id : null,
params = !isFunction(valueOrCallback) ? valueOrCallback : null,
callback = isFunction(valueOrCallback) ? valueOrCallback : null;
// Store the callback for get functions
if (callback) {
storeCallback(method, callback, target_id);
}
postMessage(method, params, element);
return self;
},
/*
* Registers an event listener and a callback function that gets called when the event fires.
*
* @param eventName (String): Name of the event to listen for.
* @param callback (Function): Function that should be called when the event fires.
*/
addEvent: function(eventName, callback) {
if (!this.element) {
return false;
}
var self = this,
element = self.element,
target_id = element.id !== '' ? element.id : null;
storeCallback(eventName, callback, target_id);
// The ready event is not registered via postMessage. It fires regardless.
if (eventName != 'ready') {
postMessage('addEventListener', eventName, element);
}
else if (eventName == 'ready' && isReady) {
callback.call(null, target_id);
}
return self;
},
/*
* Unregisters an event listener that gets called when the event fires.
*
* @param eventName (String): Name of the event to stop listening for.
*/
removeEvent: function(eventName) {
if (!this.element) {
return false;
}
var self = this,
element = self.element,
target_id = element.id !== '' ? element.id : null,
removed = removeCallback(eventName, target_id);
// The ready event is not registered
if (eventName != 'ready' && removed) {
postMessage('removeEventListener', eventName, element);
}
}
};
/**
* Handles posting a message to the parent window.
*
* @param method (String): name of the method to call inside the player. For api calls
* this is the name of the api method (api_play or api_pause) while for events this method
* is api_addEventListener.
* @param params (Object or Array): List of parameters to submit to the method. Can be either
* a single param or an array list of parameters.
* @param target (HTMLElement): Target iframe to post the message to.
*/
function postMessage(method, params, target) {
if (!target.contentWindow.postMessage) {
return false;
}
var url = target.getAttribute('src').split('?')[0],
data = JSON.stringify({
method: method,
value: params
});
if (url.substr(0, 2) === '//') {
url = window.location.protocol + url;
}
target.contentWindow.postMessage(data, url);
}
/**
* Event that fires whenever the window receives a message from its parent
* via window.postMessage.
*/
function onMessageReceived(event) {
var data, method;
try {
data = JSON.parse(event.data);
method = data.event || data.method;
}
catch(e) {
//fail silently... like a ninja!
}
if (method == 'ready' && !isReady) {
isReady = true;
}
// Handles messages from moogaloop only
if (event.origin != playerDomain) {
return false;
}
var value = data.value,
eventData = data.data,
target_id = target_id === '' ? null : data.player_id,
callback = getCallback(method, target_id),
params = [];
if (!callback) {
return false;
}
if (value !== undefined) {
params.push(value);
}
if (eventData) {
params.push(eventData);
}
if (target_id) {
params.push(target_id);
}
return params.length > 0 ? callback.apply(null, params) : callback.call();
}
/**
* Stores submitted callbacks for each iframe being tracked and each
* event for that iframe.
*
* @param eventName (String): Name of the event. Eg. api_onPlay
* @param callback (Function): Function that should get executed when the
* event is fired.
* @param target_id (String) [Optional]: If handling more than one iframe then
* it stores the different callbacks for different iframes based on the iframe's
* id.
*/
function storeCallback(eventName, callback, target_id) {
if (target_id) {
if (!eventCallbacks[target_id]) {
eventCallbacks[target_id] = {};
}
eventCallbacks[target_id][eventName] = callback;
}
else {
eventCallbacks[eventName] = callback;
}
}
/**
* Retrieves stored callbacks.
*/
function getCallback(eventName, target_id) {
if (target_id) {
return eventCallbacks[target_id][eventName];
}
else {
return eventCallbacks[eventName];
}
}
function removeCallback(eventName, target_id) {
if (target_id && eventCallbacks[target_id]) {
if (!eventCallbacks[target_id][eventName]) {
return false;
}
eventCallbacks[target_id][eventName] = null;
}
else {
if (!eventCallbacks[eventName]) {
return false;
}
eventCallbacks[eventName] = null;
}
return true;
}
/**
* Returns a domain's root domain.
* Eg. returns http://vimeo.com when http://vimeo.com/channels is sbumitted
*
* @param url (String): Url to test against.
* @return url (String): Root domain of submitted url
*/
function getDomainFromUrl(url) {
if (url.substr(0, 2) === '//') {
url = window.location.protocol + url;
}
var url_pieces = url.split('/'),
domain_str = '';
for(var i = 0, length = url_pieces.length; i < length; i++) {
if(i<3) {domain_str += url_pieces[i];}
else {break;}
if(i<2) {domain_str += '/';}
}
return domain_str;
}
function isFunction(obj) {
return !!(obj && obj.constructor && obj.call && obj.apply);
}
function isArray(obj) {
return toString.call(obj) === '[object Array]';
}
// Give the init function the Froogaloop prototype for later instantiation
Froogaloop.fn.init.prototype = Froogaloop.fn;
// Listens for the message event.
// W3C
if (window.addEventListener) {
window.addEventListener('message', onMessageReceived, false);
}
// IE
else {
window.attachEvent('onmessage', onMessageReceived);
}
// Expose froogaloop to the global object
return (window.Froogaloop = window.$f = Froogaloop);
})();
/**
* @fileoverview YouTube Media Controller - Wrapper for YouTube Media API
* @version https://github.com/eXon/videojs-youtube/tree/df7aa7de60dc4a5bae5d0f27d803f13cc2cdac5e/src/media.youtube.js
*/
/**
* YouTube Media Controller - Wrapper for YouTube Media API
* @param {videojs.Player|Object} player
* @param {Object=} options
* @param {Function=} ready
* @constructor
*/
videojs.Youtube = videojs.MediaTechController.extend({
/** @constructor */
init: function(player, options, ready){
videojs.MediaTechController.call(this, player, options, ready);
// Copy the JavaScript options if they exists
if (typeof options['source'] != 'undefined') {
for (var key in options['source']) {
player.options()[key] = options['source'][key];
}
}
// Save those for internal usage
this.player_ = player;
this.player_el_ = document.getElementById(player.id());
this.player_el_.className = this.player_el_.className + ' vjs-youtube';
this.parseVideoUrl(player.options()['src']);
// Make sure nothing get in the way of the native player for iOS
if (videojs.IS_IOS) {
player.options()['ytcontrols'] = true;
}
if (player.options()['ytcontrols']){
// Disable the video.js controls if we use the YouTube controls
player.controls(false);
} else {
// Show the YouTube poster if their is no custom poster
if (!player.poster()) {
player.poster('https://img.youtube.com/vi/' + this.videoId + '/0.jpg');
}
}
this.id_ = this.player_.id() + '_youtube_api';
this.el_ = videojs.Component.prototype.createEl('iframe', {
id: this.id_,
className: 'vjs-tech',
scrolling: 'no',
marginWidth: 0,
marginHeight: 0,
frameBorder: 0,
webkitAllowFullScreen: 'true',
mozallowfullscreen: 'true',
allowFullScreen: 'true'
});
// This makes sure the mousemove is not lost within the iframe
// Only way to make sure the control bar shows when we come back in the video player
var iframeblocker = videojs.Component.prototype.createEl('div', {
className: 'iframeblocker'
});
// Make sure to not block the pause
var self = this;
var pauseThis = function() {
self.pause();
};
if (iframeblocker.addEventListener) {
iframeblocker.addEventListener('click', pauseThis);
} else {
iframeblocker.attachEvent('onclick', pauseThis);
}
this.player_el_.insertBefore(iframeblocker, this.player_el_.firstChild);
this.player_el_.insertBefore(this.el_, iframeblocker);
var params = {
enablejsapi: 1,
iv_load_policy: 3,
playerapiid: this.id(),
disablekb: 1,
wmode: 'transparent',
controls: (player.options()['ytcontrols'])?1:0,
showinfo: 0,
modestbranding: 1,
rel: 0,
autoplay: (player.options()['autoplay'])?1:0,
loop: (player.options()['loop'])?1:0,
list: this.playlistId
};
if (typeof params.list == 'undefined') {
delete params.list;
}
// If we are not on a server, don't specify the origin (it will crash)
if (window.location.protocol != 'file:'){
params.origin = window.location.protocol + '//' + window.location.host;
}
this.el_.src = 'https://www.youtube.com/embed/' + this.videoId + '?' + videojs.Youtube.makeQueryString(params);
if (videojs.Youtube.apiReady){
this.loadYoutube();
} else {
// Add to the queue because the YouTube API is not ready
videojs.Youtube.loadingQueue.push(this);
// Load the YouTube API if it is the first YouTube video
if(!videojs.Youtube.apiLoading){
var tag = document.createElement('script');
tag.src = '//www.youtube.com/iframe_api';
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
videojs.Youtube.apiLoading = true;
}
}
this.triggerReady();
}
});
videojs.Youtube.prototype.dispose = function(){
videojs.MediaTechController.prototype.dispose.call(this);
};
videojs.Youtube.prototype.src = function(src){
this.parseVideoUrl(src);
};
videojs.Youtube.prototype.play = function(){
if (this.isReady_){
this.ytplayer.playVideo();
} else {
// Display the spinner until the YouTube video is ready to play
this.player_.trigger('waiting');
this.playOnReady = true;
}
};
videojs.Youtube.prototype.pause = function(){ this.ytplayer.pauseVideo(); };
videojs.Youtube.prototype.paused = function(){ return (this.ytplayer)?(this.lastState !== YT.PlayerState.PLAYING && this.lastState !== YT.PlayerState.BUFFERING):true; };
videojs.Youtube.prototype.currentTime = function(){ return (this.ytplayer)?this.ytplayer.getCurrentTime():0; };
videojs.Youtube.prototype.setCurrentTime = function(seconds){ this.ytplayer.seekTo(seconds, true); this.player_.trigger('timeupdate'); };
videojs.Youtube.prototype.duration = function(){ return (this.ytplayer)?this.ytplayer.getDuration():0; };
videojs.Youtube.prototype.volume = function() {
if (this.ytplayer && isNaN(this.volumeVal)) {
this.volumeVal = this.ytplayer.getVolume() / 100.0;
}
return this.volumeVal;
};
videojs.Youtube.prototype.setVolume = function(percentAsDecimal){
if (percentAsDecimal && percentAsDecimal != this.volumeVal) {
this.ytplayer.setVolume(percentAsDecimal * 100.0);
this.volumeVal = percentAsDecimal;
this.player_.trigger('volumechange');
}
};
videojs.Youtube.prototype.muted = function() { return (this.ytplayer)?this.ytplayer.isMuted():false; };
videojs.Youtube.prototype.setMuted = function(muted) {
if (muted) {
this.ytplayer.mute();
} else {
this.ytplayer.unMute();
}
var self = this;
setTimeout(function() { self.player_.trigger('volumechange'); }, 50);
};
videojs.Youtube.prototype.buffered = function(){
if (this.ytplayer && this.ytplayer.getVideoBytesLoaded) {
var loadedBytes = this.ytplayer.getVideoBytesLoaded();
var totalBytes = this.ytplayer.getVideoBytesTotal();
if (!loadedBytes || !totalBytes) return 0;
var duration = this.ytplayer.getDuration();
var secondsBuffered = (loadedBytes / totalBytes) * duration;
var secondsOffset = (this.ytplayer.getVideoStartBytes() / totalBytes) * duration;
return videojs.createTimeRange(secondsOffset, secondsOffset + secondsBuffered);
} else {
return videojs.createTimeRange(0, 0);
}
};
videojs.Youtube.prototype.supportsFullScreen = function(){ return true; };
// YouTube is supported on all platforms
videojs.Youtube.isSupported = function(){ return true; };
// You can use video/youtube as a media in your HTML5 video to specify the source
videojs.Youtube.canPlaySource = function(srcObj){
return (srcObj.type == 'video/youtube');
};
// Always can control the volume
videojs.Youtube.canControlVolume = function(){ return true; };
////////////////////////////// YouTube specific functions //////////////////////////////
// Parse the whole URL
videojs.Youtube.prototype.parseVideoUrl = function(url){
// Regex to parse the video ID
var regId = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/;
var match = url.match(regId);
if (match && match[2].length == 11){
this.videoId = match[2];
} else {
this.videoId = null;
}
// Regex to parse the playlist ID
var regPlaylist = /[?&]list=([^#\&\?]+)/;
match = url.match(regPlaylist);
if (match != null && match.length > 1) {
this.playlistId = match[1];
} else {
// Make sure their is no playlist
if (this.playlistId) {
delete this.playlistId;
}
}
};
// All videos created before YouTube API is loaded
videojs.Youtube.loadingQueue = [];
// Create the YouTube player
videojs.Youtube.prototype.loadYoutube = function(){
this.ytplayer = new YT.Player(this.id_, {
events: {
onReady: function(e) { e.target.vjsTech.onReady(); },
onStateChange: function(e) { e.target.vjsTech.onStateChange(e.data); },
onPlaybackQualityChange: function(e){ e.target.vjsTech.onPlaybackQualityChange(e.data); },
onError: function(e){ e.target.vjsTech.onError(e.data); }
}
});
this.ytplayer.vjsTech = this;
};
// Transform a JavaScript object into URL params
videojs.Youtube.makeQueryString = function(args){
var array = [];
for (var key in args){
if (args.hasOwnProperty(key)){
array.push(encodeURIComponent(key) + '=' + encodeURIComponent(args[key]));
}
}
return array.join('&');
};
// Called when YouTube API is ready to be used
window.onYouTubeIframeAPIReady = function(){
var yt;
while ((yt = videojs.Youtube.loadingQueue.shift())){
yt.loadYoutube();
}
videojs.Youtube.loadingQueue = [];
videojs.Youtube.apiReady = true;
};
videojs.Youtube.prototype.onReady = function(){
this.isReady_ = true;
// Play ASAP if they clicked play before it's ready
if (this.playOnReady) {
this.play();
}
};
videojs.Youtube.prototype.onStateChange = function(state){
if (state != this.lastState){
switch(state){
case -1:
this.player_.trigger('durationchange');
break;
case YT.PlayerState.ENDED:
this.player_.trigger('ended');
break;
case YT.PlayerState.PLAYING:
this.player_.trigger('timeupdate');
this.player_.trigger('durationchange');
this.player_.trigger('playing');
this.player_.trigger('play');
break;
case YT.PlayerState.PAUSED:
this.player_.trigger('pause');
break;
case YT.PlayerState.BUFFERING:
this.player_.trigger('timeupdate');
this.player_.trigger('waiting');
break;
case YT.PlayerState.CUED:
break;
}
this.lastState = state;
}
};
videojs.Youtube.prototype.onPlaybackQualityChange = function(quality){
switch(quality){
case 'medium':
this.player_.videoWidth = 480;
this.player_.videoHeight = 360;
break;
case 'large':
this.player_.videoWidth = 640;
this.player_.videoHeight = 480;
break;
case 'hd720':
this.player_.videoWidth = 960;
this.player_.videoHeight = 720;
break;
case 'hd1080':
this.player_.videoWidth = 1440;
this.player_.videoHeight = 1080;
break;
case 'highres':
this.player_.videoWidth = 1920;
this.player_.videoHeight = 1080;
break;
case 'small':
this.player_.videoWidth = 320;
this.player_.videoHeight = 240;
break;
default:
this.player_.videoWidth = 0;
this.player_.videoHeight = 0;
break;
}
this.player_.trigger('ratechange');
};
videojs.Youtube.prototype.onError = function(error){
this.player_.error = error;
this.player_.trigger('error');
};
// Stretch the YouTube poster
// Keep the iframeblocker in front of the player when the user is inactive
// (ONLY way because the iframe is so selfish with events)
(function() {
var style = document.createElement('style');
style.innerHTML = ' \
.vjs-youtube .vjs-poster { background-size: cover; }\
.vjs-youtube.vjs-user-inactive .iframeblocker { position:absolute;top:0;left:0;width:100%;height:100%; }\
';
document.head.appendChild(style);
})();
/**
* @description URI.js provides a ruby URI likish class for JavaScript with Rails likish params en/decoding.
* @version https://github.com/jwagener/uri.js/tree/f08f509d3e5f146ed87702e4c4703f31e79f0240/build/uri.min.js
*/
var __hasProp=Object.prototype.hasOwnProperty;
window.URI=function(g,h){var i,j;g==null&&(g="");h==null&&(h={});j=/^(?:([^:\/?\#]+):)?(?:\/\/([^\/?\#]*))?([^?\#]*)(?:\?([^\#]*))?(?:\#(.*))?/;i=/^(?:([^@]*)@)?([^:]*)(?::(\d*))?/;this.scheme=this.user=this.password=this.host=this.port=this.path=this.query=this.fragment=null;this.toString=function(){var a;a="";this.isAbsolute()&&(a+=this.scheme,a+="://",this.user!=null&&(a+=this.user+":"+this.password+"@"),a+=this.host,this.port!=null&&(a+=":"+this.port));a+=this.path;if(this.path===""&&(this.query!=
null||this.fragment!=null))a+="/";this.query!=null&&(a+=this.encodeParamsWithPrepend(this.query,"?"));this.fragment!=null&&(a+=this.encodeParamsWithPrepend(this.fragment,"#"));return a};this.isRelative=function(){return!this.isAbsolute()};this.isAbsolute=function(){return this.host!=null};this.decodeParams=function(a){var c,d,b,e,f;a==null&&(a="");d={};f=a.split("&");a=0;for(e=f.length;a<e;a++)c=f[a],c!==""&&(b=c.split("="),c=decodeURIComponent(b[0]),b=decodeURIComponent(b[1]||"").replace(/\+/g," "),
this.normalizeParams(d,c,b));return d};this.normalizeParams=function(a,c,d){var b,e;d==null&&(d=NULL);b=c.match(/^[\[\]]*([^\[\]]+)\]*(.*)/);c=b[1]||"";b=b[2]||"";b===""?a[c]=d:b==="[]"?(a[c]||(a[c]=[]),a[c].push(d)):(e=b.match(/^\[\]\[([^\[\]]+)\]$/)||(e=b.match(/^\[\](.+)$/)))?(b=e[1],a[c]||(a[c]=[]),e=a[c][a[c].length-1],e!=null&&e.constructor===Object&&e[b]==null?this.normalizeParams(e,b,d):a[c].push(this.normalizeParams({},b,d))):(a[c]||(a[c]={}),a[c]=this.normalizeParams(a[c],b,d));return a};
this.encodeParamsWithPrepend=function(a,c){var d;d=this.encodeParams(a);return d!==""?c+d:""};this.encodeParams=function(a){var c,d,b,e,f;if(a.constructor===String)return a;else{a=this.flattenParams(a);d=[];e=0;for(f=a.length;e<f;e++)b=a[e],c=b[0],b=b[1],b===null?d.push(c):d.push(c+"="+encodeURIComponent(b));return d.join("&")}};this.flattenParams=function(a,c,d){var b,e,f;c==null&&(c="");d==null&&(d=[]);if(a==null)c!=null&&d.push([c,null]);else if(a.constructor===Object)for(b in a)__hasProp.call(a,
b)&&(f=a[b],e=c!==""?c+"["+b+"]":b,this.flattenParams(f,e,d));else if(a.constructor===Array){b=0;for(e=a.length;b<e;b++)f=a[b],this.flattenParams(f,c+"[]",d)}else c!==""&&d.push([c,a]);return d};this.parse=function(a,c){var d,b,e,f;a==null&&(a="");c==null&&(c={});b=function(a){return a===""?null:a};e=a.match(j);this.scheme=b(e[1]);d=e[2];if(d!=null){d=d.match(i);f=b(d[1]);if(f!=null)this.user=f.split(":")[0],this.password=f.split(":")[1];this.host=b(d[2]);this.port=parseInt(d[3],10)||null}this.path=
e[3];this.query=b(e[4]);if(c.decodeQuery)this.query=this.decodeParams(this.query);this.fragment=b(e[5]);if(c.decodeFragment)return this.fragment=this.decodeParams(this.fragment)};this.parse(g.toString(),h);return this};