@gcorevideo/player 2.22.5 → 2.22.7

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 (40) hide show
  1. package/assets/media-control/media-control.ejs +1 -2
  2. package/dist/core.js +2 -2
  3. package/dist/index.css +1432 -1432
  4. package/dist/index.js +127 -91
  5. package/dist/plugins/index.css +1495 -1495
  6. package/dist/plugins/index.js +123 -86
  7. package/lib/playback/dash-playback/DashPlayback.d.ts.map +1 -1
  8. package/lib/playback/dash-playback/DashPlayback.js +1 -1
  9. package/lib/plugins/bottom-gear/BottomGear.d.ts.map +1 -1
  10. package/lib/plugins/bottom-gear/BottomGear.js +2 -2
  11. package/lib/plugins/media-control/MediaControl.d.ts +37 -6
  12. package/lib/plugins/media-control/MediaControl.d.ts.map +1 -1
  13. package/lib/plugins/media-control/MediaControl.js +54 -36
  14. package/lib/plugins/playback-rate/PlaybackRate.d.ts +5 -4
  15. package/lib/plugins/playback-rate/PlaybackRate.d.ts.map +1 -1
  16. package/lib/plugins/playback-rate/PlaybackRate.js +46 -24
  17. package/lib/plugins/subtitles/ClosedCaptions.js +1 -1
  18. package/lib/plugins/utils/fullscreen.d.ts +4 -0
  19. package/lib/plugins/utils/fullscreen.d.ts.map +1 -0
  20. package/lib/plugins/utils/fullscreen.js +30 -0
  21. package/lib/plugins/utils.d.ts +0 -1
  22. package/lib/plugins/utils.d.ts.map +1 -1
  23. package/lib/plugins/utils.js +0 -28
  24. package/lib/utils/fullscreen.d.ts +3 -0
  25. package/lib/utils/fullscreen.d.ts.map +1 -0
  26. package/lib/utils/fullscreen.js +2 -0
  27. package/package.json +1 -1
  28. package/src/playback/dash-playback/DashPlayback.ts +1 -4
  29. package/src/plugins/bottom-gear/BottomGear.ts +2 -2
  30. package/src/plugins/bottom-gear/__tests__/BottomGear.test.ts +15 -3
  31. package/src/plugins/media-control/MediaControl.ts +111 -62
  32. package/src/plugins/media-control/__tests__/MediaControl.test.ts +118 -8
  33. package/src/plugins/media-control/__tests__/__snapshots__/MediaControl.test.ts.snap +149 -5
  34. package/src/plugins/playback-rate/PlaybackRate.ts +48 -26
  35. package/src/plugins/playback-rate/__tests__/PlaybackRate.test.ts +125 -55
  36. package/src/plugins/playback-rate/__tests__/__snapshots__/PlaybackRate.test.ts.snap +1 -1
  37. package/src/plugins/subtitles/ClosedCaptions.ts +1 -1
  38. package/src/plugins/utils/fullscreen.ts +34 -0
  39. package/src/plugins/utils.ts +0 -31
  40. package/tsconfig.tsbuildinfo +1 -1
@@ -9,7 +9,8 @@ import { reportError, trace } from '@gcorevideo/utils';
9
9
  // TODO replace Kibo with mousetrap
10
10
  import { Kibo } from '../kibo/index.js';
11
11
  import { CLAPPR_VERSION } from '../../build.js';
12
- import { getPageX, isFullscreen } from '../utils.js';
12
+ import { getPageX } from '../utils.js';
13
+ import { fullscreenEnabled, isFullscreen } from '../utils/fullscreen.js';
13
14
  import '../../../assets/media-control/media-control.scss';
14
15
  import mediaControlHTML from '../../../assets/media-control/media-control.ejs';
15
16
  import playIcon from '../../../assets/icons/new/play.svg';
@@ -20,20 +21,29 @@ import volumeOffIcon from '../../../assets/icons/new/volume-off.svg';
20
21
  import fullscreenOffIcon from '../../../assets/icons/new/fullscreen-off.svg';
21
22
  import fullscreenOnIcon from '../../../assets/icons/new/fullscreen-on.svg';
22
23
  const DEFAULT_SETTINGS = {
23
- left: [],
24
+ default: [],
25
+ left: [
26
+ 'dvr'
27
+ ],
24
28
  right: [
29
+ 'audiotracks',
30
+ 'cc',
31
+ // 'dvr',
32
+ // 'duration',
25
33
  'fullscreen',
26
- 'pip',
27
34
  'gear',
28
- 'cc',
29
35
  'multicamera',
30
- // 'playbackrate',
36
+ 'pip',
31
37
  'vr',
32
- 'audiotracks',
33
38
  ],
34
- default: [],
35
39
  seekEnabled: true,
36
40
  };
