/**
 * @파일 dom.js
 * @모듈 돔
 */
'글로벌/문서'에서 문서 가져오기;
'글로벌/창'에서 창 가져오기;
'../fullscreen-api'에서 fs 가져오기;
'./log.js'에서 로그 가져오기;
'./obj'에서 {isObject} 가져오기;
import computedStyle from './computed-style';
* './browser'에서 브라우저로 가져오기;

/**
 * 값이 공백이 아닌 문자가 포함된 문자열인지 감지합니다.
 *
 * @사적인
 * @param {문자열} 문자열
 * 확인할 문자열
 *
 * @return {부울}
 * 문자열이 비어 있지 않으면 `true`, 그렇지 않으면 `false`입니다.
 *
 */
함수 isNonBlankString(str) {
  // str.trim을 사용하여 공백 문자를 제거합니다.
  // 공백이 아닌 문자의 앞이나 뒤에서. 일명
  // 공백이 아닌 문자를 포함하는 모든 문자열은
  // 여전히 `trim` 이후에 포함되지만 공백만 문자열입니다.
  // 길이가 0이 되어 이 검사에 실패합니다.
  반환 typeof str === '문자열' && 부울(str.trim());
}

/**
 * 전달된 문자열에 공백이 있으면 오류가 발생합니다. 이것은 다음에 의해 사용됩니다
 * classList API와 상대적으로 일치하는 클래스 메소드.
 *
 * @사적인
 * @param {문자열} 문자열
 * 공백을 확인할 문자열입니다.
 *
 * @throws {오류}
 * 문자열에 공백이 있으면 오류가 발생합니다.
 */
함수 throwIfWhitespace(str) {
  // str.indexOf가 성능 측면에서 더 빠르기 때문에 regex 대신 str.indexOf를 사용합니다.
  if (str.indexOf(' ') > = 0) {
    throw new Error('클래스에 잘못된 공백 문자가 있습니다.');
  }
}

/**
 * 요소 className 내에서 className을 일치시키기 위한 정규식을 생성합니다.
 *
 * @사적인
 * @param {문자열} 클래스 이름
 * RegExp를 생성할 className입니다.
 *
 * @return {정규 표현식}
 * 요소에서 특정 `className`을 확인할 RegExp
 * 클래스명.
 */
함수 classRegExp(클래스 이름) {
  return new RegExp('(^|\\s)' + className + '($|\\s)');
}

/**
 * 현재 DOM 인터페이스가 실제처럼 보이는지 여부(즉, 시뮬레이션되지 않음).
 *
 * @return {부울}
 * DOM이 진짜로 보이면 `true`, 그렇지 않으면 `false`가 됩니다.
 */
내보내기 기능 isReal() {
  // 문서와 창 모두 `global` 덕분에 정의되지 않습니다.
  반환 문서 === window.document;
}

/**
 * 덕 타이핑을 통해 값이 DOM 요소인지 여부를 결정합니다.
 *
 * @param {혼합} 값
 * 확인할 값입니다.
 *
 * @return {부울}
 * 값이 DOM 요소이면 `true`, 그렇지 않으면 `false`입니다.
 */
내보내기 함수 isEl(value) {
  isObject(값) 반환 && 값.노드타입 === 1;
}

/**
 * 현재 DOM이 iframe에 포함되어 있는지 확인합니다.
 *
 * @return {부울}
 * DOM이 iframe에 포함된 경우 `true`, `false`
 * 그렇지 않으면.
 */
내보내기 기능 isInFrame() {

  // 시도할 때 Safari에서 오류가 발생하므로 여기에서 try/catch가 필요합니다.
  // `parent` 또는 `self`를 가져오기 위해
  {
    return window.parent !== window.self;
  } 잡기 (x) {
    true를 반환합니다.
  }
}

/**
 * 주어진 메서드를 사용하여 DOM을 쿼리하는 함수를 만듭니다.
 *
 * @사적인
 * @param {문자열} 메서드
 * 쿼리를 만드는 방법입니다.
 *
 * @return {함수}
 * 쿼리 방식
 */
함수 createQuery(메서드) {
  반환 함수(선택자, 컨텍스트) {
    if (!isNonBlankString(선택자)) {
      return 문서[방법](null);
    }
    if (isNonBlankString(context)) {
      컨텍스트 = document.querySelector(컨텍스트);
    }

    const ctx = isEl(컨텍스트) ? 컨텍스트: 문서;

    ctx[메서드] 반환 && ctx[방법](선택자);
  };
}

