@gcorevideo/player 2.28.20 → 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.
@@ -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 {
@@ -936,6 +938,23 @@ export class MediaControl extends UICorePlugin {
936
938
  mount(name, element) {
937
939
  mountTo(this.getMountParent(name), element);
938
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
+ }
939
958
  getMountParent(name) {
940
959
  switch (name) {
941
960
  case 'root':
@@ -1235,13 +1254,7 @@ export class MediaControl extends UICorePlugin {
1235
1254
  delayHide() {
1236
1255
  this.hide(this.options.hideMediaControlDelay || DEFAULT_HIDE_DELAY);
1237
1256
  }
1238
- // 2 seconds delay is needed since on mobile devices mouse(touch)move events are not dispatched immediately
1239
- // as opposed to the click event
1240
- clickaway = clickaway(() => {
1241
- if (Browser.isMobile) {
1242
- setTimeout(this.resetUserKeepVisible, 0);
1243
- }
1244
- });
1257
+ clickaway = mediaControlClickaway(() => this.resetUserKeepVisible());
1245
1258
  }
1246
1259
  MediaControl.extend = function (properties) {
1247
1260
  return extend(MediaControl, properties);
@@ -1282,19 +1295,3 @@ function mergeElements(a, b) {
1282
1295
  return acc;
1283
1296
  }, a);
1284
1297
  }
1285
- function clickaway(callback) {
1286
- let handler = (event) => { };
1287
- return (node) => {
1288
- window.removeEventListener('click', handler);
1289
- if (!node) {
1290
- return;
1291
- }
1292
- handler = (event) => {
1293
- if (!node.contains(event.target)) {
1294
- window.removeEventListener('click', handler);
1295
- callback();
1296
- }
1297
- };
1298
- window.addEventListener('click', handler);
1299
- };
1300
- }
@@ -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;AAgB7C;;;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;IAgD1B,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;IAMhB,OAAO,CAAC,UAAU;IAalB,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;CAMd"}
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';
@@ -118,6 +119,9 @@ export class ClosedCaptions extends UICorePlugin {
118
119
  onContainerChanged() {
119
120
  this.listenTo(this.core.activeContainer, Events.CONTAINER_FULLSCREEN, this.onContainerResize);
120
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
+ });
121
125
  this.listenTo(this.core.activeContainer, 'container:advertisement:start', this.onStartAd);
122
126
  this.listenTo(this.core.activePlayback, Events.PLAYBACK_SUBTITLE_AVAILABLE, this.onSubtitleAvailable);
123
127
  this.listenTo(this.core.activePlayback, Events.PLAYBACK_SUBTITLE_CHANGED, this.onSubtitleChanged);
@@ -315,6 +319,7 @@ export class ClosedCaptions extends UICorePlugin {
315
319
  this.open = false;
316
320
  this.$el.find('#gplayer-cc-menu').hide();
317
321
  this.$el.find('#gplayer-cc-button').attr('aria-expanded', 'false');
322
+ this.setKeepVisible(false);
318
323
  }
319
324
  toggleMenu() {
320
325
  this.core
@@ -328,6 +333,13 @@ export class ClosedCaptions extends UICorePlugin {
328
333
  this.$el.find('#gplayer-cc-menu').hide();
329
334
  }
330
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
+ }
331
343
  }
332
344
  itemElement(id) {
333
345
  // TODO fix semantically
@@ -402,4 +414,8 @@ export class ClosedCaptions extends UICorePlugin {
402
414
  mediaControl.slot('cc', this.$el);
403
415
  }
404
416
  }
417
+ get shouldKeepVisible() {
418
+ return !!this.options.cc?.keepVisible;
419
+ }
420
+ clickaway = mediaControlClickaway(() => this.hideMenu());
405
421
  }
@@ -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,gBAuB/C;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,GAAG,OAe7C"}
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gcorevideo/player",
3
- "version": "2.28.20",
3
+ "version": "2.28.21",
4
4
  "description": "Gcore JavaScript video player",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -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
- (id !== undefined ? `[data-item="${id}"]` : ''),
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
  }
