import { globalThis } from '../utils/server-safe-globals.js'; import { MediaUIAttributes } from '../constants.js'; import { MediaChromeMenuButton } from './media-chrome-menu-button.js'; import { getMediaController } from '../utils/element-utils.js'; import { areSubsOn, parseTextTracksStr, stringifyTextTrackList, } from '../utils/captions.js'; import { TextTrackLike } from '../utils/TextTrackLike.js'; import { t } from '../utils/i18n.js'; const ccIconOn = ``; const ccIconOff = ``; function getSlotTemplateHTML() { return /*html*/ ` ${ccIconOn} ${ccIconOff} `; } function getTooltipContentHTML() { return t('Captions'); } const updateAriaChecked = (el: HTMLElement): void => { el.setAttribute('data-captions-enabled', areSubsOn(el).toString()); }; const updateAriaLabel = (el: HTMLElement): void => { el.setAttribute('aria-label', t('closed captions')); }; /** * @slot on - An element that will be shown while closed captions or subtitles are on. * @slot off - An element that will be shown while closed captions or subtitles are off. * @slot icon - An element for representing on and off states in a single icon * * @attr {string} mediasubtitleslist - (read-only) A list of all subtitles and captions. * @attr {string} mediasubtitlesshowing - (read-only) A list of the showing subtitles and captions. * * @cssproperty [--media-captions-menu-button-display = inline-flex] - `display` property of button. */ class MediaCaptionsMenuButton extends MediaChromeMenuButton { static getSlotTemplateHTML = getSlotTemplateHTML; static getTooltipContentHTML = getTooltipContentHTML; static get observedAttributes(): string[] { return [ ...super.observedAttributes, MediaUIAttributes.MEDIA_SUBTITLES_LIST, MediaUIAttributes.MEDIA_SUBTITLES_SHOWING, MediaUIAttributes.MEDIA_LANG ]; } connectedCallback(): void { super.connectedCallback(); updateAriaLabel(this); updateAriaChecked(this); } attributeChangedCallback( attrName: string, oldValue: string, newValue: string ): void { super.attributeChangedCallback(attrName, oldValue, newValue); if (attrName === MediaUIAttributes.MEDIA_SUBTITLES_SHOWING) { updateAriaChecked(this); } else if (attrName === MediaUIAttributes.MEDIA_LANG) { updateAriaLabel(this); } } /** * Returns the element with the id specified by the `invoketarget` attribute. * @return {HTMLElement | null} */ get invokeTargetElement(): HTMLElement | null { if (this.invokeTarget != undefined) return super.invokeTargetElement; return getMediaController(this)?.querySelector('media-captions-menu'); } /** * An array of TextTrack-like objects. * Objects must have the properties: kind, language, and label. */ get mediaSubtitlesList(): TextTrackLike[] { return getSubtitlesListAttr(this, MediaUIAttributes.MEDIA_SUBTITLES_LIST); } set mediaSubtitlesList(list: TextTrackLike[]) { setSubtitlesListAttr(this, MediaUIAttributes.MEDIA_SUBTITLES_LIST, list); } /** * An array of TextTrack-like objects. * Objects must have the properties: kind, language, and label. */ get mediaSubtitlesShowing(): TextTrackLike[] { return getSubtitlesListAttr( this, MediaUIAttributes.MEDIA_SUBTITLES_SHOWING ); } set mediaSubtitlesShowing(list: TextTrackLike[]) { setSubtitlesListAttr(this, MediaUIAttributes.MEDIA_SUBTITLES_SHOWING, list); } } /** * @param el - Should be HTMLElement but issues with globalThis shim * @param attrName - The attribute name to get * @returns An array of TextTrack-like objects. */ const getSubtitlesListAttr = ( el: HTMLElement, attrName: string ): TextTrackLike[] => { const attrVal = el.getAttribute(attrName); return attrVal ? parseTextTracksStr(attrVal) : []; }; /** * * @param el - Should be HTMLElement but issues with globalThis shim * @param attrName - The attribute name to set * @param list - An array of TextTrack-like objects */ const setSubtitlesListAttr = ( el: HTMLElement, attrName: string, list: TextTrackLike[] ): void => { // null, undefined, and empty arrays are treated as "no value" here if (!list?.length) { el.removeAttribute(attrName); return; } // don't set if the new value is the same as existing const newValStr = stringifyTextTrackList(list); const oldVal = el.getAttribute(attrName); if (oldVal === newValStr) return; el.setAttribute(attrName, newValStr); }; if (!globalThis.customElements.get('media-captions-menu-button')) { globalThis.customElements.define( 'media-captions-menu-button', MediaCaptionsMenuButton ); } export { MediaCaptionsMenuButton }; export default MediaCaptionsMenuButton;