/**
 * 요소를 생성하고 속성, 속성을 적용하고 내용을 삽입합니다.
 *
 * @param {string} [tagName='div']
 * 생성할 태그의 이름.
 *
 * @param {객체} [속성={}]
 * 적용할 요소 속성.
 *
 * @param {객체} [속성={}]
 * 적용할 요소 속성.
 *
 * @param {module:dom~ContentDescriptor} 콘텐츠
 * 콘텐츠 설명자 개체입니다.
 *
 * @return {요소}
 * 생성된 요소입니다.
 */
내보내기 기능 createEl(tagName = 'div', 속성 = {}, 속성 = {}, 콘텐츠) {
  const el = document.createElement(태그 이름);

  Object.getOwnPropertyNames(properties).forEach(function(propName) {
    const val = 속성[propName];

    // #2176 참조
    // 우리는 원래 속성과 속성을 모두 허용했습니다.
    // 같은 객체이지만 잘 작동하지 않습니다.
    if (propName.indexOf('aria-') !== -1 || propName === '역할' || propName === '유형') {
      log.warn('createEl()의 두 번째 인수에 속성 설정\n' +
               '가 폐기되었습니다. 대신 세 번째 인수를 사용하세요.\n' +
               `createEl(유형, 속성, 속성). ${propName}을(를) ${val}(으)로 설정하려고 합니다.`);
      el.setAttribute(propName, val);

    // textContent는 모든 곳에서 지원되지 않으므로 처리합니다.
    // 방법입니다.
    } 그렇지 않으면 (propName === 'textContent') {
      textContent(el, val);
    } else if (el[propName] !== val || propName === 'tabIndex') {
      el[propName] = 값;
    }
  });

  Object.getOwnPropertyNames(attributes).forEach(function(attrName) {
    el.setAttribute(attrName, 속성[attrName]);
  });

  if (내용) {
    appendContent(엘, 콘텐츠);
  }

  반환 엘;
}

/**
 * 요소에 텍스트를 삽입하여 기존 내용을 완전히 바꿉니다.
 *
 * @param {요소} 엘
 * 텍스트 콘텐츠를 추가할 요소
 *
 * @param {문자열} 텍스트
 * 추가할 텍스트 내용입니다.
 *
 * @return {요소}
 * 텍스트 콘텐츠가 추가된 요소입니다.
 */
내보내기 기능 textContent(el, text) {
  if (typeof el.textContent === '정의되지 않음') {
    el.innerText = 텍스트;
  } else {
    el.textContent = 텍스트;
  }
  반환 엘;
}

/**
 * 요소를 다른 요소의 첫 번째 자식 노드로 삽입
 *
 * @param {요소} 자식
 * 삽입할 요소
 *
 * @param {요소} 부모
 * 자식을 삽입할 요소
 */
내보내기 기능 prependTo(자식, 부모) {
  경우 (부모.첫 번째 자식) {
    parent.insertBefore(자식, 부모.firstChild);
  } else {
    parent.appendChild(자식);
  }
}

/**
 * 요소에 클래스 이름이 있는지 확인하십시오.
 *
 * @param {요소} 요소
 * 확인할 요소
 *
 * @param {string} classToCheck
 * 확인할 클래스 이름
 *
 * @return {부울}
 * 요소에 클래스가 있으면 `true`, 그렇지 않으면 `false`가 됩니다.
 *
 * @throws {오류}
 * `classToCheck`에 공백이 있으면 오류가 발생합니다.
 */
내보내기 기능 hasClass(요소, classToCheck) {
  throwIfWhitespace(classToCheck);
  if (요소.클래스리스트) {
    return element.classList.contains(classToCheck);
  }
  return classRegExp(classToCheck).test(element.className);
}

/**
 * 요소에 클래스 이름을 추가합니다.
 *
 * @param {요소} 요소
 * 클래스 이름을 추가할 요소.
 *
 * @param {문자열} classToAdd
 * 추가할 클래스 이름.
 *
 * @return {요소}
 * 클래스 이름이 추가된 DOM 요소.
 */
