aboutsummaryrefslogtreecommitdiffstats
path: root/dist/plyr.js
diff options
context:
space:
mode:
Diffstat (limited to 'dist/plyr.js')
-rw-r--r--dist/plyr.js1077
1 files changed, 541 insertions, 536 deletions
diff --git a/dist/plyr.js b/dist/plyr.js
index 01e5e233..e8697c88 100644
--- a/dist/plyr.js
+++ b/dist/plyr.js
@@ -304,21 +304,6 @@ return loadjs;
}));
});
-// ==========================================================================
-// Plyr supported types and providers
-// ==========================================================================
-
-var providers = {
- html5: 'html5',
- youtube: 'youtube',
- vimeo: 'vimeo'
-};
-
-var types = {
- audio: 'audio',
- video: 'video'
-};
-
var classCallCheck = function (instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
@@ -408,6 +393,102 @@ var toConsumableArray = function (arr) {
// ==========================================================================
+var Storage = function () {
+ function Storage(player) {
+ classCallCheck(this, Storage);
+
+ this.enabled = player.config.storage.enabled;
+ this.key = player.config.storage.key;
+ }
+
+ // Check for actual support (see if we can use it)
+
+
+ createClass(Storage, [{
+ key: 'get',
+ value: function get$$1(key) {
+ if (!Storage.supported) {
+ return null;
+ }
+
+ var store = window.localStorage.getItem(this.key);
+
+ if (utils.is.empty(store)) {
+ return null;
+ }
+
+ var json = JSON.parse(store);
+
+ return utils.is.string(key) && key.length ? json[key] : json;
+ }
+ }, {
+ key: 'set',
+ value: function set$$1(object) {
+ // Bail if we don't have localStorage support or it's disabled
+ if (!Storage.supported || !this.enabled) {
+ return;
+ }
+
+ // Can only store objectst
+ if (!utils.is.object(object)) {
+ return;
+ }
+
+ // Get current storage
+ var storage = this.get();
+
+ // Default to empty object
+ if (utils.is.empty(storage)) {
+ storage = {};
+ }
+
+ // Update the working copy of the values
+ utils.extend(storage, object);
+
+ // Update storage
+ window.localStorage.setItem(this.key, JSON.stringify(storage));
+ }
+ }], [{
+ key: 'supported',
+ get: function get$$1() {
+ try {
+ if (!('localStorage' in window)) {
+ return false;
+ }
+
+ var test = '___test';
+
+ // Try to use it (it might be disabled, e.g. user is in private mode)
+ // see: https://github.com/sampotts/plyr/issues/131
+ window.localStorage.setItem(test, test);
+ window.localStorage.removeItem(test);
+
+ return true;
+ } catch (e) {
+ return false;
+ }
+ }
+ }]);
+ return Storage;
+}();
+
+// ==========================================================================
+// Plyr supported types and providers
+// ==========================================================================
+
+var providers = {
+ html5: 'html5',
+ youtube: 'youtube',
+ vimeo: 'vimeo'
+};
+
+var types = {
+ audio: 'audio',
+ video: 'video'
+};
+
+// ==========================================================================
+
var utils = {
// Check variable types
is: {
@@ -561,6 +642,8 @@ var utils = {
// Only load once if ID set
if (!hasId || !exists()) {
+ var useStorage = Storage.supported;
+
// Create container
var container = document.createElement('div');
utils.toggleHidden(container, true);
@@ -570,7 +653,7 @@ var utils = {
}
// Check in cache
- if (support.storage) {
+ if (useStorage) {
var cached = window.localStorage.getItem(prefix + id);
isCached = cached !== null;
@@ -587,7 +670,7 @@ var utils = {
return;
}
- if (support.storage) {
+ if (useStorage) {
window.localStorage.setItem(prefix + id, JSON.stringify({
content: result
}));
@@ -1475,6 +1558,7 @@ var support = {
}
});
window.addEventListener('test', null, options);
+ window.removeEventListener('test', null, options);
} catch (e) {
// Do nothing
}
@@ -1689,411 +1773,12 @@ var i18n = {
// Sniff out the browser
var browser = utils.getBrowser();
-var ui = {
- addStyleHook: function addStyleHook() {
- utils.toggleClass(this.elements.container, this.config.selectors.container.replace('.', ''), true);
- utils.toggleClass(this.elements.container, this.config.classNames.uiSupported, this.supported.ui);
- },
-
-
- // Toggle native HTML5 media controls
- toggleNativeControls: function toggleNativeControls() {
- var toggle = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
-
- if (toggle && this.isHTML5) {
- this.media.setAttribute('controls', '');
- } else {
- this.media.removeAttribute('controls');
- }
- },
-
-
- // Setup the UI
- build: function build() {
- var _this = this;
-
- // Re-attach media element listeners
- // TODO: Use event bubbling?
- this.listeners.media();
-
- // Don't setup interface if no support
- if (!this.supported.ui) {
- this.debug.warn('Basic support only for ' + this.provider + ' ' + this.type);
-
- // Restore native controls
- ui.toggleNativeControls.call(this, true);
-
- // Bail
- return;
- }
-
- // Inject custom controls if not present
- if (!utils.is.element(this.elements.controls)) {
- // Inject custom controls
- controls.inject.call(this);
-
- // Re-attach control listeners
- this.listeners.controls();
- }
-
- // Remove native controls
- ui.toggleNativeControls.call(this);
-
- // Captions
- captions.setup.call(this);
-
- // Reset volume
- this.volume = null;
-
- // Reset mute state
- this.muted = null;
-
- // Reset speed
- this.speed = null;
-
- // Reset loop state
- this.loop = null;
-
- // Reset quality setting
- this.quality = null;
-
- // Reset volume display
- ui.updateVolume.call(this);
-
- // Reset time display
- ui.timeUpdate.call(this);
-
- // Update the UI
- ui.checkPlaying.call(this);
-
- // Check for picture-in-picture support
- utils.toggleClass(this.elements.container, this.config.classNames.pip.supported, support.pip && this.isHTML5 && this.isVideo);
-
- // Check for airplay support
- utils.toggleClass(this.elements.container, this.config.classNames.airplay.supported, support.airplay && this.isHTML5);
-
- // Add iOS class
- utils.toggleClass(this.elements.container, this.config.classNames.isIos, browser.isIos);
-
- // Add touch class
- utils.toggleClass(this.elements.container, this.config.classNames.isTouch, this.touch);
-
- // Ready for API calls
- this.ready = true;
-
- // Ready event at end of execution stack
- setTimeout(function () {
- utils.dispatchEvent.call(_this, _this.media, 'ready');
- }, 0);
-
- // Set the title
- ui.setTitle.call(this);
-
- // Set the poster image
- ui.setPoster.call(this);
- },
-
-
- // Setup aria attribute for play and iframe title
- setTitle: function setTitle() {
- // Find the current text
- var label = i18n.get('play', this.config);
-
- // If there's a media title set, use that for the label
- if (utils.is.string(this.config.title) && !utils.is.empty(this.config.title)) {
- label += ', ' + this.config.title;
-
- // Set container label
- this.elements.container.setAttribute('aria-label', this.config.title);
- }
-
- // If there's a play button, set label
- if (utils.is.nodeList(this.elements.buttons.play)) {
- Array.from(this.elements.buttons.play).forEach(function (button) {
- button.setAttribute('aria-label', label);
- });
- }
-
- // Set iframe title
- // https://github.com/sampotts/plyr/issues/124
- if (this.isEmbed) {
- var iframe = utils.getElement.call(this, 'iframe');
-
- if (!utils.is.element(iframe)) {
- return;
- }
-
- // Default to media type
- var title = !utils.is.empty(this.config.title) ? this.config.title : 'video';
- var format = i18n.get('frameTitle', this.config);
-
- iframe.setAttribute('title', format.replace('{title}', title));
- }
- },
-
-
- // Set the poster image
- setPoster: function setPoster() {
- if (!utils.is.element(this.elements.poster) || utils.is.empty(this.poster)) {
- return;
- }
-
- // Set the inline style
- var posters = this.poster.split(',');
- this.elements.poster.style.backgroundImage = posters.map(function (p) {
- return 'url(\'' + p + '\')';
- }).join(',');
- },
-
-
- // Check playing state
- checkPlaying: function checkPlaying(event) {
- // Class hooks
- utils.toggleClass(this.elements.container, this.config.classNames.playing, this.playing);
- utils.toggleClass(this.elements.container, this.config.classNames.paused, this.paused);
- utils.toggleClass(this.elements.container, this.config.classNames.stopped, this.stopped);
-
- // Set ARIA state
- utils.toggleState(this.elements.buttons.play, this.playing);
-
- // Only update controls on non timeupdate events
- if (utils.is.event(event) && event.type === 'timeupdate') {
- return;
- }
-
- // Toggle controls
- this.toggleControls(!this.playing);
- },
-
-
- // Check if media is loading
- checkLoading: function checkLoading(event) {
- var _this2 = this;
-
- this.loading = ['stalled', 'waiting'].includes(event.type);
-
- // Clear timer
- clearTimeout(this.timers.loading);
-
- // Timer to prevent flicker when seeking
- this.timers.loading = setTimeout(function () {
- // Toggle container class hook
- utils.toggleClass(_this2.elements.container, _this2.config.classNames.loading, _this2.loading);
-
- // Show controls if loading, hide if done
- _this2.toggleControls(_this2.loading);
- }, this.loading ? 250 : 0);
- },
-
-
- // Check if media failed to load
- checkFailed: function checkFailed() {
- var _this3 = this;
-
- // https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/networkState
- this.failed = this.media.networkState === 3;
-
- if (this.failed) {
- utils.toggleClass(this.elements.container, this.config.classNames.loading, false);
- utils.toggleClass(this.elements.container, this.config.classNames.error, true);
- }
-
- // Clear timer
- clearTimeout(this.timers.failed);
-
- // Timer to prevent flicker when seeking
- this.timers.loading = setTimeout(function () {
- // Toggle container class hook
- utils.toggleClass(_this3.elements.container, _this3.config.classNames.loading, _this3.loading);
-
- // Show controls if loading, hide if done
- _this3.toggleControls(_this3.loading);
- }, this.loading ? 250 : 0);
- },
-
-
- // Update volume UI and storage
- updateVolume: function updateVolume() {
- if (!this.supported.ui) {
- return;
- }
-
- // Update range
- if (utils.is.element(this.elements.inputs.volume)) {
- ui.setRange.call(this, this.elements.inputs.volume, this.muted ? 0 : this.volume);
- }
-
- // Update mute state
- if (utils.is.element(this.elements.buttons.mute)) {
- utils.toggleState(this.elements.buttons.mute, this.muted || this.volume === 0);
- }
- },
-
-
- // Update seek value and lower fill
- setRange: function setRange(target) {
- var value = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
-
- if (!utils.is.element(target)) {
- return;
- }
-
- // eslint-disable-next-line
- target.value = value;
-
- // Webkit range fill
- controls.updateRangeFill.call(this, target);
- },
-
-
- // Set <progress> value
- setProgress: function setProgress(target, input) {
- var value = utils.is.number(input) ? input : 0;
- var progress = utils.is.element(target) ? target : this.elements.display.buffer;
-
- // Update value and label
- if (utils.is.element(progress)) {
- progress.value = value;
-
- // Update text label inside
- var label = progress.getElementsByTagName('span')[0];
- if (utils.is.element(label)) {
- label.childNodes[0].nodeValue = value;
- }
- }
- },
-
-
- // Update <progress> elements
- updateProgress: function updateProgress(event) {
- if (!this.supported.ui || !utils.is.event(event)) {
- return;
- }
-
- var value = 0;
-
- if (event) {
- switch (event.type) {
- // Video playing
- case 'timeupdate':
- case 'seeking':
- value = utils.getPercentage(this.currentTime, this.duration);
-
- // Set seek range value only if it's a 'natural' time event
- if (event.type === 'timeupdate') {
- ui.setRange.call(this, this.elements.inputs.seek, value);
- }
-
- break;
-
- // Check buffer status
- case 'playing':
- case 'progress':
- ui.setProgress.call(this, this.elements.display.buffer, this.buffered * 100);
-
- break;
-
- default:
- break;
- }
- }
- },
-
-
- // Update the displayed time
- updateTimeDisplay: function updateTimeDisplay() {
- var target = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
- var time = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
- var inverted = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
-
- // Bail if there's no element to display or the value isn't a number
- if (!utils.is.element(target) || !utils.is.number(time)) {
- return;
- }
-
- // Always display hours if duration is over an hour
- var forceHours = utils.getHours(this.duration) > 0;
-
- // eslint-disable-next-line no-param-reassign
- target.textContent = utils.formatTime(time, forceHours, inverted);
- },
-
-
- // Handle time change event
- timeUpdate: function timeUpdate(event) {
- // Only invert if only one time element is displayed and used for both duration and currentTime
- var invert = !utils.is.element(this.elements.display.duration) && this.config.invertTime;
-
- // Duration
- ui.updateTimeDisplay.call(this, this.elements.display.currentTime, invert ? this.duration - this.currentTime : this.currentTime, invert);
-
- // Ignore updates while seeking
- if (event && event.type === 'timeupdate' && this.media.seeking) {
- return;
- }
-
- // Playing progress
- ui.updateProgress.call(this, event);
- },
-
-
- // Show the duration on metadataloaded
- durationUpdate: function durationUpdate() {
- if (!this.supported.ui) {
- return;
- }
-
- // If there's a spot to display duration
- var hasDuration = utils.is.element(this.elements.display.duration);
-
- // If there's only one time display, display duration there
- if (!hasDuration && this.config.displayDuration && this.paused) {
- ui.updateTimeDisplay.call(this, this.elements.display.currentTime, this.duration);
- }
-
- // If there's a duration element, update content
- if (hasDuration) {
- ui.updateTimeDisplay.call(this, this.elements.display.duration, this.duration);
- }
-
- // Update the tooltip (if visible)
- controls.updateSeekTooltip.call(this);
- }
-};
-
-// ==========================================================================
-
-// Sniff out the browser
-var browser$1 = utils.getBrowser();
-
var controls = {
- // Webkit polyfill for lower fill range
- updateRangeFill: function updateRangeFill(target) {
- // Get range from event if event passed
- var range = utils.is.event(target) ? target.target : target;
-
- // Needs to be a valid <input type='range'>
- if (!utils.is.element(range) || range.getAttribute('type') !== 'range') {
- return;
- }
-
- // Set aria value for https://github.com/sampotts/plyr/issues/905
- range.setAttribute('aria-valuenow', range.value);
-
- // WebKit only
- if (!browser$1.isWebkit) {
- return;
- }
-
- // Set CSS custom property
- range.style.setProperty('--value', range.value / range.max * 100 + '%');
- },
-
// Get icon URL
getIconUrl: function getIconUrl() {
var url = new URL(this.config.iconUrl, window.location);
- var cors = url.host !== window.location.host || browser$1.isIE && !window.svg4everybody;
+ var cors = url.host !== window.location.host || browser.isIE && !window.svg4everybody;
return {
url: this.config.iconUrl,
@@ -2467,9 +2152,139 @@ var controls = {
},
+ // Update the displayed time
+ updateTimeDisplay: function updateTimeDisplay() {
+ var target = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
+ var time = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
+ var inverted = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
+
+ // Bail if there's no element to display or the value isn't a number
+ if (!utils.is.element(target) || !utils.is.number(time)) {
+ return;
+ }
+
+ // Always display hours if duration is over an hour
+ var forceHours = utils.getHours(this.duration) > 0;
+
+ // eslint-disable-next-line no-param-reassign
+ target.textContent = utils.formatTime(time, forceHours, inverted);
+ },
+
+
+ // Update volume UI and storage
+ updateVolume: function updateVolume() {
+ if (!this.supported.ui) {
+ return;
+ }
+
+ // Update range
+ if (utils.is.element(this.elements.inputs.volume)) {
+ controls.setRange.call(this, this.elements.inputs.volume, this.muted ? 0 : this.volume);
+ }
+
+ // Update mute state
+ if (utils.is.element(this.elements.buttons.mute)) {
+ utils.toggleState(this.elements.buttons.mute, this.muted || this.volume === 0);
+ }
+ },
+
+
+ // Update seek value and lower fill
+ setRange: function setRange(target) {
+ var value = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
+
+ if (!utils.is.element(target)) {
+ return;
+ }
+
+ // eslint-disable-next-line
+ target.value = value;
+
+ // Webkit range fill
+ controls.updateRangeFill.call(this, target);
+ },
+
+
+ // Update <progress> elements
+ updateProgress: function updateProgress(event) {
+ var _this = this;
+
+ if (!this.supported.ui || !utils.is.event(event)) {
+ return;
+ }
+
+ var value = 0;
+
+ var setProgress = function setProgress(target, input) {
+ var value = utils.is.number(input) ? input : 0;
+ var progress = utils.is.element(target) ? target : _this.elements.display.buffer;
+
+ // Update value and label
+ if (utils.is.element(progress)) {
+ progress.value = value;
+
+ // Update text label inside
+ var label = progress.getElementsByTagName('span')[0];
+ if (utils.is.element(label)) {
+ label.childNodes[0].nodeValue = value;
+ }
+ }
+ };
+
+ if (event) {
+ switch (event.type) {
+ // Video playing
+ case 'timeupdate':
+ case 'seeking':
+ value = utils.getPercentage(this.currentTime, this.duration);
+
+ // Set seek range value only if it's a 'natural' time event
+ if (event.type === 'timeupdate') {
+ controls.setRange.call(this, this.elements.inputs.seek, value);
+ }
+
+ break;
+
+ // Check buffer status
+ case 'playing':
+ case 'progress':
+ setProgress(this.elements.display.buffer, this.buffered * 100);
+
+ break;
+
+ default:
+ break;
+ }
+ }
+ },
+
+
+ // Webkit polyfill for lower fill range
+ updateRangeFill: function updateRangeFill(target) {
+ // Get range from event if event passed
+ var range = utils.is.event(target) ? target.target : target;
+
+ // Needs to be a valid <input type='range'>
+ if (!utils.is.element(range) || range.getAttribute('type') !== 'range') {
+ return;
+ }
+
+ // Set aria value for https://github.com/sampotts/plyr/issues/905
+ range.setAttribute('aria-valuenow', range.value);
+
+ // WebKit only
+ if (!browser.isWebkit) {
+ return;
+ }
+
+ // Set CSS custom property
+ range.style.setProperty('--value', range.value / range.max * 100 + '%');
+ },
+
+
// Update hover tooltip for seeking
updateSeekTooltip: function updateSeekTooltip(event) {
- var _this = this;
+ var _this2 = this;
// Bail if setting not true
if (!this.config.tooltips.seek || !utils.is.element(this.elements.inputs.seek) || !utils.is.element(this.elements.display.seekTooltip) || this.duration === 0) {
@@ -2482,7 +2297,7 @@ var controls = {
var visible = this.config.classNames.tooltip + '--visible';
var toggle = function toggle(_toggle) {
- utils.toggleClass(_this.elements.display.seekTooltip, visible, _toggle);
+ utils.toggleClass(_this2.elements.display.seekTooltip, visible, _toggle);
};
// Hide on touch
@@ -2508,7 +2323,7 @@ var controls = {
}
// Display the time a click would seek to
- ui.updateTimeDisplay.call(this, this.elements.display.seekTooltip, this.duration / 100 * percent);
+ controls.updateTimeDisplay.call(this, this.elements.display.seekTooltip, this.duration / 100 * percent);
// Set position
this.elements.display.seekTooltip.style.left = percent + '%';
@@ -2521,6 +2336,48 @@ var controls = {
},
+ // Handle time change event
+ timeUpdate: function timeUpdate(event) {
+ // Only invert if only one time element is displayed and used for both duration and currentTime
+ var invert = !utils.is.element(this.elements.display.duration) && this.config.invertTime;
+
+ // Duration
+ controls.updateTimeDisplay.call(this, this.elements.display.currentTime, invert ? this.duration - this.currentTime : this.currentTime, invert);
+
+ // Ignore updates while seeking
+ if (event && event.type === 'timeupdate' && this.media.seeking) {
+ return;
+ }
+
+ // Playing progress
+ controls.updateProgress.call(this, event);
+ },
+
+
+ // Show the duration on metadataloaded
+ durationUpdate: function durationUpdate() {
+ if (!this.supported.ui) {
+ return;
+ }
+
+ // If there's a spot to display duration
+ var hasDuration = utils.is.element(this.elements.display.duration);
+
+ // If there's only one time display, display duration there
+ if (!hasDuration && this.config.displayDuration && this.paused) {
+ controls.updateTimeDisplay.call(this, this.elements.display.currentTime, this.duration);
+ }
+
+ // If there's a duration element, update content
+ if (hasDuration) {
+ controls.updateTimeDisplay.call(this, this.elements.display.duration, this.duration);
+ }
+
+ // Update the tooltip (if visible)
+ controls.updateSeekTooltip.call(this);
+ },
+
+
// Hide/show a tab
toggleTab: function toggleTab(setting, toggle) {
utils.toggleHidden(this.elements.settings.tabs[setting], !toggle);
@@ -2530,7 +2387,7 @@ var controls = {
// Set the quality menu
// TODO: Vimeo support
setQualityMenu: function setQualityMenu(options) {
- var _this2 = this;
+ var _this3 = this;
// Menu required
if (!utils.is.element(this.elements.settings.panes.quality)) {
@@ -2543,7 +2400,7 @@ var controls = {
// Set options if passed and filter based on config
if (utils.is.array(options)) {
this.options.quality = options.filter(function (quality) {
- return _this2.config.quality.options.includes(quality);
+ return _this3.config.quality.options.includes(quality);
});
}
@@ -2590,16 +2447,16 @@ var controls = {
return null;
}
- return controls.createBadge.call(_this2, label);
+ return controls.createBadge.call(_this3, label);
};
// Sort options by the config and then render options
this.options.quality.sort(function (a, b) {
- var sorting = _this2.config.quality.options;
+ var sorting = _this3.config.quality.options;
return sorting.indexOf(a) > sorting.indexOf(b) ? 1 : -1;
}).forEach(function (quality) {
- var label = controls.getLabel.call(_this2, 'quality', quality);
- controls.createMenuItem.call(_this2, quality, list, type, label, getBadge(quality));
+ var label = controls.getLabel.call(_this3, 'quality', quality);
+ controls.createMenuItem.call(_this3, quality, list, type, label, getBadge(quality));
});
controls.updateSetting.call(this, type, list);
@@ -2738,7 +2595,7 @@ var controls = {
// Set a list of available captions languages
setCaptionsMenu: function setCaptionsMenu() {
- var _this3 = this;
+ var _this4 = this;
// TODO: Captions or language? Currently it's mixed
var type = 'captions';
@@ -2763,7 +2620,7 @@ var controls = {
var tracks = captions.getTracks.call(this).map(function (track) {
return {
language: !utils.is.empty(track.language) ? track.language : 'enabled',
- label: captions.getLabel.call(_this3, track)
+ label: captions.getLabel.call(_this4, track)
};
});
@@ -2775,7 +2632,7 @@ var controls = {
// Generate options
tracks.forEach(function (track) {
- controls.createMenuItem.call(_this3, track.language, list, 'language', track.label, track.language !== 'enabled' ? controls.createBadge.call(_this3, track.language.toUpperCase()) : null, track.language.toLowerCase() === _this3.captions.language.toLowerCase());
+ controls.createMenuItem.call(_this4, track.language, list, 'language', track.label, track.language !== 'enabled' ? controls.createBadge.call(_this4, track.language.toUpperCase()) : null, track.language.toLowerCase() === _this4.captions.language.toLowerCase());
});
// Store reference
@@ -2789,7 +2646,7 @@ var controls = {
// Set a list of available captions languages
setSpeedMenu: function setSpeedMenu(options) {
- var _this4 = this;
+ var _this5 = this;
// Do nothing if not selected
if (!this.config.controls.includes('settings') || !this.config.settings.includes('speed')) {
@@ -2812,7 +2669,7 @@ var controls = {
// Set options if passed and filter based on config
this.options.speed = this.options.speed.filter(function (speed) {
- return _this4.config.speed.options.includes(speed);
+ return _this5.config.speed.options.includes(speed);
});
// Toggle the pane and tab
@@ -2835,8 +2692,8 @@ var controls = {
// Create items
this.options.speed.forEach(function (speed) {
- var label = controls.getLabel.call(_this4, 'speed', speed);
- controls.createMenuItem.call(_this4, speed, list, type, label);
+ var label = controls.getLabel.call(_this5, 'speed', speed);
+ controls.createMenuItem.call(_this5, speed, list, type, label);
});
controls.updateSetting.call(this, type, list);
@@ -3014,7 +2871,7 @@ var controls = {
// Build the default HTML
// TODO: Set order based on order in the config.controls array?
create: function create(data) {
- var _this5 = this;
+ var _this6 = this;
// Do nothing if we want no controls
if (utils.is.empty(this.config.controls)) {
@@ -3163,17 +3020,17 @@ var controls = {
hidden: ''
});
- var button = utils.createElement('button', utils.extend(utils.getAttributesFromSelector(_this5.config.selectors.buttons.settings), {
+ var button = utils.createElement('button', utils.extend(utils.getAttributesFromSelector(_this6.config.selectors.buttons.settings), {
type: 'button',
- class: _this5.config.classNames.control + ' ' + _this5.config.classNames.control + '--forward',
+ class: _this6.config.classNames.control + ' ' + _this6.config.classNames.control + '--forward',
id: 'plyr-settings-' + data.id + '-' + type + '-tab',
'aria-haspopup': true,
'aria-controls': 'plyr-settings-' + data.id + '-' + type,
'aria-expanded': false
- }), i18n.get(type, _this5.config));
+ }), i18n.get(type, _this6.config));
var value = utils.createElement('span', {
- class: _this5.config.classNames.menu.value
+ class: _this6.config.classNames.menu.value
});
// Speed contains HTML entities
@@ -3183,7 +3040,7 @@ var controls = {
tab.appendChild(button);
tabs.appendChild(tab);
- _this5.elements.settings.tabs[type] = tab;
+ _this6.elements.settings.tabs[type] = tab;
});
home.appendChild(tabs);
@@ -3201,11 +3058,11 @@ var controls = {
var back = utils.createElement('button', {
type: 'button',
- class: _this5.config.classNames.control + ' ' + _this5.config.classNames.control + '--back',
+ class: _this6.config.classNames.control + ' ' + _this6.config.classNames.control + '--back',
'aria-haspopup': true,
'aria-controls': 'plyr-settings-' + data.id + '-home',
'aria-expanded': false
- }, i18n.get(type, _this5.config));
+ }, i18n.get(type, _this6.config));
pane.appendChild(back);
@@ -3214,7 +3071,7 @@ var controls = {
pane.appendChild(options);
inner.appendChild(pane);
- _this5.elements.settings.panes[type] = pane;
+ _this6.elements.settings.panes[type] = pane;
});
form.appendChild(inner);
@@ -3259,7 +3116,7 @@ var controls = {
// Insert controls
inject: function inject() {
- var _this6 = this;
+ var _this7 = this;
// Sprite
if (this.config.loadSprite) {
@@ -3366,8 +3223,8 @@ var controls = {
var labels = utils.getElements.call(this, [this.config.selectors.controls.wrapper, ' ', this.config.selectors.labels, ' .', this.config.classNames.hidden].join(''));
Array.from(labels).forEach(function (label) {
- utils.toggleClass(label, _this6.config.classNames.hidden, false);
- utils.toggleClass(label, _this6.config.classNames.tooltip, true);
+ utils.toggleClass(label, _this7.config.classNames.hidden, false);
+ utils.toggleClass(label, _this7.config.classNames.tooltip, true);
label.setAttribute('role', 'tooltip');
});
}
@@ -4012,7 +3869,7 @@ var defaults$1 = {
// ==========================================================================
-var browser$2 = utils.getBrowser();
+var browser$1 = utils.getBrowser();
function onChange() {
if (!this.enabled) {
@@ -4029,7 +3886,7 @@ function onChange() {
utils.dispatchEvent.call(this.player, this.target, this.active ? 'enterfullscreen' : 'exitfullscreen', true);
// Trap focus in container
- if (!browser$2.isIos) {
+ if (!browser$1.isIos) {
utils.trapFocus.call(this.player, this.target, this.active);
}
}
@@ -4123,7 +3980,7 @@ var Fullscreen = function () {
}
// iOS native fullscreen doesn't need the request step
- if (browser$2.isIos && this.player.config.fullscreen.iosNative) {
+ if (browser$1.isIos && this.player.config.fullscreen.iosNative) {
if (this.player.playing) {
this.target.webkitEnterFullscreen();
}
@@ -4146,7 +4003,7 @@ var Fullscreen = function () {
}
// iOS native fullscreen
- if (browser$2.isIos && this.player.config.fullscreen.iosNative) {
+ if (browser$1.isIos && this.player.config.fullscreen.iosNative) {
this.target.webkitExitFullscreen();
this.player.play();
} else if (!Fullscreen.native) {
@@ -4203,7 +4060,7 @@ var Fullscreen = function () {
}, {
key: 'target',
get: function get$$1() {
- return browser$2.isIos && this.player.config.fullscreen.iosNative ? this.player.media : this.player.elements.container;
+ return browser$1.isIos && this.player.config.fullscreen.iosNative ? this.player.media : this.player.elements.container;
}
}], [{
key: 'native',
@@ -4248,6 +4105,234 @@ var Fullscreen = function () {
// ==========================================================================
// Sniff out the browser
+var browser$2 = utils.getBrowser();
+
+var ui = {
+ addStyleHook: function addStyleHook() {
+ utils.toggleClass(this.elements.container, this.config.selectors.container.replace('.', ''), true);
+ utils.toggleClass(this.elements.container, this.config.classNames.uiSupported, this.supported.ui);
+ },
+
+
+ // Toggle native HTML5 media controls
+ toggleNativeControls: function toggleNativeControls() {
+ var toggle = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
+
+ if (toggle && this.isHTML5) {
+ this.media.setAttribute('controls', '');
+ } else {
+ this.media.removeAttribute('controls');
+ }
+ },
+
+
+ // Setup the UI
+ build: function build() {
+ var _this = this;
+
+ // Re-attach media element listeners
+ // TODO: Use event bubbling?
+ this.listeners.media();
+
+ // Don't setup interface if no support
+ if (!this.supported.ui) {
+ this.debug.warn('Basic support only for ' + this.provider + ' ' + this.type);
+
+ // Restore native controls
+ ui.toggleNativeControls.call(this, true);
+
+ // Bail
+ return;
+ }
+
+ // Inject custom controls if not present
+ if (!utils.is.element(this.elements.controls)) {
+ // Inject custom controls
+ controls.inject.call(this);
+
+ // Re-attach control listeners
+ this.listeners.controls();
+ }
+
+ // Remove native controls
+ ui.toggleNativeControls.call(this);
+
+ // Captions
+ captions.setup.call(this);
+
+ // Reset volume
+ this.volume = null;
+
+ // Reset mute state
+ this.muted = null;
+
+ // Reset speed
+ this.speed = null;
+
+ // Reset loop state
+ this.loop = null;
+
+ // Reset quality setting
+ this.quality = null;
+
+ // Reset volume display
+ controls.updateVolume.call(this);
+
+ // Reset time display
+ controls.timeUpdate.call(this);
+
+ // Update the UI
+ ui.checkPlaying.call(this);
+
+ // Check for picture-in-picture support
+ utils.toggleClass(this.elements.container, this.config.classNames.pip.supported, support.pip && this.isHTML5 && this.isVideo);
+
+ // Check for airplay support
+ utils.toggleClass(this.elements.container, this.config.classNames.airplay.supported, support.airplay && this.isHTML5);
+
+ // Add iOS class
+ utils.toggleClass(this.elements.container, this.config.classNames.isIos, browser$2.isIos);
+
+ // Add touch class
+ utils.toggleClass(this.elements.container, this.config.classNames.isTouch, this.touch);
+
+ // Ready for API calls
+ this.ready = true;
+
+ // Ready event at end of execution stack
+ setTimeout(function () {
+ utils.dispatchEvent.call(_this, _this.media, 'ready');
+ }, 0);
+
+ // Set the title
+ ui.setTitle.call(this);
+
+ // Set the poster image
+ ui.setPoster.call(this);
+ },
+
+
+ // Setup aria attribute for play and iframe title
+ setTitle: function setTitle() {
+ // Find the current text
+ var label = i18n.get('play', this.config);
+
+ // If there's a media title set, use that for the label
+ if (utils.is.string(this.config.title) && !utils.is.empty(this.config.title)) {
+ label += ', ' + this.config.title;
+
+ // Set container label
+ this.elements.container.setAttribute('aria-label', this.config.title);
+ }
+
+ // If there's a play button, set label
+ if (utils.is.nodeList(this.elements.buttons.play)) {
+ Array.from(this.elements.buttons.play).forEach(function (button) {
+ button.setAttribute('aria-label', label);
+ });
+ }
+
+ // Set iframe title
+ // https://github.com/sampotts/plyr/issues/124
+ if (this.isEmbed) {
+ var iframe = utils.getElement.call(this, 'iframe');
+
+ if (!utils.is.element(iframe)) {
+ return;
+ }
+
+ // Default to media type
+ var title = !utils.is.empty(this.config.title) ? this.config.title : 'video';
+ var format = i18n.get('frameTitle', this.config);
+
+ iframe.setAttribute('title', format.replace('{title}', title));
+ }
+ },
+
+
+ // Set the poster image
+ setPoster: function setPoster() {
+ if (!utils.is.element(this.elements.poster) || utils.is.empty(this.poster)) {
+ return;
+ }
+
+ // Set the inline style
+ var posters = this.poster.split(',');
+ this.elements.poster.style.backgroundImage = posters.map(function (p) {
+ return 'url(\'' + p + '\')';
+ }).join(',');
+ },
+
+
+ // Check playing state
+ checkPlaying: function checkPlaying(event) {
+ // Class hooks
+ utils.toggleClass(this.elements.container, this.config.classNames.playing, this.playing);
+ utils.toggleClass(this.elements.container, this.config.classNames.paused, this.paused);
+ utils.toggleClass(this.elements.container, this.config.classNames.stopped, this.stopped);
+
+ // Set ARIA state
+ utils.toggleState(this.elements.buttons.play, this.playing);
+
+ // Only update controls on non timeupdate events
+ if (utils.is.event(event) && event.type === 'timeupdate') {
+ return;
+ }
+
+ // Toggle controls
+ this.toggleControls(!this.playing);
+ },
+
+
+ // Check if media is loading
+ checkLoading: function checkLoading(event) {
+ var _this2 = this;
+
+ this.loading = ['stalled', 'waiting'].includes(event.type);
+
+ // Clear timer
+ clearTimeout(this.timers.loading);
+
+ // Timer to prevent flicker when seeking
+ this.timers.loading = setTimeout(function () {
+ // Toggle container class hook
+ utils.toggleClass(_this2.elements.container, _this2.config.classNames.loading, _this2.loading);
+
+ // Show controls if loading, hide if done
+ _this2.toggleControls(_this2.loading);
+ }, this.loading ? 250 : 0);
+ },
+
+
+ // Check if media failed to load
+ checkFailed: function checkFailed() {
+ var _this3 = this;
+
+ // https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/networkState
+ this.failed = this.media.networkState === 3;
+
+ if (this.failed) {
+ utils.toggleClass(this.elements.container, this.config.classNames.loading, false);
+ utils.toggleClass(this.elements.container, this.config.classNames.error, true);
+ }
+
+ // Clear timer
+ clearTimeout(this.timers.failed);
+
+ // Timer to prevent flicker when seeking
+ this.timers.loading = setTimeout(function () {
+ // Toggle container class hook
+ utils.toggleClass(_this3.elements.container, _this3.config.classNames.loading, _this3.loading);
+
+ // Show controls if loading, hide if done
+ _this3.toggleControls(_this3.loading);
+ }, this.loading ? 250 : 0);
+ }
+};
+
+// ==========================================================================
+
+// Sniff out the browser
var browser$3 = utils.getBrowser();
var Listeners = function () {
@@ -4497,12 +4582,12 @@ var Listeners = function () {
// Time change on media
utils.on(this.player.media, 'timeupdate seeking', function (event) {
- return ui.timeUpdate.call(_this3.player, event);
+ return controls.timeUpdate.call(_this3.player, event);
});
// Display duration
utils.on(this.player.media, 'durationchange loadeddata loadedmetadata', function (event) {
- return ui.durationUpdate.call(_this3.player, event);
+ return controls.durationUpdate.call(_this3.player, event);
});
// Check for audio tracks on load
@@ -4523,12 +4608,12 @@ var Listeners = function () {
// Check for buffer progress
utils.on(this.player.media, 'progress playing', function (event) {
- return ui.updateProgress.call(_this3.player, event);
+ return controls.updateProgress.call(_this3.player, event);
});
// Handle volume changes
utils.on(this.player.media, 'volumechange', function (event) {
- return ui.updateVolume.call(_this3.player, event);
+ return controls.updateVolume.call(_this3.player, event);
});
// Handle play/pause
@@ -4780,7 +4865,8 @@ var Listeners = function () {
}
_this4.player.config.invertTime = !_this4.player.config.invertTime;
- ui.timeUpdate.call(_this4.player);
+
+ controls.timeUpdate.call(_this4.player);
});
}
@@ -6617,87 +6703,6 @@ var source = {
// ==========================================================================
-var Storage = function () {
- function Storage(player) {
- classCallCheck(this, Storage);
-
- this.enabled = player.config.storage.enabled;
- this.key = player.config.storage.key;
- }
-
- // Check for actual support (see if we can use it)
-
-
- createClass(Storage, [{
- key: 'get',
- value: function get$$1(key) {
- if (!Storage.supported) {
- return null;
- }
-
- var store = window.localStorage.getItem(this.key);
-
- if (utils.is.empty(store)) {
- return null;
- }
-
- var json = JSON.parse(store);
-
- return utils.is.string(key) && key.length ? json[key] : json;
- }
- }, {
- key: 'set',
- value: function set$$1(object) {
- // Bail if we don't have localStorage support or it's disabled
- if (!Storage.supported || !this.enabled) {
- return;
- }
-
- // Can only store objectst
- if (!utils.is.object(object)) {
- return;
- }
-
- // Get current storage
- var storage = this.get();
-
- // Default to empty object
- if (utils.is.empty(storage)) {
- storage = {};
- }
-
- // Update the working copy of the values
- utils.extend(storage, object);
-
- // Update storage
- window.localStorage.setItem(this.key, JSON.stringify(storage));
- }
- }], [{
- key: 'supported',
- get: function get$$1() {
- try {
- if (!('localStorage' in window)) {
- return false;
- }
-
- var test = '___test';
-
- // Try to use it (it might be disabled, e.g. user is in private mode)
- // see: https://github.com/sampotts/plyr/issues/131
- window.localStorage.setItem(test, test);
- window.localStorage.removeItem(test);
-
- return true;
- } catch (e) {
- return false;
- }
- }
- }]);
- return Storage;
-}();
-
-// ==========================================================================
-
// Private properties
// TODO: Use a WeakMap for private globals
// const globals = new WeakMap();