從“全局/窗口”導入窗口;
從“全局/文檔”導入文檔;
從 '../utils/merge-options' 導入 mergeOptions;
從 '../utils/url' 導入 {getAbsoluteURL};
/**
* 此函數用於在有內容時觸發源集
* 類似於被調用的 `mediaEl.load()`。它將嘗試通過以下方式找到來源
* `src` 屬性,然後是 `<source>` 元素。然後它會觸發 `sourceset`
* 如果我們不知道,則使用找到的源或空字符串。如果不能
* 找到源然後 `sourceset` 將不會被解僱。
*
* @param {Html5} 技術
* 設置 sourceset 的技術對象
*
* @return {布爾值}
* 如果源集未被觸發則返回 false,否則返回 true。
*/
const sourcesetLoad = (tech) => {
const el = tech.el();
// 如果設置了 `el.src`,將加載該源。
如果(el.hasAttribute('src')){
tech.triggerSourceset(el.src);
返回真;
}
/**
* 由於媒體元素上沒有 src 屬性,源元素將用於
* 實施源選擇算法。這是異步發生的,並且
* 在大多數情況下,如果有多個來源,我們無法確定哪個來源會
* 加載,無需重新實現源選擇算法。這個時候我們不
* 打算這樣做。不過,我們確實在這里處理了三種特殊情況:
*
* 1。如果沒有來源,請不要觸發 `sourceset`。
* 2。如果只有一個帶有 `src` 屬性的 `<source>` 就是我們的 `src`
* 3。如果有多個 `<source>` 但它們都具有相同的 `src` url。
* 那將是我們的 src。
*/
const sources = tech.$$('來源');
常量 srcUrls = [];
讓 src = '';
// 如果沒有源,不觸發源集
如果(!sources.length){
返回假;
}
// 只計算有效/不重複的源元素
為 (讓我 = 0; 我 < 來源. 長度; 我 ++) {
const url = 來源[i].src;
如果 (url && srcUrls.indexOf(url) === -1) {
srcUrls.push(url);
}
}
// 沒有有效的來源
如果(!srcUrls.length){
返回假;
}
// 只有一個有效的源元素 url
// 使用那個
如果(srcUrls.length === 1){
src = srcUrls[0];
}
tech.triggerSourceset(src);
返回真;
};
/**
* 我們為瀏覽器實現的 `innerHTML` 描述符
* 沒有。
*/
const innerHTMLDescriptorPolyfill = Object.defineProperty({}, 'innerHTML', {
得到() {
返回 this.cloneNode(true).innerHTML;
},
設置(v){
// 創建一個虛擬節點以在其上使用 innerHTML
const dummy = document.createElement(this.nodeName.toLowerCase());
// 將 innerHTML 設置為提供的值
dummy.innerHTML = v;
// 製作一個文檔片段來保存來自 dummy 的節點
const docFrag = document.createDocumentFragment();
// 將 innerHTML 創建的所有節點複製到 dummy
// 到文檔片段
while (dummy.childNodes.length) {
docFrag.appendChild(dummy.childNodes[0]);
}
// 刪除內容
this.innerText = '';
// 現在我們通過附加
// 文檔片段。這就是如何做到這一點。
window.Element.prototype.appendChild.call(this, docFrag);
// 然後返回 innerHTML 的 setter 的結果
返回 this.innerHTML;
}
});
/**
* 獲取屬性描述符給定優先級列表和
* 財產得到。
*/
const getDescriptor = (priority, prop) => {
讓描述符 = {};
對於(讓我 = 0; 我 < 優先級。長度; 我 ++){
descriptor = Object.getOwnPropertyDescriptor(priority[i], prop);
如果(描述符 && descriptor.set && descriptor.get){
休息;
}
}
descriptor.enumerable = true;
descriptor.configurable = true;
返回描述符;
};
const getInnerHTMLDescriptor = (tech) => getDescriptor([
tech.el(),
window.HTMLMediaElement.prototype,
window.Element.prototype,
innerHTMLDescriptorPolyfill
], 'innerHTML');
/**
* 修補瀏覽器內部函數,以便我們可以同步判斷
* 如果將 `<source>` 附加到媒體元素。出於某種原因,這
* 如果媒體元素已準備就緒且沒有源,則會導致 `sourceset`。
* 這發生在:
* - 頁面剛剛加載,媒體元素沒有來源。
* - 媒體元素清空了所有來源,然後調用了 load() 。
*
* 它通過在支持以下功能/屬性時修補它們來實現:
*
* - `append()` - 可用於將 `<source>` 元素添加到媒體元素
* - `appendChild()` - 可用於將 `<source>` 元素添加到媒體元素
* - `insertAdjacentHTML()` - 可用於將 `<source>` 元素添加到媒體元素
* - `innerHTML` - 可用於將 `<source>` 元素添加到媒體元素
*
* @param {Html5} 技術
* 正在設置 sourceset 的技術對象。
*/
const firstSourceWatch = 函數(技術){
const el = tech.el();
// 確保 firstSourceWatch 沒有設置兩次。
如果(el.resetSourceWatch_){
返回;
}
常量舊 = {};
const innerDescriptor = getInnerHTMLDescriptor(技術);
const appendWrapper = (appendFn) => (...args) => {
const retval = appendFn.apply(el, args);
sourcesetLoad(技術);
返回retval;
};
['append', 'appendChild', 'insertAdjacentHTML'].forEach((k) => {
如果(!埃爾 [k]) {
返回;
}
// 存儲舊函數
舊[k] = el[k];
// 如果有源,則使用源集調用舊函數
// 已加載
el[k] = appendWrapper(舊[k]);
});
Object.defineProperty(el, 'innerHTML', mergeOptions(innerDescriptor, {
設置:appendWrapper(innerDescriptor.set)
}));
el.resetSourceWatch_ = () => {
el.resetSourceWatch_ = null;
Object.keys(old).forEach((k) => {
el[k] = 舊[k];
});
Object.defineProperty(el, 'innerHTML', innerDescriptor);
};
// 在第一個源集上,我們需要還原我們的更改
tech.one('sourceset', el.resetSourceWatch_);
};
/**
* 我們為瀏覽器實現的 `src` 描述符
* 沒有。
*/
const srcDescriptorPolyfill = Object.defineProperty({}, 'src', {
得到() {
如果 (this.hasAttribute('src')) {
返回 getAbsoluteURL(window.Element.prototype.getAttribute.call(this, 'src'));
}
返回 '';
},
設置(v){
window.Element.prototype.setAttribute.call(this, 'src', v);
返回 v;
}
});
const getSrcDescriptor = (tech) => getDescriptor([tech.el(), window.HTMLMediaElement.prototype, srcDescriptorPolyfill], 'src');
/**
* 在 `Html5` 技術上設置 `sourceset` 處理。這個功能
* 修補以下元素屬性/功能:
*
* - `src` - 確定何時設置 `src`
* - `setAttribute()` - 確定何時設置 `src`
* - `load()` - 這會重新觸發源選擇算法,並且可以
* 導致源集。
*
* 如果在我們添加 `sourceset` 支持時或在 `load()` 期間沒有源
* 我們還修補了 `firstSourceWatch` 中列出的函數。
*
* @param {Html5} 技術
* 打補丁的技術
*/
const setupSourceset = 函數(技術){
如果(!tech.featuresSourceset){
返回;
}
const el = tech.el();
// 確保 sourceset 沒有設置兩次。
如果(el.resetSourceset_){
返回;
}
const srcDescriptor = getSrcDescriptor(技術);
const oldSetAttribute = el.setAttribute;
const oldLoad = el.load;
Object.defineProperty(el, 'src', mergeOptions(srcDescriptor, {
設置:(v)=> {
const retval = srcDescriptor.set.call(el, v);
// 我們在這裡使用 getter 獲取 src 上設置的實際值
tech.triggerSourceset(el.src);
返回retval;
}
}));
el.setAttribute = (n, v) => {
const retval = oldSetAttribute.call(el, n, v);
如果 ((/src/i).test(n)) {
tech.triggerSourceset(el.src);
}
返回retval;
};
el.load = () => {
const retval = oldLoad.call(el);
// 如果調用了 load,但沒有觸發源
// 源碼開啟。我們必須注意源追加
// 因為當媒體元素出現時可以觸發 `sourceset`
//沒有來源
如果(!sourcesetLoad(技術)){
tech.triggerSourceset('');
firstSourceWatch(技術);
}
返回retval;
};
如果(el.currentSrc){
tech.triggerSourceset(el.currentSrc);
} 否則,如果(!來源載入 (技術)) {
firstSourceWatch(技術);
}
el.resetSourceset_ = () => {
el.resetSourceset_ = null;
el.load = oldLoad;
el.setAttribute = oldSetAttribute;
Object.defineProperty(el, 'src', srcDescriptor);
如果(el.resetSourceWatch_){
el.resetSourceWatch_();
}
};
};
導出默認設置源集;