내보내기 기능 addClass(요소, classToAdd) {
  if (요소.클래스리스트) {
    element.classList.add(classToAdd);

  // `hasElClass`가 수행하므로 여기서 `throwIfWhitespace`를 수행할 필요가 없습니다.
  // classList가 지원되지 않는 경우.
  } 그렇지 않으면 (!hasClass(요소, classToAdd)) {
    element.className = (element.className + ' ' + classToAdd).trim();
  }

  반환 요소;
}

/**
 * 요소에서 클래스 이름을 제거합니다.
 *
 * @param {요소} 요소
 * 클래스 이름을 제거할 요소.
 *
 * @param {string} classToRemove
 * 제거할 클래스 이름
 *
 * @return {요소}
 * 클래스 이름이 제거된 DOM 요소.
 */
내보내기 기능 removeClass(요소, classToRemove) {
  // 플레이어가 처리되는 경우 보호
  경우 (! 요소) {
    log.warn("removeClass가 존재하지 않는 요소로 호출되었습니다.");
    null을 반환합니다.
  }
  if (요소.클래스리스트) {
    element.classList.remove(classToRemove);
  } else {
    throwIfWhitespace(classToRemove);
    element.className = element.className.split(/\s+/).filter(function(c) {
      c 반환 !== classToRemove;
    }).가입하다(' ');
  }

  반환 요소;
}

/**
 * toggleClass에 대한 콜백 정의입니다.
 *
 * @callback 모듈:dom~PredicateCallback
 * @param {요소} 요소
 * 컴포넌트의 DOM 요소.
 *
 * @param {문자열} classToToggle
 * 토글하려는 `className`
 *
 * @return {부울|정의되지 않음}
 * `true`가 반환되면 `classToToggle`이
 * `요소`. `false`인 경우 `classToToggle`이 다음에서 제거됩니다.
 * '요소'. `undefined`인 경우 콜백이 무시됩니다.
 */

/**
 * 옵션에 따라 요소에 클래스 이름을 추가하거나 요소에서 제거합니다.
 * 조건 또는 클래스 이름의 유무.
 *
 * @param {요소} 요소
 * 클래스 이름을 켜는 요소입니다.
 *
 * @param {문자열} 클래스투토글
 * 토글되어야 하는 클래스.
 *
 * @param {부울|모듈:dom~PredicateCallback} [술어]
 * {@link module:dom~PredicateCallback}에 대한 반환 값 참조
 *
 * @return {요소}
 * 토글된 클래스가 있는 요소.
 */
내보내기 기능 toggleClass(요소, classToToggle, 술어) {

  // 이것은 IE11이 지원하지 않기 때문에 내부적으로 `classList`를 사용할 수 없습니다.
  // `classList.toggle()` 메서드의 두 번째 매개변수! 어떤 것이 좋기 때문에
  // 'classList'는 추가/제거 기능에서 사용됩니다.
  const has = hasClass(element, classToToggle);

  if (술어 유형 === '함수') {
    술어 = 술어(요소, classToToggle);
  }

  if (술어 유형 !== '부울') {
    술어 = !has;
  }

  // 필요한 클래스 연산이 클래스의 현재 상태와 일치하는 경우
  // 요소, 조치가 필요하지 않습니다.
  if (술어 ===가) {
    반품;
  }

  if (술어) {
    addClass(요소, classToggle);
  } else {
    removeClass(요소, classToggle);
  }

  반환 요소;
}

/**
 * HTML 요소에 속성을 적용합니다.
 *
 * @param {요소} 엘
 * 속성을 추가할 요소.
 *
 * @param {객체} [속성]
 * 적용할 속성입니다.
 */
내보내기 기능 setAttributes(el, 속성) {
  Object.getOwnPropertyNames(attributes).forEach(function(attrName) {
    const attrValue = 속성[attrName];

    if (attrValue === null || typeof attrValue === 'undefined' || attrValue === false) {
      el.removeAttribute(attrName);
    } else {
      el.setAttribute(attrName, (attrValue === true ? '' : attrValue));
    }
  });
}

/**
 * HTML 태그에 정의된 대로 요소의 속성 값을 가져옵니다.
 *
 * 속성은 속성과 동일하지 않습니다. 태그에 정의되어 있습니다.
 * 또는 setAttribute 사용.
 *
 * @param {요소} 태그
 * 태그 속성을 가져올 요소.
 *
 * @return {객체}
 * 요소의 모든 속성. 부울 속성은 'true'이거나
 * `false`, 나머지는 문자열입니다.
 */
