'global/window'에서 창 가져오기;
'글로벌/문서'에서 문서 가져오기;
'../utils/merge-options'에서 mergeOptions 가져오기;
'../utils/url'에서 {getAbsoluteURL} 가져오기;

/**
 * 이 함수는 무언가가 있을 때 소스 세트를 실행하는 데 사용됩니다.
 * `mediaEl.load()`가 호출되는 것과 유사합니다. 다음을 통해 소스를 찾으려고 시도합니다.
 * `src` 속성과 `< 원천> ` 요소. 그런 다음 `sourceset`을 실행합니다.
 * 찾은 소스 또는 알 수 없는 경우 빈 문자열. 할 수 없다면
 * 소스를 찾으면 `sourceset`이 실행되지 않습니다.
 *
 * @param {Html5} 기술
 * sourceset이 설정된 기술 객체
 *
 * @return {부울}
 * 소스 세트가 실행되지 않으면 false를 반환하고 그렇지 않으면 true를 반환합니다.
 */
const sourcesetLoad = (기술) => {
  const el = tech.el();

  // `el.src`가 설정되어 있으면 해당 소스가 로드됩니다.
  if (el.hasAttribute('src')) {
    tech.triggerSourceset(el.src);
    true를 반환합니다.
  }

  /**
   * 미디어 요소에 src 속성이 없기 때문에 소스 요소를 사용합니다.
   * 소스 선택 알고리즘 구현. 이는 비동기식으로 발생하며
   * 대부분의 경우 소스가 둘 이상인 경우 어떤 소스가 나올지 알 수 없습니다.
   * 소스 선택 알고리즘을 다시 구현하지 않고 로드됩니다. 이때 우리는
   * 그렇게 할 것입니다. 하지만 여기에서 처리하는 세 가지 특별한 경우가 있습니다.
   *
   * 1. 소스가 없으면 `sourceset`을 실행하지 마십시오.
   * 2. 하나뿐인 경우`< 원천> ` 우리의 `src`인 `src` 속성/속성을 가진 `
   * 삼. 하나 이상인 경우 `< 원천> ` 그러나 그들 모두는 동일한 `src` URL을 가지고 있습니다.
   * 그것이 우리의 src가 될 것입니다.
   */
  const sources = tech.$$('소스');
  const srcUrls = [];
  let src = '';

  // 소스가 없으면 소스셋을 실행하지 않음
  if (!소스.길이) {
    거짓을 반환합니다.
  }

  // 유효한/중복되지 않은 소스 요소만 계산
  에 대한 (하자 i = 0; i < 출처.길이; i++) {
    const url = 소스[i].src;

    만약 (URL && srcUrls.indexOf(url) === -1) {
      srcUrls.push(url);
    }
  }

  // 유효한 소스가 없습니다.
  if (!srcUrls.length) {
    거짓을 반환합니다.
  }

  // 유효한 소스 요소 URL은 하나만 있습니다.
  // 사용
  if (srcUrls.length === 1) {
    src = srcUrls[0];
  }

  tech.triggerSourceset(src);
  true를 반환합니다.
};

/**
 * 브라우저용 `innerHTML` 디스크립터 구현
 * 가지고 있지 않은 것.
 */
const innerHTMLDescriptorPolyfill = Object.defineProperty({}, 'innerHTML', {
  얻다() {
    return this.cloneNode(true).innerHTML;
  },
  집합(v) {
    // innerHTML을 사용할 더미 노드를 만듭니다.
    const dummy = document.createElement(this.nodeName.toLowerCase());

    // innerHTML을 제공된 값으로 설정
    dummy.innerHTML = v;

    // 더미에서 노드를 보유할 문서 조각을 만듭니다.
    const docFrag = document.createDocumentFragment();

    // innerHTML에 의해 생성된 모든 노드를 dummy에 복사합니다.
    // 문서 조각으로
    동안 (dummy.childNodes.length) {
      docFrag.appendChild(dummy.childNodes[0]);
    }

    // 콘텐츠 제거
    this.innerText = '';

    // 이제 우리는
    // 문서 조각. 이것이 innerHTML이 수행하는 방식입니다.
    window.Element.prototype.appendChild.call(this, docFrag);

    // 그런 다음 innerHTML의 setter가 수행할 결과를 반환합니다.
    this.innerHTML을 반환합니다.
  }
});

/**
 * 우선 순위 목록이 주어진 속성 설명자를 가져오고
 * 얻을 속성.
 */
