@gcorevideo/player 2.22.30 → 2.23.0

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 (69) hide show
  1. package/assets/media-control/container.scss +2 -3
  2. package/assets/poster/poster.ejs +3 -1
  3. package/assets/poster/poster.scss +3 -3
  4. package/assets/style/main.scss +1 -1
  5. package/assets/thumbnails/scrub-thumbnails.ejs +5 -10
  6. package/assets/thumbnails/style.scss +4 -5
  7. package/dist/core.js +1 -1
  8. package/dist/index.css +533 -532
  9. package/dist/index.js +273 -377
  10. package/dist/player.d.ts +63 -33
  11. package/docs/api/{player.seektime.bindevents.md → player.clapprstats.clearmetrics.md} +3 -3
  12. package/docs/api/player.clapprstats.md +14 -0
  13. package/docs/api/player.extendedevents.md +14 -0
  14. package/docs/api/player.md +13 -2
  15. package/docs/api/player.seektime.attributes.md +0 -1
  16. package/docs/api/player.seektime.md +6 -197
  17. package/docs/api/{player.seektime.render.md → player.seektimesettings.md} +7 -7
  18. package/docs/api/player.skiptime.md +3 -184
  19. package/lib/plugins/clips/Clips.d.ts +7 -0
  20. package/lib/plugins/clips/Clips.d.ts.map +1 -1
  21. package/lib/plugins/clips/Clips.js +8 -0
  22. package/lib/plugins/media-control/MediaControl.d.ts +1 -7
  23. package/lib/plugins/media-control/MediaControl.d.ts.map +1 -1
  24. package/lib/plugins/media-control/MediaControl.js +9 -18
  25. package/lib/plugins/poster/Poster.d.ts +24 -14
  26. package/lib/plugins/poster/Poster.d.ts.map +1 -1
  27. package/lib/plugins/poster/Poster.js +67 -97
  28. package/lib/plugins/thumbnails/Thumbnails.d.ts +36 -33
  29. package/lib/plugins/thumbnails/Thumbnails.d.ts.map +1 -1
  30. package/lib/plugins/thumbnails/Thumbnails.js +174 -259
  31. package/lib/plugins/thumbnails/utils.d.ts +5 -0
  32. package/lib/plugins/thumbnails/utils.d.ts.map +1 -0
  33. package/lib/plugins/thumbnails/utils.js +12 -0
  34. package/lib/testUtils.d.ts +13 -39
  35. package/lib/testUtils.d.ts.map +1 -1
  36. package/lib/testUtils.js +15 -67
  37. package/package.json +2 -1
  38. package/src/plugins/clips/Clips.ts +10 -1
  39. package/src/plugins/media-control/MediaControl.ts +10 -21
  40. package/src/plugins/media-control/__tests__/__snapshots__/MediaControl.test.ts.snap +1 -1
  41. package/src/plugins/poster/Poster.ts +91 -110
  42. package/src/plugins/poster/__tests__/Poster.test.ts +119 -0
  43. package/src/plugins/poster/__tests__/__snapshots__/Poster.test.ts.snap +8 -0
  44. package/src/plugins/source-controller/__tests__/SourceController.test.ts +1 -2
  45. package/src/plugins/thumbnails/Thumbnails.ts +228 -330
  46. package/src/plugins/thumbnails/__tests__/Thumbnails.test.ts +72 -0
  47. package/src/plugins/thumbnails/__tests__/__snapshots__/Thumbnails.test.ts.snap +10 -0
  48. package/src/plugins/thumbnails/utils.ts +12 -0
  49. package/src/testUtils.ts +15 -88
  50. package/temp/player.api.json +295 -829
  51. package/tsconfig.tsbuildinfo +1 -1
  52. package/docs/api/player.seektime.durationshown.md +0 -14
  53. package/docs/api/player.seektime.getseektime.md +0 -20
  54. package/docs/api/player.seektime.islivestreamwithdvr.md +0 -14
  55. package/docs/api/player.seektime.mediacontrol.md +0 -14
  56. package/docs/api/player.seektime.mediacontrolcontainer.md +0 -14
  57. package/docs/api/player.seektime.shouldbevisible.md +0 -18
  58. package/docs/api/player.seektime.template.md +0 -14
  59. package/docs/api/player.seektime.update.md +0 -18
  60. package/docs/api/player.skiptime.attributes.md +0 -17
  61. package/docs/api/player.skiptime.bindevents.md +0 -18
  62. package/docs/api/player.skiptime.events.md +0 -18
  63. package/docs/api/player.skiptime.handlerewindclicks.md +0 -18
  64. package/docs/api/player.skiptime.render.md +0 -18
  65. package/docs/api/player.skiptime.setback.md +0 -18
  66. package/docs/api/player.skiptime.setforward.md +0 -18
  67. package/docs/api/player.skiptime.setmidclick.md +0 -18
  68. package/docs/api/player.skiptime.template.md +0 -14
  69. package/docs/api/player.skiptime.togglefullscreen.md +0 -18
package/dist/index.js CHANGED
@@ -43303,7 +43303,7 @@ class Player {
43303
43303
  }
43304
43304
  }
43305
43305
 
43306
- var version$1 = "2.22.30";
43306
+ var version$1 = "2.23.0";
43307
43307
 