내보내기 기능 getAttributes(태그) {
  const 개체 = {};

  // 알려진 부울 속성
  // 일치하는 부울 속성을 확인할 수 있지만 모든 브라우저가 그런 것은 아닙니다.
  // 모든 태그가 이러한 속성에 대해 아는 것은 아니므로 여전히 수동으로 확인해야 합니다.
  const knownBooleans = ',' + 'autoplay,controls,playsinline,loop,muted,default,defaultMuted' + ',';

  만약 (태그 && 태그.속성 && 태그.속성.길이 > 0) {
    const attrs = tag.attributes;

    for (let i = attrs.length - 1; i > = 0; 나--) {
      const attrName = attrs[i].name;
      let attrVal = attrs[i].value;

      // 알려진 부울을 확인합니다.
      // 일치하는 요소 속성은 typeof에 대한 값을 반환합니다.
      if (typeof tag[attrName] === 'boolean' || knownBooleans.indexOf(',' + attrName + ',') !== -1) {
        // 포함된 부울 속성의 값은 일반적으로 비어 있습니다.
        // 문자열('')은 거짓 값만 확인하면 거짓이 됩니다.
        // 또한 autoplay='false'와 같은 잘못된 코드를 지원하는 것을 원하지 않습니다.
        attrVal = (attrVal !== null) ? 허위 사실;
      }

      obj[attrName] = attrVal;
    }
  }

  객체 반환;
}

/**
 * 요소의 속성 값을 가져옵니다.
 *
 * @param {요소} 엘
 * DOM 요소.
 *
 * @param {문자열} 속성
 * 값을 가져올 속성입니다.
 *
 * @return {문자열}
 * 속성 값입니다.
 */
내보내기 기능 getAttribute(el, attribute) {
  return el.getAttribute(속성);
}

/**
 * 요소의 속성 값을 설정합니다.
 *
 * @param {요소} 엘
 * DOM 요소.
 *
 * @param {문자열} 속성
 * 설정할 속성.
 *
 * @param {문자열} 값
 * 속성을 설정할 값.
 */
내보내기 기능 setAttribute(el, 속성, 값) {
  el.setAttribute(속성, 값);
}

/**
 * 요소의 속성을 제거합니다.
 *
 * @param {요소} 엘
 * DOM 요소.
 *
 * @param {문자열} 속성
 * 제거할 속성입니다.
 */
내보내기 기능 removeAttribute(el, attribute) {
  el.removeAttribute(속성);
}

/**
 * 텍스트를 선택하는 기능을 차단하려고 시도합니다.
 */
수출 기능 blockTextSelection() {
  document.body.focus();
  document.onselectstart = 함수() {
    거짓을 반환합니다.
  };
}

/**
 * 텍스트 선택 차단을 끕니다.
 */
내보내기 기능 unblockTextSelection() {
  document.onselectstart = 함수() {
    true를 반환합니다.
  };
}

/**
 * 기본 `getBoundingClientRect` 함수와 동일하지만 다음을 보장합니다.
 * 이 방법은 전혀 지원되지 않습니다(지원한다고 주장하는 모든 브라우저에 있음)
 * 계속하기 전에 해당 요소가 DOM에 있는지 확인합니다.
 *
 * 이 래퍼 함수는 일부에서 제공하지 않는 속성도 shim합니다.
 * 이전 브라우저(즉, IE8).
 *
 * 또한 일부 브라우저는 속성 추가를 지원하지 않습니다.
 * `ClientRect`/`DOMRect` 개체; 그래서 우리는 그것을 표준으로 얕게 복사합니다.
 * 속성(널리 지원되지 않는 `x` 및 `y` 제외). 도움이 됩니다.
 * 키를 열거할 수 없는 구현을 피하십시오.
 *
 * @param {요소} 엘
 * `ClientRect`를 계산하려는 요소.
 *
 * @return {객체|정의되지 않음}
 * 항상 일반 객체를 반환하거나 그렇지 않은 경우 `정의되지 않음`을 반환합니다.
 */
