/**
* @file 텍스트-트랙-디스플레이.js
*/
'../component'에서 컴포넌트 가져오기;
import * as Fn from '../utils/fn.js';
import * as Dom from '../utils/dom.js';
'글로벌/창'에서 창 가져오기;
const darkGray = '#222';
const lightGray = '#ccc';
const fontMap = {
고정 폭: '단일 간격',
sansSerif: '산세리프',
serif: '세리프',
monospaceSansSerif: '"안달레 모노", "루시다 콘솔", 모노스페이스',
monospaceSerif: '"Courier New", 모노스페이스',
ProportionalSansSerif: '산세리프',
ProportionalSerif: '세리프',
casual: '"Comic Sans MS", 임팩트, 판타지',
script: '"모노타이프 코르시바", 필기체',
smallcaps: '"Andale 모노", "Lucida Console", 모노스페이스, 산세리프'
};
/**
* 주어진 16진수 색상 코드에서 rgba 색상을 구성합니다.
*
* @param {숫자} 색상
* #f0e 또는 #f604e2와 같은 색상의 16진수.
*
* @param {숫자} 불투명도
* 불투명도 값, 0.0 - 1.0.
*
* @return {문자열}
* 'rgba(255, 0, 0, 0.3)'과 같이 생성된 rgba 색상입니다.
*/
내보내기 기능 constructColor(색상, 불투명도) {
16 진수하자;
if (색상 길이 === 4) {
// 색상은 "#f0e"처럼 보입니다.
hex = 색상[1] + 색상[1] + 색상[2] + 색상[2] + 색상[3] + 색상[3];
} 그렇지 않으면 (color.length === 7) {
// 색상은 "#f604e2"처럼 보입니다.
hex = color.slice(1);
} else {
throw new Error('잘못된 색상 코드가 제공되었습니다. ' + color + '; #f0e 또는 #f604e2와 같은 형식이어야 합니다.');
}
리턴 'rgba(' +
parseInt(hex.slice(0, 2), 16) + ',' +
parseInt(hex.slice(2, 4), 16) + ',' +
parseInt(hex.slice(4, 6), 16) + ',' +
불투명도 + ')';
}
/**
* DOM 요소의 스타일을 업데이트하십시오. 일부 스타일을 변경하면 오류가 발생합니다.
* 특히 IE8에서. 그것들은 멍청해야합니다.
*
* @param {요소} 엘
* 스타일을 지정할 DOM 요소입니다.
*
* @param {문자열} 스타일
* 스타일을 지정해야 하는 요소의 CSS 속성입니다.
*
* @param {문자열} 규칙
* 속성에 적용해야 하는 스타일 규칙입니다.
*
* @사적인
*/
function tryUpdateStyle(el, 스타일, 규칙) {
{
el.style[스타일] = 규칙;
} 잡기 (e) {
// 린터를 만족시킵니다.
반품;
}
}
/**
* 텍스트 트랙 큐를 표시하기 위한 구성 요소입니다.
*
* @extends 컴포넌트
*/
클래스 TextTrackDisplay는 구성 요소 {를 확장합니다.
/**
* 이 클래스의 인스턴스를 만듭니다.
*
* @param {플레이어} 플레이어
* 이 클래스가 연결되어야 하는 `Player`.
*
* @param {객체} [옵션]
* 플레이어 옵션의 키/값 저장소.
*
* @param {Component~ReadyCallback} [준비]
* `TextTrackDisplay`가 준비되었을 때 호출할 함수.
*/
생성자(플레이어, 옵션, 준비) {
super(플레이어, 옵션, 준비);
const updateDisplayHandler = (e) => this.updateDisplay(e);
player.on('loadstart', (e) => this.toggleDisplay(e));
player.on('texttrackchange', updateDisplayHandler);
player.on('loadedmetadata', (e) => this.preselectTrack(e));
// 플레이어 초기화 중에 호출되었지만 오류가 발생했습니다.
// 트랙이 기본적으로 표시되어야 하고 디스플레이가 아직 로드되지 않은 경우.
// 지원할 때 외부 트랙 로더로 이동해야 합니다.
// 디스플레이가 필요하지 않은 트랙.
player.ready(Fn.bind(this, function() {
만약 (player.tech_ && player.tech_.featuresNativeTextTracks) {
this.hide();
반품;
}
player.on('fullscreenchange', updateDisplayHandler);
player.on('playerresize', updateDisplayHandler);
window.addEventListener('orientationchange', updateDisplayHandler);
player.on('처리', () => window.removeEventListener('orientationchange', updateDisplayHandler));
const 트랙 = this.options_.playerOptions.tracks || [];
에 대한 (하자 i = 0; i < 트랙.길이; i++) {
this.player_.addRemoteTextTrack(tracks[i], true);
}
this.preselectTrack();
}));
}
/**
* 다음 우선순위에 따라 트랙을 미리 선택합니다.
* - 이전에 선택한 {@link TextTrack}의 언어 및 종류와 일치
* - 이전에 선택한 {@link TextTrack}의 언어만 일치
* - 첫 번째 기본 캡션 트랙입니다.
* - 첫 번째 기본 설명 트랙입니다.
*
* @listens Player#loadstart
*/
preselectTrack() {
const 모드 = {캡션: 1, 자막: 1};
const trackList = this.player_.textTracks();
const userPref = this.player_.cache_.selectedLanguage;
let firstDesc;
let firstCaptions;
선호 추적하자;
에 대한 (하자 i = 0; i < trackList.length; i++) {
const track = trackList[i];
만약에 (
userPref && userPref.활성화 &&
userPref.언어 && userPref.언어 === 트랙.언어 &&
모드의 track.kind
) {
// 항상 언어와 종류가 모두 일치하는 트랙을 선택합니다.
if (track.kind === userPref.kind) {
선호트랙 = 트랙;
// 또는 언어와 일치하는 첫 번째 트랙을 선택합니다.
} 그렇지 않으면 (!preferredTrack) {
선호트랙 = 트랙;
}
// offTextTrackMenuItem을 클릭한 경우 모든 항목을 지웁니다.
} 그렇지 않으면 (userPref && !userPref.활성화) {
선호트랙 = null;
firstDesc = null;
firstCaptions = null;
} 그렇지 않으면 (track.default) {
if (track.kind === '설명' && !firstDesc) {
firstDesc = 트랙;
} else if (모드에서 track.kind && !firstCaptions) {
firstCaptions = 트랙;
}
}
}
// preferredTrack은 사용자 기본 설정과 일치하고
// 다른 모든 트랙보다 우선합니다.
// 따라서 첫 번째 기본 트랙 앞에 preferredTrack을 표시합니다.
// 설명 트랙 이전에 자막/캡션 트랙
if (preferredTrack) {
PreferredTrack.mode = '보여주기';
} 그렇지 않으면 (firstCaptions) {
firstCaptions.mode = '보여주기';
} 그렇지 않으면 (firstDesc) {
firstDesc.mode = '보여주기';
}
}
/**
* {@link TextTrack}의 표시를 현재 상태에서 다른 상태로 전환합니다.
* 두 가지 상태만 있습니다.
* - '보여진'
* - '숨김'
*
* @listens Player#loadstart
*/
토글디스플레이() {
if (this.player_.tech_ && this.player_.tech_.featuresNativeTextTracks) {
this.hide();
} else {
this.show();
}
}
/**
* {@link Component}의 DOM 요소를 생성합니다.
*
* @return {요소}
* 생성된 요소입니다.
*/
createEl() {
return super.createEl('div', {
className: 'vjs-텍스트-트랙-디스플레이'
}, {
'번역하다': '예',
'aria-live': '끄기',
'aria-atomic': '참'
});
}
/**
* 표시된 모든 {@link TextTrack}을 지웁니다.
*/
클리어디스플레이() {
if (typeof window.WebVTT === '함수') {
window.WebVTT.processCues(window, [], this.el_);
}
}
/**
* {@link Player#texttrackchange} 또는
* {@link Player#fullscreenchange}가 실행됩니다.
*
* @listens Player#texttrackchange
* @listens Player#fullscreenchange
*/
업데이트디스플레이() {
const 트랙 = this.player_.textTracks();
const allowMultipleShowingTracks = this.options_.allowMultipleShowingTracks;
this.clearDisplay();
if (allowMultipleShowingTracks) {
const 보여주는 트랙 = [];
에 대한 (하자 i = 0; i < 트랙.길이; ++i) {
const 트랙 = 트랙[i];
if (track.mode !== '표시') {
계속하다;
}
보여주는Tracks.push(트랙);
}
this.updateForTrack(showingTracks);
반품;
}
// 트랙 표시 우선 순위 모델: 여러 트랙이 '표시'되는 경우,
// 'showing'인 첫 번째 '자막' 또는 '자막' 트랙 표시,
// 그렇지 않으면 'showing'인 첫 번째 'descriptions' 트랙을 표시합니다.
let descriptionsTrack = null;
let captionsSubtitlesTrack = null;
let i = 트랙 길이;
동안 (i--) {
const 트랙 = 트랙[i];
if (track.mode === '표시') {
if (track.kind === '설명') {
descriptionsTrack = 트랙;
} else {
captionsSubtitlesTrack = 트랙;
}
}
}
if (captionsSubtitlesTrack) {
if (this.getAttribute('aria-live') !== 'off') {
this.setAttribute('aria-live', 'off');
}
this.updateForTrack(captionsSubtitlesTrack);
} 그렇지 않으면 (descriptionsTrack) {
if (this.getAttribute('aria-live') !== '주장') {
this.setAttribute('aria-live', 'assertive');
}
this.updateForTrack(descriptionsTrack);
}
}
/**
* {@Link TextTrackSettings}에 따라 {@Link TextTrack} activeCue의 스타일을 지정합니다.
*
* @param {TextTrack} 트랙
* 스타일에 대한 활성 신호를 포함하는 텍스트 트랙 개체.
*/
updateDisplayState(트랙) {
const overrides = this.player_.textTrackSettings.getValues();
const 큐 = track.activeCues;
i = cues.length;
동안 (i--) {
const 큐 = 큐[i];
if (!cue) {
계속하다;
}
const cueDiv = cue.displayState;
if (overrides.color) {
cueDiv.firstChild.style.color = overrides.color;
}
if (overrides.textOpacity) {
tryUpdateStyle(
cueDiv.firstChild,
'색상',
구성 색상(
overrides.color || '#fff',
overrides.textOpacity
)
);
}
if (overrides.backgroundColor) {
cueDiv.firstChild.style.backgroundColor = overrides.backgroundColor;
}
if (overrides.backgroundOpacity) {
tryUpdateStyle(
cueDiv.firstChild,
'배경색',
구성 색상(
overrides.backgroundColor || '#000',
overrides.backgroundOpacity
)
);
}
if (overrides.windowColor) {
if (overrides.windowOpacity) {
tryUpdateStyle(
큐디브,
'배경색',
구성 색상(overrides.windowColor, overrides.windowOpacity)
);
} else {
cueDiv.style.backgroundColor = overrides.windowColor;
}
}
if (overrides.edgeStyle) {
if (overrides.edgeStyle === 'dropshadow') {
cueDiv.firstChild.style.textShadow = `2px 2px 3px ${darkGray}, 2px 2px 4px ${darkGray}, 2px 2px 5px ${darkGray}`;
} 그렇지 않으면 (overrides.edgeStyle === '올림') {
cueDiv.firstChild.style.textShadow = `1px 1px ${darkGray}, 2px 2px ${darkGray}, 3px 3px ${darkGray}`;
} 그렇지 않으면 (overrides.edgeStyle === '우울함') {
cueDiv.firstChild.style.textShadow = `1px 1px ${lightGray}, 0 1px ${lightGray}, -1px -1px ${darkGray}, 0 -1px ${darkGray}`;
} 그렇지 않으면 (overrides.edgeStyle === 'uniform') {
cueDiv.firstChild.style.textShadow = `0 0 4px ${darkGray}, 0 0 4px ${darkGray}, 0 0 4px ${darkGray}, 0 0 4px ${darkGray}`;
}
}
if (overrides.fontPercent && overrides.fontPercent !== 1) {
const fontSize = window.parseFloat(cueDiv.style.fontSize);
cueDiv.style.fontSize = (fontSize * overrides.fontPercent) + 'px';
cueDiv.style.height = '자동';
cueDiv.style.top = '자동';
}
if (overrides.fontFamily && overrides.fontFamily !== '기본값') {
if (overrides.fontFamily === '작은 대문자') {
cueDiv.firstChild.style.fontVariant = '작은 대문자';
} else {
cueDiv.firstChild.style.fontFamily = fontMap[overrides.fontFamily];
}
}
}
}
/**
* {@link Tech}의 {@link TextTrackList}에 {@link TextTrack}을 추가합니다.
*
* @param {TextTrack|TextTrack[]} 트랙
* 목록에 추가할 텍스트 트랙 개체 또는 텍스트 트랙 배열.
*/
updateForTrack(트랙) {
if (!Array.isArray(트랙)) {
트랙 = [트랙];
}
if (window.WebVTT 유형 !== '함수' ||
tracks.every((트랙)=> {
return !track.activeCues;
})) {
반품;
}
const 큐 = [];
// 모든 활성 트랙 큐를 푸시합니다.
에 대한 (하자 i = 0; i < 트랙.길이; ++i) {
const 트랙 = 트랙[i];
에 대한 (하자 j = 0; j < track.activeCues.length; ++j) {
cues.push(track.activeCues[j]);
}
}
// 새로운 큐를 처리하기 전에 모든 큐를 제거합니다.
window.WebVTT.processCues(window, cues, this.el_);
// 각 언어 텍스트 트랙에 고유한 클래스를 추가합니다. & 필요한 경우 설정 스타일 추가
에 대한 (하자 i = 0; i < 트랙.길이; ++i) {
const 트랙 = 트랙[i];
에 대한 (하자 j = 0; j < track.activeCues.length; ++j) {
const cueEl = track.activeCues[j].displayState;
Dom.addClass(cueEl, 'vjs-text-track-cue');
Dom.addClass(cueEl, 'vjs-text-track-cue-' + ((track.language) ? track.language : i));
if (트랙.언어) {
Dom.setAttribute(cueEl, 'lang', track.language);
}
}
if (this.player_.textTrackSettings) {
this.updateDisplayState(트랙);
}
}
}
}
Component.registerComponent('TextTrackDisplay', TextTrackDisplay);
기본 TextTrackDisplay 내보내기;