importar ventana desde 'global/window';
importar documento desde 'global/document';
importar mergeOptions desde '../utils/merge-options';
importar {getAbsoluteURL} desde '../utils/url';

/**
 * Esta función se usa para disparar un conjunto de fuentes cuando hay algo
 * similar a `mediaEl.load()` siendo llamado. Intentará encontrar la fuente a través de
 * el atributo `src` y luego el `< fuente> ` elementos. Luego disparará `sourceset`
 * con la fuente que se encontró o una cadena vacía si no podemos saberlo. si no puede
 * encuentre una fuente, entonces `sourceset` no se activará.
 *
 * @param {Html5} tecnología
 * El objeto tecnológico en el que se configuró sourceset
 *
 * @return {booleano}
 * devuelve falso si el conjunto de fuentes no se activó y verdadero en caso contrario.
 * /
const sourcesetLoad = (tecnología) => {
  const el = tech.el();

  // si se establece `el.src`, esa fuente se cargará.
  if (el.hasAttribute('src')) {
    tech.triggerSourceset(el.src);
    devolver verdadero;
  }

  /**
   * Dado que no existe una propiedad src en el elemento multimedia, los elementos fuente se utilizarán para
   * implementar el algoritmo de selección de fuente. Esto sucede de forma asíncrona y
   * para la mayoría de los casos donde hay más de una fuente, no podemos decir qué fuente
   * cargarse, sin volver a implementar el algoritmo de selección de fuente. en este momento no estamos
   * va a hacer eso. Sin embargo, hay tres casos especiales que manejamos aquí:
   *
   * 1. Si no hay fuentes, no active 'sourceset'.
   * 2. Si solo hay un `< fuente> ` con una propiedad/atributo `src` que es nuestro `src`
   * 3. Si hay más de un `< fuente> ` pero todos tienen la misma URL `src`.
   * Ese será nuestro src.
   * /
  const fuentes = tech.$$('fuente');
  const srcUrls = [];
  let src = '';

  // si no hay fuentes, no active el conjunto de fuentes
  if (!fuentes.longitud) {
    falso retorno;
  }

  // solo cuenta elementos fuente válidos/no duplicados
  para (sea i = 0; i < fuentes.longitud; i++) {
    const url = fuentes[i].src;

    si (url && srcUrls.indexOf(url) === -1) {
      srcUrls.push(url);
    }
  }

  // no había fuentes válidas
  if (!srcUrls.longitud) {
    falso retorno;
  }

  // solo hay una URL de elemento fuente válida
  // usa eso
  if (srcUrls.longitud === 1) {
    src = srcUrls[0];
  }

  tech.triggerSourceset(src);
  devolver verdadero;
};

/**
 * nuestra implementación de un descriptor `innerHTML` para navegadores
 * que no tienen uno.
 * /
const innerHTMLDescriptorPolyfill = Object.defineProperty({}, 'innerHTML', {
  conseguir() {
    devuelve this.cloneNode(true).innerHTML;
  },
  conjunto (v) {
    // hacer un nodo ficticio para usar innerHTML en
    const dummy = document.createElement(this.nodeName.toLowerCase());

    // establece innerHTML en el valor proporcionado
    ficticio.innerHTML = v;

    // crea un fragmento de documento para contener los nodos del dummy
    const docFrag = documento.createDocumentFragment();

    // copia todos los nodos creados por innerHTML en dummy
    // al fragmento del documento
    while (dummy.childNodes.longitud) {
      docFrag.appendChild(dummy.childNodes[0]);
    }

    // eliminar contenido
    this.innerText = '';

    // ahora agregamos todo ese html en uno agregando el
    // fragmento de documento. Así es como lo hace innerHTML.
    ventana.Elemento.prototipo.appendChild.call(esto, docFrag);

    // luego devuelve el resultado que el setter de innerHTML daría
    devuelve esto.innerHTML;
  }
});

/**
 * Obtener un descriptor de propiedad dada una lista de prioridades y el
 * propiedad para obtener.
 * /
const getDescriptor = (prioridad, propiedad) => {
  let descriptor = {};

  para (sea i = 0; i < prioridad.longitud; i++) {
    descriptor = Object.getOwnPropertyDescriptor(prioridad[i], prop);

    si (descriptor && descriptor.set && descriptor.get) {
      romper;
    }
  }

  descriptor.enumerable = verdadero;
  descriptor.configurable = verdadero;

  descriptor de retorno;
};

const getInnerHTMLDescriptor = (tecnología) => getDescriptor([
  tecnología.el(),
  ventana.HTMLMediaElement.prototipo,
  ventana.Elemento.prototipo,
  Description HTML interno Polyfill
], 'HTML interno');

/**
 * Parches funciones internas del navegador para que podamos decir sincrónicamente
 * si un `< fuente> ` se agregó al elemento multimedia. Por alguna razón esto
 * provoca un `conjunto de fuentes` si el elemento multimedia está listo y no tiene ninguna fuente.
 * Esto sucede cuando:
 * - La página acaba de cargarse y el elemento multimedia no tiene una fuente.
 * - El elemento multimedia se vació de todas las fuentes, luego se llamó a `load()`.
 *
 * Lo hace parcheando las siguientes funciones/propiedades cuando son compatibles:
 *
 * - `append()` - se puede usar para agregar un `< fuente> ` elemento al elemento multimedia
 * - `appendChild()` - se puede usar para agregar un `< fuente> ` elemento al elemento multimedia
 * - `insertAdjacentHTML()` - se puede usar para agregar un `< fuente> ` elemento al elemento multimedia
 * - `innerHTML` - se puede usar para agregar un `< fuente> ` elemento al elemento multimedia
 *
 * @param {Html5} tecnología
 * El objeto tecnológico en el que se está configurando sourceset.
 * /
const firstSourceWatch = función (tecnología) {
  const el = tech.el();

  // asegúrese de que firstSourceWatch no esté configurado dos veces.
  si (el.resetSourceWatch_) {
    devolver;
  }

  const viejo = {};
  const innerDescriptor = getInnerHTMLDescriptor(tecnología);
  const appendWrapper = (appendFn) => (... argumentos) => {
    const retval = appendFn.apply(el, args);

    sourcesetLoad(tecnología);

    recuperación de retorno;
  };

  ['agregar', 'agregarNiño', 'insertarHTMLAdyacente'].forEach((k) => {
    si (!el[k]) {
      devolver;
    }

    // almacena la función anterior
    viejo[k] = el[k];

    // llama a la función anterior con un conjunto de fuentes si es una fuente
    // fue cargado
    el[k] = appendWrapper(antiguo[k]);
  });

  Object.defineProperty(el, 'innerHTML', mergeOptions(innerDescriptor, {
    conjunto: appendWrapper (innerDescriptor.set)
  }));

  el.resetSourceWatch_ = () => {
    el.resetSourceWatch_ = nulo;
    Objeto.claves(antiguo).forEach((k) => {
      el[k] = viejo[k];
    });

    Object.defineProperty(el, 'innerHTML', innerDescriptor);
  };

  // en el primer conjunto de fuentes, necesitamos revertir nuestros cambios
  tech.one('sourceset', el.resetSourceWatch_);
};

/**
 * nuestra implementación de un descriptor `src` para navegadores
 * que no tienen uno.
 * /
const srcDescriptorPolyfill = Object.defineProperty({}, 'src', {
  conseguir() {
    if (this.hasAttribute('src')) {
      return getAbsoluteURL(window.Element.prototype.getAttribute.call(this, 'src'));
    }

    devolver '';
  },
  conjunto (v) {
    window.Element.prototype.setAttribute.call(this, 'src', v);

    volver v;
  }
});

const getSrcDescriptor = (tecnología) => getDescriptor([tech.el(), window.HTMLMediaElement.prototype, srcDescriptorPolyfill], 'src');

/**
 * configurar el manejo de `sourceset` en la tecnología `Html5`. Esta función
 * parchea las siguientes propiedades/funciones de los elementos:
 *
 * - `src` - para determinar cuándo se establece `src`
 * - `setAttribute()` - para determinar cuándo se establece `src`
 * - `load()` - esto vuelve a activar el algoritmo de selección de fuente y puede
 * causar un conjunto de fuentes.
 *
 * Si no hay una fuente cuando estamos agregando soporte para `sourceset` o durante una `carga ()`
 * también parcheamos las funciones enumeradas en `firstSourceWatch`.
 *
 * @param {Html5} tecnología
 * La tecnología para parchear
 * /
const setupSourceset = función (tecnología) {
  if (!tech.featuresSourceset) {
    devolver;
  }

  const el = tech.el();

  // asegúrese de que sourceset no esté configurado dos veces.
  si (el.resetSourceset_) {
    devolver;
  }

  const srcDescriptor = getSrcDescriptor(tech);
  const oldSetAttribute = el.setAttribute;
  const oldLoad = el.load;

  Object.defineProperty(el, 'src', mergeOptions(srcDescriptor, {
    conjunto: (v) => {
      const retval = srcDescriptor.set.call(el, v);

      // usamos el captador aquí para obtener el valor real establecido en src
      tech.triggerSourceset(el.src);

      recuperación de retorno;
    }
  }));

  el.setAttribute = (n, v) => {
    const retval = oldSetAttribute.call(el, n, v);

    si ((/src/i).prueba(n)) {
      tech.triggerSourceset(el.src);
    }

    recuperación de retorno;
  };

  carga eléctrica = () => {
    const retval = oldLoad.call(el);

    // si se llamó a load, pero no había una fuente para disparar
    // conjunto de fuentes activado. Tenemos que estar atentos a una fuente adjunta
    // ya que eso puede desencadenar un `sourceset` cuando el elemento multimedia
    // no tiene fuente
    si (! sourcesetLoad (tecnología)) {
      tech.triggerSourceset('');
      firstSourceWatch(tecnología);
    }

    recuperación de retorno;
  };

  if (el.currentSrc) {
    tech.triggerSourceset(el.currentSrc);
  } más si (! sourcesetLoad (tecnología)) {
    firstSourceWatch(tecnología);
  }

  el.resetSourceset_ = () => {
    el.resetSourceset_ = nulo;
    el.load = oldLoad;
    el.setAttribute = oldSetAttribute;
    Object.defineProperty(el, 'src', srcDescriptor);
    si (el.resetSourceWatch_) {
      el.resetSourceWatch_();
    }
  };
};

exportar el conjunto de fuentes de configuración predeterminado;