@gcorevideo/player 2.22.18 → 2.22.20

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.
Files changed (84) hide show
  1. package/assets/audio-selector/track-selector.ejs +3 -3
  2. package/assets/bottom-gear/bottomgear.ejs +3 -3
  3. package/assets/dvr-controls/dvr_controls.scss +0 -12
  4. package/dist/core.js +1 -1
  5. package/dist/index.css +803 -810
  6. package/dist/index.js +195 -176
  7. package/dist/player.d.ts +121 -108
  8. package/dist/plugins/index.css +904 -911
  9. package/dist/plugins/index.js +132 -112
  10. package/docs/api/player.bitratetrackrecord.md +20 -0
  11. package/docs/api/player.clapprstats.exportmetrics.md +2 -2
  12. package/docs/api/player.clapprstats.md +0 -4
  13. package/docs/api/player.clapprstatschronograph.md +115 -0
  14. package/docs/api/player.clapprstatscounter.md +211 -0
  15. package/docs/api/player.clapprstatsevents.md +51 -0
  16. package/docs/api/player.clapprstatsmetrics.md +52 -0
  17. package/docs/api/player.clipspluginsettings.md +1 -1
  18. package/docs/api/player.md +57 -2
  19. package/docs/api/player.nerdstats.md +3 -3
  20. package/docs/api/player.playerconfig.md +1 -1
  21. package/docs/api/player.playerconfig.playbacktype.md +6 -1
  22. package/docs/api/player.timeupdate.md +6 -3
  23. package/lib/plugins/audio-selector/AudioSelector.d.ts +1 -1
  24. package/lib/plugins/audio-selector/AudioSelector.d.ts.map +1 -1
  25. package/lib/plugins/audio-selector/AudioSelector.js +15 -8
  26. package/lib/plugins/bottom-gear/BottomGear.d.ts +1 -1
  27. package/lib/plugins/bottom-gear/BottomGear.js +2 -2
  28. package/lib/plugins/clappr-nerd-stats/NerdStats.d.ts +4 -4
  29. package/lib/plugins/clappr-nerd-stats/NerdStats.js +4 -4
  30. package/lib/plugins/clappr-stats/ClapprStats.d.ts +5 -2
  31. package/lib/plugins/clappr-stats/ClapprStats.d.ts.map +1 -1
  32. package/lib/plugins/clappr-stats/ClapprStats.js +31 -33
  33. package/lib/plugins/clappr-stats/types.d.ts +21 -21
  34. package/lib/plugins/clappr-stats/types.d.ts.map +1 -1
  35. package/lib/plugins/clappr-stats/types.js +22 -22
  36. package/lib/plugins/clappr-stats/utils.d.ts +2 -2
  37. package/lib/plugins/clappr-stats/utils.d.ts.map +1 -1
  38. package/lib/plugins/click-to-pause/ClickToPause.js +1 -1
  39. package/lib/plugins/clips/Clips.d.ts +1 -1
  40. package/lib/plugins/dvr-controls/DvrControls.d.ts +6 -2
  41. package/lib/plugins/dvr-controls/DvrControls.d.ts.map +1 -1
  42. package/lib/plugins/dvr-controls/DvrControls.js +39 -27
  43. package/lib/plugins/media-control/MediaControl.d.ts +6 -2
  44. package/lib/plugins/media-control/MediaControl.d.ts.map +1 -1
  45. package/lib/plugins/media-control/MediaControl.js +20 -9
  46. package/lib/plugins/picture-in-picture/PictureInPicture.js +1 -1
  47. package/lib/plugins/subtitles/ClosedCaptions.js +1 -1
  48. package/lib/plugins/vast-ads/VastAds.js +1 -1
  49. package/lib/plugins/vast-ads/rollmanager.js +1 -1
  50. package/lib/testUtils.d.ts.map +1 -1
  51. package/lib/testUtils.js +7 -4
  52. package/lib/types.d.ts +1 -1
  53. package/package.json +3 -3
  54. package/src/playback/__tests__/HTML5Video.test.ts +2 -2
  55. package/src/plugins/audio-selector/AudioSelector.ts +14 -7
  56. package/src/plugins/audio-selector/__tests__/AudioSelector.test.ts +8 -8
  57. package/src/plugins/audio-selector/__tests__/__snapshots__/AudioSelector.test.ts.snap +15 -15
  58. package/src/plugins/bottom-gear/BottomGear.ts +2 -2
  59. package/src/plugins/bottom-gear/__tests__/BottomGear.test.ts +8 -5
  60. package/src/plugins/bottom-gear/__tests__/__snapshots__/BottomGear.test.ts.snap +3 -3
  61. package/src/plugins/clappr-nerd-stats/NerdStats.ts +5 -5
  62. package/src/plugins/clappr-stats/ClapprStats.ts +41 -40
  63. package/src/plugins/clappr-stats/__tests__/ClapprStats.test.ts +12 -12
  64. package/src/plugins/clappr-stats/types.ts +21 -21
  65. package/src/plugins/clappr-stats/utils.ts +2 -2
  66. package/src/plugins/click-to-pause/ClickToPause.ts +1 -1
  67. package/src/plugins/clips/Clips.ts +1 -1
  68. package/src/plugins/clips/__tests__/Clips.test.ts +1 -1
  69. package/src/plugins/clips/__tests__/__snapshots__/Clips.test.ts.snap +1 -1
  70. package/src/plugins/dvr-controls/DvrControls.ts +51 -37
  71. package/src/plugins/dvr-controls/__tests__/DvrControls.test.ts +84 -26
  72. package/src/plugins/dvr-controls/__tests__/__snapshots__/DvrControls.test.ts.snap +0 -12
  73. package/src/plugins/media-control/MediaControl.ts +21 -9
  74. package/src/plugins/media-control/__tests__/MediaControl.test.ts +8 -5
  75. package/src/plugins/media-control/__tests__/__snapshots__/MediaControl.test.ts.snap +20 -20
  76. package/src/plugins/picture-in-picture/PictureInPicture.ts +1 -1
  77. package/src/plugins/subtitles/ClosedCaptions.ts +1 -1
  78. package/src/plugins/subtitles/__tests__/ClosedCaptions.test.ts +1 -1
  79. package/src/plugins/vast-ads/VastAds.ts +1 -1
  80. package/src/plugins/vast-ads/rollmanager.ts +1 -1
  81. package/src/testUtils.ts +11 -5
  82. package/src/types.ts +1 -1
  83. package/temp/player.api.json +630 -12
  84. package/tsconfig.tsbuildinfo +1 -1
