@gcorevideo/player 2.19.14 → 2.19.15

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 (77) hide show
  1. package/assets/level-selector/list.ejs +2 -2
  2. package/dist/core.js +1 -1
  3. package/dist/index.css +1054 -1054
  4. package/dist/index.js +1232 -1154
  5. package/dist/player.d.ts +157 -22
  6. package/dist/plugins/index.css +634 -634
  7. package/dist/plugins/index.js +1024 -944
  8. package/docs/api/player.bottomgear.getelement.md +56 -0
  9. package/docs/api/player.bottomgear.md +51 -0
  10. package/docs/api/player.bottomgear.setcontent.md +56 -0
  11. package/docs/api/{player.subtitles.levelelement.md → player.gearevents.md} +11 -13
  12. package/docs/api/{player.sourcecontroller.name.md → player.gearitemelement.md} +5 -3
  13. package/docs/api/player.levelselector.md +9 -1
  14. package/docs/api/player.md +37 -0
  15. package/docs/api/{player.sourcecontroller.supportedversion.md → player.mediacontrol.getrightpanel.md} +11 -5
  16. package/docs/api/player.mediacontrol.md +14 -0
  17. package/docs/api/player.mediacontrolelement.md +1 -1
  18. package/docs/api/player.sourcecontroller.md +0 -90
  19. package/docs/api/player.spinnerevents.md +49 -0
  20. package/docs/api/player.spinnerthreebounce._constructor_.md +3 -0
  21. package/docs/api/player.spinnerthreebounce.hide.md +5 -0
  22. package/docs/api/player.spinnerthreebounce.md +14 -95
  23. package/docs/api/player.spinnerthreebounce.show.md +6 -37
  24. package/docs/api/player.subtitles.hide.md +5 -0
  25. package/docs/api/player.subtitles.md +23 -275
  26. package/docs/api/player.subtitles.show.md +5 -0
  27. package/lib/plugins/bottom-gear/BottomGear.d.ts +20 -1
  28. package/lib/plugins/bottom-gear/BottomGear.d.ts.map +1 -1
  29. package/lib/plugins/bottom-gear/BottomGear.js +28 -7
  30. package/lib/plugins/clappr-nerd-stats/ClapprNerdStats.js +4 -4
  31. package/lib/plugins/level-selector/LevelSelector.d.ts +10 -3
  32. package/lib/plugins/level-selector/LevelSelector.d.ts.map +1 -1
  33. package/lib/plugins/level-selector/LevelSelector.js +20 -19
  34. package/lib/plugins/media-control/MediaControl.d.ts +6 -2
  35. package/lib/plugins/media-control/MediaControl.d.ts.map +1 -1
  36. package/lib/plugins/media-control/MediaControl.js +40 -39
  37. package/lib/plugins/source-controller/SourceController.d.ts +9 -0
  38. package/lib/plugins/source-controller/SourceController.d.ts.map +1 -1
  39. package/lib/plugins/source-controller/SourceController.js +11 -1
  40. package/lib/plugins/spinner-three-bounce/SpinnerThreeBounce.d.ts +35 -1
  41. package/lib/plugins/spinner-three-bounce/SpinnerThreeBounce.d.ts.map +1 -1
  42. package/lib/plugins/spinner-three-bounce/SpinnerThreeBounce.js +46 -23
  43. package/lib/plugins/subtitles/Subtitles.d.ts +65 -16
  44. package/lib/plugins/subtitles/Subtitles.d.ts.map +1 -1
  45. package/lib/plugins/subtitles/Subtitles.js +131 -109
  46. package/package.json +1 -1
  47. package/src/plugins/bottom-gear/BottomGear.ts +26 -4
  48. package/src/plugins/clappr-nerd-stats/ClapprNerdStats.ts +4 -4
  49. package/src/plugins/level-selector/LevelSelector.ts +22 -19
  50. package/src/plugins/media-control/MediaControl.ts +43 -41
  51. package/src/plugins/source-controller/SourceController.ts +11 -1
  52. package/src/plugins/source-controller/__tests__/SourceController.test.ts +1 -1
  53. package/src/plugins/spinner-three-bounce/SpinnerThreeBounce.ts +46 -22
  54. package/src/plugins/subtitles/Subtitles.ts +146 -155
  55. package/temp/player.api.json +293 -822
  56. package/tsconfig.tsbuildinfo +1 -1
  57. package/docs/api/player.sourcecontroller.version.md +0 -14
  58. package/docs/api/player.spinnerthreebounce.attributes.md +0 -14
  59. package/docs/api/player.spinnerthreebounce.name.md +0 -11
  60. package/docs/api/player.spinnerthreebounce.render.md +0 -15
  61. package/docs/api/player.spinnerthreebounce.supportedversion.md +0 -13
  62. package/docs/api/player.subtitles.attributes.md +0 -14
  63. package/docs/api/player.subtitles.bindevents.md +0 -15
  64. package/docs/api/player.subtitles.buttonelement.md +0 -15
  65. package/docs/api/player.subtitles.events.md +0 -14
  66. package/docs/api/player.subtitles.name.md +0 -11
  67. package/docs/api/player.subtitles.preselectedlanguage.md +0 -11
  68. package/docs/api/player.subtitles.reload.md +0 -15
  69. package/docs/api/player.subtitles.render.md +0 -15
  70. package/docs/api/player.subtitles.selectsubtitles.md +0 -15
  71. package/docs/api/player.subtitles.startlevelswitch.md +0 -15
  72. package/docs/api/player.subtitles.stoplevelswitch.md +0 -15
  73. package/docs/api/player.subtitles.supportedversion.md +0 -13
  74. package/docs/api/player.subtitles.template.md +0 -11
  75. package/docs/api/player.subtitles.templatestring.md +0 -11
  76. package/docs/api/player.subtitles.unbindevents.md +0 -15
  77. package/docs/api/player.subtitles.version.md +0 -11
