// ========================================================================== // Plyr controls // TODO: This needs to be split into smaller files and cleaned up // ========================================================================== import captions from './captions'; import html5 from './html5'; import i18n from './i18n'; import support from './support'; import { repaint, transitionEndEvent } from './utils/animation'; import { dedupe } from './utils/arrays'; import browser from './utils/browser'; import { createElement, emptyElement, getAttributesFromSelector, getElement, getElements, hasClass, matches, removeElement, setAttributes, setFocus, toggleClass, toggleHidden } from './utils/elements'; import { off, on } from './utils/events'; import is from './utils/is'; import loadSprite from './utils/loadSprite'; import { extend } from './utils/objects'; import { getPercentage, replaceAll, toCamelCase, toTitleCase } from './utils/strings'; import { formatTime, getHours } from './utils/time'; // TODO: Don't export a massive object - break down and create class const controls = { // Get icon URL getIconUrl() { const url = new URL(this.config.iconUrl, window.location); const cors = url.host !== window.location.host || (browser.isIE && !window.svg4everybody); return { url: this.config.iconUrl, cors, }; }, // Find the UI controls findElements() { try { this.elements.controls = getElement.call( this, this.config.selectors.controls.wrapper, ); // Buttons this.elements.buttons = { play: getElements.call( this, this.config.selectors.buttons.play, ), pause: getElement.call( this, this.config.selectors.buttons.pause, ), restart: getElement.call( this, this.config.selectors.buttons.restart, ), rewind: getElement.call( this, this.config.selectors.buttons.rewind, ), fastForward: getElement.call( this, this.config.selectors.buttons.fastForward, ), mute: getElement.call(this, this.config.selectors.buttons.mute), pip: getElement.call(this, this.config.selectors.buttons.pip), airplay: getElement.call( this, this.config.selectors.buttons.airplay, ), settings: getElement.call( this, this.config.selectors.buttons.settings, ), captions: getElement.call( this, this.config.selectors.buttons.captions, ), fullscreen: getElement.call( this, this.config.selectors.buttons.fullscreen, ), }; // Progress this.elements.progress = getElement.call( this, this.config.selectors.progress, ); // Inputs this.elements.inputs = { seek: getElement.call(this, this.config.selectors.inputs.seek), volume: getElement.call( this, this.config.selectors.inputs.volume, ), }; // Display this.elements.display = { buffer: getElement.call( this, this.config.selectors.display.buffer, ), currentTime: getElement.call( this, this.config.selectors.display.currentTime, ), duration: getElement.call( this, this.config.selectors.display.duration, ), }; // Seek tooltip if (is.element(this.elements.progress)) { this.elements.display.seekTooltip = this.elements.progress.querySelector( `.${this.config.classNames.tooltip}`, ); } return true; } catch (error) { // Log it this.debug.warn( 'It looks like there is a problem with your custom controls HTML', error, ); // Restore native video controls this.toggleNativeControls(true); return false; } }, // Create icon createIcon(type, attributes) { const namespace = 'http://www.w3.org/2000/svg'; const iconUrl = controls.getIconUrl.call(this); const iconPath = `${!iconUrl.cors ? iconUrl.url : ''}#${ this.config.iconPrefix }`; // Create const icon = document.createElementNS(namespace, 'svg'); setAttributes( icon, extend(attributes, { role: 'presentation', focusable: 'false', }), ); // Create the to reference sprite const use = document.createElementNS(namespace, 'use'); const path = `${iconPath}-${type}`; // Set `href` attributes // https://github.com/sampotts/plyr/issues/460 // https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/xlink:href if ('href' in use) { use.setAttributeNS('http://www.w3.org/1999/xlink', 'href', path); } else { use.setAttributeNS( 'http://www.w3.org/1999/xlink', 'xlink:href', path, ); } // Add to icon.appendChild(use); return icon; }, // Create hidden text label createLabel(type, attr = {}) { // Skip i18n for abbreviations and brand names const universals = { pip: 'PIP', airplay: 'AirPlay', }; const text = universals[type] || i18n.get(type, this.config); const attributes = Object.assign({}, attr, { class: [attr.class, this.config.classNames.hidden] .filter(Boolean) .join(' '), }); return createElement('span', attributes, text); }, // Create a badge createBadge(text) { if (is.empty(text)) { return null; } const badge = createElement('span', { class: this.config.classNames.menu.value, }); badge.appendChild( createElement( 'span', { class: this.config.classNames.menu.badge, }, text, ), ); return badge; }, // Create a