@@ -20,14 +20,21 @@ 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
+ const MANAGED_ELEMENTS = [
24
+ 'dvr',
25
+ 'duration',
26
+ 'fullscreen',
27
+ 'hd-indicator',
28
+ 'position',
29
+ 'seekbar',
30
+ 'volume',
31
+ ];
23
32
  const DEFAULT_SETTINGS = {
24
33
  default: [],
25
34
  left: ['dvr'],
26
35
  right: [
27
36
  'audiotracks',
28
37
  'cc',
29
- // 'dvr',
30
- // 'duration',
31
38
  'fullscreen',
32
39
  'gear',
33
40
  'multicamera',
@@ -46,10 +53,10 @@ const T = 'plugins.media_control';
46
53
  const LEFT_ORDER = [
47
54
  'playpause',
48
55
  'playstop',
49
- 'dvr',
50
56
  'volume',
51
57
  'position',
52
58
  'duration',
59
+ 'dvr',
53
60
  ];
54
61
  const { Config, Fullscreen, formatTime, extend, removeArrayItem } = Utils;
55
62
  function orderByOrderPattern(arr, order) {
@@ -696,8 +703,7 @@ export class MediaControl extends UICorePlugin {
696
703
  }
697
704
  this.$el.show();
698
705
  this.trigger(Events.MEDIACONTROL_SHOW, this.name);
699
- this.container &&
700
- this.container.trigger(Events.CONTAINER_MEDIACONTROL_SHOW, this.name);
706
+ this.core.activeContainer?.trigger(Events.CONTAINER_MEDIACONTROL_SHOW, this.name);
701
707
  this.$el.removeClass('media-control-hide');
702
708
  this.hideId = setTimeout(() => this.hide(), timeout);
703
709
  if (event) {
@@ -751,7 +757,7 @@ export class MediaControl extends UICorePlugin {
751
757
  right: [],
752
758
  }, this.core.activeContainer.settings);
753
759
  trace(`${T} updateSettings`, { newSettings });
754
- newSettings.left.push('clips'); // TODO
760
+ newSettings.left.push('clips'); // TODO settings
755
761
  // TODO make order controlled via CSS
756
762
  newSettings.left = orderByOrderPattern([...newSettings.left, 'volume', 'clips'], LEFT_ORDER);
757
763
  if (this.core.activePlayback.getPlaybackType() === Playback.LIVE &&
@@ -814,7 +820,6 @@ export class MediaControl extends UICorePlugin {
814
820
  * Get a media control element DOM node
815
821
  * @param name - The name of the media control element
816
822
  * @returns The DOM node to render to or extend
817
- * @deprecated Use {@link MediaControl.putElement} instead
818
823
  * @remarks
819
824
  * Use this method to render custom media control UI in a plugin
820
825
  * @example
@@ -830,7 +835,7 @@ export class MediaControl extends UICorePlugin {
830
835
  */
831
836
  mount(name, element) {
832
837
  const panel = this.getElementLocation(name);
833
- trace(`${T} putElement`, { name, panel: !!panel });
838
+ trace(`${T} mount`, { name, panel: !!panel });
834
839
  if (panel) {
835
840
  const current = panel.find(`[data-${name}]`);
836
841
  element.attr(`data-${name}`, '');
@@ -847,13 +852,18 @@ export class MediaControl extends UICorePlugin {
847
852
  return;
848
853
  }
849
854
  }
855
+ /**
856
+ * @deprecated Use {@link MediaControl.mount} instead
857
+ * @param name
858
+ * @param element
859
+ */
850
860
  putElement(name, element) {
851
861
  this.mount(name, element);
852
862
  }
853
863
  /**
854
864
  * Toggle the visibility of a media control element
855
865
  * @param name - The name of the media control element
856
- * @param show - Whether to show or hide the element
866
+ * @param show - Visibility state
857
867
  */
858
868
  toggleElement(area, show) {
859
869
  this.$el.find(`[data-${area}]`).toggle(show);
@@ -1069,6 +1079,7 @@ export class MediaControl extends UICorePlugin {
1069
1079
  width: this.options.width,
1070
1080
  height: this.options.height,
1071
1081
  });
1082
+ // TODO check out
1072
1083
  this.hideVolumeBar(0);
1073
1084
  }, 0);
1074
1085
  this.parseColors();
@@ -80,7 +80,7 @@ export class PictureInPicture extends UICorePlugin {
80
80
  this.$el.html(PictureInPicture.buttonTemplate({ pipIcon }));
81
81
  const mediaControl = this.core.getPlugin('media_control');
82
82
  if (mediaControl) {
83
- mediaControl.putElement('pip', this.$el);
83
+ mediaControl.mount('pip', this.$el);
84
84
  }
85
85
  return this;
86
86
  }
@@ -248,7 +248,7 @@ export class ClosedCaptions extends UICorePlugin {
248
248
  this.$line = $(ClosedCaptions.templateString());
249
249
  this.resizeFont();
250
250
  this.core.activeContainer.$el.append(this.$line);
251
- mediaControl.putElement('cc', this.$el);
251
+ mediaControl.mount('cc', this.$el);
252
252
  this.updateSelection();
253
253
  this.renderIcon();
254
254
  return this;
@@ -440,7 +440,7 @@ export class VastAds extends UICorePlugin {
440
440
  // Attempt to get poster plugin. (May interfere with media control)
441
441
  this._posterPlugin = this._container?.getPlugin('poster');
442
442
  // Attempt to get click-to-pause plugin. (May interfere with advert click handling)
443
- this._clickToPausePlugin = this._container?.getPlugin('click_to_pause_custom');
443
+ this._clickToPausePlugin = this._container?.getPlugin('click_to_pause');
444
444
  assert(this.playback, 'playback is not defined');
445
445
  this._contentElement = this.playback.el;
446
446
  if (this._pluginIsReady) {
@@ -56,7 +56,7 @@ export default class RollManager extends Events {
56
56
  this._playback = this.core.activePlayback;
57
57
  this._contentElement = this._playback.el;
58
58
  this._posterPlugin = this._container.getPlugin('poster');
59
- this._clickToPausePlugin = this._container.getPlugin('click_to_pause_custom');
59
+ this._clickToPausePlugin = this._container.getPlugin('click_to_pause');
60
60
  this._adContainer = _adContainer;
61
61
  this._events = {};
62
62
  this._pr = Math.floor(Math.random() * 1000000);
@@ -1 +1 @@
1
- {"version":3,"file":"testUtils.d.ts","sourceRoot":"","sources":["../src/testUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,YAAY,EAAE,MAAM,cAAc,CAAA;AACxD,OAAO,MAAM,MAAM,eAAe,CAAA;AAElC;;;;GAIG;AACH,qBAAa,aAAc,SAAQ,MAAM;IAErC,SAAS,CAAC,OAAO,EAAE,GAAG;IACtB,QAAQ,CAAC,IAAI,EAAE,GAAG;IAClB,SAAS,CAAC,WAAW,CAAC,EAAE,GAAG;gBAFjB,OAAO,EAAE,GAAG,EACb,IAAI,EAAE,GAAG,EACR,WAAW,CAAC,EAAE,GAAG,YAAA;IAK7B,IAAI,IAAI,WAEP;IAED,OAAO;IAEP,IAAI;IAEJ,KAAK;IAEL,IAAI;IAEJ,OAAO;IAEP,IAAI;IAEJ,cAAc;IAEd,WAAW;IAIX,QAAQ;IAER,OAAO;IAEP,eAAe;IAIf,kBAAkB;IAIlB,cAAc;IAId,qBAAqB;IAIrB,IAAI;IAEJ,MAAM;IAEN,MAAM;IAEN,SAAS;IAET,eAAe;IAIf,WAAW;IAIX,QAAQ;IAIR,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE;CAGtC;AAED,wBAAgB,cAAc,CAC5B,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACrC,SAAS,GAAE,GAAkC;;;;;;;;;;;;;;;;EAqB9C;AAED,wBAAgB,gBAAgB;;;EAK/B;AAED,wBAAgB,mBAAmB;;;;;;EAKlC;AAED,wBAAgB,kBAAkB,CAAC,IAAI,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkC/C;AAED,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EAAE,QAAQ,GAAE,GAA0B;;;;;;;;;;;;;;;EAmB9G;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,GAAG,gBAc/C;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,GAAG,OAY7C"}
1
+ {"version":3,"file":"testUtils.d.ts","sourceRoot":"","sources":["../src/testUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,YAAY,EAAE,MAAM,cAAc,CAAA;AACxD,OAAO,MAAM,MAAM,eAAe,CAAA;AAElC;;;;GAIG;AACH,qBAAa,aAAc,SAAQ,MAAM;IAErC,SAAS,CAAC,OAAO,EAAE,GAAG;IACtB,QAAQ,CAAC,IAAI,EAAE,GAAG;IAClB,SAAS,CAAC,WAAW,CAAC,EAAE,GAAG;gBAFjB,OAAO,EAAE,GAAG,EACb,IAAI,EAAE,GAAG,EACR,WAAW,CAAC,EAAE,GAAG,YAAA;IAK7B,IAAI,IAAI,WAEP;IAED,OAAO;IAEP,IAAI;IAEJ,KAAK;IAEL,IAAI;IAEJ,OAAO;IAEP,IAAI;IAEJ,cAAc;IAEd,WAAW;IAIX,QAAQ;IAER,OAAO;IAEP,eAAe;IAIf,kBAAkB;IAIlB,cAAc;IAId,qBAAqB;IAIrB,IAAI;IAEJ,MAAM;IAEN,MAAM;IAEN,SAAS;IAET,eAAe;IAIf,WAAW;IAIX,QAAQ;IAIR,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE;CAGtC;AAED,wBAAgB,cAAc,CAC5B,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACrC,SAAS,GAAE,GAAkC;;;;;;;;;;;;;;;;EAqB9C;AAED,wBAAgB,gBAAgB;;;EAK/B;AAED,wBAAgB,mBAAmB;;;;;;EAKlC;AAED,wBAAgB,kBAAkB,CAAC,IAAI,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkC/C;AAED,wBAAgB,mBAAmB,CACjC,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACrC,QAAQ,GAAE,GAA0B;;;;;;;;;;;;;;;EAoBrC;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,GAAG,gBAc/C;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,GAAG,OAe7C"}
package/lib/testUtils.js CHANGED
@@ -75,7 +75,7 @@ export function createMockCore(options = {}, container = createMockContainer(opt
75
75
  ...options,
76
76
  },
77
77
  configure: vi.fn(),
78
- getPlaybackType: vi.fn().mockReturnValue(Playback.LIVE),
78
+ getPlaybackType: vi.fn(),
79
79
  getPlugin: vi.fn(),
80
80
  load: vi.fn(),
81
81
  trigger: emitter.emit,
@@ -112,7 +112,7 @@ export function createMockPlayback(name = 'mock') {
112
112
  getDuration: vi.fn().mockImplementation(() => 100),
113
113
  enterPiP: vi.fn(),
114
114
  exitPiP: vi.fn(),
115
- getPlaybackType: vi.fn().mockImplementation(() => Playback.LIVE),
115
+ getPlaybackType: vi.fn(),
116
116
  getStartTimeOffset: vi.fn().mockImplementation(() => 0),
117
117
  getCurrentTime: vi.fn().mockImplementation(() => 0),
118
118
  isHighDefinitionInUse: vi.fn().mockImplementation(() => false),
@@ -138,7 +138,7 @@ export function createMockContainer(options = {}, playback = createMockPlayback(
138
138
  $el: $(el),
139
139
  getDuration: vi.fn().mockReturnValue(0),
140
140
  getPlugin: vi.fn(),
141
- getPlaybackType: vi.fn().mockReturnValue(Playback.LIVE),
141
+ getPlaybackType: vi.fn(),
142
142
  isDvrInUse: vi.fn().mockReturnValue(false),
143
143
  isDvrEnabled: vi.fn().mockReturnValue(false),
144
144
  isPlaying: vi.fn().mockReturnValue(false),
@@ -169,7 +169,10 @@ export function createMockBottomGear(core) {
169
169
  if (existing.length) {
170
170
  return existing;
171
171
  }
172
- return $('<li></li>').attr(`data-${name}`, '').append($el).appendTo(plugin.$el);
172
+ return $('<li></li>')
173
+ .attr(`data-${name}`, '')
174
+ .append($el)
175
+ .appendTo(plugin.$el);
173
176
  });
174
177
  plugin.refresh = vi.fn();
175
178
  return plugin;
package/lib/types.d.ts CHANGED
@@ -145,7 +145,7 @@ export interface PlayerConfig extends Record<string, unknown> {
145
145
  mute?: boolean;
146
146
  /**
147
147
  * Stream type.
148
- * @remark
148
+ * @remarks
149
149
  * Should only be set if known in advance, as it should not change once determined.
150
150
  * Otherwise it might cause inconsistencies in the UI plugins behavior, for instance, glitches with rendering of the DVR controls or seek bar.
151
151
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gcorevideo/player",
3
- "version": "2.22.18",
3
+ "version": "2.22.20",
4
4
  "description": "Gcore JavaScript video player",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -17,7 +17,7 @@
17
17
  "format": "prettier --write src/",
18
18
  "lint": "oxlint -c ../../.oxlintrc.json --tsconfig=./tsconfig.json --fix --ignore-path=../../.gitignore src",
19
19
  "ship": "npm run build && npm run build:bundle && npm publish --access public",
20
- "test": "NODE_OPTIONS='--trace-deprecation' vitest"
20
+ "test": "NODE_OPTIONS='--trace-deprecation' vitest --dom"
21
21
  },
22
22
  "repository": {
23
23
  "type": "git",
@@ -43,7 +43,7 @@
43
43
  "@types/sinonjs__fake-timers": "^8.1.5",
44
44
  "assert": "^2.1.0",
45
45
  "eventemitter3": "^5.0.1",
46
- "jsdom": "^26.0.0",
46
+ "happy-dom": "^17.4.4",
47
47
  "nodemon": "^3.1.9",
48
48
  "rollup": "^4.27.4",
49
49
  "rollup-plugin-polyfill-node": "^0.13.0",
@@ -33,10 +33,10 @@ describe('HTML5Video', () => {
33
33
  }
34
34
  const html5Video = new HTML5Video({}, i18n, playerError)
35
35
  html5Video.load('https://example.com/video.mp4')
36
- ;(html5Video.el as any).error = {
36
+ vi.spyOn(html5Video.el as HTMLVideoElement, 'error', 'get').mockReturnValue({
37
37
  code: MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED,
38
38
  message: 'Media source not supported',
39
- }
39
+ } as any)
40
40
  html5Video.el.dispatchEvent(new Event('error', { bubbles: true }))
41
41
  expect(playerError.createError).toHaveBeenCalledWith(expect.objectContaining({
42
42
  code: PlaybackErrorCode.MediaSourceUnavailable,
@@ -9,6 +9,7 @@ import '../../../assets/audio-selector/style.scss'
9
9
  import audioArrow from '../../../assets/icons/old/quality-arrow.svg'
10
10
  import { ZeptoResult } from '../../types.js'
11
11
  import { MediaControl } from '../media-control/MediaControl.js'
12
+ // import { trace } from '@gcorevideo/utils'
12
13
 
13
14
  const VERSION: string = '2.22.4'
14
15
 
@@ -87,7 +88,7 @@ export class AudioTracks extends UICorePlugin {
87
88
  const mediaControl = this.core.getPlugin('media_control')
88
89
  assert(mediaControl, 'media_control plugin is required')
89
90
  this.listenTo(mediaControl, Events.MEDIACONTROL_RENDERED, () => {
90
- mediaControl.putElement('audiotracks', this.$el)
91
+ mediaControl.mount('audiotracks', this.$el)
91
92
  })
92
93
  this.listenTo(mediaControl, Events.MEDIACONTROL_HIDE, this.hideMenu)
93
94
  }
@@ -130,12 +131,12 @@ export class AudioTracks extends UICorePlugin {
130
131
  return this
131
132
  }
132
133
 
133
- const mediaControl = this.core.getPlugin('media_control') as MediaControl
134
134
  this.$el.html(
135
135
  AudioTracks.template({
136
136
  tracks: this.tracks,
137
137
  title: this.getTitle(),
138
138
  icon: audioArrow,
139
+ current: this.currentTrack?.id,
139
140
  }),
140
141
  )
141
142
  this.updateText()
@@ -145,7 +146,7 @@ export class AudioTracks extends UICorePlugin {
145
146
  }
146
147
 
147
148
  private onTrackSelect(event: MouseEvent) {
148
- const id = (event.target as HTMLElement)?.dataset?.audiotracksSelect
149
+ const id = (event.currentTarget as HTMLElement)?.dataset?.audiotracksSelect
149
150
  if (id) {
150
151
  this.selectAudioTrack(id)
151
152
  }
@@ -155,7 +156,7 @@ export class AudioTracks extends UICorePlugin {
155
156
  }
156
157
 
157
158
  private selectAudioTrack(id: string) {
158
- this.startTrackSwitch()
159
+ this.startTrackSwitching()
159
160
  this.core.activeContainer.switchAudioTrack(id)
160
161
  this.updateText()
161
162
  }
@@ -165,7 +166,9 @@ export class AudioTracks extends UICorePlugin {
165
166
  }
166
167
 
167
168
  private toggleContextMenu() {
168
- this.$el.find('#audiotracks-select').toggleClass('hidden')
169
+ this.$el.find('#audiotracks-select').toggleClass('hidden') // TODO use plain CSS display: none
170
+ const open = !this.$el.find('#audiotracks-select').hasClass('hidden') // TODO hold state
171
+ this.$el.find('#audiotracks-button').attr('aria-expanded', open)
169
172
  }
170
173
 
171
174
  private buttonElement(): ZeptoResult {
@@ -192,7 +195,7 @@ export class AudioTracks extends UICorePlugin {
192
195
  return this.currentTrack.label || this.currentTrack.language
193
196
  }
194
197
 
195
- private startTrackSwitch() {
198
+ private startTrackSwitching() {
196
199
  this.buttonElement().addClass('changing')
197
200
  }
198
201
 
@@ -205,13 +208,17 @@ export class AudioTracks extends UICorePlugin {
205
208
 
206
209
  private highlightCurrentTrack() {
207
210
  this.trackElement().removeClass('current')
208
- this.trackElement().find('a').removeClass('gcore-skin-active')
211
+ this.trackElement()
212
+ .find('a')
213
+ .removeClass('gcore-skin-active')
214
+ .attr('aria-checked', 'false')
209
215
 
210
216
  if (this.currentTrack) {
211
217
  this.trackElement(this.currentTrack.id)
212
218
  .addClass('current')
213
219
  .find('a')
214
220
  .addClass('gcore-skin-active')
221
+ .attr('aria-checked', 'true')
215
222
  }
216
223
  }
217
224
  }
@@ -34,7 +34,7 @@ describe('AudioSelector', () => {
34
34
  emitTracksAvailable(core, TRACKS)
35
35
  })
36
36
  it('should not attach to the media control', () => {
37
- expect(mediaControl.putElement).not.toHaveBeenCalledWith(
37
+ expect(mediaControl.mount).not.toHaveBeenCalledWith(
38
38
  'audiotracks',
39
39
  expect.anything(),
40
40
  )
@@ -45,20 +45,20 @@ describe('AudioSelector', () => {
45
45
  mediaControl.trigger(Events.MEDIACONTROL_RENDERED)
46
46
  })
47
47
  it('should attach to the media control', () => {
48
- expect(mediaControl.putElement).toHaveBeenCalledWith(
48
+ expect(mediaControl.mount).toHaveBeenCalledWith(
49
49
  'audiotracks',
50
50
  audioSelector.$el,
51
51
  )
52
52
  })
53
53
  })
54
- describe('when audio tracks are available', () => {
54
+ describe('given that audio tracks are available', () => {
55
55
  beforeEach(() => {
56
56
  emitTracksAvailable(core, TRACKS)
57
57
  })
58
- it('should render the button', () => {
58
+ it('should render button', () => {
59
59
  expect(audioSelector.$el.find('#audiotracks-button').length).toBe(1)
60
60
  })
61
- it('should render the menu hidden', () => {
61
+ it('should render menu hidden', () => {
62
62
  expect(audioSelector.el.innerHTML).toMatchSnapshot()
63
63
  expect(
64
64
  audioSelector.$el.find('#audiotracks-select').hasClass('hidden'),
@@ -68,17 +68,17 @@ describe('AudioSelector', () => {
68
68
  expect(trackItems.eq(0).text().trim()).toBe('English')
69
69
  expect(trackItems.eq(1).text().trim()).toBe('Spanish')
70
70
  })
71
- describe('when the button is clicked', () => {
71
+ describe('when button is clicked', () => {
72
72
  beforeEach(() => {
73
73
  audioSelector.$el.find('#audiotracks-button').click()
74
74
  })
75
- it('should show the menu', () => {
75
+ it('should show menu', () => {
76
76
  expect(audioSelector.$el.html()).toMatchSnapshot()
77
77
  expect(
78
78
  audioSelector.$el.find('#audiotracks-select').hasClass('hidden'),
79
79
  ).toBe(false)
80
80
  })
81
- describe('when an audio track is selected', () => {
81
+ describe('when audio track is selected', () => {
82
82
  beforeEach(() => {
83
83
  audioSelector.$el
84
84
  .find('#audiotracks-select [data-audiotracks-select="2"]')
@@ -1,19 +1,19 @@
1
1
  // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
2
 
3
- exports[`AudioSelector > when audio tracks are available > should render the menu hidden 1`] = `
4
- "<button data-audiotracks-button="" class="gcore-skin-button-color" id="audiotracks-button">
3
+ exports[`AudioSelector > given that audio tracks are available > should render menu hidden 1`] = `
4
+ "<button data-audiotracks-button="" class="gcore-skin-button-color" id="audiotracks-button" aria-haspopup="menu" aria-expanded="false">
5
5
  <span class="audio-text"></span> <span class="audio-arrow">/assets/icons/old/quality-arrow.svg</span>
6
6
  </button>
7
- <ul class="gcore-skin-bg-color menu hidden" id="audiotracks-select">
7
+ <ul class="gcore-skin-bg-color menu hidden" id="audiotracks-select" role="menu">
8
8
 
9
9
  <li class="">
10
- <a href="#" class="gcore-skin-text-color" data-audiotracks-select="1">
10
+ <a href="#" class="gcore-skin-text-color" data-audiotracks-select="1" role="menuitemradio" aria-checked="false">
11
11
  English
12
12
  </a>
13
13
  </li>
14
14
 
15
15
  <li class="">
16
- <a href="#" class="gcore-skin-text-color" data-audiotracks-select="2">
16
+ <a href="#" class="gcore-skin-text-color" data-audiotracks-select="2" role="menuitemradio" aria-checked="false">
17
17
  Spanish
18
18
  </a>
19
19
  </li>
@@ -22,20 +22,20 @@ exports[`AudioSelector > when audio tracks are available > should render the men
22
22
  "
23
23
  `;
24
24
 
25
- exports[`AudioSelector > when audio tracks are available > when the button is clicked > should show the menu 1`] = `
26
- "<button data-audiotracks-button="" class="gcore-skin-button-color" id="audiotracks-button">
25
+ exports[`AudioSelector > given that audio tracks are available > when button is clicked > should show menu 1`] = `
26
+ "<button data-audiotracks-button="" class="gcore-skin-button-color" id="audiotracks-button" aria-haspopup="menu" aria-expanded="true">
27
27
  <span class="audio-text"></span> <span class="audio-arrow">/assets/icons/old/quality-arrow.svg</span>
28
28
  </button>
29
- <ul class="gcore-skin-bg-color menu" id="audiotracks-select">
29
+ <ul class="gcore-skin-bg-color menu" id="audiotracks-select" role="menu">
30
30
 
31
31
  <li class="">
32
- <a href="#" class="gcore-skin-text-color" data-audiotracks-select="1">
32
+ <a href="#" class="gcore-skin-text-color" data-audiotracks-select="1" role="menuitemradio" aria-checked="false">
33
33
  English
34
34
  </a>
35
35
  </li>
36
36
 
37
37
  <li class="">
38
- <a href="#" class="gcore-skin-text-color" data-audiotracks-select="2">
38
+ <a href="#" class="gcore-skin-text-color" data-audiotracks-select="2" role="menuitemradio" aria-checked="false">
39
39
  Spanish
40
40
  </a>
41
41
  </li>
@@ -44,20 +44,20 @@ exports[`AudioSelector > when audio tracks are available > when the button is cl
44
44
  "
45
45
  `;
46
46
 
47
- exports[`AudioSelector > when audio tracks are available > when the button is clicked > when an audio track is selected > should hide the menu 1`] = `
48
- "<button data-audiotracks-button="" class="gcore-skin-button-color changing" id="audiotracks-button">
47
+ exports[`AudioSelector > given that audio tracks are available > when button is clicked > when audio track is selected > should hide the menu 1`] = `
48
+ "<button data-audiotracks-button="" class="gcore-skin-button-color changing" id="audiotracks-button" aria-haspopup="menu" aria-expanded="true">
49
49
  <span class="audio-text"></span> <span class="audio-arrow">/assets/icons/old/quality-arrow.svg</span>
50
50
  </button>
51
- <ul class="gcore-skin-bg-color menu hidden" id="audiotracks-select">
51
+ <ul class="gcore-skin-bg-color menu hidden" id="audiotracks-select" role="menu">
52
52
 
53
53
  <li class="">
54
- <a href="#" class="gcore-skin-text-color" data-audiotracks-select="1">
54
+ <a href="#" class="gcore-skin-text-color" data-audiotracks-select="1" role="menuitemradio" aria-checked="false">
55
55
  English
56
56
  </a>
57
57
  </li>
58
58
 
59
59
  <li class="">
60
- <a href="#" class="gcore-skin-text-color" data-audiotracks-select="2">
60
+ <a href="#" class="gcore-skin-text-color" data-audiotracks-select="2" role="menuitemradio" aria-checked="false">
61
61
  Spanish
62
62
  </a>
63
63
  </li>
@@ -21,7 +21,7 @@ const T = 'plugins.bottom_gear'
21
21
  */
22
22
  export enum GearEvents {
23
23
  /**
24
- * Use this event to accurately attach an item to the gear menu
24
+ * Subscribe to this event to accurately attach an item to the gear menu
25
25
  */
26
26
  RENDERED = 'rendered',
27
27
  }
@@ -276,6 +276,6 @@ export class BottomGear extends UICorePlugin {
276
276
  private onMediaControlRendered() {
277
277
  trace(`${T} onMediaControlRendered`)
278
278
  const mediaControl = this.core.getPlugin('media_control')
279
- mediaControl.putElement('gear', this.$el)
279
+ mediaControl.mount('gear', this.$el)
280
280
  }
281
281
  }
@@ -38,7 +38,10 @@ describe('BottomGear', () => {
38
38
  })
39
39
  describe('until media control is rendered', () => {
40
40
  it('should not attach to media control', () => {
41
- expect(mediaControl.putElement).not.toHaveBeenCalledWith('gear', expect.anything())
41
+ expect(mediaControl.mount).not.toHaveBeenCalledWith(
42
+ 'gear',
43
+ expect.anything(),
44
+ )
42
45
  })
43
46
  })
44
47
  describe('when media control is rendered', () => {
@@ -46,7 +49,7 @@ describe('BottomGear', () => {
46
49
  mediaControl.trigger(Events.MEDIACONTROL_RENDERED)
47
50
  })
48
51
  it('should attach to media control', () => {
49
- expect(mediaControl.putElement).toHaveBeenCalledWith('gear', bottomGear.$el)
52
+ expect(mediaControl.mount).toHaveBeenCalledWith('gear', bottomGear.$el)
50
53
  })
51
54
  })
52
55
  describe('when clicked', () => {
@@ -54,9 +57,9 @@ describe('BottomGear', () => {
54
57
  bottomGear.$el.find('#gear-button').click()
55
58
  })
56
59
  it('should toggle the gear menu', () => {
57
- expect(bottomGear.$el.find('#gear-options-wrapper').css('display')).toBe(
58
- 'block',
59
- )
60
+ expect(
61
+ bottomGear.$el.find('#gear-options-wrapper').css('display'),
62
+ ).not.toBe('none')
60
63
  })
61
64
  })
62
65
  })
@@ -1,11 +1,11 @@
1
1
  // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
2
 
3
3
  exports[`BottomGear > should render 1`] = `
4
- "<button type="button" class="media-control-button gplayer-lite-btn gcore-skin-button-color gear-icon" id="gear-button">
4
+ "<button class="media-control-button gplayer-lite-btn gcore-skin-button-color gear-icon" id="gear-button">
5
5
  /assets/icons/new/gear.svg
6
6
  </button>
7
- <div class="gear-wrapper gcore-skin-bg-color" id="gear-options-wrapper" style="display: none;">
8
- <ul class="gear-options-list" id="gear-options"></ul>
7
+ <div class="gear-wrapper gcore-skin-bg-color" id="gear-options-wrapper" style="display:none">
8
+ <ul class="gear-options-list" id="gear-options" role="menu"></ul>
9
9
  </div>
10
10
  "
11
11
  `;
@@ -13,7 +13,7 @@ import assert from 'assert'
13
13
  import { CLAPPR_VERSION } from '../../build.js'
14
14
  import {
15
15
  ClapprStatsEvents,
16
- Metrics as PerfMetrics,
16
+ ClapprStatsMetrics as PerfMetrics,
17
17
  } from '../clappr-stats/types.js'
18
18
  import { newMetrics as newBaseMetrics } from '../clappr-stats/utils.js'
19
19
  import Formatter from './formatter.js'
@@ -62,16 +62,16 @@ type Metrics = PerfMetrics & {
62
62
  const T = 'plugins.nerd_stats'
63
63
 
64
64
  /**
65
- * `PLUGIN` that displays useful network-related statistics.
65
+ * `PLUGIN` that displays useful statistics regarding the playback as well as the network quality estimation.
66
66
  * @beta
67
67
  *
68
68
  * @remarks
69
69
  * Depends on:
70
70
  *
71
- * - {@link BottomGear}
72
- *
73
- * - {@link ClapprStats}
71
+ * - {@link BottomGear} - where the button is attached
74
72
  *
73
+ * - {@link ClapprStats} - to get the metrics from
74
+ *
75
75
  * The plugin is rendered as an item in the gear menu.
76
76
  *
77
77
  * When clicked, it shows an overlay window with the information about the network speed, latency, etc,