@@ -1,5 +1,5 @@
1
1
  import { Events, UICorePlugin, Browser, template, $, } from '@clappr/core';
2
- import { reportError } from '@gcorevideo/utils';
2
+ import { reportError, trace } from '@gcorevideo/utils';
3
3
  import assert from 'assert';
4
4
  import { CLAPPR_VERSION } from '../../build.js';
5
5
  import '../../../assets/subtitles/style.scss';
@@ -8,88 +8,102 @@ import subtitlesOnIcon from '../../../assets/icons/new/subtitles-on.svg';
8
8
  import comboboxHTML from '../../../assets/subtitles/combobox.ejs';
9
9
  import stringHTML from '../../../assets/subtitles/string.ejs';
10
10
  import { isFullscreen } from '../utils.js';
11
- const VERSION = '0.0.1';
12
- const LOCAL_STORAGE_SUBTITLES_ID = 'subtitles_select';
13
- const T = 'plugins.media_control_subtitles';
11
+ const VERSION = '2.19.14';
12
+ const LOCAL_STORAGE_SUBTITLES_ID = 'gplayer.plugins.subtitles.selected';
13
+ const T = 'plugins.subtitles';
14
14
  const NO_TRACK = { language: 'off' };
15
+ /**
16
+ * A {@link MediaControl | media control} plugin that provides a UI to select the subtitles when available.
17
+ * @beta
18
+ *
19
+ * @remarks
20
+ * Depends on:
21
+ *
22
+ * - {@link MediaControl}
23
+ *
24
+ * Configuration options:
25
+ *
26
+ * - subtitles.language - The language of the subtitles to select by default.
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * import { Subtitles } from '@gcorevideo/player'
31
+ *
32
+ * Player.registerPlugin(Subtitles)
33
+ *
34
+ * new Player({
35
+ * ...
36
+ * subtitles: {
37
+ * language: 'en',
38
+ * },
39
+ * })
40
+ * ```
41
+ */
15
42
  export class Subtitles extends UICorePlugin {
16
- currentContainer;
17
- currentLevel;
18
- currentPlayback;
43
+ currentLevel = null;
44
+ isPreselectedApplied = false;
19
45
  isShowing = false;
20
- tracks;
21
- $string;
46
+ track = { ...NO_TRACK };
47
+ tracks = null;
48
+ $string = null;
49
+ /**
50
+ * @internal
51
+ */
22
52
  get name() {
23
- return 'media_control_subtitles';
53
+ return 'subtitles';
24
54
  }
55
+ /**
56
+ * @internal
57
+ */
25
58
  get supportedVersion() {
26
59
  return { min: CLAPPR_VERSION };
27
60
  }
61
+ /**
62
+ * @internal
63
+ */
28
64
  static get version() {
29
65
  return VERSION;
30
66
  }
31
- get template() {
32
- return template(comboboxHTML);
33
- }
34
- get templateString() {
35
- return template(stringHTML);
36
- }
67
+ static template = template(comboboxHTML);
68
+ static templateString = template(stringHTML);
69
+ /**
70
+ * @internal
71
+ */
37
72
  get attributes() {
38
73
  return {
39
74
  class: this.name,
40
75
  'data-subtitles': '',
41
76
  };
42
77
  }
78
+ /**
79
+ * @internal
80
+ */
43
81
  get events() {
44
82
  return {
45
83
  'click [data-subtitles-select]': 'onLevelSelect',
46
84
  'click [data-subtitles-button]': 'onShowLevelSelectMenu',
47
85
  };
48
86
  }
49
- isPreselectedApplied = false;
50
- track = { ...NO_TRACK };
51
87
  get preselectedLanguage() {
52
88
  return this.core.options.subtitles?.language ?? 'off';
53
89
  }
90
+ /**
91
+ * @internal
92
+ */
54
93
  bindEvents() {
94
+ const mediaControl = this.core.getPlugin('media_control');
95
+ assert(mediaControl, 'media_control plugin is required');
55
96
  this.listenTo(this.core, Events.CORE_RESIZE, this.playerResize);
56
- this.listenToOnce(this.core, Events.CORE_READY, this.bindPlaybackEvents);
57
- this.listenTo(this.core.mediaControl, Events.MEDIACONTROL_CONTAINERCHANGED, this.reload);
58
- this.listenTo(this.core.mediaControl, Events.MEDIACONTROL_RENDERED, this.render);
59
- this.listenTo(this.core.mediaControl, Events.MEDIACONTROL_HIDE, this.hideSelectLevelMenu);
60
- }
61
- unBindEvents() {
62
- // @ts-ignore
63
- this.stopListening(this.core, Events.CORE_READY);
64
- // @ts-ignore
65
- this.stopListening(this.core.mediaControl, Events.MEDIACONTROL_CONTAINERCHANGED);
66
- // @ts-ignore
67
- this.stopListening(this.core.mediaControl, Events.MEDIACONTROL_RENDERED);
68
- // @ts-ignore
69
- this.stopListening(this.core.mediaControl, Events.MEDIACONTROL_HIDE);
70
- // @ts-ignore
71
- this.stopListening(this.core.mediaControl, Events.MEDIACONTROL_SHOW);
72
- if (this.currentContainer) {
73
- // @ts-ignore
74
- this.stopListening(this.currentContainer, Events.CONTAINER_FULLSCREEN);
75
- // @ts-ignore
76
- this.stopListening(this.currentContainer, 'container:advertisement:start', this.onStartAd);
77
- // @ts-ignore
78
- this.stopListening(this.currentContainer, 'container:advertisement:finish', this.onFinishAd);
79
- }
97
+ this.listenTo(this.core, Events.CORE_ACTIVE_CONTAINER_CHANGED, this.bindPlaybackEvents);
98
+ this.listenTo(mediaControl, Events.MEDIACONTROL_RENDERED, this.render);
99
+ this.listenTo(mediaControl, Events.MEDIACONTROL_HIDE, this.hideSelectLevelMenu);
80
100
  }
81
101
  bindPlaybackEvents() {
82
- if (this.currentPlayback &&
83
- this.currentPlayback === this.core.activePlayback) {
84
- return;
85
- }
86
- this.currentPlayback = this.core.activePlayback;
87
- this.currentContainer = this.core.activeContainer;
88
- this.listenTo(this.currentContainer, Events.CONTAINER_FULLSCREEN, this.playerResize);
89
- this.listenToOnce(this.currentPlayback, Events.PLAYBACK_PLAY, this.getTracks);
90
- this.listenTo(this.currentContainer, 'container:advertisement:start', this.onStartAd);
102
+ this.listenTo(this.core.activeContainer, Events.CONTAINER_FULLSCREEN, this.playerResize);
103
+ this.listenToOnce(this.core.activePlayback, Events.PLAYBACK_PLAY, this.getTracks);
104
+ this.listenTo(this.core.activeContainer, 'container:advertisement:start', this.onStartAd);
91
105
  // fix for iOS
92
- const video = this.currentPlayback?.el;
106
+ const video = this.core.activePlayback.el;
93
107
  assert(video, 'video element is required');
94
108
  video.addEventListener('webkitbeginfullscreen', () => {
95
109
  if (Browser.isiOS) {
@@ -103,10 +117,13 @@ export class Subtitles extends UICorePlugin {
103
117
  });
104
118
  }
105
119
  getTracks() {
106
- if (this.currentPlayback) {
120
+ if (this.core.activePlayback) {
107
121
  try {
108
- const tracks = this.currentPlayback.el.textTracks;
109
- tracks.length > 0 && this.fillLevels(tracks);
122
+ const tracks = this.core.activePlayback.el
123
+ .textTracks;
124
+ if (tracks.length > 0) {
125
+ this.setTracks(tracks);
126
+ }
110
127
  }
111
128
  catch (error) {
112
129
  reportError(error);
@@ -114,23 +131,18 @@ export class Subtitles extends UICorePlugin {
114
131
  }
115
132
  }
116
133
  onStartAd() {
117
- if (this.isShowing && this.currentContainer) {
134
+ if (this.isShowing && this.core.activeContainer) {
118
135
  this.hide();
119
- this.listenTo(this.currentContainer, 'container:advertisement:finish', this.onFinishAd);
136
+ this.listenTo(this.core.activeContainer, 'container:advertisement:finish', this.onFinishAd);
120
137
  }
121
138
  }
122
139
  onFinishAd() {
123
140
  this.show();
124
- this.stopListening(this.currentContainer, 'container:advertisement:finish', this.onFinishAd);
125
- }
126
- reload() {
127
- this.unBindEvents();
128
- this.bindEvents();
129
- this.bindPlaybackEvents();
141
+ this.stopListening(this.core.activeContainer, 'container:advertisement:finish', this.onFinishAd);
130
142
  }
131
143
  playerResize() {
132
- const shouldShow = this.currentContainer &&
133
- isFullscreen(this.currentContainer.el) &&
144
+ const shouldShow = this.core.activeContainer &&
145
+ isFullscreen(this.core.activeContainer.el) &&
134
146
  this.currentLevel &&
135
147
  this.currentLevel.mode &&
136
148
  Browser.isiOS &&
@@ -145,6 +157,9 @@ export class Subtitles extends UICorePlugin {
145
157
  reportError(error);
146
158
  }
147
159
  }
160
+ /**
161
+ * Hides the subtitles menu and the subtitles.
162
+ */
148
163
  hide() {
149
164
  this.isShowing = false;
150
165
  this.renderIcon();
@@ -155,11 +170,14 @@ export class Subtitles extends UICorePlugin {
155
170
  }
156
171
  }
157
172
  }
173
+ /**
174
+ * Shows the subtitles menu and the subtitles.
175
+ */
158
176
  show() {
159
177
  this.isShowing = true;
160
178
  this.renderIcon();
161
- if (this.currentContainer &&
162
- isFullscreen(this.currentContainer.el) &&
179
+ if (this.core.activeContainer &&
180
+ isFullscreen(this.core.activeContainer.el) &&
163
181
  this.currentLevel &&
164
182
  this.currentLevel.mode &&
165
183
  Browser.isiOS) {
@@ -171,59 +189,61 @@ export class Subtitles extends UICorePlugin {
171
189
  }
172
190
  }
173
191
  shouldRender() {
174
- if (!this.currentContainer) {
175
- return false;
176
- }
177
- if (!this.currentPlayback) {
178
- return false;
179
- }
180
- // Only care if we have at least 2 to choose from
181
- const hasLevels = !!(this.tracks && this.tracks.length > 0);
182
- return hasLevels;
192
+ return !!(this.tracks && this.tracks.length > 0);
183
193
  }
184
194
  resizeFont() {
185
- if (!this.currentContainer) {
195
+ if (!this.core.activeContainer) {
186
196
  return;
187
197
  }
188
198
  if (!this.$string) {
189
199
  return;
190
200
  }
191
- const skinWidth = this.currentContainer.$el.width();
201
+ const skinWidth = this.core.activeContainer.$el.width();
192
202
  this.$string.find('p').css('font-size', skinWidth * 0.03);
193
203
  }
204
+ /**
205
+ * @internal
206
+ */
194
207
  render() {
195
- if (this.shouldRender()) {
196
- this.$el.html(this.template({ tracks: this.tracks }));
197
- this.currentContainer?.$el.find('.subtitle-string').remove();
198
- this.$string = $(this.templateString());
199
- this.resizeFont();
200
- this.currentContainer?.$el.append(this.$string[0]);
201
- if (this.core.mediaControl.$subtitlesSelector &&
202
- this.core.mediaControl.$subtitlesSelector.length > 0) {
203
- this.core.mediaControl.$subtitlesSelector.append(this.el);
204
- }
205
- else {
206
- this.core.mediaControl.$('.media-control-right-panel').append(this.el);
207
- }
208
- this.updateCurrentLevel(this.track);
209
- this.highlightCurrentSubtitles();
210
- this.applyPreselectedSubtitles();
208
+ if (!this.core.activeContainer) {
209
+ return this;
210
+ }
211
+ if (!this.shouldRender()) {
212
+ return this;
213
+ }
214
+ trace(`${T} render`, {
215
+ tracks: this.tracks?.length,
216
+ track: this.track?.language,
217
+ });
218
+ const mediaControl = this.core.getPlugin('media_control');
219
+ assert(mediaControl, 'media_control plugin is required');
220
+ this.$el.html(Subtitles.template({ tracks: this.tracks }));
221
+ this.core.activeContainer.$el.find('.subtitle-string').remove();
222
+ this.$string = $(Subtitles.templateString());
223
+ this.resizeFont();
224
+ this.core.activeContainer.$el.append(this.$string[0]);
225
+ const ss = mediaControl.getElement('subtitlesSelector');
226
+ if (ss && ss.length > 0) {
227
+ ss.append(this.el);
211
228
  }
212
- if (this.core.mediaControl.$subtitlesSelector?.find('span.subtitle-text')
213
- .length > 0) {
214
- this.renderIcon();
229
+ else {
230
+ mediaControl.getRightPanel().append(this.el);
215
231
  }
232
+ this.updateCurrentLevel(this.track);
233
+ this.highlightCurrentSubtitles();
234
+ this.applyPreselectedSubtitles();
235
+ this.renderIcon();
216
236
  return this;
217
237
  }
218
- fillLevels(tracks) {
238
+ setTracks(tracks) {
219
239
  this.tracks = tracks;
220
240
  this.render();
221
241
  }
222
242
  findLevelBy(id) {
223
243
  if (this.tracks) {
224
- for (let i = 0; i < this.tracks.length; i++) {
225
- if (this.tracks[i].language === id) {
226
- return this.tracks[i]; // TODO TrackInfo?
244
+ for (const track of this.tracks) {
245
+ if (track.language === id) {
246
+ return track; // TODO TrackInfo?
227
247
  }
228
248
  }
229
249
  }
@@ -254,21 +274,21 @@ export class Subtitles extends UICorePlugin {
254
274
  }
255
275
  }
256
276
  onShowLevelSelectMenu() {
277
+ trace(`${T} onShowLevelSelectMenu`);
257
278
  this.toggleContextMenu();
258
279
  }
259
280
  hideSelectLevelMenu() {
260
281
  ;
261
- this.$('.subtitles ul').hide();
282
+ this.$('[data-subtitles] ul').hide();
262
283
  }
263
284
  toggleContextMenu() {
264
- ;
265
- this.$('.subtitles ul').toggle();
285
+ this.$('[data-subtitles] ul').toggle();
266
286
  }
267
287
  buttonElement() {
268
- return this.$('.subtitles button');
288
+ return this.$('[data-subtitles] button');
269
289
  }
270
290
  levelElement(id) {
271
- return this.$('.subtitles ul a' + (id ? '[data-subtitles-select="' + id + '"]' : '')).parent();
291
+ return this.$('[data-subtitles] ul a' + (id ? '[data-subtitles-select="' + id + '"]' : '')).parent();
272
292
  }
273
293
  startLevelSwitch() {
274
294
  this.buttonElement().addClass('changing');
@@ -285,7 +305,7 @@ export class Subtitles extends UICorePlugin {
285
305
  const track = this.tracks[i];
286
306
  if (track.language === this.currentLevel.language) {
287
307
  track.mode = 'showing';
288
- const currentTime = this.currentPlayback?.getCurrentTime() ?? 0;
308
+ const currentTime = this.core.activePlayback?.getCurrentTime() ?? 0;
289
309
  const cues = track.cues;
290
310
  let subtitleText = '';
291
311
  if (cues && cues.length) {
@@ -347,8 +367,10 @@ export class Subtitles extends UICorePlugin {
347
367
  }
348
368
  renderIcon() {
349
369
  const icon = this.isShowing ? subtitlesOnIcon : subtitlesOffIcon;
350
- this.core.mediaControl.$subtitlesSelector
351
- .find('span.subtitle-text')
370
+ this.core
371
+ .getPlugin('media_control')
372
+ .getElement('subtitlesSelector')
373
+ ?.find('span.subtitle-text')
352
374
  .html(icon);
353
375
  }
354
376
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gcorevideo/player",
3
- "version": "2.19.14",
3
+ "version": "2.19.15",
4
4
  "description": "Gcore JavaScript video player",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -13,9 +13,15 @@ import { ZeptoResult } from '../../utils/types.js';
13
13
 
14
14
  const VERSION = '2.19.12';
15
15
 
16
- const T = 'plugins.media_control_gear';
16
+ const T = 'plugins.bottom_gear';
17
17
 
18
- export enum Events {
18
+ /**
19
+ * Custom events emitted by the plugin
20
+ */
21
+ export enum GearEvents {
22
+ /**
23
+ * Emitted when the gear menu is rendered
24
+ */
19
25
  MEDIACONTROL_GEAR_RENDERED = 'mediacontrol:gear:rendered',
20
26
  }
21
27
 
@@ -30,6 +36,10 @@ export type GearItemElement = 'quality' | 'rate' | 'nerd';
30
36
  * @beta
31
37
  * @remarks
32
38
  * The plugins provides a base for attaching custom settings UI in the gear menu
39
+ *
40
+ * Depends on:
41
+ *
42
+ * - {@link MediaControl | media_control}
33
43
  */
34
44
  export class BottomGear extends UICorePlugin {
35
45
  private isHd = false;
@@ -38,7 +48,7 @@ export class BottomGear extends UICorePlugin {
38
48
  * @internal
39
49
  */
40
50
  get name() {
41
- return 'media_control_gear';
51
+ return 'bottom_gear';
42
52
  }
43
53
 
44
54
  /**
@@ -89,10 +99,22 @@ export class BottomGear extends UICorePlugin {
89
99
  this.listenTo(mediaControl, ClapprEvents.MEDIACONTROL_HIDE, this.hide); // TODO mediacontrol show as well
90
100
  }
91
101
 
102
+ /**
103
+ * @param name - Name of a gear menu placeholder item to attach custom UI
104
+ * @returns Zepto result of the element
105
+ */
92
106
  getElement(name: GearItemElement): ZeptoResult | null {
93
107
  return this.core.getPlugin('media_control')?.getElement('gear')?.find(`.gear-options-list [data-${name}]`);
94
108
  }
95
109
 
110
+ /**
111
+ * Replaces the content of the gear menu
112
+ * @param content - Zepto result of the element
113
+ */
114
+ setContent(content: ZeptoResult) {
115
+ this.$el.find('.gear-wrapper').html(content);
116
+ }
117
+
96
118
  private onActiveContainerChanged() {
97
119
  trace(`${T} onActiveContainerChanged`);
98
120
  this.bindContainerEvents();
@@ -131,7 +153,7 @@ export class BottomGear extends UICorePlugin {
131
153
 
132
154
  mediaControl.getElement('gear')?.html(this.el);
133
155
  this.core.trigger('gear:rendered'); // @deprecated
134
- mediaControl.trigger(Events.MEDIACONTROL_GEAR_RENDERED);
156
+ mediaControl.trigger(GearEvents.MEDIACONTROL_GEAR_RENDERED);
135
157
  return this;
136
158
  }
137
159
 
@@ -21,7 +21,7 @@ import '../../../assets/clappr-nerd-stats/clappr-nerd-stats.scss';
21
21
  import pluginHtml from '../../../assets/clappr-nerd-stats/clappr-nerd-stats.ejs';
22
22
  import buttonHtml from '../../../assets/clappr-nerd-stats/button.ejs';
23
23
  import statsIcon from '../../../assets/icons/new/stats.svg';
24
- import { BottomGear, Events as BottomGearEvents } from '../bottom-gear/BottomGear.js';
24
+ import { BottomGear, GearEvents } from '../bottom-gear/BottomGear.js';
25
25
  import { MediaControl } from '../media-control/MediaControl.js';
26
26
  import assert from 'assert';
27
27
 
@@ -128,7 +128,7 @@ export class ClapprNerdStats extends UICorePlugin {
128
128
  private iconPosition: IconPosition;
129
129
 
130
130
  get name() {
131
- return 'media_control_nerd_stats';
131
+ return 'nerd_stats';
132
132
  }
133
133
 
134
134
  get supportedVersion() {
@@ -186,7 +186,7 @@ export class ClapprNerdStats extends UICorePlugin {
186
186
  const mediaControl = this.core.getPlugin('media_control') as MediaControl;
187
187
  assert(mediaControl, 'media_control plugin is required');
188
188
  this.listenToOnce(this.core, Events.CORE_READY, this.init);
189
- this.listenTo(mediaControl, BottomGearEvents.MEDIACONTROL_GEAR_RENDERED, this.addToBottomGear);
189
+ this.listenTo(mediaControl, GearEvents.MEDIACONTROL_GEAR_RENDERED, this.addToBottomGear);
190
190
  }
191
191
 
192
192
  private init() {
@@ -316,7 +316,7 @@ export class ClapprNerdStats extends UICorePlugin {
316
316
  }
317
317
 
318
318
  private addToBottomGear() {
319
- const gear = this.core.getPlugin('media_control_gear') as BottomGear;
319
+ const gear = this.core.getPlugin('bottom_gear') as BottomGear;
320
320
  const $el = gear.getElement('nerd')
321
321
  $el.html(buttonHtml)
322
322
  const $button = $el.find('.nerd-button');
@@ -5,6 +5,7 @@ import { type QualityLevel } from '../../playback.types.js'
5
5
  import { CLAPPR_VERSION } from '../../build.js'
6
6
  import { ZeptoResult } from '../../utils/types.js'
7
7
  import { TemplateFunction } from '../types.js'
8
+ import { BottomGear } from '../bottom-gear/BottomGear.js'
8
9
 
9
10
  import buttonHtml from '../../../assets/level-selector/button.ejs'
10
11
  import listHtml from '../../../assets/level-selector/list.ejs'
@@ -13,9 +14,10 @@ import arrowRightIcon from '../../../assets/icons/new/arrow-right.svg'
13
14
  import arrowLeftIcon from '../../../assets/icons/new/arrow-left.svg'
14
15
  import checkIcon from '../../../assets/icons/new/check.svg'
15
16
  import '../../../assets/level-selector/style.scss'
17
+ import assert from 'assert'
16
18
 
17
19
 
18
- const T = 'plugins.media_control_level_selector'
20
+ const T = 'plugins.level_selector'
19
21
  const VERSION = '2.19.4'
20
22
 
21
23
  /**
@@ -23,7 +25,14 @@ const VERSION = '2.19.4'
23
25
  * @beta
24
26
  *
25
27
  * @remarks
26
- * The plugin is rendered as a button in the {@link BottomGear | gear menu}.
28
+ * Depends on:
29
+ *
30
+ * - {@link MediaControl}
31
+ *
32
+ * - {@link BottomGear}
33
+ *
34
+ * The plugin is rendered as an item in the gear menu.
35
+ *
27
36
  * When clicked, it shows a list of quality levels to choose from.
28
37
  *
29
38
  * Configuration options:
@@ -53,15 +62,15 @@ export class LevelSelector extends UICorePlugin {
53
62
 
54
63
  private isOpen = false
55
64
 
56
- private buttonTemplate: TemplateFunction | null = null
65
+ private static readonly buttonTemplate: TemplateFunction = template(buttonHtml)
57
66
 
58
- private listTemplate: TemplateFunction | null = null
67
+ private static readonly listTemplate: TemplateFunction = template(listHtml)
59
68
 
60
69
  /**
61
70
  * @internal
62
71
  */
63
72
  get name() {
64
- return 'media_control_level_selector'
73
+ return 'level_selector'
65
74
  }
66
75
 
67
76
  /**
@@ -183,6 +192,8 @@ export class LevelSelector extends UICorePlugin {
183
192
  * @internal
184
193
  */
185
194
  override render() {
195
+ assert(this.core.getPlugin('bottom_gear'), 'bottom_gear plugin is required')
196
+
186
197
  if (!this.shouldRender()) {
187
198
  return this
188
199
  }
@@ -193,29 +204,21 @@ export class LevelSelector extends UICorePlugin {
193
204
  }
194
205
 
195
206
  private renderButton() {
196
- if (!this.buttonTemplate) {
197
- this.buttonTemplate = template(buttonHtml)
198
- }
199
207
  if (!this.isOpen) {
200
- const html = this.buttonTemplate?.({
208
+ const html = LevelSelector.buttonTemplate({
201
209
  arrowRightIcon,
202
210
  currentText: this.currentText,
203
211
  isHd: this.isHd,
204
212
  hdIcon,
205
213
  })
206
214
  this.$el.html(html)
207
- const mediaControl = this.core.getPlugin('media_control')
208
- mediaControl.getElement('bottomGear')
209
- ?.find('.gear-options-list [data-quality]')
210
- ?.html(this.el)
215
+ const gear = this.core.getPlugin('bottom_gear') as BottomGear
216
+ gear.getElement('quality')?.html(this.el)
211
217
  }
212
218
  }
213
219
 
214
220
  private renderDropdown() {
215
- if (!this.listTemplate) {
216
- this.listTemplate = template(listHtml)
217
- }
218
- const html = this.listTemplate!({
221
+ const html = LevelSelector.listTemplate({
219
222
  arrowLeftIcon,
220
223
  checkIcon,
221
224
  labels: this.levelLabels,
@@ -224,8 +227,8 @@ export class LevelSelector extends UICorePlugin {
224
227
  removeAuto: this.removeAuto,
225
228
  })
226
229
  this.$el.html(html)
227
- const mediaControl = this.core.getPlugin('media_control')
228
- mediaControl.getElement('bottomGear')?.find('.gear-wrapper').html(this.el)
230
+ const gear = this.core.getPlugin('bottom_gear') as BottomGear
231
+ gear?.setContent(this.el)
229
232
  }
230
233
 
231
234
  private get maxLevel() {