const getDescriptor = (우선 순위, 소품) => {
  let 설명자 = {};

  에 대한 (하자 i = 0; i < 우선순위.길이; i++) {
    디스크립터 = Object.getOwnPropertyDescriptor(priority[i], prop);

    if (설명자 && 디스크립터.세트 && 디스크립터.get) {
      부서지다;
    }
  }

  descriptor.enumerable = 참;
  설명자.구성 가능 = 참;

  반환 설명자;
};

const getInnerHTMLDescriptor = (기술) => getDescriptor([
  tech.el(),
  window.HTMLMediaElement.prototype,
  창.요소.프로토타입,
  innerHTMLDescriptorPolyfill
], 'innerHTML');

/**
 * 동기식으로 알 수 있도록 브라우저 내부 기능 패치
 * `인 경우< 원천> `가 미디어 요소에 추가되었습니다. 왠지 이
 * 미디어 요소가 준비되었고 소스가 없는 경우 `sourceset`을 발생시킵니다.
 * 다음과 같은 경우에 발생합니다.
 * - 페이지가 방금 로드되었으며 미디어 요소에 소스가 없습니다.
 * - 미디어 요소가 모든 소스에서 비워진 다음 `load()`가 호출되었습니다.
 *
 * 지원되는 경우 다음 기능/속성을 패치하여 이를 수행합니다.
 *
 * - `append()` - `를 추가하는 데 사용할 수 있습니다.< 원천> ` 요소를 미디어 요소로
 * - `appendChild()` - `를 추가하는 데 사용할 수 있습니다.< 원천> ` 요소를 미디어 요소로
 * - `insertAdjacentHTML()` - `를 추가하는 데 사용할 수 있습니다.< 원천> ` 요소를 미디어 요소로
 * - `innerHTML` - `를 추가하는 데 사용할 수 있습니다.< 원천> ` 요소를 미디어 요소로
 *
 * @param {Html5} 기술
 * sourceset이 설정되는 기술 개체입니다.
 */
const firstSourceWatch = 함수(기술) {
  const el = tech.el();

  // firstSourceWatch가 두 번 설정되지 않았는지 확인합니다.
  if (el.resetSourceWatch_) {
    반품;
  }

  오래된 const = {};
  const innerDescriptor = getInnerHTMLDescriptor(tech);
  const appendWrapper = (appendFn) => (...인수) => {
    const retval = appendFn.apply(el, args);

    sourcesetLoad(기술);

    반환 반환;
  };

  ['append', 'appendChild', 'insertAdjacentHTML'].forEach((k) => {
    if (!el[k]) {
      반품;
    }

    // 이전 함수 저장
    old[k] = 엘[k];

    // 소스가 있는 경우 소스 세트로 이전 함수를 호출합니다.
    // 로드됨
    el[k] = appendWrapper(old[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', {
  얻다() {
    if (this.hasAttribute('src')) {
      return getAbsoluteURL(window.Element.prototype.getAttribute.call(this, 'src'));
    }

    반품 '';
  },
  집합(v) {
    window.Element.prototype.setAttribute.call(this, 'src', v);

    v를 반환합니다.
  }
});

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

/**
 * `Html5` 기술에서 `sourceset` 처리 설정. 이 기능
 * 다음 요소 속성/기능을 패치합니다.
 *
 * - `src` - `src`가 설정되는 시점을 결정합니다.
 * - `setAttribute()` - `src`가 설정되는 시기 결정
 * - `load()` - 소스 선택 알고리즘을 다시 트리거하고
 * 원인 소스셋.
 *
 * `sourceset` 지원을 추가하거나 `load()` 중에 소스가 없는 경우
 * `firstSourceWatch`에 나열된 기능도 패치합니다.
 *
 * @param {Html5} 기술
 * 패치할 기술
 */
const setupSourceset = 기능(기술) {
  if (!tech.featuresSourceset) {
    반품;
  }

  const el = tech.el();

  // sourceset이 두 번 설정되지 않았는지 확인합니다.
  if (el.resetSourceset_) {
    반품;
  }

  const srcDescriptor = getSrcDescriptor(tech);
  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);

      반환 반환;
    }
  }));

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

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

    반환 반환;
  };

  엘로드 = () => {
    const retval = oldLoad.call(el);

    // 로드가 호출되었지만 실행할 소스가 없는 경우
    // 소스셋을 켭니다. 우리는 소스 추가를 감시해야 합니다.
    // 미디어 요소가
    // 소스가 없습니다
    if (!sourcesetLoad(tech)) {
      tech.triggerSourceset('');
      firstSourceWatch(기술);
    }

    반환 반환;
  };

  if (el.currentSrc) {
    tech.triggerSourceset(el.currentSrc);
  } 그렇지 않으면 (!sourcesetLoad(tech)) {
    firstSourceWatch(기술);
  }

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

기본 setupSourceset 내보내기;