내보내기 기능 getBoundingClientRect(el) {
  만약 (엘 && el.getBoundingClientRect && el.parentNode) {
    const rect = el.getBoundingClientRect();
    const 결과 = {};

    ['하단', '높이', '왼쪽', '오른쪽', '상단', '너비'].forEach(k => {
      if (rect[k] !== 정의되지 않음) {
        결과[k] = rect[k];
      }
    });

    if (!결과.높이) {
      result.height = parseFloat(computedStyle(el, '높이'));
    }

    if (!결과.폭) {
      result.width = parseFloat(computedStyle(el, 'width'));
    }

    반환 결과;
  }
}

/**
 * 페이지에서 DOM 요소의 위치를 나타냅니다.
 *
 * @typedef {객체} 모듈:dom~Position
 *
 * @property {숫자} 남음
 * 왼쪽 픽셀.
 *
 * @property {숫자} 상단
 * 상단부터 픽셀.
 */

/**
 * DOM에서 요소의 위치를 가져옵니다.
 *
 * John Resig의 `getBoundingClientRect` 기법을 사용합니다.
 *
 * @see http://ejohn.org/blog/getboundingclientrect-is-awesome/
 *
 * @param {요소} 엘
 * 오프셋을 가져올 요소.
 *
 * @return {module:dom~Position}
 * 전달된 요소의 위치입니다.
 */
내보내기 기능 findPosition(el) {
  if (!엘 || (엘 && !el.offsetParent)) {
    반환 {
      왼쪽: 0,
      맨 위: 0,
      폭: 0,
      높이: 0
    };
  }
  const 너비 = el.offsetWidth;
  const 높이 = el.offsetHeight;
  왼쪽으로 = 0;
  상단 = 0;

  동안 (el.offsetParent && 엘 !== 문서[fs.fullscreenElement]) {
    왼쪽 += el.offsetLeft;
    상단 += el.offsetTop;

    el = el.offsetParent;
  }

  반환 {
    왼쪽,
    맨 위,
    너비,
    신장
  };
}

/**
 * DOM 요소 또는 마우스 포인터의 x 및 y 좌표를 나타냅니다.
 *
 * @typedef {객체} 모듈:dom~Coordinates
 *
 * @property {숫자} x
 * x 좌표(픽셀 단위)
 *
 * @property {번호} y
 * 픽셀 단위의 y 좌표
 */

/**
 * 요소 내에서 포인터 위치를 가져옵니다.
 *
 * 좌표의 기준은 요소의 왼쪽 하단입니다.
 *
 * @param {요소} 엘
 * 포인터 위치를 가져올 요소.
 *
 * @param {EventTarget~Event} 이벤트
 * 이벤트 객체.
 *
 * @return {module:dom~Coordinates}
 * 마우스 위치에 해당하는 좌표 객체.
 *
 */
내보내기 기능 getPointerPosition(el, event) {
  번역된 const = {
    엑스: 0,
    와이: 0
  };

  경우 (브라우저.IS_IOS) {
    let item = 엘;

    동안 (항목 && item.nodeName.toLowerCase() !== 'html') {
      const transform = computedStyle(item, 'transform');

      if (/^matrix/.test(transform)) {
        const values = transform.slice(7, -1).split(/,\s/).map(Number);

        translate.x += values[4];
        translate.y += values[5];
      } 그렇지 않으면 (/^matrix3d/.test(변환)) {
        const values = transform.slice(9, -1).split(/,\s/).map(Number);

        translate.x += values[12];
        translate.y += values[13];
      }

      item = item.parentNode;
    }
  }

  상수 위치 = {};
  const boxTarget = findPosition(event.target);
  const box = findPosition(el);
  const boxW = box.width;
  const boxH = 상자 높이;
  let offsetY = event.offsetY - (box.top - boxTarget.top);
  let offsetX = event.offsetX - (box.left - boxTarget.left);

  if (event.changedTouches) {
    offsetX = event.changedTouches[0].pageX - box.left;
    offsetY = event.changedTouches[0].pageY + box.top;
    경우 (브라우저.IS_IOS) {
      offsetX -= translate.x;
      offsetY -= translate.y;
    }
  }

  position.y = (1 - Math.max(0, Math.min(1, offsetY / boxH)));
  position.x = Math.max(0, Math.min(1, offsetX / boxW));
  반환 위치;
}

/**
 * 덕 타이핑을 통해 값이 텍스트 노드인지 여부를 결정합니다.
 *
 * @param {혼합} 값
 * 이 값이 텍스트 노드인지 확인하십시오.
 *
 * @return {부울}
 * 값이 텍스트 노드이면 `true`, 그렇지 않으면 `false`입니다.
 */
