/**
* @file plugin.js
*/
從'./mixins/evented'導入事件;
從“./mixins/stateful”導入有狀態;
從“./utils/events”導入 * 作為事件;
從“./utils/log”導入日誌;
從'./player'導入播放器;
/**
* 基本插件名稱。
*
* @私人的
* @持續的
* @type {字符串}
*/
const BASE_PLUGIN_NAME = '插件';
/**
* 存儲播放器的活動插件緩存的密鑰。
*
* @私人的
* @持續的
* @type {字符串}
*/
const PLUGIN_CACHE_KEY = 'activePlugins_';
/**
* 將已註冊的插件存儲在私人空間中。
*
* @私人的
* @type {對象}
*/
const pluginStorage = {};
/**
* 報告插件是否已註冊。
*
* @私人的
* @param {string} 名稱
* 插件名稱。
*
* @return {布爾值}
* 插件是否已經註冊。
*/
const pluginExists = (name) => pluginStorage.hasOwnProperty(name);
/**
* 按名稱獲取單個註冊插件。
*
* @私人的
* @param {string} 名稱
* 插件名稱。
*
* @return {函數|未定義}
* 插件(或未定義)。
*/
常量獲取插件 =(名稱)=> 插件存在(名稱)?插件風暴 [名稱]:未定義;
/**
* 在播放器上將插件標記為“活動”。
*
* 此外,確保播放器具有用於跟踪活動插件的對象。
*
* @私人的
* @param {Player} 播放器
* Video.js 播放器實例。
*
* @param {string} 名稱
* 插件名稱。
*/
const markPluginAsActive = (player, name) => {
播放器[PLUGIN_CACHE_KEY] = 播放器[PLUGIN_CACHE_KEY] || {};
播放器[PLUGIN_CACHE_KEY][名稱] = true;
};
/**
* 觸發一對插件設置事件。
*
* @私人的
* @param {Player} 播放器
* Video.js 播放器實例。
*
* @param {Plugin~PluginEventHash} 散列
* 插件事件哈希。
*
* @param {boolean} [之前]
* 如果為真,則在事件名稱前加上“before”前綴。換句話說,
* 使用它來觸發“beforepluginsetup”而不是“pluginsetup”。
*/
const triggerSetupEvent = (player, hash, before) => {
常量事件名稱 =(之前?'之前':「)+ '插件設置';
player.trigger(eventName, hash);
player.trigger(eventName + ':' + hash.name, hash);
};
/**
* 採用一個基本的插件函數並返回一個包裝函數,它標記
* 在插件已激活的播放器上。
*
* @私人的
* @param {string} 名稱
* 插件名稱。
*
* @param {函數} 插件
* 基本插件。
*
* @return {函數}
* 給定插件的包裝函數。
*/
const createBasicPlugin = function(名稱, 插件) {
const basicPluginWrapper = function() {
// 我們在播放器上觸發“beforepluginsetup”和“pluginsetup”事件
// 不管怎樣,但我們希望散列值與提供的散列值一致
// 對於高級插件。
//
// 這裡唯一可能違反直覺的是 `instance`
// "pluginsetup" 事件是 `plugin` 函數返回的值。
triggerSetupEvent(this, {name, plugin, instance: null}, true);
const instance = plugin.apply(this, arguments);
markPluginAsActive(這個,名字);
triggerSetupEvent(this, {name, plugin, instance});
返回實例;
};
Object.keys(插件).forEach(函數(prop) {
basicPluginWrapper[prop] = 插件[prop];
});
返回 basicPluginWrapper;
};
/**
* 採用插件子類並返回用於生成的工廠函數
* 它的實例。
*
* 這個工廠函數將自己替換為請求的實例
* 插件的子類。
*
* @私人的
* @param {string} 名稱
* 插件名稱。
*
* @param {Plugin} PluginSubClass
* 高級插件。
*
* @return {函數}
*/
const createPluginFactory = (name, PluginSubClass) => {
// 給插件原型添加一個 `name` 屬性,這樣每個插件都可以
// 通過名稱引用自身。
PluginSubClass.prototype.name = 名稱;
返回函數(...參數){
triggerSetupEvent(這個,{名稱,插件:PluginSubClass, 實例: null}, true);
常量實例 = 新的插件子類(...[這,... 參數]);
// 插件替換為返回當前實例的函數。
這個[名稱] = () => 實例;
triggerSetupEvent(this, instance.getEventHash());
返回實例;
};
};
/**
* 所有高級插件的父類。
*
* @mixes 模塊:事件〜事件混入
* @mixes 模塊:stateful~StatefulMixin
* @fires Player#beforepluginsetup
* @fires 播放器 #beforepluginsetup: $ 名稱
* @fires Player#pluginsetup
* @fires 播放器 #pluginsetup: $ 名稱
* @listens Player#dispose
* @throws {錯誤}
* 如果試圖實例化基類 {@link Plugin}
* 直接而不是通過子類。
*/
類插件{
/**
* 創建此類的一個實例。
*
* 子類應該調用 `super` 以確保插件被正確初始化。
*
* @param {Player} 播放器
* Video.js 播放器實例。
*/
構造函數(玩家){
如果(this.constructor === 插件){
throw new Error('插件必須被子類化;不能直接實例化。');
}
this.player = 播放器;
如果(!this.log){
this.log = this.player.log.createLogger(this.name);
}
// 使這個對象事件化,但刪除添加的 `trigger` 方法,這樣我們
// 改用原型版本。
事件(這);
刪除這個。觸發器;
有狀態的(這,this.constructor.defaultState);
markPluginAsActive(播放器, this.name);
// 自動綁定 dispose 方法,以便我們可以將其用作偵聽器並取消綁定
// 以後很容易。
this.dispose = this.dispose.bind(this);
// 如果播放器被處置,則處置插件。
player.on('dispose', this.dispose);
}
/**
* 獲取在 <pluginName>.VERSION 上設置的插件版本
*/
版本() {
返回 this.constructor.VERSION;
}
/**
* 插件觸發的每個事件都包含附加數據的哈希值
* 常規屬性。
*
* 這將返回該對像或改變現有哈希。
*
* @param {對象} [散列={}]
* 用作事件哈希的對象。
*
* @return {插件~PluginEventHash}
* 混合了提供的屬性的事件哈希對象。
*/
getEventHash(散列 = {}) {
hash.name = this.name;
hash.plugin = this.constructor;
hash.instance = this;
返回散列;
}
/**
* 在插件對像上觸發事件並覆蓋
* {@link 模塊:事件〜事件混合。觸發器 | 事件混合。觸發}。
*
* @param {string|Object} 事件
* 事件類型或具有類型屬性的對象。
*
* @param {對象} [散列={}]
* 附加數據散列與
* {@link Plugin~PluginEventHash|PluginEventHash}。
*
* @return {布爾值}
* 是否防止違約。
*/
觸發器(事件,哈希={}){
返回 Events.trigger(this.eventBusEl_, event, this.getEventHash(hash));
}
/**
* 處理插件上的“狀態改變”事件。默認情況下無操作,由
* 子類化。
*
* @抽象的
* @param {事件} e
* 由“statechanged”事件提供的事件對象。
*
* @param {Object} e.changes
* 一個描述“狀態改變”發生的變化的對象
* 事件。
*/
handleStateChanged(e) {}
/**
* 配置一個插件。
*
* 子類可以根據需要覆蓋它,但為了安全起見,
* 最好訂閱“dispose”事件。
*
* @fires 插件#dispose
*/
處置(){
const {name, player} = this;
/**
* 高級插件即將被處理的信號。
*
* @event 插件#dispose
* @type {EventTarget~Event}
*/
this.trigger('處置');
這個。關閉();
player.off('dispose', this.dispose);
// 通過清理消除任何可能的內存洩漏源
// 播放器和插件實例之間的引用並清空
// 插件的狀態並用拋出的函數替換方法。
播放器[PLUGIN_CACHE_KEY][名稱] = false;
this.player = this.state = null;
// 最後,用新工廠替換播放器上的插件名稱
// 函數,以便插件準備好再次設置。
player[name] = createPluginFactory(name, pluginStorage[name]);
}
/**
* 確定插件是否是基本插件(即不是 `Plugin` 的子類)。
*
* @param {string|Function} 插件
* 如果是字符串,則匹配插件的名稱。如果一個函數,將是
* 直接測試。
*
* @return {布爾值}
* 插件是否是基本插件。
*/
靜態 isBasic(插件){
常量 p =(插件 === '字符串' 的類型)?獲取插件(插件):插件;
返回 p === '函數' &&!外掛程式原型. 原型 (p.原型);
}
/**
* 註冊一個 Video.js 插件。
*
* @param {string} 名稱
* 要註冊的插件名稱。必須是一個字符串並且
* 不得與 `Player` 上的現有插件或方法匹配
* 原型。
*
* @param {函數} 插件
* `Plugin` 的子類或基本插件的函數。
*
* @return {函數}
* 對於高級插件,該插件的工廠函數。為了
* 基本插件,一個初始化插件的包裝函數。
*/
靜態註冊插件(名稱,插件){
如果(類型名稱!=='字符串'){
throw new Error(`非法插件名稱,“${name}”,必須是一個字符串,是 ${typeof name}。`);
}
如果(插件存在(名稱)){
log.warn(`名為“${name}”的插件已經存在。您可能希望避免重新註冊插件!`);
} else if (Player.prototype.hasOwnProperty(name)) {
throw new Error(`非法插件名稱,“${name}”,不能與現有播放器方法共享名稱!`);
}
如果(插件的類型!== '功能') {
throw new Error(`“${name}”的非法插件,必須是函數,是 ${typeof plugin}。`);
}
pluginStorage[名稱] = 插件;
// 為所有子類插件添加一個播放器原型方法(但不是
// 基插件類)。
如果(名稱!== BASE_PLUGIN_NAME){
如果(插件.isBasic(插件)){
Player.prototype[名稱] = createBasicPlugin(名稱, 插件);
}其他{
Player.prototype[名稱] = createPluginFactory(名稱, 插件);
}
}
返回插件;
}
/**
* 取消註冊 Video.js 插件。
*
* @param {string} 名稱
* 要註銷的插件名稱。必須是一個字符串
* 匹配現有插件。
*
* @throws {錯誤}
* 如果嘗試取消註冊基本插件。
*/
靜態取消註冊插件(名稱){
如果(名稱=== BASE_PLUGIN_NAME){
throw new Error('無法註銷基本插件。');
}
如果(插件存在(名稱)){
刪除插件存儲[名稱];
刪除 Player.prototype[名稱];
}
}
/**
* 獲取包含多個 Video.js 插件的對象。
*
* @param {數組} [名稱]
* 如果提供,應該是一個插件名稱數組。默認為_all_
* 插件名稱。
*
* @return {對象|未定義}
* 包含與其名稱關聯的插件的對像或
* `undefined` 如果不存在匹配的插件)。
*/
靜態 getPlugins(名稱 = Object.keys(pluginStorage)){
讓結果;
names.forEach(名稱=> {
const plugin = getPlugin(名稱);
如果(插件){
結果=結果|| {};
結果[名稱] = 插件;
}
});
返回結果;
}
/**
* 獲取插件的版本(如果可用)
*
* @param {string} 名稱
* 插件名稱。
*
* @return {字符串}
* 插件的版本或空字符串。
*/
靜態 getPluginVersion(名稱){
const plugin = getPlugin(名稱);
返回插件 && plugin.VERSION || '';
}
}
/**
* 如果存在,則按名稱獲取插件。
*
* @靜止的
* @method 獲取插件
* @memberOf 插件
* @param {string} 名稱
* 插件名稱。
*
* @returns {函數|未定義}
* 插件(或 `undefined`)。
*/
Plugin.getPlugin = getPlugin;
/**
* 註冊的基礎插件類的名稱。
*
* @type {字符串}
*/
Plugin.BASE_PLUGIN_NAME = BASE_PLUGIN_NAME;
Plugin.registerPlugin(BASE_PLUGIN_NAME, Plugin);
/**
* 記錄在 player.js 中
*
* @忽略
*/
Player.prototype.usingPlugin = function(name) {
返回!!這個 [插件快取密鑰] && 這個 [插件 _ 快取密鑰] [名稱] === 真;
};
/**
* 記錄在 player.js 中
*
* @忽略
*/
Player.prototype.hasPlugin = 函數(名稱){
返回!!插件存在(名稱);
};
導出默認插件;
/**
* 表示即將在播放器上安裝插件的信號。
*
* @event Player#beforepluginsetup
* @type {插件~PluginEventHash}
*/
/**
* 插件即將在播放器上安裝的信號 - 按名稱。名字
* 是插件的名稱。
*
* @event 播放器 #beforepluginsetup: $ 名稱
* @type {插件~PluginEventHash}
*/
/**
* 表示剛剛在播放器上安裝了插件。
*
* @event Player#pluginsetup
* @type {插件~PluginEventHash}
*/
/**
* 表示剛剛在播放器上設置了插件 - 按名稱。名字
* 是插件的名稱。
*
* @event 播放器 #pluginsetup: $ 名稱
* @type {插件~PluginEventHash}
*/
/**
* @typedef {Object} 插件~PluginEventHash
*
* @property {string} 實例
* 對於基本插件,插件函數的返回值。為了
* 高級插件,觸發事件的插件實例。
*
* @property {string} 名稱
* 插件名稱。
*
* @property {string} 插件
* 對於基本插件,插件功能。對於高級插件,
* 插件類/構造函數。
*/