mirror of
https://github.com/shaka-project/shaka-player.git
synced 2026-06-13 15:46:46 +03:00
feat(UI): Add live subtitle style preview on hover (#10077)
This commit is contained in:
@@ -46,6 +46,7 @@
|
||||
+../../ui/text_position.js
|
||||
+../../ui/text_selection.js
|
||||
+../../ui/text_size.js
|
||||
+../../ui/text_style_preview.js
|
||||
+../../ui/toggle_stereoscopic.js
|
||||
+../../ui/ui.js
|
||||
+../../ui/ui_utils.js
|
||||
|
||||
@@ -7640,6 +7640,16 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
||||
return this.videoContainer_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the active text displayer, if one has been created. This is not
|
||||
* exported; it is for internal integrations such as the UI controls.
|
||||
*
|
||||
* @return {?shaka.extern.TextDisplayer}
|
||||
*/
|
||||
getTextDisplayer() {
|
||||
return this.textDisplayer_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {!shaka.util.Error} error
|
||||
* @private
|
||||
|
||||
+120
-13
@@ -56,6 +56,15 @@ shaka.text.UITextDisplayer = class {
|
||||
/** @private {?shaka.extern.TextDisplayerConfiguration} */
|
||||
this.config_ = null;
|
||||
|
||||
/** @private {?shaka.extern.TextDisplayerConfiguration} */
|
||||
this.previewConfig_ = null;
|
||||
|
||||
/** @private {?shaka.text.Cue} */
|
||||
this.previewCue_ = null;
|
||||
|
||||
/** @private {boolean} */
|
||||
this.showingPreviewCue_ = false;
|
||||
|
||||
/** @type {HTMLElement} */
|
||||
this.textContainer_ = shaka.util.Dom.createHTMLElement('div');
|
||||
this.textContainer_.classList.add('shaka-text-container');
|
||||
@@ -231,6 +240,38 @@ shaka.text.UITextDisplayer = class {
|
||||
this.updateCaptions_(/* forceUpdate= */ true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporarily previews text displayer style settings using the normal UI
|
||||
* text rendering path.
|
||||
*
|
||||
* @param {!shaka.extern.TextDisplayerConfiguration} config
|
||||
* @param {string} exampleText
|
||||
* @export
|
||||
*/
|
||||
setTextStylePreview(config, exampleText) {
|
||||
this.previewConfig_ =
|
||||
/** @type {!shaka.extern.TextDisplayerConfiguration} */(
|
||||
Object.assign({}, config));
|
||||
this.previewCue_ = new shaka.text.Cue(
|
||||
Number.NEGATIVE_INFINITY, Number.POSITIVE_INFINITY, exampleText);
|
||||
this.updateCaptions_(/* forceUpdate= */ true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears temporary text style preview settings.
|
||||
* @export
|
||||
*/
|
||||
clearTextStylePreview() {
|
||||
if (!this.previewConfig_ && !this.previewCue_) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.previewConfig_ = null;
|
||||
this.previewCue_ = null;
|
||||
this.showingPreviewCue_ = false;
|
||||
this.updateCaptions_(/* forceUpdate= */ true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
@@ -278,6 +319,9 @@ shaka.text.UITextDisplayer = class {
|
||||
|
||||
this.isTextVisible_ = false;
|
||||
this.cues_ = [];
|
||||
this.previewConfig_ = null;
|
||||
this.previewCue_ = null;
|
||||
this.showingPreviewCue_ = false;
|
||||
this.captionsTimer_?.stop();
|
||||
this.captionsTimer_ = null;
|
||||
this.applyVisibilityTimer_?.stop();
|
||||
@@ -381,6 +425,14 @@ shaka.text.UITextDisplayer = class {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {?shaka.extern.TextDisplayerConfiguration}
|
||||
* @private
|
||||
*/
|
||||
getActiveConfig_() {
|
||||
return this.previewConfig_ || this.config_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@@ -450,6 +502,8 @@ shaka.text.UITextDisplayer = class {
|
||||
* @private
|
||||
*/
|
||||
updateCuesRecursive_(cues, container, currentTime, parents) {
|
||||
const config = this.getActiveConfig_();
|
||||
|
||||
// Set to true if the cues have changed in some way, which will require
|
||||
// DOM changes. E.g. if a cue was added or removed.
|
||||
let updateDOM = false;
|
||||
@@ -474,8 +528,8 @@ shaka.text.UITextDisplayer = class {
|
||||
let cueKey = cue;
|
||||
|
||||
let cueRegistry = this.currentCuesMap_.get(cue);
|
||||
if (!cueRegistry && this.config_ &&
|
||||
this.config_.positionArea != shaka.config.PositionArea.DEFAULT) {
|
||||
if (!cueRegistry && config &&
|
||||
config.positionArea != shaka.config.PositionArea.DEFAULT) {
|
||||
for (const key of this.currentCuesMap_.keys()) {
|
||||
if (shaka.text.Cue.equal(cue, key)) {
|
||||
cueKey = key;
|
||||
@@ -551,8 +605,8 @@ shaka.text.UITextDisplayer = class {
|
||||
});
|
||||
for (const cue of toPlant) {
|
||||
let cueRegistry = this.currentCuesMap_.get(cue);
|
||||
if (!cueRegistry &&
|
||||
this.config_.positionArea != shaka.config.PositionArea.DEFAULT) {
|
||||
if (!cueRegistry && config &&
|
||||
config.positionArea != shaka.config.PositionArea.DEFAULT) {
|
||||
for (const key of this.currentCuesMap_.keys()) {
|
||||
if (shaka.text.Cue.equal(cue, key)) {
|
||||
cueRegistry = this.currentCuesMap_.get(key);
|
||||
@@ -573,6 +627,30 @@ shaka.text.UITextDisplayer = class {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {!Array<!shaka.text.Cue>} cues
|
||||
* @param {number} currentTime
|
||||
* @return {boolean}
|
||||
* @private
|
||||
*/
|
||||
hasCueAtTime_(cues, currentTime) {
|
||||
return cues.some((cue) => {
|
||||
return cue.startTime <= currentTime && cue.endTime > currentTime &&
|
||||
this.hasCueContent_(cue);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {!shaka.text.Cue} cue
|
||||
* @return {boolean}
|
||||
* @private
|
||||
*/
|
||||
hasCueContent_(cue) {
|
||||
const text = cue.payload.replace(/[\u00a0\u200B]/g, ' ').trim();
|
||||
return !!text || !!cue.backgroundImage ||
|
||||
cue.nestedCues.some((nestedCue) => this.hasCueContent_(nestedCue));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the current captions.
|
||||
* @param {boolean=} forceUpdate
|
||||
@@ -583,9 +661,34 @@ shaka.text.UITextDisplayer = class {
|
||||
return;
|
||||
}
|
||||
|
||||
const delay = this.config_?.subtitleDelay ?? 0;
|
||||
const config = this.getActiveConfig_();
|
||||
const delay = config?.subtitleDelay ?? 0;
|
||||
const currentTime = this.video_.currentTime - delay;
|
||||
if (!this.isTextVisible_ || forceUpdate) {
|
||||
const showPreviewCue = !!this.previewCue_ &&
|
||||
!this.hasCueAtTime_(this.cues_, currentTime);
|
||||
if (showPreviewCue != this.showingPreviewCue_) {
|
||||
forceUpdate = true;
|
||||
this.showingPreviewCue_ = showPreviewCue;
|
||||
}
|
||||
|
||||
if (showPreviewCue) {
|
||||
// Force a full clear if we are showing a preview. This prevents
|
||||
// "stacking" bugs because the preview cue object changes
|
||||
// frequently during hover.
|
||||
forceUpdate = true;
|
||||
}
|
||||
const shouldBeVisible = this.isTextVisible_ || showPreviewCue;
|
||||
if (shouldBeVisible) {
|
||||
if (!this.textContainer_.parentElement) {
|
||||
this.videoContainer_.appendChild(this.textContainer_);
|
||||
}
|
||||
} else {
|
||||
if (this.textContainer_.parentElement) {
|
||||
this.videoContainer_.removeChild(this.textContainer_);
|
||||
}
|
||||
}
|
||||
|
||||
if (!shouldBeVisible || forceUpdate) {
|
||||
// Remove child elements from all regions.
|
||||
for (const regionElement of this.regionElements_.values()) {
|
||||
shaka.util.Dom.removeAllChildren(regionElement);
|
||||
@@ -596,7 +699,7 @@ shaka.text.UITextDisplayer = class {
|
||||
this.currentCuesMap_.clear();
|
||||
this.regionElements_.clear();
|
||||
}
|
||||
if (this.isTextVisible_) {
|
||||
if (shouldBeVisible) {
|
||||
// Log currently attached cue elements for verification, later.
|
||||
const previousCuesMap = new Map();
|
||||
if (goog.DEBUG) {
|
||||
@@ -605,9 +708,10 @@ shaka.text.UITextDisplayer = class {
|
||||
}
|
||||
}
|
||||
|
||||
let cues = this.cues_;
|
||||
if (this.config_ &&
|
||||
this.config_.positionArea != shaka.config.PositionArea.DEFAULT) {
|
||||
let cues = showPreviewCue && this.previewCue_ ?
|
||||
[this.previewCue_] : this.cues_;
|
||||
if (config &&
|
||||
config.positionArea != shaka.config.PositionArea.DEFAULT) {
|
||||
cues = cues.map((cue) => this.processCueStyle_(cue));
|
||||
}
|
||||
|
||||
@@ -634,13 +738,15 @@ shaka.text.UITextDisplayer = class {
|
||||
|
||||
/** @private */
|
||||
processCueStyle_(cue) {
|
||||
const config = /** @type {!shaka.extern.TextDisplayerConfiguration} */(
|
||||
this.getActiveConfig_());
|
||||
goog.asserts.assert(
|
||||
this.config_.positionArea !== shaka.config.PositionArea.DEFAULT,
|
||||
config.positionArea !== shaka.config.PositionArea.DEFAULT,
|
||||
'processCueStyle_ is intended to use on non default positioning');
|
||||
const modifiedCue = cue.clone();
|
||||
shaka.text.Utils.resetCuePositioning(modifiedCue);
|
||||
modifiedCue.region = shaka.text.UITextDisplayer.CustomRegion_.value();
|
||||
switch (this.config_.positionArea) {
|
||||
switch (config.positionArea) {
|
||||
case shaka.config.PositionArea.TOP_LEFT:
|
||||
modifiedCue.textAlign = shaka.text.Cue.textAlign.LEFT;
|
||||
modifiedCue.displayAlign = shaka.text.Cue.displayAlign.BEFORE;
|
||||
@@ -1011,7 +1117,8 @@ shaka.text.UITextDisplayer = class {
|
||||
style.fontWeight = cue.fontWeight.toString();
|
||||
style.fontStyle = cue.fontStyle;
|
||||
style.letterSpacing = cue.letterSpacing;
|
||||
const fontScaleFactor = this.config_ ? this.config_.fontScaleFactor : 1;
|
||||
const config = this.getActiveConfig_();
|
||||
const fontScaleFactor = config ? config.fontScaleFactor : 1;
|
||||
if (fontScaleFactor !== 1 || cue.fontSize) {
|
||||
// Use browser default (1em) if fontSize is not set or empty
|
||||
const fontSize = cue.fontSize || '1em';
|
||||
|
||||
@@ -20,6 +20,7 @@ describe('CastUtils', () => {
|
||||
'getNetworkingEngine', // Handled specially
|
||||
'getDrmEngine', // Handled specially
|
||||
'getMediaElement', // Handled specially
|
||||
'getTextDisplayer', // Internal UI hook, not proxied.
|
||||
'destroy', // Should use CastProxy.destroy instead
|
||||
'drmInfo', // Too large to proxy
|
||||
'getManifest', // Too large to proxy
|
||||
|
||||
@@ -631,13 +631,106 @@ describe('UITextDisplayer', () => {
|
||||
expect(videoContainer.childNodes.length).toBe(0);
|
||||
});
|
||||
|
||||
it('previews text styles through the normal renderer', () => {
|
||||
const cue = new shaka.text.Cue(0, 100, 'Previewed cue');
|
||||
const config =
|
||||
shaka.util.PlayerConfiguration.createDefault().textDisplayer;
|
||||
|
||||
textDisplayer.configure(config);
|
||||
textDisplayer.setTextVisibility(true);
|
||||
textDisplayer.append([cue]);
|
||||
updateCaptions();
|
||||
|
||||
const textContainer = videoContainer.querySelector('.shaka-text-container');
|
||||
let cueElement = textContainer.querySelector('div');
|
||||
expect(cueElement.textContent).toBe('Previewed cue');
|
||||
expect(cueElement.style.fontSize).toBe('');
|
||||
|
||||
const previewConfig =
|
||||
/** @type {!shaka.extern.TextDisplayerConfiguration} */(
|
||||
Object.assign({}, config, {'fontScaleFactor': 2}));
|
||||
textDisplayer.setTextStylePreview(
|
||||
previewConfig, 'Subtitles example');
|
||||
|
||||
cueElement = textContainer.querySelector('div');
|
||||
expect(cueElement.textContent).toBe('Previewed cue');
|
||||
expect(cueElement.style.fontSize).toBe('2em');
|
||||
|
||||
textDisplayer.clearTextStylePreview();
|
||||
|
||||
cueElement = textContainer.querySelector('div');
|
||||
expect(cueElement.textContent).toBe('Previewed cue');
|
||||
expect(cueElement.style.fontSize).toBe('');
|
||||
});
|
||||
|
||||
it('shows example text only when no cue is active during preview', () => {
|
||||
const config =
|
||||
shaka.util.PlayerConfiguration.createDefault().textDisplayer;
|
||||
|
||||
textDisplayer.configure(config);
|
||||
textDisplayer.setTextVisibility(true);
|
||||
|
||||
textDisplayer.setTextStylePreview(config, 'Subtitles example');
|
||||
|
||||
const textContainer = videoContainer.querySelector('.shaka-text-container');
|
||||
expect(textContainer.textContent).toBe('Subtitles example');
|
||||
|
||||
textDisplayer.clearTextStylePreview();
|
||||
expect(textContainer.textContent).toBe('');
|
||||
|
||||
textDisplayer.setTextStylePreview(config, 'Subtitles example');
|
||||
expect(textContainer.textContent).toBe('Subtitles example');
|
||||
|
||||
textDisplayer.append([new shaka.text.Cue(0, 100, 'Real subtitle')]);
|
||||
updateCaptions();
|
||||
|
||||
expect(textContainer.textContent).toBe('Real subtitle');
|
||||
});
|
||||
|
||||
it('shows example text while normal text visibility is off', () => {
|
||||
const config =
|
||||
shaka.util.PlayerConfiguration.createDefault().textDisplayer;
|
||||
|
||||
textDisplayer.configure(config);
|
||||
|
||||
textDisplayer.setTextStylePreview(config, 'Subtitles example');
|
||||
|
||||
const textContainer = videoContainer.querySelector('.shaka-text-container');
|
||||
expect(textContainer.textContent).toBe('Subtitles example');
|
||||
|
||||
textDisplayer.clearTextStylePreview();
|
||||
|
||||
expect(videoContainer.querySelector('.shaka-text-container')).toBe(null);
|
||||
});
|
||||
|
||||
it('replaces example text during repeated preview updates', () => {
|
||||
const config =
|
||||
shaka.util.PlayerConfiguration.createDefault().textDisplayer;
|
||||
|
||||
textDisplayer.configure(config);
|
||||
textDisplayer.setTextVisibility(true);
|
||||
|
||||
textDisplayer.setTextStylePreview(config, 'First example');
|
||||
|
||||
const textContainer = videoContainer.querySelector('.shaka-text-container');
|
||||
let cueElements = textContainer.querySelectorAll('div');
|
||||
expect(cueElements.length).toBe(1);
|
||||
expect(textContainer.textContent).toBe('First example');
|
||||
|
||||
textDisplayer.setTextStylePreview(config, 'Second example');
|
||||
|
||||
cueElements = textContainer.querySelectorAll('div');
|
||||
expect(cueElements.length).toBe(1);
|
||||
expect(textContainer.textContent).toBe('Second example');
|
||||
});
|
||||
|
||||
it('positions cue at top-left when positionArea=TOP_LEFT', () => {
|
||||
/** @type {!shaka.text.Cue} */
|
||||
const cue = new shaka.text.Cue(0, 100, 'Top-Left');
|
||||
|
||||
textDisplayer.setTextVisibility(true);
|
||||
const player = new shaka.Player();
|
||||
const config = player.getConfiguration().textDisplayer;
|
||||
const config =
|
||||
shaka.util.PlayerConfiguration.createDefault().textDisplayer;
|
||||
config.positionArea = shaka.config.PositionArea.TOP_LEFT;
|
||||
textDisplayer.configure(config);
|
||||
|
||||
|
||||
@@ -656,6 +656,230 @@ describe('UI', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('caption style preview', () => {
|
||||
/** @type {shaka.ui.Controls} */
|
||||
let controls;
|
||||
/** @type {!jasmine.Spy} */
|
||||
let setPreviewSpy;
|
||||
/** @type {!jasmine.Spy} */
|
||||
let clearPreviewSpy;
|
||||
|
||||
/**
|
||||
* @param {!HTMLElement} menu
|
||||
* @param {string} label
|
||||
* @return {!HTMLElement}
|
||||
*/
|
||||
function getStyleOption(menu, label) {
|
||||
const buttons = Array.from(
|
||||
menu.querySelectorAll('button[role="menuitemradio"]'));
|
||||
const button = buttons.find((button) => {
|
||||
const span = button.querySelector('span');
|
||||
return span && span.textContent == label;
|
||||
});
|
||||
expect(button).not.toBe(undefined);
|
||||
return /** @type {!HTMLElement} */(button);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {!shaka.extern.TextDisplayerConfiguration}
|
||||
*/
|
||||
function latestPreviewConfig() {
|
||||
const calls = setPreviewSpy.calls;
|
||||
expect(calls.count()).toBeGreaterThan(0);
|
||||
return /** @type {!shaka.extern.TextDisplayerConfiguration} */(
|
||||
calls.mostRecent().args[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {?shaka.Player} player
|
||||
*/
|
||||
function usePreviewTextDisplayer(player) {
|
||||
expect(player).not.toBe(null);
|
||||
const localPlayer = /** @type {!shaka.Player} */(player);
|
||||
/** @type {?} */
|
||||
const textDisplayer = {};
|
||||
setPreviewSpy = textDisplayer.setTextStylePreview =
|
||||
jasmine.createSpy('setTextStylePreview');
|
||||
clearPreviewSpy = textDisplayer.clearTextStylePreview =
|
||||
jasmine.createSpy('clearTextStylePreview');
|
||||
spyOn(localPlayer, 'getTextDisplayer').and.returnValue(
|
||||
/** @type {!shaka.extern.TextDisplayer} */(textDisplayer));
|
||||
}
|
||||
|
||||
it('does not require player or displayer preview methods', () => {
|
||||
const player = /** @type {?} */(new shaka.util.FakeEventTarget());
|
||||
player.getConfiguration = () => {
|
||||
return shaka.util.PlayerConfiguration.createDefault();
|
||||
};
|
||||
const localization = new shaka.ui.Localization('en');
|
||||
shaka.ui.Locales.addTo(localization);
|
||||
const preview = new shaka.ui.TextStylePreview(
|
||||
/** @type {!shaka.Player} */(player), localization);
|
||||
|
||||
expect(() => {
|
||||
preview.show();
|
||||
preview.update({'fontScaleFactor': 2});
|
||||
preview.reset();
|
||||
preview.hide();
|
||||
}).not.toThrow();
|
||||
|
||||
preview.release();
|
||||
});
|
||||
|
||||
it('updates and reverts font size on hover', async () => {
|
||||
const config = {
|
||||
controlPanelElements: [
|
||||
'captions-size',
|
||||
],
|
||||
customContextMenu: false,
|
||||
};
|
||||
const ui = await UiUtils.createUIThroughAPI(
|
||||
videoContainer, video, config);
|
||||
controls = ui.getControls();
|
||||
player = controls.getLocalPlayer();
|
||||
usePreviewTextDisplayer(player);
|
||||
player.configure('textDisplayer.fontScaleFactor', 1.25);
|
||||
controls.showUI();
|
||||
|
||||
const menu = UiUtils.getElementByClassName(
|
||||
videoContainer, 'shaka-text-positions');
|
||||
const button = UiUtils.getElementByClassName(
|
||||
videoContainer, 'shaka-caption-size-button');
|
||||
button.click();
|
||||
|
||||
expect(latestPreviewConfig().fontScaleFactor).toBe(1.25);
|
||||
|
||||
const largerOption = getStyleOption(menu, '200%');
|
||||
UiUtils.simulateEvent(largerOption, 'mouseenter');
|
||||
expect(latestPreviewConfig().fontScaleFactor).toBe(2);
|
||||
|
||||
UiUtils.simulateEvent(largerOption, 'mouseleave');
|
||||
expect(latestPreviewConfig().fontScaleFactor).toBe(1.25);
|
||||
|
||||
const selectedOption = getStyleOption(menu, '150%');
|
||||
selectedOption.click();
|
||||
expect(latestPreviewConfig().fontScaleFactor).toBe(1.5);
|
||||
|
||||
UiUtils.simulateEvent(selectedOption, 'mouseleave');
|
||||
expect(latestPreviewConfig().fontScaleFactor).toBe(1.5);
|
||||
|
||||
controls.hideSettingsMenus();
|
||||
});
|
||||
|
||||
it('updates and reverts text position on focus', async () => {
|
||||
const config = {
|
||||
controlPanelElements: [
|
||||
'captions-position',
|
||||
],
|
||||
customContextMenu: false,
|
||||
};
|
||||
const ui = await UiUtils.createUIThroughAPI(
|
||||
videoContainer, video, config);
|
||||
controls = ui.getControls();
|
||||
player = controls.getLocalPlayer();
|
||||
usePreviewTextDisplayer(player);
|
||||
controls.showUI();
|
||||
|
||||
const menu = UiUtils.getElementByClassName(
|
||||
videoContainer, 'shaka-text-positions');
|
||||
const button = UiUtils.getElementByClassName(
|
||||
videoContainer, 'shaka-caption-position-button');
|
||||
button.click();
|
||||
|
||||
expect(latestPreviewConfig().positionArea)
|
||||
.toBe(shaka.config.PositionArea.DEFAULT);
|
||||
|
||||
const topLeftOption = getStyleOption(menu, 'Top left');
|
||||
topLeftOption.dispatchEvent(new Event('focus'));
|
||||
expect(latestPreviewConfig().positionArea)
|
||||
.toBe(shaka.config.PositionArea.TOP_LEFT);
|
||||
|
||||
topLeftOption.dispatchEvent(new Event('blur'));
|
||||
expect(latestPreviewConfig().positionArea)
|
||||
.toBe(shaka.config.PositionArea.DEFAULT);
|
||||
});
|
||||
|
||||
it('keeps a committed text size as the preview baseline', async () => {
|
||||
const config = {
|
||||
controlPanelElements: [
|
||||
'captions-size',
|
||||
],
|
||||
customContextMenu: false,
|
||||
};
|
||||
const ui = await UiUtils.createUIThroughAPI(
|
||||
videoContainer, video, config);
|
||||
controls = ui.getControls();
|
||||
player = controls.getLocalPlayer();
|
||||
usePreviewTextDisplayer(player);
|
||||
player.configure('textDisplayer.fontScaleFactor', 1.25);
|
||||
controls.showUI();
|
||||
|
||||
const menu = UiUtils.getElementByClassName(
|
||||
videoContainer, 'shaka-text-positions');
|
||||
const button = UiUtils.getElementByClassName(
|
||||
videoContainer, 'shaka-caption-size-button');
|
||||
button.click();
|
||||
|
||||
const largerOption = getStyleOption(menu, '200%');
|
||||
UiUtils.simulateEvent(largerOption, 'mouseenter');
|
||||
expect(latestPreviewConfig().fontScaleFactor).toBe(2);
|
||||
|
||||
player.configure('textDisplayer.fontScaleFactor', 1.5);
|
||||
expect(latestPreviewConfig().fontScaleFactor).toBe(2);
|
||||
|
||||
UiUtils.simulateEvent(largerOption, 'mouseleave');
|
||||
expect(latestPreviewConfig().fontScaleFactor).toBe(1.5);
|
||||
});
|
||||
|
||||
it('hides the preview when a context menu closes', async () => {
|
||||
const config = {
|
||||
controlPanelElements: [],
|
||||
contextMenuElements: [
|
||||
'captions-size',
|
||||
],
|
||||
customContextMenu: true,
|
||||
};
|
||||
const ui = await UiUtils.createUIThroughAPI(
|
||||
videoContainer, video, config);
|
||||
controls = ui.getControls();
|
||||
player = controls.getLocalPlayer();
|
||||
usePreviewTextDisplayer(player);
|
||||
controls.showUI();
|
||||
|
||||
const controlsContainer = controls.getControlsContainer();
|
||||
UiUtils.simulateEvent(controlsContainer, 'contextmenu');
|
||||
|
||||
const captionsSizeButton = UiUtils.getElementByClassName(
|
||||
videoContainer, 'shaka-caption-size-button');
|
||||
captionsSizeButton.click();
|
||||
|
||||
UiUtils.simulateEvent(controlsContainer, 'click');
|
||||
expect(clearPreviewSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('hides the preview when the UI is reconfigured', async () => {
|
||||
const config = {
|
||||
controlPanelElements: [
|
||||
'captions-size',
|
||||
],
|
||||
customContextMenu: false,
|
||||
};
|
||||
const ui = await UiUtils.createUIThroughAPI(
|
||||
videoContainer, video, config);
|
||||
controls = ui.getControls();
|
||||
player = controls.getLocalPlayer();
|
||||
usePreviewTextDisplayer(player);
|
||||
controls.showUI();
|
||||
|
||||
const button = UiUtils.getElementByClassName(
|
||||
videoContainer, 'shaka-caption-size-button');
|
||||
button.click();
|
||||
|
||||
ui.configure('showUIAlways', true);
|
||||
expect(clearPreviewSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolutions menu', () => {
|
||||
/** @type {!HTMLElement} */
|
||||
let resolutionsMenu;
|
||||
|
||||
@@ -132,6 +132,7 @@ shaka.ui.ContextMenu = class extends shaka.ui.Element {
|
||||
*/
|
||||
closeMenu() {
|
||||
shaka.ui.Utils.setDisplay(this.contextMenu_, false);
|
||||
this.controls.hideTextStylePreview();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Vendored
+42
@@ -26,6 +26,7 @@ goog.require('shaka.ui.Localization');
|
||||
goog.require('shaka.ui.MediaSession');
|
||||
goog.require('shaka.ui.SeekBar');
|
||||
goog.require('shaka.ui.SkipAdButton');
|
||||
goog.require('shaka.ui.TextStylePreview');
|
||||
goog.require('shaka.ui.Utils');
|
||||
goog.require('shaka.ui.VRManager');
|
||||
goog.require('shaka.util.ArrayUtils');
|
||||
@@ -290,6 +291,8 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
|
||||
for (const menu of this.menus_) {
|
||||
shaka.ui.Utils.setDisplay(menu, /* visible= */ false);
|
||||
}
|
||||
this.dispatchEvent(new shaka.util.FakeEvent('submenuclose'));
|
||||
this.hideTextStylePreview();
|
||||
if (this.config_.enableTooltips) {
|
||||
this.controlsButtonPanel_.classList.add('shaka-tooltips-on');
|
||||
}
|
||||
@@ -331,6 +334,10 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
|
||||
/** @private {shaka.ui.Localization} */
|
||||
this.localization_ = shaka.ui.Controls.createLocalization_();
|
||||
|
||||
/** @private {?shaka.ui.TextStylePreview} */
|
||||
this.textStylePreview_ = new shaka.ui.TextStylePreview(
|
||||
this.localPlayer_, this.localization_);
|
||||
|
||||
/** @private {shaka.util.EventManager} */
|
||||
this.eventManager_ = new shaka.util.EventManager();
|
||||
|
||||
@@ -431,6 +438,9 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
|
||||
await document.exitPictureInPicture();
|
||||
}
|
||||
|
||||
this.textStylePreview_?.release();
|
||||
this.textStylePreview_ = null;
|
||||
|
||||
this.eventManager_?.release();
|
||||
this.eventManager_ = null;
|
||||
|
||||
@@ -568,6 +578,7 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
|
||||
*/
|
||||
configure(config) {
|
||||
this.config_ = config;
|
||||
this.hideTextStylePreview();
|
||||
|
||||
this.castProxy_.changeReceiverId(config.castReceiverAppId,
|
||||
config.castAndroidReceiverCompatible);
|
||||
@@ -912,6 +923,35 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
|
||||
for (const menu of this.contextMenus_) {
|
||||
shaka.ui.Utils.setDisplay(menu, /* visible= */ false);
|
||||
}
|
||||
this.hideTextStylePreview();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a temporary subtitle with the current text displayer style.
|
||||
*/
|
||||
showTextStylePreview() {
|
||||
this.textStylePreview_?.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the temporary subtitle style without changing player config.
|
||||
*
|
||||
* @param {!shaka.ui.TextStylePreview.Configuration=} config
|
||||
*/
|
||||
updateTextStylePreview(config = {}) {
|
||||
this.textStylePreview_?.update(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverts the temporary subtitle to the style captured when the menu opened.
|
||||
*/
|
||||
resetTextStylePreview() {
|
||||
this.textStylePreview_?.reset();
|
||||
}
|
||||
|
||||
/** Removes the temporary subtitle style preview. */
|
||||
hideTextStylePreview() {
|
||||
this.textStylePreview_?.hide();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1318,6 +1358,8 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
|
||||
this.videoContainer_.getElementsByClassName('shaka-settings-menu'));
|
||||
this.menus_.push(...Array.from(
|
||||
this.videoContainer_.getElementsByClassName('shaka-overflow-menu')));
|
||||
this.menus_.push(...Array.from(
|
||||
this.videoContainer_.getElementsByClassName('shaka-sub-menu')));
|
||||
|
||||
this.contextMenus_ = Array.from(
|
||||
this.videoContainer_.getElementsByClassName('shaka-context-menu'));
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"SKIP_TO_LIVE": "Skip ahead to live",
|
||||
"STATISTICS": "Statistics",
|
||||
"SUBTITLE_FORCED": "Forced",
|
||||
"SUBTITLES_EXAMPLE": "Subtitles example",
|
||||
"SURROUND": "Surround",
|
||||
"TOGGLE_STEREOSCOPIC": "Toggle stereoscopic",
|
||||
"UNDETERMINED_LANGUAGE": "Undetermined",
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
"SKIP_TO_LIVE": "الانتقال إلى بث مباشر",
|
||||
"STATISTICS": "الإحصاءات",
|
||||
"SUBTITLE_FORCED": "عرض إجباري",
|
||||
"SUBTITLES_EXAMPLE": "مثال على الترجمة",
|
||||
"SURROUND": "صوت محيطي",
|
||||
"TOGGLE_STEREOSCOPIC": "إيقاف العرض المجسّم أو تفعيله",
|
||||
"UNDETERMINED_LANGUAGE": "غير محدد",
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"SKIP_TO_LIVE": "Прапусціць і перайсці да жывога эфіра",
|
||||
"STATISTICS": "Статыстыка",
|
||||
"SUBTITLE_FORCED": "Субцітры прымусова",
|
||||
"SUBTITLES_EXAMPLE": "Прыклад субцітраў",
|
||||
"SURROUND": "Аб'ёмны гук",
|
||||
"TOGGLE_STEREOSCOPIC": "Уключыць або адключыць стэрэаскапічны рэжым",
|
||||
"UNDETERMINED_LANGUAGE": "Не пазначана",
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"SKIP_TO_LIVE": "Преминаване напред към предаването на живо",
|
||||
"STATISTICS": "Статистически данни",
|
||||
"SUBTITLE_FORCED": "Принудително",
|
||||
"SUBTITLES_EXAMPLE": "Примерни субтитри",
|
||||
"SURROUND": "Съраунд",
|
||||
"TOGGLE_STEREOSCOPIC": "Превключване на стереоскопичния режим",
|
||||
"UNDETERMINED_LANGUAGE": "Неопределено",
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"SKIP_TO_LIVE": "Passa a l'emissió en directe",
|
||||
"STATISTICS": "Estadístiques",
|
||||
"SUBTITLE_FORCED": "Forçat",
|
||||
"SUBTITLES_EXAMPLE": "Exemple de subtítols",
|
||||
"SURROUND": "So envoltant",
|
||||
"TOGGLE_STEREOSCOPIC": "Commuta l'opció estereoscòpica",
|
||||
"UNDETERMINED_LANGUAGE": "Sense especificar",
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"SKIP_TO_LIVE": "Přeskočit na živé vysílání",
|
||||
"STATISTICS": "Statistiky",
|
||||
"SUBTITLE_FORCED": "Vynuceno",
|
||||
"SUBTITLES_EXAMPLE": "Ukázka titulků",
|
||||
"SURROUND": "Prostorový zvuk",
|
||||
"TOGGLE_STEREOSCOPIC": "Přepnout stereoskopické zobrazení",
|
||||
"UNDETERMINED_LANGUAGE": "Neurčeno",
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"SKIP_TO_LIVE": "Gå videre til liveudsendelse",
|
||||
"STATISTICS": "Statistik",
|
||||
"SUBTITLE_FORCED": "Gennemtvunget",
|
||||
"SUBTITLES_EXAMPLE": "Eksempel på undertekster",
|
||||
"SURROUND": "Surroundsound",
|
||||
"TOGGLE_STEREOSCOPIC": "Slå stereoskopisk visning til/fra",
|
||||
"UNDETERMINED_LANGUAGE": "Ikke fastslået",
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
"SKIP_TO_LIVE": "Zum Live-Videostream wechseln",
|
||||
"STATISTICS": "Statistiken",
|
||||
"SUBTITLE_FORCED": "Erzwungen",
|
||||
"SUBTITLES_EXAMPLE": "Beispiel für Untertitel",
|
||||
"SUBTITLE_POSITION": "Untertitelposition",
|
||||
"SURROUND": "Surround",
|
||||
"TOGGLE_STEREOSCOPIC": "Einstellung „stereoskopisch“ ein‑/ausschalten",
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"SKIP_TO_LIVE": "Μετάβαση στη ζωντανή μετάδοση",
|
||||
"STATISTICS": "Στατιστικά στοιχεία",
|
||||
"SUBTITLE_FORCED": "Αναγκαστικό",
|
||||
"SUBTITLES_EXAMPLE": "Παράδειγμα υπότιτλων",
|
||||
"SURROUND": "Surround",
|
||||
"TOGGLE_STEREOSCOPIC": "Εναλλαγή στερεοσκοπικής",
|
||||
"UNDETERMINED_LANGUAGE": "Δεν έχει καθοριστεί",
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"SKIP_TO_LIVE": "Skip ahead to live",
|
||||
"STATISTICS": "Statistics",
|
||||
"SUBTITLE_FORCED": "Forced",
|
||||
"SUBTITLES_EXAMPLE": "Subtitles example",
|
||||
"SURROUND": "Surround",
|
||||
"TOGGLE_STEREOSCOPIC": "Toggle stereoscopic",
|
||||
"UNDETERMINED_LANGUAGE": "Undetermined",
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"SKIP_TO_LIVE": "[Šķîþ åĥéåð ţö ļîvé one two three four]",
|
||||
"STATISTICS": "[Šţåţîšţîçš one two]",
|
||||
"SUBTITLE_FORCED": "[Föŕçéð one]",
|
||||
"SUBTITLES_EXAMPLE": "[Šüƀţîţļéš éẋåɱƥļé one two]",
|
||||
"SURROUND": "[Šûŕŕöûñð one]",
|
||||
"TOGGLE_STEREOSCOPIC": "[Ţöĝĝļé šţéŕéöšçöþîç one two three]",
|
||||
"UNDETERMINED_LANGUAGE": "[Ûñðéţéŕmîñéð one two]",
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
"SKIP_PREVIOUS": "Previous",
|
||||
"STATISTICS": "Statistics",
|
||||
"SUBTITLE_FORCED": "Forced",
|
||||
"SUBTITLES_EXAMPLE": "Subtitles example",
|
||||
"SUBTITLE_POSITION": "Subtitle position",
|
||||
"SUBTITLE_SIZE": "Subtitle size",
|
||||
"SURROUND": "Surround",
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"SKIP_TO_LIVE": "Adelantar hasta la transmisión en vivo",
|
||||
"STATISTICS": "Estadísticas",
|
||||
"SUBTITLE_FORCED": "Forzado",
|
||||
"SUBTITLES_EXAMPLE": "Ejemplo de subtítulos",
|
||||
"SURROUND": "Sonido envolvente",
|
||||
"TOGGLE_STEREOSCOPIC": "Activar o desactivar el modo estereoscópico",
|
||||
"UNDETERMINED_LANGUAGE": "Sin especificar",
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
"SKIP_PREVIOUS": "Anterior",
|
||||
"STATISTICS": "Estadísticas",
|
||||
"SUBTITLE_FORCED": "Forzado",
|
||||
"SUBTITLES_EXAMPLE": "Ejemplo de subtítulos",
|
||||
"SUBTITLE_POSITION": "Posición de los subtítulos",
|
||||
"SUBTITLE_SIZE": "Tamaño de los subtítulos",
|
||||
"SURROUND": "Envolvente",
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"SKIP_TO_LIVE": "برای پخش مستقیم رد شوید و به جلو بروید",
|
||||
"STATISTICS": "آمار",
|
||||
"SUBTITLE_FORCED": "نمایش اجباری",
|
||||
"SUBTITLES_EXAMPLE": "نمونه زیرنویس",
|
||||
"SURROUND": "فراگیر",
|
||||
"TOGGLE_STEREOSCOPIC": "روشن/خاموش کردن برجستهنمایی",
|
||||
"UNDETERMINED_LANGUAGE": "نامعین",
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"SKIP_TO_LIVE": "Siirry suoraan lähetykseen",
|
||||
"STATISTICS": "Tilastot",
|
||||
"SUBTITLE_FORCED": "Pakotettu",
|
||||
"SUBTITLES_EXAMPLE": "Tekstitysesimerkki",
|
||||
"SURROUND": "Surround",
|
||||
"TOGGLE_STEREOSCOPIC": "Stereoskooppi päälle/pois",
|
||||
"UNDETERMINED_LANGUAGE": "Määrittämätön",
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"SKIP_TO_LIVE": "Lumaktaw sa live",
|
||||
"STATISTICS": "Mga Istatistika",
|
||||
"SUBTITLE_FORCED": "Sapilitan",
|
||||
"SUBTITLES_EXAMPLE": "Halimbawa ng subtitle",
|
||||
"SURROUND": "Surround",
|
||||
"TOGGLE_STEREOSCOPIC": "I-toggle sa stereoscopic",
|
||||
"UNDETERMINED_LANGUAGE": "Hindi tinukoy",
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
"SKIP_PREVIOUS": "Précédente",
|
||||
"STATISTICS": "Statistiques",
|
||||
"SUBTITLE_FORCED": "Forcé",
|
||||
"SUBTITLES_EXAMPLE": "Exemple de sous-titres",
|
||||
"SUBTITLE_POSITION": "Position des sous-titres",
|
||||
"SUBTITLE_SIZE": "Taille des sous-titres",
|
||||
"SURROUND": "Surround",
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"SKIP_TO_LIVE": "सीधा लाइव प्रसारण देखने के लिए छोड़ें",
|
||||
"STATISTICS": "आंकड़े",
|
||||
"SUBTITLE_FORCED": "फ़ोर्स्ड",
|
||||
"SUBTITLES_EXAMPLE": "उपशीर्षक उदाहरण",
|
||||
"SURROUND": "सराउंड",
|
||||
"TOGGLE_STEREOSCOPIC": "स्टीरियोस्कोपिक को टॉगल करें",
|
||||
"UNDETERMINED_LANGUAGE": "जानकारी नहीं है",
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"SKIP_TO_LIVE": "Preskoči na uživo",
|
||||
"STATISTICS": "Statistika",
|
||||
"SUBTITLE_FORCED": "Prisilno",
|
||||
"SUBTITLES_EXAMPLE": "Primjer titlova",
|
||||
"SURROUND": "Surround",
|
||||
"TOGGLE_STEREOSCOPIC": "Prebaci stereoskopski prikaz",
|
||||
"UNDETERMINED_LANGUAGE": "Neodređeno",
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"SKIP_TO_LIVE": "Ugrás az élő közvetítésre",
|
||||
"STATISTICS": "Statisztikák",
|
||||
"SUBTITLE_FORCED": "Kényszerített",
|
||||
"SUBTITLES_EXAMPLE": "Feliratminta",
|
||||
"SURROUND": "Térhatású hangzás",
|
||||
"TOGGLE_STEREOSCOPIC": "Sztereó térhatás be-/kikapcsolása",
|
||||
"UNDETERMINED_LANGUAGE": "Meghatározatlan",
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"SKIP_TO_LIVE": "Langsung ke live streaming",
|
||||
"STATISTICS": "Statistik",
|
||||
"SUBTITLE_FORCED": "Dipaksa",
|
||||
"SUBTITLES_EXAMPLE": "Contoh subtitle",
|
||||
"SURROUND": "Surround",
|
||||
"TOGGLE_STEREOSCOPIC": "Aktifkan/nonaktifkan stereoskop",
|
||||
"UNDETERMINED_LANGUAGE": "Belum ditentukan",
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"SKIP_TO_LIVE": "Passa alla trasmissione dal vivo",
|
||||
"STATISTICS": "Statistiche",
|
||||
"SUBTITLE_FORCED": "Forzata",
|
||||
"SUBTITLES_EXAMPLE": "Esempio di sottotitoli",
|
||||
"SURROUND": "Surround",
|
||||
"TOGGLE_STEREOSCOPIC": "Pulsante di attivazione/disattivazione stereoscopico",
|
||||
"UNDETERMINED_LANGUAGE": "Indeterminata",
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"SKIP_TO_LIVE": "דילוג קדימה לשידור הישיר",
|
||||
"STATISTICS": "נתונים סטטיסטיים",
|
||||
"SUBTITLE_FORCED": "מאולץ",
|
||||
"SUBTITLES_EXAMPLE": "דוגמה לכתוביות",
|
||||
"SURROUND": "סראונד",
|
||||
"TOGGLE_STEREOSCOPIC": "החלפת מצב סטריאוסקופי",
|
||||
"UNDETERMINED_LANGUAGE": "לא ידוע",
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"SKIP_TO_LIVE": "ライブ配信までスキップ",
|
||||
"STATISTICS": "統計情報",
|
||||
"SUBTITLE_FORCED": "強制",
|
||||
"SUBTITLES_EXAMPLE": "字幕の例",
|
||||
"SURROUND": "サラウンド",
|
||||
"TOGGLE_STEREOSCOPIC": "立体画像を切り替える",
|
||||
"UNDETERMINED_LANGUAGE": "不明",
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"SKIP_TO_LIVE": "라이브 동영상으로 건너뛰기",
|
||||
"STATISTICS": "통계",
|
||||
"SUBTITLE_FORCED": "강제",
|
||||
"SUBTITLES_EXAMPLE": "자막 예시",
|
||||
"SURROUND": "서라운드",
|
||||
"TOGGLE_STEREOSCOPIC": "입체 보기 전환",
|
||||
"UNDETERMINED_LANGUAGE": "미정",
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"SKIP_TO_LIVE": "Praleisti ir transliuoti tiesiogiai",
|
||||
"STATISTICS": "Statistika",
|
||||
"SUBTITLE_FORCED": "Priverstinis",
|
||||
"SUBTITLES_EXAMPLE": "Subtitrų pavyzdys",
|
||||
"SURROUND": "Erdvinis garsas",
|
||||
"TOGGLE_STEREOSCOPIC": "Perjungti stereoskopinį vaizdą",
|
||||
"UNDETERMINED_LANGUAGE": "Nenustatyta",
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"SKIP_TO_LIVE": "Pāriet uz tiešraidi",
|
||||
"STATISTICS": "Statistika",
|
||||
"SUBTITLE_FORCED": "Piespiedu kārtā",
|
||||
"SUBTITLES_EXAMPLE": "Subtitru piemērs",
|
||||
"SURROUND": "Telpiskā skaņa",
|
||||
"TOGGLE_STEREOSCOPIC": "Pārslēgt stereoskopisko režīmu",
|
||||
"UNDETERMINED_LANGUAGE": "Nenoteikts",
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
"SKIP_TO_LIVE": "Doorgaan naar live",
|
||||
"STATISTICS": "Statistieken",
|
||||
"SUBTITLE_FORCED": "Afgedwongen",
|
||||
"SUBTITLES_EXAMPLE": "Voorbeeld van ondertitels",
|
||||
"SUBTITLE_POSITION": "Positie van ondertitels",
|
||||
"SURROUND": "Surround",
|
||||
"TOGGLE_STEREOSCOPIC": "Stereoscopisch aan-/uitzetten",
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
"SKIP_TO_LIVE": "Hopp frem til direktesending",
|
||||
"STATISTICS": "Statistikk",
|
||||
"SUBTITLE_FORCED": "Tvungent",
|
||||
"SUBTITLES_EXAMPLE": "Eksempel på undertekster",
|
||||
"SURROUND": "Surround-lyd",
|
||||
"TOGGLE_STEREOSCOPIC": "Slå stereoskopisk modus av/på",
|
||||
"UNDETERMINED_LANGUAGE": "Ubestemt",
|
||||
|
||||
+2
-1
@@ -35,8 +35,9 @@
|
||||
"SKIP_TO_LIVE": "Anar al dirècte",
|
||||
"STATISTICS": "Estatisticas",
|
||||
"SUBTITLE_FORCED": "Forçat",
|
||||
"SUBTITLES_EXAMPLE": "Exemple de sostítols",
|
||||
"UNDETERMINED_LANGUAGE": "Lenga indeterminada",
|
||||
"UNMUTE": "Desamudir",
|
||||
"UNRECOGNIZED_LANGUAGE": "Non reconeguda",
|
||||
"VOLUME": "Volum",
|
||||
"VOLUME": "Volum"
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
"SKIP_TO_LIVE": "Przejdź do transmisji na żywo",
|
||||
"STATISTICS": "Statystyki",
|
||||
"SUBTITLE_FORCED": "Wymuszone",
|
||||
"SUBTITLES_EXAMPLE": "Przykład napisów",
|
||||
"SUBTITLE_POSITION": "Pozycja napisów",
|
||||
"SUBTITLE_SIZE": "Rozmiar napisów",
|
||||
"SURROUND": "Przestrzenny",
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
"SKIP_TO_LIVE": "Pular para transmissão ao vivo",
|
||||
"STATISTICS": "Estatísticas",
|
||||
"SUBTITLE_FORCED": "Exibição forçada",
|
||||
"SUBTITLES_EXAMPLE": "Exemplo de legendas",
|
||||
"SURROUND": "Surround",
|
||||
"TOGGLE_STEREOSCOPIC": "Alternar imagem estereoscópica",
|
||||
"UNDETERMINED_LANGUAGE": "Indeterminado",
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
"SKIP_TO_LIVE": "Avançar para o direto",
|
||||
"STATISTICS": "Estatísticas",
|
||||
"SUBTITLE_FORCED": "Forçada",
|
||||
"SUBTITLES_EXAMPLE": "Exemplo de legendas",
|
||||
"SURROUND": "Surround",
|
||||
"TOGGLE_STEREOSCOPIC": "Ativar/desativar estereoscópico",
|
||||
"UNDETERMINED_LANGUAGE": "Indeterminado",
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"SKIP_TO_LIVE": "Accesează difuzarea live",
|
||||
"STATISTICS": "Statistici",
|
||||
"SUBTITLE_FORCED": "Forțat",
|
||||
"SUBTITLES_EXAMPLE": "Exemplu de subtitrări",
|
||||
"SURROUND": "Surround",
|
||||
"TOGGLE_STEREOSCOPIC": "Comută la stereoscopic",
|
||||
"UNDETERMINED_LANGUAGE": "Nestabilită",
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
"SKIP_TO_LIVE": "Пропустить и перейти к прямой трансляции",
|
||||
"STATISTICS": "Статистика",
|
||||
"SUBTITLE_FORCED": "Субтитры принудительно",
|
||||
"SUBTITLES_EXAMPLE": "Пример субтитров",
|
||||
"SURROUND": "Объемный звук",
|
||||
"TOGGLE_STEREOSCOPIC": "Включить или отключить стереоскопический режим",
|
||||
"UNDETERMINED_LANGUAGE": "Не указано",
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"SKIP_TO_LIVE": " ",
|
||||
"STATISTICS": " ",
|
||||
"SUBTITLE_FORCED": " ",
|
||||
"SUBTITLES_EXAMPLE": "Subtitles example",
|
||||
"SURROUND": "",
|
||||
"TOGGLE_STEREOSCOPIC": "",
|
||||
"UNDETERMINED_LANGUAGE": " ",
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"SKIP_TO_LIVE": "Preskočiť na živé vysielanie",
|
||||
"STATISTICS": "Štatistiky",
|
||||
"SUBTITLE_FORCED": "Vynútené",
|
||||
"SUBTITLES_EXAMPLE": "Príklad titulkov",
|
||||
"SURROUND": "Priestorový zvuk",
|
||||
"TOGGLE_STEREOSCOPIC": "Prepnúť na stereoskopický režim",
|
||||
"UNDETERMINED_LANGUAGE": "Neurčené",
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"SKIP_TO_LIVE": "Preskoči naprej na oddajanje v živo",
|
||||
"STATISTICS": "Statistični podatki",
|
||||
"SUBTITLE_FORCED": "Vsiljeno",
|
||||
"SUBTITLES_EXAMPLE": "Primer podnapisov",
|
||||
"SURROUND": "Prostorski zvok",
|
||||
"TOGGLE_STEREOSCOPIC": "Preklop stereoskopskega načina",
|
||||
"UNDETERMINED_LANGUAGE": "Nedoločen",
|
||||
|
||||
@@ -223,6 +223,10 @@
|
||||
"description": "Label used to identify a subtitle track that is forced to be shown.",
|
||||
"message": "Forced"
|
||||
},
|
||||
"SUBTITLES_EXAMPLE": {
|
||||
"description": "Example subtitle text shown while previewing subtitle style settings when no subtitle is currently visible.",
|
||||
"message": "Subtitles example"
|
||||
},
|
||||
"SUBTITLE_POSITION": {
|
||||
"description": "Label for a button used to indicate a subtitle position.",
|
||||
"message": "Subtitle position"
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"SKIP_TO_LIVE": "Прескочи на емитовање уживо",
|
||||
"STATISTICS": "Статистика",
|
||||
"SUBTITLE_FORCED": "Принудно",
|
||||
"SUBTITLES_EXAMPLE": "Пример титлов",
|
||||
"SURROUND": "Просторни звук",
|
||||
"TOGGLE_STEREOSCOPIC": "Укључи/искључи стерео",
|
||||
"UNDETERMINED_LANGUAGE": "Неодређено",
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"SKIP_TO_LIVE": "Fortsätt direkt till direktsändning",
|
||||
"STATISTICS": "Statistik",
|
||||
"SUBTITLE_FORCED": "Framtvingad",
|
||||
"SUBTITLES_EXAMPLE": "Exempel på undertexter",
|
||||
"SURROUND": "Surroundljud",
|
||||
"TOGGLE_STEREOSCOPIC": "Aktivera/inaktivera stereoskopiska bilder",
|
||||
"UNDETERMINED_LANGUAGE": "Obestämt",
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"SKIP_TO_LIVE": "ข้ามไปที่การถ่ายทอดสด",
|
||||
"STATISTICS": "สถิติ",
|
||||
"SUBTITLE_FORCED": "บังคับ",
|
||||
"SUBTITLES_EXAMPLE": "ตัวอย่างคำบรรยาย",
|
||||
"SURROUND": "เซอร์ราวด์",
|
||||
"TOGGLE_STEREOSCOPIC": "เปิด/ปิดฟีเจอร์สามมิติ",
|
||||
"UNDETERMINED_LANGUAGE": "ไม่กำหนด",
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"SKIP_TO_LIVE": "Canlı yayına atla",
|
||||
"STATISTICS": "İstatistikler",
|
||||
"SUBTITLE_FORCED": "Zorunlu",
|
||||
"SUBTITLES_EXAMPLE": "Altyazı örneği",
|
||||
"SURROUND": "Surround",
|
||||
"TOGGLE_STEREOSCOPIC": "Stereoskopik modu aç/kapat",
|
||||
"UNDETERMINED_LANGUAGE": "Belirsiz",
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"SKIP_TO_LIVE": "Перейти до прямої трансляції",
|
||||
"STATISTICS": "Статистика",
|
||||
"SUBTITLE_FORCED": "Обов’язкові субтитри",
|
||||
"SUBTITLES_EXAMPLE": "Приклад субтитрів",
|
||||
"SURROUND": "Об’ємний звук",
|
||||
"TOGGLE_STEREOSCOPIC": "Увімкнути або вимкнути стереоскопічний режим",
|
||||
"UNDETERMINED_LANGUAGE": "Не визначено",
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"SKIP_TO_LIVE": "Tua tới chương trình phát trực tiếp",
|
||||
"STATISTICS": "Số liệu thống kê",
|
||||
"SUBTITLE_FORCED": "Buộc hiển thị",
|
||||
"SUBTITLES_EXAMPLE": "Ví dụ phụ đề",
|
||||
"SURROUND": "Vòm",
|
||||
"TOGGLE_STEREOSCOPIC": "Bật/tắt chế độ hình nổi",
|
||||
"UNDETERMINED_LANGUAGE": "Chưa xác định",
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"SKIP_TO_LIVE": "跳至当前直播",
|
||||
"STATISTICS": "統計資料",
|
||||
"SUBTITLE_FORCED": "強制",
|
||||
"SUBTITLES_EXAMPLE": "字幕範例",
|
||||
"SURROUND": "環迴音效",
|
||||
"TOGGLE_STEREOSCOPIC": "切換立體視覺",
|
||||
"UNDETERMINED_LANGUAGE": "不明",
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"SKIP_TO_LIVE": "跳至当前直播",
|
||||
"STATISTICS": "統計資料",
|
||||
"SUBTITLE_FORCED": "強制顯示",
|
||||
"SUBTITLES_EXAMPLE": "字幕範例",
|
||||
"SURROUND": "環場音效",
|
||||
"TOGGLE_STEREOSCOPIC": "切換立體影像",
|
||||
"UNDETERMINED_LANGUAGE": "不明",
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"SKIP_TO_LIVE": "跳至当前直播",
|
||||
"STATISTICS": "统计信息",
|
||||
"SUBTITLE_FORCED": "已强制显示",
|
||||
"SUBTITLES_EXAMPLE": "字幕示例",
|
||||
"SURROUND": "环绕声",
|
||||
"TOGGLE_STEREOSCOPIC": "切换立体声",
|
||||
"UNDETERMINED_LANGUAGE": "未确定",
|
||||
|
||||
@@ -40,6 +40,9 @@ shaka.ui.SettingsMenu = class extends shaka.ui.Element {
|
||||
|
||||
this.addMenu_();
|
||||
|
||||
/** @private {boolean} */
|
||||
this.isMenuOpened_ = false;
|
||||
|
||||
this.inOverflowMenu_();
|
||||
|
||||
this.eventManager.listen(this.button, 'click', () => {
|
||||
@@ -55,6 +58,23 @@ shaka.ui.SettingsMenu = class extends shaka.ui.Element {
|
||||
/** @private {MutationObserver} */
|
||||
this.mutationObserver_ = null;
|
||||
|
||||
/** @private {MutationObserver} */
|
||||
this.menuMutationObserver_ = null;
|
||||
|
||||
if (window.MutationObserver) {
|
||||
this.menuMutationObserver_ = new MutationObserver(() => {
|
||||
if (this.menu.classList.contains('shaka-hidden')) {
|
||||
this.notifyMenuClose_();
|
||||
} else {
|
||||
this.notifyMenuOpen_();
|
||||
}
|
||||
});
|
||||
this.menuMutationObserver_.observe(this.menu, {
|
||||
attributes: true,
|
||||
attributeFilter: ['class'],
|
||||
});
|
||||
}
|
||||
|
||||
const resize = () => this.adjustCustomStyle_();
|
||||
|
||||
// Use ResizeObserver if available, fallback to window resize event
|
||||
@@ -77,6 +97,10 @@ shaka.ui.SettingsMenu = class extends shaka.ui.Element {
|
||||
this.mutationObserver_.disconnect();
|
||||
this.mutationObserver_ = null;
|
||||
}
|
||||
if (this.menuMutationObserver_) {
|
||||
this.menuMutationObserver_.disconnect();
|
||||
this.menuMutationObserver_ = null;
|
||||
}
|
||||
super.release();
|
||||
}
|
||||
|
||||
@@ -162,6 +186,7 @@ shaka.ui.SettingsMenu = class extends shaka.ui.Element {
|
||||
this.backIcon_.use(shaka.ui.Enums.MaterialDesignSVGIcons['BACK']);
|
||||
|
||||
this.eventManager.listen(this.menu, 'click', () => {
|
||||
this.notifyMenuClose_();
|
||||
this.controls.dispatchEvent(new shaka.util.FakeEvent('submenuclose'));
|
||||
shaka.ui.Utils.setDisplay(this.menu, false);
|
||||
shaka.ui.Utils.setDisplay(this.parent, true);
|
||||
@@ -177,6 +202,9 @@ shaka.ui.SettingsMenu = class extends shaka.ui.Element {
|
||||
if (m.type === 'attributes' && m.attributeName === 'class') {
|
||||
const newHidden = this.parent.classList.contains('shaka-hidden');
|
||||
if (newHidden && prevHidden != newHidden) {
|
||||
if (!this.menu.classList.contains('shaka-hidden')) {
|
||||
this.notifyMenuClose_();
|
||||
}
|
||||
this.controls.dispatchEvent(
|
||||
new shaka.util.FakeEvent('submenuclose'));
|
||||
shaka.ui.Utils.setDisplay(this.menu, false);
|
||||
@@ -209,10 +237,12 @@ shaka.ui.SettingsMenu = class extends shaka.ui.Element {
|
||||
this.controls.dispatchEvent(new shaka.util.FakeEvent('submenuopen'));
|
||||
}
|
||||
shaka.ui.Utils.setDisplay(this.menu, true);
|
||||
this.notifyMenuOpen_();
|
||||
shaka.ui.Utils.focusOnTheChosenItem(this.menu);
|
||||
this.adjustCustomStyle_();
|
||||
this.button.setAttribute('aria-expanded', 'true');
|
||||
} else {
|
||||
this.notifyMenuClose_();
|
||||
shaka.ui.Utils.setDisplay(this.menu, false);
|
||||
this.button.setAttribute('aria-expanded', 'false');
|
||||
this.button.focus();
|
||||
@@ -220,6 +250,31 @@ shaka.ui.SettingsMenu = class extends shaka.ui.Element {
|
||||
}
|
||||
}
|
||||
|
||||
/** @private */
|
||||
notifyMenuOpen_() {
|
||||
if (this.isMenuOpened_) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isMenuOpened_ = true;
|
||||
this.onMenuOpen();
|
||||
}
|
||||
|
||||
/** @private */
|
||||
notifyMenuClose_() {
|
||||
if (!this.isMenuOpened_) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isMenuOpened_ = false;
|
||||
this.onMenuClose();
|
||||
}
|
||||
|
||||
/** @protected */
|
||||
onMenuOpen() {}
|
||||
|
||||
/** @protected */
|
||||
onMenuClose() {}
|
||||
|
||||
/**
|
||||
* @private
|
||||
|
||||
@@ -135,12 +135,28 @@ shaka.ui.TextPosition = class extends shaka.ui.SettingsMenu {
|
||||
this.updateTextPositionSelection_();
|
||||
});
|
||||
|
||||
const previewConfig = {'positionArea': position};
|
||||
shaka.ui.Utils.addHoverAndFocusListeners(
|
||||
this.eventManager, button,
|
||||
() => this.controls.updateTextStylePreview(previewConfig),
|
||||
() => this.controls.resetTextStylePreview());
|
||||
|
||||
this.menu.appendChild(button);
|
||||
}
|
||||
this.updateTextPositionSelection_();
|
||||
shaka.ui.Utils.focusOnTheChosenItem(this.menu);
|
||||
}
|
||||
|
||||
/** @override */
|
||||
onMenuOpen() {
|
||||
this.controls.showTextStylePreview();
|
||||
}
|
||||
|
||||
/** @override */
|
||||
onMenuClose() {
|
||||
this.controls.hideTextStylePreview();
|
||||
}
|
||||
|
||||
/** @private */
|
||||
updateTextPositionSelection_() {
|
||||
// Remove the old checkmark icon and related tags and classes if it exists.
|
||||
|
||||
@@ -133,12 +133,28 @@ shaka.ui.TextSize = class extends shaka.ui.SettingsMenu {
|
||||
this.updateTextSizeSelection_();
|
||||
});
|
||||
|
||||
const previewConfig = {'fontScaleFactor': fontScaleFactor};
|
||||
shaka.ui.Utils.addHoverAndFocusListeners(
|
||||
this.eventManager, button,
|
||||
() => this.controls.updateTextStylePreview(previewConfig),
|
||||
() => this.controls.resetTextStylePreview());
|
||||
|
||||
this.menu.appendChild(button);
|
||||
}
|
||||
this.updateTextSizeSelection_();
|
||||
shaka.ui.Utils.focusOnTheChosenItem(this.menu);
|
||||
}
|
||||
|
||||
/** @override */
|
||||
onMenuOpen() {
|
||||
this.controls.showTextStylePreview();
|
||||
}
|
||||
|
||||
/** @override */
|
||||
onMenuClose() {
|
||||
this.controls.hideTextStylePreview();
|
||||
}
|
||||
|
||||
/** @private */
|
||||
updateTextSizeSelection_() {
|
||||
// Remove the old checkmark icon and related tags and classes if it exists.
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
/*! @license
|
||||
* Shaka Player
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
|
||||
goog.provide('shaka.ui.TextStylePreview');
|
||||
|
||||
goog.require('shaka.ui.Locales');
|
||||
goog.require('shaka.ui.Localization');
|
||||
goog.require('shaka.util.EventManager');
|
||||
goog.requireType('shaka.config.PositionArea');
|
||||
goog.requireType('shaka.Player');
|
||||
|
||||
|
||||
/**
|
||||
* Manages the temporary subtitle style preview shown while subtitle style
|
||||
* settings are hovered or focused.
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
shaka.ui.TextStylePreview = class {
|
||||
/**
|
||||
* @param {!shaka.Player} player
|
||||
* @param {!shaka.ui.Localization} localization
|
||||
*/
|
||||
constructor(player, localization) {
|
||||
/** @private {?shaka.Player} */
|
||||
this.player_ = player;
|
||||
|
||||
/** @private {?shaka.ui.Localization} */
|
||||
this.localization_ = localization;
|
||||
|
||||
/** @private {?shaka.extern.TextDisplayerConfiguration} */
|
||||
this.baseConfig_ = null;
|
||||
|
||||
/** @private {!shaka.ui.TextStylePreview.Configuration} */
|
||||
this.previewConfig_ = {};
|
||||
|
||||
/** @private {boolean} */
|
||||
this.shown_ = false;
|
||||
|
||||
/** @private {?shaka.util.EventManager} */
|
||||
this.eventManager_ = new shaka.util.EventManager();
|
||||
|
||||
this.eventManager_.listen(player, 'configurationchanged', () => {
|
||||
this.updateBaseConfig_();
|
||||
});
|
||||
|
||||
this.eventManager_.listenMulti(
|
||||
localization,
|
||||
[
|
||||
shaka.ui.Localization.LOCALE_UPDATED,
|
||||
shaka.ui.Localization.LOCALE_CHANGED,
|
||||
], () => {
|
||||
this.apply_();
|
||||
});
|
||||
}
|
||||
|
||||
/** Releases all resources owned by this preview. */
|
||||
release() {
|
||||
this.hide();
|
||||
this.eventManager_?.release();
|
||||
this.eventManager_ = null;
|
||||
this.player_ = null;
|
||||
this.localization_ = null;
|
||||
}
|
||||
|
||||
/** Shows a temporary subtitle with the current text displayer style. */
|
||||
show() {
|
||||
if (!this.player_) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.shown_ = true;
|
||||
this.previewConfig_ = {};
|
||||
this.updateBaseConfig_();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the temporary subtitle style without changing player config.
|
||||
*
|
||||
* @param {!shaka.ui.TextStylePreview.Configuration=} config
|
||||
*/
|
||||
update(config = {}) {
|
||||
if (!this.player_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.shown_) {
|
||||
this.show();
|
||||
}
|
||||
|
||||
this.previewConfig_ = Object.assign({}, config);
|
||||
this.apply_();
|
||||
}
|
||||
|
||||
/** Reverts the temporary subtitle to the style captured when shown. */
|
||||
reset() {
|
||||
if (!this.shown_) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.previewConfig_ = {};
|
||||
this.apply_();
|
||||
}
|
||||
|
||||
/** Removes the temporary subtitle style preview. */
|
||||
hide() {
|
||||
if (!this.shown_) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.shown_ = false;
|
||||
this.baseConfig_ = null;
|
||||
this.previewConfig_ = {};
|
||||
const displayer = this.getTextDisplayer_();
|
||||
if (displayer &&
|
||||
typeof displayer['clearTextStylePreview'] == 'function') {
|
||||
displayer['clearTextStylePreview']();
|
||||
}
|
||||
}
|
||||
|
||||
/** @private */
|
||||
updateBaseConfig_() {
|
||||
if (!this.shown_ || !this.player_) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.baseConfig_ = this.getCurrentTextDisplayerConfig_();
|
||||
this.apply_();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {!shaka.extern.TextDisplayerConfiguration}
|
||||
* @private
|
||||
*/
|
||||
getCurrentTextDisplayerConfig_() {
|
||||
const player = /** @type {!shaka.Player} */(this.player_);
|
||||
return /** @type {!shaka.extern.TextDisplayerConfiguration} */(
|
||||
Object.assign({}, player.getConfiguration().textDisplayer));
|
||||
}
|
||||
|
||||
/** @private */
|
||||
apply_() {
|
||||
if (!this.shown_ || !this.player_ || !this.baseConfig_) {
|
||||
return;
|
||||
}
|
||||
|
||||
const previewConfig =
|
||||
/** @type {!shaka.extern.TextDisplayerConfiguration} */(
|
||||
Object.assign({}, this.baseConfig_, this.previewConfig_));
|
||||
const displayer = this.getTextDisplayer_();
|
||||
if (displayer && typeof displayer['setTextStylePreview'] == 'function') {
|
||||
displayer['setTextStylePreview'](
|
||||
previewConfig, this.getLocalizedExampleText_());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {?}
|
||||
* @private
|
||||
*/
|
||||
getTextDisplayer_() {
|
||||
const player = /** @type {?} */(this.player_);
|
||||
if (!player || typeof player.getTextDisplayer != 'function') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return player.getTextDisplayer();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
* @private
|
||||
*/
|
||||
getLocalizedExampleText_() {
|
||||
if (!this.localization_) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return this.localization_.resolve(
|
||||
shaka.ui.Locales.Ids.SUBTITLES_EXAMPLE);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* fontScaleFactor: (number|undefined),
|
||||
* positionArea: (shaka.config.PositionArea|undefined),
|
||||
* }}
|
||||
*
|
||||
* @description
|
||||
* Text displayer fields that the style preview can temporarily override.
|
||||
*/
|
||||
shaka.ui.TextStylePreview.Configuration;
|
||||
@@ -11,6 +11,7 @@ goog.require('goog.asserts');
|
||||
goog.require('shaka.ui.Enums');
|
||||
goog.require('shaka.ui.Icon');
|
||||
goog.require('shaka.util.Mp4Parser');
|
||||
goog.requireType('shaka.util.EventManager');
|
||||
|
||||
|
||||
shaka.ui.Utils = class {
|
||||
@@ -96,6 +97,20 @@ shaka.ui.Utils = class {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {!shaka.util.EventManager} eventManager
|
||||
* @param {!Element} element
|
||||
* @param {function()} onHoverOrFocus
|
||||
* @param {function()} onLeaveOrBlur
|
||||
*/
|
||||
static addHoverAndFocusListeners(
|
||||
eventManager, element, onHoverOrFocus, onLeaveOrBlur) {
|
||||
eventManager.listen(element, 'mouseenter', onHoverOrFocus);
|
||||
eventManager.listen(element, 'mouseleave', onLeaveOrBlur);
|
||||
eventManager.listen(element, 'focus', onHoverOrFocus);
|
||||
eventManager.listen(element, 'blur', onLeaveOrBlur);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Builds a time string, e.g., 01:04:23, from |displayTime|.
|
||||
|
||||
Reference in New Issue
Block a user