내보내기 기능 isTextNode(value) {
  isObject(값) 반환 && 값.노드타입 === 3;
}

/**
 * 요소의 내용을 비웁니다.
 *
 * @param {요소} 엘
 * 자식을 비울 요소
 *
 * @return {요소}
 * 자식이 없는 요소
 */
내보내기 기능 emptyEl(el) {
  동안 (el.firstChild) {
    el.removeChild(el.firstChild);
  }
  반환 엘;
}

/**
 * DOM에 삽입할 내용을 기술하는 혼합된 값입니다.
 * 어떤 방법을 통해. 다음과 같은 유형일 수 있습니다.
 *
 * 유형 | 설명
 * -----------|-------------
 * `문자열` | 값은 텍스트 노드로 정규화됩니다.
 * `요소` | 값은 있는 그대로 수락됩니다.
 * `텍스트노드` | 값은 있는 그대로 수락됩니다.
 * `배열` | 문자열, 요소, 텍스트 노드 또는 함수의 1차원 배열입니다. 이러한 함수는 문자열, 요소 또는 텍스트 노드를 반환해야 합니다(배열과 같은 다른 반환 값은 무시됨).
 * `함수` | 문자열, 요소, 텍스트 노드 또는 배열(위에 설명된 다른 가능한 값)을 반환할 것으로 예상되는 함수입니다. 즉, 콘텐츠 설명자는 함수 배열을 반환하는 함수일 수 있지만 이러한 두 번째 수준 함수는 문자열, 요소 또는 텍스트 노드를 반환해야 합니다.
 *
 * @typedef {string|요소|TextNode|배열|함수} module:dom~ContentDescriptor
 */

/**
 * 최종적으로 DOM에 삽입하기 위해 콘텐츠를 정규화합니다.
 *
 * 이것은 광범위한 콘텐츠 정의 방법을 허용하지만 보호하는 데 도움이 됩니다.
 * 단순히 `innerHTML`에 작성하는 함정에 빠지지 않도록 합니다.
 * XSS 문제가 되십시오.
 *
 * 요소의 내용은 여러 유형으로 전달될 수 있으며
 * 조합의 동작은 다음과 같습니다.
 *
 * @param {module:dom~ContentDescriptor} 콘텐츠
 * 콘텐츠 설명자 값입니다.
 *
 * @return {배열}
 * 전달된 모든 콘텐츠는 다음의 배열로 정규화됩니다.
 * 요소 또는 텍스트 노드.
 */
내보내기 기능 normalizeContent(content) {

  // 먼저 콘텐츠가 함수인 경우 호출합니다. 배열을 생성하는 경우,
  // 정규화 전에 발생해야 합니다.
  if (콘텐츠 유형 === '함수') {
    내용 = 내용();
  }

  // 다음으로 배열로 정규화하여 하나 이상의 항목을 정규화할 수 있습니다.
  // 필터링되어 반환됩니다.
  return (Array.isArray(내용) ? 내용 : [내용]).map(값 => {

    // 먼저 새로운 값을 생성하는 함수라면 value를 호출하고,
    // 이후에 어떤 종류의 노드로 정규화됩니다.
    if (값 유형 === '함수') {
      값 = 값();
    }

    if (isEl(값) || isTextNode(값)) {
      반환 값;
    }

    if (값 유형 === '문자열' && (/\S/).테스트(값)) {
      return document.createTextNode(값);
    }
  }).필터(값 => 값);
}

/**
 * 콘텐츠를 정규화하고 요소에 추가합니다.
 *
 * @param {요소} 엘
 * 정규화된 콘텐츠를 추가할 요소.
 *
 * @param {module:dom~ContentDescriptor} 콘텐츠
 * 콘텐츠 설명자 값입니다.
 *
 * @return {요소}
 * 정규화된 콘텐츠가 추가된 요소입니다.
 */
내보내기 기능 appendContent(el, content) {
  normalizeContent(콘텐츠).forEach(노드 => el.appendChild(노드));
  반환 엘;
}

/**
 * 콘텐츠를 정규화하고 요소에 삽입합니다. 이것은 동일하다
 * `appendContent()`, 요소를 먼저 비운다는 점만 제외.
 *
 * @param {요소} 엘
 * 정규화된 콘텐츠를 삽입할 요소입니다.
 *
 * @param {module:dom~ContentDescriptor} 콘텐츠
 * 콘텐츠 설명자 값입니다.
 *
 * @return {요소}
 * 정규화된 콘텐츠가 삽입된 요소입니다.
 */
