@js-toolkit/web-utils 1.67.0 → 1.68.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/EventEmitterListener.d.ts +3 -3
- package/EventEmitterListener.js +222 -1
- package/EventEmitterListener.utils.d.ts +24 -13
- package/EventEmitterListener.utils.js +41 -1
- package/EventListeners.js +58 -1
- package/FullscreenController.js +193 -1
- package/README.md +159 -20
- package/WakeLockController.js +76 -1
- package/base64ToDataUrl.js +3 -1
- package/blobToDataUrl.js +10 -1
- package/checkPermission.js +8 -1
- package/copyToClipboard.js +37 -1
- package/createLoop.js +30 -1
- package/createRafLoop.js +56 -1
- package/dataUrlToBlob.js +13 -1
- package/fromBase64.js +10 -1
- package/fullscreen.js +167 -1
- package/fullscreenUtils.js +37 -1
- package/getAspectRatio.js +8 -1
- package/getBrowserLanguage.js +10 -1
- package/getCurrentScriptUrl.js +4 -1
- package/getEventAwaiter.js +41 -1
- package/getGeoCoordinates.js +6 -1
- package/getGeoLocality.js +19 -1
- package/getInnerRect.js +8 -1
- package/getInnerXDimensions.js +9 -1
- package/getInnerYDimensions.js +9 -1
- package/getPinchZoomHandlers.js +134 -1
- package/getRandomID.js +4 -1
- package/getScreenSize.js +23 -1
- package/getSecondsCounter.js +47 -1
- package/iframe/getAutoConnector.js +251 -1
- package/iframe/getOriginFromMessage.js +3 -1
- package/iframe/isIframeLoaded.js +9 -1
- package/iframe/messages.d.ts +2 -2
- package/iframe/messages.js +50 -1
- package/iframe/utils.js +33 -1
- package/imageToBlob.js +20 -1
- package/isImageTypeSupported.js +8 -1
- package/isWebPSupported.js +15 -1
- package/loadImage.js +29 -1
- package/loadScript.d.ts +1 -1
- package/loadScript.js +67 -1
- package/media/Capabilities.js +44 -1
- package/media/MediaNotAttachedError.d.ts +1 -1
- package/media/MediaNotAttachedError.js +6 -1
- package/media/MediaStreamController.js +84 -1
- package/media/PipController.d.ts +2 -2
- package/media/PipController.js +140 -1
- package/media/TextTracksController/TextTracksController.d.ts +0 -3
- package/media/TextTracksController/TextTracksController.js +251 -1
- package/media/TextTracksController/index.js +1 -1
- package/media/TextTracksController/utils.js +147 -1
- package/media/getDurationTime.js +3 -1
- package/media/getMediaSource.js +11 -1
- package/media/getSourceBuffer.js +5 -1
- package/media/isMediaSeekable.js +4 -1
- package/media/parseCueText.js +224 -1
- package/media/resetMedia.js +17 -1
- package/media/timeRanges.js +9 -1
- package/media/toggleNativeSubtitles.js +21 -1
- package/metrics/ga/DataLayerProxy.js +11 -1
- package/metrics/ga/getHandler.js +99 -1
- package/metrics/ga/types.js +1 -1
- package/metrics/types.js +1 -1
- package/metrics/yandex/DataLayerProxy.js +11 -1
- package/metrics/yandex/getHandler.js +63 -1
- package/metrics/yandex/types.js +2 -1
- package/onDOMReady.js +12 -1
- package/onPageReady.js +27 -1
- package/package.json +16 -13
- package/patchConsoleLogging.js +27 -1
- package/performance/getNavigationTiming.js +14 -1
- package/platform/Semver.js +23 -1
- package/platform/getChromeVersion.d.ts +2 -0
- package/platform/getChromeVersion.js +16 -0
- package/platform/getIOSVersion.js +18 -1
- package/platform/getPlatformInfo.js +49 -1
- package/platform/isAirPlayAvailable.js +3 -1
- package/platform/isAndroid.js +7 -1
- package/platform/isChrome.js +7 -1
- package/platform/isEMESupported.js +9 -1
- package/platform/isIOS.js +9 -1
- package/platform/isMSESupported.js +16 -1
- package/platform/isMacOS.js +12 -1
- package/platform/isMediaCapabilitiesSupported.js +6 -1
- package/platform/isMobile.js +16 -1
- package/platform/isMobileSimulation.js +7 -1
- package/platform/isSafari.js +14 -1
- package/platform/isScreenHDR.js +4 -1
- package/platform/isStandaloneApp.js +4 -1
- package/platform/isTelegramWebView.js +3 -1
- package/platform/isTouchSupported.js +4 -1
- package/preventDefault.d.ts +2 -2
- package/preventDefault.js +5 -1
- package/rafCallback.js +9 -1
- package/responsive/MediaQuery.js +40 -1
- package/responsive/MediaQueryListener.js +55 -1
- package/responsive/ViewSize.js +82 -1
- package/responsive/getViewSizeQueryMap.js +7 -1
- package/saveFileAs.js +22 -1
- package/serviceWorker/ServiceWorkerInstaller.d.ts +0 -1
- package/serviceWorker/ServiceWorkerInstaller.js +112 -1
- package/serviceWorker/utils.d.ts +1 -1
- package/serviceWorker/utils.js +86 -1
- package/stopPropagation.d.ts +2 -2
- package/stopPropagation.js +3 -1
- package/takeScreenshot.js +51 -1
- package/toBase64.js +9 -1
- package/toLocalPoint.js +9 -1
- package/types/index.js +2 -1
- package/types/refs.js +1 -1
- package/viewableTracker.js +69 -1
- package/webrtc/PeerConnection.js +212 -1
- package/webrtc/sdputils.js +417 -1
- package/ws/WSController.js +148 -1
|
@@ -1 +1,251 @@
|
|
|
1
|
-
import{EventEmitter}from
|
|
1
|
+
import { EventEmitter } from '@js-toolkit/utils/EventEmitter';
|
|
2
|
+
import { EventListeners } from '../../EventListeners';
|
|
3
|
+
import { MediaNotAttachedError } from '../MediaNotAttachedError';
|
|
4
|
+
import { parseTextTracks, setActiveTextTrack, addTextTracks, isIOSFullscreen, splitRows, buildCueId, } from './utils';
|
|
5
|
+
function dispatchNativeEvent(media, type, data) {
|
|
6
|
+
media.dispatchEvent(new CustomEvent(type, { detail: data }));
|
|
7
|
+
}
|
|
8
|
+
export class TextTracksController extends EventEmitter {
|
|
9
|
+
options;
|
|
10
|
+
eventListeners = new EventListeners();
|
|
11
|
+
addedTracks = [];
|
|
12
|
+
textTrackList = [];
|
|
13
|
+
media;
|
|
14
|
+
/** Already set. */
|
|
15
|
+
textTrack;
|
|
16
|
+
/** To be set. */
|
|
17
|
+
nextTextTrack;
|
|
18
|
+
// eslint-disable-next-line class-methods-use-this
|
|
19
|
+
get Events() {
|
|
20
|
+
return TextTracksController.Events;
|
|
21
|
+
}
|
|
22
|
+
constructor(options) {
|
|
23
|
+
super();
|
|
24
|
+
this.options = {
|
|
25
|
+
emitNativeEvents: false,
|
|
26
|
+
hideActiveTrack: true,
|
|
27
|
+
preferCueRowLength: 0,
|
|
28
|
+
};
|
|
29
|
+
if (options)
|
|
30
|
+
this.setOptions(options);
|
|
31
|
+
}
|
|
32
|
+
setOptions(options) {
|
|
33
|
+
Object.assign(this.options, {
|
|
34
|
+
...options,
|
|
35
|
+
emitNativeEvents: options.emitNativeEvents ?? this.options.emitNativeEvents,
|
|
36
|
+
hideActiveTrack: options.hideActiveTrack ?? this.options.hideActiveTrack,
|
|
37
|
+
preferCueRowLength: options.preferCueRowLength ?? this.options.preferCueRowLength,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
isAttached() {
|
|
41
|
+
return !!this.media;
|
|
42
|
+
}
|
|
43
|
+
getMediaElement() {
|
|
44
|
+
if (!this.media)
|
|
45
|
+
throw new MediaNotAttachedError();
|
|
46
|
+
return this.media;
|
|
47
|
+
}
|
|
48
|
+
detach() {
|
|
49
|
+
this.eventListeners.removeAllListeners();
|
|
50
|
+
this.media = undefined;
|
|
51
|
+
this.addedTracks = [];
|
|
52
|
+
this.textTrackList = [];
|
|
53
|
+
this.textTrack = undefined;
|
|
54
|
+
this.nextTextTrack = undefined;
|
|
55
|
+
}
|
|
56
|
+
attach(media) {
|
|
57
|
+
// if (this.media === media) return;
|
|
58
|
+
this.detach();
|
|
59
|
+
const changeHandler = () => {
|
|
60
|
+
// const textTracks = media.textTracks as PartialSome<TextTrackList, number>;
|
|
61
|
+
const { textTracks } = media;
|
|
62
|
+
const { nextTextTrack } = this;
|
|
63
|
+
let newCurrentIndex = -1;
|
|
64
|
+
// console.log(
|
|
65
|
+
// 'onchange',
|
|
66
|
+
// nextTextTrack,
|
|
67
|
+
// Array.from(textTracks).map((tt) =>
|
|
68
|
+
// JSON.stringify({ lang: tt.language, mode: tt.mode, native: tt.native })
|
|
69
|
+
// ),
|
|
70
|
+
// media.webkitDisplayingFullscreen
|
|
71
|
+
// );
|
|
72
|
+
const { length } = textTracks;
|
|
73
|
+
for (let i = 0; i < length; i += 1) {
|
|
74
|
+
const track = textTracks[i];
|
|
75
|
+
// Disable default texttracks but allow select tracks in iOS native menu in fullscreen
|
|
76
|
+
if ((track.language !== nextTextTrack?.language || track.kind !== nextTextTrack?.kind) &&
|
|
77
|
+
!isIOSFullscreen(media)) {
|
|
78
|
+
track.mode = 'disabled';
|
|
79
|
+
}
|
|
80
|
+
// Disable other tracks if some track already active
|
|
81
|
+
if (track.mode !== 'disabled' && newCurrentIndex >= 0) {
|
|
82
|
+
track.mode = 'disabled';
|
|
83
|
+
}
|
|
84
|
+
if (track.mode !== 'disabled') {
|
|
85
|
+
// console.log(nextTextTrack?.lang, track.mode);
|
|
86
|
+
newCurrentIndex = i;
|
|
87
|
+
// In iOS we use toggleNativeSubtitles so mode will be 'showing' in fullscreen and track.native will be 'true'.
|
|
88
|
+
// Also keep 'showing' mode if iOS and video fullscreen.
|
|
89
|
+
if (!track.native && !isIOSFullscreen(media)) {
|
|
90
|
+
// If texttracks from manifest (eg. m3u8) the mode changes to 'showing'
|
|
91
|
+
// so we need to set 'hidden' or leave it as is.
|
|
92
|
+
const mode = this.options.hideActiveTrack ? 'hidden' : 'showing';
|
|
93
|
+
if (track.mode !== mode)
|
|
94
|
+
track.mode = mode;
|
|
95
|
+
}
|
|
96
|
+
if (track.native &&
|
|
97
|
+
track.language === nextTextTrack?.language &&
|
|
98
|
+
track.kind === nextTextTrack.kind) {
|
|
99
|
+
track.mode = 'showing';
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const newCurrentTrack =
|
|
104
|
+
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
|
105
|
+
textTracks[newCurrentIndex] &&
|
|
106
|
+
((this.textTrackList.at(newCurrentIndex) && {
|
|
107
|
+
...this.textTrackList[newCurrentIndex],
|
|
108
|
+
mode: textTracks[newCurrentIndex].mode,
|
|
109
|
+
}) ??
|
|
110
|
+
{
|
|
111
|
+
id: textTracks[newCurrentIndex].id,
|
|
112
|
+
kind: textTracks[newCurrentIndex].kind,
|
|
113
|
+
language: textTracks[newCurrentIndex].language,
|
|
114
|
+
label: textTracks[newCurrentIndex].label,
|
|
115
|
+
mode: textTracks[newCurrentIndex].mode,
|
|
116
|
+
});
|
|
117
|
+
// console.log('onchange end', nextTextTrack, newCurrentIndex, newCurrentTrack);
|
|
118
|
+
if (this.textTrack?.language !== newCurrentTrack?.language ||
|
|
119
|
+
this.textTrack?.kind !== newCurrentTrack?.kind ||
|
|
120
|
+
this.textTrack?.mode !== newCurrentTrack?.mode) {
|
|
121
|
+
this.textTrack = newCurrentTrack;
|
|
122
|
+
this.nextTextTrack = this.textTrack;
|
|
123
|
+
this.emit(this.Events.CurrentTextTrackChanged, {
|
|
124
|
+
textTrack: this.textTrack,
|
|
125
|
+
index: newCurrentIndex,
|
|
126
|
+
});
|
|
127
|
+
if (this.options.emitNativeEvents) {
|
|
128
|
+
dispatchNativeEvent(media, 'texttrackchange', {
|
|
129
|
+
textTrack: this.textTrack,
|
|
130
|
+
index: newCurrentIndex,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
const cueChangeHandler = (() => {
|
|
136
|
+
const lastCues = [];
|
|
137
|
+
return (event) => {
|
|
138
|
+
const { activeCues } = event.target;
|
|
139
|
+
if (!activeCues)
|
|
140
|
+
return;
|
|
141
|
+
const { length: activeCuesLength } = activeCues;
|
|
142
|
+
const { length: lastCuesLength } = lastCues;
|
|
143
|
+
// Because cuechange triggered twice.
|
|
144
|
+
let changed = lastCuesLength !== activeCuesLength;
|
|
145
|
+
const cues = new Array(activeCuesLength);
|
|
146
|
+
for (let i = 0; i < activeCuesLength; i += 1) {
|
|
147
|
+
const cue = activeCues[i];
|
|
148
|
+
cue.id = cue.id || buildCueId(cue, i);
|
|
149
|
+
cue.rows = splitRows(cue.text, this.options.preferCueRowLength);
|
|
150
|
+
cues[i] = cue;
|
|
151
|
+
if (!changed && lastCues[i]?.id !== cue.id) {
|
|
152
|
+
changed = true;
|
|
153
|
+
}
|
|
154
|
+
lastCues[i] = cue;
|
|
155
|
+
}
|
|
156
|
+
// Trim
|
|
157
|
+
if (lastCuesLength > activeCuesLength) {
|
|
158
|
+
lastCues.splice(activeCuesLength - lastCuesLength);
|
|
159
|
+
}
|
|
160
|
+
if (changed) {
|
|
161
|
+
this.emit(this.Events.TextTrackCueChanged, { textTrack: event.target, cues });
|
|
162
|
+
if (this.options.emitNativeEvents) {
|
|
163
|
+
dispatchNativeEvent(media, 'texttrackcuechange', { textTrack: event.target, cues });
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
})();
|
|
168
|
+
const onTextTracksUpdate = () => {
|
|
169
|
+
this.textTrackList = parseTextTracks(media);
|
|
170
|
+
this.emit(this.Events.TextTrackListChanged, { textTracks: this.textTrackList });
|
|
171
|
+
if (this.options.emitNativeEvents) {
|
|
172
|
+
dispatchNativeEvent(media, 'texttracklistchange', { textTracks: this.textTrackList });
|
|
173
|
+
}
|
|
174
|
+
setActiveTextTrack(media, this.textTrack ?? this.nextTextTrack, this.options.hideActiveTrack && !isIOSFullscreen(media));
|
|
175
|
+
};
|
|
176
|
+
this.media = media;
|
|
177
|
+
let lockUpdate = false;
|
|
178
|
+
const addTrack = (track) => {
|
|
179
|
+
if (!lockUpdate)
|
|
180
|
+
onTextTracksUpdate();
|
|
181
|
+
this.eventListeners.scope(track).on('cuechange', cueChangeHandler);
|
|
182
|
+
};
|
|
183
|
+
const removeTrack = (track) => {
|
|
184
|
+
onTextTracksUpdate();
|
|
185
|
+
this.eventListeners.scope(track).off('cuechange', cueChangeHandler);
|
|
186
|
+
};
|
|
187
|
+
this.eventListeners
|
|
188
|
+
.scope(this.media.textTracks)
|
|
189
|
+
.on('change', changeHandler)
|
|
190
|
+
.on('addtrack', ({ track }) => track && addTrack(track))
|
|
191
|
+
.on('removetrack', ({ track }) => track && removeTrack(track));
|
|
192
|
+
if (this.media.textTracks.length > 0) {
|
|
193
|
+
this.textTrackList = parseTextTracks(media);
|
|
194
|
+
lockUpdate = true;
|
|
195
|
+
try {
|
|
196
|
+
Array.prototype.forEach.call(this.media.textTracks, addTrack);
|
|
197
|
+
onTextTracksUpdate();
|
|
198
|
+
}
|
|
199
|
+
finally {
|
|
200
|
+
lockUpdate = false;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
getTextTracks() {
|
|
205
|
+
return this.textTrackList;
|
|
206
|
+
}
|
|
207
|
+
setTextTracks(textTrackList) {
|
|
208
|
+
const media = this.getMediaElement();
|
|
209
|
+
this.eventListeners.scope(media, '@@setTextTracks').removeAllListeners();
|
|
210
|
+
this.addedTracks.forEach((el) => el.remove());
|
|
211
|
+
if (textTrackList.length === 0)
|
|
212
|
+
return;
|
|
213
|
+
const addTracks = () => {
|
|
214
|
+
addTextTracks(media, textTrackList, (el) => this.addedTracks.push(el));
|
|
215
|
+
// Because event 'addtrack' will be fired in the next tick.
|
|
216
|
+
this.textTrackList = parseTextTracks(media);
|
|
217
|
+
};
|
|
218
|
+
if (media.readyState >= media.HAVE_CURRENT_DATA) {
|
|
219
|
+
addTracks();
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
this.eventListeners.scope(media, '@@setTextTracks').once('loadeddata', addTracks);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
getActiveTextTrack() {
|
|
226
|
+
return this.textTrack;
|
|
227
|
+
}
|
|
228
|
+
setActiveTextTrack(tt) {
|
|
229
|
+
const media = this.getMediaElement();
|
|
230
|
+
this.nextTextTrack =
|
|
231
|
+
tt &&
|
|
232
|
+
this.textTrackList.find((t) => t.language === tt.language && (!tt.kind || t.kind === tt.kind));
|
|
233
|
+
return setActiveTextTrack(media, this.nextTextTrack, this.options.hideActiveTrack && !isIOSFullscreen(media));
|
|
234
|
+
}
|
|
235
|
+
destroy() {
|
|
236
|
+
this.detach();
|
|
237
|
+
this.removeAllListeners();
|
|
238
|
+
}
|
|
239
|
+
[Symbol.dispose]() {
|
|
240
|
+
this.destroy();
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
244
|
+
(function (TextTracksController) {
|
|
245
|
+
let Events;
|
|
246
|
+
(function (Events) {
|
|
247
|
+
Events["TextTrackListChanged"] = "TrackListChanged";
|
|
248
|
+
Events["CurrentTextTrackChanged"] = "CurrentTextTrackChanged";
|
|
249
|
+
Events["TextTrackCueChanged"] = "TextTrackCueChanged";
|
|
250
|
+
})(Events = TextTracksController.Events || (TextTracksController.Events = {}));
|
|
251
|
+
})(TextTracksController || (TextTracksController = {}));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export*from
|
|
1
|
+
export * from './TextTracksController';
|
|
@@ -1 +1,147 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { isIOS } from '../../platform/isIOS';
|
|
2
|
+
const DETACHED_GROUP_ID = '__detached__';
|
|
3
|
+
/** Hack: MSE can't remove texttracks on detaching because of no browser api for that. */
|
|
4
|
+
export function fakeDetachTextTracks(media) {
|
|
5
|
+
Array.prototype.forEach.call(media.textTracks, (textTrack) => {
|
|
6
|
+
const track = textTrack;
|
|
7
|
+
if (track.customGroupId) {
|
|
8
|
+
track.customGroupId = DETACHED_GROUP_ID;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
export function parseTextTracks(media) {
|
|
13
|
+
if (media.textTracks.length === 0)
|
|
14
|
+
return [];
|
|
15
|
+
return (Array.prototype.reduce).call(media.textTracks, (acc, { customGroupId, id, kind, language, label }) => {
|
|
16
|
+
if (customGroupId !== DETACHED_GROUP_ID && language) {
|
|
17
|
+
acc.push({ id, kind, language, label: label ?? '' });
|
|
18
|
+
}
|
|
19
|
+
return acc;
|
|
20
|
+
}, []);
|
|
21
|
+
}
|
|
22
|
+
export function isIOSFullscreen(media) {
|
|
23
|
+
return isIOS() && !!media.webkitDisplayingFullscreen;
|
|
24
|
+
}
|
|
25
|
+
export function findTextTrack(media, conditions) {
|
|
26
|
+
return Array.prototype.find.call(media.textTracks, ({ mode, language }) => {
|
|
27
|
+
return conditions.some((condition) => {
|
|
28
|
+
return ((condition.mode == null || condition.mode === mode) &&
|
|
29
|
+
(condition.language == null || condition.language === language));
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
export function setActiveTextTrack(media, tt, hideActiveTrack) {
|
|
34
|
+
const { textTracks } = media;
|
|
35
|
+
const { length } = textTracks;
|
|
36
|
+
if (length === 0)
|
|
37
|
+
return false;
|
|
38
|
+
// console.log('setActiveTextTrack', tt);
|
|
39
|
+
let changed = false;
|
|
40
|
+
let activated = false;
|
|
41
|
+
for (let i = 0; i < length; i += 1) {
|
|
42
|
+
const track = textTracks[i];
|
|
43
|
+
// Hide active track in favor of custom/native display cues and disable others.
|
|
44
|
+
if (!activated && track.language === tt?.language && (!tt.kind || track.kind === tt.kind)) {
|
|
45
|
+
// Show native subtitles for IOS in video fullscreen mode
|
|
46
|
+
// Otherwise hide subtitles in favor of custom implementation
|
|
47
|
+
const nextMode = hideActiveTrack ? 'hidden' : 'showing';
|
|
48
|
+
track.mode = nextMode;
|
|
49
|
+
activated = true;
|
|
50
|
+
changed = true;
|
|
51
|
+
}
|
|
52
|
+
else if (track.mode !== 'disabled') {
|
|
53
|
+
track.mode = 'disabled';
|
|
54
|
+
changed = true;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return changed;
|
|
58
|
+
}
|
|
59
|
+
/** Dynamically add text tracks if they not exist. */
|
|
60
|
+
export function addTextTracks(media, textTrackList, onAdd) {
|
|
61
|
+
// console.log('addTracks', media.textTracks.length, media.readyState);
|
|
62
|
+
const keySet = new Set();
|
|
63
|
+
for (const { language, kind } of media.textTracks) {
|
|
64
|
+
keySet.add(`${language} ${kind}`);
|
|
65
|
+
}
|
|
66
|
+
textTrackList.forEach((tt) => {
|
|
67
|
+
const kind = tt.kind ?? 'subtitles';
|
|
68
|
+
if (!keySet.has(`${tt.language} ${kind}`)) {
|
|
69
|
+
const trackEl = document.createElement('track');
|
|
70
|
+
trackEl.src = tt.src;
|
|
71
|
+
trackEl.srclang = tt.language;
|
|
72
|
+
trackEl.label = tt.label;
|
|
73
|
+
trackEl.kind = kind;
|
|
74
|
+
// Ignore `default` because the most times there are problems with loading vtt file
|
|
75
|
+
// because of track mode changed from `showing` to `disabled` in the middle of loading
|
|
76
|
+
// and therefore no cues are loaded.
|
|
77
|
+
trackEl.default = false;
|
|
78
|
+
// Add texttrack
|
|
79
|
+
onAdd(media.appendChild(trackEl));
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
export function buildCueId(cue, index) {
|
|
84
|
+
return `${cue.startTime}-${index}`;
|
|
85
|
+
}
|
|
86
|
+
export function splitRows(text, preferLineLength) {
|
|
87
|
+
if (preferLineLength <= 0)
|
|
88
|
+
return text.split(/\r?\n/);
|
|
89
|
+
const lines = [];
|
|
90
|
+
let lineIdx = 0;
|
|
91
|
+
let nextLineIdx = lineIdx;
|
|
92
|
+
let word = '';
|
|
93
|
+
const { length } = text;
|
|
94
|
+
for (let i = 0; i <= length; i += 1) {
|
|
95
|
+
const char = text[i];
|
|
96
|
+
// Oversized from the prev line
|
|
97
|
+
if (word.length > 0 && lineIdx < nextLineIdx) {
|
|
98
|
+
lines[nextLineIdx] = char == null ? word.trim() : word.trimStart();
|
|
99
|
+
word = '';
|
|
100
|
+
}
|
|
101
|
+
lineIdx = nextLineIdx;
|
|
102
|
+
let line = lines[lineIdx] ?? '';
|
|
103
|
+
// Line break
|
|
104
|
+
if (char === '\n' || char === '\r\n') {
|
|
105
|
+
// Add last word to the current line or keep it for the next one.
|
|
106
|
+
if (word.length > 0 && line.length + word.length <= preferLineLength) {
|
|
107
|
+
line += line.length > 0 ? word : word.trimStart();
|
|
108
|
+
word = '';
|
|
109
|
+
}
|
|
110
|
+
nextLineIdx += 1;
|
|
111
|
+
}
|
|
112
|
+
// Word break
|
|
113
|
+
else if (char === ' ') {
|
|
114
|
+
if (word.length > 0) {
|
|
115
|
+
const oversize = line.length + word.length > preferLineLength;
|
|
116
|
+
// console.log(oversize, line, word, word.length);
|
|
117
|
+
if (oversize && line.length > 0) {
|
|
118
|
+
nextLineIdx += 1;
|
|
119
|
+
line = line.trimEnd();
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
line += line.length === 0 ? word.trimStart() : word;
|
|
123
|
+
word = '';
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
word += char;
|
|
127
|
+
}
|
|
128
|
+
// The last word
|
|
129
|
+
else if (char == null && word.length > 0) {
|
|
130
|
+
const oversize = line.length + word.length > preferLineLength;
|
|
131
|
+
// console.log(char, oversize, line);
|
|
132
|
+
if (!oversize || line.length === 0) {
|
|
133
|
+
line += word;
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
lineIdx += 1;
|
|
137
|
+
line = word.trim();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
// Just add to word
|
|
141
|
+
else if (char) {
|
|
142
|
+
word += char;
|
|
143
|
+
}
|
|
144
|
+
lines[lineIdx] = line;
|
|
145
|
+
}
|
|
146
|
+
return lines;
|
|
147
|
+
}
|
package/media/getDurationTime.js
CHANGED
package/media/getMediaSource.js
CHANGED
|
@@ -1 +1,11 @@
|
|
|
1
|
-
export function getMediaSource(
|
|
1
|
+
export function getMediaSource(
|
|
2
|
+
/**
|
|
3
|
+
* `prefer` - returns ManagedMediaSource or MediaSource.
|
|
4
|
+
* `maybe` - returns ManagedMediaSource.
|
|
5
|
+
* Otherwise returns MediaSource.
|
|
6
|
+
*/
|
|
7
|
+
managedMediaSource) {
|
|
8
|
+
if (managedMediaSource === 'maybe')
|
|
9
|
+
return window.ManagedMediaSource;
|
|
10
|
+
return (managedMediaSource === 'prefer' && window.ManagedMediaSource) || window.MediaSource; // || window.WebKitMediaSource;
|
|
11
|
+
}
|
package/media/getSourceBuffer.js
CHANGED
|
@@ -1 +1,5 @@
|
|
|
1
|
-
export function getSourceBuffer(
|
|
1
|
+
export function getSourceBuffer(managedMediaSource) {
|
|
2
|
+
if (managedMediaSource === 'maybe')
|
|
3
|
+
return window.ManagedSourceBuffer;
|
|
4
|
+
return (managedMediaSource === 'prefer' && window.ManagedSourceBuffer) || window.SourceBuffer; // || window.WebKitSourceBuffer;
|
|
5
|
+
}
|
package/media/isMediaSeekable.js
CHANGED
|
@@ -1 +1,4 @@
|
|
|
1
|
-
import{getTimeRangeDuration}from
|
|
1
|
+
import { getTimeRangeDuration } from './timeRanges';
|
|
2
|
+
export function isMediaSeekable(media) {
|
|
3
|
+
return getTimeRangeDuration(media.seekable) > 0;
|
|
4
|
+
}
|