eb-player 2.0.2 → 2.0.4

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.
@@ -85,8 +85,8 @@
85
85
  --eb-radius-control: 8px;
86
86
  --eb-duration-transition: 200ms;
87
87
  font-family: 'Inter', -apple-system, sans-serif;
88
- border-radius: 14px;
89
- box-shadow: 0 40px 80px rgba(0,0,0,.8), 0 0 0 1px rgba(255,255,255,.06);
88
+ border-radius: 0;
89
+ box-shadow: none;
90
90
  color: #fff;
91
91
  }
92
92
 
@@ -349,9 +349,9 @@
349
349
  transition: height .15s;
350
350
  }
351
351
 
352
- /* Red gradient fill */
352
+ /* Solid red fill */
353
353
  [data-theme="lequipe"] .eb-player .eb-seekbar-progress {
354
- background: linear-gradient(90deg, #d61e00, color-mix(in srgb, #d61e00 70%, #fff));
354
+ background: #d61e00;
355
355
  border-radius: 3px;
356
356
  transition: height .15s;
357
357
  }
@@ -525,72 +525,184 @@
525
525
  Settings panel: glassmorphism dropdown
526
526
  ============================================================ */
527
527
  [data-theme="lequipe"] .eb-player .eb-settings-panel {
528
- background: rgba(10,10,20,.55);
529
- backdrop-filter: blur(18px) saturate(160%);
530
- -webkit-backdrop-filter: blur(18px) saturate(160%);
531
- border-radius: 12px;
532
- min-width: 300px;
533
- box-shadow: 0 16px 48px rgba(0,0,0,.6), 0 0 0 1px rgba(255,255,255,.1);
528
+ background: rgba(30,30,45,.6);
529
+ backdrop-filter: blur(24px) saturate(160%);
530
+ -webkit-backdrop-filter: blur(24px) saturate(160%);
531
+ border-radius: 14px;
532
+ width: min(340px, 80vw);
533
+ max-height: 60vh;
534
+ overflow-y: auto;
535
+ overflow-x: hidden;
536
+ box-shadow: 0 16px 48px rgba(0,0,0,.6), 0 0 0 1px rgba(255,255,255,.08);
537
+ }
538
+
539
+ /* Small viewports: compact rows */
540
+ @media (max-height: 400px) {
541
+ [data-theme="lequipe"] .eb-player .eb-settings-category { padding: 12px 14px; font-size: 13px; gap: 10px; }
542
+ [data-theme="lequipe"] .eb-player .eb-settings-item { padding: 11px 14px; font-size: 13px; }
543
+ [data-theme="lequipe"] .eb-player .eb-settings-header { padding: 10px 14px; }
544
+ [data-theme="lequipe"] .eb-player .eb-settings-panel { min-width: 220px; border-radius: 10px; }
545
+ [data-theme="lequipe"] .eb-player .eb-settings-back { width: 30px; height: 30px; }
534
546
  }
535
547
 
536
548
  [data-theme="lequipe"] .eb-player .eb-settings-menu {
537
549
  padding: 0;
538
550
  }
539
551
 
540
- [data-theme="lequipe"] .eb-player .eb-settings-category,
541
- [data-theme="lequipe"] .eb-player .eb-settings-item,
542
- [data-theme="lequipe"] .eb-player .eb-settings-back {
543
- padding: 15px 20px;
552
+ /* Root menu category rows — icon | label | value | chevron */
553
+ [data-theme="lequipe"] .eb-player .eb-settings-category {
554
+ display: flex;
555
+ align-items: center;
556
+ gap: 14px;
557
+ padding: 18px 20px;
558
+ font-size: 14px;
559
+ color: rgba(255,255,255,.95);
560
+ border-bottom: 1px solid rgba(255,255,255,.06);
561
+ transition: background .12s;
562
+ width: 100%;
563
+ border: none;
564
+ background: none;
565
+ cursor: pointer;
566
+ text-align: left;
567
+ }
568
+
569
+ [data-theme="lequipe"] .eb-player .eb-settings-category:last-child {
570
+ border-bottom: none;
571
+ }
572
+
573
+ [data-theme="lequipe"] .eb-player .eb-settings-category:hover {
574
+ background: rgba(255,255,255,.05);
575
+ }
576
+
577
+ [data-theme="lequipe"] .eb-player .eb-settings-category__icon {
578
+ flex-shrink: 0;
579
+ display: flex;
580
+ align-items: center;
581
+ justify-content: center;
582
+ color: rgba(255,255,255,.6);
583
+ }
584
+
585
+ [data-theme="lequipe"] .eb-player .eb-settings-category__icon .eb-icon {
586
+ width: 22px;
587
+ height: 22px;
588
+ }
589
+
590
+ [data-theme="lequipe"] .eb-player .eb-settings-category__label {
591
+ flex: 1;
592
+ font-weight: 400;
593
+ }
594
+
595
+ [data-theme="lequipe"] .eb-player .eb-settings-category__value {
596
+ color: rgba(255,255,255,.45);
544
597
  font-size: 13px;
545
- color: rgba(255,255,255,.9);
598
+ font-weight: 400;
599
+ }
600
+
601
+ [data-theme="lequipe"] .eb-player .eb-settings-category__chevron {
602
+ flex-shrink: 0;
603
+ display: flex;
604
+ align-items: center;
605
+ color: rgba(255,255,255,.3);
606
+ }
607
+
608
+ [data-theme="lequipe"] .eb-player .eb-settings-category__chevron .eb-icon {
609
+ width: 16px;
610
+ height: 16px;
611
+ }
612
+
613
+ /* Sub-menu header — circular back button + title */
614
+ [data-theme="lequipe"] .eb-player .eb-settings-header {
615
+ display: flex;
616
+ align-items: center;
617
+ gap: 12px;
618
+ padding: 14px 18px;
619
+ border-bottom: 1px solid rgba(255,255,255,.08);
620
+ }
621
+
622
+ [data-theme="lequipe"] .eb-player .eb-settings-header__title {
623
+ font-size: 15px;
624
+ font-weight: 600;
625
+ color: #fff;
626
+ }
627
+
628
+ [data-theme="lequipe"] .eb-player .eb-settings-back {
629
+ width: 36px;
630
+ height: 36px;
631
+ border-radius: 50%;
632
+ background: rgba(255,255,255,.12);
633
+ border: none;
634
+ cursor: pointer;
635
+ display: flex;
636
+ align-items: center;
637
+ justify-content: center;
638
+ padding: 0;
639
+ transition: background .15s;
640
+ flex-shrink: 0;
641
+ color: #fff;
642
+ }
643
+
644
+ [data-theme="lequipe"] .eb-player .eb-settings-back:hover {
645
+ background: rgba(255,255,255,.2);
646
+ }
647
+
648
+ [data-theme="lequipe"] .eb-player .eb-settings-back .eb-icon {
649
+ width: 18px;
650
+ height: 18px;
651
+ }
652
+
653
+ /* Sub-menu items */
654
+ [data-theme="lequipe"] .eb-player .eb-settings-item {
655
+ display: flex;
656
+ align-items: center;
657
+ justify-content: space-between;
658
+ padding: 16px 20px;
659
+ font-size: 14px;
660
+ color: rgba(255,255,255,.7);
546
661
  border-bottom: 1px solid rgba(255,255,255,.06);
547
662
  transition: background .12s;
663
+ width: 100%;
664
+ border-left: none;
665
+ border-right: none;
666
+ border-top: none;
667
+ background: none;
668
+ cursor: pointer;
669
+ text-align: left;
548
670
  }
549
671
 
550
- [data-theme="lequipe"] .eb-player .eb-settings-category:last-child,
551
672
  [data-theme="lequipe"] .eb-player .eb-settings-item:last-child {
552
673
  border-bottom: none;
553
674
  }
554
675
 
555
- [data-theme="lequipe"] .eb-player .eb-settings-category:hover,
556
- [data-theme="lequipe"] .eb-player .eb-settings-item:hover,
557
- [data-theme="lequipe"] .eb-player .eb-settings-back:hover {
676
+ [data-theme="lequipe"] .eb-player .eb-settings-item:hover {
558
677
  background: rgba(255,255,255,.05);
559
678
  }
560
679
 
561
- /* Selected item text */
680
+ /* Selected item text — brighter */
562
681
  [data-theme="lequipe"] .eb-player .eb-settings-item--selected {
563
682
  color: #fff;
564
683
  font-weight: 500;
565
684
  }
566
685
 
567
- /* Active selection dot (filled red) */
686
+ /* Active selection dot (filled accent) */
568
687
  [data-theme="lequipe"] .eb-player .eb-settings-item--selected::after {
569
688
  content: '';
570
- width: 10px;
571
- height: 10px;
689
+ width: 12px;
690
+ height: 12px;
572
691
  border-radius: 50%;
573
692
  background: var(--eb-accent, #d61e00);
574
- box-shadow: 0 0 0 3px rgba(214,30,0,.25);
575
693
  flex-shrink: 0;
576
694
  }
577
695
 
578
696
  /* Non-selected items: hollow dot */
579
697
  [data-theme="lequipe"] .eb-player .eb-settings-item:not(.eb-settings-item--selected)::after {
580
698
  content: '';
581
- width: 10px;
582
- height: 10px;
699
+ width: 12px;
700
+ height: 12px;
583
701
  border-radius: 50%;
584
702
  border: 2px solid rgba(255,255,255,.2);
585
703
  flex-shrink: 0;
586
704
  }
587
705
 
588
- /* Back button */
589
- [data-theme="lequipe"] .eb-player .eb-settings-back {
590
- gap: 14px;
591
- font-weight: 600;
592
- }
593
-
594
706
  /* ============================================================
595
707
  Loading spinner
596
708
  ============================================================ */
@@ -2219,9 +2331,10 @@
2219
2331
  position: absolute;
2220
2332
  background: rgba(0, 0, 0, 0.85);
2221
2333
  border-radius: 6px;
2222
- min-width: 180px;
2334
+ min-width: 160px;
2335
+ max-width: 220px;
2223
2336
  overflow: hidden;
2224
- font-size: 13px;
2337
+ font-size: 12px;
2225
2338
  z-index: 40;
2226
2339
  }
2227
2340
 
@@ -2249,6 +2362,20 @@
2249
2362
  padding: 4px 0;
2250
2363
  }
2251
2364
 
2365
+ .eb-settings-submenu .eb-settings-menu {
2366
+ max-height: 200px;
2367
+ overflow-y: auto;
2368
+ }
2369
+
2370
+ .eb-settings-submenu .eb-settings-menu::-webkit-scrollbar {
2371
+ width: 4px;
2372
+ }
2373
+
2374
+ .eb-settings-submenu .eb-settings-menu::-webkit-scrollbar-thumb {
2375
+ background: rgba(255, 255, 255, 0.3);
2376
+ border-radius: 2px;
2377
+ }
2378
+
2252
2379
  .eb-settings-category,
2253
2380
  .eb-settings-item,
2254
2381
  .eb-settings-back {
@@ -2256,12 +2383,12 @@
2256
2383
  align-items: center;
2257
2384
  justify-content: space-between;
2258
2385
  width: 100%;
2259
- padding: 8px 14px;
2386
+ padding: 5px 12px;
2260
2387
  border: none;
2261
2388
  background: none;
2262
2389
  color: #fff;
2263
2390
  cursor: pointer;
2264
- font-size: 13px;
2391
+ font-size: 12px;
2265
2392
  text-align: left;
2266
2393
  }
2267
2394
 
@@ -4,6 +4,8 @@
4
4
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.EBPlayer = {}));
5
5
  })(this, (function (exports) { 'use strict';
6
6
 
7
+ var __EB_PLAYER_VERSION__ = "2.0.4";
8
+
7
9
  /**
8
10
  * Finite State Machine for player playback state transitions.
9
11
  *
@@ -308,9 +310,25 @@
308
310
  right: ['forward']
309
311
  }
310
312
  };
313
+ const LEQUIPE_LAYOUT = {
314
+ topBar: {
315
+ left: [],
316
+ right: ['pip', 'settings']
317
+ },
318
+ bottomBar: {
319
+ left: ['play-pause', 'live-sync', 'time'],
320
+ center: ['seekbar'],
321
+ right: ['volume', 'fullscreen']
322
+ },
323
+ middleBar: {
324
+ left: ['rewind'],
325
+ center: [],
326
+ right: ['forward']
327
+ }
328
+ };
311
329
  const THEME_LAYOUTS = {
312
330
  v2: V2_LAYOUT,
313
- lequipe: V2_LAYOUT,
331
+ lequipe: LEQUIPE_LAYOUT,
314
332
  modern: V2_LAYOUT,
315
333
  };
316
334
  /**
@@ -532,6 +550,30 @@
532
550
  fr: 'La diffusion a échoué. Reprise de la lecture locale.',
533
551
  ar: 'Casting failed. Resuming local playback.',
534
552
  es: 'Error en la transmisión. Reanudando la reproducción local.'
553
+ },
554
+ 'settings.audio': {
555
+ en: 'Audio track', fr: 'Piste audio', ar: 'مسار صوتي', es: 'Pista de audio'
556
+ },
557
+ 'settings.subtitles': {
558
+ en: 'Subtitles', fr: 'Sous-titres', ar: 'ترجمات', es: 'Subtítulos'
559
+ },
560
+ 'settings.speed': {
561
+ en: 'Playback speed', fr: 'Vitesse de lecture', ar: 'سرعة التشغيل', es: 'Velocidad'
562
+ },
563
+ 'settings.quality': {
564
+ en: 'Quality', fr: 'Qualité', ar: 'الجودة', es: 'Calidad'
565
+ },
566
+ 'settings.default': {
567
+ en: 'Default', fr: 'Défaut', ar: 'افتراضي', es: 'Predeterminado'
568
+ },
569
+ 'settings.off': {
570
+ en: 'Off', fr: 'Désactivés', ar: 'إيقاف', es: 'Desactivado'
571
+ },
572
+ 'settings.normal': {
573
+ en: 'Normal', fr: 'Normale', ar: 'عادي', es: 'Normal'
574
+ },
575
+ 'settings.auto': {
576
+ en: 'Auto', fr: 'Auto', ar: 'تلقائي', es: 'Auto'
535
577
  }
536
578
  };
537
579
  /**
@@ -1330,8 +1372,14 @@
1330
1372
  const trackEl = event.currentTarget;
1331
1373
  this.dragValue = this.eventToTime(event, trackEl);
1332
1374
  this.scheduleRender();
1375
+ // During drag with pointer capture, only update tooltip when pointer is within track bounds
1376
+ const rect = trackEl.getBoundingClientRect();
1377
+ if (event.clientX >= rect.left && event.clientX <= rect.right) {
1378
+ this.updateTooltip(event);
1379
+ }
1380
+ return;
1333
1381
  }
1334
- // Always update tooltip on pointermove over the track
1382
+ // Always update tooltip on pointermove over the track when not dragging
1335
1383
  this.updateTooltip(event);
1336
1384
  }
1337
1385
  handlePointerUp(event) {
@@ -1341,6 +1389,11 @@
1341
1389
  const seekTime = this.eventToTime(event, trackEl);
1342
1390
  this.isDragging = false;
1343
1391
  this.bus.emit('seek', { time: seekTime });
1392
+ // Hide tooltip if pointer is outside the track bounds after drag ends
1393
+ const rect = trackEl.getBoundingClientRect();
1394
+ if (event.clientX < rect.left || event.clientX > rect.right) {
1395
+ this.tooltipVisible = false;
1396
+ }
1344
1397
  this.render();
1345
1398
  }
1346
1399
  handlePointerLeave() {
@@ -1655,16 +1708,20 @@
1655
1708
  this.mode = 'root';
1656
1709
  this.verticalDir = 'up';
1657
1710
  this.horizontalDir = 'right';
1711
+ this.outsideClickHandler = null;
1712
+ this.outsideClickTimer = null;
1658
1713
  }
1659
1714
  onConnect() {
1660
1715
  this.state.on('settingsOpen', () => {
1661
1716
  // When settings close, reset to root mode
1662
1717
  if (!this.state.settingsOpen) {
1663
1718
  this.mode = 'root';
1719
+ this.removeOutsideClickListener();
1664
1720
  }
1665
1721
  else {
1666
1722
  // Compute placement when opening
1667
1723
  this.computePlacement();
1724
+ this.addOutsideClickListener();
1668
1725
  }
1669
1726
  this.render();
1670
1727
  }, { signal: this.signal });
@@ -1675,6 +1732,8 @@
1675
1732
  this.state.on('subtitleTracks', () => this.render(), { signal: this.signal });
1676
1733
  this.state.on('currentSubtitleTrack', () => this.render(), { signal: this.signal });
1677
1734
  this.state.on('playbackRate', () => this.render(), { signal: this.signal });
1735
+ // Clean up outside-click listener when component disconnects
1736
+ this.signal.addEventListener('abort', () => this.removeOutsideClickListener());
1678
1737
  this.render();
1679
1738
  }
1680
1739
  /**
@@ -1702,53 +1761,76 @@
1702
1761
  this.mode = 'root';
1703
1762
  this.render();
1704
1763
  }
1764
+ /**
1765
+ * Returns the display label for the currently selected value in a category.
1766
+ */
1767
+ currentValueLabel(mode) {
1768
+ const i18n = this.i18n;
1769
+ if (mode === 'quality') {
1770
+ const levels = this.state.qualityLevels;
1771
+ const current = this.state.currentQuality;
1772
+ if (current === -1)
1773
+ return i18n.t('settings.auto');
1774
+ const level = levels[current];
1775
+ return level?.height ? `${level.height}p` : i18n.t('settings.auto');
1776
+ }
1777
+ if (mode === 'speed') {
1778
+ const rate = this.state.playbackRate;
1779
+ return rate === 1 ? i18n.t('settings.normal') : `${rate}x`;
1780
+ }
1781
+ if (mode === 'audio') {
1782
+ const tracks = this.state.audioTracks;
1783
+ const current = this.state.currentAudioTrack;
1784
+ const track = tracks[current];
1785
+ return track?.name || track?.lang || i18n.t('settings.default');
1786
+ }
1787
+ if (mode === 'subtitles') {
1788
+ const current = this.state.currentSubtitleTrack;
1789
+ if (current === -1)
1790
+ return i18n.t('settings.off');
1791
+ const tracks = this.state.subtitleTracks;
1792
+ const track = tracks[current];
1793
+ return track?.name || track?.lang || `Track ${current}`;
1794
+ }
1795
+ return '';
1796
+ }
1705
1797
  renderRootMenu() {
1706
1798
  const qualityLevels = this.state.qualityLevels;
1707
1799
  const audioTracks = this.state.audioTracks;
1708
1800
  const subtitleTracks = this.state.subtitleTracks;
1801
+ const i18n = this.i18n;
1709
1802
  const showQuality = qualityLevels.length > 0;
1710
1803
  const showSpeed = this.config.speed === true;
1711
1804
  const showAudio = audioTracks.length > 1;
1712
1805
  const showSubtitles = subtitleTracks.length > 0;
1806
+ const row = (iconName, label, value, mode) => b `
1807
+ <li>
1808
+ <button class="eb-settings-category" @click="${() => this.navigateTo(mode)}">
1809
+ <span class="eb-settings-category__icon">${icon(iconName)}</span>
1810
+ <span class="eb-settings-category__label">${label}</span>
1811
+ <span class="eb-settings-category__value">${value}</span>
1812
+ <span class="eb-settings-category__chevron">${icon('chevron-right')}</span>
1813
+ </button>
1814
+ </li>
1815
+ `;
1713
1816
  return b `
1714
1817
  <ul class="eb-settings-menu eb-settings-root">
1715
- ${showQuality ? b `
1716
- <li>
1717
- <button class="eb-settings-category" @click="${() => this.navigateTo('quality')}">
1718
- Quality
1719
- </button>
1720
- </li>
1721
- ` : ''}
1722
- ${showSpeed ? b `
1723
- <li>
1724
- <button class="eb-settings-category" @click="${() => this.navigateTo('speed')}">
1725
- Speed
1726
- </button>
1727
- </li>
1728
- ` : ''}
1729
- ${showAudio ? b `
1730
- <li>
1731
- <button class="eb-settings-category" @click="${() => this.navigateTo('audio')}">
1732
- Audio
1733
- </button>
1734
- </li>
1735
- ` : ''}
1736
- ${showSubtitles ? b `
1737
- <li>
1738
- <button class="eb-settings-category" @click="${() => this.navigateTo('subtitles')}">
1739
- Subtitles
1740
- </button>
1741
- </li>
1742
- ` : ''}
1818
+ ${showAudio ? row('audio', i18n.t('settings.audio'), this.currentValueLabel('audio'), 'audio') : ''}
1819
+ ${showSubtitles ? row('subtitle', i18n.t('settings.subtitles'), this.currentValueLabel('subtitles'), 'subtitles') : ''}
1820
+ ${showSpeed ? row('speed', i18n.t('settings.speed'), this.currentValueLabel('speed'), 'speed') : ''}
1821
+ ${showQuality ? row('quality', i18n.t('settings.quality'), this.currentValueLabel('quality'), 'quality') : ''}
1743
1822
  </ul>
1744
1823
  `;
1745
1824
  }
1746
1825
  renderSubMenu(title, items, onSelect) {
1747
1826
  return b `
1748
1827
  <div class="eb-settings-submenu">
1749
- <button class="eb-settings-back" @click="${() => this.navigateBack()}">
1750
- ${title}
1751
- </button>
1828
+ <div class="eb-settings-header">
1829
+ <button class="eb-settings-back" @click="${() => this.navigateBack()}">
1830
+ ${icon('chevron-left')}
1831
+ </button>
1832
+ <span class="eb-settings-header__title">${title}</span>
1833
+ </div>
1752
1834
  <ul class="eb-settings-menu">
1753
1835
  ${items.map((item) => b `
1754
1836
  <li>
@@ -1768,14 +1850,14 @@
1768
1850
  const levels = this.state.qualityLevels;
1769
1851
  const currentQuality = this.state.currentQuality;
1770
1852
  const items = getQualityItems(levels, currentQuality);
1771
- return this.renderSubMenu('Quality', items, (item) => {
1853
+ return this.renderSubMenu(this.i18n.t('settings.quality'), items, (item) => {
1772
1854
  this.bus.emit('settings-select-quality', { index: item.value });
1773
1855
  });
1774
1856
  }
1775
1857
  renderSpeedMenu() {
1776
1858
  const currentRate = this.state.playbackRate;
1777
1859
  const items = getSpeedItems(currentRate);
1778
- return this.renderSubMenu('Speed', items, (item) => {
1860
+ return this.renderSubMenu(this.i18n.t('settings.speed'), items, (item) => {
1779
1861
  this.bus.emit('settings-select-speed', { rate: item.value });
1780
1862
  });
1781
1863
  }
@@ -1783,7 +1865,7 @@
1783
1865
  const tracks = this.state.audioTracks;
1784
1866
  const currentTrack = this.state.currentAudioTrack;
1785
1867
  const items = getAudioItems(tracks, currentTrack);
1786
- return this.renderSubMenu('Audio', items, (item) => {
1868
+ return this.renderSubMenu(this.i18n.t('settings.audio'), items, (item) => {
1787
1869
  this.bus.emit('settings-select-audio', { index: item.value });
1788
1870
  });
1789
1871
  }
@@ -1791,10 +1873,37 @@
1791
1873
  const tracks = this.state.subtitleTracks;
1792
1874
  const currentTrack = this.state.currentSubtitleTrack;
1793
1875
  const items = getSubtitleItems(tracks, currentTrack);
1794
- return this.renderSubMenu('Subtitles', items, (item) => {
1876
+ return this.renderSubMenu(this.i18n.t('settings.subtitles'), items, (item) => {
1795
1877
  this.bus.emit('settings-select-subtitle', { index: item.value });
1796
1878
  });
1797
1879
  }
1880
+ addOutsideClickListener() {
1881
+ this.removeOutsideClickListener();
1882
+ // Defer to next tick so the opening click doesn't immediately close
1883
+ this.outsideClickTimer = setTimeout(() => {
1884
+ this.outsideClickTimer = null;
1885
+ // Guard: panel may have closed before this timer fires
1886
+ if (!this.state?.settingsOpen)
1887
+ return;
1888
+ this.outsideClickHandler = (event) => {
1889
+ const wrapper = this.el?.querySelector('.eb-settings-wrapper');
1890
+ if (wrapper && !wrapper.contains(event.target)) {
1891
+ this.state.settingsOpen = false;
1892
+ }
1893
+ };
1894
+ document.addEventListener('click', this.outsideClickHandler, { capture: true });
1895
+ }, 0);
1896
+ }
1897
+ removeOutsideClickListener() {
1898
+ if (this.outsideClickTimer !== null) {
1899
+ clearTimeout(this.outsideClickTimer);
1900
+ this.outsideClickTimer = null;
1901
+ }
1902
+ if (this.outsideClickHandler !== null) {
1903
+ document.removeEventListener('click', this.outsideClickHandler, { capture: true });
1904
+ this.outsideClickHandler = null;
1905
+ }
1906
+ }
1798
1907
  toggleSettings() {
1799
1908
  this.state.settingsOpen = !this.state.settingsOpen;
1800
1909
  }
@@ -5034,7 +5143,7 @@
5034
5143
  // Build driver config — only spread known engineSettings keys that hls.js recognises,
5035
5144
  // not the entire engineSettings bag (which may contain player-specific keys like
5036
5145
  // extraParamsCallback that should NOT leak into the hls.js constructor config).
5037
- const { emeEnabled, drmSystems, ...hlsEngineSettings } = config.engineSettings;
5146
+ const { emeEnabled: _emeEnabled, drmSystems: _drmSystems, ...hlsEngineSettings } = config.engineSettings;
5038
5147
  // Remove player-specific keys that are NOT hls.js config options
5039
5148
  const hlsSafeSettings = { ...hlsEngineSettings };
5040
5149
  delete hlsSafeSettings['extraParamsCallback'];
@@ -6226,10 +6335,17 @@
6226
6335
  }
6227
6336
  }
6228
6337
  // ---------------------------------------------------------------------------
6338
+ // Version
6339
+ // ---------------------------------------------------------------------------
6340
+ // Injected at build time by Rollup's @rollup/plugin-virtual or replaced by bundler.
6341
+ // Falls back to 'dev' when running unbundled (tests, dev server).
6342
+ const VERSION = typeof __EB_PLAYER_VERSION__ !== 'undefined' ? __EB_PLAYER_VERSION__ : 'dev';
6343
+ // ---------------------------------------------------------------------------
6229
6344
  // window.EBPlayer assignment
6230
6345
  // ---------------------------------------------------------------------------
6231
6346
  if (typeof window !== 'undefined') {
6232
- window.EBPlayer = { start, stop, destroy, AVAILABLE_THEMES, THEME_LAYOUTS };
6347
+ console.info(`%cEBPlayer v${VERSION}`, 'color: #1FA9DD; font-weight: bold');
6348
+ window.EBPlayer = { start, stop, destroy, AVAILABLE_THEMES, THEME_LAYOUTS, version: VERSION };
6233
6349
  }
6234
6350
 
6235
6351
  /**