내보내기 기능 insertContent(el, content) {
  return appendContent(emptyEl(el), content);
}

/**
 * 이벤트가 단일 왼쪽 클릭인지 확인하십시오.
 *
 * @param {EventTarget~Event} 이벤트
 * 이벤트 객체.
 *
 * @return {부울}
 * 왼쪽 클릭이 한 번이면 'true', 그렇지 않으면 'false'가 됩니다.
 */
내보내기 기능 isSingleLeftClick(event) {
  // 참고: 드래그할 수 있는 항목을 만드는 경우
  // `mousedown` 및 `mousemove` 이벤트 모두에서 호출합니다.
  // 그렇지 않으면 'mousedown'이 버튼에 충분해야 합니다.

  if (event.button === 정의되지 않음 && event.buttons === 정의되지 않음) {
    // `버튼`이 필요한 이유는 무엇입니까?
    // 가운데 마우스에는 때때로 다음이 있기 때문입니다.
    // e.버튼 === 0 및 e.버튼 === 4
    // 또한 조합 클릭을 방지하고 싶습니다.
    // 가운데 마우스를 누른 상태에서 왼쪽 클릭하면
    // e.버튼 === 0, e.버튼 === 5
    // `버튼`은 작동하지 않습니다.

    // 좋습니다. 그러면 이 블록은 무엇을 합니까?
    // 크롬 `simulate mobile devices`용입니다.
    // 이것도 지원하고 싶습니다.

    true를 반환합니다.
  }

  if (이벤트 버튼 === 0 && event.buttons === 정의되지 않음) {
    // 터치 스크린, 때로는 일부 특정 장치에서 `버튼`
    // 아무것도 없음(iOS의 사파리, 블랙베리...)

    true를 반환합니다.
  }

  // 단일 왼쪽 클릭에 대한 `mouseup` 이벤트는
  // `버튼` 및 `버튼` = 0
  if (event.type === 'mouseup' && 이벤트 버튼 === 0 &&
      이벤트 버튼 === 0) {
    true를 반환합니다.
  }

  if (이벤트.버튼 !== 0 || 이벤트.버튼 !== 1) {
    // 이것이 위의 if else 블록이 있는 이유입니다.
    // 특수한 경우 잡아서 미끄러지게 할 수 있습니다.
    // 위의 작업을 수행합니다. 여기에 도달하면 확실히
    // is-not-left-click

    거짓을 반환합니다.
  }

  true를 반환합니다.
}

/**
 * 선택 항목 내에서 `selector`와 일치하는 단일 DOM 요소를 찾습니다.
 * 다른 DOM 요소의 `context`(기본값은 `document`).
 *
 * @param {문자열} 선택자
 * `querySelector`에 전달되는 유효한 CSS 선택기.
 *
 * @param {요소|문자열} [컨텍스트=문서]
 * 조회할 DOM 요소. 셀렉터로도 가능
 * 문자열 이 경우 첫 번째로 일치하는 요소가 사용됩니다.
 * 문맥으로. 누락된 경우(또는 선택기와 일치하는 요소가 없는 경우)
 * `문서`로 돌아가기.
 *
 * @return {요소|null}
 * 찾은 요소 또는 null입니다.
 */
export const $ = createQuerie('querySelector');

/**
 * 선택 항목 내에서 `selector`와 일치하는 모든 DOM 요소를 찾습니다.
 * 다른 DOM 요소의 `context`(기본값은 `document`).
 *
 * @param {문자열} 선택자
 * `querySelectorAll`에 전달되는 유효한 CSS 선택기.
 *
 * @param {요소|문자열} [컨텍스트=문서]
 * 조회할 DOM 요소. 셀렉터로도 가능
 * 문자열 이 경우 첫 번째로 일치하는 요소가 사용됩니다.
 * 문맥으로. 누락된 경우(또는 선택기와 일치하는 요소가 없는 경우)
 * `문서`로 돌아가기.
 *
 * @return {노드리스트}
 * 찾은 요소의 요소 목록입니다. 없는 경우 비어 있음
 * 발견되었습니다.
 *
 */
export const $$ = createQuerie('querySelectorAll');