myetv-player 1.6.0 → 1.6.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.
@@ -0,0 +1,1037 @@
1
+ /**
2
+ * MyeTV Iframe Banner Ads Plugin
3
+ * Display iframe banner ads with countdown timer and auto-close functionality
4
+ * Supports both direct iframe URL and external ad script injection
5
+ *
6
+ * Created by https://www.myetv.tv - https://oskarcosimo.com
7
+ *
8
+ * @example
9
+ * // Method 1: Direct iframe URL
10
+ * const player = new MYETVvideoplayer('video', {
11
+ * plugins: {
12
+ * 'iframe-banner-ads': {
13
+ * url: 'https://your-ads-server.com/banner.html',
14
+ * duration: 7,
15
+ * opacity: 0.8,
16
+ * minTimeBetweenAds: 60, // seconds - minimum time before showing next ad
17
+ * repeatInterval: 90 // seconds - show ad every 90 seconds during playback
18
+ * }
19
+ * }
20
+ * });
21
+ *
22
+ * // Method 2: External ad script
23
+ * const player = new MYETVvideoplayer('video', {
24
+ * plugins: {
25
+ * 'iframe-banner-ads': {
26
+ * adScript: 'https://store.myetv.tv/ads/CDN/publishers/adcode.js',
27
+ * adParams: {
28
+ * 'data-size': '728x90',
29
+ * 'data-language': '',
30
+ * 'data-country': 'all',
31
+ * 'data-category': 'all',
32
+ * 'data-uidcode': '7b71af83c540d5919a8c47baf3ef2b76d6c0aa4554c716ddce0a26d9cb88fd57'
33
+ * },
34
+ * duration: 7,
35
+ * opacity: 0.8,
36
+ * minTimeBetweenAds: 60,
37
+ * repeatInterval: 90
38
+ * }
39
+ * }
40
+ * });
41
+ */
42
+
43
+ (function () {
44
+ 'use strict';
45
+
46
+ class IframeBannerAds {
47
+ constructor(player, options) {
48
+ this.player = player;
49
+ this.options = {
50
+ url: options.url || '',
51
+ adScript: options.adScript || '',
52
+ adParams: options.adParams || {},
53
+ duration: options.duration || 5, // seconds
54
+ opacity: options.opacity !== undefined ? options.opacity : 0.85,
55
+ showOnPlay: options.showOnPlay !== undefined ? options.showOnPlay : true,
56
+ showOnPause: options.showOnPause !== undefined ? options.showOnPause : false,
57
+ closeable: options.closeable !== undefined ? options.closeable : true,
58
+ minTimeBetweenAds: options.minTimeBetweenAds || 0, // seconds - 0 = disabled
59
+ repeatInterval: options.repeatInterval || 0, // seconds - 0 = disabled
60
+ cookieName: options.cookieName || 'myetv_last_ad_timestamp',
61
+ debug: options.debug || false
62
+ };
63
+
64
+ // Plugin state
65
+ this.banner = null;
66
+ this.contentContainer = null;
67
+ this.adContainer = null;
68
+ this.timerElement = null;
69
+ this.closeButton = null;
70
+ this.countdown = this.options.duration;
71
+ this.countdownInterval = null;
72
+ this.autoCloseTimeout = null;
73
+ this.repeatIntervalTimeout = null;
74
+ this.isVisible = false;
75
+ this.mode = null; // 'iframe' or 'script'
76
+ this.adShownCount = 0; // Track how many times ad was shown in this session
77
+
78
+ // Determine mode based on options
79
+ if (this.options.adScript) {
80
+ this.mode = 'script';
81
+ } else if (this.options.url) {
82
+ this.mode = 'iframe';
83
+ }
84
+
85
+ if (this.options.debug) {
86
+ console.log('[IframeBannerAds] Initialized with options:', this.options);
87
+ console.log('[IframeBannerAds] Mode:', this.mode);
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Setup plugin - called by player after initialization
93
+ */
94
+ setup() {
95
+ if (!this.mode) {
96
+ console.error('[IframeBannerAds] No URL or adScript provided');
97
+ return;
98
+ }
99
+
100
+ this.createBanner();
101
+ this.bindEvents();
102
+ this.injectStyles();
103
+
104
+ if (this.options.debug) {
105
+ console.log('[IframeBannerAds] Setup completed');
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Cookie management - Set cookie
111
+ */
112
+ setCookie(name, value, seconds) {
113
+ const date = new Date();
114
+ date.setTime(date.getTime() + (seconds * 1000));
115
+ const expires = "expires=" + date.toUTCString();
116
+ document.cookie = name + "=" + value + ";" + expires + ";path=/;SameSite=Lax";
117
+
118
+ if (this.options.debug) {
119
+ console.log('[IframeBannerAds] Cookie set:', name, '=', value, 'for', seconds, 'seconds');
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Cookie management - Get cookie
125
+ */
126
+ getCookie(name) {
127
+ const nameEQ = name + "=";
128
+ const ca = document.cookie.split(';');
129
+ for (let i = 0; i < ca.length; i++) {
130
+ let c = ca[i];
131
+ while (c.charAt(0) === ' ') c = c.substring(1, c.length);
132
+ if (c.indexOf(nameEQ) === 0) {
133
+ return c.substring(nameEQ.length, c.length);
134
+ }
135
+ }
136
+ return null;
137
+ }
138
+
139
+ /**
140
+ * Check if enough time has passed since last ad
141
+ */
142
+ canShowAd() {
143
+ if (this.options.minTimeBetweenAds <= 0) {
144
+ // Feature disabled
145
+ return true;
146
+ }
147
+
148
+ const lastAdTimestamp = this.getCookie(this.options.cookieName);
149
+
150
+ if (!lastAdTimestamp) {
151
+ // No previous ad shown
152
+ if (this.options.debug) {
153
+ console.log('[IframeBannerAds] No previous ad found - can show');
154
+ }
155
+ return true;
156
+ }
157
+
158
+ const now = Math.floor(Date.now() / 1000); // Current timestamp in seconds
159
+ const lastAd = parseInt(lastAdTimestamp, 10);
160
+ const timePassed = now - lastAd;
161
+
162
+ if (this.options.debug) {
163
+ console.log('[IframeBannerAds] Time since last ad:', timePassed, 'seconds / Required:', this.options.minTimeBetweenAds);
164
+ }
165
+
166
+ return timePassed >= this.options.minTimeBetweenAds;
167
+ }
168
+
169
+ /**
170
+ * Update last ad timestamp
171
+ */
172
+ updateLastAdTimestamp() {
173
+ const now = Math.floor(Date.now() / 1000);
174
+ // Set cookie for double the minTimeBetweenAds to ensure it persists
175
+ const cookieDuration = this.options.minTimeBetweenAds > 0 ? this.options.minTimeBetweenAds * 2 : 86400; // 24 hours default
176
+ this.setCookie(this.options.cookieName, now.toString(), cookieDuration);
177
+ }
178
+
179
+ /**
180
+ * Start repeat interval timer
181
+ */
182
+ startRepeatInterval() {
183
+ // Clear any existing repeat timer
184
+ this.stopRepeatInterval();
185
+
186
+ if (this.options.repeatInterval <= 0) {
187
+ // Feature disabled
188
+ return;
189
+ }
190
+
191
+ if (this.options.debug) {
192
+ console.log('[IframeBannerAds] Starting repeat interval:', this.options.repeatInterval, 'seconds');
193
+ }
194
+
195
+ this.repeatIntervalTimeout = setTimeout(() => {
196
+ if (this.options.debug) {
197
+ console.log('[IframeBannerAds] Repeat interval triggered - showing ad again');
198
+ }
199
+
200
+ // Show ad again if allowed
201
+ if (this.canShowAd()) {
202
+ this.show();
203
+ } else {
204
+ if (this.options.debug) {
205
+ console.log('[IframeBannerAds] Repeat blocked by minTimeBetweenAds');
206
+ }
207
+ // Try again after the remaining time
208
+ const lastAdTimestamp = this.getCookie(this.options.cookieName);
209
+ if (lastAdTimestamp) {
210
+ const now = Math.floor(Date.now() / 1000);
211
+ const timePassed = now - parseInt(lastAdTimestamp, 10);
212
+ const timeRemaining = this.options.minTimeBetweenAds - timePassed;
213
+
214
+ if (timeRemaining > 0) {
215
+ setTimeout(() => this.startRepeatInterval(), timeRemaining * 1000);
216
+ }
217
+ }
218
+ }
219
+ }, this.options.repeatInterval * 1000);
220
+ }
221
+
222
+ /**
223
+ * Stop repeat interval timer
224
+ */
225
+ stopRepeatInterval() {
226
+ if (this.repeatIntervalTimeout) {
227
+ clearTimeout(this.repeatIntervalTimeout);
228
+ this.repeatIntervalTimeout = null;
229
+
230
+ if (this.options.debug) {
231
+ console.log('[IframeBannerAds] Repeat interval stopped');
232
+ }
233
+ }
234
+ }
235
+
236
+ injectStyles() {
237
+ const styleId = 'myetv-iframe-banner-ads-styles';
238
+
239
+ // Check if styles already exist
240
+ if (document.getElementById(styleId)) {
241
+ return;
242
+ }
243
+
244
+ const style = document.createElement('style');
245
+ style.id = styleId;
246
+ style.textContent = `
247
+ /* Iframe Banner Ads Plugin Styles */
248
+ .myetv-iframe-banner {
249
+ position: absolute;
250
+ bottom: var(--player-controls-height, 60px);
251
+ left: 50%;
252
+ transform: translateX(-50%);
253
+ width: clamp(300px, 90vw, 728px);
254
+ height: auto;
255
+ background: rgba(0, 0, 0, ${this.options.opacity});
256
+ border-radius: 8px;
257
+ z-index: 998;
258
+ display: none;
259
+ flex-direction: column;
260
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
261
+ overflow: visible;
262
+ transition: bottom 0.3s ease, all 0.3s ease;
263
+ }
264
+
265
+ .myetv-iframe-banner.visible {
266
+ display: flex;
267
+ animation: slideUp 0.4s ease-out;
268
+ }
269
+
270
+ @keyframes slideUp {
271
+ from {
272
+ opacity: 0;
273
+ transform: translateX(-50%) translateY(20px);
274
+ }
275
+ to {
276
+ opacity: 1;
277
+ transform: translateX(-50%) translateY(0);
278
+ }
279
+ }
280
+
281
+ .myetv-iframe-banner.hidden {
282
+ animation: fadeOutSlideDown 0.5s ease-out forwards;
283
+ }
284
+
285
+ @keyframes fadeOutSlideDown {
286
+ 0% {
287
+ opacity: 1;
288
+ transform: translateX(-50%) translateY(0);
289
+ }
290
+ 100% {
291
+ opacity: 0;
292
+ transform: translateX(-50%) translateY(15px);
293
+ }
294
+ }
295
+
296
+ .myetv-iframe-banner-content {
297
+ flex: 0 0 90px;
298
+ position: relative;
299
+ overflow: hidden;
300
+ display: flex;
301
+ align-items: center;
302
+ justify-content: center;
303
+ border-radius: 8px 8px 0 0;
304
+ }
305
+
306
+ .myetv-iframe-banner-ad-container {
307
+ width: 100%;
308
+ height: 100%;
309
+ display: flex;
310
+ align-items: center;
311
+ justify-content: center;
312
+ }
313
+
314
+ .myetv-iframe-banner iframe {
315
+ width: 100%;
316
+ height: 100%;
317
+ border: none;
318
+ display: block;
319
+ }
320
+
321
+ .myetv-iframe-banner-close {
322
+ position: absolute;
323
+ top: 8px;
324
+ right: 8px;
325
+ width: 28px;
326
+ height: 28px;
327
+ background: rgba(0, 0, 0, 0.7);
328
+ border: 1px solid rgba(255, 255, 255, 0.3);
329
+ border-radius: 50%;
330
+ color: #fff;
331
+ font-size: 18px;
332
+ line-height: 26px;
333
+ text-align: center;
334
+ cursor: pointer;
335
+ z-index: 999;
336
+ transition: background 0.2s ease, transform 0.2s ease;
337
+ font-weight: bold;
338
+ user-select: none;
339
+ }
340
+
341
+ .myetv-iframe-banner-close:hover {
342
+ background: rgba(255, 0, 0, 0.8);
343
+ transform: scale(1.1);
344
+ }
345
+
346
+ .myetv-iframe-banner-timer {
347
+ position: absolute;
348
+ bottom: -20px;
349
+ left: 50%;
350
+ transform: translateX(-50%);
351
+ background: rgba(0, 0, 0, 0.85);
352
+ color: #fff;
353
+ font-size: 12px;
354
+ padding: 6px 12px;
355
+ text-align: center;
356
+ font-family: Arial, sans-serif;
357
+ border-radius: 4px;
358
+ white-space: nowrap;
359
+ z-index: 999;
360
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
361
+ }
362
+
363
+ /* Compact button mode when space is limited */
364
+ @media (max-width: 640px) and (max-height: 500px) {
365
+ .myetv-iframe-banner {
366
+ width: 75%; /* 3/4 of player width */
367
+ min-width: 300px;
368
+ max-width: 600px;
369
+ left: 50%; /* Center */
370
+ transform: translateX(-50%);
371
+ border-radius: 6px;
372
+ }
373
+
374
+ .myetv-iframe-banner.visible {
375
+ animation: slideUpLeft 0.4s ease-out;
376
+ }
377
+
378
+ @keyframes slideUpLeft {
379
+ from {
380
+ opacity: 0;
381
+ transform: translateX(-20px) translateY(20px);
382
+ }
383
+ to {
384
+ opacity: 1;
385
+ transform: translateX(0) translateY(0);
386
+ }
387
+ }
388
+
389
+ .myetv-iframe-banner-content {
390
+ flex: 0 0 50px; /* Smaller height */
391
+ border-radius: 6px 6px 0 0;
392
+ }
393
+
394
+ .myetv-iframe-banner-close {
395
+ width: 20px;
396
+ height: 20px;
397
+ font-size: 14px;
398
+ line-height: 18px;
399
+ top: 4px;
400
+ right: 4px;
401
+ }
402
+
403
+ .myetv-iframe-banner-timer {
404
+ font-size: 10px;
405
+ padding: 4px 6px;
406
+ }
407
+ }
408
+
409
+ /* Extra compact mode for very small screens */
410
+ @media (max-width: 480px) and (max-height: 400px) {
411
+ .myetv-iframe-banner {
412
+ width: 75%; /* 3/4 of player width */
413
+ min-width: 250px;
414
+ max-width: 500px;
415
+ }
416
+
417
+ .myetv-iframe-banner-content {
418
+ flex: 0 0 40px;
419
+ }
420
+
421
+ .myetv-iframe-banner-timer {
422
+ font-size: 9px;
423
+ padding: 3px 4px;
424
+ }
425
+ }
426
+
427
+ /* Tablet adjustments - keep normal size */
428
+ @media (min-width: 641px) and (max-width: 768px) {
429
+ .myetv-iframe-banner-content {
430
+ flex: 0 0 75px;
431
+ }
432
+ }
433
+
434
+ /* Mobile portrait - keep full width but smaller */
435
+ @media (max-width: 480px) and (min-height: 501px) {
436
+ .myetv-iframe-banner-content {
437
+ flex: 0 0 60px;
438
+ }
439
+
440
+ .myetv-iframe-banner-close {
441
+ width: 24px;
442
+ height: 24px;
443
+ font-size: 16px;
444
+ line-height: 22px;
445
+ top: 6px;
446
+ right: 6px;
447
+ }
448
+
449
+ .myetv-iframe-banner-timer {
450
+ font-size: 11px;
451
+ padding: 6px 8px;
452
+ }
453
+ }
454
+ `;
455
+
456
+ document.head.appendChild(style);
457
+
458
+ if (this.options.debug) {
459
+ console.log('[IframeBannerAds] Styles injected');
460
+ }
461
+ }
462
+
463
+ /**
464
+ * Create banner DOM structure
465
+ */
466
+ createBanner() {
467
+ // Create main banner container
468
+ this.banner = document.createElement('div');
469
+ this.banner.className = 'myetv-iframe-banner';
470
+ this.banner.setAttribute('role', 'complementary');
471
+ this.banner.setAttribute('aria-label', 'Advertisement');
472
+
473
+ // Create content container
474
+ this.contentContainer = document.createElement('div');
475
+ this.contentContainer.className = 'myetv-iframe-banner-content';
476
+
477
+ // Create ad container
478
+ this.adContainer = document.createElement('div');
479
+ this.adContainer.className = 'myetv-iframe-banner-ad-container';
480
+
481
+ // Load ad based on mode
482
+ if (this.mode === 'iframe') {
483
+ this.loadDirectIframe();
484
+ } else if (this.mode === 'script') {
485
+ this.loadAdScript();
486
+ }
487
+
488
+ this.contentContainer.appendChild(this.adContainer);
489
+
490
+ // Create close button (only if closeable option is true)
491
+ if (this.options.closeable) {
492
+ this.closeButton = document.createElement('div');
493
+ this.closeButton.className = 'myetv-iframe-banner-close';
494
+ this.closeButton.innerHTML = '&times;';
495
+ this.closeButton.setAttribute('role', 'button');
496
+ this.closeButton.setAttribute('aria-label', 'Close advertisement');
497
+ this.closeButton.addEventListener('click', (e) => {
498
+ e.stopPropagation();
499
+ this.hideBanner();
500
+ });
501
+
502
+ this.contentContainer.appendChild(this.closeButton);
503
+ }
504
+
505
+ // Create timer element
506
+ this.timerElement = document.createElement('div');
507
+ this.timerElement.className = 'myetv-iframe-banner-timer';
508
+ this.updateTimerText();
509
+
510
+ // Assemble banner
511
+ this.banner.appendChild(this.contentContainer);
512
+ this.banner.appendChild(this.timerElement);
513
+
514
+ // Append to player container
515
+ if (this.player.container) {
516
+ this.player.container.appendChild(this.banner);
517
+ } else {
518
+ console.error('[IframeBannerAds] Player container not found');
519
+ }
520
+
521
+ if (this.options.debug) {
522
+ console.log('[IframeBannerAds] Banner created');
523
+ }
524
+ }
525
+
526
+ /**
527
+ * Load direct iframe
528
+ */
529
+ loadDirectIframe() {
530
+ const iframe = document.createElement('iframe');
531
+ iframe.src = this.options.url;
532
+ iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-forms');
533
+ iframe.setAttribute('loading', 'lazy');
534
+ iframe.setAttribute('title', 'Advertisement');
535
+
536
+ this.adContainer.appendChild(iframe);
537
+
538
+ if (this.options.debug) {
539
+ console.log('[IframeBannerAds] Direct iframe loaded:', this.options.url);
540
+ }
541
+ }
542
+
543
+ /**
544
+ * Load external ad script with parameters
545
+ */
546
+ loadAdScript() {
547
+ // Create script element
548
+ const script = document.createElement('script');
549
+ script.src = this.options.adScript;
550
+
551
+ // Apply data-* attributes from adParams
552
+ if (this.options.adParams && typeof this.options.adParams === 'object') {
553
+ for (const [key, value] of Object.entries(this.options.adParams)) {
554
+ script.setAttribute(key, value);
555
+ }
556
+ }
557
+
558
+ // Append script to ad container
559
+ this.adContainer.appendChild(script);
560
+
561
+ if (this.options.debug) {
562
+ console.log('[IframeBannerAds] Ad script loaded:', this.options.adScript);
563
+ console.log('[IframeBannerAds] Ad params:', this.options.adParams);
564
+ }
565
+ }
566
+
567
+ /**
568
+ * Bind player events
569
+ */
570
+ bindEvents() {
571
+ if (this.options.showOnPlay) {
572
+ this.player.addEventListener('played', () => {
573
+ if (this.options.debug) {
574
+ console.log('[IframeBannerAds] Video played - checking if can show banner');
575
+ }
576
+
577
+ if (this.canShowAd()) {
578
+ this.show();
579
+ } else {
580
+ if (this.options.debug) {
581
+ console.log('[IframeBannerAds] Ad blocked by minTimeBetweenAds');
582
+ }
583
+ }
584
+ });
585
+ }
586
+
587
+ if (this.options.showOnPause) {
588
+ this.player.addEventListener('paused', () => {
589
+ if (this.options.debug) {
590
+ console.log('[IframeBannerAds] Video paused - checking if can show banner');
591
+ }
592
+
593
+ if (this.canShowAd()) {
594
+ this.show();
595
+ } else {
596
+ if (this.options.debug) {
597
+ console.log('[IframeBannerAds] Ad blocked by minTimeBetweenAds');
598
+ }
599
+ }
600
+ });
601
+ }
602
+
603
+ // Hide banner when video ends and stop repeat interval
604
+ this.player.addEventListener('ended', () => {
605
+ if (this.options.debug) {
606
+ console.log('[IframeBannerAds] Video ended - hiding banner and stopping repeat');
607
+ }
608
+ this.hide();
609
+ this.stopRepeatInterval();
610
+ });
611
+
612
+ if (this.options.debug) {
613
+ console.log('[IframeBannerAds] Events bound');
614
+ }
615
+
616
+ // Setup controlbar tracking
617
+ this.setupControlbarTracking();
618
+ }
619
+
620
+
621
+ /**
622
+ * Setup controlbar tracking to move banner with controlbar
623
+ */
624
+ setupControlbarTracking() {
625
+ if (!this.player || !this.player.container) {
626
+ if (this.options.debug) {
627
+ console.log('[IframeBannerAds] Cannot setup controlbar tracking');
628
+ }
629
+ return;
630
+ }
631
+
632
+ const updatePosition = () => {
633
+ requestAnimationFrame(() => this.updateBannerPosition());
634
+ };
635
+
636
+ this.controlbarObserver = new MutationObserver(() => updatePosition());
637
+ this.controlbarObserver.observe(this.player.container, {
638
+ attributes: true,
639
+ attributeFilter: ['class', 'style'],
640
+ subtree: true,
641
+ childList: false
642
+ });
643
+
644
+ let mouseTimer;
645
+ this.player.container.addEventListener('mousemove', () => {
646
+ clearTimeout(mouseTimer);
647
+ mouseTimer = setTimeout(() => updatePosition(), 50);
648
+ });
649
+
650
+ this.player.container.addEventListener('mouseleave', () => {
651
+ clearTimeout(mouseTimer);
652
+ mouseTimer = setTimeout(() => updatePosition(), 300);
653
+ });
654
+
655
+ if (this.player.addEventListener) {
656
+ this.player.addEventListener('controlsshown', () => updatePosition());
657
+ this.player.addEventListener('controlshidden', () => updatePosition());
658
+ }
659
+
660
+ setTimeout(() => updatePosition(), 100);
661
+
662
+ if (this.options.debug) {
663
+ console.log('[IframeBannerAds] Controlbar tracking setup completed');
664
+ }
665
+ }
666
+
667
+ /**
668
+ * Update banner position based on controlbar visibility
669
+ */
670
+ updateBannerPosition() {
671
+ if (!this.banner || !this.player || !this.player.container || !this.isVisible) {
672
+ return;
673
+ }
674
+
675
+ const controlbar = this.player.container.querySelector('.myetv-controls-container') ||
676
+ this.player.container.querySelector('.myetv-controls') ||
677
+ this.player.container.querySelector('[class*="controls"]');
678
+
679
+ if (!controlbar) {
680
+ if (this.options.debug) {
681
+ console.log('[IframeBannerAds] Controlbar not found');
682
+ }
683
+ this.banner.style.bottom = '10px';
684
+ return;
685
+ }
686
+
687
+ const computedStyle = window.getComputedStyle(controlbar);
688
+ const rect = controlbar.getBoundingClientRect();
689
+
690
+ const isHidden =
691
+ controlbar.classList.contains('hidden') ||
692
+ controlbar.classList.contains('myetv-controls-hidden') ||
693
+ computedStyle.opacity === '0' ||
694
+ computedStyle.visibility === 'hidden' ||
695
+ computedStyle.display === 'none' ||
696
+ rect.height === 0;
697
+
698
+ let newBottom;
699
+ if (isHidden) {
700
+ newBottom = '10px';
701
+ } else {
702
+ const controlbarHeight = controlbar.offsetHeight || rect.height || 60;
703
+ newBottom = `${controlbarHeight + 5}px`;
704
+ }
705
+
706
+ if (this.banner.style.bottom !== newBottom) {
707
+ this.banner.style.bottom = newBottom;
708
+
709
+ if (this.options.debug) {
710
+ console.log(`[IframeBannerAds] Banner repositioned to: ${newBottom} (controlbar ${isHidden ? 'hidden' : 'visible'})`);
711
+ }
712
+ }
713
+ }
714
+ /**
715
+ * Update timer text display
716
+ */
717
+ updateTimerText() {
718
+ if (this.timerElement) {
719
+ if (this.countdown > 0) {
720
+ this.timerElement.textContent = `This ad will close in ${this.countdown} second${this.countdown !== 1 ? 's' : ''}`;
721
+ } else {
722
+ this.timerElement.textContent = 'Closing...';
723
+ }
724
+ }
725
+ }
726
+
727
+ /**
728
+ * Start countdown timer
729
+ */
730
+ startCountdown() {
731
+ // Clear any existing timers
732
+ this.stopCountdown();
733
+
734
+ this.countdown = this.options.duration;
735
+ this.updateTimerText();
736
+
737
+ // Countdown interval (updates every second)
738
+ this.countdownInterval = setInterval(() => {
739
+ this.countdown--;
740
+ this.updateTimerText();
741
+
742
+ if (this.countdown <= 0) {
743
+ clearInterval(this.countdownInterval);
744
+ this.countdownInterval = null;
745
+ }
746
+ }, 1000);
747
+
748
+ // Auto-close timeout
749
+ this.autoCloseTimeout = setTimeout(() => {
750
+ if (this.options.debug) {
751
+ console.log('[IframeBannerAds] Auto-closing banner');
752
+ }
753
+
754
+ // Clear interval first
755
+ if (this.countdownInterval) {
756
+ clearInterval(this.countdownInterval);
757
+ this.countdownInterval = null;
758
+ }
759
+
760
+ // Then hide
761
+ this.hideBanner();
762
+
763
+ }, this.options.duration * 1000);
764
+
765
+ if (this.options.debug) {
766
+ console.log('[IframeBannerAds] Countdown started:', this.options.duration, 'seconds');
767
+ }
768
+ }
769
+
770
+ /**
771
+ * Stop countdown timer
772
+ */
773
+ stopCountdown() {
774
+ if (this.countdownInterval) {
775
+ clearInterval(this.countdownInterval);
776
+ this.countdownInterval = null;
777
+ }
778
+
779
+ if (this.autoCloseTimeout) {
780
+ clearTimeout(this.autoCloseTimeout);
781
+ this.autoCloseTimeout = null;
782
+ }
783
+
784
+ if (this.options.debug) {
785
+ console.log('[IframeBannerAds] Countdown stopped');
786
+ }
787
+ }
788
+
789
+ /**
790
+ * Show banner
791
+ */
792
+ show() {
793
+ if (this.isVisible || !this.banner) {
794
+ return;
795
+ }
796
+
797
+ // Check again if we can show the ad
798
+ if (!this.canShowAd()) {
799
+ if (this.options.debug) {
800
+ console.log('[IframeBannerAds] Cannot show ad - time restriction');
801
+ }
802
+ return;
803
+ }
804
+
805
+ this.banner.style.display = 'flex';
806
+ this.banner.style.opacity = '1';
807
+ this.banner.classList.remove('hidden');
808
+ this.banner.classList.add('visible');
809
+ this.isVisible = true;
810
+ this.adShownCount++;
811
+
812
+ // Update banner position
813
+ setTimeout(() => this.updateBannerPosition(), 150);
814
+
815
+ // Update last ad timestamp
816
+ this.updateLastAdTimestamp();
817
+
818
+ // Start countdown
819
+ this.startCountdown();
820
+
821
+ // Start repeat interval if configured
822
+ if (this.options.repeatInterval > 0) {
823
+ this.startRepeatInterval();
824
+ }
825
+
826
+ if (this.options.debug) {
827
+ console.log('[IframeBannerAds] Banner shown (count:', this.adShownCount + ')');
828
+ }
829
+ }
830
+
831
+ /**
832
+ * Hide banner - SINGLE METHOD used by both X button and auto-close
833
+ */
834
+ hideBanner() {
835
+ if (!this.banner) {
836
+ return;
837
+ }
838
+
839
+ // Stop all timers
840
+ if (this.countdownInterval) {
841
+ clearInterval(this.countdownInterval);
842
+ this.countdownInterval = null;
843
+ }
844
+
845
+ if (this.autoCloseTimeout) {
846
+ clearTimeout(this.autoCloseTimeout);
847
+ this.autoCloseTimeout = null;
848
+ }
849
+
850
+ // Set flag immediately
851
+ this.isVisible = false;
852
+
853
+ // Fadeout animation via JavaScript
854
+ let opacity = 1;
855
+ const fadeInterval = setInterval(() => {
856
+ opacity -= 0.05;
857
+
858
+ if (opacity <= 0) {
859
+ clearInterval(fadeInterval);
860
+
861
+ // Hide completely after fadeout
862
+ if (this.banner) {
863
+ this.banner.style.display = 'none';
864
+ this.banner.style.opacity = '1';
865
+ this.banner.classList.remove('visible');
866
+ this.banner.classList.remove('hidden');
867
+ }
868
+
869
+ if (this.options.debug) {
870
+ console.log('[IframeBannerAds] Banner hidden');
871
+ }
872
+ } else {
873
+ if (this.banner) {
874
+ this.banner.style.opacity = opacity;
875
+ }
876
+ }
877
+ }, 25);
878
+
879
+ // Add hidden class for other animations
880
+ this.banner.classList.add('hidden');
881
+ this.banner.classList.remove('visible');
882
+ }
883
+
884
+ /**
885
+ * Hide banner
886
+ */
887
+ hide() {
888
+ this.hideBanner();
889
+ // Don't stop repeat interval here - it continues until video ends
890
+ }
891
+
892
+ /**
893
+ * Toggle banner visibility
894
+ */
895
+ toggle() {
896
+ if (this.isVisible) {
897
+ this.hide();
898
+ } else {
899
+ this.show();
900
+ }
901
+ }
902
+
903
+ /**
904
+ * Update iframe URL (only for direct iframe mode)
905
+ */
906
+ setUrl(newUrl) {
907
+ if (this.mode !== 'iframe') {
908
+ console.warn('[IframeBannerAds] setUrl() only works in iframe mode');
909
+ return;
910
+ }
911
+
912
+ if (newUrl) {
913
+ this.options.url = newUrl;
914
+
915
+ // Clear and reload
916
+ this.adContainer.innerHTML = '';
917
+ this.loadDirectIframe();
918
+
919
+ if (this.options.debug) {
920
+ console.log('[IframeBannerAds] URL updated:', newUrl);
921
+ }
922
+ }
923
+ }
924
+
925
+ /**
926
+ * Update ad script parameters (only for script mode)
927
+ */
928
+ setAdParams(newParams) {
929
+ if (this.mode !== 'script') {
930
+ console.warn('[IframeBannerAds] setAdParams() only works in script mode');
931
+ return;
932
+ }
933
+
934
+ if (newParams && typeof newParams === 'object') {
935
+ this.options.adParams = { ...this.options.adParams, ...newParams };
936
+
937
+ // Clear and reload
938
+ this.adContainer.innerHTML = '';
939
+ this.loadAdScript();
940
+
941
+ if (this.options.debug) {
942
+ console.log('[IframeBannerAds] Ad params updated:', this.options.adParams);
943
+ }
944
+ }
945
+ }
946
+
947
+ /**
948
+ * Update banner duration
949
+ */
950
+ setDuration(seconds) {
951
+ if (typeof seconds === 'number' && seconds > 0) {
952
+ this.options.duration = seconds;
953
+
954
+ if (this.options.debug) {
955
+ console.log('[IframeBannerAds] Duration updated:', seconds);
956
+ }
957
+ }
958
+ }
959
+
960
+ /**
961
+ * Update banner opacity
962
+ */
963
+ setOpacity(opacity) {
964
+ if (typeof opacity === 'number' && opacity >= 0 && opacity <= 1) {
965
+ this.options.opacity = opacity;
966
+
967
+ if (this.banner) {
968
+ this.banner.style.background = `rgba(0, 0, 0, ${opacity})`;
969
+ }
970
+
971
+ if (this.options.debug) {
972
+ console.log('[IframeBannerAds] Opacity updated:', opacity);
973
+ }
974
+ }
975
+ }
976
+
977
+ /**
978
+ * Get plugin state
979
+ */
980
+ getState() {
981
+ return {
982
+ mode: this.mode,
983
+ isVisible: this.isVisible,
984
+ countdown: this.countdown,
985
+ url: this.options.url,
986
+ adScript: this.options.adScript,
987
+ adParams: this.options.adParams,
988
+ duration: this.options.duration,
989
+ opacity: this.options.opacity,
990
+ minTimeBetweenAds: this.options.minTimeBetweenAds,
991
+ repeatInterval: this.options.repeatInterval,
992
+ adShownCount: this.adShownCount,
993
+ lastAdTimestamp: this.getCookie(this.options.cookieName)
994
+ };
995
+ }
996
+
997
+ /**
998
+ * Dispose plugin - cleanup
999
+ */
1000
+ dispose() {
1001
+ if (this.options.debug) {
1002
+ console.log('[IframeBannerAds] Disposing plugin');
1003
+ }
1004
+
1005
+ // Stop timers
1006
+ this.stopCountdown();
1007
+ this.stopRepeatInterval();
1008
+
1009
+ // Cleanup controlbar tracking
1010
+ if (this.controlbarObserver) {
1011
+ this.controlbarObserver.disconnect();
1012
+ this.controlbarObserver = null;
1013
+ }
1014
+
1015
+ // Remove DOM elements
1016
+ if (this.banner && this.banner.parentNode) {
1017
+ this.banner.parentNode.removeChild(this.banner);
1018
+ }
1019
+
1020
+ // Clear references
1021
+ this.banner = null;
1022
+ this.contentContainer = null;
1023
+ this.adContainer = null;
1024
+ this.timerElement = null;
1025
+ this.closeButton = null;
1026
+ }
1027
+ }
1028
+
1029
+ // Register plugin globally
1030
+ if (typeof window !== 'undefined' && typeof window.registerMYETVPlugin === 'function') {
1031
+ window.registerMYETVPlugin('iframe-banner-ads', IframeBannerAds);
1032
+ console.log('[MyeTV] Iframe Banner Ads plugin registered');
1033
+ } else {
1034
+ console.error('[IframeBannerAds] Plugin registration failed - registerMYETVPlugin not available');
1035
+ }
1036
+
1037
+ })();