diff options
author | Sam Potts <sam@potts.es> | 2018-03-27 10:36:08 +1100 |
---|---|---|
committer | Sam Potts <sam@potts.es> | 2018-03-27 10:36:08 +1100 |
commit | 9c1bc6ab08e4fe68565a12f76b58684f4c6a8354 (patch) | |
tree | f36ad0d9fbd5591038bba52822bad9c21121636d /src | |
parent | 3d2ba8c00942075ae387a09ab5aade1f680142c7 (diff) | |
download | plyr-9c1bc6ab08e4fe68565a12f76b58684f4c6a8354.tar.lz plyr-9c1bc6ab08e4fe68565a12f76b58684f4c6a8354.tar.xz plyr-9c1bc6ab08e4fe68565a12f76b58684f4c6a8354.zip |
Fixes for fast forward and issues with event.preventDefault()
Diffstat (limited to 'src')
-rw-r--r-- | src/js/controls.js | 33 | ||||
-rw-r--r-- | src/js/defaults.js | 7 | ||||
-rw-r--r-- | src/js/fullscreen.js | 6 | ||||
-rw-r--r-- | src/js/i18n.js | 31 | ||||
-rw-r--r-- | src/js/listeners.js | 209 | ||||
-rw-r--r-- | src/js/plugins/ads.js | 3 | ||||
-rw-r--r-- | src/js/plyr.js | 2 | ||||
-rw-r--r-- | src/js/ui.js | 5 | ||||
-rw-r--r-- | src/js/utils.js | 52 |
9 files changed, 217 insertions, 131 deletions
diff --git a/src/js/controls.js b/src/js/controls.js index b0159115..295a2eef 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 i18n from './i18n'; import captions from './captions'; // Sniff out the browser @@ -74,7 +75,7 @@ const controls = { // Create hidden text label createLabel(type, attr) { - let text = this.config.i18n[type]; + let text = i18n.get(type, this.config); const attributes = Object.assign({}, attr); switch (type) { @@ -126,7 +127,7 @@ const controls = { createButton(buttonType, attr) { const button = utils.createElement('button'); const attributes = Object.assign({}, attr); - let type = buttonType; + let type = utils.toCamelCase(buttonType); let toggle = false; let label; @@ -147,7 +148,7 @@ const controls = { } // Large play button - switch (type) { + switch (buttonType) { case 'play': toggle = true; label = 'play'; @@ -189,7 +190,7 @@ const controls = { default: label = type; - icon = type; + icon = buttonType; } // Setup toggle icon and labels @@ -204,7 +205,7 @@ const controls = { // Add aria attributes attributes['aria-pressed'] = false; - attributes['aria-label'] = this.config.i18n[label]; + attributes['aria-label'] = i18n.get(label, this.config); } else { button.appendChild(controls.createIcon.call(this, icon)); button.appendChild(controls.createLabel.call(this, label)); @@ -238,7 +239,7 @@ const controls = { for: attributes.id, class: this.config.classNames.hidden, }, - this.config.i18n[type], + i18n.get(type, this.config), ); // Seek input @@ -291,11 +292,11 @@ const controls = { let suffix = ''; switch (type) { case 'played': - suffix = this.config.i18n.played; + suffix = i18n.get('played', this.config); break; case 'buffer': - suffix = this.config.i18n.buffered; + suffix = i18n.get('buffered', this.config); break; default: @@ -322,7 +323,7 @@ const controls = { { class: this.config.classNames.hidden, }, - this.config.i18n[type], + i18n.get(type, this.config), ), ); @@ -617,7 +618,7 @@ const controls = { class: this.config.classNames.control, 'data-plyr-loop-action': option, }), - this.config.i18n[option] + i18n.get(option, this.config) ); if (['start', 'end'].includes(option)) { @@ -638,7 +639,7 @@ const controls = { } if (!support.textTracks || !captions.getTracks.call(this).length) { - return this.config.i18n.none; + return i18n.get('none', this.config); } if (this.captions.active) { @@ -649,7 +650,7 @@ const controls = { } } - return this.config.i18n.disabled; + return i18n.get('disabled', this.config); }, // Set a list of available captions languages @@ -679,7 +680,7 @@ const controls = { // Add the "None" option to turn off captions tracks.unshift({ language: '', - label: this.config.i18n.none, + label: i18n.get('none', this.config), }); // Generate options @@ -927,7 +928,7 @@ const controls = { // Fast forward button if (this.config.controls.includes('fast-forward')) { - container.appendChild(controls.createButton.call(this, 'fastForward')); + container.appendChild(controls.createButton.call(this, 'fast-forward')); } // Progress @@ -1069,7 +1070,7 @@ const controls = { 'aria-controls': `plyr-settings-${data.id}-${type}`, 'aria-expanded': false, }), - this.config.i18n[type], + i18n.get(type, this.config), ); const value = utils.createElement('span', { @@ -1109,7 +1110,7 @@ const controls = { 'aria-controls': `plyr-settings-${data.id}-home`, 'aria-expanded': false, }, - this.config.i18n[type], + i18n.get(type, this.config), ); pane.appendChild(back); diff --git a/src/js/defaults.js b/src/js/defaults.js index e8b9e187..56a45539 100644 --- a/src/js/defaults.js +++ b/src/js/defaults.js @@ -132,7 +132,10 @@ const defaults = { // Default controls controls: [ 'play-large', + // 'restart', + // 'rewind', 'play', + // 'fast-forward', 'progress', 'current-time', 'mute', @@ -155,7 +158,7 @@ const defaults = { rewind: 'Rewind {seektime} secs', play: 'Play', pause: 'Pause', - forward: 'Forward {seektime} secs', + fastForward: 'Forward {seektime} secs', seek: 'Seek', played: 'Played', buffered: 'Buffered', @@ -203,7 +206,7 @@ const defaults = { pause: null, restart: null, rewind: null, - forward: null, + fastForward: null, mute: null, volume: null, captions: null, diff --git a/src/js/fullscreen.js b/src/js/fullscreen.js index ffed442f..2e898042 100644 --- a/src/js/fullscreen.js +++ b/src/js/fullscreen.js @@ -161,6 +161,8 @@ class Fullscreen { return; } + console.warn(this.prefix); + // iOS native fullscreen doesn't need the request step if (browser.isIos && this.player.config.fullscreen.iosNative) { if (this.player.playing) { @@ -169,7 +171,7 @@ class Fullscreen { } else if (!Fullscreen.native) { toggleFallback.call(this, true); } else if (!this.prefix) { - this.target.requestFullScreen(); + this.target.requestFullscreen(); } else if (!utils.is.empty(this.prefix)) { this.target[`${this.prefix}Request${this.name}`](); } @@ -197,6 +199,8 @@ class Fullscreen { // Toggle state toggle() { + console.warn('TOGGLE'); + if (!this.active) { this.enter(); } else { diff --git a/src/js/i18n.js b/src/js/i18n.js new file mode 100644 index 00000000..58c3e7cf --- /dev/null +++ b/src/js/i18n.js @@ -0,0 +1,31 @@ +// ========================================================================== +// Plyr internationalization +// ========================================================================== + +import utils from './utils'; + +const i18n = { + get(key = '', config = {}) { + if (utils.is.empty(key) || utils.is.empty(config) || !Object.keys(config.i18n).includes(key)) { + return ''; + } + + let string = config.i18n[key]; + + const replace = { + '{seektime}': config.seekTime, + '{title}': config.title, + }; + + Object.entries(replace).forEach(([ + key, + value, + ]) => { + string = utils.replaceAll(string, key, value); + }); + + return string; + }, +}; + +export default i18n; diff --git a/src/js/listeners.js b/src/js/listeners.js index a543bfcd..9f4400d6 100644 --- a/src/js/listeners.js +++ b/src/js/listeners.js @@ -128,7 +128,7 @@ class Listeners { case 39: // Arrow forward - this.player.forward(); + this.player.fastForward(); break; case 37: @@ -379,12 +379,13 @@ class Listeners { // IE doesn't support input event, so we fallback to change const inputEvent = browser.isIE ? 'change' : 'input'; - // Trigger custom and default handlers - const proxy = (event, handlerKey, defaultHandler) => { - const customHandler = this.player.config.listeners[handlerKey]; + // Run default and custom handlers + const proxy = (event, defaultHandler, customHandlerKey) => { + const customHandler = this.player.config.listeners[customHandlerKey]; + const hasCustomHandler = utils.is.function(customHandler); // Execute custom handler - if (utils.is.function(customHandler)) { + if (hasCustomHandler) { customHandler.call(this.player, event); } @@ -394,107 +395,110 @@ class Listeners { } }; + // Trigger custom and default handlers + const on = (element, type, defaultHandler, customHandlerKey, passive = true) => { + const customHandler = this.player.config.listeners[customHandlerKey]; + const hasCustomHandler = utils.is.function(customHandler); + + utils.on(element, type, event => proxy(event, defaultHandler, customHandlerKey), passive && !hasCustomHandler); + }; + // Play/pause toggle - utils.on(this.player.elements.buttons.play, 'click', event => - proxy(event, 'play', () => { - this.player.togglePlay(); - }), - ); + on(this.player.elements.buttons.play, 'click', this.player.togglePlay, 'play'); // Pause - utils.on(this.player.elements.buttons.restart, 'click', event => - proxy(event, 'restart', () => { - this.player.restart(); - }), - ); + on(this.player.elements.buttons.restart, 'click', this.player.restart, 'restart'); // Rewind - utils.on(this.player.elements.buttons.rewind, 'click', event => - proxy(event, 'rewind', () => { - this.player.rewind(); - }), - ); + on(this.player.elements.buttons.rewind, 'click', this.player.rewind, 'rewind'); // Rewind - utils.on(this.player.elements.buttons.forward, 'click', event => - proxy(event, 'forward', () => { - this.player.forward(); - }), - ); + on(this.player.elements.buttons.fastForward, 'click', this.player.fastForward, 'fastForward'); // Mute toggle - utils.on(this.player.elements.buttons.mute, 'click', event => - proxy(event, 'mute', () => { + on( + this.player.elements.buttons.mute, + 'click', + () => { this.player.muted = !this.player.muted; - }), + }, + 'mute', ); // Captions toggle - utils.on(this.player.elements.buttons.captions, 'click', event => - proxy(event, 'captions', () => { - this.player.toggleCaptions(); - }), - ); + on(this.player.elements.buttons.captions, 'click', this.player.toggleCaptions); // Fullscreen toggle - utils.on(this.player.elements.buttons.fullscreen, 'click', event => - proxy(event, 'fullscreen', () => { + on( + this.player.elements.buttons.fullscreen, + 'click', + () => { this.player.fullscreen.toggle(); - }), + }, + 'fullscreen', ); // Picture-in-Picture - utils.on(this.player.elements.buttons.pip, 'click', event => - proxy(event, 'pip', () => { + on( + this.player.elements.buttons.pip, + 'click', + () => { this.player.pip = 'toggle'; - }), + }, + 'pip', ); // Airplay - utils.on(this.player.elements.buttons.airplay, 'click', event => - proxy(event, 'airplay', () => { - this.player.airplay(); - }), - ); + on(this.player.elements.buttons.airplay, 'click', this.player.airplay, 'airplay'); // Settings menu - utils.on(this.player.elements.buttons.settings, 'click', event => { + on(this.player.elements.buttons.settings, 'click', event => { controls.toggleMenu.call(this.player, event); }); // Settings menu - utils.on(this.player.elements.settings.form, 'click', event => { + on(this.player.elements.settings.form, 'click', event => { event.stopPropagation(); // Settings menu items - use event delegation as items are added/removed if (utils.matches(event.target, this.player.config.selectors.inputs.language)) { - proxy(event, 'language', () => { - this.player.language = event.target.value; - }); + proxy( + event, + () => { + this.player.language = event.target.value; + }, + 'language', + ); } else if (utils.matches(event.target, this.player.config.selectors.inputs.quality)) { - proxy(event, 'quality', () => { - this.player.quality = event.target.value; - }); + proxy( + event, + () => { + this.player.quality = event.target.value; + }, + 'quality', + ); } else if (utils.matches(event.target, this.player.config.selectors.inputs.speed)) { - proxy(event, 'speed', () => { - this.player.speed = parseFloat(event.target.value); - }); + proxy( + event, + () => { + this.player.speed = parseFloat(event.target.value); + }, + 'speed', + ); } else { controls.showTab.call(this.player, event); } }); // Seek - utils.on(this.player.elements.inputs.seek, inputEvent, event => - proxy(event, 'seek', () => { - this.player.currentTime = event.target.value / event.target.max * this.player.duration; - }), - ); + on(this.player.elements.inputs.seek, inputEvent, 'seek', event => { + this.player.currentTime = event.target.value / event.target.max * this.player.duration; + }); // Current time invert // Only if one time element is used for both currentTime and duration if (this.player.config.toggleInvert && !utils.is.element(this.player.elements.display.duration)) { - utils.on(this.player.elements.display.currentTime, 'click', () => { + on(this.player.elements.display.currentTime, 'click', () => { // Do nothing if we're at the start if (this.player.currentTime === 0) { return; @@ -506,31 +510,34 @@ class Listeners { } // Volume - utils.on(this.player.elements.inputs.volume, inputEvent, event => - proxy(event, 'volume', () => { + on( + this.player.elements.inputs.volume, + inputEvent, + event => { this.player.volume = event.target.value; - }), + }, + 'volume', ); // Polyfill for lower fill in <input type="range"> for webkit if (browser.isWebkit) { - utils.on(utils.getElements.call(this.player, 'input[type="range"]'), 'input', event => { + on(utils.getElements.call(this.player, 'input[type="range"]'), 'input', event => { controls.updateRangeFill.call(this.player, event.target); }); } // Seek tooltip - utils.on(this.player.elements.progress, 'mouseenter mouseleave mousemove', event => controls.updateSeekTooltip.call(this.player, event)); + on(this.player.elements.progress, 'mouseenter mouseleave mousemove', event => controls.updateSeekTooltip.call(this.player, event)); // Toggle controls visibility based on mouse movement if (this.player.config.hideControls) { // Watch for cursor over controls so they don't hide when trying to interact - utils.on(this.player.elements.controls, 'mouseenter mouseleave', event => { + on(this.player.elements.controls, 'mouseenter mouseleave', event => { this.player.elements.controls.hover = event.type === 'mouseenter'; }); // Watch for cursor over controls so they don't hide when trying to interact - utils.on(this.player.elements.controls, 'mousedown mouseup touchstart touchend touchcancel', event => { + on(this.player.elements.controls, 'mousedown mouseup touchstart touchend touchcancel', event => { this.player.elements.controls.pressed = [ 'mousedown', 'touchstart', @@ -538,50 +545,50 @@ class Listeners { }); // Focus in/out on controls - utils.on(this.player.elements.controls, 'focusin focusout', event => { + on(this.player.elements.controls, 'focusin focusout', event => { this.player.toggleControls(event); }); } // Mouse wheel for volume - utils.on( + on( this.player.elements.inputs.volume, 'wheel', - event => - proxy(event, 'volume', () => { - // Detect "natural" scroll - suppored on OS X Safari only - // Other browsers on OS X will be inverted until support improves - const inverted = event.webkitDirectionInvertedFromDevice; - const step = 1 / 50; - let direction = 0; - - // Scroll down (or up on natural) to decrease - if (event.deltaY < 0 || event.deltaX > 0) { - if (inverted) { - this.player.decreaseVolume(step); - direction = -1; - } else { - this.player.increaseVolume(step); - direction = 1; - } + event => { + // Detect "natural" scroll - suppored on OS X Safari only + // Other browsers on OS X will be inverted until support improves + const inverted = event.webkitDirectionInvertedFromDevice; + const step = 1 / 50; + let direction = 0; + + // Scroll down (or up on natural) to decrease + if (event.deltaY < 0 || event.deltaX > 0) { + if (inverted) { + this.player.decreaseVolume(step); + direction = -1; + } else { + this.player.increaseVolume(step); + direction = 1; } + } - // Scroll up (or down on natural) to increase - if (event.deltaY > 0 || event.deltaX < 0) { - if (inverted) { - this.player.increaseVolume(step); - direction = 1; - } else { - this.player.decreaseVolume(step); - direction = -1; - } + // Scroll up (or down on natural) to increase + if (event.deltaY > 0 || event.deltaX < 0) { + if (inverted) { + this.player.increaseVolume(step); + direction = 1; + } else { + this.player.decreaseVolume(step); + direction = -1; } + } - // Don't break page scrolling at max and min - if ((direction === 1 && this.player.media.volume < 1) || (direction === -1 && this.player.media.volume > 0)) { - event.preventDefault(); - } - }), + // Don't break page scrolling at max and min + if ((direction === 1 && this.player.media.volume < 1) || (direction === -1 && this.player.media.volume > 0)) { + event.preventDefault(); + } + }, + 'volume', false, ); } diff --git a/src/js/plugins/ads.js b/src/js/plugins/ads.js index 31a797c2..3e8f0923 100644 --- a/src/js/plugins/ads.js +++ b/src/js/plugins/ads.js @@ -7,6 +7,7 @@ /* global google */ import utils from '../utils'; +import i18n from '../i18n'; class Ads { /** @@ -178,7 +179,7 @@ class Ads { const update = () => { const time = utils.formatTime(Math.max(this.manager.getRemainingTime(), 0)); - const label = `${this.player.config.i18n.advertisement} - ${time}`; + const label = `${i18n.get('advertisement', this.player.config)} - ${time}`; this.elements.container.setAttribute('data-badge-text', label); }; diff --git a/src/js/plyr.js b/src/js/plyr.js index 061ac25c..3a6090ae 100644 --- a/src/js/plyr.js +++ b/src/js/plyr.js @@ -405,7 +405,7 @@ class Plyr { * Fast forward * @param {number} seekTime - how far to fast forward in seconds. Defaults to the config.seekTime */ - forward(seekTime) { + fastForward(seekTime) { this.currentTime = this.currentTime + (utils.is.number(seekTime) ? seekTime : this.config.seekTime); } diff --git a/src/js/ui.js b/src/js/ui.js index a4f22413..a3b3754a 100644 --- a/src/js/ui.js +++ b/src/js/ui.js @@ -5,6 +5,7 @@ import utils from './utils'; import captions from './captions'; import controls from './controls'; +import i18n from './i18n'; const ui = { addStyleHook() { @@ -94,7 +95,7 @@ const ui = { // Setup aria attribute for play and iframe title setTitle() { // Find the current text - let label = this.config.i18n.play; + let 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)) { @@ -123,7 +124,7 @@ const ui = { // Default to media type const title = !utils.is.empty(this.config.title) ? this.config.title : 'video'; - iframe.setAttribute('title', this.config.i18n.frameTitle.replace('{title}', title)); + iframe.setAttribute('title', i18n.get('frameTitle', this.config)); } }, diff --git a/src/js/utils.js b/src/js/utils.js index 493fb15e..172e29ca 100644 --- a/src/js/utils.js +++ b/src/js/utils.js @@ -540,7 +540,7 @@ const utils = { }, // Toggle event listener - toggleListener(elements, event, callback, toggle, passive, capture) { + toggleListener(elements, event, callback, toggle = false, passive = true, capture = false) { // Bail if no elemetns, event, or callback if (utils.is.empty(elements) || utils.is.empty(event) || !utils.is.function(callback)) { return; @@ -562,16 +562,16 @@ const utils = { const events = event.split(' '); // Build options - // Default to just capture boolean - let options = utils.is.boolean(capture) ? capture : false; + // Default to just the capture boolean for browsers with no passive listener support + let options = capture; // If passive events listeners are supported if (support.passiveListeners) { options = { // Whether the listener can be passive (i.e. default never prevented) - passive: utils.is.boolean(passive) ? passive : true, + passive, // Whether the listener is a capturing listener or not - capture: utils.is.boolean(capture) ? capture : false, + capture, }; } @@ -582,12 +582,12 @@ const utils = { }, // Bind event handler - on(element, events, callback, passive, capture) { + on(element, events = '', callback, passive = true, capture = false) { utils.toggleListener(element, events, callback, true, passive, capture); }, // Unbind event handler - off(element, events, callback, passive, capture) { + off(element, events = '', callback, passive = true, capture = false) { utils.toggleListener(element, events, callback, false, passive, capture); }, @@ -678,6 +678,44 @@ const utils = { return `${inverted ? '-' : ''}${hours}${format(mins)}:${format(secs)}`; }, + // Replace all occurances of a string in a string + replaceAll(input = '', find = '', replace = '') { + return input.replace(new RegExp(find.toString().replace(/([.*+?^=!:${}()|[\]/\\])/g, '\\$1'), 'g'), replace.toString()); + }, + + // Convert to title case + toTitleCase(input = '') { + return input.toString().replace(/\w\S*/g, text => text.charAt(0).toUpperCase() + text.substr(1).toLowerCase()); + }, + + // Convert string to pascalCase + toPascalCase(input = '') { + let string = input.toString(); + + // Convert kebab case + string = utils.replaceAll(string, '-', ' '); + + // Convert snake case + string = utils.replaceAll(string, '_', ' '); + + // Convert to title case + string = utils.toTitleCase(string); + + // Convert to pascal case + return utils.replaceAll(string, ' ', ''); + }, + + // Convert string to pascalCase + toCamelCase(input = '') { + let string = input.toString(); + + // Convert to pascal case + string = utils.toPascalCase(string); + + // Convert first character to lowercase + return string.charAt(0).toLowerCase() + string.slice(1); + }, + // Deep extend destination object with N more objects extend(target = {}, ...sources) { if (!sources.length) { |