Fenster aus 'global/window' importieren;
dokument aus 'global/document' importieren;
importiere MergeOptions aus '.. /utils/merge-options ';
importiere {getAbsoluteUrl} aus '.. /utils/url ';

/**
 * Diese Funktion wird verwendet, um ein Sourceset auszulösen, wenn etwas vorhanden ist
 * ähnlich wie `Mediael.load () `aufgerufen wird. Es wird versuchen, die Quelle zu finden über
 * das `src`-Attribut und dann die<`source>` -Elemente. Es wird dann `sourceset` auslösen
 * mit der gefundenen Quelle oder einer leeren Zeichenfolge, falls wir es nicht wissen können. Wenn es nicht kann
 * finde eine Quelle dann wird `sourceset` nicht ausgelöst.
 *
 * @param {Html5} tech
 * Das Tech-Objekt, auf dem Sourceset eingerichtet wurde
 *
 * @return {boolean}
 * gibt false zurück, wenn das Sourceset nicht ausgelöst wurde, andernfalls true.
 * /
const sourceSetLoad = (tech) => {
  const el = tech.el();

  //wenn `el.src` gesetzt ist, wird diese Quelle geladen.
  wenn (el.hasAttribute ('src')) {
    tech.triggerSourceset(el.src);
    true zurückgeben;
  }

  /**
   * Da es für das Medienelement keine src-Eigenschaft gibt, werden Quellelemente verwendet für
   * Implementierung des Algorithmus zur Quellenauswahl. Das passiert asynchron und
   * In den meisten Fällen, in denen es mehr als eine Quelle gibt, können wir nicht sagen, welche Quelle
   * geladen werden, ohne den Quellauswahlalgorithmus erneut zu implementieren. Zu diesem Zeitpunkt sind wir nicht
   * werde das machen. Es gibt jedoch drei Sonderfälle, mit denen wir uns hier befassen:
   *
   * 1. Wenn es keine Quellen gibt, feuere `sourceset` nicht aus.
   * 2. Wenn es nur eine `<source>` mit einer `src`-Eigenschaft/einem `src`-Attribut gibt, ist das unser `src`
   * 3. Wenn es mehr als eine `<source>` gibt, aber alle dieselbe `src`-URL haben.
   * Das wird unser SRC sein.
   * /
  const sources = tech.$$ ('Quelle');
  const srCurls = [];
  sei src = „;

  //wenn es keine Quellen gibt, feuere Sourceset nicht
  if (!sources.length) {
    return false;
  }

  //Zähle nur gültige/nicht doppelte Quellelemente
  for (let i = 0; i < sources.length; i++) {
    const url = quellen [i] .src;

    if (url && srCurls.indexOf (url) === -1) {
      srcURLs.push (URL);
    }
  }

  //es gab keine gültigen Quellen
  wenn (! srcurls.Länge) {
    return false;
  }

  //es gibt nur eine gültige URL für das Quellelement
  //benutze das
  wenn (srCURLs.length === 1) {
    src = srCurls [0];
  }

  tech.triggerSourceSet (src);
  true zurückgeben;
};

/**
 * unsere Implementierung eines `innerHTML`-Deskriptors für Browser
 * die nicht über eine solche verfügen.
 * /
const innerHTMLDescriptorPolyFill = Object.defineProperty ({}, 'innerHTML', {
  get() {
    gib this.cloneNode (true) .innerHTML zurück;
  },
  set(v) {
    //erstelle einen Dummy-Node, auf dem innerHTML verwendet werden soll
    const dummy = document.createElement (this.nodeName.toLowerCase ());

    //setze innerHTML auf den angegebenen Wert
    dummy.innerHTML = v;

    //erstelle ein Dokumentfragment, das die Knoten von Dummy enthält
    const docFrag = document.createDocumentFragment ();

    //kopiere alle vom innerHTML erstellten Knoten auf Dummy
    //zum Dokumentfragment
    während (dummy.childNodes.length) {
      docFrag.appendChild (dummy.childNodes [0]);
    }

    //Inhalt entfernen
    this.innerText = „;

    //jetzt fügen wir all diesen HTML-Code in einem hinzu, indem wir das anhängen
    //Dokumentfragment. So macht InnerHTML das.
    window.element.prototype.appendChild.call (das, docFrag);

    //dann das Ergebnis zurückgeben, das der Setter von innerHTML tun würde
    gib this.innerHTML zurück;
  }
});

/**
 * Holen Sie sich einen Eigenschaftsdeskriptor mit einer Prioritätenliste und
 * Eigentum zum Anschaffen.
 * /
const getDescriptor = (Priorität, Requisite) => {
  sei der Deskriptor = {};

  für (sei i = 0; i < Priorität.Länge; i++) {
    Deskriptor = Object.getOwnPropertyDescriptor (Priorität [i], Eigenschaft);

    if (Deskriptor && Deskriptor.set && descriptor.get) {
      pause;
    }
  }

  descriptor.enumerable = wahr;
  descriptor.configurable = wahr;

  Deskriptor zurückgeben;
};

const getInnerHTMLDescriptor = (tech) => getDescriptor ([
  tech.el (),
  Fenster.HtmlMediaElement.Prototyp,
  Fensterelement.Prototyp,
  innerHtmlDescriptorPolyFill
], 'innerHTML');

/**
 * Patcht die internen Funktionen des Browsers, sodass wir synchron erkennen können
 * wenn eine `<Quelle>` an das Medienelement angehängt wurde. Aus irgendeinem Grund das
 * verursacht ein `Sourceset`, wenn das Medienelement bereit ist und keine Quelle hat.
 * Das passiert, wenn:
 * - Die Seite wurde gerade geladen und das Medienelement hat keine Quelle.
 * - Das Medienelement wurde von allen Quellen geleert, dann wurde `load () `aufgerufen.
 *
 * Dazu werden die folgenden Funktionen/Eigenschaften gepatcht, sofern sie unterstützt werden:
 *
 * - `append () `- kann verwendet werden, um dem Medienelement ein<` source>` -Element hinzuzufügen
 * - `appendChild () `- kann verwendet werden, um dem Medienelement ein<` source>` -Element hinzuzufügen
 * - `insertAdjacentHTML () `- kann verwendet werden, um dem Medienelement ein<` source>` -Element hinzuzufügen
 * - `innerHTML` - kann verwendet werden, um dem Medienelement ein<`source>` -Element hinzuzufügen
 *
 * @param {Html5} tech
 * Das Tech-Objekt, für das Sourceset eingerichtet wird.
 * /
const firstSourceWatch = Funktion (Technik) {
  const el = tech.el();

  //stelle sicher, dass FirstSourceWatch nicht zweimal eingerichtet wird.
  if (el.resetSourceWatch_) {
    rückkehr;
  }

  const old = {};
  const innerDescriptor = getInnerHTMLDescriptor (technisch);
  const AppendWrapper = (AppendFn) => (... Argumente) => {
    const retval = appendFn.apply (el, args);

    sourceSetLoad (tech);

    return retval;
  };

  ['anhängen', 'appendChild', 'insertAdjacentHTML'] .forEach (k) => {
    wenn (! el [k]) {
      rückkehr;
    }

    //speichere die alte Funktion
    alt [k] = el [k];

    //rufe die alte Funktion mit einem Sourceset auf, wenn eine Quelle
    //wurde geladen
    el [k] = AppendWrapper (alt [k]);
  });

  Object.defineProperty (de), 'innerHTML', mergeOptions (innerDescriptor, {
    Satz: AppendWrapper (innerDescriptor.set)
  }));

  el.resetSourceWatch_ = () => {
    el.resetSourceWatch_ = null;
    Object.keys (alt) .forEach (k) => {
      el [k] = alt [k];
    });

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

  //Im ersten Sourceset müssen wir unsere Änderungen rückgängig machen
  tech.one ('sourceset', el.resetSourceWatch_);
};

/**
 * unsere Implementierung eines `src`-Deskriptors für Browser
 * die nicht über eine solche verfügen.
 * /
const srcDescriptorPolyFill = Object.defineProperty ({}, 'src', {
  get() {
    wenn (this.hasAttribute ('src')) {
      return getAbsoluteUrl (window.element.prototype.getAttribute.call (this, 'src'));
    }

    zurückgeben '';
  },
  set(v) {
    window.element.prototype.setAttribute.call (this, 'src', v);

    gib v zurück;
  }
});

const getSrcDescriptor = (tech) => getDescriptor ([tech.el (), window.HtmlMediaElement.prototype, srcDescriptorPolyFill], 'src');

/**
 * Richten Sie die `Sourceset`-Handhabung auf der `Html5`-Technologie ein. Diese Funktion
 * patcht die folgenden Elementeigenschaften/Funktionen:
 *
 * - `src` - um festzustellen, wann `src` gesetzt ist
 * - `setAttribute () `- um festzustellen, wann `src` gesetzt ist
 * - `load () `- dies löst den Algorithmus zur Quellenauswahl erneut aus und kann
 * verursacht ein Sourceset.
 *
 * Wenn es keine Quelle gibt, wenn wir die `sourceset`-Unterstützung hinzufügen oder während eines `load () `
 * Wir patchen auch die in `FirstSourceWatch` aufgelisteten Funktionen.
 *
 * @param {Html5} tech
 * Die Technik zum Patchen
 * /
const setupSourceSet = Funktion (Technik) {
  wenn (! tech.featureResourceSet) {
    rückkehr;
  }

  const el = tech.el();

  //stelle sicher, dass Sourceset nicht zweimal eingerichtet wird.
  wenn (el.resetSourceSet_) {
    rückkehr;
  }

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

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

      //wir verwenden den Getter hier, um den tatsächlichen Wert zu erhalten, der auf src gesetzt wurde
      tech.triggerSourceset(el.src);

      return retval;
    }
  }));

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

    wenn ((/src/i) .test (n)) {
      tech.triggerSourceset(el.src);
    }

    return retval;
  };

  el.load = () => {
    const retval = oldLoad.Call (el);

    //wenn load aufgerufen wurde, aber es keine Feuerquelle gab
    //sourceset on. Wir müssen nach einem Quellanhängen Ausschau halten
    //da das ein `Sourceset` auslösen kann, wenn das Medienelement
    //hat keine Quelle
    wenn (! sourceSetLoad (tech)) {
      tech.triggerSourceSet („);
      firstSourceWatch(tech);
    }

    return retval;
  };

  wenn (el.currentSrc) {
    tech.triggerSourceSet (el.CurrentSrc);
  } sonst wenn (! sourceSetLoad (tech)) {
    firstSourceWatch(tech);
  }

  el.resetSourceSet_ = () => {
    el.resetSourceSet_ = null;
    el.load = OldLoad;
    el.setAttribute = Altes SetAttribut;
    Object.defineProperty (de, 'src', srcDescriptor);
    if (el.resetSourceWatch_) {
      el.resetSourceWatch_ ();
    }
  };
};

exportiert das Standard-SetupSourceSet;