import { MediaChromeButton } from './media-chrome-button.js';
import { globalThis } from './utils/server-safe-globals.js';
import { MediaUIAttributes, MediaUIEvents } from './constants.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(_attrs: Record) {
return /*html*/ `
${ccIconOn}${ccIconOff}
`;
}
function getTooltipContentHTML() {
return /*html*/ `
${t('Enable captions')}${t('Disable captions')}
`;
}
const updateAriaChecked = (el: HTMLElement) => {
el.setAttribute('aria-checked', areSubsOn(el).toString());
};
/**
* @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-button-display = inline-flex] - `display` property of button.
*/
class MediaCaptionsButton extends MediaChromeButton {
static getSlotTemplateHTML = getSlotTemplateHTML;
static getTooltipContentHTML = getTooltipContentHTML;
static get observedAttributes() {
return [
...super.observedAttributes,
MediaUIAttributes.MEDIA_SUBTITLES_LIST,
MediaUIAttributes.MEDIA_SUBTITLES_SHOWING,
];
}
connectedCallback(): void {
super.connectedCallback();
this.setAttribute('role', 'button');
this.setAttribute('aria-label', t('closed captions'));
updateAriaChecked(this);
}
attributeChangedCallback(
attrName: string,
oldValue: string,
newValue: string
) {
super.attributeChangedCallback(attrName, oldValue, newValue);
if (attrName === MediaUIAttributes.MEDIA_SUBTITLES_SHOWING) {
updateAriaChecked(this);
}
}
/**
* 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);
}
handleClick() {
this.dispatchEvent(
new globalThis.CustomEvent(MediaUIEvents.MEDIA_TOGGLE_SUBTITLES_REQUEST, {
composed: true,
bubbles: true,
})
);
}
}
/**
* @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[]
) => {
// 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-button')) {
globalThis.customElements.define(
'media-captions-button',
MediaCaptionsButton
);
}
export { MediaCaptionsButton };
export default MediaCaptionsButton;