41
+ const INITIAL_SETTINGS = {
42
+ left: [],
43
+ right: [],
44
+ default: [],
45
+ seekEnabled: false,
46
+ };
37
47
  const T = 'plugins.media_control';
38
48
  const LEFT_ORDER = [
39
49
  'playpause',
@@ -55,11 +65,17 @@ function orderByOrderPattern(arr, order) {
55
65
  * @beta
56
66
  * @remarks
57
67
  * The methods exposed are to be used by the other plugins that extend the media control UI.
68
+ *
69
+ * Configuration options:
70
+ *
71
+ * - `mediaControl`: {@link MediaControlSettings} - specifies the allowed media control elements in each area
72
+ *
73
+ * - `persistConfig`: boolean - `common` option, makes the plugin persist the media control settings
74
+ *
75
+ * - `chromeless`: boolean
58
76
  */
59
77
  export class MediaControl extends UICorePlugin {
60
78
  // private advertisementPlaying = false
61
- customAreaElements = {};
62
- customAreaHandler;
63
79
  buttonsColor = null;
64
80
  currentDurationValue = 0;
65
81
  currentPositionValue = 0;
@@ -70,7 +86,7 @@ export class MediaControl extends UICorePlugin {
70
86
  displayedSeekBarPercentage = null;
71
87
  draggingSeekBar = false;
72
88
  draggingVolumeBar = false;
73
- fullScreenOnVideoTagSupported = null;
89
+ fullScreenOnVideoTagSupported = false;
74
90
  hideId = null;
75
91
  hideVolumeId = null;
76
92
  intendedVolume = 100;
@@ -78,9 +94,10 @@ export class MediaControl extends UICorePlugin {
78
94
  kibo;
79
95
  lastMouseX = 0;
80
96
  lastMouseY = 0;
97
+ needsUpdate = false;
81
98
  persistConfig;
82
99
  rendered = false;
83
- settings = DEFAULT_SETTINGS;
100
+ settings = INITIAL_SETTINGS;
84
101
  userDisabled = false;
85
102
  userKeepVisible = false;
86
103
  verticalVolume = false;
@@ -91,7 +108,6 @@ export class MediaControl extends UICorePlugin {
91
108
  $multiCameraSelector = null;
92
109
  $playPauseToggle = null;
93
110
  $playStopToggle = null;
94
- $playbackRate = null;
95
111
  $position = null;
96
112
  $seekBarContainer = null;
97
113
  $seekBarHover = null;
@@ -305,7 +321,8 @@ export class MediaControl extends UICorePlugin {
305
321
  const video = this.core.activePlayback?.el;
306
322
  // video.webkitSupportsFullscreen is deprecated but iOS appears to only use this
307
323
  // see https://github.com/clappr/clappr/issues/1127
308
- if (!Fullscreen.fullscreenEnabled() && video.webkitSupportsFullscreen) {
324
+ if (!fullscreenEnabled() && video.webkitSupportsFullscreen) {
325
+ // TODO sort out, use single utility function
309
326
  this.fullScreenOnVideoTagSupported = true;
310
327
  }
311
328
  this.updateSettings();
@@ -537,22 +554,21 @@ export class MediaControl extends UICorePlugin {
537
554
  }
538
555
  }
539
556
  onActiveContainerChanged() {
540
- this.fullScreenOnVideoTagSupported = null;
557
+ this.fullScreenOnVideoTagSupported = false;
541
558
  // set the new container to match the volume of the last one
542
559
  this.setInitialVolume();
543
560
  this.changeTogglePlay();
544
561
  this.bindContainerEvents();
562
+ // TODO remove?
545
563
  this.updateSettings();
546
- // TODO remove
547
- this.core.activeContainer.trigger(Events.CONTAINER_PLAYBACKDVRSTATECHANGED, this.core.activeContainer.isDvrInUse());
548
- // TODO test
564
+ // TODO test, figure out if this is needed
549
565
  if (this.core.activeContainer.mediaControlDisabled) {
550
566
  this.disable();
551
567
  }
552
568
  else {
553
569
  this.enable();
554
570
  }
555
- this.trigger(Events.MEDIACONTROL_CONTAINERCHANGED); // TODO check
571
+ this.trigger(Events.MEDIACONTROL_CONTAINERCHANGED); // TODO figure out
556
572
  if (this.core.activeContainer.$el) {
557
573
  this.core.activeContainer.$el.addClass('container-skin-1');
558
574
  }
@@ -723,11 +739,13 @@ export class MediaControl extends UICorePlugin {
723
739
  }
724
740
  }
725
741
  updateSettings() {
742
+ trace(`${T} updateSettings`, { settings: this.settings });
726
743
  const newSettings = $.extend(true, {
727
744
  left: [],
728
745
  default: [],
729
746
  right: [],
730
747
  }, this.core.activeContainer.settings);
748
+ trace(`${T} updateSettings`, { newSettings });
731
749
  // TODO make order controlled via CSS
732
750
  newSettings.left = orderByOrderPattern([...newSettings.left, 'clipsText', 'volume'], LEFT_ORDER);
733
751
  if (this.core.activePlayback.getPlaybackType() === Playback.LIVE &&
@@ -735,10 +753,15 @@ export class MediaControl extends UICorePlugin {
735
753
  newSettings.left.push('dvr');
736
754
  }
737
755
  // actual order of the items appear rendered is controlled by CSS
738
- newSettings.right = DEFAULT_SETTINGS.right;
756
+ newSettings.right = DEFAULT_SETTINGS.right; // TODO get from the options
739
757
  if ((!this.fullScreenOnVideoTagSupported &&
740
- !Fullscreen.fullscreenEnabled()) ||
758
+ !fullscreenEnabled()) ||
741
759
  this.options.fullscreenDisable) {
760
+ trace(`${T} updateSettings removing fullscreen`, {
761
+ supported: this.fullScreenOnVideoTagSupported,
762
+ enabled: Fullscreen.fullscreenEnabled(),
763
+ optionsDisable: this.options.fullscreenDisable,
764
+ });
742
765
  // remove fullscreen from settings if it is not available
743
766
  removeArrayItem(newSettings.default, 'fullscreen');
744
767
  removeArrayItem(newSettings.left, 'fullscreen');
@@ -753,6 +776,7 @@ export class MediaControl extends UICorePlugin {
753
776
  const settingsChanged = serializeSettings(this.settings) !== serializeSettings(newSettings);
754
777
  if (settingsChanged) {
755
778
  this.settings = newSettings;
779
+ this.needsUpdate = true;
756
780
  this.render();
757
781
  }
758
782
  }
@@ -777,7 +801,6 @@ export class MediaControl extends UICorePlugin {
777
801
  this.$volumeBarBackground = this.$el.find('.bar-background[data-volume]');
778
802
  this.$volumeBarFill = this.$el.find('.bar-fill-1[data-volume]');
779
803
  this.$volumeBarScrubber = this.$el.find('.bar-scrubber[data-volume]');
780
- this.$playbackRate = this.$el.find('.media-control-playbackrate[data-playbackrate]');
781
804
  this.$multiCameraSelector = this.$el.find('.media-control-multicamera[data-multicamera]');
782
805
  this.$clipText = this.$el.find('.media-clip-text[data-clipstext]'); // TODO
783
806
  this.$clipTextContainer = this.$el.find('.media-clip-container[data-clipstext]');
@@ -809,8 +832,6 @@ export class MediaControl extends UICorePlugin {
809
832
  return null;
810
833
  case 'clipText':
811
834
  return this.$clipText;
812
- case 'playbackRate':
813
- return this.$playbackRate;
814
835
  case 'seekBarContainer':
815
836
  return this.$seekBarContainer;
816
837
  }
@@ -834,13 +855,6 @@ export class MediaControl extends UICorePlugin {
834
855
  return;
835
856
  }
836
857
  }
837
- handleCustomArea(handler) {
838
- this.customAreaHandler = handler;
839
- Object.entries(this.customAreaElements).forEach(([name, element]) => {
840
- handler(name, element);
841
- });
842
- this.customAreaElements = {};
843
- }
844
858
  /**
845
859
  * Toggle the visibility of a media control element
846
860
  * @param name - The name of the media control element
@@ -938,8 +952,8 @@ export class MediaControl extends UICorePlugin {
938
952
  keys.forEach((i) => {
939
953
  this.bindKeyAndShow(i, () => {
940
954
  this.settings.seekEnabled &&
941
- this.container &&
942
- this.container.seekPercentage(Number(i) * 10);
955
+ this.core.activeContainer &&
956
+ this.core.activeContainer.seekPercentage(Number(i) * 10);
943
957
  return false;
944
958
  });
945
959
  });
@@ -1005,10 +1019,12 @@ export class MediaControl extends UICorePlugin {
1005
1019
  * @internal
1006
1020
  */
1007
1021
  render() {
1008
- trace(`${T} render`);
1022
+ trace(`${T} render`, { needsUpdate: this.needsUpdate });
1023
+ if (!this.needsUpdate) {
1024
+ return this;
1025
+ }
1009
1026
  const timeout = this.options.hideMediaControlDelay || 2000;
1010
- const html = MediaControl.template({ settings: this.settings ?? {} });
1011
- this.$el.html(html);
1027
+ this.$el.html(MediaControl.template({ settings: this.settings }));
1012
1028
  // const style = Styler.getStyleFor(mediaControlStyle, { baseUrl: this.options.baseUrl });
1013
1029
  // this.$el.append(style[0]);
1014
1030
  this.createCachedElements();
@@ -1054,7 +1070,8 @@ export class MediaControl extends UICorePlugin {
1054
1070
  this.core.$el.append(this.el);
1055
1071
  this.rendered = true;
1056
1072
  this.updateVolumeUI();
1057
- // TODO setTimeout
1073
+ this.needsUpdate = false;
1074
+ // TODO setTimeout?
1058
1075
  this.trigger(Events.MEDIACONTROL_RENDERED);
1059
1076
  return this;
1060
1077
  }
@@ -1120,6 +1137,7 @@ export class MediaControl extends UICorePlugin {
1120
1137
  element.el.css({ 'pointer-events': 'none' });
1121
1138
  });
1122
1139
  }
1140
+ // TODO drop
1123
1141
  isSeekEnabledForHtml5Playback() {
1124
1142
  if (this.core.getPlaybackType() === Playback.LIVE) {
1125
1143
  return this.options.dvrEnabled;
@@ -40,12 +40,11 @@ export type PlaybackRateSettings = {
40
40
  * { value: 1, label: '1x' },
41
41
  * ],
42
42
  * defaultValue: 1,
43
- * } as PlaybackRateSettings,
43
+ * },
44
44
  * })
45
45
  * ```
46
46
  */
47
47
  export declare class PlaybackRate extends UICorePlugin {
48
- private playbackRates;
49
48
  private selectedRate;
50
49
  /**
51
50
  * @internal
@@ -60,6 +59,7 @@ export declare class PlaybackRate extends UICorePlugin {
60
59
  private static readonly buttonTemplate;
61
60
  private static readonly listTemplate;
62
61
  constructor(core: Core);
62
+ private get playbackRates();
63
63
  /**
64
64
  * @internal
65
65
  */
@@ -81,17 +81,18 @@ export declare class PlaybackRate extends UICorePlugin {
81
81
  private onActiveContainerChange;
82
82
  private onMediaControlRendered;
83
83
  private onGearRendered;
84
- private addGearItem;
84
+ private mount;
85
85
  private onMetaDataLoaded;
86
86
  private allRateElements;
87
87
  private rateElement;
88
88
  private onPlaybackRateChange;
89
- private shouldRender;
89
+ private shouldMount;
90
90
  /**
91
91
  * @internal
92
92
  */
93
93
  render(): this;
94
94
  private onPlay;
95
+ private syncRate;
95
96
  private resetPlaybackRate;
96
97
  private onStop;
97
98
  private onSelect;
@@ -1 +1 @@
1
- {"version":3,"file":"PlaybackRate.d.ts","sourceRoot":"","sources":["../../../src/plugins/playback-rate/PlaybackRate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,YAAY,EAAsB,IAAI,EAAK,MAAM,cAAc,CAAA;AAiBhF;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAED;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,OAAO,CAAC,EAAE,kBAAkB,EAAE,CAAA;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,CAAA;AAgBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,qBAAa,YAAa,SAAQ,YAAY;IAC5C,OAAO,CAAC,aAAa,CAA+C;IAKpE,OAAO,CAAC,YAAY,CAAwB;IAE5C;;OAEG;IACH,IAAI,IAAI,WAEP;IAED;;OAEG;IACH,IAAI,gBAAgB;;MAEnB;IAED,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAuB;IAE7D,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAqB;gBAE7C,IAAI,EAAE,IAAI;IAQtB;;OAEG;IACH,IAAa,UAAU;;MAItB;IAED;;OAEG;IACH,IAAa,MAAM;;;MAKlB;IAED;;OAEG;IACM,UAAU;IASnB,OAAO,CAAC,WAAW;IAenB,OAAO,CAAC,uBAAuB;IAgB/B,OAAO,CAAC,sBAAsB;IAK9B,OAAO,CAAC,cAAc;IAKtB,OAAO,CAAC,WAAW;IAiBnB,OAAO,CAAC,gBAAgB;IAKxB,OAAO,CAAC,eAAe;IAIvB,OAAO,CAAC,WAAW;IAMnB,OAAO,CAAC,oBAAoB;IAe5B,OAAO,CAAC,YAAY;IAepB;;OAEG;IACM,MAAM;IAwCf,OAAO,CAAC,MAAM;IAWd,OAAO,CAAC,iBAAiB;IAIzB,OAAO,CAAC,MAAM;IAEd,OAAO,CAAC,QAAQ;IAchB,OAAO,CAAC,MAAM;IAMd,OAAO,CAAC,eAAe;IAKvB,OAAO,CAAC,QAAQ;IAOhB,OAAO,CAAC,oBAAoB;IAU5B,OAAO,CAAC,qBAAqB;CAI9B"}
1
+ {"version":3,"file":"PlaybackRate.d.ts","sourceRoot":"","sources":["../../../src/plugins/playback-rate/PlaybackRate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,YAAY,EAAsB,IAAI,EAAK,MAAM,cAAc,CAAA;AAiBhF;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAED;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,OAAO,CAAC,EAAE,kBAAkB,EAAE,CAAA;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,CAAA;AAgBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,qBAAa,YAAa,SAAQ,YAAY;IAI5C,OAAO,CAAC,YAAY,CAAwB;IAE5C;;OAEG;IACH,IAAI,IAAI,WAEP;IAED;;OAEG;IACH,IAAI,gBAAgB;;MAEnB;IAED,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAuB;IAE7D,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAqB;gBAE7C,IAAI,EAAE,IAAI;IAOtB,OAAO,KAAK,aAAa,GAExB;IAED;;OAEG;IACH,IAAa,UAAU;;MAItB;IAED;;OAEG;IACH,IAAa,MAAM;;;MAKlB;IAED;;OAEG;IACM,UAAU;IASnB,OAAO,CAAC,WAAW;IAenB,OAAO,CAAC,uBAAuB;IAgB/B,OAAO,CAAC,sBAAsB;IAK9B,OAAO,CAAC,cAAc;IAKtB,OAAO,CAAC,KAAK;IAsBb,OAAO,CAAC,gBAAgB;IAKxB,OAAO,CAAC,eAAe;IAIvB,OAAO,CAAC,WAAW;IAMnB,OAAO,CAAC,oBAAoB;IAe5B,OAAO,CAAC,WAAW;IAenB;;OAEG;IACM,MAAM;IAoCf,OAAO,CAAC,MAAM;IAWd,OAAO,CAAC,QAAQ;IAOhB,OAAO,CAAC,iBAAiB;IAQzB,OAAO,CAAC,MAAM;IAEd,OAAO,CAAC,QAAQ;IAYhB,OAAO,CAAC,MAAM;IAMd,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,QAAQ;IAQhB,OAAO,CAAC,oBAAoB;IAa5B,OAAO,CAAC,qBAAqB;CAM9B"}
@@ -48,12 +48,11 @@ const T = 'plugins.playback_rate';
48
48
  * { value: 1, label: '1x' },
49
49
  * ],
50
50
  * defaultValue: 1,
51
- * } as PlaybackRateSettings,
51
+ * },
52
52
  * })
53
53
  * ```
54
54
  */
55
55
  export class PlaybackRate extends UICorePlugin {
56
- playbackRates = DEFAULT_PLAYBACK_RATES;
57
56
  // Saved when an ad starts to restore after it finishes
58
57
  // private prevSelectedRate: string | undefined
59
58
  selectedRate = DEFAULT_PLAYBACK_RATE;
@@ -73,10 +72,12 @@ export class PlaybackRate extends UICorePlugin {
73
72
  static listTemplate = template(listHtml);
74
73
  constructor(core) {
75
74
  super(core);
76
- this.playbackRates =
77
- core.options.playbackRate?.options || DEFAULT_PLAYBACK_RATES;
78
- this.selectedRate =
79
- core.options.playbackRate?.defaultValue || DEFAULT_PLAYBACK_RATE;
75
+ if (this.core.options.playbackRate?.defaultValue) {
76
+ this.setSelectedRate(this.core.options.playbackRate.defaultValue);
77
+ }
78
+ }
79
+ get playbackRates() {
80
+ return this.core.options.playbackRate?.options || DEFAULT_PLAYBACK_RATES;
80
81
  }
81
82
  /**
82
83
  * @internal
@@ -124,10 +125,15 @@ export class PlaybackRate extends UICorePlugin {
124
125
  }
125
126
  onGearRendered() {
126
127
  trace(`${T} onGearRendered`);
127
- this.addGearItem();
128
+ this.mount();
128
129
  }
129
- addGearItem() {
130
- trace(`${T} addGearItem`);
130
+ mount() {
131
+ trace(`${T} mount`, {
132
+ shouldMount: this.shouldMount(),
133
+ });
134
+ if (!this.shouldMount()) {
135
+ return;
136
+ }
131
137
  this.core
132
138
  .getPlugin('bottom_gear')
133
139
  ?.addItem('rate', this.$el)
@@ -160,7 +166,7 @@ export class PlaybackRate extends UICorePlugin {
160
166
  });
161
167
  }
162
168
  }
163
- shouldRender() {
169
+ shouldMount() {
164
170
  if (!this.core.activePlayback) {
165
171
  return false;
166
172
  }
@@ -175,11 +181,8 @@ export class PlaybackRate extends UICorePlugin {
175
181
  */
176
182
  render() {
177
183
  trace(`${T} render`, {
178
- shouldRender: this.shouldRender(),
184
+ shouldMount: this.shouldMount(),
179
185
  });
180
- if (!this.shouldRender()) {
181
- return this;
182
- }
183
186
  this.$el.html(PlaybackRate.listTemplate({
184
187
  arrowLeftIcon,
185
188
  checkIcon,
@@ -187,7 +190,7 @@ export class PlaybackRate extends UICorePlugin {
187
190
  i18n: this.core.i18n,
188
191
  playbackRates: this.playbackRates,
189
192
  }));
190
- this.addGearItem();
193
+ this.mount();
191
194
  return this;
192
195
  }
193
196
  // private onStartAd() {
@@ -210,11 +213,21 @@ export class PlaybackRate extends UICorePlugin {
210
213
  this.resetPlaybackRate();
211
214
  }
212
215
  else {
213
- this.setSelectedRate(this.selectedRate);
216
+ this.syncRate();
214
217
  }
215
218
  }
219
+ syncRate() {
220
+ trace(`${T} syncRate`, {
221
+ selectedRate: this.selectedRate,
222
+ });
223
+ this.core.activePlayback?.setPlaybackRate(this.selectedRate);
224
+ }
216
225
  resetPlaybackRate() {
217
- this.setSelectedRate(DEFAULT_PLAYBACK_RATE);
226
+ trace(`${T} resetPlaybackRate`, {
227
+ selectedRate: this.selectedRate,
228
+ });
229
+ this.core.activePlayback?.setPlaybackRate(DEFAULT_PLAYBACK_RATE);
230
+ this.selectedRate = DEFAULT_PLAYBACK_RATE;
218
231
  }
219
232
  onStop() { }
220
233
  onSelect(event) {
@@ -222,8 +235,6 @@ export class PlaybackRate extends UICorePlugin {
222
235
  const rate = parseFloat(event.currentTarget.dataset.rate || '');
223
236
  if (rate) {
224
237
  this.setSelectedRate(rate);
225
- this.highlightCurrentRate();
226
- this.updateGearOptionLabel();
227
238
  }
228
239
  return false;
229
240
  }
@@ -233,14 +244,23 @@ export class PlaybackRate extends UICorePlugin {
233
244
  }, 0);
234
245
  }
235
246
  setSelectedRate(rate) {
236
- this.core.activePlayback?.setPlaybackRate(rate);
247
+ if (rate === this.selectedRate) {
248
+ return;
249
+ }
237
250
  this.selectedRate = rate;
251
+ this.syncRate();
252
+ this.highlightCurrentRate();
253
+ this.updateGearOptionLabel();
238
254
  }
239
255
  getTitle() {
240
- return (this.playbackRates.find((r) => r.value === this.selectedRate)?.label ||
241
- `x${this.selectedRate}`);
256
+ const rate = this.selectedRate;
257
+ return (this.playbackRates.find((r) => r.value === rate)?.label ||
258
+ `x${rate}`);
242
259
  }
243
260
  highlightCurrentRate() {
261
+ trace(`${T} highlightCurrentRate`, {
262
+ selectedRate: this.selectedRate,
263
+ });
244
264
  this.allRateElements().removeClass('current');
245
265
  this.allRateElements().find('a').removeClass('gcore-skin-active');
246
266
  this.rateElement(this.selectedRate)
@@ -249,7 +269,9 @@ export class PlaybackRate extends UICorePlugin {
249
269
  .addClass('gcore-skin-active');
250
270
  }
251
271
  updateGearOptionLabel() {
252
- trace(`${T} updateGearOptionLabel`);
253
- this.addGearItem();
272
+ trace(`${T} updateGearOptionLabel`, {
273
+ selectedRate: this.selectedRate,
274
+ });
275
+ this.mount();
254
276
  }
255
277
  }
@@ -7,7 +7,7 @@ import subtitlesOffIcon from '../../../assets/icons/new/subtitles-off.svg';
7
7
  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
- import { isFullscreen } from '../utils.js';
10
+ import { isFullscreen } from '../utils/fullscreen.js';
11
11
  const VERSION = '2.19.14';
12
12
  const LOCAL_STORAGE_CC_ID = 'gplayer.plugins.cc.selected';
13
13
  const T = 'plugins.cc';
@@ -0,0 +1,4 @@
1
+ import { Utils } from '@clappr/core';
2
+ export declare const fullscreenEnabled: typeof Utils.Fullscreen.fullscreenEnabled;
3
+ export declare function isFullscreen(el: HTMLElement): boolean;
4
+ //# sourceMappingURL=fullscreen.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fullscreen.d.ts","sourceRoot":"","sources":["../../../src/plugins/utils/fullscreen.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,KAAK,EAAE,MAAM,cAAc,CAAC;AAG9C,eAAO,MAAM,iBAAiB,2CAAqC,CAAA;AAEnE,wBAAgB,YAAY,CAAC,EAAE,EAAE,WAAW,GAAG,OAAO,CAYrD"}
@@ -0,0 +1,30 @@
1
+ import { Browser, Utils } from '@clappr/core';
2
+ import { reportError } from '@gcorevideo/utils';
3
+ export const fullscreenEnabled = Utils.Fullscreen.fullscreenEnabled;
4
+ export function isFullscreen(el) {
5
+ const video = el.nodeName === 'video'
6
+ ? el
7
+ : el.querySelector('video');
8
+ if (!video) {
9
+ return false;
10
+ }
11
+ if (Browser.isiOS) {
12
+ return FullscreenIOS.isFullscreen(video);
13
+ }
14
+ return !!document.fullscreenElement;
15
+ }
16
+ const FullscreenIOS = {
17
+ isFullscreen: function (el) {
18
+ try {
19
+ // @ts-ignore
20
+ if (el.webkitDisplayingFullscreen !== undefined) {
21
+ // @ts-ignore
22
+ return !!el.webkitDisplayingFullscreen;
23
+ }
24
+ }
25
+ catch (e) {
26
+ reportError(e);
27
+ }
28
+ return false;
29
+ },
30
+ };
@@ -1,5 +1,4 @@
1
1
  export declare function getLocation(href: string): HTMLAnchorElement;
2
2
  export declare function strtimeToMiliseconds(str: string): number;
3
- export declare function isFullscreen(el: HTMLElement): boolean;
4
3
  export declare function getPageX(event: MouseEvent | TouchEvent): number;
5
4
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/plugins/utils.ts"],"names":[],"mappings":"AAGA,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,qBAMvC;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAyBxD;AAGD,wBAAgB,YAAY,CAAC,EAAE,EAAE,WAAW,GAAG,OAAO,CASrD;AAkBD,wBAAgB,QAAQ,CAAC,KAAK,EAAE,UAAU,GAAG,UAAU,GAAG,MAAM,CAU/D"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/plugins/utils.ts"],"names":[],"mappings":"AAAA,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,qBAMvC;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAyBxD;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,UAAU,GAAG,UAAU,GAAG,MAAM,CAU/D"}
@@ -1,5 +1,3 @@
1
- import { Browser } from '@clappr/core';
2
- import { reportError } from '@gcorevideo/utils';
3
1
  export function getLocation(href) {
4
2
  const l = document.createElement('a');
5
3
  l.href = href;
@@ -31,32 +29,6 @@ export function strtimeToMiliseconds(str) {
31
29
  }
32
30
  return (h + m + s);
33
31
  }
34
- // TODO refactor
35
- export function isFullscreen(el) {
36
- const video = el.nodeName === "video" ? el : el.querySelector('video');
37
- if (!video) {
38
- return false;
39
- }
40
- if (Browser.isiOS) {
41
- return FullscreenIOS.isFullscreen(video);
42
- }
43
- return !!(document.fullscreenElement);
44
- }
45
- const FullscreenIOS = {
46
- isFullscreen: function (el) {
47
- try {
48
- // @ts-ignore
49
- if (el.webkitDisplayingFullscreen !== undefined) {
50
- // @ts-ignore
51
- return !!(el.webkitDisplayingFullscreen);
52
- }
53
- }
54
- catch (e) {
55
- reportError(e);
56
- }
57
- return false;
58
- }
59
- };
60
32
  export function getPageX(event) {
61
33
  if (event.pageX) {
62
34
  return event.pageX;
@@ -0,0 +1,3 @@
1
+ import { Utils } from '@clappr/core';
2
+ export declare const fullscreenEnabled: typeof Utils.Fullscreen.fullscreenEnabled;
3
+ //# sourceMappingURL=fullscreen.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fullscreen.d.ts","sourceRoot":"","sources":["../../src/utils/fullscreen.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAA;AAEpC,eAAO,MAAM,iBAAiB,2CAAqC,CAAA"}
@@ -0,0 +1,2 @@
1
+ import { Utils } from '@clappr/core';
2
+ export const fullscreenEnabled = Utils.Fullscreen.fullscreenEnabled;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gcorevideo/player",
3
- "version": "2.22.5",
3
+ "version": "2.22.7",
4
4
  "description": "Gcore JavaScript video player",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -604,11 +604,8 @@ export default class DashPlayback extends BasePlayback {
604
604
  }
605
605
 
606
606
  _updatePlaybackType() {
607
- assert.ok(
608
- this._dash,
609
- 'An instance of dashjs MediaPlayer is required to update the playback type',
610
- )
611
607
  const prevPlaybackType = this._playbackType
608
+ // @ts-ignore
612
609
  this._playbackType = this._dash.isDynamic() ? Playback.LIVE : Playback.VOD
613
610
  if (prevPlaybackType !== this._playbackType) {
614
611
  this._updateSettings()
@@ -235,7 +235,6 @@ export class BottomGear extends UICorePlugin {
235
235
  .hide()
236
236
 
237
237
  // TODO make non-clickable when there are no items
238
- mediaControl.putElement('gear', this.$el)
239
238
 
240
239
  setTimeout(() => {
241
240
  this.trigger(GearEvents.RENDERED)
@@ -276,6 +275,7 @@ export class BottomGear extends UICorePlugin {
276
275
 
277
276
  private onMediaControlRendered() {
278
277
  trace(`${T} onMediaControlRendered`)
279
- this.render()
278
+ const mediaControl = this.core.getPlugin('media_control')
279
+ mediaControl.putElement('gear', this.$el)
280
280
  }
281
281
  }
@@ -2,6 +2,7 @@ import { MockedFunction, beforeEach, describe, expect, it, vi } from 'vitest'
2
2
 
3
3
  import { BottomGear, GearEvents } from '../BottomGear'
4
4
  import { createMockCore, createMockMediaControl } from '../../../testUtils'
5
+ import { Events } from '@clappr/core'
5
6
 
6
7
  describe('BottomGear', () => {
7
8
  let mediaControl: any
@@ -20,13 +21,11 @@ describe('BottomGear', () => {
20
21
  onGearRendered = vi.fn()
21
22
  bottomGear.on(GearEvents.RENDERED, onGearRendered, null)
22
23
  bottomGear.render()
24
+ core.emit(Events.CORE_READY)
23
25
  })
24
26
  it('should render', () => {
25
27
  expect(bottomGear.el.innerHTML).toMatchSnapshot()
26
28
  })
27
- it('should attach to media control', () => {
28
- expect(mediaControl.putElement).toHaveBeenCalledWith('gear', bottomGear.$el)
29
- })
30
29
  it('should emit event in the next cycle', async () => {
31
30
  expect(onGearRendered).not.toHaveBeenCalled()
32
31
  await new Promise((resolve) => setTimeout(resolve, 0))
@@ -37,6 +36,19 @@ describe('BottomGear', () => {
37
36
  'none',
38
37
  )
39
38
  })
39
+ describe('until media control is rendered', () => {
40
+ it('should not attach to media control', () => {
41
+ expect(mediaControl.putElement).not.toHaveBeenCalledWith('gear', expect.anything())
42
+ })
43
+ })
44
+ describe('when media control is rendered', () => {
45
+ beforeEach(() => {
46
+ mediaControl.trigger(Events.MEDIACONTROL_RENDERED)
47
+ })
48
+ it('should attach to media control', () => {
49
+ expect(mediaControl.putElement).toHaveBeenCalledWith('gear', bottomGear.$el)
50
+ })
51
+ })
40
52
  describe('when clicked', () => {
41
53
  beforeEach(() => {
42
54
  bottomGear.$el.find('#gear-button').click()