/*! @license
* Shaka Player
* Copyright 2016 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
goog.provide('shaka.ui.VideoTypeSelection');
goog.require('shaka.ui.Controls');
goog.require('shaka.ui.Enums');
goog.require('shaka.ui.LanguageUtils');
goog.require('shaka.ui.Locales');
goog.require('shaka.ui.OverflowMenu');
goog.require('shaka.ui.SettingsMenu');
goog.require('shaka.ui.Utils');
goog.require('shaka.util.Dom');
goog.requireType('shaka.ui.Controls');
/**
* @extends {shaka.ui.SettingsMenu}
* @final
* @export
*/
shaka.ui.VideoTypeSelection = class extends shaka.ui.SettingsMenu {
/**
* @param {!HTMLElement} parent
* @param {!shaka.ui.Controls} controls
*/
constructor(parent, controls) {
super(parent, controls,
shaka.ui.Enums.MaterialDesignSVGIcons['VIDEO_TYPE']);
this.button.classList.add('shaka-playbackrate-button');
this.menu.classList.add('shaka-video-type');
this.button.classList.add('shaka-tooltip-status');
this.eventManager.listenMulti(
this.player,
[
'loading',
'loaded',
'unloading',
'variantchanged',
'trackschanged',
'abrstatuschanged',
'adaptation',
], () => {
this.updateVideoRoles_();
});
this.updateLocalizedStrings();
}
/** @override */
updateLocalizedStrings() {
const LocIds = shaka.ui.Locales.Ids;
this.backButton.ariaLabel = this.localization.resolve(LocIds.BACK);
const label = this.localization.resolve(LocIds.VIDEO_TYPE);
this.button.ariaLabel = label;
this.nameSpan.textContent = label;
this.backSpan.textContent = label;
this.updateVideoRoles_();
}
/** @override */
checkAvailability() {
this.updateVideoRoles_();
}
/**
* @private
*/
updateVideoRoles_() {
// Remove old shaka-resolutions
shaka.ui.Utils.clearMenuKeepingBackButton(this.menu);
/** @type {!Array<shaka.extern.VideoTrack>} */
const tracks = this.player.getVideoTracks() || [];
const selectedTrack = tracks.find((track) => track.active);
const isSelectedTrack = (role, language, label) => {
if (!selectedTrack) {
return false;
}
if (language != selectedTrack.language) {
return false;
}
if (label != selectedTrack.label) {
return false;
}
if (role == '' && !selectedTrack.roles.length) {
return true;
}
return selectedTrack.roles.includes(role);
};
/**
* @type {!Map<string, {role: string, language: string, label: ?string}>}
*/
const options = new Map();
for (const track of tracks) {
const roles = track.roles.slice();
if (!roles.length) {
roles.push('');
}
for (const role of roles) {
const key = role + '\0' + track.language + '\0' + track.label;
if (!options.has(key)) {
options.set(key, {
role,
language: track.language,
label: track.label,
});
}
}
}
// Only surface the video language in the label when it actually helps
// distinguish the options (i.e. more than one option has a real,
// non-'und' language). Otherwise, keep the previous role-only behavior.
const realLanguages = new Set();
const distinctRoles = new Set();
for (const option of options.values()) {
if (option.language) {
realLanguages.add(option.language);
}
if (option.label) {
realLanguages.add(option.label);
}
distinctRoles.add(option.role);
}
const showLanguage = realLanguages.size > 1;
// If every option shares the same role, the role adds no information
// (it's identical on every button); show the language alone instead of
// repeating a redundant role prefix on each entry.
const roleIsRedundant = showLanguage && distinctRoles.size == 1;
if (options.size > 1) {
for (const {role, language, label} of options.values()) {
const button = shaka.util.Dom.createButton();
// ARIA: single-select menu item
button.setAttribute('role', 'menuitemradio');
button.setAttribute('aria-checked', 'false');
this.eventManager.listen(button, 'click',
() => this.onVideoRoleSelected_(role, language, label));
const span = shaka.util.Dom.createHTMLElement('span');
const hasRealLanguage = (language && language != 'und') || label;
if (roleIsRedundant && hasRealLanguage) {
span.textContent = this.getLanguageLabel_(language, label);
} else {
span.textContent = this.getRoleLabel_(role);
if (showLanguage && hasRealLanguage) {
span.textContent +=
' (' + this.getLanguageLabel_(language, label) + ')';
}
}
button.appendChild(span);
if (isSelectedTrack(role, language, label)) {
button.appendChild(shaka.ui.Utils.checkmarkIcon());
shaka.ui.Utils.setChosenItem(button, span);
this.currentSelection.textContent = span.textContent;
}
this.menu.appendChild(button);
}
}
shaka.ui.Utils.setDisplay(
this.button, options.size > 1 && !this.isSubMenuOpened);
}
/**
* @param {string} role
* @return {string}
* @private
*/
getRoleLabel_(role) {
const LocIds = shaka.ui.Locales.Ids;
switch (role) {
case 'main':
case '':
return this.localization.resolve(LocIds.VIDEO_MAIN);
case 'sign':
return this.localization.resolve(LocIds.VIDEO_SIGN);
default:
return role;
}
}
/**
* @param {string} language
* @param {?string} label
* @return {string}
* @private
*/
getLanguageLabel_(language, label) {
if (label) {
return label;
}
const preferIntlDisplayNames =
this.controls.getConfig().preferIntlDisplayNames;
const name = shaka.ui.LanguageUtils.getLanguageName(
language, this.localization, preferIntlDisplayNames);
if (name) {
return name;
}
return this.localization.resolve(
shaka.ui.Locales.Ids.UNRECOGNIZED_LANGUAGE) + ' (' + language + ')';
}
/**
* @param {string} role
* @param {string} language
* @param {?string} label
* @private
*/
onVideoRoleSelected_(role, language, label) {
this.player.configure({
preferredVideo: [{
role,
label: label || '',
language: language || '',
codec: '',
hdrLevel: '',
layout: '',
}],
});
}
};
/**
* @implements {shaka.extern.IUIElement.Factory}
* @final
*/
shaka.ui.VideoTypeSelection.Factory = class {
/** @override */
create(rootElement, controls) {
return new shaka.ui.VideoTypeSelection(rootElement, controls);
}
};
shaka.ui.OverflowMenu.registerElement(
'video_type', new shaka.ui.VideoTypeSelection.Factory());
shaka.ui.Controls.registerElement(
'video_type', new shaka.ui.VideoTypeSelection.Factory());