aboutsummaryrefslogtreecommitdiffstats
path: root/src/js
diff options
context:
space:
mode:
authorSam Potts <me@sampotts.me>2017-11-19 17:54:38 +1100
committerSam Potts <me@sampotts.me>2017-11-19 17:54:38 +1100
commit4b62a5c74dc5f67d0f12126f554df53f02541ef7 (patch)
treefbf9060236f658d86117a1b97cf29a2e48444b11 /src/js
parent3f744ef63a33f479b6d30a2f40717c6b44beb331 (diff)
downloadplyr-4b62a5c74dc5f67d0f12126f554df53f02541ef7.tar.lz
plyr-4b62a5c74dc5f67d0f12126f554df53f02541ef7.tar.xz
plyr-4b62a5c74dc5f67d0f12126f554df53f02541ef7.zip
Captions fix
Diffstat (limited to 'src/js')
-rw-r--r--src/js/captions.js111
-rw-r--r--src/js/controls.js17
-rw-r--r--src/js/fullscreen.js4
-rw-r--r--src/js/listeners.js27
-rw-r--r--src/js/plugins/vimeo.js4
-rw-r--r--src/js/plyr.js26
-rw-r--r--src/js/utils.js35
7 files changed, 96 insertions, 128 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;
}