@@ -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 {
@@ -1267,6 +1269,23 @@ export class MediaControl extends UICorePlugin {
1267
1269
  mountTo(this.getMountParent(name), element)
1268
1270
  }
1269
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
+
1270
1289
  private getMountParent(name: MediaControlSlotMountPoint): ZeptoResult {
1271
1290
  switch (name) {
1272
1291
  case 'root':
@@ -1628,13 +1647,7 @@ export class MediaControl extends UICorePlugin {
1628
1647
  this.hide(this.options.hideMediaControlDelay || DEFAULT_HIDE_DELAY)
1629
1648
  }
1630
1649
 
1631
- // 2 seconds delay is needed since on mobile devices mouse(touch)move events are not dispatched immediately
1632
- // as opposed to the click event
1633
- private clickaway = clickaway(() => {
1634
- if (Browser.isMobile) {
1635
- setTimeout(this.resetUserKeepVisible, 0)
1636
- }
1637
- })
1650
+ private clickaway = mediaControlClickaway(() => this.resetUserKeepVisible())
1638
1651
  }
1639
1652
 
1640
1653
  MediaControl.extend = function (properties) {
@@ -1688,21 +1701,3 @@ function mergeElements(
1688
1701
  return acc
1689
1702
  }, a)
1690
1703
  }
1691
-
1692
- function clickaway(callback: () => void) {
1693
- let handler = (event: MouseEvent | TouchEvent) => {}
1694
-
1695
- return (node: HTMLElement | null) => {
1696
- window.removeEventListener('click', handler)
1697
- if (!node) {
1698
- return
1699
- }
1700
- handler = (event: MouseEvent | TouchEvent) => {
1701
- if (!node.contains(event.target as Node)) {
1702
- window.removeEventListener('click', handler)
1703
- callback()
1704
- }
1705
- }
1706
- window.addEventListener('click', handler)
1707
- }
1708
- }
@@ -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
 
@@ -172,6 +173,9 @@ export class ClosedCaptions extends UICorePlugin {
172
173
  Events.CONTAINER_RESIZE,
173
174
  this.onContainerResize,
174
175
  )
176
+ this.listenTo(this.core.activeContainer, Events.CONTAINER_DESTROYED, () => {
177
+ this.clickaway(null)
178
+ })
175
179
  this.listenTo(
176
180
  this.core.activeContainer,
177
181
  'container:advertisement:start',
@@ -426,6 +430,7 @@ export class ClosedCaptions extends UICorePlugin {
426
430
  this.open = false
427
431
  this.$el.find('#gplayer-cc-menu').hide()
428
432
  this.$el.find('#gplayer-cc-button').attr('aria-expanded', 'false')
433
+ this.setKeepVisible(false)
429
434
  }
430
435
 
431
436
  private toggleMenu() {
@@ -439,6 +444,14 @@ export class ClosedCaptions extends UICorePlugin {
439
444
  this.$el.find('#gplayer-cc-menu').hide()
440
445
  }
441
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
+ }
442
455
  }
443
456
 
444
457
  private itemElement(id: number): ZeptoResult {
@@ -529,4 +542,10 @@ export class ClosedCaptions extends UICorePlugin {
529
542
  mediaControl.slot('cc', this.$el)
530
543
  }
531
544
  }
545
+
546
+ private get shouldKeepVisible() {
547
+ return !!this.options.cc?.keepVisible
548
+ }
549
+
550
+ private clickaway = mediaControlClickaway(() => this.hideMenu())
532
551
  }
package/src/testUtils.ts CHANGED
@@ -137,6 +137,8 @@ export function createMockMediaControl(core: any) {
137
137
  mediaControl.getAvailablePopupHeight = vi.fn().mockReturnValue(286)
138
138
  // @ts-ignore
139
139
  mediaControl.toggleElement = vi.fn()
140
+ // @ts-ignore
141
+ mediaControl.setKeepVisible = vi.fn()
140
142
  vi.spyOn(mediaControl, 'trigger')
141
143
  core.$el.append(mediaControl.$el)
142
144
  return mediaControl
@@ -0,0 +1,43 @@
1
+ import { Browser } from '@clappr/core'
2
+
3
+ /**
4
+ *
5
+ * @param {() => void} callback - The callback to call when the user clicks away from the element
6
+ * @returns {(HTMLElement | null) => void}
7
+ */
8
+ export function clickaway(callback: () => void) {
9
+ let handler = (event: MouseEvent | TouchEvent) => { }
10
+
11
+ return (node: HTMLElement | null) => {
12
+ window.removeEventListener('click', handler)
13
+ if (!node) {
14
+ return
15
+ }
16
+ handler = (event: MouseEvent | TouchEvent) => {
17
+ if (!node.contains(event.target as Node)) {
18
+ window.removeEventListener('click', handler)
19
+ callback()
20
+ }
21
+ }
22
+ window.addEventListener('click', handler)
23
+ }
24
+ }
25
+
26
+ /**
27
+ * Sets up a clickaway handler for the media control on mobile devices.
28
+ * The handler is deferred to ensure it is called after the next event loop tick.
29
+ *
30
+ * @param {() => void} callback - The callback to call when the user clicks away from the media control
31
+ * @returns {(HTMLElement | null) => void}
32
+ */
33
+ export function mediaControlClickaway(callback: () => void) {
34
+ if (!Browser.isMobile) {
35
+ return () => { }
36
+ }
37
+ const cw = clickaway(callback)
38
+ return (node: HTMLElement | null) => {
39
+ setTimeout(() => {
40
+ cw(node)
41
+ }, 0)
42
+ }
43
+ }