summaryrefslogtreecommitdiff
path: root/javascript/videojs/src/js/tech/setup-sourceset.js
diff options
context:
space:
mode:
Diffstat (limited to 'javascript/videojs/src/js/tech/setup-sourceset.js')
-rw-r--r--javascript/videojs/src/js/tech/setup-sourceset.js310
1 files changed, 0 insertions, 310 deletions
diff --git a/javascript/videojs/src/js/tech/setup-sourceset.js b/javascript/videojs/src/js/tech/setup-sourceset.js
deleted file mode 100644
index 621b62f..0000000
--- a/javascript/videojs/src/js/tech/setup-sourceset.js
+++ /dev/null
@@ -1,310 +0,0 @@
-import window from 'global/window';
-import document from 'global/document';
-import {merge} from '../utils/obj';
-import {getAbsoluteURL} from '../utils/url';
-
-/** @import Html5 from './html5' */
-
-/**
- * This function is used to fire a sourceset when there is something
- * similar to `mediaEl.load()` being called. It will try to find the source via
- * the `src` attribute and then the `<source>` elements. It will then fire `sourceset`
- * with the source that was found or empty string if we cannot know. If it cannot
- * find a source then `sourceset` will not be fired.
- *
- * @param {Html5} tech
- * The tech object that sourceset was setup on
- *
- * @return {boolean}
- * returns false if the sourceset was not fired and true otherwise.
- */
-const sourcesetLoad = (tech) => {
- const el = tech.el();
-
- // if `el.src` is set, that source will be loaded.
- if (el.hasAttribute('src')) {
- tech.triggerSourceset(el.src);
- return true;
- }
-
- /**
- * Since there isn't a src property on the media element, source elements will be used for
- * implementing the source selection algorithm. This happens asynchronously and
- * for most cases were there is more than one source we cannot tell what source will
- * be loaded, without re-implementing the source selection algorithm. At this time we are not
- * going to do that. There are three special cases that we do handle here though:
- *
- * 1. If there are no sources, do not fire `sourceset`.
- * 2. If there is only one `<source>` with a `src` property/attribute that is our `src`
- * 3. If there is more than one `<source>` but all of them have the same `src` url.
- * That will be our src.
- */
- const sources = tech.$$('source');
- const srcUrls = [];
- let src = '';
-
- // if there are no sources, do not fire sourceset
- if (!sources.length) {
- return false;
- }
-
- // only count valid/non-duplicate source elements
- for (let i = 0; i < sources.length; i++) {
- const url = sources[i].src;
-
- if (url && srcUrls.indexOf(url) === -1) {
- srcUrls.push(url);
- }
- }
-
- // there were no valid sources
- if (!srcUrls.length) {
- return false;
- }
-
- // there is only one valid source element url
- // use that
- if (srcUrls.length === 1) {
- src = srcUrls[0];
- }
-
- tech.triggerSourceset(src);
- return true;
-};
-
-/**
- * our implementation of an `innerHTML` descriptor for browsers
- * that do not have one.
- */
-const innerHTMLDescriptorPolyfill = Object.defineProperty({}, 'innerHTML', {
- get() {
- return this.cloneNode(true).innerHTML;
- },
- set(v) {
- // make a dummy node to use innerHTML on
- const dummy = document.createElement(this.nodeName.toLowerCase());
-
- // set innerHTML to the value provided
- dummy.innerHTML = v;
-
- // make a document fragment to hold the nodes from dummy
- const docFrag = document.createDocumentFragment();
-
- // copy all of the nodes created by the innerHTML on dummy
- // to the document fragment
- while (dummy.childNodes.length) {
- docFrag.appendChild(dummy.childNodes[0]);
- }
-
- // remove content
- this.innerText = '';
-
- // now we add all of that html in one by appending the
- // document fragment. This is how innerHTML does it.
- window.Element.prototype.appendChild.call(this, docFrag);
-
- // then return the result that innerHTML's setter would
- return this.innerHTML;
- }
-});
-
-/**
- * Get a property descriptor given a list of priorities and the
- * property to get.
- */
-const getDescriptor = (priority, prop) => {
- let descriptor = {};
-
- for (let i = 0; i < priority.length; i++) {
- descriptor = Object.getOwnPropertyDescriptor(priority[i], prop);
-
- if (descriptor && descriptor.set && descriptor.get) {
- break;
- }
- }
-
- descriptor.enumerable = true;
- descriptor.configurable = true;
-
- return descriptor;
-};
-
-const getInnerHTMLDescriptor = (tech) => getDescriptor([
- tech.el(),
- window.HTMLMediaElement.prototype,
- window.Element.prototype,
- innerHTMLDescriptorPolyfill
-], 'innerHTML');
-
-/**
- * Patches browser internal functions so that we can tell synchronously
- * if a `<source>` was appended to the media element. For some reason this
- * causes a `sourceset` if the the media element is ready and has no source.
- * This happens when:
- * - The page has just loaded and the media element does not have a source.
- * - The media element was emptied of all sources, then `load()` was called.
- *
- * It does this by patching the following functions/properties when they are supported:
- *
- * - `append()` - can be used to add a `<source>` element to the media element
- * - `appendChild()` - can be used to add a `<source>` element to the media element
- * - `insertAdjacentHTML()` - can be used to add a `<source>` element to the media element
- * - `innerHTML` - can be used to add a `<source>` element to the media element
- *
- * @param {Html5} tech
- * The tech object that sourceset is being setup on.
- */
-const firstSourceWatch = function(tech) {
- const el = tech.el();
-
- // make sure firstSourceWatch isn't setup twice.
- if (el.resetSourceWatch_) {
- return;
- }
-
- const old = {};
- const innerDescriptor = getInnerHTMLDescriptor(tech);
- const appendWrapper = (appendFn) => (...args) => {
- const retval = appendFn.apply(el, args);
-
- sourcesetLoad(tech);
-
- return retval;
- };
-
- ['append', 'appendChild', 'insertAdjacentHTML'].forEach((k) => {
- if (!el[k]) {
- return;
- }
-
- // store the old function
- old[k] = el[k];
-
- // call the old function with a sourceset if a source
- // was loaded
- el[k] = appendWrapper(old[k]);
- });
-
- Object.defineProperty(el, 'innerHTML', merge(innerDescriptor, {
- set: appendWrapper(innerDescriptor.set)
- }));
-
- el.resetSourceWatch_ = () => {
- el.resetSourceWatch_ = null;
- Object.keys(old).forEach((k) => {
- el[k] = old[k];
- });
-
- Object.defineProperty(el, 'innerHTML', innerDescriptor);
- };
-
- // on the first sourceset, we need to revert our changes
- tech.one('sourceset', el.resetSourceWatch_);
-};
-
-/**
- * our implementation of a `src` descriptor for browsers
- * that do not have one
- */
-const srcDescriptorPolyfill = Object.defineProperty({}, 'src', {
- get() {
- if (this.hasAttribute('src')) {
- return getAbsoluteURL(window.Element.prototype.getAttribute.call(this, 'src'));
- }
-
- return '';
- },
- set(v) {
- window.Element.prototype.setAttribute.call(this, 'src', v);
-
- return v;
- }
-});
-
-const getSrcDescriptor = (tech) => getDescriptor([tech.el(), window.HTMLMediaElement.prototype, srcDescriptorPolyfill], 'src');
-
-/**
- * setup `sourceset` handling on the `Html5` tech. This function
- * patches the following element properties/functions:
- *
- * - `src` - to determine when `src` is set
- * - `setAttribute()` - to determine when `src` is set
- * - `load()` - this re-triggers the source selection algorithm, and can
- * cause a sourceset.
- *
- * If there is no source when we are adding `sourceset` support or during a `load()`
- * we also patch the functions listed in `firstSourceWatch`.
- *
- * @param {Html5} tech
- * The tech to patch
- */
-const setupSourceset = function(tech) {
- if (!tech.featuresSourceset) {
- return;
- }
-
- const el = tech.el();
-
- // make sure sourceset isn't setup twice.
- if (el.resetSourceset_) {
- return;
- }
-
- const srcDescriptor = getSrcDescriptor(tech);
- const oldSetAttribute = el.setAttribute;
- const oldLoad = el.load;
-
- Object.defineProperty(el, 'src', merge(srcDescriptor, {
- set: (v) => {
- const retval = srcDescriptor.set.call(el, v);
-
- // we use the getter here to get the actual value set on src
- tech.triggerSourceset(el.src);
-
- return retval;
- }
- }));
-
- el.setAttribute = (n, v) => {
- const retval = oldSetAttribute.call(el, n, v);
-
- if ((/src/i).test(n)) {
- tech.triggerSourceset(el.src);
- }
-
- return retval;
- };
-
- el.load = () => {
- const retval = oldLoad.call(el);
-
- // if load was called, but there was no source to fire
- // sourceset on. We have to watch for a source append
- // as that can trigger a `sourceset` when the media element
- // has no source
- if (!sourcesetLoad(tech)) {
- tech.triggerSourceset('');
- firstSourceWatch(tech);
- }
-
- return retval;
- };
-
- if (el.currentSrc) {
- tech.triggerSourceset(el.currentSrc);
- } else if (!sourcesetLoad(tech)) {
- firstSourceWatch(tech);
- }
-
- el.resetSourceset_ = () => {
- el.resetSourceset_ = null;
- el.load = oldLoad;
- el.setAttribute = oldSetAttribute;
- Object.defineProperty(el, 'src', srcDescriptor);
- if (el.resetSourceWatch_) {
- el.resetSourceWatch_();
- }
- };
-};
-
-export default setupSourceset;