/**
* @파일 이벤트.js. 이벤트 시스템(John Resig - JS Ninja의 비밀 http://jsninja.com/)
* (원본 버전은 완전히 사용할 수 없었기 때문에 몇 가지 사항을 수정하고 클로저 컴파일러와 호환되도록 했습니다.)
* 이것은 jQuery의 이벤트와 매우 유사하게 작동해야 하지만 책 버전을 기반으로 합니다.
* jquery만큼 강력하므로 약간의 차이가 있을 수 있습니다.
*
* @파일 이벤트.js
* @module 이벤트
*/
'./dom-data'에서 DomData 가져오기;
import * as Guid from './guid.js';
'./log.js'에서 로그 가져오기;
'글로벌/창'에서 창 가져오기;
'글로벌/문서'에서 문서 가져오기;
/**
* 리스너 캐시 및 디스패처 정리
*
* @param {요소|객체} 요소
* 정리할 요소
*
* @param {문자열} 유형
* 정리할 이벤트 유형
*/
function _cleanUpEvents(요소, 유형) {
if (!DomData.has(elem)) {
반품;
}
const 데이터 = DomData.get(elem);
// 남아있는 이벤트가 없으면 특정 유형의 이벤트를 제거합니다.
if (data.handlers[유형].길이 === 0) {
데이터 삭제.핸들러[유형];
// data.handlers[유형] = null;
// null로 설정하면 data.handlers에서 오류가 발생했습니다.
// 요소에서 메타 핸들러를 제거합니다.
if (elem.removeEventListener) {
elem.removeEventListener(type, data.dispatcher, false);
} 그렇지 않으면 (elem.detachEvent) {
elem.detachEvent('on' + type, data.dispatcher);
}
}
// 남은 유형이 없으면 이벤트 객체를 제거합니다.
if (Object.getOwnPropertyNames(data.handlers).length < = 0) {
data.handlers 삭제;
data.dispatcher 삭제;
데이터 삭제.비활성화;
}
// 데이터가 남아 있지 않으면 마지막으로 요소 데이터를 제거합니다.
if (Object.getOwnPropertyNames(data).length === 0) {
DomData.delete(elem);
}
}
/**
* 이벤트 유형 배열을 반복하고 각 유형에 대해 요청된 메서드를 호출합니다.
*
* @param {함수} fn
* 우리가 사용하고자 하는 이벤트 메소드.
*
* @param {요소|객체} 요소
* 리스너를 바인딩할 요소 또는 객체
*
* @param {문자열} 유형
* 바인딩할 이벤트 유형.
*
* @param {EventTarget~EventListener} 콜백
* 이벤트 리스너.
*/
function _handleMultipleEvents(fn, 요소, 유형, 콜백) {
types.forEach(함수(유형) {
// 각 유형에 대한 이벤트 메소드를 호출합니다.
fn(요소, 유형, 콜백);
});
}
/**
* 표준 속성 값을 갖도록 기본 이벤트 수정
*
* @param {객체} 이벤트
* 수정할 이벤트 개체입니다.
*
* @return {객체}
* 고정 이벤트 개체.
*/
내보내기 기능 fixEvent(event) {
경우 (event.fixed_) {
반환 이벤트;
}
함수 returnTrue() {
true를 반환합니다.
}
함수 returnFalse() {
거짓을 반환합니다.
}
// 수정이 필요한지 테스트
// isPropagationStopped 대신 !event.stopPropagation인지 확인하는 데 사용됩니다.
// 하지만 네이티브 이벤트는 stopPropagation에 대해 true를 반환하지만
// isPropagationStopped와 같은 다른 예상 메서드입니다. 문제인듯
// Javascript Ninja 코드로. 이제 모든 이벤트를 무시하고 있습니다.
if (!event || !event.isPropagationStopped || !event.isImmediatePropagationStopped) {
const 이전 = 이벤트 || 창.이벤트;
이벤트 = {};
// 값을 수정할 수 있도록 이전 개체를 복제합니다. event = {};
// IE8은 기본 이벤트 속성을 엉망으로 만드는 것을 좋아하지 않습니다.
// Firefox는 event.hasOwnProperty('type') 및 기타 소품에 대해 false를 반환합니다.
// 복사를 더 어렵게 만듭니다.
// 할 것: 이벤트 소품의 화이트리스트를 만드는 것이 가장 좋습니다.
for (예전의 const 키) {
// 더 이상 사용되지 않는 layerX/Y를 복사하려고 하면 Safari 6.0.3에서 경고합니다.
// 지원 중단된 keyboardEvent.keyLocation을 복사하려고 하면 Chrome에서 경고를 표시합니다.
// 그리고 webkitMovementX/Y
// Lighthouse는 Event.path가 복사되면 불평합니다.
if (키 !== '레이어X' && 키 !== '레이어Y' && 키 !== '키 위치' &&
키 !== 'webkitMovementX' && 키 !== 'webkitMovementY' &&
키 !== '경로') {
// Chrome 32+는 더 이상 사용되지 않는 returnValue를 복사하려고 하면 경고하지만
// preventDefault가 지원되지 않는 경우(IE8) 여전히 원합니다.
if (!(키 === '반환값' && old.preventDefault)) {
이벤트[키] = 이전[키];
}
}
}
// 이 요소에서 발생한 이벤트
if (!이벤트.대상) {
event.target = event.srcElement || 문서;
}
// 이벤트와 관련된 다른 요소를 처리합니다.
if (!event.relatedTarget) {
event.relatedTarget = event.fromElement === event.target ?
event.toElement :
이벤트.fromElement;
}
// 기본 브라우저 동작 중지
event.preventDefault = 함수() {
경우 (old.preventDefault) {
old.preventDefault();
}
event.returnValue = 거짓;
old.returnValue = 거짓;
event.defaultPrevented = 참;
};
event.defaultPrevented = 거짓;
// 이벤트 버블링 중지
event.stopPropagation = 함수() {
if (old.stopPropagation) {
old.stopPropagation();
}
event.cancelBubble = 참;
old.cancelBubble = 참;
event.isPropagationStopped = returnTrue;
};
event.isPropagationStopped = returnFalse;
// 이벤트가 버블링되고 다른 핸들러가 실행되는 것을 중지합니다.
event.stopImmediatePropagation = 함수() {
if (old.stopImmediatePropagation) {
old.stopImmediatePropagation();
}
event.isImmediatePropagationStopped = returnTrue;
event.stopPropagation();
};
event.isImmediatePropagationStopped = returnFalse;
// 마우스 위치 처리
if (event.clientX !== null && event.clientX !== 정의되지 않음) {
const doc = 문서.문서 요소;
const 본문 = 문서.본문;
event.pageX = event.clientX +
(문서 && doc.scrollLeft || 몸 && body.scrollLeft || 0) -
(문서 && doc.clientLeft || 몸 && body.clientLeft || 0);
이벤트.페이지Y = 이벤트.클라이언트Y +
(문서 && doc.scrollTop || 몸 && body.scrollTop || 0) -
(문서 && doc.clientTop || 몸 && body.client탑 || 0);
}
// 키 누름 처리
event.which = event.charCode || 이벤트.키코드;
// 마우스 클릭 버튼 수정:
// 0 == 왼쪽; 1 == 중간; 2 == 오른쪽
if (event.button !== null && event.button !== 정의되지 않음) {
// 다음은 videojs-standard를 통과하지 않기 때문에 비활성화됩니다.
// 그리고... 이런.
/* eslint 비활성화 */
이벤트.버튼 = (이벤트.버튼 & 1 ? 0 :
(이벤트.버튼 & 4 ? 1 :
(이벤트.버튼 & 2? 2: 0)));
/* eslint 활성화 */
}
}
event.fixed_ = 참;
// 고정된 인스턴스를 반환합니다.
반환 이벤트;
}
/**
* 패시브 이벤트 리스너 지원 여부
*/
let _supportsPassive;
const supportsPassive = function() {
if (typeof _supportsPassive !== '부울') {
_supportsPassive = 거짓;
{
const opts = Object.defineProperty({}, 'passive', {
얻다() {
_supportsPassive = 참;
}
});
window.addEventListener('테스트', null, opts);
window.removeEventListener('테스트', null, opts);
} 잡기 (e) {
// 무시
}
}
return _supportsPassive;
};
/**
* 크롬이 수동적일 것으로 예상되는 터치 이벤트
*/
const passiveEvents = [
'터치스타트',
'터치무브'
];
/**
* 요소에 이벤트 리스너 추가
* 핸들러 기능을 별도의 캐시 객체에 저장
* 요소의 이벤트에 일반 핸들러를 추가합니다.
* 요소에 대한 고유 ID(guid)와 함께.
*
* @param {요소|객체} 요소
* 리스너를 바인딩할 요소 또는 객체
*
* @param {문자열|문자열[]} 유형
* 바인딩할 이벤트 유형.
*
* @param {EventTarget~EventListener} fn
* 이벤트 리스너.
*/
내보내기 기능 on(elem, type, fn) {
if (Array.isArray(유형)) {
return _handleMultipleEvents(on, 요소, 유형, fn);
}
if (!DomData.has(elem)) {
DomData.set(elem, {});
}
const 데이터 = DomData.get(elem);
// 모든 핸들러 데이터를 저장할 장소가 필요합니다.
if (!data.handlers) {
데이터 핸들러 = {};
}
if (!data.handlers[유형]) {
data.handlers[유형] = [];
}
if (!fn.guid) {
fn.guid = Guid.newGUID();
}
data.handlers[유형].push(fn);
if (!data.dispatcher) {
데이터.비활성화 = 거짓;
data.dispatcher = 함수(이벤트, 해시) {
if (데이터.비활성화) {
반품;
}
이벤트 = 고정 이벤트(이벤트);
const 핸들러 = data.handlers[event.type];
if (핸들러) {
// 핸들러를 복사하여 프로세스 중에 핸들러가 추가/제거되더라도 모든 것을 버리지 않습니다.
const handlersCopy = handlers.slice(0);
for (let m = 0, n = handlersCopy.length; m < N; m++) {
if (event.isImmediatePropagationStopped()) {
부서지다;
} else {
{
handlersCopy[m].call(elem, event, hash);
} 잡기 (e) {
log.error(e);
}
}
}
}
};
}
if (data.handlers[유형].길이 === 1) {
if (elem.addEventListener) {
let 옵션 = 거짓;
if (supportsPassive() &&
passiveEvents.indexOf(유형) > -1) {
옵션 = {수동: 참};
}
elem.addEventListener(유형, data.dispatcher, 옵션);
} 그렇지 않으면 (elem.attachEvent) {
elem.attachEvent('on' + type, data.dispatcher);
}
}
}
/**
* 요소에서 이벤트 리스너 제거
*
* @param {요소|객체} 요소
* 리스너를 제거할 객체.
*
* @param {문자열|문자열[]} [유형]
* 제거할 리스너 유형. 요소에서 모든 이벤트를 제거하려면 포함하지 마십시오.
*
* @param {EventTarget~EventListener} [fn]
* 제거할 특정 리스너. 이벤트에 대한 리스너를 제거하려면 포함하지 마십시오.
* 유형.
*/
내보내기 기능 해제(요소, 유형, fn) {
// 필요하지 않은 경우 getElData를 통해 캐시 객체를 추가하고 싶지 않음
if (!DomData.has(elem)) {
반품;
}
const 데이터 = DomData.get(elem);
// 이벤트가 없으면 바인딩 해제할 항목이 없습니다.
if (!data.handlers) {
반품;
}
if (Array.isArray(유형)) {
return _handleMultipleEvents(off, 요소, 유형, fn);
}
// 유틸리티 함수
const removeType = 함수(el, t) {
data.handlers[t] = [];
_cleanUpEvents(el, t);
};
// 바인딩된 모든 이벤트를 제거하고 있습니까?
if (유형 === 정의되지 않음) {
for (data.handlers의 const t) {
if (Object.prototype.hasOwnProperty.call(data.handlers || {}, t)) {
removeType(요소, t);
}
}
반품;
}
const 핸들러 = data.handlers[유형];
// 핸들러가 없으면 바인딩 해제할 항목이 없습니다.
if (!핸들러) {
반품;
}
// 리스너가 제공되지 않은 경우 유형에 대한 모든 리스너를 제거합니다.
경우 (!fn) {
removeType(요소, 유형);
반품;
}
// 단일 핸들러만 제거합니다.
경우 (fn.guid) {
에 대한 (n = 0; n < 핸들러.길이; n++) {
if (핸들러[n].guid === fn.guid) {
handlers.splice(n--, 1);
}
}
}
_cleanUpEvents(요소, 유형);
}
/**
* 요소에 대한 이벤트 트리거
*
* @param {요소|객체} 요소
* 이벤트를 트리거하는 요소
*
* @param {EventTarget~Event|문자열} 이벤트
* 문자열(유형) 또는 유형 속성이 있는 이벤트 객체
*
* @param {객체} [해시]
* 이벤트와 함께 전달할 데이터 해시
*
* @return {부울|정의되지 않음}
* defaultPrevented의 반대인 경우 defaultPrevented를 반환합니다.
* 방지. 그렇지 않으면 `정의되지 않음`을 반환합니다.
*/
내보내기 기능 트리거(요소, 이벤트, 해시) {
// 요소 데이터와 부모에 대한 참조를 가져옵니다(버블링용).
// 모든 부모에 대해 캐시할 데이터 객체를 추가하고 싶지는 않습니다.
// 따라서 hasElData를 먼저 확인합니다.
const elemData = DomData.has(elem) ? DomData.get(elem) : {};
const 부모 = elem.parentNode || elem.ownerDocument;
// 유형 = 이벤트.유형 || 이벤트,
// 핸들러;
// 이벤트 이름이 문자열로 전달된 경우 이벤트를 생성합니다.
if (이벤트 유형 === '문자열') {
이벤트 = {유형: 이벤트, 대상: 요소};
} 그렇지 않으면 (!event.target) {
event.target = 요소;
}
// 이벤트 속성을 정규화합니다.
이벤트 = 고정 이벤트(이벤트);
// 전달된 요소에 디스패처가 있으면 설정된 핸들러를 실행합니다.
if (elemData.dispatcher) {
elemData.dispatcher.call(요소, 이벤트, 해시);
}
// 명시적으로 중지되거나 이벤트가 버블링되지 않는 한(예: 미디어 이벤트)
// 이 함수를 재귀적으로 호출하여 이벤트를 DOM 위로 버블링합니다.
만약 (부모 && !event.isPropagationStopped() && event.bubbles === true) {
trigger.call(null, 부모, 이벤트, 해시);
// DOM의 맨 위에 있는 경우 비활성화되지 않는 한 기본 작업을 트리거합니다.
} 그렇지 않으면 (!부모 && !event.default방지됨 && 이벤트.대상 && 이벤트.대상[이벤트.유형]) {
if (!DomData.has(event.target)) {
DomData.set(event.target, {});
}
const targetData = DomData.get(event.target);
// 대상에 이 이벤트에 대한 기본 동작이 있는지 확인합니다.
if (이벤트.대상[이벤트.유형]) {
// 이미 핸들러를 실행했기 때문에 대상에서 이벤트 디스패치를 일시적으로 비활성화합니다.
targetData.disabled = 참;
// 기본 동작을 실행합니다.
if (typeof event.target[event.type] === '함수') {
이벤트.대상[이벤트.유형]();
}
// 이벤트 디스패치를 다시 활성화합니다.
targetData.disabled = 거짓;
}
}
// false를 반환하여 기본값이 차단된 경우 트리거러에 알립니다.
반환 !event.defaultPrevented;
}
/**
* 이벤트에 대해 리스너를 한 번만 트리거합니다.
*
* @param {요소|객체} 요소
* 바인딩할 요소 또는 객체.
*
* @param {문자열|문자열[]} 유형
* 행사명/종류
*
* @param {이벤트~이벤트리스너} fn
* 이벤트 리스너 기능
*/
내보내기 기능 one(elem, type, fn) {
if (Array.isArray(유형)) {
return _handleMultipleEvents(one, elem, type, fn);
}
const func = 함수() {
off(요소, 유형, 기능);
fn.apply(this, 인수);
};
// 원래 함수의 ID를 사용하여 제거할 수 있도록 새 함수에 guid를 복사합니다.
func.guid = fn.guid = fn.guid || Guid.newGUID();
on(요소, 유형, 기능);
}
/**
* 리스너를 한 번만 트리거한 다음 모두 끄면 끕니다.
* 구성된 이벤트
*
* @param {요소|객체} 요소
* 바인딩할 요소 또는 객체.
*
* @param {문자열|문자열[]} 유형
* 행사명/종류
*
* @param {이벤트~이벤트리스너} fn
* 이벤트 리스너 기능
*/
내보내기 기능 any(elem, type, fn) {
const func = 함수() {
off(요소, 유형, 기능);
fn.apply(this, 인수);
};
// 원래 함수의 ID를 사용하여 제거할 수 있도록 새 함수에 guid를 복사합니다.
func.guid = fn.guid = fn.guid || Guid.newGUID();
// 여러 번 켜지만 모든 것에 대해 한 번만 꺼짐
on(요소, 유형, 기능);
}