File: /var/www/studis.kauko.lt/wp-content/uploads/h5p/libraries/H5P.Video-1.6/scripts/video.js
/** @namespace H5P */
H5P.Video = (function ($, ContentCopyrights, MediaCopyright, handlers) {
/**
* The ultimate H5P video player!
*
* @class
* @param {Object} parameters Options for this library.
* @param {Object} parameters.visuals Visual options
* @param {Object} parameters.playback Playback options
* @param {Object} parameters.a11y Accessibility options
* @param {Boolean} [parameters.startAt] Start time of video
* @param {Number} id Content identifier
* @param {Object} [extras] Extra parameters.
*/
function Video(parameters, id, extras = {}) {
var self = this;
self.oldTime = extras.previousState?.time;
self.contentId = id;
self.WAS_RESET = false;
self.startAt = parameters.startAt || 0;
// Ref youtube.js - ipad & youtube - issue
self.pressToPlay = false;
// Reference to the handler
var handlerName = '';
// Initialize event inheritance
H5P.EventDispatcher.call(self);
// Default language localization
parameters = $.extend(true, parameters, {
l10n: {
name: 'Video',
loading: 'Video player loading...',
noPlayers: 'Found no video players that supports the given video format.',
noSources: 'Video source is missing.',
aborted: 'Media playback has been aborted.',
networkFailure: 'Network failure.',
cannotDecode: 'Unable to decode media.',
formatNotSupported: 'Video format not supported.',
mediaEncrypted: 'Media encrypted.',
unknownError: 'Unknown error.',
vimeoPasswordError: 'Password-protected Vimeo videos are not supported.',
vimeoPrivacyError: 'The Vimeo video cannot be used due to its privacy settings.',
vimeoLoadingError: 'The Vimeo video could not be loaded.',
invalidYtId: 'Invalid YouTube ID.',
unknownYtId: 'Unable to find video with the given YouTube ID.',
restrictedYt: 'The owner of this video does not allow it to be embedded.'
}
});
parameters.a11y = parameters.a11y || [];
parameters.playback = parameters.playback || {};
parameters.visuals = $.extend(
true, { disableFullscreen: false }, parameters.visuals
);
/** @private */
var sources = [];
if (parameters.sources) {
for (var i = 0; i < parameters.sources.length; i++) {
// Clone to avoid changing of parameters.
var source = $.extend(true, {}, parameters.sources[i]);
// Create working URL without html entities.
source.path = $cleaner.html(source.path).text();
sources.push(source);
}
}
/** @private */
var tracks = [];
parameters.a11y.forEach(function (track) {
// Clone to avoid changing of parameters.
var clone = $.extend(true, {}, track);
// Create working URL without html entities
if (clone.track && clone.track.path) {
clone.track.path = $cleaner.html(clone.track.path).text();
tracks.push(clone);
}
});
/**
* Handle autoplay. If autoplay is disabled, it will still autopause when
* video is not visible.
*
* @param {*} $container
*/
const handleAutoPlayPause = function ($container) {
// Keep the current state
let state;
self.on('stateChange', function(event) {
state = event.data;
});
// Keep record of autopauses.
// I.e: we don't wanna autoplay if the user has excplicitly paused.
self.autoPaused = !self.pressToPlay;
new IntersectionObserver(function (entries) {
const entry = entries[0];
// This video element became visible
if (entry.isIntersecting) {
// Autoplay if autoplay is enabled and it was not explicitly
// paused by a user
if (parameters.playback.autoplay && self.autoPaused) {
self.autoPaused = false;
self.play();
}
}
else if (state !== Video.PAUSED && state !== Video.ENDED) {
self.autoPaused = true;
self.pause();
}
}, {
root: null,
threshold: [0, 1] // Get events when it is shown and hidden
}).observe($container.get(0));
};
/**
* Attaches the video handler to the given container.
* Inserts text if no handler is found.
*
* @public
* @param {jQuery} $container
*/
self.attach = function ($container) {
$container.addClass('h5p-video').html('');
if (self.appendTo !== undefined) {
self.appendTo($container);
// Avoid autoplaying in authoring tool
if (window.H5PEditor === undefined) {
handleAutoPlayPause($container);
}
}
else if (sources.length) {
$container.text(parameters.l10n.noPlayers);
}
else {
$container.text(parameters.l10n.noSources);
}
};
/**
* Get name of the video handler
*
* @public
* @returns {string}
*/
self.getHandlerName = function() {
return handlerName;
};
/**
* @public
* Get current state for resume support.
*
* @returns {object} Current state.
*/
self.getCurrentState = function () {
if (self.getCurrentTime) {
return {
time: self.getCurrentTime() || self.oldTime,
};
}
};
/**
* The two functions below needs to be defined in this base class,
* since it is used in this class even if no handler was found.
*/
self.seek = () => {};
self.pause = () => {};
/**
* @public
* Reset current state (time).
*
*/
self.resetTask = function () {
delete self.oldTime;
self.resetPlayback(parameters.startAt || 0);
};
/**
* Default implementation of resetPlayback. May be overridden by sub classes.
*
* @param {*} startAt
*/
self.resetPlayback = startAt => {
self.seek(startAt);
self.pause();
self.WAS_RESET = true;
};
// Resize the video when we know its aspect ratio
self.on('loaded', function () {
self.trigger('resize');
// reset time if wasn't done immediately
if (self.WAS_RESET) {
self.seek(parameters.startAt || 0);
if (!parameters.playback.autoplay) {
self.pause();
}
self.WAS_RESET = false;
}
});
// Find player for video sources
if (sources.length) {
const options = {
controls: parameters.visuals.controls,
autoplay: parameters.playback.autoplay,
loop: parameters.playback.loop,
fit: parameters.visuals.fit,
poster: parameters.visuals.poster === undefined ? undefined : parameters.visuals.poster,
tracks: tracks,
disableRemotePlayback: parameters.visuals.disableRemotePlayback === true,
disableFullscreen: parameters.visuals.disableFullscreen === true,
deactivateSound: parameters.playback.deactivateSound,
}
if (!self.WAS_RESET) {
options.startAt = self.oldTime !== undefined ? self.oldTime : (parameters.startAt || 0);
}
var html5Handler;
for (var i = 0; i < handlers.length; i++) {
var handler = handlers[i];
if (handler.canPlay !== undefined && handler.canPlay(sources)) {
handler.call(self, sources, options, parameters.l10n);
handlerName = handler.name;
return;
}
if (handler === H5P.VideoHtml5) {
html5Handler = handler;
handlerName = handler.name;
}
}
// Fallback to trying HTML5 player
if (html5Handler) {
html5Handler.call(self, sources, options, parameters.l10n);
}
}
}
// Extends the event dispatcher
Video.prototype = Object.create(H5P.EventDispatcher.prototype);
Video.prototype.constructor = Video;
// Player states
/** @constant {Number} */
Video.ENDED = 0;
/** @constant {Number} */
Video.PLAYING = 1;
/** @constant {Number} */
Video.PAUSED = 2;
/** @constant {Number} */
Video.BUFFERING = 3;
/**
* When video is queued to start
* @constant {Number}
*/
Video.VIDEO_CUED = 5;
// Used to convert between html and text, since URLs have html entities.
var $cleaner = H5P.jQuery('<div/>');
/**
* Help keep track of key value pairs used by the UI.
*
* @class
* @param {string} label
* @param {string} value
*/
Video.LabelValue = function (label, value) {
this.label = label;
this.value = value;
};
/**
* Determine whether video can be autoplayed.
* @returns {Promise<boolean>} Whether autoplay is allowed.
*/
Video.isAutoplayAllowed = async () => {
if (document.featurePolicy?.allowsFeature('autoplay')) {
return true; // Browser supports `featurePolicy` and can tell directly
}
const video = document.createElement('video');
/*
* Without a video source, the play Promise will be rejected with an error
* if it cannot be autoplayed, but not resolve at all if it can be
* autoplayed. Using a timeout to detect the latter case here.
*/
const timeoutMs = 50; // If play promise rejects, then within few ms
const timeoutPromise = new Promise((resolve) => {
window.setTimeout(() => {
resolve(true); // Timeout reached, autoplay is allowed
}, timeoutMs);
});
let result;
try {
result = (await Promise.race([video.play(), timeoutPromise])) ?? true;
} catch (error) {
result = false;
}
return result;
};
/** @constant {Boolean} */
Video.IE11_PLAYBACK_RATE_FIX = (navigator.userAgent.match(/Trident.*rv[ :]*11\./) ? true : false);
return Video;
})(H5P.jQuery, H5P.ContentCopyrights, H5P.MediaCopyright, H5P.videoHandlers || []);