/**
* @file modal-dialog.js
*/
從 './utils/dom' 導入 * 作為 Dom;
從 './component' 導入組件;
從“全局/窗口”導入窗口;
從“全局/文檔”導入文檔;
從“鍵碼”導入鍵碼;
const MODAL_CLASS_NAME = 'vjs-modal-dialog';
/**
* `ModalDialog` 顯示在視頻及其控件上,它會阻止
* 與播放器的互動,直到它被關閉。
*
*模態對話框包括一個“關閉”按鈕,當該按鈕時將關閉
* 被激活 - 或者當 ESC 在任何地方被按下時。
*
* @extends 組件
*/
類 ModalDialog 擴展組件 {
/**
* 創建此類的一個實例。
*
* @param {Player} 播放器
* 此類應附加到的 `Player`。
*
* @param {對象} [選項]
* 播放器選項的鍵/值存儲。
*
* @param {混合} [options.content=undefined]
* 為此模態提供自定義內容。
*
* @param {string} [選項.描述]
* 模式的文本描述,主要用於輔助功能。
*
* @param {boolean} [options.fillAlways=false]
* 通常,模態框只會在第一次自動填充
* 他們打開。這告訴模態刷新它的內容
* 每次打開。
*
* @param {string} [選項.標籤]
* 模式的文本標籤,主要用於可訪問性。
*
* @param {boolean} [options.pauseOnOpen=true]
* 如果為 `true`,播放將暫停
* 模式打開,並在關閉時恢復。
*
* @param {boolean} [options.temporary=true]
* 如果為 `true`,模態框只能打開一次;這將是
*關閉後立即處理。
*
* @param {boolean} [options.uncloseable=false]
* 如果為 `true`,用戶將無法關閉模態
* 以正常方式通過 UI。程序化關閉是
* 仍有可能。
*/
構造函數(播放器,選項){
超級(播放器,選項);
this.handleKeyDown_ = (e) => this.handleKeyDown(e);
this.close_ = (e) => this.close(e);
this.opened_ = this.hasBeenOpened_ = this.hasBeenFilled_ = false;
this.closeable(!this.options_.uncloseable);
this.content(this.options_.content);
// 確保在任何子項初始化後定義了 contentEl
// 因為我們只想要 contentEl 中模態的內容
//(不是像關閉按鈕這樣的 UI 元素)。
this.contentEl_ = Dom.createEl('div', {
類名:`${MODAL_CLASS_NAME}-內容`
},{
角色:'文檔'
});
this.descEl_ = Dom.createEl('p', {
className: `${MODAL_CLASS_NAME}-description vjs-control-text`,
id: this.el().getAttribute('aria-describedby')
});
Dom.textContent(this.descEl_, this.description());
this.el_.appendChild(this.descEl_);
this.el_.appendChild(this.contentEl_);
}
/**
* 創建 `ModalDialog` 的 DOM 元素
*
* @return {元素}
* 創建的 DOM 元素。
*/
創建El() {
返回 super.createEl('div', {
類名:this.buildCSSClass(),
選項卡索引:-1
},{
'aria-describedby': `${this.id()}_description`,
'aria-hidden':'真實',
'aria-label': this.label(),
'角色':'對話'
});
}
處置(){
this.contentEl_ = null;
this.descEl_ = null;
this.previouslyActiveEl_ = null;
super.dispose();
}
/**
* 構建默認的 DOM `className`。
*
* @return {字符串}
* 此對象的 DOM `className`。
*/
buildCSSClass() {
返回`${MODAL_CLASS_NAME} vjs-hidden ${super.buildCSSClass()}`;
}
/**
* 返回此模式的標籤字符串。主要用於可訪問性。
*
* @return {字符串}
* 此模態的本地化或原始標籤。
*/
標籤() {
return this.localize(this.options_.label || 'Modal Window');
}
/**
* 返回此模式的描述字符串。主要用於
* 無障礙。
*
* @return {字符串}
* 此模態的本地化或原始描述。
*/
描述() {
讓 desc = this.options_.description || this.localize('這是一個模態窗口。');
// 如果模態是可關閉的,則附加通用可關閉性消息。
如果(this.closeable()){
desc += ' ' + this.localize('可以通過按 Escape 鍵或激活關閉按鈕來關閉此模式。');
}
返回描述;
}
/**
* 打開模式。
*
* @fires ModalDialog#beforemodalopen
* @fires ModalDialog#modalopen
*/
打開() {
如果(!this.opened_){
const player = this.player();
/**
* 在 `ModalDialog` 打開之前觸發。
*
* @event ModalDialog#beforemodalopen
* @type {EventTarget~Event}
*/
this.trigger('beforemodalopen');
this.opened_ = true;
// 如果模式之前從未打開過,則填充內容
// 從未被填充。
如果(this.options_.fillAlways || !this.hasBeenOpened_ && !this.hasBeenFilled_){
這個.填充();
}
// 如果玩家正在玩,暫停它並記下它之前的
//播放狀態。
this.wasPlaying_ = !player.paused();
如果(this.options_.pauseOnOpen && this.wasPlaying_){
播放器.暫停();
}
this.on('keydown', this.handleKeyDown_);
// 隱藏控件並註意它們是否已啟用。
this.hadControls_ = player.controls();
player.controls(false);
這個。顯示();
this.conditionalFocus_();
this.el().setAttribute('aria-hidden', 'false');
/**
* 在 `ModalDialog` 打開後觸發。
*
* @event ModalDialog#modalopen
* @type {EventTarget~Event}
*/
this.trigger('modalopen');
this.hasBeenOpened_ = true;
}
}
/**
* 如果 `ModalDialog` 當前打開或關閉。
*
* @param {boolean} [值]
* 如果給定,它將打開(`true`)或關閉(`false`)模態。
*
* @return {布爾值}
* 模態對話框的當前打開狀態
*/
打開(值){
如果(類型值==='布爾值'){
這個[價值? '開關']();
}
返回this.opened_;
}
/**
* 關閉模式,如果 `ModalDialog` 是什麼都不做
* 沒開。
*
* @fires ModalDialog#beforemodalclose
* @fires ModalDialog#modalclose
*/
關閉() {
如果(!this.opened_){
返回;
}
const player = this.player();
/**
* 在 `ModalDialog` 關閉之前觸發。
*
* @event ModalDialog#beforemodalclose
* @type {EventTarget~Event}
*/
this.trigger('beforemodalclose');
this.opened_ = false;
如果(this.wasPlaying_ && this.options_.pauseOnOpen){
播放器.play();
}
this.off('keydown', this.handleKeyDown_);
如果(this.hadControls_){
播放器控制(真);
}
這個。隱藏();
this.el().setAttribute('aria-hidden', 'true');
/**
* 在 `ModalDialog` 關閉後觸發。
*
* @event ModalDialog#modalclose
* @type {EventTarget~Event}
*/
this.trigger('modalclose');
this.conditionalBlur_();
如果(this.options_.temporary){
這個。處置();
}
}
/**
* 檢查 `ModalDialog` 是否可以通過 UI 關閉。
*
* @param {boolean} [值]
* 如果作為布爾值給出,它將設置 `closeable` 選項。
*
* @return {布爾值}
* 返回可關閉選項的最終值。
*/
可關閉(值){
如果(類型值==='布爾值'){
const closeable = this.closeable_ = !!value;
讓 close = this.getChild('closeButton');
// 如果這是可關閉的並且沒有關閉按鈕,請添加一個。
如果(可關閉的&&!關閉){
// 關閉按鈕應該是模態的子元素——而不是它的
// 內容元素,所以暫時改變內容元素。
const temp = this.contentEl_;
this.contentEl_ = this.el_;
close = this.addChild('closeButton', {controlText:'關閉模態對話框'});
this.contentEl_ = temp;
this.on(close, 'close', this.close_);
}
// 如果這是不可關閉的並且有一個關閉按鈕,請將其刪除。
如果(!關閉&&關閉){
this.off(close, 'close', this.close_);
這個.removeChild(關閉);
關閉.處置();
}
}
返回this.closeable_;
}
/**
* 使用模態的“內容”選項填充模態的內容元素。
* 內容元素將在此更改發生之前清空。
*/
充滿() {
this.fillWith(this.content());
}
/**
* 用任意內容填充模式的內容元素。
* 內容元素將在此更改發生之前清空。
*
* @fires ModalDialog#beforemodalfill
* @fires ModalDialog#modalfill
*
* @param {混合} [內容]
* 適用於此的規則與適用於 `content` 選項的規則相同。
*/
填充(內容){
const contentEl = this.contentEl();
const parentEl = contentEl.parentNode;
const nextSiblingEl = contentEl.nextSibling;
/**
* 在 `ModalDialog` 被內容填充之前觸發。
*
* @event ModalDialog#beforemodalfill
* @type {EventTarget~Event}
*/
this.trigger('beforemodalfill');
this.hasBeenFilled_ = true;
// 在執行之前從 DOM 中分離內容元素
// 避免多次修改動態 DOM 的操作。
parentEl.removeChild(contentEl);
這個.empty();
Dom.insertContent(contentEl, content);
/**
* 在 `ModalDialog` 充滿內容後觸發。
*
* @event ModalDialog#modalfill
* @type {EventTarget~Event}
*/
this.trigger('modalfill');
// 重新註入重新填充的內容元素。
如果(下一個兄弟姐妹){
parentEl.insertBefore(contentEl, nextSiblingEl);
}其他{
parentEl.appendChild(contentEl);
}
// 確保關閉按鈕在對話框 DOM 中位於最後
const closeButton = this.getChild('closeButton');
如果(關閉按鈕){
parentEl.appendChild(closeButton.el_);
}
}
/**
* 清空內容元素。只要模態被填充,就會發生這種情況。
*
* @fires ModalDialog#beforemodalempty
* @fires ModalDialog#modalempty
*/
空的() {
/**
* 在 `ModalDialog` 清空之前觸發。
*
* @event ModalDialog#beforemodalempty
* @type {EventTarget~Event}
*/
this.trigger('beforemodalempty');
Dom.emptyEl(this.contentEl());
/**
* 在 `ModalDialog` 被清空後觸發。
*
* @event ModalDialog#modalempty
* @type {EventTarget~Event}
*/
this.trigger('modalempty');
}
/**
* 獲取或設置模態內容,模態內容在被規範化之前
* 渲染到 DOM 中。
*
* 這不會更新 DOM 或填充模式,但會在
*那個過程。
*
* @param {混合} [值]
* 如果已定義,則設置要在
* 下一次調用 `fill`。該值在被歸一化之前
* 插入。要“清除”內部內容值,請傳遞“null”。
*
* @return {混合}
* 模態對話框的當前內容
*/
內容(價值){
如果(類型值!=='未定義'){
this.content_ = 值;
}
返回 this.content_;
}
/**
* 如果焦點先前在播放器上,則有條件地聚焦模式對話框。
*
* @私人的
*/
conditionalFocus_() {
const activeEl = document.activeElement;
const playerEl = this.player_.el_;
this.previouslyActiveEl_ = null;
如果 (playerEl.contains(activeEl) || playerEl === activeEl) {
this.previouslyActiveEl_ = activeEl;
這個.焦點();
}
}
/**
* 有條件地模糊元素並重新聚焦最後聚焦的元素
*
* @私人的
*/
條件模糊_() {
如果(this.previouslyActiveEl_){
this.previouslyActiveEl_.focus();
this.previouslyActiveEl_ = null;
}
}
/**
* 按鍵處理程序。聚焦模式時附加。
*
* @listens 按鍵
*/
handleKeyDown(事件){
// 不允許按鍵超出模態對話框。
事件.stopPropagation();
if (keycode.isEventKey(event, 'Escape') && this.closeable()) {
事件.preventDefault();
這個。關閉();
返回;
}
// 如果不是 tab 鍵則提前退出
如果 (!keycode.isEventKey(event, 'Tab')) {
返回;
}
const focusableEls = this.focusableEls_();
const activeEl = this.el_.querySelector(':focus');
讓 focusIndex;
for (let i = 0; i < focusableEls.length; i++) {
如果 (activeEl === focusableEls[i]) {
焦點指數=我;
休息;
}
}
如果(document.activeElement === this.el_){
焦點指數 = 0;
}
如果 (event.shiftKey && focusIndex === 0) {
focusableEls[focusableEls.length - 1].focus();
事件.preventDefault();
} else if (!event.shiftKey && focusIndex === focusableEls.length - 1) {
focusableEls[0].focus();
事件.preventDefault();
}
}
/**
* 獲取所有可聚焦元素
*
* @私人的
*/
focusableEls_() {
const allChildren = this.el_.querySelectorAll('*');
返回 Array.prototype.filter.call(allChildren, (child) => {
返回((子instanceof window.HTMLAnchorElement ||
child instanceof window.HTMLAreaElement) && child.hasAttribute('href')) ||
((窗口的子實例。HTMLInputElement ||
window.HTMLSelectElement 的子實例 ||
window.HTMLTextAreaElement 的子實例 ||
child instanceof window.HTMLButtonElement) && !child.hasAttribute('disabled')) ||
(child instanceof window.HTMLIFrameElement ||
window.HTMLObjectElement 的子實例 ||
window.HTMLEmbedElement 的子實例)||
(child.hasAttribute('tabindex') && child.getAttribute('tabindex') !== -1) ||
(child.hasAttribute('contenteditable'));
});
}
}
/**
* `ModalDialog` 默認選項的默認選項。
*
* @type {對象}
* @私人的
*/
ModalDialog.prototype.options_ = {
暫停打開:真,
臨時:真
};
Component.registerComponent('ModalDialog', ModalDialog);
導出默認的 ModalDialog;