@gcorevideo/player 2.28.19 → 2.28.21
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/assets/bottom-gear/gear.scss +2 -2
- package/assets/icons/new/arrow-right.svg +1 -1
- package/assets/icons/new/hd.svg +1 -1
- package/assets/icons/new/speed.svg +1 -1
- package/dist/core.js +1 -1
- package/dist/index.css +303 -303
- package/dist/index.embed.js +118 -37
- package/dist/index.js +118 -38
- package/lib/plugins/audio-selector/AudioTracks.d.ts +2 -0
- package/lib/plugins/audio-selector/AudioTracks.d.ts.map +1 -1
- package/lib/plugins/audio-selector/AudioTracks.js +11 -0
- package/lib/plugins/bottom-gear/BottomGear.d.ts +2 -0
- package/lib/plugins/bottom-gear/BottomGear.d.ts.map +1 -1
- package/lib/plugins/bottom-gear/BottomGear.js +11 -0
- package/lib/plugins/level-selector/QualityLevels.d.ts.map +1 -1
- package/lib/plugins/level-selector/QualityLevels.js +4 -0
- package/lib/plugins/media-control/MediaControl.d.ts +10 -0
- package/lib/plugins/media-control/MediaControl.d.ts.map +1 -1
- package/lib/plugins/media-control/MediaControl.js +21 -22
- package/lib/plugins/subtitles/ClosedCaptions.d.ts +3 -0
- package/lib/plugins/subtitles/ClosedCaptions.d.ts.map +1 -1
- package/lib/plugins/subtitles/ClosedCaptions.js +30 -11
- package/lib/testUtils.d.ts.map +1 -1
- package/lib/testUtils.js +2 -0
- package/lib/utils/clickaway.d.ts +15 -0
- package/lib/utils/clickaway.d.ts.map +1 -0
- package/lib/utils/clickaway.js +40 -0
- package/package.json +1 -1
- package/src/plugins/audio-selector/AudioTracks.ts +15 -1
- package/src/plugins/bottom-gear/BottomGear.ts +13 -0
- package/src/plugins/level-selector/QualityLevels.ts +4 -0
- package/src/plugins/media-control/MediaControl.ts +21 -24
- package/src/plugins/subtitles/ClosedCaptions.ts +34 -12
- package/src/plugins/subtitles/__tests__/ClosedCaptions.test.ts +9 -3
- package/src/testUtils.ts +2 -0
- package/src/utils/clickaway.ts +43 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -20,6 +20,7 @@ import volumeMaxIcon from '../../../assets/icons/new/volume-max.svg';
|
|
|
20
20
|
import volumeOffIcon from '../../../assets/icons/new/volume-off.svg';
|
|
21
21
|
import fullscreenOffIcon from '../../../assets/icons/new/fullscreen-off.svg';
|
|
22
22
|
import fullscreenOnIcon from '../../../assets/icons/new/fullscreen-on.svg';
|
|
23
|
+
import { mediaControlClickaway } from '../../utils/clickaway.js';
|
|
23
24
|
const STANDARD_MEDIA_CONTROL_ELEMENTS = [
|
|
24
25
|
'duration',
|
|
25
26
|
'fullscreen',
|
|
@@ -268,6 +269,7 @@ export class MediaControl extends UICorePlugin {
|
|
|
268
269
|
}
|
|
269
270
|
/**
|
|
270
271
|
* @internal
|
|
272
|
+
* The methods declared here will be exposed via the main player object API
|
|
271
273
|
*/
|
|
272
274
|
getExternalInterface() {
|
|
273
275
|
return {
|
|
@@ -312,9 +314,7 @@ export class MediaControl extends UICorePlugin {
|
|
|
312
314
|
this.listenTo(this.core.activeContainer, Events.CONTAINER_PAUSE, this.changeTogglePlay);
|
|
313
315
|
this.listenTo(this.core.activeContainer, Events.CONTAINER_STOP, this.changeTogglePlay);
|
|
314
316
|
this.listenTo(this.core.activeContainer, Events.CONTAINER_DBLCLICK, this.toggleFullscreen);
|
|
315
|
-
this.listenTo(this.core.activeContainer, Events.CONTAINER_CLICK, () =>
|
|
316
|
-
this.clickaway(this.core.activeContainer.$el[0]);
|
|
317
|
-
});
|
|
317
|
+
this.listenTo(this.core.activeContainer, Events.CONTAINER_CLICK, () => this.clickaway(this.core.activeContainer.$el[0]));
|
|
318
318
|
this.listenTo(this.core.activeContainer, Events.CONTAINER_TIMEUPDATE, this.onTimeUpdate);
|
|
319
319
|
this.listenTo(this.core.activeContainer, Events.CONTAINER_PROGRESS, this.updateProgressBar);
|
|
320
320
|
this.listenTo(this.core.activeContainer, Events.CONTAINER_SETTINGSUPDATE, this.updateSettings);
|
|
@@ -938,6 +938,23 @@ export class MediaControl extends UICorePlugin {
|
|
|
938
938
|
mount(name, element) {
|
|
939
939
|
mountTo(this.getMountParent(name), element);
|
|
940
940
|
}
|
|
941
|
+
/**
|
|
942
|
+
* Set or reset the keep visibility state
|
|
943
|
+
*
|
|
944
|
+
* Keep visibility state controls whether the media control is hidden automatically after a delay.
|
|
945
|
+
* Keep visibility prevents the the auto-hide behaviour
|
|
946
|
+
*
|
|
947
|
+
* @param keepVisible - The state
|
|
948
|
+
*/
|
|
949
|
+
setKeepVisible(keepVisible) {
|
|
950
|
+
this.keepVisible = keepVisible;
|
|
951
|
+
if (keepVisible) {
|
|
952
|
+
this.clickaway(this.core.activeContainer.$el[0]);
|
|
953
|
+
}
|
|
954
|
+
else {
|
|
955
|
+
this.clickaway(null);
|
|
956
|
+
}
|
|
957
|
+
}
|
|
941
958
|
getMountParent(name) {
|
|
942
959
|
switch (name) {
|
|
943
960
|
case 'root':
|
|
@@ -1237,9 +1254,7 @@ export class MediaControl extends UICorePlugin {
|
|
|
1237
1254
|
delayHide() {
|
|
1238
1255
|
this.hide(this.options.hideMediaControlDelay || DEFAULT_HIDE_DELAY);
|
|
1239
1256
|
}
|
|
1240
|
-
|
|
1241
|
-
// as opposed to the click event
|
|
1242
|
-
clickaway = clickaway(() => setTimeout(this.resetUserKeepVisible, 0));
|
|
1257
|
+
clickaway = mediaControlClickaway(() => this.resetUserKeepVisible());
|
|
1243
1258
|
}
|
|
1244
1259
|
MediaControl.extend = function (properties) {
|
|
1245
1260
|
return extend(MediaControl, properties);
|
|
@@ -1280,19 +1295,3 @@ function mergeElements(a, b) {
|
|
|
1280
1295
|
return acc;
|
|
1281
1296
|
}, a);
|
|
1282
1297
|
}
|
|
1283
|
-
function clickaway(callback) {
|
|
1284
|
-
let handler = (event) => { };
|
|
1285
|
-
return (node) => {
|
|
1286
|
-
window.removeEventListener('click', handler);
|
|
1287
|
-
if (!node) {
|
|
1288
|
-
return;
|
|
1289
|
-
}
|
|
1290
|
-
handler = (event) => {
|
|
1291
|
-
if (!node.contains(event.target)) {
|
|
1292
|
-
window.removeEventListener('click', handler);
|
|
1293
|
-
callback();
|
|
1294
|
-
}
|
|
1295
|
-
};
|
|
1296
|
-
window.addEventListener('click', handler);
|
|
1297
|
-
};
|
|
1298
|
-
}
|
|
@@ -114,6 +114,7 @@ export declare class ClosedCaptions extends UICorePlugin {
|
|
|
114
114
|
private applyPreselectedSubtitles;
|
|
115
115
|
private hideMenu;
|
|
116
116
|
private toggleMenu;
|
|
117
|
+
private setKeepVisible;
|
|
117
118
|
private itemElement;
|
|
118
119
|
private allItemElements;
|
|
119
120
|
private selectSubtitles;
|
|
@@ -125,5 +126,7 @@ export declare class ClosedCaptions extends UICorePlugin {
|
|
|
125
126
|
private renderIcon;
|
|
126
127
|
private clampPopup;
|
|
127
128
|
private mount;
|
|
129
|
+
private get shouldKeepVisible();
|
|
130
|
+
private clickaway;
|
|
128
131
|
}
|
|
129
132
|
//# sourceMappingURL=ClosedCaptions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ClosedCaptions.d.ts","sourceRoot":"","sources":["../../../src/plugins/subtitles/ClosedCaptions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,YAAY,EAAwB,MAAM,cAAc,CAAA;AAOzE,OAAO,sCAAsC,CAAA;
|
|
1
|
+
{"version":3,"file":"ClosedCaptions.d.ts","sourceRoot":"","sources":["../../../src/plugins/subtitles/ClosedCaptions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,YAAY,EAAwB,MAAM,cAAc,CAAA;AAOzE,OAAO,sCAAsC,CAAA;AAiB7C;;;GAGG;AACH,MAAM,MAAM,4BAA4B,GAAG;IACzC;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,qBAAa,cAAe,SAAQ,YAAY;IAC9C,OAAO,CAAC,oBAAoB,CAAQ;IAEpC,OAAO,CAAC,MAAM,CAAQ;IAEtB,OAAO,CAAC,IAAI,CAAQ;IAEpB,OAAO,CAAC,KAAK,CAA6B;IAE1C,OAAO,CAAC,MAAM,CAAsB;IAEpC,OAAO,CAAC,KAAK,CAA2B;IAExC;;OAEG;IACH,IAAI,IAAI,WAEP;IAED;;OAEG;IACH,IAAI,gBAAgB;;MAEnB;IAED;;OAEG;IACH,MAAM,KAAK,OAAO,WAEjB;IAED,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAyB;IAEhE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAuB;IAE3D;;OAEG;IACH,IAAa,UAAU;;MAItB;IAED;;OAEG;IACH,IAAa,MAAM;;;MAKlB;IAED,OAAO,KAAK,mBAAmB,GAK9B;IAED;;OAEG;IACM,UAAU;IASnB,OAAO,CAAC,WAAW;IAkBnB,OAAO,CAAC,kBAAkB;IAmD1B,OAAO,CAAC,mBAAmB;IAQ3B,OAAO,CAAC,iBAAiB;IAkCzB,OAAO,CAAC,WAAW;IAUnB,OAAO,CAAC,SAAS;IAWjB,OAAO,CAAC,UAAU;IASlB,OAAO,CAAC,iBAAiB;IAqBzB;;OAEG;IACH,IAAI;IAcJ;;OAEG;IACH,IAAI;IAiBJ,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,UAAU;IAUlB;;OAEG;IACM,MAAM;IA4Bf,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,UAAU;IAOlB,OAAO,CAAC,YAAY;IAYpB,OAAO,CAAC,yBAAyB;IAejC,OAAO,CAAC,QAAQ;IAOhB,OAAO,CAAC,UAAU;IAclB,OAAO,CAAC,cAAc;IAOtB,OAAO,CAAC,WAAW;IAKnB,OAAO,CAAC,eAAe;IAIvB,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,eAAe;IAiBvB,OAAO,CAAC,eAAe;IAIvB,OAAO,CAAC,iBAAiB;IAIzB,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,yBAAyB;IAiBjC,OAAO,CAAC,UAAU;IAMlB,OAAO,CAAC,UAAU;IAOlB,OAAO,CAAC,KAAK;IAOb,OAAO,KAAK,iBAAiB,GAE5B;IAED,OAAO,CAAC,SAAS,CAA+C;CACjE"}
|
|
@@ -9,6 +9,7 @@ import comboboxHTML from '../../../assets/subtitles/combobox.ejs';
|
|
|
9
9
|
import stringHTML from '../../../assets/subtitles/string.ejs';
|
|
10
10
|
import { isFullscreen } from '../utils/fullscreen.js';
|
|
11
11
|
import { ExtendedEvents } from '../media-control/MediaControl.js';
|
|
12
|
+
import { mediaControlClickaway } from '../../utils/clickaway.js';
|
|
12
13
|
const VERSION = '2.19.14';
|
|
13
14
|
const LOCAL_STORAGE_CC_ID = 'gplayer.plugins.cc.selected';
|
|
14
15
|
const T = 'plugins.cc';
|
|
@@ -93,8 +94,7 @@ export class ClosedCaptions extends UICorePlugin {
|
|
|
93
94
|
}
|
|
94
95
|
get preselectedLanguage() {
|
|
95
96
|
return (this.core.options.cc?.language ??
|
|
96
|
-
this.core.options.subtitles?.language
|
|
97
|
-
'');
|
|
97
|
+
this.core.options.subtitles?.language);
|
|
98
98
|
}
|
|
99
99
|
/**
|
|
100
100
|
* @internal
|
|
@@ -119,6 +119,9 @@ export class ClosedCaptions extends UICorePlugin {
|
|
|
119
119
|
onContainerChanged() {
|
|
120
120
|
this.listenTo(this.core.activeContainer, Events.CONTAINER_FULLSCREEN, this.onContainerResize);
|
|
121
121
|
this.listenTo(this.core.activeContainer, Events.CONTAINER_RESIZE, this.onContainerResize);
|
|
122
|
+
this.listenTo(this.core.activeContainer, Events.CONTAINER_DESTROYED, () => {
|
|
123
|
+
this.clickaway(null);
|
|
124
|
+
});
|
|
122
125
|
this.listenTo(this.core.activeContainer, 'container:advertisement:start', this.onStartAd);
|
|
123
126
|
this.listenTo(this.core.activePlayback, Events.PLAYBACK_SUBTITLE_AVAILABLE, this.onSubtitleAvailable);
|
|
124
127
|
this.listenTo(this.core.activePlayback, Events.PLAYBACK_SUBTITLE_CHANGED, this.onSubtitleChanged);
|
|
@@ -147,14 +150,17 @@ export class ClosedCaptions extends UICorePlugin {
|
|
|
147
150
|
this.applyTracks();
|
|
148
151
|
this.mount();
|
|
149
152
|
}
|
|
150
|
-
onSubtitleChanged({ id }) {
|
|
151
|
-
|
|
153
|
+
onSubtitleChanged({ id: _ }) {
|
|
154
|
+
// ignoring the subtitle selected by the playback engine or user agent
|
|
155
|
+
const id = this.track?.id ?? -1;
|
|
152
156
|
if (id === -1) {
|
|
153
157
|
this.clearSubtitleText();
|
|
154
158
|
}
|
|
155
159
|
for (const track of this.tracks) {
|
|
160
|
+
// Native subtitles are always hidden
|
|
161
|
+
track.track.mode = 'hidden';
|
|
156
162
|
if (track.id === id) {
|
|
157
|
-
track.track.mode = 'showing'
|
|
163
|
+
// track.track.mode = 'showing'
|
|
158
164
|
this.setSubtitleText(this.getSubtitleText(track.track));
|
|
159
165
|
track.track.oncuechange = (e) => {
|
|
160
166
|
try {
|
|
@@ -173,7 +179,7 @@ export class ClosedCaptions extends UICorePlugin {
|
|
|
173
179
|
}
|
|
174
180
|
else {
|
|
175
181
|
track.track.oncuechange = null;
|
|
176
|
-
track.track.mode = 'hidden'
|
|
182
|
+
// track.track.mode = 'hidden'
|
|
177
183
|
}
|
|
178
184
|
}
|
|
179
185
|
}
|
|
@@ -302,18 +308,18 @@ export class ClosedCaptions extends UICorePlugin {
|
|
|
302
308
|
applyPreselectedSubtitles() {
|
|
303
309
|
if (!this.isPreselectedApplied) {
|
|
304
310
|
this.isPreselectedApplied = true;
|
|
305
|
-
if
|
|
306
|
-
|
|
307
|
-
}
|
|
311
|
+
// if the language is undefined, then let the engine decide
|
|
312
|
+
// to hide the subtitles forcefully, set the language to 'none'
|
|
308
313
|
setTimeout(() => {
|
|
309
314
|
this.selectItem(this.tracks.find((t) => t.track.language === this.preselectedLanguage) ?? null);
|
|
310
|
-
},
|
|
315
|
+
}, 0);
|
|
311
316
|
}
|
|
312
317
|
}
|
|
313
318
|
hideMenu() {
|
|
314
319
|
this.open = false;
|
|
315
320
|
this.$el.find('#gplayer-cc-menu').hide();
|
|
316
321
|
this.$el.find('#gplayer-cc-button').attr('aria-expanded', 'false');
|
|
322
|
+
this.setKeepVisible(false);
|
|
317
323
|
}
|
|
318
324
|
toggleMenu() {
|
|
319
325
|
this.core
|
|
@@ -327,6 +333,13 @@ export class ClosedCaptions extends UICorePlugin {
|
|
|
327
333
|
this.$el.find('#gplayer-cc-menu').hide();
|
|
328
334
|
}
|
|
329
335
|
this.$el.find('#gplayer-cc-button').attr('aria-expanded', this.open);
|
|
336
|
+
this.setKeepVisible(this.open);
|
|
337
|
+
}
|
|
338
|
+
setKeepVisible(keepVisible) {
|
|
339
|
+
if (this.shouldKeepVisible) {
|
|
340
|
+
this.core.getPlugin('media_control').setKeepVisible(keepVisible);
|
|
341
|
+
this.clickaway(keepVisible ? this.core.activeContainer.$el[0] : null);
|
|
342
|
+
}
|
|
330
343
|
}
|
|
331
344
|
itemElement(id) {
|
|
332
345
|
// TODO fix semantically
|
|
@@ -337,7 +350,9 @@ export class ClosedCaptions extends UICorePlugin {
|
|
|
337
350
|
}
|
|
338
351
|
selectSubtitles() {
|
|
339
352
|
const trackId = this.track ? this.track.id : -1;
|
|
340
|
-
|
|
353
|
+
// TODO find out if this is needed
|
|
354
|
+
// this.core.activePlayback.closedCaptionsTrackId = trackId
|
|
355
|
+
this.core.activePlayback.closedCaptionsTrackId = -1;
|
|
341
356
|
}
|
|
342
357
|
getSubtitleText(track) {
|
|
343
358
|
const currentTime = this.core.activePlayback?.getCurrentTime() ?? 0;
|
|
@@ -399,4 +414,8 @@ export class ClosedCaptions extends UICorePlugin {
|
|
|
399
414
|
mediaControl.slot('cc', this.$el);
|
|
400
415
|
}
|
|
401
416
|
}
|
|
417
|
+
get shouldKeepVisible() {
|
|
418
|
+
return !!this.options.cc?.keepVisible;
|
|
419
|
+
}
|
|
420
|
+
clickaway = mediaControlClickaway(() => this.hideMenu());
|
|
402
421
|
}
|
package/lib/testUtils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testUtils.d.ts","sourceRoot":"","sources":["../src/testUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAK,YAAY,EAAE,MAAM,cAAc,CAAA;AAC9C,OAAO,MAAM,MAAM,eAAe,CAAA;AAGlC,wBAAgB,cAAc,CAC5B,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACrC,SAAS,GAAE,GAAkC;;;;;;;;;;;;;;;;;EAsB9C;AAED,wBAAgB,gBAAgB;;;EAK/B;AAED,wBAAgB,mBAAmB;;;;;;EAKlC;AAED,wBAAgB,kBAAkB,CAChC,IAAI,SAAS,EACb,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqCtC;AAED,wBAAgB,mBAAmB,CACjC,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACrC,QAAQ,GAAE,GAAgD;;;;;;;;;;;;;;;;;;;;;;;;;EA8B3D;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,GAAG,
|
|
1
|
+
{"version":3,"file":"testUtils.d.ts","sourceRoot":"","sources":["../src/testUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAK,YAAY,EAAE,MAAM,cAAc,CAAA;AAC9C,OAAO,MAAM,MAAM,eAAe,CAAA;AAGlC,wBAAgB,cAAc,CAC5B,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACrC,SAAS,GAAE,GAAkC;;;;;;;;;;;;;;;;;EAsB9C;AAED,wBAAgB,gBAAgB;;;EAK/B;AAED,wBAAgB,mBAAmB;;;;;;EAKlC;AAED,wBAAgB,kBAAkB,CAChC,IAAI,SAAS,EACb,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqCtC;AAED,wBAAgB,mBAAmB,CACjC,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACrC,QAAQ,GAAE,GAAgD;;;;;;;;;;;;;;;;;;;;;;;;;EA8B3D;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,GAAG,gBAyB/C;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,GAAG,OAe7C"}
|
package/lib/testUtils.js
CHANGED
|
@@ -120,6 +120,8 @@ export function createMockMediaControl(core) {
|
|
|
120
120
|
mediaControl.getAvailablePopupHeight = vi.fn().mockReturnValue(286);
|
|
121
121
|
// @ts-ignore
|
|
122
122
|
mediaControl.toggleElement = vi.fn();
|
|
123
|
+
// @ts-ignore
|
|
124
|
+
mediaControl.setKeepVisible = vi.fn();
|
|
123
125
|
vi.spyOn(mediaControl, 'trigger');
|
|
124
126
|
core.$el.append(mediaControl.$el);
|
|
125
127
|
return mediaControl;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* @param {() => void} callback - The callback to call when the user clicks away from the element
|
|
4
|
+
* @returns {(HTMLElement | null) => void}
|
|
5
|
+
*/
|
|
6
|
+
export declare function clickaway(callback: () => void): (node: HTMLElement | null) => void;
|
|
7
|
+
/**
|
|
8
|
+
* Sets up a clickaway handler for the media control on mobile devices.
|
|
9
|
+
* The handler is deferred to ensure it is called after the next event loop tick.
|
|
10
|
+
*
|
|
11
|
+
* @param {() => void} callback - The callback to call when the user clicks away from the media control
|
|
12
|
+
* @returns {(HTMLElement | null) => void}
|
|
13
|
+
*/
|
|
14
|
+
export declare function mediaControlClickaway(callback: () => void): (node: HTMLElement | null) => void;
|
|
15
|
+
//# sourceMappingURL=clickaway.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clickaway.d.ts","sourceRoot":"","sources":["../../src/utils/clickaway.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,IAGlC,MAAM,WAAW,GAAG,IAAI,UAanC;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,IAAI,UAKxC,WAAW,GAAG,IAAI,UAKnC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Browser } from '@clappr/core';
|
|
2
|
+
/**
|
|
3
|
+
*
|
|
4
|
+
* @param {() => void} callback - The callback to call when the user clicks away from the element
|
|
5
|
+
* @returns {(HTMLElement | null) => void}
|
|
6
|
+
*/
|
|
7
|
+
export function clickaway(callback) {
|
|
8
|
+
let handler = (event) => { };
|
|
9
|
+
return (node) => {
|
|
10
|
+
window.removeEventListener('click', handler);
|
|
11
|
+
if (!node) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
handler = (event) => {
|
|
15
|
+
if (!node.contains(event.target)) {
|
|
16
|
+
window.removeEventListener('click', handler);
|
|
17
|
+
callback();
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
window.addEventListener('click', handler);
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Sets up a clickaway handler for the media control on mobile devices.
|
|
25
|
+
* The handler is deferred to ensure it is called after the next event loop tick.
|
|
26
|
+
*
|
|
27
|
+
* @param {() => void} callback - The callback to call when the user clicks away from the media control
|
|
28
|
+
* @returns {(HTMLElement | null) => void}
|
|
29
|
+
*/
|
|
30
|
+
export function mediaControlClickaway(callback) {
|
|
31
|
+
if (!Browser.isMobile) {
|
|
32
|
+
return () => { };
|
|
33
|
+
}
|
|
34
|
+
const cw = clickaway(callback);
|
|
35
|
+
return (node) => {
|
|
36
|
+
setTimeout(() => {
|
|
37
|
+
cw(node);
|
|
38
|
+
}, 0);
|
|
39
|
+
};
|
|
40
|
+
}
|
package/package.json
CHANGED
|
@@ -9,6 +9,7 @@ import pluginHtml from '../../../assets/audio-tracks/template.ejs'
|
|
|
9
9
|
import audioArrow from '../../../assets/icons/old/quality-arrow.svg'
|
|
10
10
|
import { ZeptoResult } from '../../types.js'
|
|
11
11
|
import { ExtendedEvents, MediaControl } from '../media-control/MediaControl.js'
|
|
12
|
+
import { mediaControlClickaway } from '../../utils/clickaway.js'
|
|
12
13
|
|
|
13
14
|
const VERSION: string = '2.22.4'
|
|
14
15
|
|
|
@@ -132,6 +133,9 @@ export class AudioTracks extends UICorePlugin {
|
|
|
132
133
|
this.listenTo(this.core.activeContainer, Events.CONTAINER_CLICK, () => {
|
|
133
134
|
this.hideMenu()
|
|
134
135
|
})
|
|
136
|
+
this.listenTo(this.core.activeContainer, Events.CONTAINER_DESTROYED, () => {
|
|
137
|
+
this.clickaway(null)
|
|
138
|
+
})
|
|
135
139
|
}
|
|
136
140
|
|
|
137
141
|
private shouldRender() {
|
|
@@ -179,6 +183,7 @@ export class AudioTracks extends UICorePlugin {
|
|
|
179
183
|
this.open = false
|
|
180
184
|
this.$el.find('#gplayer-audiotracks-menu').hide()
|
|
181
185
|
this.$el.find('#gplayer-audiotracks-button').attr('aria-expanded', 'false')
|
|
186
|
+
this.setKeepVisible(false)
|
|
182
187
|
}
|
|
183
188
|
|
|
184
189
|
private toggleMenu() {
|
|
@@ -195,6 +200,13 @@ export class AudioTracks extends UICorePlugin {
|
|
|
195
200
|
this.$el
|
|
196
201
|
.find('#gplayer-audiotracks-button')
|
|
197
202
|
.attr('aria-expanded', this.open)
|
|
203
|
+
|
|
204
|
+
this.setKeepVisible(this.open)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
private setKeepVisible(keepVisible: boolean) {
|
|
208
|
+
this.core.getPlugin('media_control').setKeepVisible(keepVisible)
|
|
209
|
+
this.clickaway(keepVisible ? this.core.activeContainer.$el[0] : null)
|
|
198
210
|
}
|
|
199
211
|
|
|
200
212
|
private buttonElement(): ZeptoResult {
|
|
@@ -209,7 +221,7 @@ export class AudioTracks extends UICorePlugin {
|
|
|
209
221
|
return (
|
|
210
222
|
this.$(
|
|
211
223
|
'#gplayer-audiotracks-menu a' +
|
|
212
|
-
|
|
224
|
+
(id !== undefined ? `[data-item="${id}"]` : ''),
|
|
213
225
|
) as ZeptoResult
|
|
214
226
|
).parent()
|
|
215
227
|
}
|
|
@@ -253,4 +265,6 @@ export class AudioTracks extends UICorePlugin {
|
|
|
253
265
|
this.core.getPlugin('media_control')?.slot('audiotracks', this.$el)
|
|
254
266
|
}
|
|
255
267
|
}
|
|
268
|
+
|
|
269
|
+
private clickaway = mediaControlClickaway(() => this.hideMenu())
|
|
256
270
|
}
|
|
@@ -17,6 +17,7 @@ import gearIcon from '../../../assets/icons/new/gear.svg'
|
|
|
17
17
|
import gearHdIcon from '../../../assets/icons/new/gear-hd.svg'
|
|
18
18
|
import { ZeptoResult } from '../../types.js'
|
|
19
19
|
import { ExtendedEvents } from '../media-control/MediaControl.js'
|
|
20
|
+
import { mediaControlClickaway } from '../../utils/clickaway.js'
|
|
20
21
|
|
|
21
22
|
const VERSION = '2.19.12'
|
|
22
23
|
|
|
@@ -214,6 +215,9 @@ export class BottomGear extends UICorePlugin {
|
|
|
214
215
|
this.listenTo(container, ClapprEvents.CONTAINER_CLICK, () => {
|
|
215
216
|
this.collapse()
|
|
216
217
|
})
|
|
218
|
+
this.listenTo(container, ClapprEvents.CONTAINER_DESTROYED, () => {
|
|
219
|
+
this.clickaway(null)
|
|
220
|
+
})
|
|
217
221
|
}
|
|
218
222
|
|
|
219
223
|
private highDefinitionUpdate(isHd: boolean) {
|
|
@@ -272,6 +276,12 @@ export class BottomGear extends UICorePlugin {
|
|
|
272
276
|
this.$el
|
|
273
277
|
.find('#gear-button')
|
|
274
278
|
.attr('aria-expanded', (!this.collapsed).toString())
|
|
279
|
+
this.setKeepVisible(!this.collapsed)
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
private setKeepVisible(keepVisible: boolean) {
|
|
283
|
+
this.core.getPlugin('media_control').setKeepVisible(keepVisible)
|
|
284
|
+
this.clickaway(keepVisible ? this.core.activeContainer.$el[0] : null)
|
|
275
285
|
}
|
|
276
286
|
|
|
277
287
|
private collapse() {
|
|
@@ -280,6 +290,7 @@ export class BottomGear extends UICorePlugin {
|
|
|
280
290
|
this.$el.find('#gear-button').attr('aria-expanded', 'false')
|
|
281
291
|
// TODO hide submenus
|
|
282
292
|
this.collapseSubmenus()
|
|
293
|
+
this.setKeepVisible(false)
|
|
283
294
|
}
|
|
284
295
|
|
|
285
296
|
private onCoreReady() {
|
|
@@ -328,4 +339,6 @@ export class BottomGear extends UICorePlugin {
|
|
|
328
339
|
.find('.gear-sub-menu')
|
|
329
340
|
.css('max-height', `${availableHeight - MENU_BACKLINK_HEIGHT}px`)
|
|
330
341
|
}
|
|
342
|
+
|
|
343
|
+
private clickaway = mediaControlClickaway(() => this.collapse())
|
|
331
344
|
}
|
|
@@ -217,6 +217,7 @@ export class QualityLevels extends UICorePlugin {
|
|
|
217
217
|
*/
|
|
218
218
|
override render() {
|
|
219
219
|
if (!this.shouldRender()) {
|
|
220
|
+
this.$el.hide()
|
|
220
221
|
return this
|
|
221
222
|
}
|
|
222
223
|
this.renderDropdown()
|
|
@@ -241,6 +242,9 @@ export class QualityLevels extends UICorePlugin {
|
|
|
241
242
|
}
|
|
242
243
|
|
|
243
244
|
private updateButton() {
|
|
245
|
+
if (!this.shouldRender()) {
|
|
246
|
+
return
|
|
247
|
+
}
|
|
244
248
|
;(this.core.getPlugin('bottom_gear') as BottomGear)
|
|
245
249
|
?.addItem('quality', this.$el)
|
|
246
250
|
.html(
|
|
@@ -37,6 +37,7 @@ import volumeMaxIcon from '../../../assets/icons/new/volume-max.svg'
|
|
|
37
37
|
import volumeOffIcon from '../../../assets/icons/new/volume-off.svg'
|
|
38
38
|
import fullscreenOffIcon from '../../../assets/icons/new/fullscreen-off.svg'
|
|
39
39
|
import fullscreenOnIcon from '../../../assets/icons/new/fullscreen-on.svg'
|
|
40
|
+
import { mediaControlClickaway } from '../../utils/clickaway.js'
|
|
40
41
|
|
|
41
42
|
const STANDARD_MEDIA_CONTROL_ELEMENTS: string[] = [
|
|
42
43
|
'duration',
|
|
@@ -399,6 +400,7 @@ export class MediaControl extends UICorePlugin {
|
|
|
399
400
|
|
|
400
401
|
/**
|
|
401
402
|
* @internal
|
|
403
|
+
* The methods declared here will be exposed via the main player object API
|
|
402
404
|
*/
|
|
403
405
|
override getExternalInterface() {
|
|
404
406
|
return {
|
|
@@ -471,9 +473,7 @@ export class MediaControl extends UICorePlugin {
|
|
|
471
473
|
this.listenTo(
|
|
472
474
|
this.core.activeContainer,
|
|
473
475
|
Events.CONTAINER_CLICK,
|
|
474
|
-
() =>
|
|
475
|
-
this.clickaway(this.core.activeContainer.$el[0])
|
|
476
|
-
},
|
|
476
|
+
() => this.clickaway(this.core.activeContainer.$el[0]),
|
|
477
477
|
)
|
|
478
478
|
this.listenTo(
|
|
479
479
|
this.core.activeContainer,
|
|
@@ -1269,6 +1269,23 @@ export class MediaControl extends UICorePlugin {
|
|
|
1269
1269
|
mountTo(this.getMountParent(name), element)
|
|
1270
1270
|
}
|
|
1271
1271
|
|
|
1272
|
+
/**
|
|
1273
|
+
* Set or reset the keep visibility state
|
|
1274
|
+
*
|
|
1275
|
+
* Keep visibility state controls whether the media control is hidden automatically after a delay.
|
|
1276
|
+
* Keep visibility prevents the the auto-hide behaviour
|
|
1277
|
+
*
|
|
1278
|
+
* @param keepVisible - The state
|
|
1279
|
+
*/
|
|
1280
|
+
setKeepVisible(keepVisible: boolean) {
|
|
1281
|
+
this.keepVisible = keepVisible
|
|
1282
|
+
if (keepVisible) {
|
|
1283
|
+
this.clickaway(this.core.activeContainer.$el[0])
|
|
1284
|
+
} else {
|
|
1285
|
+
this.clickaway(null)
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1272
1289
|
private getMountParent(name: MediaControlSlotMountPoint): ZeptoResult {
|
|
1273
1290
|
switch (name) {
|
|
1274
1291
|
case 'root':
|
|
@@ -1630,9 +1647,7 @@ export class MediaControl extends UICorePlugin {
|
|
|
1630
1647
|
this.hide(this.options.hideMediaControlDelay || DEFAULT_HIDE_DELAY)
|
|
1631
1648
|
}
|
|
1632
1649
|
|
|
1633
|
-
|
|
1634
|
-
// as opposed to the click event
|
|
1635
|
-
private clickaway = clickaway(() => setTimeout(this.resetUserKeepVisible, 0))
|
|
1650
|
+
private clickaway = mediaControlClickaway(() => this.resetUserKeepVisible())
|
|
1636
1651
|
}
|
|
1637
1652
|
|
|
1638
1653
|
MediaControl.extend = function (properties) {
|
|
@@ -1686,21 +1701,3 @@ function mergeElements(
|
|
|
1686
1701
|
return acc
|
|
1687
1702
|
}, a)
|
|
1688
1703
|
}
|
|
1689
|
-
|
|
1690
|
-
function clickaway(callback: () => void) {
|
|
1691
|
-
let handler = (event: MouseEvent | TouchEvent) => {}
|
|
1692
|
-
|
|
1693
|
-
return (node: HTMLElement | null) => {
|
|
1694
|
-
window.removeEventListener('click', handler)
|
|
1695
|
-
if (!node) {
|
|
1696
|
-
return
|
|
1697
|
-
}
|
|
1698
|
-
handler = (event: MouseEvent | TouchEvent) => {
|
|
1699
|
-
if (!node.contains(event.target as Node)) {
|
|
1700
|
-
window.removeEventListener('click', handler)
|
|
1701
|
-
callback()
|
|
1702
|
-
}
|
|
1703
|
-
}
|
|
1704
|
-
window.addEventListener('click', handler)
|
|
1705
|
-
}
|
|
1706
|
-
}
|
|
@@ -14,6 +14,7 @@ import stringHTML from '../../../assets/subtitles/string.ejs'
|
|
|
14
14
|
import { isFullscreen } from '../utils/fullscreen.js'
|
|
15
15
|
import type { ZeptoResult } from '../../types.js'
|
|
16
16
|
import { ExtendedEvents } from '../media-control/MediaControl.js'
|
|
17
|
+
import { mediaControlClickaway } from '../../utils/clickaway.js'
|
|
17
18
|
|
|
18
19
|
const VERSION: string = '2.19.14'
|
|
19
20
|
|
|
@@ -124,11 +125,10 @@ export class ClosedCaptions extends UICorePlugin {
|
|
|
124
125
|
}
|
|
125
126
|
}
|
|
126
127
|
|
|
127
|
-
private get preselectedLanguage(): string {
|
|
128
|
+
private get preselectedLanguage(): string | undefined {
|
|
128
129
|
return (
|
|
129
130
|
this.core.options.cc?.language ??
|
|
130
|
-
this.core.options.subtitles?.language
|
|
131
|
-
''
|
|
131
|
+
this.core.options.subtitles?.language
|
|
132
132
|
)
|
|
133
133
|
}
|
|
134
134
|
|
|
@@ -173,6 +173,9 @@ export class ClosedCaptions extends UICorePlugin {
|
|
|
173
173
|
Events.CONTAINER_RESIZE,
|
|
174
174
|
this.onContainerResize,
|
|
175
175
|
)
|
|
176
|
+
this.listenTo(this.core.activeContainer, Events.CONTAINER_DESTROYED, () => {
|
|
177
|
+
this.clickaway(null)
|
|
178
|
+
})
|
|
176
179
|
this.listenTo(
|
|
177
180
|
this.core.activeContainer,
|
|
178
181
|
'container:advertisement:start',
|
|
@@ -218,14 +221,17 @@ export class ClosedCaptions extends UICorePlugin {
|
|
|
218
221
|
this.mount()
|
|
219
222
|
}
|
|
220
223
|
|
|
221
|
-
private onSubtitleChanged({ id }: { id: number }) {
|
|
222
|
-
|
|
224
|
+
private onSubtitleChanged({ id: _ }: { id: number }) {
|
|
225
|
+
// ignoring the subtitle selected by the playback engine or user agent
|
|
226
|
+
const id = this.track?.id ?? -1
|
|
223
227
|
if (id === -1) {
|
|
224
228
|
this.clearSubtitleText()
|
|
225
229
|
}
|
|
226
230
|
for (const track of this.tracks) {
|
|
231
|
+
// Native subtitles are always hidden
|
|
232
|
+
track.track.mode = 'hidden'
|
|
227
233
|
if (track.id === id) {
|
|
228
|
-
track.track.mode = 'showing'
|
|
234
|
+
// track.track.mode = 'showing'
|
|
229
235
|
|
|
230
236
|
this.setSubtitleText(this.getSubtitleText(track.track))
|
|
231
237
|
|
|
@@ -244,7 +250,7 @@ export class ClosedCaptions extends UICorePlugin {
|
|
|
244
250
|
}
|
|
245
251
|
} else {
|
|
246
252
|
track.track.oncuechange = null
|
|
247
|
-
track.track.mode = 'hidden'
|
|
253
|
+
// track.track.mode = 'hidden'
|
|
248
254
|
}
|
|
249
255
|
}
|
|
250
256
|
}
|
|
@@ -408,16 +414,15 @@ export class ClosedCaptions extends UICorePlugin {
|
|
|
408
414
|
private applyPreselectedSubtitles() {
|
|
409
415
|
if (!this.isPreselectedApplied) {
|
|
410
416
|
this.isPreselectedApplied = true
|
|
411
|
-
if
|
|
412
|
-
|
|
413
|
-
}
|
|
417
|
+
// if the language is undefined, then let the engine decide
|
|
418
|
+
// to hide the subtitles forcefully, set the language to 'none'
|
|
414
419
|
setTimeout(() => {
|
|
415
420
|
this.selectItem(
|
|
416
421
|
this.tracks.find(
|
|
417
422
|
(t) => t.track.language === this.preselectedLanguage,
|
|
418
423
|
) ?? null,
|
|
419
424
|
)
|
|
420
|
-
},
|
|
425
|
+
}, 0)
|
|
421
426
|
}
|
|
422
427
|
}
|
|
423
428
|
|
|
@@ -425,6 +430,7 @@ export class ClosedCaptions extends UICorePlugin {
|
|
|
425
430
|
this.open = false
|
|
426
431
|
this.$el.find('#gplayer-cc-menu').hide()
|
|
427
432
|
this.$el.find('#gplayer-cc-button').attr('aria-expanded', 'false')
|
|
433
|
+
this.setKeepVisible(false)
|
|
428
434
|
}
|
|
429
435
|
|
|
430
436
|
private toggleMenu() {
|
|
@@ -438,6 +444,14 @@ export class ClosedCaptions extends UICorePlugin {
|
|
|
438
444
|
this.$el.find('#gplayer-cc-menu').hide()
|
|
439
445
|
}
|
|
440
446
|
this.$el.find('#gplayer-cc-button').attr('aria-expanded', this.open)
|
|
447
|
+
this.setKeepVisible(this.open)
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
private setKeepVisible(keepVisible: boolean) {
|
|
451
|
+
if (this.shouldKeepVisible) {
|
|
452
|
+
this.core.getPlugin('media_control').setKeepVisible(keepVisible)
|
|
453
|
+
this.clickaway(keepVisible ? this.core.activeContainer.$el[0] : null)
|
|
454
|
+
}
|
|
441
455
|
}
|
|
442
456
|
|
|
443
457
|
private itemElement(id: number): ZeptoResult {
|
|
@@ -452,7 +466,9 @@ export class ClosedCaptions extends UICorePlugin {
|
|
|
452
466
|
private selectSubtitles() {
|
|
453
467
|
const trackId = this.track ? this.track.id : -1
|
|
454
468
|
|
|
455
|
-
|
|
469
|
+
// TODO find out if this is needed
|
|
470
|
+
// this.core.activePlayback.closedCaptionsTrackId = trackId
|
|
471
|
+
this.core.activePlayback.closedCaptionsTrackId = -1
|
|
456
472
|
}
|
|
457
473
|
|
|
458
474
|
private getSubtitleText(track: TextTrack) {
|
|
@@ -526,4 +542,10 @@ export class ClosedCaptions extends UICorePlugin {
|
|
|
526
542
|
mediaControl.slot('cc', this.$el)
|
|
527
543
|
}
|
|
528
544
|
}
|
|
545
|
+
|
|
546
|
+
private get shouldKeepVisible() {
|
|
547
|
+
return !!this.options.cc?.keepVisible
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
private clickaway = mediaControlClickaway(() => this.hideMenu())
|
|
529
551
|
}
|
|
@@ -132,6 +132,7 @@ describe('ClosedCaptions', () => {
|
|
|
132
132
|
describe('when subtitle is changed', () => {
|
|
133
133
|
beforeEach(async () => {
|
|
134
134
|
emitSubtitleAvailable(core)
|
|
135
|
+
cc.$el.find('#gplayer-cc-button').click()
|
|
135
136
|
await new Promise((resolve) => setTimeout(resolve, 100))
|
|
136
137
|
core.activePlayback.getCurrentTime = vi.fn().mockReturnValue(7)
|
|
137
138
|
core.activeContainer.getCurrentTime = vi.fn().mockReturnValue(7)
|
|
@@ -167,11 +168,16 @@ describe('ClosedCaptions', () => {
|
|
|
167
168
|
),
|
|
168
169
|
},
|
|
169
170
|
]
|
|
171
|
+
cc.$el.find('#gplayer-cc-menu li:nth-child(2) a').click()
|
|
172
|
+
await new Promise((resolve) => setTimeout(resolve, 100))
|
|
173
|
+
// TODO test explicitly that PLAYBACK_SUBTITLE_CHANGED event does not cause track switch
|
|
170
174
|
core.activePlayback.emit(Events.PLAYBACK_SUBTITLE_CHANGED, { id: 2 })
|
|
175
|
+
await new Promise((resolve) => setTimeout(resolve, 100))
|
|
176
|
+
|
|
171
177
|
})
|
|
172
178
|
it('should activate subtitle track', () => {
|
|
173
179
|
expect(core.activePlayback.closedCaptionsTracks[1].track.mode).toBe(
|
|
174
|
-
'
|
|
180
|
+
'hidden',
|
|
175
181
|
)
|
|
176
182
|
expect(core.activePlayback.closedCaptionsTracks[0].track.mode).toBe(
|
|
177
183
|
'hidden',
|
|
@@ -188,8 +194,8 @@ describe('ClosedCaptions', () => {
|
|
|
188
194
|
emitSubtitleAvailable(core)
|
|
189
195
|
cc.$el.find('#gplayer-cc-menu li:nth-child(2) a').click()
|
|
190
196
|
})
|
|
191
|
-
it('should
|
|
192
|
-
expect(core.activePlayback.closedCaptionsTrackId).toEqual(
|
|
197
|
+
it('should deactivate native subtitles track', () => {
|
|
198
|
+
expect(core.activePlayback.closedCaptionsTrackId).toEqual(-1)
|
|
193
199
|
})
|
|
194
200
|
it('should highlight selected menu item', () => {
|
|
195
201
|
expect(
|