diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/js/captions.js | 111 | ||||
-rw-r--r-- | src/js/controls.js | 17 | ||||
-rw-r--r-- | src/js/fullscreen.js | 4 | ||||
-rw-r--r-- | src/js/listeners.js | 27 | ||||
-rw-r--r-- | src/js/plugins/vimeo.js | 4 | ||||
-rw-r--r-- | src/js/plyr.js | 26 | ||||
-rw-r--r-- | src/js/utils.js | 35 | ||||
-rw-r--r-- | src/less/components/menus.less | 21 |
8 files changed, 107 insertions, 138 deletions
diff --git a/src/js/captions.js b/src/js/captions.js index 2cab9414..0db755ac 100644 --- a/src/js/captions.js +++ b/src/js/captions.js @@ -33,8 +33,6 @@ const captions = { // Only Vimeo and HTML5 video supported at this point if (!['video', 'vimeo'].includes(this.type) || (this.type === 'video' && !support.textTracks)) { - this.captions.tracks = null; - // Clear menu and hide if (this.config.controls.includes('settings') && this.config.settings.includes('captions')) { controls.setCaptionsMenu.call(this); @@ -49,97 +47,76 @@ const captions = { 'div', utils.getAttributesFromSelector(this.config.selectors.captions) ); - utils.insertAfter(this.elements.captions, this.elements.wrapper); - } - // Get tracks from HTML5 - if (this.type === 'video') { - this.captions.tracks = this.media.textTracks; + utils.insertAfter(this.elements.captions, this.elements.wrapper); } // Set the class hook utils.toggleClass( this.elements.container, this.config.classNames.captions.enabled, - !utils.is.empty(this.captions.tracks) + !utils.is.empty(captions.getTracks.call(this)) ); // If no caption file exists, hide container for caption text - if (utils.is.empty(this.captions.tracks)) { + if (utils.is.empty(captions.getTracks.call(this))) { return; } + // Set language + captions.setLanguage.call(this); + // Enable UI captions.show.call(this); - // Get a track - const setCurrentTrack = () => { - // Reset by default - this.captions.currentTrack = null; - - // Filter doesn't seem to work for a TextTrackList :-( - Array.from(this.captions.tracks).forEach(track => { - if (track.language.toLowerCase() === this.language.toLowerCase()) { - this.captions.currentTrack = track; - console.warn(`Set current track to ${this.language}`); - } - }); - }; - - // Get current track - setCurrentTrack(); - - // If we couldn't get the requested language, revert to default - if (!utils.is.track(this.captions.currentTrack)) { - const { language } = this.config.captions; - - // Reset to default - // We don't update user storage as the selected language could become available - this.captions.language = language; - - // Get fallback track - setCurrentTrack(); - - // If no match, disable captions - if (!utils.is.track(this.captions.currentTrack)) { - this.toggleCaptions(false); - } - - controls.updateSetting.call(this, 'captions'); + // Set available languages in list + if (this.config.controls.includes('settings') && this.config.settings.includes('captions')) { + controls.setCaptionsMenu.call(this); } + }, + // Set the captions language + setLanguage() { // Setup HTML5 track rendering if (this.type === 'video') { - // Turn off native caption rendering to avoid double captions - Array.from(this.captions.tracks).forEach(track => { - // Remove previous bindings (if we've changed source or language) - utils.off(track, 'cuechange', event => captions.setCue.call(this, event)); + captions.getTracks.call(this).forEach(track => { + // Remove previous bindings + utils.on(track, 'cuechange', event => captions.setCue.call(this, event)); - // Hide captions + // Turn off native caption rendering to avoid double captions // eslint-disable-next-line track.mode = 'hidden'; }); - // Check if suported kind - const supported = - this.captions.currentTrack && ['captions', 'subtitles'].includes(this.captions.currentTrack.kind); - - if (utils.is.track(this.captions.currentTrack) && supported) { - utils.on(this.captions.currentTrack, 'cuechange', event => captions.setCue.call(this, event)); + // Get current track + const currentTrack = captions.getCurrentTrack.call(this); + // Check if suported kind + if (utils.is.track(currentTrack)) { // If we change the active track while a cue is already displayed we need to update it - if (this.captions.currentTrack.activeCues && this.captions.currentTrack.activeCues.length > 0) { - captions.setCue.call(this, this.captions.currentTrack); + if (Array.from(currentTrack.activeCues || []).length) { + captions.setCue.call(this, currentTrack); } } } else if (this.type === 'vimeo' && this.captions.active) { - this.embed.enableTextTrack(this.captions.language); + this.embed.enableTextTrack(this.language); } + }, - // Set available languages in list - if (this.config.controls.includes('settings') && this.config.settings.includes('captions')) { - controls.setCaptionsMenu.call(this); + // Get the tracks + getTracks() { + // Return empty array at least + if (utils.is.nullOrUndefined(this.media)) { + return []; } + + // Only get accepted kinds + return Array.from(this.media.textTracks || []).filter(track => ['captions', 'subtitles'].includes(track.kind)); + }, + + // Get the current track for the current language + getCurrentTrack() { + return captions.getTracks.call(this).find(track => track.language.toLowerCase() === this.language); }, // Display active caption if it contains text @@ -147,19 +124,25 @@ const captions = { // Get the track from the event if needed const track = utils.is.event(input) ? input.target : input; const active = track.activeCues[0]; + const currentTrack = captions.getCurrentTrack.call(this); + + // Only display current track + if (track !== currentTrack) { + return; + } // Display a cue, if there is one if (utils.is.cue(active)) { - captions.set.call(this, active.getCueAsHTML()); + captions.setText.call(this, active.getCueAsHTML()); } else { - captions.set.call(this); + captions.setText.call(this, null); } utils.dispatchEvent.call(this, this.media, 'cuechange'); }, // Set the current caption - set(input) { + setText(input) { // Requires UI if (!this.supported.ui) { return; @@ -172,7 +155,7 @@ const captions = { utils.emptyElement(this.elements.captions); // Default to empty - const caption = !utils.is.undefined(input) ? input : ''; + const caption = !utils.is.nullOrUndefined(input) ? input : ''; // Set the span content if (utils.is.string(caption)) { diff --git a/src/js/controls.js b/src/js/controls.js index 8d96e427..e0b7150c 100644 --- a/src/js/controls.js +++ b/src/js/controls.js @@ -5,6 +5,7 @@ import support from './support'; import utils from './utils'; import ui from './ui'; +import captions from './captions'; // Sniff out the browser const browser = utils.getBrowser(); @@ -642,12 +643,16 @@ const controls = { return null; } - if (!support.textTracks || utils.is.empty(this.captions.tracks)) { + if (!support.textTracks || !captions.getTracks.call(this).length) { return this.config.i18n.none; } if (this.captions.enabled) { - return this.captions.currentTrack.label; + const currentTrack = captions.getCurrentTrack.call(this); + + if (utils.is.track(currentTrack)) { + return currentTrack.label; + } } return this.config.i18n.disabled; @@ -660,19 +665,19 @@ const controls = { const list = this.elements.settings.panes.captions.querySelector('ul'); // Toggle the pane and tab - const toggle = !utils.is.empty(this.captions.tracks); - controls.toggleTab.call(this, type, toggle); + const hasTracks = captions.getTracks.call(this).length; + controls.toggleTab.call(this, type, hasTracks); // Empty the menu utils.emptyElement(list); // If there's no captions, bail - if (utils.is.empty(this.captions.tracks)) { + if (!hasTracks) { return; } // Re-map the tracks into just the data we need - const tracks = Array.from(this.captions.tracks).map(track => ({ + const tracks = captions.getTracks.call(this).map(track => ({ language: track.language, label: !utils.is.empty(track.label) ? track.label : track.language.toUpperCase(), })); diff --git a/src/js/fullscreen.js b/src/js/fullscreen.js index 3dcdc1bf..2dde1f1a 100644 --- a/src/js/fullscreen.js +++ b/src/js/fullscreen.js @@ -51,7 +51,7 @@ const fullscreen = { return false; } - const target = utils.is.undefined(element) ? document.body : element; + const target = utils.is.nullOrUndefined(element) ? document.body : element; switch (prefix) { case '': @@ -71,7 +71,7 @@ const fullscreen = { return false; } - const target = utils.is.undefined(element) ? document.body : element; + const target = utils.is.nullOrUndefined(element) ? document.body : element; return !prefix.length ? target.requestFullScreen() diff --git a/src/js/listeners.js b/src/js/listeners.js index f93d4242..d9cd5bce 100644 --- a/src/js/listeners.js +++ b/src/js/listeners.js @@ -459,41 +459,24 @@ const listeners = { // Settings menu utils.on(this.elements.settings.form, 'click', event => { - // Show tab in menu - controls.showTab.call(this, event); + event.stopPropagation(); // Settings menu items - use event delegation as items are added/removed if (utils.matches(event.target, this.config.selectors.inputs.language)) { - // Settings - Language proxy(event, 'language', () => { - const language = event.target.value; - - this.toggleCaptions(!utils.is.empty(language)); - - if (!utils.is.empty(language)) { - this.language = event.target.value.toLowerCase(); - } + this.language = event.target.value; }); } else if (utils.matches(event.target, this.config.selectors.inputs.quality)) { - // Settings - Quality proxy(event, 'quality', () => { this.quality = event.target.value; }); } else if (utils.matches(event.target, this.config.selectors.inputs.speed)) { - // Settings - Speed proxy(event, 'speed', () => { this.speed = parseFloat(event.target.value); }); - } /* else if (utils.matches(event.target, this.config.selectors.buttons.loop)) { - // Settings - Looping - // TODO: use toggle buttons - proxy(event, 'loop', () => { - // TODO: This should be done in the method itself I think - // var value = event.target.getAttribute('data-loop__value') || event.target.getAttribute('data-loop__type'); - - this.console.warn('Set loop'); - }); - } */ + } else { + controls.showTab.call(this, event); + } }); // Seek diff --git a/src/js/plugins/vimeo.js b/src/js/plugins/vimeo.js index 10c0fc62..f033a969 100644 --- a/src/js/plugins/vimeo.js +++ b/src/js/plugins/vimeo.js @@ -221,7 +221,7 @@ const vimeo = { // Get captions player.embed.getTextTracks().then(tracks => { - player.captions.tracks = tracks; + player.media.textTracks = tracks; captions.setup.call(player); }); @@ -232,7 +232,7 @@ const vimeo = { cue = utils.stripHTML(data.cues[0].text); } - captions.set.call(player, cue); + captions.setText.call(player, cue); }); player.embed.on('loaded', () => { diff --git a/src/js/plyr.js b/src/js/plyr.js index 87fb014a..af9d57bf 100644 --- a/src/js/plyr.js +++ b/src/js/plyr.js @@ -81,7 +81,6 @@ class Plyr { // Captions this.captions = { enabled: null, - tracks: null, currentTrack: null, }; @@ -116,7 +115,7 @@ class Plyr { this.console.log('Support', support); // We need an element to setup - if (this.media === null || utils.is.undefined(this.media) || !utils.is.htmlElement(this.media)) { + if (utils.is.nullOrUndefined(this.media) || !utils.is.htmlElement(this.media)) { this.console.error('Setup failed: no suitable element passed'); return; } @@ -665,6 +664,14 @@ class Plyr { return; } + // Toggle captions based on input + this.toggleCaptions(!utils.is.empty(input)); + + // If empty string is passed, assume disable captions + if (utils.is.empty(input)) { + return; + } + // Normalize const language = input.toLowerCase(); @@ -673,20 +680,17 @@ class Plyr { return; } - // Reset UI - this.toggleCaptions(true); - // Update config this.captions.language = language; - // Trigger an event - utils.dispatchEvent.call(this, this.media, 'languagechange'); - // Clear caption - captions.set.call(this); + captions.setText.call(this, null); + + // Update captions + captions.setLanguage.call(this); - // Re-run setup - captions.setup.call(this); + // Trigger an event + utils.dispatchEvent.call(this, this.media, 'languagechange'); } get language() { diff --git a/src/js/utils.js b/src/js/utils.js index bb576576..aeb5ecd2 100644 --- a/src/js/utils.js +++ b/src/js/utils.js @@ -23,49 +23,42 @@ const utils = { return this.getConstructor(input) === Function; }, array(input) { - return !this.undefined(input) && Array.isArray(input); + return !this.nullOrUndefined(input) && Array.isArray(input); }, nodeList(input) { - return !this.undefined(input) && input instanceof NodeList; + return this.instanceof(input, window.NodeList); }, htmlElement(input) { - return !this.undefined(input) && input instanceof HTMLElement; + return this.instanceof(input, window.HTMLElement); }, textNode(input) { return this.getConstructor(input) === Text; }, event(input) { - return !this.undefined(input) && input instanceof Event; + return this.instanceof(input, window.Event); }, cue(input) { - return this.instanceOf(input, window.TextTrackCue) || this.instanceOf(input, window.VTTCue); + return this.instanceof(input, window.TextTrackCue) || this.instanceof(input, window.VTTCue); }, track(input) { - return ( - !this.undefined(input) && (this.instanceOf(input, window.TextTrack) || typeof input.kind === 'string') - ); + return this.instanceof(input, window.TextTrack) || this.string(input.kind); }, - undefined(input) { - return input !== null && typeof input === 'undefined'; + nullOrUndefined(input) { + return input === null || typeof input === 'undefined'; }, empty(input) { return ( - input === null || - typeof input === 'undefined' || + this.nullOrUndefined(input) || ((this.string(input) || this.array(input) || this.nodeList(input)) && !input.length) || (this.object(input) && !Object.keys(input).length) ); }, - getConstructor(input) { - if (input === null || typeof input === 'undefined') { - return null; - } - - return input.constructor; - }, - instanceOf(input, constructor) { + instanceof(input, constructor) { return Boolean(input && constructor && input instanceof constructor); }, + getConstructor(input) { + return !this.nullOrUndefined(input) ? input.constructor : null; + }, }, // Unfortunately, due to mixed support, UA sniffing is required @@ -474,7 +467,7 @@ const utils = { // Toggle event listener toggleListener(elements, event, callback, toggle, passive, capture) { // Bail if no elements - if (elements === null || utils.is.undefined(elements)) { + if (utils.is.nullOrUndefined(elements)) { return; } diff --git a/src/less/components/menus.less b/src/less/components/menus.less index f96393e0..8c2d400d 100644 --- a/src/less/components/menus.less +++ b/src/less/components/menus.less @@ -131,28 +131,24 @@ label.plyr__control { padding-left: @plyr-control-padding; - /*input[type='radio'] { - position: relative; - left: -@plyr-control-padding; - }*/ - input[type='radio'] + span { position: relative; display: block; - height: 14px; - width: 14px; + flex-shrink: 0; + height: 16px; + width: 16px; border-radius: 100%; background: fade(#000, 10%); margin-right: @plyr-control-spacing; - box-shadow: inset 0 1px 1px fade(#000, 15%); + transition: all 0.3s ease; &::after { content: ''; position: absolute; height: 6px; width: 6px; - top: 4px; - left: 4px; + top: 5px; + left: 5px; transform: scale(0); opacity: 0; background: #fff; @@ -169,6 +165,11 @@ opacity: 1; } } + + &.plyr__tab-focus input[type='radio'] + span, + &:hover input[type='radio'] + span { + background: fade(#000, 10%); + } } // Option value |