myetv-player 1.1.0 → 1.1.2
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.
- package/README.md +69 -0
- package/css/myetv-player.css +726 -11937
- package/css/myetv-player.min.css +1 -1
- package/dist/myetv-player.js +254 -111
- package/dist/myetv-player.min.js +249 -106
- package/package.json +3 -1
- package/plugins/youtube/myetv-player-youtube-plugin.js +237 -19
- package/scss/_controls.scss +120 -317
- package/scss/_menus.scss +117 -4023
- package/scss/_progress-bar.scss +168 -2052
- package/scss/_title-overlay.scss +10 -2239
- package/scss/_video.scss +62 -2167
- package/scss/_volume.scss +25 -1846
- package/src/controls.js +7 -7
- package/src/core.js +121 -69
- package/src/events.js +123 -25
- package/src/subtitles.js +3 -10
package/dist/myetv-player.js
CHANGED
|
@@ -433,6 +433,7 @@ constructor(videoElement, options = {}) {
|
|
|
433
433
|
brandLogoEnabled: false, // Enable/disable brand logo
|
|
434
434
|
brandLogoUrl: '', // URL for brand logo image
|
|
435
435
|
brandLogoLinkUrl: '', // Optional URL to open when clicking the logo
|
|
436
|
+
brandLogoTooltipText: '', // Tooltip text for brand logo
|
|
436
437
|
playlistEnabled: true, // Enable/disable playlist detection
|
|
437
438
|
playlistAutoPlay: true, // Auto-play next video when current ends
|
|
438
439
|
playlistLoop: false, // Loop playlist when reaching the end
|
|
@@ -465,7 +466,6 @@ constructor(videoElement, options = {}) {
|
|
|
465
466
|
this.currentQualityIndex = 0;
|
|
466
467
|
this.qualities = [];
|
|
467
468
|
this.originalSources = [];
|
|
468
|
-
this.setupMenuToggles(); // Initialize menu toggle system
|
|
469
469
|
this.isPiPSupported = this.checkPiPSupport();
|
|
470
470
|
this.seekTooltip = null;
|
|
471
471
|
this.titleOverlay = null;
|
|
@@ -508,18 +508,42 @@ constructor(videoElement, options = {}) {
|
|
|
508
508
|
|
|
509
509
|
// Custom event system
|
|
510
510
|
this.eventCallbacks = {
|
|
511
|
-
|
|
512
|
-
'
|
|
513
|
-
'
|
|
514
|
-
'
|
|
515
|
-
'
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
'
|
|
519
|
-
'
|
|
520
|
-
'
|
|
521
|
-
'
|
|
522
|
-
|
|
511
|
+
// Core lifecycle events
|
|
512
|
+
'playerready': [], // Fired when player is fully initialized and ready
|
|
513
|
+
'played': [], // Fired when video starts playing
|
|
514
|
+
'paused': [], // Fired when video is paused
|
|
515
|
+
'ended': [], // Fired when video playback ends
|
|
516
|
+
|
|
517
|
+
// Playback state events
|
|
518
|
+
'playing': [], // Fired when video is actually playing (after buffering)
|
|
519
|
+
'waiting': [], // Fired when video is waiting for data (buffering)
|
|
520
|
+
'seeking': [], // Fired when seek operation starts
|
|
521
|
+
'seeked': [], // Fired when seek operation completes
|
|
522
|
+
|
|
523
|
+
// Loading events
|
|
524
|
+
'loadstart': [], // Fired when browser starts looking for media
|
|
525
|
+
'loadedmetadata': [], // Fired when metadata (duration, dimensions) is loaded
|
|
526
|
+
'loadeddata': [], // Fired when data for current frame is loaded
|
|
527
|
+
'canplay': [], // Fired when browser can start playing video
|
|
528
|
+
'progress': [], // Fired periodically while downloading media
|
|
529
|
+
'durationchange': [], // Fired when duration attribute changes
|
|
530
|
+
|
|
531
|
+
// Error events
|
|
532
|
+
'error': [], // Fired when media loading or playback error occurs
|
|
533
|
+
'stalled': [], // Fired when browser is trying to get data but it's not available
|
|
534
|
+
|
|
535
|
+
// Control events
|
|
536
|
+
'timeupdate': [], // Fired when current playback position changes
|
|
537
|
+
'volumechange': [], // Fired when volume or muted state changes
|
|
538
|
+
'speedchange': [], // Fired when playback speed changes
|
|
539
|
+
'qualitychange': [], // Fired when video quality changes
|
|
540
|
+
|
|
541
|
+
// Feature events
|
|
542
|
+
'subtitlechange': [], // Fired when subtitle track changes
|
|
543
|
+
'chapterchange': [], // Fired when video chapter changes
|
|
544
|
+
'pipchange': [], // Fired when picture-in-picture mode changes
|
|
545
|
+
'fullscreenchange': [], // Fired when fullscreen mode changes
|
|
546
|
+
'playlistchange': [] // Fired when playlist item changes
|
|
523
547
|
};
|
|
524
548
|
|
|
525
549
|
// Playlist management
|
|
@@ -553,6 +577,7 @@ constructor(videoElement, options = {}) {
|
|
|
553
577
|
this.interceptAutoLoading();
|
|
554
578
|
this.createPlayerStructure();
|
|
555
579
|
this.initializeElements();
|
|
580
|
+
this.setupMenuToggles(); // Initialize menu toggle system
|
|
556
581
|
// audio player adaptation
|
|
557
582
|
this.adaptToAudioFile = function () {
|
|
558
583
|
if (this.options.audiofile) {
|
|
@@ -881,6 +906,14 @@ markPlayerReady() {
|
|
|
881
906
|
this.container.classList.add('player-initialized');
|
|
882
907
|
}
|
|
883
908
|
|
|
909
|
+
this.triggerEvent('playerready', {
|
|
910
|
+
playerState: this.getPlayerState(),
|
|
911
|
+
qualities: this.qualities,
|
|
912
|
+
subtitles: this.textTracks,
|
|
913
|
+
chapters: this.chapters,
|
|
914
|
+
playlist: this.getPlaylistInfo()
|
|
915
|
+
});
|
|
916
|
+
|
|
884
917
|
if (this.video) {
|
|
885
918
|
this.video.style.visibility = '';
|
|
886
919
|
this.video.style.opacity = '';
|
|
@@ -1161,74 +1194,84 @@ initializeElements() {
|
|
|
1161
1194
|
this.speedMenu = this.controls?.querySelector('.speed-menu');
|
|
1162
1195
|
this.qualityMenu = this.controls?.querySelector('.quality-menu');
|
|
1163
1196
|
this.subtitlesMenu = this.controls?.querySelector('.subtitles-menu');
|
|
1197
|
+
// Apply seek handle shape from options
|
|
1198
|
+
if (this.progressHandle && this.options.seekHandleShape) {
|
|
1199
|
+
this.setSeekHandleShape(this.options.seekHandleShape);
|
|
1200
|
+
}
|
|
1164
1201
|
}
|
|
1165
1202
|
|
|
1166
1203
|
// Generic method to close all active menus (works with plugins too)
|
|
1167
1204
|
closeAllMenus() {
|
|
1168
|
-
|
|
1169
|
-
const allMenus = this.controls?.querySelectorAll('[class*="-menu"].active');
|
|
1170
|
-
allMenus?.forEach(menu => {
|
|
1171
|
-
menu.classList.remove('active');
|
|
1172
|
-
});
|
|
1205
|
+
if (!this.controls) return;
|
|
1173
1206
|
|
|
1174
|
-
|
|
1175
|
-
const
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1207
|
+
const menus = this.controls.querySelectorAll('.speed-menu, .quality-menu, .subtitles-menu, .settings-menu');
|
|
1208
|
+
const buttons = this.controls.querySelectorAll('.control-btn');
|
|
1209
|
+
|
|
1210
|
+
menus.forEach(menu => menu.classList.remove('active'));
|
|
1211
|
+
buttons.forEach(btn => btn.classList.remove('active'));
|
|
1212
|
+
|
|
1213
|
+
this.currentOpenMenu = null;
|
|
1214
|
+
|
|
1215
|
+
if (this.options.debug) {
|
|
1216
|
+
console.log('All menus closed');
|
|
1217
|
+
}
|
|
1179
1218
|
}
|
|
1180
1219
|
|
|
1181
1220
|
// Generic menu toggle setup (works with core menus and plugin menus)
|
|
1182
1221
|
setupMenuToggles() {
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
const menu = this.controls.querySelector('.' + menuName);
|
|
1200
|
-
if (menu) {
|
|
1201
|
-
menuClass = menuName;
|
|
1202
|
-
break;
|
|
1203
|
-
}
|
|
1204
|
-
}
|
|
1222
|
+
if (!this.controls) return;
|
|
1223
|
+
|
|
1224
|
+
this.currentOpenMenu = null;
|
|
1225
|
+
|
|
1226
|
+
this.controls.addEventListener('click', (e) => {
|
|
1227
|
+
const button = e.target.closest('.control-btn');
|
|
1228
|
+
if (!button) return;
|
|
1229
|
+
|
|
1230
|
+
const buttonClasses = Array.from(button.classList);
|
|
1231
|
+
let menuElement = null;
|
|
1232
|
+
|
|
1233
|
+
for (const cls of buttonClasses) {
|
|
1234
|
+
if (cls.endsWith('-btn')) {
|
|
1235
|
+
const menuClass = cls.replace('-btn', '-menu');
|
|
1236
|
+
menuElement = this.controls.querySelector(`.${menuClass}`);
|
|
1237
|
+
if (menuElement) break;
|
|
1205
1238
|
}
|
|
1239
|
+
}
|
|
1206
1240
|
|
|
1207
|
-
|
|
1241
|
+
if (!menuElement) return;
|
|
1208
1242
|
|
|
1209
|
-
|
|
1243
|
+
e.stopPropagation();
|
|
1244
|
+
e.preventDefault();
|
|
1210
1245
|
|
|
1211
|
-
|
|
1212
|
-
const menu = this.controls.querySelector('.' + menuClass);
|
|
1213
|
-
const isOpen = menu.classList.contains('active');
|
|
1246
|
+
const isOpen = menuElement.classList.contains('active');
|
|
1214
1247
|
|
|
1215
|
-
|
|
1216
|
-
this.closeAllMenus();
|
|
1248
|
+
this.closeAllMenus();
|
|
1217
1249
|
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1250
|
+
if (!isOpen) {
|
|
1251
|
+
menuElement.classList.add('active');
|
|
1252
|
+
button.classList.add('active');
|
|
1253
|
+
this.currentOpenMenu = menuElement;
|
|
1254
|
+
if (this.options.debug) {
|
|
1255
|
+
console.log('Menu opened:', menuElement.className);
|
|
1222
1256
|
}
|
|
1223
|
-
}
|
|
1224
|
-
|
|
1257
|
+
} else {
|
|
1258
|
+
this.currentOpenMenu = null;
|
|
1259
|
+
if (this.options.debug) {
|
|
1260
|
+
console.log('Menu closed:', menuElement.className);
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
});
|
|
1225
1264
|
|
|
1226
|
-
// Close menus when clicking outside controls
|
|
1227
1265
|
document.addEventListener('click', (e) => {
|
|
1228
|
-
if (!this.controls
|
|
1266
|
+
if (!this.controls) return;
|
|
1267
|
+
if (!this.controls.contains(e.target)) {
|
|
1229
1268
|
this.closeAllMenus();
|
|
1230
1269
|
}
|
|
1231
1270
|
});
|
|
1271
|
+
|
|
1272
|
+
if (this.options.debug) {
|
|
1273
|
+
console.log('✅ Menu toggle system initialized (click-based, auto-close)');
|
|
1274
|
+
}
|
|
1232
1275
|
}
|
|
1233
1276
|
|
|
1234
1277
|
updateVolumeSliderVisual() {
|
|
@@ -1539,9 +1582,11 @@ updateBuffer() {
|
|
|
1539
1582
|
}
|
|
1540
1583
|
|
|
1541
1584
|
startSeeking(e) {
|
|
1585
|
+
if (e.cancelable) e.preventDefault();
|
|
1542
1586
|
if (this.isChangingQuality) return;
|
|
1543
1587
|
|
|
1544
1588
|
this.isUserSeeking = true;
|
|
1589
|
+
this.progressContainer.classList.add('seeking');
|
|
1545
1590
|
this.seek(e);
|
|
1546
1591
|
e.preventDefault();
|
|
1547
1592
|
|
|
@@ -1553,6 +1598,7 @@ startSeeking(e) {
|
|
|
1553
1598
|
}
|
|
1554
1599
|
|
|
1555
1600
|
continueSeeking(e) {
|
|
1601
|
+
if (e.cancelable) e.preventDefault();
|
|
1556
1602
|
if (this.isUserSeeking && !this.isChangingQuality) {
|
|
1557
1603
|
this.seek(e);
|
|
1558
1604
|
}
|
|
@@ -1560,9 +1606,13 @@ continueSeeking(e) {
|
|
|
1560
1606
|
|
|
1561
1607
|
endSeeking() {
|
|
1562
1608
|
this.isUserSeeking = false;
|
|
1609
|
+
this.progressContainer.classList.remove('seeking');
|
|
1563
1610
|
}
|
|
1564
1611
|
|
|
1565
1612
|
seek(e) {
|
|
1613
|
+
if (e.cancelable) {
|
|
1614
|
+
e.preventDefault();
|
|
1615
|
+
}
|
|
1566
1616
|
if (!this.video || !this.progressContainer || !this.progressFilled || !this.progressHandle || this.isChangingQuality) return;
|
|
1567
1617
|
|
|
1568
1618
|
const rect = this.progressContainer.getBoundingClientRect();
|
|
@@ -1742,7 +1792,14 @@ createBrandLogo() {
|
|
|
1742
1792
|
const logo = document.createElement('img');
|
|
1743
1793
|
logo.className = 'brand-logo';
|
|
1744
1794
|
logo.src = this.options.brandLogoUrl;
|
|
1745
|
-
logo.alt =
|
|
1795
|
+
logo.alt = 'Brand logo';
|
|
1796
|
+
|
|
1797
|
+
// Add tooltip ONLY if link URL is present
|
|
1798
|
+
if (this.options.brandLogoLinkUrl) {
|
|
1799
|
+
// Use custom tooltip text if provided, otherwise fallback to URL
|
|
1800
|
+
logo.title = this.options.brandLogoTooltipText || this.options.brandLogoLinkUrl;
|
|
1801
|
+
// NON usare data-tooltip per evitare che venga sovrascritto da updateTooltips()
|
|
1802
|
+
}
|
|
1746
1803
|
|
|
1747
1804
|
// Handle loading error
|
|
1748
1805
|
logo.onerror = () => {
|
|
@@ -1758,7 +1815,7 @@ createBrandLogo() {
|
|
|
1758
1815
|
if (this.options.brandLogoLinkUrl) {
|
|
1759
1816
|
logo.style.cursor = 'pointer';
|
|
1760
1817
|
logo.addEventListener('click', (e) => {
|
|
1761
|
-
e.stopPropagation();
|
|
1818
|
+
e.stopPropagation();
|
|
1762
1819
|
window.open(this.options.brandLogoLinkUrl, '_blank', 'noopener,noreferrer');
|
|
1763
1820
|
if (this.options.debug) console.log('Brand logo clicked, opening:', this.options.brandLogoLinkUrl);
|
|
1764
1821
|
});
|
|
@@ -1766,15 +1823,10 @@ createBrandLogo() {
|
|
|
1766
1823
|
logo.style.cursor = 'default';
|
|
1767
1824
|
}
|
|
1768
1825
|
|
|
1769
|
-
// Position the brand logo at the right of the controlbar (at the left of the buttons)
|
|
1770
1826
|
controlsRight.insertBefore(logo, controlsRight.firstChild);
|
|
1771
1827
|
|
|
1772
1828
|
if (this.options.debug) {
|
|
1773
|
-
|
|
1774
|
-
console.log('Brand logo with click handler created for:', this.options.brandLogoLinkUrl);
|
|
1775
|
-
} else {
|
|
1776
|
-
console.log('Brand logo created (no link)');
|
|
1777
|
-
}
|
|
1829
|
+
console.log('Brand logo created with tooltip:', logo.title || 'no tooltip');
|
|
1778
1830
|
}
|
|
1779
1831
|
}
|
|
1780
1832
|
|
|
@@ -1981,7 +2033,7 @@ bindPosterEvents() {
|
|
|
1981
2033
|
// Hide poster when video is loading/playing
|
|
1982
2034
|
this.video.addEventListener('playing', () => {
|
|
1983
2035
|
this.hidePoster();
|
|
1984
|
-
|
|
2036
|
+
});
|
|
1985
2037
|
|
|
1986
2038
|
// Show poster on load if not autoplay
|
|
1987
2039
|
if (!this.options.autoplay) {
|
|
@@ -2420,36 +2472,130 @@ addEventListener(eventType, callback) {
|
|
|
2420
2472
|
}
|
|
2421
2473
|
|
|
2422
2474
|
bindEvents() {
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2475
|
+
if (this.video) {
|
|
2476
|
+
|
|
2477
|
+
// Playback events
|
|
2478
|
+
this.video.addEventListener('playing', () => {
|
|
2479
|
+
this.hideLoading();
|
|
2480
|
+
this.closeAllMenus();
|
|
2481
|
+
// Trigger playing event - video is now actually playing
|
|
2482
|
+
this.triggerEvent('playing', {
|
|
2483
|
+
currentTime: this.getCurrentTime(),
|
|
2484
|
+
duration: this.getDuration()
|
|
2429
2485
|
});
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2486
|
+
});
|
|
2487
|
+
|
|
2488
|
+
this.video.addEventListener('waiting', () => {
|
|
2489
|
+
if (!this.isChangingQuality) {
|
|
2490
|
+
this.showLoading();
|
|
2491
|
+
// Trigger waiting event - video is buffering
|
|
2492
|
+
this.triggerEvent('waiting', {
|
|
2493
|
+
currentTime: this.getCurrentTime()
|
|
2494
|
+
});
|
|
2495
|
+
}
|
|
2496
|
+
});
|
|
2497
|
+
|
|
2498
|
+
this.video.addEventListener('seeking', () => {
|
|
2499
|
+
// Trigger seeking event - seek operation started
|
|
2500
|
+
this.triggerEvent('seeking', {
|
|
2501
|
+
currentTime: this.getCurrentTime(),
|
|
2502
|
+
targetTime: this.video.currentTime
|
|
2436
2503
|
});
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2504
|
+
});
|
|
2505
|
+
|
|
2506
|
+
this.video.addEventListener('seeked', () => {
|
|
2507
|
+
// Trigger seeked event - seek operation completed
|
|
2508
|
+
this.triggerEvent('seeked', {
|
|
2509
|
+
currentTime: this.getCurrentTime()
|
|
2441
2510
|
});
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2511
|
+
});
|
|
2512
|
+
|
|
2513
|
+
// Loading events
|
|
2514
|
+
this.video.addEventListener('loadstart', () => {
|
|
2515
|
+
if (!this.isChangingQuality) {
|
|
2516
|
+
this.showLoading();
|
|
2517
|
+
}
|
|
2518
|
+
// Trigger loadstart event - browser started loading media
|
|
2519
|
+
this.triggerEvent('loadstart');
|
|
2520
|
+
});
|
|
2521
|
+
|
|
2522
|
+
this.video.addEventListener('loadedmetadata', () => {
|
|
2523
|
+
this.updateDuration();
|
|
2524
|
+
|
|
2525
|
+
// Trigger loadedmetadata event - video metadata loaded
|
|
2526
|
+
this.triggerEvent('loadedmetadata', {
|
|
2527
|
+
duration: this.getDuration(),
|
|
2528
|
+
videoWidth: this.video.videoWidth,
|
|
2529
|
+
videoHeight: this.video.videoHeight
|
|
2447
2530
|
});
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2531
|
+
|
|
2532
|
+
// Initialize subtitles after metadata is loaded
|
|
2533
|
+
setTimeout(() => {
|
|
2534
|
+
this.initializeSubtitles();
|
|
2535
|
+
}, 100);
|
|
2536
|
+
});
|
|
2537
|
+
|
|
2538
|
+
this.video.addEventListener('loadeddata', () => {
|
|
2539
|
+
if (!this.isChangingQuality) {
|
|
2540
|
+
this.hideLoading();
|
|
2541
|
+
}
|
|
2542
|
+
// Trigger loadeddata event - current frame data loaded
|
|
2543
|
+
this.triggerEvent('loadeddata', {
|
|
2544
|
+
currentTime: this.getCurrentTime()
|
|
2545
|
+
});
|
|
2546
|
+
});
|
|
2547
|
+
|
|
2548
|
+
this.video.addEventListener('canplay', () => {
|
|
2549
|
+
if (!this.isChangingQuality) {
|
|
2550
|
+
this.hideLoading();
|
|
2551
|
+
}
|
|
2552
|
+
// Trigger canplay event - video can start playing
|
|
2553
|
+
this.triggerEvent('canplay', {
|
|
2554
|
+
currentTime: this.getCurrentTime(),
|
|
2555
|
+
duration: this.getDuration()
|
|
2452
2556
|
});
|
|
2557
|
+
});
|
|
2558
|
+
|
|
2559
|
+
this.video.addEventListener('progress', () => {
|
|
2560
|
+
this.updateBuffer();
|
|
2561
|
+
// Trigger progress event - browser is downloading media
|
|
2562
|
+
this.triggerEvent('progress', {
|
|
2563
|
+
buffered: this.getBufferedTime(),
|
|
2564
|
+
duration: this.getDuration()
|
|
2565
|
+
});
|
|
2566
|
+
});
|
|
2567
|
+
|
|
2568
|
+
this.video.addEventListener('durationchange', () => {
|
|
2569
|
+
this.updateDuration();
|
|
2570
|
+
// Trigger durationchange event - video duration changed
|
|
2571
|
+
this.triggerEvent('durationchange', {
|
|
2572
|
+
duration: this.getDuration()
|
|
2573
|
+
});
|
|
2574
|
+
});
|
|
2575
|
+
|
|
2576
|
+
// Error events
|
|
2577
|
+
this.video.addEventListener('error', (e) => {
|
|
2578
|
+
this.onVideoError(e);
|
|
2579
|
+
// Trigger error event - media loading/playback error occurred
|
|
2580
|
+
this.triggerEvent('error', {
|
|
2581
|
+
code: this.video.error?.code,
|
|
2582
|
+
message: this.video.error?.message,
|
|
2583
|
+
src: this.video.currentSrc || this.video.src
|
|
2584
|
+
});
|
|
2585
|
+
});
|
|
2586
|
+
|
|
2587
|
+
this.video.addEventListener('stalled', () => {
|
|
2588
|
+
// Trigger stalled event - browser is trying to fetch data but it's not available
|
|
2589
|
+
this.triggerEvent('stalled', {
|
|
2590
|
+
currentTime: this.getCurrentTime()
|
|
2591
|
+
});
|
|
2592
|
+
});
|
|
2593
|
+
|
|
2594
|
+
|
|
2595
|
+
this.video.addEventListener('timeupdate', () => this.updateProgress());
|
|
2596
|
+
|
|
2597
|
+
this.video.addEventListener('ended', () => this.onVideoEnded());
|
|
2598
|
+
|
|
2453
2599
|
// Complete video click logic with doubleTapPause support (DESKTOP)
|
|
2454
2600
|
this.video.addEventListener('click', () => {
|
|
2455
2601
|
if (!this.options.pauseClick) return;
|
|
@@ -2556,6 +2702,10 @@ addEventListener(eventType, callback) {
|
|
|
2556
2702
|
// Mouse events (desktop)
|
|
2557
2703
|
this.progressContainer.addEventListener('click', (e) => this.seek(e));
|
|
2558
2704
|
this.progressContainer.addEventListener('mousedown', (e) => this.startSeeking(e));
|
|
2705
|
+
if (this.progressHandle) {
|
|
2706
|
+
this.progressHandle.addEventListener('mousedown', this.startSeeking.bind(this));
|
|
2707
|
+
this.progressHandle.addEventListener('touchstart', this.startSeeking.bind(this), { passive: false });
|
|
2708
|
+
}
|
|
2559
2709
|
|
|
2560
2710
|
// Touch events (mobile)
|
|
2561
2711
|
this.progressContainer.addEventListener('touchstart', (e) => {
|
|
@@ -2978,13 +3128,13 @@ createControls() {
|
|
|
2978
3128
|
const controlsHTML = `
|
|
2979
3129
|
<div class="controls" id="${controlsId}">
|
|
2980
3130
|
<div class="progress-container">
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
3131
|
+
<div class="progress-bar">
|
|
3132
|
+
<div class="progress-buffer"></div>
|
|
3133
|
+
<div class="progress-filled"></div>
|
|
3134
|
+
</div>
|
|
3135
|
+
<div class="progress-handle progress-handle-${this.options.seekHandleShape}"></div> <!-- ✅ Fuori da progress-bar -->
|
|
3136
|
+
${this.options.showSeekTooltip ? '<div class="seek-tooltip">0:00</div>' : ''}
|
|
3137
|
+
</div>
|
|
2988
3138
|
|
|
2989
3139
|
<div class="controls-main">
|
|
2990
3140
|
<div class="controls-left">
|
|
@@ -4979,11 +5129,11 @@ updateSubtitlesButton() {
|
|
|
4979
5129
|
var subtitlesBtn = this.controls && this.controls.querySelector('.subtitles-btn');
|
|
4980
5130
|
if (!subtitlesBtn) return;
|
|
4981
5131
|
|
|
5132
|
+
subtitlesBtn.classList.remove('active');
|
|
5133
|
+
|
|
4982
5134
|
if (this.subtitlesEnabled) {
|
|
4983
|
-
subtitlesBtn.classList.add('active');
|
|
4984
5135
|
subtitlesBtn.title = this.t('subtitlesdisable');
|
|
4985
5136
|
} else {
|
|
4986
|
-
subtitlesBtn.classList.remove('active');
|
|
4987
5137
|
subtitlesBtn.title = this.t('subtitlesenable');
|
|
4988
5138
|
}
|
|
4989
5139
|
}
|
|
@@ -5019,14 +5169,6 @@ updateSubtitlesUI() {
|
|
|
5019
5169
|
bindSubtitleEvents() {
|
|
5020
5170
|
var self = this;
|
|
5021
5171
|
|
|
5022
|
-
var subtitlesBtn = this.controls && this.controls.querySelector('.subtitles-btn');
|
|
5023
|
-
if (subtitlesBtn) {
|
|
5024
|
-
subtitlesBtn.addEventListener('click', function (e) {
|
|
5025
|
-
e.stopPropagation();
|
|
5026
|
-
self.toggleSubtitles();
|
|
5027
|
-
});
|
|
5028
|
-
}
|
|
5029
|
-
|
|
5030
5172
|
var subtitlesMenu = this.controls && this.controls.querySelector('.subtitles-menu');
|
|
5031
5173
|
if (subtitlesMenu) {
|
|
5032
5174
|
subtitlesMenu.addEventListener('click', function (e) {
|
|
@@ -5035,6 +5177,7 @@ bindSubtitleEvents() {
|
|
|
5035
5177
|
}
|
|
5036
5178
|
}
|
|
5037
5179
|
|
|
5180
|
+
|
|
5038
5181
|
handleSubtitlesMenuClick(e) {
|
|
5039
5182
|
if (!e.target.classList.contains('subtitles-option')) return;
|
|
5040
5183
|
|