/**
* 플레이어 구성 요소 - 모든 UI 개체의 기본 클래스
*
* @파일 구성요소.js
*/
'글로벌/창'에서 창 가져오기;
'./mixins/evented'에서 이벤트 가져오기;
'./mixins/stateful'에서 상태 저장 가져오기;
import * as Dom from './utils/dom.js';
import * as Fn from './utils/fn.js';
import * as Guid from './utils/guid.js';
'./utils/string-cases.js'에서 {toTitleCase, toLowerCase} 가져오기;
'./utils/merge-options.js'에서 mergeOptions 가져오기;
'./utils/computed-style'에서 computedStyle 가져오기;
'./utils/map.js'에서 지도 가져오기;
'./utils/set.js'에서 세트 가져오기;
'키코드'에서 키코드 가져오기;
/**
* 모든 UI 구성 요소의 기본 클래스입니다.
* 구성요소는 자바스크립트 객체와 요소를 모두 나타내는 UI 객체입니다.
* DOM에서. 다른 구성 요소의 자식일 수 있으며 다음을 가질 수 있습니다.
* 아이들 스스로.
*
* 구성 요소는 {@link EventTarget}의 메서드도 사용할 수 있습니다.
*/
클래스 구성 요소 {
/**
* 구성 요소가 준비되면 호출되는 콜백입니다. 없음
* 매개변수 및 모든 콜백 값은 무시됩니다.
*
* @callback 컴포넌트~ReadyCallback
* @이 컴포넌트
*/
/**
* 이 클래스의 인스턴스를 만듭니다.
*
* @param {플레이어} 플레이어
* 이 클래스가 연결되어야 하는 `Player`.
*
* @param {객체} [옵션]
* 구성 요소 옵션의 키/값 저장소입니다.
*
* @param {객체[]} [옵션.어린이]
* 이 구성 요소를 초기화할 자식 개체의 배열입니다. 하위 개체에는
* 동일한 유형의 구성 요소가 두 개 이상 필요한 경우 사용할 이름 속성
* 추가했습니다.
*
* @param {문자열} [옵션.클래스 이름]
* 구성 요소를 추가할 클래스 또는 공백으로 구분된 클래스 목록
*
* @param {Component~ReadyCallback} [준비]
* `Component`가 준비되면 호출되는 함수.
*/
생성자(플레이어, 옵션, 준비) {
// 구성 요소는 플레이어 자체일 수 있으며 `this`를 super에 전달할 수 없습니다.
만약 (!플레이어 && 이.플레이) {
this.player_ = 플레이어 = 이; // eslint 비활성화 라인
} else {
this.player_ = 플레이어;
}
this.isDisposed_ = 거짓;
// `addChild` 메서드를 통해 부모 구성 요소에 대한 참조를 유지합니다.
this.parentComponent_ = null;
// 기본값을 덮어쓰지 않도록 보호하기 위해 prototype.options_의 복사본을 만듭니다.
this.options_ = mergeOptions({}, this.options_);
// 제공된 옵션으로 업데이트된 옵션
options = this.options_ = mergeOptions(this.options_, options);
// options 또는 options 요소가 제공된 경우 ID를 가져옵니다.
this.id_ = options.id || (옵션.엘 && options.el.id);
// 옵션에서 ID가 없으면 하나 생성
if (!this.id_) {
// 모의 플레이어의 경우 플레이어 ID 기능을 요구하지 않음
const id = 플레이어 && player.id && player.id() || 'no_player';
this.id_ = `${id}_component_${Guid.newGUID()}`;
}
this.name_ = options.name || 없는;
// 옵션에 요소가 제공되지 않은 경우 요소 생성
if (옵션.엘) {
this.el_ = options.el;
} 그렇지 않으면 (options.createEl !== false) {
this.el_ = this.createEl();
}
if (옵션.클래스이름 && 이.el_) {
options.className.split(' ').forEach(c => this.addClass(c));
}
// evented가 거짓이 아닌 경우 evented에 혼합하려고 합니다.
if (options.evented !== false) {
// 이것을 이벤트 객체로 만들고 가능한 경우 `el_`을 이벤트 버스로 사용합니다.
evented(이 {eventBusKey: this.el_ ? 'el_' : null});
this.handleLanguagechange = this.handleLanguagechange.bind(this);
this.on(this.player_, '언어변경', this.handleLanguagechange);
}
stateful(this, this.constructor.defaultState);
this.children_ = [];
this.childIndex_ = {};
this.childNameIndex_ = {};
this.setTimeoutIds_ = new Set();
this.setIntervalIds_ = new Set();
this.rafIds_ = new Set();
this.namedRafs_ = new Map();
this.clearingTimersOnDispose_ = 거짓;
// 옵션에 하위 구성 요소를 추가합니다.
if (options.initChildren !== false) {
this.initChildren();
}
// 여기에서 준비를 트리거하지 않으려면 실제로 init가 시작되기 전에 실행됩니다.
// 이 생성자를 실행하는 모든 자식에 대해 완료됨
this.ready(준비);
if (options.reportTouchActivity !== false) {
this.enableTouchActivity();
}
}
/**
* `Component` 및 모든 자식 구성 요소를 폐기하십시오.
*
* @fires 컴포넌트#dispose
*
* @param {객체} 옵션
* @param {Element} options.originalEl 요소로 대체할 플레이어 요소
*/
폐기(옵션 = {}) {
// 구성 요소가 이미 삭제된 경우 구제 조치를 취합니다.
if (this.isDisposed_) {
반품;
}
if (이.readyQueue_) {
this.readyQueue_.length = 0;
}
/**
* `Component`가 삭제될 때 트리거됩니다.
*
* @event 컴포넌트#dispose
* @type {이벤트대상~이벤트}
*
* @property {부울} [버블=거짓]
* dispose 이벤트가 발생하지 않도록 false로 설정
* 버블 업
*/
this.trigger({유형: '처리', 거품: 거짓});
this.isDisposed_ = 참;
// 모든 자식을 삭제합니다.
if (이.어린이_) {
for (let i = this.children_.length - 1; i > = 0; 나--) {
if (this.children_[i].dispose) {
this.children_[i].dispose();
}
}
}
// 하위 참조 삭제
this.children_ = null;
this.childIndex_ = null;
this.childNameIndex_ = null;
this.parentComponent_ = null;
if (this.el_) {
// DOM에서 요소 제거
if (this.el_.parentNode) {
if (옵션.restoreEl) {
this.el_.parentNode.replaceChild(options.restoreEl, this.el_);
} else {
this.el_.parentNode.removeChild(this.el_);
}
}
this.el_ = null;
}
// 요소를 삭제한 후 플레이어에 대한 참조를 제거합니다.
this.player_ = null;
}
/**
* 이 구성 요소가 폐기되었는지 여부를 확인합니다.
*
* @return {부울}
* 구성 요소가 삭제된 경우 'true'가 됩니다. 그렇지 않으면 '거짓'입니다.
*/
isDisposed() {
반환 부울(this.isDisposed_);
}
/**
* `Component`가 연결된 {@link Player}를 반환합니다.
*
* @return {플레이어}
* 이 `Component`가 연결된 플레이어입니다.
*/
플레이어() {
this.player_를 반환합니다.
}
/**
* 옵션 개체와 새 옵션의 심층 병합.
* > 메모: `obj`와 `options` 모두 값이 개체인 속성을 포함하는 경우.
* 두 속성은 {@link module:mergeOptions}를 사용하여 병합됩니다.
*
* @param {객체} 객체
* 새 옵션을 포함하는 개체입니다.
*
* @return {객체}
* `this.options_`와 `obj`의 새 개체가 함께 병합되었습니다.
*/
옵션(객체) {
경우 (!obj) {
this.options_를 반환합니다.
}
this.options_ = mergeOptions(this.options_, obj);
this.options_를 반환합니다.
}
/**
* `Component`의 DOM 요소 가져오기
*
* @return {요소}
* 이 `Component`에 대한 DOM 요소입니다.
*/
엘() {
this.el_을 반환합니다.
}
/**
* `Component`의 DOM 요소를 생성합니다.
*
* @param {문자열} [태그 이름]
* 요소의 DOM 노드 유형. 예: 'div'
*
* @param {객체} [속성]
* 설정해야 하는 속성의 개체입니다.
*
* @param {객체} [속성]
* 설정되어야 하는 속성의 객체.
*
* @return {요소}
* 생성되는 요소.
*/
createEl(태그 이름, 속성, 속성) {
return Dom.createEl(tagName, 속성, 속성);
}
/**
* 주어진 문자열을 영어로 현지화합니다.
*
* 토큰이 제공되면 제공된 문자열에서 간단한 토큰 교체를 시도하고 실행합니다.
* 찾는 토큰은 토큰 배열에 인덱스가 1인 `{1}`과 같습니다.
*
* `defaultValue`가 제공되면 `string`보다 이를 사용합니다.
* 제공된 언어 파일에서 값을 찾을 수 없는 경우.
* 토큰 교체를 위한 설명 키가 필요한 경우에 유용합니다.
* 그러나 간결한 현지화 문자열이 있고 `en.json`을 포함할 필요가 없습니다.
*
* 현재 진행률 표시줄 타이밍에 사용됩니다.
* ```js
* {
* "진행률 표시줄 타이밍: currentTime={1} duration={2}": "{1}/{2}"
* }
* ```
* 다음과 같이 사용합니다.
* ```js
* this.localize('진행률 표시줄 타이밍: currentTime={1} duration{2}',
* [this.player_.currentTime(), this.player_.duration()],
* '{1}/{2}');
* ```
*
* 다음과 같이 출력됩니다: `01:23 of 24:56`.
*
*
* @param {문자열} 문자열
* 현지화할 문자열과 언어 파일에서 조회할 키입니다.
* @param {문자열[]} [토큰]
* 현재 항목에 대체 토큰이 있는 경우 여기에 토큰을 제공하십시오.
* @param {문자열} [기본값]
* 기본값은 `문자열`입니다. 토큰 교체에 사용할 기본값이 될 수 있습니다.
* 별도의 조회 키가 필요한 경우.
*
* @return {문자열}
* 현지화된 문자열 또는 현지화가 없는 경우 영어 문자열.
*/
localize(문자열, 토큰, defaultValue = 문자열) {
const 코드 = this.player_.language && this.player_.language();
const 언어 = this.player_.languages && this.player_.languages();
const 언어 = 언어 && 언어[코드];
const primaryCode = 코드 && code.split('-')[0];
const primaryLang = 언어 && 언어[primaryCode];
let localizedString = defaultValue;
만약 (언어 && 언어[문자열]) {
localizedString = 언어[문자열];
} 그렇지 않으면 (primaryLang && 기본 언어[문자열]) {
localizedString = primaryLang[문자열];
}
if (토큰) {
localizedString = localizedString.replace(/\{(\d+)\}/g, function(match, index) {
const 값 = 토큰[인덱스 - 1];
ret = 값;
if (값 유형 === '정의되지 않음') {
ret = 매치;
}
반환 ret;
});
}
localizedString 반환;
}
/**
* 구성 요소에서 플레이어의 언어 변경을 처리합니다. 하위 구성요소에 의해 재정의되어야 합니다.
*
* @추상적인
*/
handleLanguagechange() {}
/**
* `Component`의 DOM 요소를 반환합니다. 이것은 아이들이 삽입되는 곳입니다.
* 일반적으로 {@link Component#el}에서 반환되는 요소와 동일합니다.
*
* @return {요소}
* 이 `Component`에 대한 콘텐츠 요소입니다.
*/
내용엘() {
return this.contentEl_ || this.el_;
}
/**
* 이 `Component` ID 얻기
*
* @return {문자열}
* 이 `Component`의 id
*/
ID() {
this.id_를 반환합니다.
}
/**
* `컴포넌트` 이름을 얻습니다. 이름은 `Component`를 참조하는 데 사용됩니다.
* 등록시 설정됩니다.
*
* @return {문자열}
* 이 `컴포넌트`의 이름.
*/
이름() {
this.name_을 반환합니다.
}
/**
* 모든 하위 구성 요소의 배열 가져오기
*
* @return {배열}
* 아이들
*/
어린이들() {
this.children_을 반환합니다.
}
/**
* 주어진 `id`를 가진 자식 `Component`를 반환합니다.
*
* @param {문자열} 아이디
* 가져올 자식 `Component`의 ID입니다.
*
* @return {구성요소|정의되지 않음}
* 주어진 `id` 또는 정의되지 않은 하위 `Component`.
*/
getChildById(id) {
return this.childIndex_[id];
}
/**
* 주어진 `name`을 가진 자식 `Component`를 반환합니다.
*
* @param {문자열} 이름
* 가져올 자식 `Component`의 이름입니다.
*
* @return {구성요소|정의되지 않음}
* 주어진 `이름`이 있거나 정의되지 않은 자식 `구성 요소`.
*/
getChild(이름) {
if (!이름) {
반품;
}
return this.childNameIndex_[이름];
}
/**
* 주어진 다음의 자손 `Component`를 반환합니다.
* 자손 `이름`. 예를 들어 ['foo', 'bar', 'baz']는
* 현재 구성 요소에서 'foo'를 가져오고 'foo'에서 'bar'를 가져오려고 시도합니다.
* 'bar' 컴포넌트의 component 및 'baz' 및 정의되지 않은 반환
* 존재하지 않는 경우.
*
* @param {...string[]|...string} 이름
* 가져올 자식 `Component`의 이름입니다.
*
* @return {구성요소|정의되지 않음}
* 주어진 하위 항목을 따르는 하위 항목 `Component`
* `이름` 또는 정의되지 않음.
*/
getDescendant(...이름) {
// 배열 인수를 기본 배열로 병합
이름 = 이름.reduce((acc, n) => acc.concat(n), []);
let currentChild = this;
에 대한 (하자 i = 0; i < 이름.길이; i++) {
currentChild = currentChild.getChild(이름[i]);
if (!currentChild || !currentChild.getChild) {
반품;
}
}
반환 currentChild;
}
/**
* 현재 `Component` 안에 자식 `Component`를 추가합니다.
*
*
* @param {string|Component} 자식
* 추가할 자식의 이름 또는 인스턴스입니다.
*
* @param {객체} [옵션={}]
* 자식에게 전달될 옵션의 키/값 저장소
* 아이.
*
* @param {번호} [인덱스=이.어린이_.길이]
* 자식을 추가하려고 시도하는 인덱스입니다.
*
* @return {구성 요소}
* 자식으로 추가되는 `Component`. 문자열을 사용할 때
* 이 과정에서 `Component`가 생성됩니다.
*/
addChild(자식, 옵션 = {}, 색인 = this.children_.length) {
컴포넌트를 보자;
let componentName;
// 자식이 문자열이면 옵션으로 구성 요소를 만듭니다.
if (자식 유형 === '문자열') {
componentName = toTitleCase(자식);
const componentClassName = options.componentClass || 구성 요소 이름;
// 옵션을 통해 이름 설정
options.name = 구성 요소 이름;
// 새로운 객체 생성 & 이 컨트롤 세트의 요소
// .player_가 없으면 플레이어입니다.
const ComponentClass = Component.getComponent(componentClassName);
if (!구성 요소 클래스) {
throw new Error(`${componentClassName} 컴포넌트가 존재하지 않음`);
}
// videojs 객체에 직접 저장된 데이터는
// 유지할 구성 요소로 잘못 식별됨
// 4.x와의 하위 호환성. 확인하기 위해 확인
// 구성 요소 클래스를 인스턴스화할 수 있습니다.
if (구성 요소 클래스 유형 !== '함수') {
null을 반환합니다.
}
component = new ComponentClass(this.player_ || this, options);
// 자식은 구성 요소 인스턴스입니다.
} else {
구성 요소 = 자식;
}
if (component.parentComponent_) {
component.parentComponent_.removeChild(컴포넌트);
}
this.children_.splice(index, 0, component);
component.parentComponent_ = 이;
if (typeof component.id === '함수') {
this.childIndex_[component.id()] = 컴포넌트;
}
// 구성 요소를 만드는 데 이름을 사용하지 않은 경우 다음을 사용할 수 있는지 확인합니다.
// 구성 요소의 이름 함수
구성 요소 이름 = 구성 요소 이름 || (컴포넌트.이름 && toTitleCase(component.name()));
if (구성 요소 이름) {
this.childNameIndex_[componentName] = 컴포넌트;
this.childNameIndex_[toLowerCase(componentName)] = 컴포넌트;
}
// UI 객체의 요소를 컨테이너에 추가 div(box)
// 요소를 가질 필요가 없습니다.
if (typeof component.el === '함수' && component.el()) {
// 컴포넌트 앞에 삽입하는 경우 해당 컴포넌트의 요소 앞에 삽입
let refNode = null;
if (이.어린이_[인덱스 + 1]) {
// 대부분의 자식은 구성 요소이지만 비디오 기술은 HTML 요소입니다.
if (이.어린이_[인덱스 + 1].el_) {
refNode = this.children_[인덱스 + 1].el_;
} 그렇지 않으면 (Dom.isEl(this.children_[index + 1])) {
refNode = this.children_[인덱스 + 1];
}
}
this.contentEl().insertBefore(component.el(), refNode);
}
// 원하는 경우 상위 개체에 저장할 수 있도록 반환합니다.
반환 구성 요소;
}
/**
* 이 `Component` 자식 목록에서 자식 `Component`를 제거합니다. 또한 제거
* 이 `Component` 요소의 하위 `Component` 요소.
*
* @param {구성 요소} 구성 요소
* 제거할 하위 `Component`.
*/
removeChild(구성 요소) {
if (구성 요소 유형 === '문자열') {
구성 요소 = this.getChild(구성 요소);
}
if (!구성요소 || !this.children_) {
반품;
}
let childFound = 거짓;
for (let i = this.children_.length - 1; i > = 0; 나--) {
if (this.children_[i] === 구성 요소) {
childFound = 참;
this.children_.splice(i, 1);
부서지다;
}
}
if (!childFound) {
반품;
}
component.parentComponent_ = null;
this.childIndex_[component.id()] = null;
this.childNameIndex_[toTitleCase(component.name())] = null;
this.childNameIndex_[toLowerCase(component.name())] = null;
const compEl = component.el();
if (강제 && compEl.parentNode === this.contentEl()) {
this.contentEl().removeChild(component.el());
}
}
/**
* 옵션에 따라 기본 자식 `Component`를 추가하고 초기화합니다.
*/
초기화 어린이() {
const children = this.options_.children;
if (어린이) {
// `this`는 `부모`입니다.
const parentOptions = this.options_;
const handleAdd = (자식) => {
const 이름 = child.name;
let opts = child.opts;
// 자식 옵션을 부모 옵션에서 설정할 수 있도록 허용
// 예를 들어 videojs(id, { controlBar: false });
// 대신 videojs(id, { children: { controlBar: false });
if (parentOptions[이름] !== 정의되지 않음) {
opts = parentOptions[이름];
}
// 기본 구성 요소 비활성화 허용
// 예: options['children']['posterImage'] = false
경우 (옵션 === 거짓) {
반품;
}
// 구성이 없는 경우 옵션이 단순 부울로 전달되도록 허용
// 필수적이다.
if (옵션 === 참) {
옵션 = {};
}
// 원래 플레이어 옵션도 전달하고 싶습니다.
// 각 구성 요소에도 적용되므로 필요하지 않습니다.
// 나중에 옵션을 위해 플레이어에 다시 도달합니다.
opts.playerOptions = this.options_.playerOptions;
// 자식 구성 요소를 만들고 추가합니다.
// 부모 인스턴스의 이름으로 자식에 대한 직접 참조를 추가합니다.
// 같은 컴포넌트를 2개 사용하는 경우 다른 이름을 지정해야 함
// 각각
const newChild = this.addChild(이름, 옵션);
경우 (newChild) {
this[이름] = newChild;
}
};
// 자식 세부 정보의 배열이 옵션에 전달되도록 허용
일하는 아이들을 보자;
const Tech = Component.getComponent('기술');
if (Array.isArray(어린이)) {
workingChildren = 어린이;
} else {
workingChildren = Object.keys(어린이);
}
일하는 아이들
// this.options_에 있지만 workingChildren에도 있는 자식은
// 원하지 않는 추가 자식을 제공합니다. 그래서 우리는 그것들을 걸러내고 싶습니다.
.concat(Object.keys(this.options_)
.filter(함수(하위) {
반환 !workingChildren.some(function(wchild) {
if (wchild 유형 === '문자열') {
반환 자식 === wchild;
}
자식 반환 === wchild.name;
});
}))
.map((하위) => {
이름을 짓다;
선택하자;
if (자식 유형 === '문자열') {
이름 = 아이;
선택 = 어린이[이름] || this.options_[이름] || {};
} else {
이름 = 아이.이름;
옵션 = 자식;
}
{이름, 옵션}을 반환합니다.
})
.filter((하위) => {
// child.name이 techOrder에 없는지 확인해야 합니다.
// 기술자는 구성 요소로 등록되지만 호환되지 않습니다.
// https://github.com/videojs/video.js/issues/2772 참조
const c = Component.getComponent(child.opts.componentClass ||
toTitleCase(child.name));
반환 c && !Tech.isTech(c);
})
.forEach(handleAdd);
}
}
/**
* 기본 DOM 클래스 이름을 빌드합니다. 하위 구성요소에 의해 재정의되어야 합니다.
*
* @return {문자열}
* 이 개체의 DOM 클래스 이름입니다.
*
* @추상적인
*/
buildCSSClass() {
// 자식 클래스는 다음을 수행하는 함수를 포함할 수 있습니다.
// return 'CLASS NAME' + this._super();
반품 '';
}
/**
* 수신기를 구성 요소의 준비 상태에 바인딩합니다.
* ready 이벤트가 이미 발생한 경우라는 점에서 이벤트 리스너와 다릅니다.
* 즉시 기능을 트리거합니다.
*
* @return {구성 요소}
* 자신을 반환합니다. 메서드를 연결할 수 있습니다.
*/
준비(fn, 동기화 = 거짓) {
경우 (!fn) {
반품;
}
if (!this.isReady_) {
this.readyQueue_ = this.readyQueue_ || [];
this.readyQueue_.push(fn);
반품;
}
경우 (동기화) {
fn.call(이);
} else {
// 일관성을 위해 기본적으로 함수를 비동기적으로 호출합니다.
this.setTimeout(fn, 1);
}
}
/**
* 이 `Component`에 대해 준비된 모든 리스너를 트리거합니다.
*
* @fires 컴포넌트#ready
*/
트리거준비() {
this.isReady_ = 참;
// 준비가 비동기적으로 트리거되도록 합니다.
this.setTimeout(함수() {
const readyQueue = this.readyQueue_;
// 준비 대기열 재설정
this.readyQueue_ = [];
if (readyQueue && readyQueue.length > 0) {
readyQueue.forEach(기능(fn) {
fn.call(이);
}, 이것);
}
// 이벤트 리스너 사용도 허용
/**
* `Component`가 준비되면 트리거됩니다.
*
* @event 컴포넌트#ready
* @type {이벤트대상~이벤트}
*/
this.trigger('준비');
}, 1);
}
/**
* `selector`와 일치하는 단일 DOM 요소를 찾습니다. 이것은 `Component`s 내에 있을 수 있습니다.
* `contentEl()` 또는 다른 사용자 지정 컨텍스트.
*
* @param {문자열} 선택자
* `querySelector`에 전달되는 유효한 CSS 선택기.
*
* @param {요소|문자열} [context=this.contentEl()]
* 조회할 DOM 요소. 다음에서 선택자 문자열이 될 수도 있습니다.
* 이 경우 첫 번째로 일치하는 요소가 컨텍스트로 사용됩니다. 만약에
* 누락된 `this.contentEl()`이 사용됨. `this.contentEl()`이 반환하는 경우
* `문서`로 돌아가는 것은 없습니다.
*
* @return {요소|null}
* 찾은 dom 요소 또는 null
*
* @see [CSS 선택자 정보](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Getting_Started/Selectors)
*/
$(선택자, 컨텍스트) {
return Dom.$(selector, context || this.contentEl());
}
/**
* `selector`와 일치하는 모든 DOM 요소를 찾습니다. 이것은 `Component`s 내에 있을 수 있습니다.
* `contentEl()` 또는 다른 사용자 지정 컨텍스트.
*
* @param {문자열} 선택자
* `querySelectorAll`에 전달되는 유효한 CSS 선택기.
*
* @param {요소|문자열} [context=this.contentEl()]
* 조회할 DOM 요소. 다음에서 선택자 문자열이 될 수도 있습니다.
* 이 경우 첫 번째로 일치하는 요소가 컨텍스트로 사용됩니다. 만약에
* 누락된 `this.contentEl()`이 사용됨. `this.contentEl()`이 반환하는 경우
* `문서`로 돌아가는 것은 없습니다.
*
* @return {노드리스트}
* 발견된 dom 요소 목록
*
* @see [CSS 선택자 정보](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Getting_Started/Selectors)
*/
$$(선택자, 컨텍스트) {
return Dom.$$(selector, context || this.contentEl());
}
/**
* 구성 요소의 요소에 CSS 클래스 이름이 있는지 확인하십시오.
*
* @param {string} classToCheck
* 확인할 CSS 클래스 이름.
*
* @return {부울}
* - `Component`에 클래스가 있으면 True입니다.
* - `Component`에 class`가 없으면 False
*/
hasClass(classToCheck) {
return Dom.hasClass(this.el_, classToCheck);
}
/**
* `Component` 요소에 CSS 클래스 이름을 추가합니다.
*
* @param {문자열} classToAdd
* 추가할 CSS 클래스 이름
*/
addClass(classToAdd) {
Dom.addClass(this.el_, classToAdd);
}
/**
* `Component` 요소에서 CSS 클래스 이름을 제거합니다.
*
* @param {string} classToRemove
* 제거할 CSS 클래스 이름
*/
removeClass(classToRemove) {
Dom.removeClass(this.el_, classToRemove);
}
/**
* 구성 요소의 요소에서 CSS 클래스 이름을 추가하거나 제거합니다.
* - {@link Component#hasClass}가 false를 반환하면 `classToToggle`이 추가됩니다.
* - {@link Component#hasClass}가 true를 반환하면 `classToToggle`이 제거됩니다.
*
* @param {문자열} 클래스투토글
* (@link Component#hasClass} 기반으로 추가하거나 제거할 클래스
*
* @param {boolean|Dom~predicate} [술어]
* {@link Dom~predicate} 함수 또는 부울
*/
toggleClass(classToToggle, 조건자) {
Dom.toggleClass(this.el_, classToToggle, predicate);
}
/**
* 숨겨진 경우 `Component` 요소를 제거하여 표시
* 'vjs-hidden' 클래스 이름.
*/
보여주다() {
this.removeClass('vjs-hidden');
}
/**
* `Component` 요소를 추가하여 현재 표시 중인 경우 숨기기
* 'vjs-hidden` 클래스 이름.
*/
숨다() {
this.addClass('vjs-hidden');
}
/**
* 'vjs-lock-showing'을 추가하여 `Component` 요소를 보이는 상태로 잠급니다.
* 클래스 이름. fadeIn/fadeOut 중에 사용됩니다.
*
* @사적인
*/
잠금 표시() {
this.addClass('vjs-lock-showing');
}
/**
* 'vjs-lock-showing'을 제거하여 `Component` 요소를 보이는 상태에서 잠금 해제
* 그것의 클래스 이름. fadeIn/fadeOut 중에 사용됩니다.
*
* @사적인
*/
잠금해제표시() {
this.removeClass('vjs-lock-showing');
}
/**
* `Component` 요소의 속성 값을 가져옵니다.
*
* @param {문자열} 속성
* 값을 가져올 속성의 이름입니다.
*
* @return {문자열|널}
* - 요청된 속성의 값입니다.
* - 속성이 존재하지 않는 경우 일부 브라우저에서 빈 문자열일 수 있습니다.
* 또는 값이 없음
* - 속성이 존재하지 않거나 속성이 있는 경우 대부분의 브라우저는 null을 반환합니다.
* 값이 없습니다.
*
* @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/getAttribute}
*/
getAttribute(속성) {
return Dom.getAttribute(this.el_, 속성);
}
/**
* `Component` 요소의 속성 값 설정
*
* @param {문자열} 속성
* 설정할 속성의 이름.
*
* @param {문자열} 값
* 속성을 설정할 값.
*
* @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/setAttribute}
*/
setAttribute(속성, 값) {
Dom.setAttribute(this.el_, 속성, 값);
}
/**
* `Component` 요소에서 속성을 제거합니다.
*
* @param {문자열} 속성
* 제거할 속성의 이름.
*
* @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/removeAttribute}
*/
제거속성(속성) {
Dom.removeAttribute(this.el_, 속성);
}
/**
* CSS 스타일에 따라 구성 요소의 너비를 가져오거나 설정합니다.
* 자세한 내용은 {@link Component#dimension}을 참조하세요.
*
* @param {숫자|문자열} [숫자]
* 설정하려는 폭은 '%', 'px' 또는 아무것도 뒤에 붙지 않습니다.
*
* @param {부울} [skipListeners]
* componentresize 이벤트 트리거 건너뛰기
*
* @return {숫자|문자열}
* 가져올 때 너비, 너비가 없으면 0입니다. 문자열이 될 수 있습니다.
* 뒤에 '%' 또는 'px'가 붙습니다.
*/
너비(숫자, skipListeners) {
return this.dimension('width', num, skipListeners);
}
/**
* CSS 스타일을 기반으로 구성 요소의 높이를 가져오거나 설정합니다.
* 자세한 내용은 {@link Component#dimension}을 참조하세요.
*
* @param {숫자|문자열} [숫자]
* 설정하려는 높이는 '%', 'px' 또는 아무것도 뒤에 붙지 않습니다.
*
* @param {부울} [skipListeners]
* componentresize 이벤트 트리거 건너뛰기
*
* @return {숫자|문자열}
* 가져올 때 너비, 너비가 없으면 0입니다. 문자열이 될 수 있습니다.
* 뒤에 '%' 또는 'px'가 붙습니다.
*/
높이(숫자, skipListeners) {
return this.dimension('높이', num, skipListeners);
}
/**
* `Component` 요소의 너비와 높이를 동시에 설정합니다.
*
* @param {숫자|문자열} 너비
* `Component` 요소를 설정할 너비.
*
* @param {숫자|문자열} 높이
* `Component` 요소를 설정할 높이.
*/
치수(너비, 높이) {
// 최적화를 위해 width에서 componentresize 리스너 건너뛰기
this.width(너비, 참);
this.높이(높이);
}
/**
* `Component` 요소의 너비 또는 높이를 가져오거나 설정합니다. 이것은 공유 코드입니다
* {@link Component#width} 및 {@link Component#height}의 경우.
*
* 알아야 할 사항:
* - 숫자의 너비 또는 높이인 경우 'px'가 뒤에 붙은 숫자를 반환합니다.
* - 너비/높이가 퍼센트인 경우 '%'가 뒤에 붙은 퍼센트를 반환합니다.
* - 숨겨진 요소의 너비는 `window.getComputedStyle`과 함께 0입니다. 이 기능
* 기본값은 `Component`의 `style.width`이며 `window.getComputedStyle`으로 돌아갑니다.
* [이]{@링크 http://www.foliotek.com/devblog/getting-the-width-of-a-hidden-element-with-jquery-using-width/}를 참조하세요.
* 자세한 내용은
* - 구성 요소의 계산된 스타일을 원하면 {@link Component#currentWidth}를 사용하십시오.
* 및 {@link {Component#currentHeight}
*
* @fires 컴포넌트#componentresize
*
* @param {문자열} 너비 또는 높이
8 '너비' 또는 '높이'
*
* @param {숫자|문자열} [숫자]
8 새로운 차원
*
* @param {부울} [skipListeners]
* componentresize 이벤트 트리거 건너뛰기
*
* @return {숫자}
* 가져올 때 차원 또는 설정하지 않으면 0
*/
차원(너비 또는 높이, 숫자, skipListeners) {
if (num !== 정의되지 않음) {
// null이거나 문자 그대로 NaN인 경우 0으로 설정합니다(NaN !== NaN).
if (숫자 === null || 숫자 !== 숫자) {
숫자 = 0;
}
// css 너비/높이(% 또는 px)를 사용하는지 확인하고 조정
if (('' + 숫자).indexOf('%') !== -1 || ('' + 숫자).indexOf('px') !== -1) {
this.el_.style[너비 또는 높이] = 숫자;
} 그렇지 않으면 (숫자 === '자동') {
this.el_.style[너비 또는 높이] = '';
} else {
this.el_.style[너비 또는 높이] = 숫자 + 'px';
}
// skipListeners를 사용하면 너비와 높이를 모두 설정할 때 크기 조정 이벤트가 발생하지 않도록 할 수 있습니다.
if (!skipListeners) {
/**
* 구성 요소의 크기가 조정될 때 트리거됩니다.
*
* @event 컴포넌트#componentresize
* @type {이벤트대상~이벤트}
*/
this.trigger('componentresize');
}
반품;
}
// 값을 설정하지 않았으므로 가져옴
// 요소가 존재하는지 확인
if (!this.el_) {
0을 반환합니다.
}
// 스타일에서 차원 값 가져오기
const val = this.el_.style[너비 또는 높이];
const pxIndex = val.indexOf('px');
if (pxIndex !== -1) {
// 'px'가 없는 픽셀 값 반환
return parseInt(val.slice(0, pxIndex), 10);
}
// px가 없으므로 %를 사용하거나 스타일이 설정되지 않았으므로 offsetWidth/height로 되돌아갑니다.
// 구성 요소에 display:none이 있는 경우 오프셋은 0을 반환합니다.
// TODO: px를 사용하여 display:none 및 no dimension 스타일 처리
return parseInt(this.el_['offset' + toTitleCase(widthOrHeight)], 10);
}
/**
* 구성요소 요소의 계산된 너비 또는 높이를 가져옵니다.
*
* `window.getComputedStyle`을 사용합니다.
*
* @param {문자열} 너비 또는 높이
* '너비' 또는 '높이'를 포함하는 문자열. 당신이 얻고 싶은 것.
*
* @return {숫자}
* 요청되는 차원 또는 아무것도 설정되지 않은 경우 0
* 해당 차원에 대해.
*/
현재 치수(너비 또는 높이) {
let computedWidthOrHeight = 0;
if (너비또는높이 !== '너비' && 너비 또는 높이 !== '높이') {
throw new Error('currentDimension은 너비 또는 높이 값만 허용합니다.');
}
computedWidthOrHeight = computedStyle(this.el_, widthOrHeight);
// 변수에서 'px'를 제거하고 정수로 구문 분석
computedWidthOrHeight = parseFloat(computedWidthOrHeight);
// 계산된 값이 여전히 0이면 브라우저가 거짓말을 하고 있을 가능성이 있습니다.
// 오프셋 값을 확인하고 싶습니다.
// 이 코드는 getComputedStyle이 존재하지 않는 곳에서도 실행됩니다.
if (computedWidthOrHeight === 0 || isNaN(computedWidthOrHeight)) {
const 규칙 = `offset${toTitleCase(widthOrHeight)}`;
computedWidthOrHeight = this.el_[rule];
}
computedWidthOrHeight 반환;
}
/**
* `Component`의 너비와 높이 값을 포함하는 객체
* 계산 스타일. `window.getComputedStyle`을 사용합니다.
*
* @typedef {객체} Component~DimensionObject
*
* @property {숫자} 너비
* `컴포넌트` 계산 스타일의 너비.
*
* @property {숫자} 높이
* `컴포넌트` 계산 스타일의 높이.
*/
/**
* 계산된 너비와 높이 값을 포함하는 개체를 가져옵니다.
* 컴포넌트의 요소.
*
* `window.getComputedStyle`을 사용합니다.
*
* @return {Component~DimensionObject}
* 구성요소 요소의 계산된 치수.
*/
현재 치수() {
반환 {
너비: this.currentDimension('너비'),
높이: this.currentDimension('높이')
};
}
/**
* 구성 요소 요소의 계산된 너비를 가져옵니다.
*
* `window.getComputedStyle`을 사용합니다.
*
* @return {숫자}
* 구성 요소 요소의 계산된 너비입니다.
*/
전류폭() {
return this.currentDimension('너비');
}
/**
* 구성요소 요소의 계산된 높이를 가져옵니다.
*
* `window.getComputedStyle`을 사용합니다.
*
* @return {숫자}
* 구성요소 요소의 계산된 높이입니다.
*/
현재 높이() {
return this.currentDimension('높이');
}
/**
* 이 구성 요소에 초점을 맞춥니다.
*/
집중하다() {
this.el_.focus();
}
/**
* 이 구성 요소에서 포커스 제거
*/
블러() {
this.el_.blur();
}
/**
* 이 컴포넌트가 처리하지 않는 `keydown` 이벤트를 수신하면,
* 처리를 위해 플레이어에게 이벤트를 전달합니다.
*
* @param {EventTarget~Event} 이벤트
* 이 함수를 호출한 `keydown` 이벤트.
*/
handleKeyDown(이벤트) {
if (이.플레이어_) {
// 처리되지 않은 이벤트가 떨어지기를 원하기 때문에 여기서 전파를 중지합니다.
// 브라우저로 돌아갑니다. 포커스 트래핑을 위해 탭을 제외합니다.
if (!keycode.isEventKey(event, 'Tab')) {
event.stopPropagation();
}
this.player_.handleKeyDown(event);
}
}
/**
* 많은 구성요소에 'handleKeyPress' 메서드가 있었는데, 이는 형편없었습니다.
* `keydown` 이벤트를 수신했기 때문에 이름이 지정되었습니다. 이 방법 이름은 지금
* `handleKeyDown`에 위임합니다. 이것은 `handleKeyPress`를 호출하는 모든 사용자를 의미합니다.
* 메소드 호출이 작동을 멈추는 것을 볼 수 없습니다.
*
* @param {EventTarget~Event} 이벤트
* 이 함수를 호출한 이벤트.
*/
handleKeyPress(이벤트) {
this.handleKeyDown(이벤트);
}
/**
* 터치 이벤트 지원이 감지되면 '탭' 이벤트를 내보냅니다. 이게 익숙해진다
* 비디오 탭을 통해 컨트롤 전환을 지원합니다. 활성화됩니다.
* 그렇지 않으면 모든 하위 구성 요소에 추가 오버헤드가 있기 때문입니다.
*
* @사적인
* @fires 컴포넌트#tap
* @listens 컴포넌트#touchstart
* @listens 컴포넌트#touchmove
* @listens 컴포넌트#touchleave
* @listens 컴포넌트#touchcancel
* @listens 컴포넌트#touchend
*/
emitTapEvents() {
// 터치가 지속된 시간을 확인할 수 있도록 시작 시간을 추적합니다.
let touchStart = 0;
let firstTouch = null;
// 여전히 탭으로 간주하기 위해 터치 이벤트 동안 허용되는 최대 이동
// 다른 대중적인 라이브러리는 2(hammer.js)에서 15까지 사용합니다.
// 따라서 10은 근사하고 근사한 숫자처럼 보입니다.
const tapMovementThreshold = 10;
// 여전히 탭으로 간주되는 동안 터치할 수 있는 최대 길이
const touchTimeThreshold = 200;
let couldBeTap;
this.on('터치스타트', function(event) {
// 손가락이 두 개 이상인 경우 클릭으로 간주하지 마세요.
if (event.touches.length === 1) {
// 객체에서 pageX/pageY 복사
퍼스트터치 = {
pageX: event.touches[0].pageX,
pageY: event.touches[0].pageY
};
// 탭과 "길게 누르기"를 감지할 수 있도록 시작 시간을 기록합니다.
touchStart = window.performance.now();
// couldBeTap 추적 재설정
couldBeTap = 참;
}
});
this.on('터치무브', 함수(이벤트) {
// 손가락이 두 개 이상인 경우 클릭으로 간주하지 마세요.
if (이벤트.터치.길이 > 1) {
couldBeTap = 거짓;
} 그렇지 않으면 (firstTouch) {
// 일부 장치는 약간의 탭을 제외하고 모두 터치 무브를 발생시킵니다.
// 따라서 우리가 약간의 거리만 움직였다면 여전히 탭이 될 수 있습니다.
const xdiff = event.touches[0].pageX - firstTouch.pageX;
const ydiff = event.touches[0].pageY - firstTouch.pageY;
const touchDistance = Math.sqrt(xdiff * xdiff + ydiff * ydiff);
if (터치거리 > tapMovementThreshold) {
couldBeTap = 거짓;
}
}
});
const noTap = 함수() {
couldBeTap = 거짓;
};
// 할 것: 원래 대상을 듣습니다. http://youtu.be/DujfpXOKUp8?t=13m8s
this.on('touchleave', noTap);
this.on('touchcancel', noTap);
// 터치가 끝나면 걸린 시간을 측정하고 적절한
// 이벤트
this.on('터치엔드', function(event) {
퍼스트터치 = null;
// touchmove/leave/cancel 이벤트가 발생하지 않은 경우에만 진행
if (couldBeTap === 참) {
// 터치가 지속된 시간 측정
const touchTime = window.performance.now() - touchStart;
// 터치가 탭으로 간주되는 임계값 미만인지 확인합니다.
if (터치타임 < touchTimeThreshold) {
// 브라우저가 이것을 클릭으로 바꾸지 못하게 합니다.
event.preventDefault();
/**
* `Component`를 탭하면 트리거됩니다.
*
* @event 컴포넌트#tap
* @type {이벤트대상~이벤트}
*/
this.trigger('탭');
// 터치엔드 이벤트 객체를 복사하여 변경하는 것이 좋을 수 있습니다.
// 입력 후 다른 이벤트 속성이 정확하지 않은 경우 탭합니다.
// Events.fixEvent 실행(예: event.target)
}
}
});
}
/**
* 이 기능은 터치 이벤트가 발생할 때마다 사용자 활동을 보고합니다. 이것은 얻을 수 있습니다
* 터치 이벤트가 다른 방식으로 작동하기를 원하는 하위 구성 요소에 의해 꺼집니다.
*
* 터치 이벤트가 발생할 때 사용자 터치 활동을 보고합니다. 사용자 활동에 익숙해짐
* 컨트롤을 표시하거나 숨길 시기를 결정합니다. 마우스에 관해서는 간단합니다
* 이벤트, 모든 마우스 이벤트가 컨트롤을 표시해야 하기 때문입니다. 그래서 우리는 마우스를 캡처
* 플레이어에게 버블링되는 이벤트와 발생 시 활동을 보고합니다.
* 터치 이벤트를 사용하면 `touchstart` 및 `touchend` 토글 플레이어만큼 쉽지 않습니다.
* 컨트롤. 따라서 터치 이벤트는 플레이어 수준에서도 도움이 되지 않습니다.
*
* 사용자 활동이 비동기적으로 확인됩니다. 따라서 발생할 수 있는 것은 탭 이벤트입니다.
* 비디오에서 컨트롤을 끕니다. 그런 다음 `touchend` 이벤트가
* 플레이어. 사용자 활동을 보고하면 컨트롤을 오른쪽으로 돌립니다.
* 다시. 또한 터치 이벤트가 버블링되는 것을 완전히 차단하고 싶지 않습니다.
* 또한 `touchmove` 이벤트 및 탭 이외의 모든 것은 회전하지 않아야 합니다.
* 컨트롤을 다시 켭니다.
*
* @listens 컴포넌트#touchstart
* @listens 컴포넌트#touchmove
* @listens 컴포넌트#touchend
* @listens 컴포넌트#touchcancel
*/
enableTouchActivity() {
// 루트 플레이어가 사용자 활동 보고를 지원하지 않는 경우 계속하지 마십시오.
if (!this.player() || !this.player().reportUserActivity) {
반품;
}
// 사용자가 활성 상태임을 보고하기 위한 리스너
const report = Fn.bind(this.player(), this.player().reportUserActivity);
let touchHolding;
this.on('터치스타트', function() {
보고서();
// 그들이 장치를 만지거나 마우스를 누르고 있는 동안,
// 손가락이나 마우스를 움직이지 않아도 활성 상태로 간주합니다.
// 그래서 우리는 그들이 활성 상태임을 계속 업데이트하고 싶습니다.
this.clearInterval(touchHolding);
// activityCheck와 같은 간격으로 보고
touchHolding = this.setInterval(보고서, 250);
});
const touchEnd = 함수(이벤트) {
보고서();
// 터치가 유지되는 경우 활동을 유지하는 간격을 중지합니다.
this.clearInterval(touchHolding);
};
this.on('touchmove', 보고서);
this.on('터치엔드', 터치엔드);
this.on('touchcancel', touchEnd);
}
/**
* 매개변수가 없고 `Component` 컨텍스트에 바인딩되는 콜백.
*
* @callback 컴포넌트~GenericCallback
* @이 컴포넌트
*/
/**
* `x`밀리초 시간 초과 후 실행되는 함수를 생성합니다. 이 기능은
* `window.setTimeout` 주변의 래퍼. 이것을 사용하는 몇 가지 이유가 있습니다.
* 대신:
* 1. 다음과 같은 경우 {@link Component#clearTimeout}을 통해 지워집니다.
* {@link Component#dispose}가 호출됩니다.
* 2. 함수 콜백은 {@link Component~GenericCallback}
*
* > 메모: 이 함수에서 반환된 ID에는 `window.clearTimeout`을 사용할 수 없습니다. 이
* dispose 리스너가 정리되지 않도록 합니다! 사용 해주세요
* 대신 {@link Component#clearTimeout} 또는 {@link Component#dispose}.
*
* @param {Component~GenericCallback} fn
* `timeout` 이후에 실행될 함수입니다.
*
* @param {숫자} 타임아웃
* 지정된 기능을 실행하기 전에 지연되는 시간 초과(밀리초).
*
* @return {숫자}
* 시간 초과를 식별하는 데 사용되는 시간 초과 ID를 반환합니다. 그것은 또한 할 수 있습니다
* {@link Component#clearTimeout}에서 사용하여 제한 시간을 지웁니다.
* 설정했습니다.
*
* @listens 컴포넌트#dispose
* @see [유사]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout}
*/
setTimeout(fn, 타임아웃) {
// 타임아웃 함수에서 적절하게 사용할 수 있도록 변수로 선언
// eslint-비활성화-다음 줄
var timeoutId, disposeFn;
fn = Fn.bind(이, fn);
this.clearTimersOnDispose_();
timeoutId = window.setTimeout(() => {
if (this.setTimeoutIds_.has(timeoutId)) {
this.setTimeoutIds_.delete(timeoutId);
}
fn();
}, 타임아웃);
this.setTimeoutIds_.add(timeoutId);
timeoutId 반환;
}
/**
* `window.setTimeout` 또는
* {@link Component#setTimeout}. {@link Component#setTimeout}을 통해 시간 초과를 설정한 경우
* `window.clearTimout` 대신 이 함수를 사용하십시오. 처분하지 않으면
* 리스너는 {@link Component#dispose}까지 정리되지 않습니다!
*
* @param {숫자} timeoutId
* 지울 제한 시간의 ID입니다. 반환 값
* {@link Component#setTimeout} 또는 `window.setTimeout`.
*
* @return {숫자}
* 지워진 타임아웃 ID를 반환합니다.
*
* @see [유사]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/clearTimeout}
*/
clearTimeout(timeoutId) {
if (this.setTimeoutIds_.has(timeoutId)) {
this.setTimeoutIds_.delete(timeoutId);
window.clearTimeout(timeoutId);
}
timeoutId 반환;
}
/**
* `x`밀리초마다 실행되는 함수를 만듭니다. 이 함수는 래퍼입니다.
* `window.setInterval` 주변. 대신 이것을 사용해야 하는 몇 가지 이유가 있습니다.
* 1. 다음과 같은 경우 {@link Component#clearInterval}을 통해 지워집니다.
* {@link Component#dispose}가 호출됩니다.
* 2. 함수 콜백은 {@link Component~GenericCallback}
*
* @param {Component~GenericCallback} fn
* `x`초마다 실행되는 기능.
*
* @param {숫자} 간격
* `x`밀리초마다 지정된 기능을 실행합니다.
*
* @return {숫자}
* 간격을 식별하는 데 사용할 수 있는 ID를 반환합니다. 그것은 또한에서 사용될 수 있습니다
* {@link Component#clearInterval} 간격을 지우십시오.
*
* @listens 컴포넌트#dispose
* @see [유사]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setInterval}
*/
setInterval(fn, 간격) {
fn = Fn.bind(이, fn);
this.clearTimersOnDispose_();
const intervalId = window.setInterval(fn, 간격);
this.setIntervalIds_.add(intervalId);
반환 간격 ID;
}
/**
* `window.setInterval`을 통해 생성되는 간격을 지우거나
* {@link Component#setInterval}. {@link Component#setInterval}을 통해 간격을 설정하면
* `window.clearInterval` 대신 이 함수를 사용하십시오. 처분하지 않으면
* 리스너는 {@link Component#dispose}까지 정리되지 않습니다!
*
* @param {숫자} 간격 ID
* 지울 간격의 ID입니다. 반환 값
* {@link Component#setInterval} 또는 `window.setInterval`.
*
* @return {숫자}
* 지워진 간격 ID를 반환합니다.
*
* @see [유사]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/clearInterval}
*/
clearInterval(간격 ID) {
if (this.setIntervalIds_.has(intervalId)) {
this.setIntervalIds_.delete(intervalId);
window.clearInterval(intervalId);
}
반환 간격 ID;
}
/**
* requestAnimationFrame(rAF)에 전달할 콜백을 대기하지만
* 몇 가지 추가 보너스 포함:
*
* - rAF를 지원하지 않는 브라우저를 지원합니다.
* {@link Component#setTimeout}.
*
* - 콜백은 {@link Component~GenericCallback}(예:
* 구성 요소에 바인딩됨).
*
* - 구성 요소가 있는 경우 rAF 콜백의 자동 취소가 처리됩니다.
* 호출되기 전에 폐기됩니다.
*
* @param {Component~GenericCallback} fn
* 이 구성 요소에 바인딩되어 바로 실행되는 함수
* 브라우저의 다음 다시 그리기 전에.
*
* @return {숫자}
* 시간 초과를 식별하는 데 사용되는 rAF ID를 반환합니다. 할 수 있습니다
* 취소를 위해 {@link Component#cancelAnimationFrame}에서도 사용
* 애니메이션 프레임 콜백.
*
* @listens 컴포넌트#dispose
* @see [유사]{@link https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame}
*/
requestAnimationFrame(fn) {
// 타이머를 사용하여 폴백합니다.
if (!this.supportsRaf_) {
this.setTimeout(fn, 1000 / 60)을 반환합니다.
}
this.clearTimersOnDispose_();
// rAF 함수에서 적절하게 사용할 수 있도록 변수로 선언
// eslint-비활성화-다음 줄
변수 ID;
fn = Fn.bind(이, fn);
id = window.requestAnimationFrame(() => {
if (this.rafIds_.has(id)) {
this.rafIds_.delete(id);
}
fn();
});
this.rafIds_.add(id);
반환 ID;
}
/**
* 애니메이션 프레임을 요청하지만 명명된 애니메이션은 하나만
* 프레임이 대기됩니다. 다른 사람은 때까지 추가되지 않습니다
* 이전 것이 끝납니다.
*
* @param {문자열} 이름
* 이 requestAnimationFrame에 제공할 이름
*
* @param {Component~GenericCallback} fn
* 이 구성 요소에 바인딩되어 바로 실행되는 함수
* 브라우저의 다음 다시 그리기 전에.
*/
requestNamedAnimationFrame(이름, fn) {
if (this.namedRafs_.has(이름)) {
반품;
}
this.clearTimersOnDispose_();
fn = Fn.bind(이, fn);
const id = this.requestAnimationFrame(() => {
fn();
if (this.namedRafs_.has(이름)) {
this.namedRafs_.delete(이름);
}
});
this.namedRafs_.set(이름, ID);
반환 이름;
}
/**
* 현재 명명된 애니메이션 프레임이 있는 경우 취소합니다.
*
* @param {문자열} 이름
* 취소할 requestAnimationFrame의 이름입니다.
*/
cancelNamedAnimationFrame(이름) {
if (!this.namedRafs_.has(이름)) {
반품;
}
this.cancelAnimationFrame(this.namedRafs_.get(이름));
this.namedRafs_.delete(이름);
}
/**
* {@link Component#requestAnimationFrame}에 전달된 대기 중인 콜백을 취소합니다.
* (RF).
*
* {@link Component#requestAnimationFrame}을 통해 rAF 콜백을 대기시키는 경우,
* `window.cancelAnimationFrame` 대신 이 함수를 사용하십시오. 그렇지 않으면
* 폐기 리스너는 {@link Component#dispose}까지 정리되지 않습니다!
*
* @param {숫자} id
* 삭제할 rAF ID입니다. {@link Component#requestAnimationFrame}의 반환 값입니다.
*
* @return {숫자}
* 지워진 rAF ID를 반환합니다.
*
* @see [유사]{@link https://developer.mozilla.org/en-US/docs/Web/API/window/cancelAnimationFrame}
*/
cancelAnimationFrame(id) {
// 타이머를 사용하여 폴백합니다.
if (!this.supportsRaf_) {
return this.clearTimeout(id);
}
if (this.rafIds_.has(id)) {
this.rafIds_.delete(id);
window.cancelAnimationFrame(id);
}
반환 ID;
}
/**
* `requestAnimationFrame`, `setTimeout`,
* 및 `setInterval`, 폐기 시 삭제.
*
* > 이전에는 각 타이머가 자체적으로 처리 리스너를 추가 및 제거했습니다.
* 더 나은 성능을 위해 모두 일괄 처리하고 `Set`을 사용하기로 결정했습니다.
* 뛰어난 타이머 ID를 추적합니다.
*
* @사적인
*/
clearTimersOnDispose_() {
if (this.clearingTimersOnDispose_) {
반품;
}
this.clearingTimersOnDispose_ = 참;
this.one('처분', () => {
[
['namedRafs_', 'cancelNamedAnimationFrame'],
['rafIds_', 'cancelAnimationFrame'],
['setTimeoutIds_', 'clearTimeout'],
['setIntervalIds_', 'clearInterval']
].forEach(([idName, 취소이름]) => {
// `Set` 키의 경우 실제로 다시 값이 됩니다.
// 따라서 forEach((값, 값) => ` 하지만 우리가 사용하고 싶은 지도에는
// 열쇠.
this[idName].forEach((값, 키) => this[취소이름](키));
});
this.clearingTimersOnDispose_ = 거짓;
});
}
/**
* 이름과 컴포넌트가 주어진 `videojs`로 `Component`를 등록합니다.
*
* > 참고: {@link Tech}는 `Component`로 등록하면 안 됩니다. {@link Tech}
* {@link Tech.registerTech}를 사용하여 등록해야 합니다. 또는
* {@link videojs:videojs.registerTech}.
*
* > 메모: 이 기능은 videojs에서도 다음과 같이 볼 수 있습니다.
* {@link videojs:videojs.registerComponent}.
*
* @param {문자열} 이름
* 등록할 `Component`의 이름입니다.
*
* @param {구성요소} ComponentToRegister
* 등록할 `Component` 클래스.
*
* @return {구성 요소}
* 등록된 `Component`.
*/
static registerComponent(이름, ComponentToRegister) {
if (이름 유형 !== '문자열' || !이름) {
throw new Error(`잘못된 구성 요소 이름, "${name}"; 비어 있지 않은 문자열이어야 합니다.`);
}
const Tech = Component.getComponent('기술');
// Tech가 등록된 경우에만 이 검사가 수행되는지 확인해야 합니다.
const isTech = 기술 && Tech.isTech(ComponentToRegister);
const isComp = 컴포넌트 === ComponentToRegister ||
Component.prototype.isPrototypeOf(ComponentToRegister.prototype);
if (isTech || !isComp) {
이유를 보자;
경우 (isTech) {
이유 = '기술자는 Tech.registerTech()를 사용하여 등록해야 합니다.';
} else {
이유 = '구성 요소 하위 클래스여야 함';
}
throw new Error(`잘못된 구성 요소, "${name}"; ${reason}.`);
}
name = toTitleCase(이름);
if (!Component.components_) {
Component.components_ = {};
}
const Player = Component.getComponent('플레이어');
if (이름 === '플레이어' && 플레이어 && 플레이어.플레이어) {
const 플레이어 = Player.players;
const playerNames = Object.keys(플레이어);
// 폐기된 플레이어가 있는 경우 이름은 그대로 유지됩니다.
// Players.players에서. 따라서 루프를 통해 값을 확인해야 합니다.
// 각 항목은 null이 아닙니다. 이렇게 하면 플레이어 구성 요소를 등록할 수 있습니다.
// 모든 플레이어가 삭제된 후 또는 생성되기 전.
만약 (플레이어 &&
플레이어 이름.길이 > 0 &&
playerNames.map((pname) => players[pname]).every(Boolean)) {
throw new Error('플레이어 생성 후 플레이어 컴포넌트를 등록할 수 없습니다.');
}
}
Component.components_[name] = ComponentToRegister;
Component.components_[toLowerCase(이름)] = ComponentToRegister;
ComponentToRegister 반환;
}
/**
* 등록된 이름을 기반으로 `Component`를 가져옵니다.
*
* @param {문자열} 이름
* 가져올 구성 요소의 이름입니다.
*
* @return {구성 요소}
* 주어진 이름으로 등록된 `Component`.
*/
정적 getComponent(이름) {
if (!name || !Component.components_) {
반품;
}
return Component.components_[이름];
}
}
/**
* 이 컴포넌트가 `requestAnimationFrame`을 지원하는지 여부.
*
* 주로 테스트 목적으로 노출됩니다.
*
* @사적인
* @type {부울}
*/
Component.prototype.supportsRaf_ = typeof window.requestAnimationFrame === '함수' &&
typeof window.cancelAnimationFrame === '함수';
Component.registerComponent('컴포넌트', 컴포넌트);
기본 구성 요소 내보내기;