43308
43308
  var packages = {
43309
43309
  "node_modules/@clappr/core": {
@@ -43727,8 +43727,8 @@ class MediaControl extends UICorePlugin {
43727
43727
  return this.userDisabled || playbackIsNOOP;
43728
43728
  }
43729
43729
  /**
43730
+ * Use in mediacontrol-based plugins to access the active container
43730
43731
  * @internal
43731
- * @deprecated Use core.activeContainer directly
43732
43732
  */
43733
43733
  get container() {
43734
43734
  return this.core.activeContainer;
@@ -43995,16 +43995,16 @@ class MediaControl extends UICorePlugin {
43995
43995
  this.applyButtonStyle(this.$playStopToggle);
43996
43996
  }
43997
43997
  mousemoveOnSeekBar(event) {
43998
+ const offset = MediaControl.getPageX(event) -
43999
+ (this.$seekBarContainer.offset().left ?? 0); // TODO check if the result can be negative
44000
+ const hoverOffset = offset -
44001
+ (this.$seekBarHover.width() ?? 0) / 2;
44002
+ const pos = offset ? Math.min(1, Math.max(offset / this.$seekBarContainer.width(), 0)) : 0;
43998
44003
  if (this.settings.seekEnabled) {
43999
- // assert.ok(this.$seekBarHover && this.$seekBarContainer, 'seek bar elements must be present');
44000
- if (this.$seekBarHover && this.$seekBarContainer) {
44001
- const offsetX = MediaControl.getPageX(event) -
44002
- this.$seekBarContainer.offset().left -
44003
- this.$seekBarHover.width() / 2;
44004
- this.$seekBarHover.css({ left: offsetX });
44005
- }
44004
+ // TODO test that it works when the element does not exist
44005
+ this.$seekBarHover.css({ left: hoverOffset });
44006
44006
  }
44007
- this.trigger(Events$1.MEDIACONTROL_MOUSEMOVE_SEEKBAR, event);
44007
+ this.trigger(Events$1.MEDIACONTROL_MOUSEMOVE_SEEKBAR, event, pos);
44008
44008
  }
44009
44009
  mouseleaveOnSeekBar(event) {
44010
44010
  this.trigger(Events$1.MEDIACONTROL_MOUSELEAVE_SEEKBAR, event);
@@ -44428,17 +44428,8 @@ class MediaControl extends UICorePlugin {
44428
44428
  else {
44429
44429
  panel.append(element);
44430
44430
  }
44431
- return;
44432
44431
  }
44433
44432
  }
44434
- /**
44435
- * @deprecated Use {@link MediaControl.mount} instead
44436
- * @param name
44437
- * @param element
44438
- */
44439
- putElement(name, element) {
44440
- this.mount(name, element);
44441
- }
44442
44433
  /**
44443
44434
  * Toggle the visibility of a media control element
44444
44435
  * @param name - The name of the media control element
@@ -48377,6 +48368,14 @@ class Clips extends UICorePlugin {
48377
48368
  this.render();
48378
48369
  return super.enable();
48379
48370
  }
48371
+ /**
48372
+ * Get the text of the clip at the given time
48373
+ * @param time - The time to get the text for
48374
+ * @returns The text of the clip at the given time
48375
+ */
48376
+ getText(time) {
48377
+ return this.clips.find((clip) => clip.start <= time && clip.end >= time)?.text;
48378
+ }
48380
48379
  onCoreReady() {
48381
48380
  trace(`${T$d} onCoreReady`);
48382
48381
  const mediaControl = this.core.getPlugin('media_control');
@@ -50001,7 +50000,7 @@ class PlaybackRate extends UICorePlugin {
50001
50000
  }
50002
50001
  }
50003
50002
 
50004
- const posterHTML = "<div class=\"play-wrapper\" data-poster></div>\n";
50003
+ const posterHTML = "<div class=\"play-wrapper\" id=\"gplayer-poster\">\n <div class='circle-poster gcore-skin-button-color gcore-skin-border-color' id='poster-play'></div>\n</div>\n";
50005
50004
 
50006
50005
  //Copyright 2014 Globo.com Player authors. All rights reserved.
50007
50006
  // Use of this source code is governed by a BSD-style
@@ -50011,19 +50010,13 @@ const T$8 = 'plugins.poster';
50011
50010
  * `PLUGIN` that displays a poster image in the background and a big play button on top when playback is stopped
50012
50011
  * @beta
50013
50012
  * @remarks
50014
- * When the playback is stopped, media control UI is disabled.
50013
+ * When the playback is stopped or not yet started, the media control UI is disabled and hidden.
50014
+ * Media control gets activated once the metadata is loaded after playback is initiated.
50015
+ * This plugin displays a big play button on top of the poster image to allow user to start playback.
50015
50016
  * Note that the poster image, if specified via the player config, will be used to update video element's poster attribute by the
50016
50017
  * HTML5-video-based playback module.
50017
50018
  *
50018
- * Configuration options:
50019
- *
50020
- * - `poster.custom` - custom CSS background
50021
- *
50022
- * - `poster.showForNoOp` - whether to show the poster when the playback is not started
50023
- *
50024
- * - `poster.url` - the URL of the poster image
50025
- *
50026
- * - `poster.showOnVideoEnd` - whether to show the poster when the playback is ended
50019
+ * Configuration options - {@link PosterPluginSettings}
50027
50020
  *
50028
50021
  * @example
50029
50022
  * ```ts
@@ -50039,10 +50032,9 @@ const T$8 = 'plugins.poster';
50039
50032
  class Poster extends UIContainerPlugin {
50040
50033
  // TODO merge non-poster related functionality into the ClickToPause plugin
50041
50034
  hasFatalError = false;
50042
- hasStartedPlaying = false;
50035
+ playing = false;
50043
50036
  playRequested = false;
50044
50037
  $playButton = null;
50045
- $playWrapper = null;
50046
50038
  /**
50047
50039
  * @internal
50048
50040
  */
@@ -50062,8 +50054,10 @@ class Poster extends UIContainerPlugin {
50062
50054
  }
50063
50055
  const showForNoOp = !!this.options.poster?.showForNoOp;
50064
50056
  return (this.container.playback.name !== 'html_img' &&
50065
- (this.container.playback.getPlaybackType() !== Playback.NO_OP ||
50066
- showForNoOp));
50057
+ (!this.isNoOp || showForNoOp));
50058
+ }
50059
+ get isNoOp() {
50060
+ return this.container.playback.getPlaybackType() === Playback.NO_OP;
50067
50061
  }
50068
50062
  /**
50069
50063
  * @internal
@@ -50071,7 +50065,6 @@ class Poster extends UIContainerPlugin {
50071
50065
  get attributes() {
50072
50066
  return {
50073
50067
  class: 'player-poster',
50074
- 'data-poster': '',
50075
50068
  };
50076
50069
  }
50077
50070
  /**
@@ -50082,9 +50075,6 @@ class Poster extends UIContainerPlugin {
50082
50075
  click: 'clicked',
50083
50076
  };
50084
50077
  }
50085
- get showOnVideoEnd() {
50086
- return this.options.poster?.showOnVideoEnd !== false;
50087
- }
50088
50078
  /**
50089
50079
  * @internal
50090
50080
  */
@@ -50093,19 +50083,22 @@ class Poster extends UIContainerPlugin {
50093
50083
  this.listenTo(this.container, Events$1.CONTAINER_PLAY, this.onPlay);
50094
50084
  this.listenTo(this.container, Events$1.CONTAINER_STATE_BUFFERING, this.update);
50095
50085
  this.listenTo(this.container, Events$1.CONTAINER_STATE_BUFFERFULL, this.update);
50096
- this.listenTo(this.container, Events$1.CONTAINER_OPTIONS_CHANGE, this.render);
50086
+ this.listenTo(this.container, Events$1.CONTAINER_OPTIONS_CHANGE, this.update);
50097
50087
  this.listenTo(this.container, Events$1.CONTAINER_ERROR, this.onError);
50098
- this.showOnVideoEnd &&
50088
+ // TODO check if this event is always accompanied with the CONTAINER_STOP
50089
+ if (this.options.poster?.showOnVideoEnd !== false) {
50099
50090
  this.listenTo(this.container, Events$1.CONTAINER_ENDED, this.onStop);
50091
+ }
50100
50092
  this.listenTo(this.container, Events$1.CONTAINER_READY, this.render);
50101
- this.listenTo(this.container, Events$1.PLAYBACK_PLAY_INTENT, this.onPlayIntent);
50093
+ this.listenTo(this.container.playback, Events$1.PLAYBACK_PLAY_INTENT, this.onPlayIntent);
50102
50094
  }
50103
50095
  /**
50104
50096
  * Reenables earlier disabled plugin
50105
50097
  */
50106
50098
  enable() {
50099
+ trace(`${T$8} enable`);
50107
50100
  super.enable();
50108
- this.hasStartedPlaying = this.container.playback.isPlaying();
50101
+ this.playing = this.container.playback.isPlaying();
50109
50102
  this.update();
50110
50103
  }
50111
50104
  /**
@@ -50113,7 +50106,7 @@ class Poster extends UIContainerPlugin {
50113
50106
  */
50114
50107
  disable() {
50115
50108
  trace(`${T$8} disable`);
50116
- this.hasStartedPlaying = false;
50109
+ this.playing = false;
50117
50110
  this.playRequested = false;
50118
50111
  super.disable();
50119
50112
  }
@@ -50122,17 +50115,15 @@ class Poster extends UIContainerPlugin {
50122
50115
  error,
50123
50116
  enabled: this.enabled,
50124
50117
  });
50125
- this.hasFatalError = error.level === PlayerError.Levels.FATAL;
50126
50118
  if (this.hasFatalError) {
50127
- this.hasStartedPlaying = false;
50128
- if (!this.playRequested) {
50129
- this.showPlayButton();
50130
- }
50119
+ return;
50131
50120
  }
50121
+ this.hasFatalError = error.level === PlayerError.Levels.FATAL;
50122
+ // this.hasFatalError is reset on container recreate
50132
50123
  }
50133
50124
  onPlay() {
50134
50125
  trace(`${T$8} onPlay`);
50135
- this.hasStartedPlaying = true;
50126
+ this.playing = true;
50136
50127
  this.playRequested = false;
50137
50128
  this.update();
50138
50129
  }
@@ -50142,21 +50133,21 @@ class Poster extends UIContainerPlugin {
50142
50133
  this.update();
50143
50134
  }
50144
50135
  onStop() {
50145
- trace(`${T$8} onStop`, {
50146
- enabled: this.enabled,
50147
- });
50148
- this.hasStartedPlaying = false;
50136
+ trace(`${T$8} onStop`);
50137
+ this.playing = false;
50149
50138
  this.playRequested = false;
50150
50139
  this.update();
50151
50140
  }
50152
- updatePlayButton(show) {
50153
- trace(`${T$8} updatePlayButton`, {
50154
- show,
50155
- chromeless: this.options.chromeless,
50156
- allowUserInteraction: this.options.allowUserInteraction,
50157
- });
50158
- if (show &&
50159
- (!this.options.chromeless || this.options.allowUserInteraction)) {
50141
+ updatePlayButton() {
50142
+ trace(`${T$8} updatePlayButton`);
50143
+ const show = !this.isNoOp &&
50144
+ !(this.options.chromeless && !this.options.allowUserInteraction) &&
50145
+ !this.playRequested &&
50146
+ !this.playing &&
50147
+ !this.container.buffering &&
50148
+ !this.hasFatalError &&
50149
+ !this.options.disableMediaControl;
50150
+ if (show) {
50160
50151
  this.showPlayButton();
50161
50152
  }
50162
50153
  else {
@@ -50164,59 +50155,42 @@ class Poster extends UIContainerPlugin {
50164
50155
  }
50165
50156
  }
50166
50157
  showPlayButton() {
50167
- if (this.options.disableMediaControl) {
50168
- return;
50169
- }
50170
- if (this.hasFatalError && !this.options.disableErrorScreen) {
50171
- return;
50172
- }
50173
- this.$playButton?.show();
50158
+ trace(`${T$8} showPlayButton`);
50159
+ this.$el.find('#poster-play').show();
50174
50160
  this.$el.addClass('clickable');
50175
50161
  this.container.$el.addClass('container-with-poster-clickable');
50176
50162
  }
50177
50163
  hidePlayButton() {
50178
- this.$playButton.hide();
50164
+ trace(`${T$8} hidePlayButton`);
50165
+ this.$el.find('#poster-play').hide();
50179
50166
  this.$el.removeClass('clickable');
50180
50167
  }
50181
- clicked() {
50182
- trace(`${T$8} clicked`, {
50183
- hasStartedPlaying: this.hasStartedPlaying,
50184
- chromeless: this.options.chromeless,
50185
- allowUserInteraction: this.options.allowUserInteraction,
50186
- });
50168
+ clicked(e) {
50169
+ trace(`${T$8} clicked`);
50170
+ e.preventDefault();
50171
+ e.stopPropagation();
50172
+ if (this.options.chromeless && !this.options.allowUserInteraction) {
50173
+ return;
50174
+ }
50187
50175
  // Let "click_to_pause" plugin handle click event if media has started playing
50188
- if (!this.hasStartedPlaying) {
50189
- if (!this.options.chromeless || this.options.allowUserInteraction) {
50190
- this.playRequested = true;
50191
- this.update();
50192
- this.container.playback.consent();
50193
- this.container.playback.play();
50194
- }
50176
+ if (!this.playing) {
50177
+ this.playRequested = true;
50178
+ this.update();
50179
+ this.container.play();
50195
50180
  }
50196
- return false;
50197
50181
  }
50198
50182
  shouldHideOnPlay() {
50199
50183
  // Audio broadcasts should keep the poster up; video should hide poster while playing.
50200
50184
  return !this.container.playback.isAudioOnly;
50201
50185
  }
50202
50186
  update() {
50203
- trace(`${T$8} update`, {
50204
- shouldRender: this.shouldRender,
50205
- });
50206
- if (!this.shouldRender) {
50207
- return;
50208
- }
50209
- const showPlayButton = !this.playRequested &&
50210
- !this.hasStartedPlaying &&
50211
- !this.container.buffering;
50212
- this.updatePlayButton(showPlayButton);
50187
+ trace(`${T$8} update`);
50188
+ this.updatePlayButton();
50213
50189
  this.updatePoster();
50214
50190
  }
50215
50191
  updatePoster() {
50216
- trace(`${T$8} updatePoster`, {
50217
- hasStartedPlaying: this.hasStartedPlaying,
50218
- });
50219
- if (!this.hasStartedPlaying) {
50192
+ trace(`${T$8} updatePoster`);
50193
+ if (!this.playing) {
50220
50194
  this.showPoster();
50221
50195
  }
50222
50196
  else {
@@ -50228,9 +50202,7 @@ class Poster extends UIContainerPlugin {
50228
50202
  this.$el.show();
50229
50203
  }
50230
50204
  hidePoster() {
50231
- trace(`${T$8} hidePoster`, {
50232
- shouldHideOnPlay: this.shouldHideOnPlay(),
50233
- });
50205
+ trace(`${T$8} hidePoster`);
50234
50206
  if (!this.options.disableMediaControl) {
50235
50207
  this.container.enableMediaControl();
50236
50208
  }
@@ -50246,27 +50218,24 @@ class Poster extends UIContainerPlugin {
50246
50218
  return this;
50247
50219
  }
50248
50220
  this.$el.html(Poster.template());
50249
- const isRegularPoster = this.options.poster && this.options.poster.custom === undefined;
50250
- if (isRegularPoster) {
50251
- const posterUrl = this.options.poster.url || this.options.poster;
50252
- this.$el.css({ 'background-image': 'url(' + posterUrl + ')' });
50253
- }
50254
- else if (this.options.poster) {
50221
+ const isCustomPoster = this.options.poster?.custom !== undefined;
50222
+ if (isCustomPoster) {
50255
50223
  this.$el.css({ background: this.options.poster.custom });
50256
50224
  }
50225
+ else {
50226
+ const posterUrl = typeof this.options.poster === 'string'
50227
+ ? this.options.poster
50228
+ : this.options.poster?.url;
50229
+ if (posterUrl) {
50230
+ this.$el.css({ 'background-image': 'url(' + posterUrl + ')' });
50231
+ }
50232
+ }
50257
50233
  this.container.$el.removeClass('container-with-poster-clickable');
50258
50234
  this.container.$el.append(this.el);
50259
- this.$playWrapper = this.$el.find('.play-wrapper');
50260
- this.$playWrapper.addClass('control-need-disable');
50261
- this.$playButton = $("<div class='circle-poster gcore-skin-button-color gcore-skin-border-color'></div>");
50262
- this.$playWrapper.append(this.$playButton);
50263
- this.$playButton.append(playIcon);
50264
- if (this.options.autoPlay) {
50265
- this.$playButton.hide();
50235
+ this.$el.find('#poster-play').append(playIcon);
50236
+ if (this.options.autoPlay || this.isNoOp) {
50237
+ this.$el.find('#poster-play').hide();
50266
50238
  }
50267
- this.$playButton.addClass('poster-icon');
50268
- this.$playButton.attr('data-poster', '');
50269
- this.update();
50270
50239
  return this;
50271
50240
  }
50272
50241
  /**
@@ -51997,12 +51966,31 @@ function requireParseSrt () {
51997
51966
  var parseSrtExports = requireParseSrt();
51998
51967
  const parseSRT = /*@__PURE__*/getDefaultExportFromCjs$1(parseSrtExports);
51999
51968
 
52000
- const pluginHtml = "<div class=\"thumbnails-text\"></div>\n<% if (backdropHeight) { %>\n <div class=\"backdrop\" style=\"height: <%= backdropHeight %>px;\">\n <div class=\"carousel\"></div>\n </div>\n<% }; %>\n<% if (spotlightHeight) { %>\n <div class=\"spotlight\" style=\"height: <%= spotlightHeight %>px;\">\n </div>\n<% }; %>\n";
51969
+ const pluginHtml = "<div class=\"thumbnails-text\" id=\"thumbnails-text\"></div>\n<div class=\"backdrop\" id=\"thumbnails-backdrop\">\n <div class=\"carousel\" id=\"thumbnails-carousel\"></div>\n</div>\n<div class=\"spotlight\" id=\"thumbnails-spotlight\"></div>\n";
51970
+
51971
+ function loadImageDimensions(url) {
51972
+ return new Promise((resolve, reject) => {
51973
+ const img = new Image();
51974
+ img.src = url;
51975
+ img.onload = () => {
51976
+ resolve({ width: img.width, height: img.height });
51977
+ };
51978
+ img.onerror = () => {
51979
+ reject(new Error('Failed to load image'));
51980
+ };
51981
+ });
51982
+ }
52001
51983
 
52002
51984
  const T$1 = 'plugins.thumbnails';
52003
51985
  /**
52004
51986
  * `PLUGIN` that displays the thumbnails of the video when available.
52005
51987
  * @beta
51988
+ * @remarks
51989
+ * The plugin needs specially crafted VTT file with a thumbnail sprite sheet to work.
51990
+ * The VTT consist of timestamp records followed by a thumbnail area
51991
+ *
51992
+ * Configuration options - {@link ThumbnailsPluginSettings}
51993
+ *
52006
51994
  * @example
52007
51995
  * ```ts
52008
51996
  * import { Thumbnails } from '@gcorevideo/player'
@@ -52023,19 +52011,15 @@ const T$1 = 'plugins.thumbnails';
52023
52011
  * ```
52024
52012
  */
52025
52013
  class Thumbnails extends UICorePlugin {
52026
- _$spotlight = null;
52027
- _$backdrop = null;
52028
- $container = null;
52029
- $img = null;
52030
- _$carousel = null;
52031
- $textThumbnail = null;
52032
- _$backdropCarouselImgs = [];
52014
+ $backdropCarouselImgs = [];
52033
52015
  spriteSheetHeight = 0;
52034
- _hoverPosition = 0;
52035
- _show = false;
52036
- _thumbsLoaded = false;
52037
- _oldContainer = null;
52038
- _thumbs = [];
52016
+ spriteSheetWidth = 0;
52017
+ hoverPosition = 0;
52018
+ showing = false;
52019
+ thumbsLoaded = false;
52020
+ spotlightHeight = 0;
52021
+ backdropHeight = 0;
52022
+ thumbs = [];
52039
52023
  /**
52040
52024
  * @internal
52041
52025
  */
@@ -52053,10 +52037,14 @@ class Thumbnails extends UICorePlugin {
52053
52037
  */
52054
52038
  get attributes() {
52055
52039
  return {
52056
- class: this.name,
52040
+ class: 'scrub-thumbnails',
52057
52041
  };
52058
52042
  }
52059
52043
  static template = tmpl(pluginHtml);
52044
+ constructor(core) {
52045
+ super(core);
52046
+ this.backdropHeight = this.options.thumbnails?.backdropHeight ?? 0;
52047
+ }
52060
52048
  /*
52061
52049
  * Helper to build the "thumbs" property for a sprite sheet.
52062
52050
  *
@@ -52068,25 +52056,21 @@ class Thumbnails extends UICorePlugin {
52068
52056
  * timeInterval- The interval (in seconds) between the thumbnails.
52069
52057
  * startTime- The time (in seconds) that the first thumbnail represents. (defaults to 0)
52070
52058
  */
52071
- // buildSpriteConfig(vtt, spriteSheetUrl, numThumbs, thumbWidth, thumbHeight, numColumns, timeInterval, startTime) {
52072
- buildSpriteConfig(vtt, spriteSheetUrl) {
52059
+ buildSpriteConfig(vtt, baseUrl) {
52073
52060
  const thumbs = [];
52074
- // let coor: string[] = [];
52075
52061
  for (const vt of vtt) {
52076
52062
  const el = vt.text;
52077
- // if (el && el.search(/\d*,\d*,\d*,\d*/g) > -1) {
52078
- // el = el.match(/\d*,\d*,\d*,\d*/g)[0];
52079
- // coor = el.split(',');
52080
- // }
52081
52063
  if (el) {
52082
- const m = el.match(/xywh=\d*,\d*,\d*,\d*/g);
52064
+ const m = el.match(/(\w+)#xywh=(\d+,\d+,\d+,\d+)/);
52083
52065
  if (m) {
52084
- const coor = m[0].split(',');
52066
+ const coor = m[2].split(',');
52085
52067
  const w = parseInt(coor[2], 10);
52086
52068
  const h = parseInt(coor[3], 10);
52087
52069
  if (w > 0 && h > 0) {
52088
52070
  thumbs.push({
52089
- url: spriteSheetUrl,
52071
+ // TODO handle relative URLs
52072
+ // url: new URL(m[0], baseUrl).toString(),
52073
+ url: baseUrl,
52090
52074
  time: vt.start,
52091
52075
  w,
52092
52076
  h,
@@ -52099,242 +52083,147 @@ class Thumbnails extends UICorePlugin {
52099
52083
  }
52100
52084
  return thumbs;
52101
52085
  }
52102
- // TODO check if seek enabled
52103
52086
  /**
52104
52087
  * @internal
52105
52088
  */
52106
52089
  bindEvents() {
52107
- this.listenToOnce(this.core, Events$1.CORE_READY, this._onCoreReady);
52108
- this.listenTo(this.core.mediaControl, Events$1.MEDIACONTROL_MOUSEMOVE_SEEKBAR, this._onMouseMove);
52109
- this.listenTo(this.core.mediaControl, Events$1.MEDIACONTROL_MOUSELEAVE_SEEKBAR, this._onMouseLeave);
52110
- this.listenTo(this.core.mediaControl, Events$1.MEDIACONTROL_RENDERED, this._init);
52111
- this.listenTo(this.core.mediaControl, Events$1.MEDIACONTROL_CONTAINERCHANGED, this._onMediaControlContainerChanged);
52090
+ this.listenToOnce(this.core, Events$1.CORE_READY, this.onCoreReady);
52112
52091
  }
52113
- _bindContainerEvents() {
52114
- if (this._oldContainer) {
52115
- this.stopListening(this._oldContainer, Events$1.CONTAINER_TIMEUPDATE, this._renderPlugin);
52116
- }
52117
- this._oldContainer = this.core.mediaControl.container;
52118
- this.listenTo(this.core.mediaControl.container, Events$1.CONTAINER_TIMEUPDATE, this._renderPlugin);
52092
+ bindContainerEvents(container) {
52093
+ this.listenTo(container, Events$1.CONTAINER_TIMEUPDATE, this.update);
52119
52094
  }
52120
- _onCoreReady() {
52121
- try {
52122
- if (!this.options.thumbnails ||
52123
- !this.options.thumbnails.sprite ||
52124
- !this.options.thumbnails.vtt) {
52125
- this.destroy();
52126
- return;
52127
- }
52128
- }
52129
- catch (error) {
52130
- reportError(error);
52095
+ onCoreReady() {
52096
+ const mediaControl = this.core.getPlugin('media_control');
52097
+ assert(mediaControl, `MediaControl is required for ${this.name} plugin to work`);
52098
+ if (!this.options.thumbnails ||
52099
+ !this.options.thumbnails.sprite ||
52100
+ !this.options.thumbnails.vtt) {
52101
+ trace(`${T$1} misconfigured: options.thumbnails.sprite and options.thumbnails.vtt are required`);
52102
+ this.destroy();
52131
52103
  return;
52132
52104
  }
52133
- // TODO options
52134
- const spriteSheet = this.options.thumbnails.sprite;
52135
- this._thumbs = this.buildSpriteConfig(parseSRT(this.options.thumbnails.vtt), spriteSheet);
52136
- if (!this._thumbs.length) {
52105
+ const { sprite: spriteSheet, vtt } = this.options.thumbnails;
52106
+ this.thumbs = this.buildSpriteConfig(parseSRT(vtt), spriteSheet);
52107
+ if (!this.thumbs.length) {
52108
+ trace(`${T$1} failed to parse the sprite sheet`);
52137
52109
  this.destroy();
52138
52110
  return;
52139
52111
  }
52112
+ this.spotlightHeight = this.options.thumbnails?.spotlightHeight ?? 0;
52140
52113
  this.loadSpriteSheet(spriteSheet).then(() => {
52141
- this._thumbsLoaded = true;
52142
- this.core.options.thumbnails.spotlightHeight = this._thumbs[0].h;
52143
- this._init();
52114
+ this.thumbsLoaded = true;
52115
+ this.spotlightHeight = this.spotlightHeight
52116
+ ? Math.min(this.spotlightHeight, this.thumbs[0].h)
52117
+ : this.thumbs[0].h;
52118
+ this.init();
52144
52119
  });
52120
+ this.listenTo(mediaControl, Events$1.MEDIACONTROL_MOUSEMOVE_SEEKBAR, this.onMouseMoveSeekbar);
52121
+ this.listenTo(mediaControl, Events$1.MEDIACONTROL_MOUSELEAVE_SEEKBAR, this.onMouseLeave);
52122
+ this.listenTo(mediaControl, Events$1.MEDIACONTROL_RENDERED, this.init);
52123
+ this.listenTo(mediaControl, Events$1.MEDIACONTROL_CONTAINERCHANGED, () => this.onContainerChanged(mediaControl.container));
52145
52124
  }
52146
52125
  async loadSpriteSheet(spriteSheetUrl) {
52147
- return new Promise((resolve, reject) => {
52148
- const img = new Image();
52149
- img.onload = () => {
52150
- this.spriteSheetHeight = img.height;
52151
- resolve();
52152
- };
52153
- img.onerror = reject;
52154
- img.src = spriteSheetUrl;
52126
+ return loadImageDimensions(spriteSheetUrl).then(({ height, width }) => {
52127
+ this.spriteSheetHeight = height;
52128
+ this.spriteSheetWidth = width;
52155
52129
  });
52156
52130
  }
52157
- _onMediaControlContainerChanged() {
52158
- this._bindContainerEvents();
52131
+ onContainerChanged(container) {
52132
+ this.bindContainerEvents(container);
52159
52133
  }
52160
- _init() {
52161
- if (!this._thumbsLoaded) {
52162
- // _init() will be called when the thumbs are loaded,
52134
+ init() {
52135
+ if (!this.thumbsLoaded) {
52136
+ // init() will be called when the thumbs are loaded,
52163
52137
  // and whenever the media control rendered event is fired as just before this the dom elements get wiped in IE (https://github.com/tjenkinson/clappr-thumbnails-plugin/issues/5)
52164
52138
  return;
52165
52139
  }
52166
52140
  // Init the backdropCarousel as array to keep reference of thumbnail images
52167
- this._$backdropCarouselImgs = [];
52168
- // create/recreate the dom elements for the plugin
52169
- this._createElements();
52170
- this._loadBackdrop();
52171
- this._renderPlugin();
52172
- }
52173
- _getOptions() {
52174
- if (!('thumbnails' in this.core.options)) {
52175
- throw "'thumbnail property missing from options object.";
52176
- }
52177
- return this.core.options.thumbnails;
52178
- }
52179
- _appendElToMediaControl() {
52180
- // insert after the background
52181
- this.core.mediaControl.$el.find('.seek-time').css('bottom', 56);
52182
- this.core.mediaControl.$el.first().after(this.el);
52183
- }
52184
- _onMouseMove(e) {
52185
- // trace(`${T} _onMouseMove`, {
52186
- // e: (e as any).name,
52187
- // t: typeof e,
52188
- // t2: typeof arguments[1],
52189
- // });
52190
- this._calculateHoverPosition(e);
52191
- this._show = true;
52192
- this._renderPlugin();
52193
- }
52194
- _onMouseLeave() {
52195
- this._show = false;
52196
- this._renderPlugin();
52197
- }
52198
- _calculateHoverPosition(e) {
52199
- const offset = getPageX(e) - this.core.mediaControl.$seekBarContainer.offset().left;
52200
- // proportion into the seek bar that the mouse is hovered over 0-1
52201
- this._hoverPosition = Math.min(1, Math.max(offset / this.core.mediaControl.$seekBarContainer.width(), 0));
52202
- }
52203
- // private _buildThumbsFromOptions() {
52204
- // const thumbs = this._thumbs;
52205
- // const promises = thumbs.map((thumb) => {
52206
- // return this._addThumbFromSrc(thumb);
52207
- // });
52208
- // return Promise.all(promises);
52209
- // }
52210
- // private _addThumbFromSrc(thumbSrc) {
52211
- // return new Promise((resolve, reject) => {
52212
- // const img = new Image();
52213
- // img.onload = () => {
52214
- // resolve(img);
52215
- // };
52216
- // img.onerror = reject;
52217
- // img.src = thumbSrc.url;
52218
- // }).then((img) => {
52219
- // const startTime = thumbSrc.time;
52220
- // // determine the thumb index
52221
- // let index = null;
52222
- // this._thumbs.some((thumb, i) => {
52223
- // if (startTime < thumb.time) {
52224
- // index = i;
52225
- // return true;
52226
- // }
52227
- // return false;
52228
- // });
52229
- // if (index === null) {
52230
- // index = this._thumbs.length;
52231
- // }
52232
- // const next = index < this._thumbs.length ? this._thumbs[index] : null;
52233
- // const prev = index > 0 ? this._thumbs[index - 1] : null;
52234
- // if (prev) {
52235
- // // update the duration of the previous thumbnail
52236
- // prev.duration = startTime - prev.time;
52237
- // }
52238
- // // the duration this thumb lasts for
52239
- // // if it is the last thumb then duration will be null
52240
- // const duration = next ? next.time - thumbSrc.time : null;
52241
- // const imageW = img.width;
52242
- // const imageH = img.height;
52243
- // const thumb = {
52244
- // imageW: imageW, // actual width of image
52245
- // imageH: imageH, // actual height of image
52246
- // x: thumbSrc.x || 0, // x coord in image of sprite
52247
- // y: thumbSrc.y || 0, // y coord in image of sprite
52248
- // w: thumbSrc.w || imageW, // width of sprite
52249
- // h: thumbSrc.h || imageH, // height of sprite
52250
- // url: thumbSrc.url,
52251
- // time: startTime, // time this thumb represents
52252
- // duration: duration, // how long (from time) this thumb represents
52253
- // src: thumbSrc
52254
- // };
52255
- // this._thumbs.splice(index, 0, thumb);
52256
- // return thumb;
52257
- // });
52258
- // }
52141
+ this.$backdropCarouselImgs = [];
52142
+ this.fixElements();
52143
+ this.loadBackdrop();
52144
+ this.update();
52145
+ }
52146
+ mount() {
52147
+ // insert after the background TODO figure out why
52148
+ const mediaControl = this.core.getPlugin('media_control');
52149
+ mediaControl.$el.find('.seek-time').css('bottom', 56); // TODO check
52150
+ // TODO use mediaControl.mount? into the `layer`
52151
+ mediaControl.$el.append(this.$el);
52152
+ }
52153
+ onMouseMoveSeekbar(_, pos) {
52154
+ this.hoverPosition = pos;
52155
+ this.showing = true;
52156
+ this.update();
52157
+ }
52158
+ onMouseLeave() {
52159
+ this.showing = false;
52160
+ this.update();
52161
+ }
52259
52162
  // builds a dom element which represents the thumbnail
52260
- // scaled to the provided height
52261
- _buildImg(thumb, height) {
52163
+ // scaled to the given height
52164
+ buildThumbImage(thumb, height) {
52262
52165
  const scaleFactor = height / thumb.h;
52263
- if (!this.$img) {
52264
- this.$img = $('<img />').addClass('thumbnail-img').attr('src', thumb.url);
52265
- }
52266
- // the container will contain the image positioned so that the correct sprite
52267
- // is visible
52268
- if (!this.$container) {
52269
- this.$container = $('<div />').addClass('thumbnail-container');
52270
- }
52271
- this.$container.css('width', thumb.w * scaleFactor);
52272
- this.$container.css('height', height);
52273
- this.$img.css({
52274
- height: this.spriteSheetHeight * scaleFactor,
52275
- left: -1 * thumb.x * scaleFactor,
52276
- top: -1 * thumb.y * scaleFactor,
52166
+ const $container = $('<div />').addClass('thumbnail-container');
52167
+ $container.css('width', thumb.w * scaleFactor);
52168
+ $container.css('height', height);
52169
+ $container.css({
52170
+ backgroundImage: `url(${thumb.url})`,
52171
+ backgroundSize: `${Math.floor(this.spriteSheetWidth * scaleFactor)}px ${Math.floor(this.spriteSheetHeight * scaleFactor)}px`,
52172
+ backgroundPosition: `-${Math.floor(thumb.x * scaleFactor)}px -${Math.floor(thumb.y * scaleFactor)}px`,
52277
52173
  });
52278
- if (this.$container.find(this.$img).length === 0) {
52279
- this.$container.append(this.$img);
52280
- }
52281
- return this.$container;
52174
+ return $container;
52282
52175
  }
52283
- _loadBackdrop() {
52284
- if (!this._getOptions().backdropHeight) {
52176
+ loadBackdrop() {
52177
+ if (!this.backdropHeight) {
52285
52178
  // disabled
52286
52179
  return;
52287
52180
  }
52288
52181
  // append each of the thumbnails to the backdrop carousel
52289
- const $carousel = this._$carousel;
52290
- for (const thumb of this._thumbs) {
52291
- const $img = this._buildImg(thumb, this._getOptions().backdropHeight);
52292
- // Keep reference to thumbnail
52293
- this._$backdropCarouselImgs.push($img);
52182
+ const $carousel = this.$el.find('#thumbnails-carousel');
52183
+ for (const thumb of this.thumbs) {
52184
+ const $img = this.buildThumbImage(thumb, this.backdropHeight);
52185
+ // Keep reference to the thumbnail
52186
+ this.$backdropCarouselImgs.push($img);
52294
52187
  // Add thumbnail to DOM
52295
52188
  $carousel.append($img);
52296
52189
  }
52297
52190
  }
52298
52191
  setText(time) {
52299
- if (this.core.getPlugin('clips')) {
52300
- const txt = this.core.getPlugin('clips').getText(time);
52301
- this.$textThumbnail.text(txt);
52192
+ const clips = this.core.getPlugin('clips');
52193
+ if (clips) {
52194
+ const txt = clips.getText(time);
52195
+ this.$el.find('#thumbnails-text').text(txt ?? '');
52302
52196
  }
52303
52197
  }
52304
52198
  // calculate how far along the carousel should currently be slid
52305
52199
  // depending on where the user is hovering on the progress bar
52306
- _updateCarousel() {
52307
- trace(`${T$1} _updateCarousel`, {
52308
- backdropHeight: this._getOptions().backdropHeight,
52309
- });
52310
- if (!this._getOptions().backdropHeight) {
52200
+ updateCarousel() {
52201
+ if (!this.backdropHeight) {
52311
52202
  // disabled
52312
52203
  return;
52313
52204
  }
52314
- const hoverPosition = this._hoverPosition;
52315
- const videoDuration = this.core.mediaControl.container.getDuration();
52316
- const startTimeOffset = this.core.mediaControl.container.getStartTimeOffset();
52205
+ const mediaControl = this.core.getPlugin('media_control');
52206
+ const videoDuration = mediaControl.container.getDuration();
52207
+ const startTimeOffset = mediaControl.container.getStartTimeOffset();
52317
52208
  // the time into the video at the current hover position
52318
- const hoverTime = startTimeOffset + videoDuration * hoverPosition;
52319
- const backdropWidth = this._$backdrop.width();
52320
- const $carousel = this._$carousel;
52209
+ const hoverTime = startTimeOffset + videoDuration * this.hoverPosition;
52210
+ const $backdrop = this.$el.find('#thumbnails-backdrop');
52211
+ const backdropWidth = $backdrop.width();
52212
+ const $carousel = this.$el.find('#thumbnails-carousel');
52321
52213
  const carouselWidth = $carousel.width();
52322
52214
  // slide the carousel so that the image on the carousel that is above where the person
52323
52215
  // is hovering maps to that position in time.
52324
52216
  // Thumbnails may not be distributed at even times along the video
52325
- const thumbs = this._thumbs;
52326
52217
  // assuming that each thumbnail has the same width
52327
- const thumbWidth = carouselWidth / thumbs.length;
52218
+ const thumbWidth = carouselWidth / this.thumbs.length;
52328
52219
  // determine which thumbnail applies to the current time
52329
- const thumbIndex = this._getThumbIndexForTime(hoverTime);
52330
- const thumb = thumbs[thumbIndex];
52331
- let thumbDuration = thumb.duration;
52332
- if (!thumbDuration) {
52333
- // the last thumbnail duration will be null as it can't be determined
52334
- // e.g the duration of the video may increase over time (live stream)
52335
- // so calculate the duration now so this last thumbnail lasts till the end
52336
- thumbDuration = Math.max(videoDuration + startTimeOffset - thumb.time, 0);
52337
- }
52220
+ const thumbIndex = this.getThumbIndexForTime(hoverTime);
52221
+ const thumb = this.thumbs[thumbIndex];
52222
+ // the last thumbnail duration will be null as it can't be determined
52223
+ // e.g the duration of the video may increase over time (live stream)
52224
+ // so calculate the duration now so this last thumbnail lasts till the end
52225
+ const thumbDuration = thumb.duration ??
52226
+ Math.max(videoDuration + startTimeOffset - thumb.time, 0);
52338
52227
  // determine how far accross that thumbnail we are
52339
52228
  const timeIntoThumb = hoverTime - thumb.time;
52340
52229
  const positionInThumb = timeIntoThumb / thumbDuration;
@@ -52342,12 +52231,12 @@ class Thumbnails extends UICorePlugin {
52342
52231
  // now calculate the position along carousel that we want to be above the hover position
52343
52232
  const xCoordInCarousel = thumbIndex * thumbWidth + xCoordInThumb;
52344
52233
  // and finally the position of the carousel when the hover position is taken in to consideration
52345
- const carouselXCoord = xCoordInCarousel - hoverPosition * backdropWidth;
52346
- $carousel.css('left', -carouselXCoord);
52347
- const maxOpacity = this._getOptions().backdropMaxOpacity || 0.6;
52348
- const minOpacity = this._getOptions().backdropMinOpacity || 0.08;
52234
+ const carouselXCoord = xCoordInCarousel - this.hoverPosition * backdropWidth;
52235
+ $carousel.css('left', -carouselXCoord); // TODO +px
52236
+ const maxOpacity = this.options.thumbnails.backdropMaxOpacity ?? 0.6;
52237
+ const minOpacity = this.options.thumbnails.backdropMinOpacity ?? 0.08;
52349
52238
  // now update the transparencies so that they fade in around the active one
52350
- for (let i = 0; i < thumbs.length; i++) {
52239
+ for (let i = 0; i < this.thumbs.length; i++) {
52351
52240
  const thumbXCoord = thumbWidth * i;
52352
52241
  let distance = thumbXCoord - xCoordInCarousel;
52353
52242
  if (distance < 0) {
@@ -52359,47 +52248,43 @@ class Thumbnails extends UICorePlugin {
52359
52248
  }
52360
52249
  // fade over the width of 2 thumbnails
52361
52250
  const opacity = Math.max(maxOpacity - Math.abs(distance) / (2 * thumbWidth), minOpacity);
52362
- this._$backdropCarouselImgs[i].css('opacity', opacity);
52251
+ this.$backdropCarouselImgs[i].css('opacity', opacity);
52363
52252
  }
52364
52253
  }
52365
- _updateSpotlightThumb() {
52366
- trace(`${T$1} _updateSpotlightThumb`, {
52367
- spotlightHeight: this._getOptions().spotlightHeight,
52368
- });
52369
- if (!this._getOptions().spotlightHeight) {
52254
+ updateSpotlightThumb() {
52255
+ if (!this.spotlightHeight) {
52370
52256
  // disabled
52371
52257
  return;
52372
52258
  }
52373
- const hoverPosition = this._hoverPosition;
52374
- const videoDuration = this.core.mediaControl.container.getDuration();
52259
+ const mediaControl = this.core.getPlugin('media_control');
52260
+ const videoDuration = mediaControl.container.getDuration();
52375
52261
  // the time into the video at the current hover position
52376
- const startTimeOffset = this.core.mediaControl.container.getStartTimeOffset();
52377
- const hoverTime = startTimeOffset + videoDuration * hoverPosition;
52262
+ const startTimeOffset = mediaControl.container.getStartTimeOffset();
52263
+ const hoverTime = startTimeOffset + videoDuration * this.hoverPosition;
52378
52264
  this.setText(hoverTime);
52379
52265
  // determine which thumbnail applies to the current time
52380
- const thumbIndex = this._getThumbIndexForTime(hoverTime);
52381
- const thumb = this._thumbs[thumbIndex];
52266
+ const thumbIndex = this.getThumbIndexForTime(hoverTime);
52267
+ const thumb = this.thumbs[thumbIndex];
52382
52268
  // update thumbnail
52383
- const $spotlight = this._$spotlight;
52269
+ const $spotlight = this.$el.find('#thumbnails-spotlight');
52384
52270
  $spotlight.empty();
52385
- $spotlight.append(this._buildImg(thumb, this._getOptions().spotlightHeight));
52271
+ $spotlight.append(this.buildThumbImage(thumb, this.spotlightHeight));
52386
52272
  const elWidth = this.$el.width();
52387
52273
  const thumbWidth = $spotlight.width();
52388
52274
  const thumbHeight = $spotlight.height();
52389
- let spotlightXPos = elWidth * hoverPosition - thumbWidth / 2;
52390
52275
  // adjust so the entire thumbnail is always visible
52391
- spotlightXPos = Math.max(Math.min(spotlightXPos, elWidth - thumbWidth), 0);
52276
+ const spotlightXPos = Math.max(Math.min(elWidth * this.hoverPosition - thumbWidth / 2, elWidth - thumbWidth), 0);
52392
52277
  $spotlight.css('left', spotlightXPos);
52393
- this.$textThumbnail.css('left', spotlightXPos);
52394
- this.$textThumbnail.css('width', thumbWidth);
52395
- this.$textThumbnail.css('bottom', thumbHeight + 1);
52278
+ const $textThumbnail = this.$el.find('#thumbnails-text');
52279
+ $textThumbnail.css('left', spotlightXPos);
52280
+ $textThumbnail.css('width', thumbWidth);
52281
+ $textThumbnail.css('bottom', thumbHeight + 1);
52396
52282
  }
52397
52283
  // returns the thumbnail which represents a time in the video
52398
52284
  // or null if there is no thumbnail that can represent the time
52399
- _getThumbIndexForTime(time) {
52400
- const thumbs = this._thumbs;
52401
- for (let i = thumbs.length - 1; i >= 0; i--) {
52402
- const thumb = thumbs[i];
52285
+ getThumbIndexForTime(time) {
52286
+ for (let i = this.thumbs.length - 1; i >= 0; i--) {
52287
+ const thumb = this.thumbs[i];
52403
52288
  if (thumb.time <= time) {
52404
52289
  return i;
52405
52290
  }
@@ -52407,37 +52292,48 @@ class Thumbnails extends UICorePlugin {
52407
52292
  // stretch the first thumbnail back to the start
52408
52293
  return 0;
52409
52294
  }
52410
- _renderPlugin() {
52411
- trace(`${T$1} _renderPlugin`, {
52412
- show: this._show,
52413
- thumbsLoaded: this._thumbsLoaded,
52414
- thumbs: this._thumbs.length,
52415
- });
52416
- if (!this._thumbsLoaded) {
52295
+ update() {
52296
+ if (!this.thumbsLoaded) {
52417
52297
  return;
52418
52298
  }
52419
- if (this._show && this._thumbs.length > 0) {
52299
+ if (this.showing && this.thumbs.length > 0) {
52300
+ this.updateCarousel();
52301
+ this.updateSpotlightThumb();
52420
52302
  this.$el.removeClass('hidden');
52421
- this._updateCarousel();
52422
- this._updateSpotlightThumb();
52423
52303
  }
52424
52304
  else {
52425
52305
  this.$el.addClass('hidden');
52426
52306
  }
52427
52307
  }
52428
- _createElements() {
52429
- trace(`${T$1} _createElements`);
52430
- this.$el.html(Thumbnails.template({
52431
- backdropHeight: this._getOptions().backdropHeight,
52432
- spotlightHeight: this._getOptions().spotlightHeight,
52433
- }));
52434
- // cache dom references
52435
- this._$spotlight = this.$el.find('.spotlight');
52436
- this._$backdrop = this.$el.find('.backdrop');
52437
- this._$carousel = this._$backdrop.find('.carousel');
52438
- this.$textThumbnail = this.$el.find('.thumbnails-text');
52308
+ fixElements() {
52309
+ const $spotlight = this.$el.find('#thumbnails-spotlight');
52310
+ if (this.spotlightHeight) {
52311
+ $spotlight.css('height', this.spotlightHeight);
52312
+ }
52313
+ else {
52314
+ $spotlight.remove();
52315
+ }
52316
+ const $backdrop = this.$el.find('#thumbnails-backdrop');
52317
+ if (this.backdropHeight) {
52318
+ $backdrop.css('height', this.backdropHeight);
52319
+ }
52320
+ else {
52321
+ $backdrop.remove();
52322
+ }
52323
+ this.mount();
52324
+ }
52325
+ get shouldRender() {
52326
+ return (this.options.thumbnails &&
52327
+ this.options.thumbnails.sprite &&
52328
+ this.options.thumbnails.vtt);
52329
+ }
52330
+ render() {
52331
+ if (!this.shouldRender) {
52332
+ return this;
52333
+ }
52334
+ this.$el.html(Thumbnails.template());
52439
52335
  this.$el.addClass('hidden');
52440
- this._appendElToMediaControl();
52336
+ return this;
52441
52337
  }
52442
52338
  }
52443
52339