myetv-player 1.6.0 → 1.6.1

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,926 @@
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: 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
+ flex: 0 0 auto;
348
+ background: rgba(0, 0, 0, 0.6);
349
+ color: #fff;
350
+ font-size: 12px;
351
+ padding: 8px 12px;
352
+ text-align: center;
353
+ font-family: Arial, sans-serif;
354
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
355
+ border-radius: 0 0 8px 8px;
356
+ }
357
+
358
+ /* Compact button mode when space is limited */
359
+ @media (max-width: 640px) and (max-height: 500px) {
360
+ .myetv-iframe-banner {
361
+ width: 25%; /* 1/4 of player width */
362
+ min-width: 120px;
363
+ max-width: 200px;
364
+ left: 10px; /* Position at left */
365
+ transform: translateX(0);
366
+ border-radius: 6px;
367
+ }
368
+
369
+ .myetv-iframe-banner.visible {
370
+ animation: slideUpLeft 0.4s ease-out;
371
+ }
372
+
373
+ @keyframes slideUpLeft {
374
+ from {
375
+ opacity: 0;
376
+ transform: translateX(-20px) translateY(20px);
377
+ }
378
+ to {
379
+ opacity: 1;
380
+ transform: translateX(0) translateY(0);
381
+ }
382
+ }
383
+
384
+ .myetv-iframe-banner-content {
385
+ flex: 0 0 50px; /* Smaller height */
386
+ border-radius: 6px 6px 0 0;
387
+ }
388
+
389
+ .myetv-iframe-banner-close {
390
+ width: 20px;
391
+ height: 20px;
392
+ font-size: 14px;
393
+ line-height: 18px;
394
+ top: 4px;
395
+ right: 4px;
396
+ }
397
+
398
+ .myetv-iframe-banner-timer {
399
+ font-size: 10px;
400
+ padding: 4px 6px;
401
+ }
402
+ }
403
+
404
+ /* Extra compact mode for very small screens */
405
+ @media (max-width: 480px) and (max-height: 400px) {
406
+ .myetv-iframe-banner {
407
+ width: 30%;
408
+ min-width: 100px;
409
+ max-width: 150px;
410
+ }
411
+
412
+ .myetv-iframe-banner-content {
413
+ flex: 0 0 40px;
414
+ }
415
+
416
+ .myetv-iframe-banner-timer {
417
+ font-size: 9px;
418
+ padding: 3px 4px;
419
+ }
420
+ }
421
+
422
+ /* Tablet adjustments - keep normal size */
423
+ @media (min-width: 641px) and (max-width: 768px) {
424
+ .myetv-iframe-banner-content {
425
+ flex: 0 0 75px;
426
+ }
427
+ }
428
+
429
+ /* Mobile portrait - keep full width but smaller */
430
+ @media (max-width: 480px) and (min-height: 501px) {
431
+ .myetv-iframe-banner-content {
432
+ flex: 0 0 60px;
433
+ }
434
+
435
+ .myetv-iframe-banner-close {
436
+ width: 24px;
437
+ height: 24px;
438
+ font-size: 16px;
439
+ line-height: 22px;
440
+ top: 6px;
441
+ right: 6px;
442
+ }
443
+
444
+ .myetv-iframe-banner-timer {
445
+ font-size: 11px;
446
+ padding: 6px 8px;
447
+ }
448
+ }
449
+ `;
450
+
451
+ document.head.appendChild(style);
452
+
453
+ if (this.options.debug) {
454
+ console.log('[IframeBannerAds] Styles injected');
455
+ }
456
+ }
457
+
458
+ /**
459
+ * Create banner DOM structure
460
+ */
461
+ createBanner() {
462
+ // Create main banner container
463
+ this.banner = document.createElement('div');
464
+ this.banner.className = 'myetv-iframe-banner';
465
+ this.banner.setAttribute('role', 'complementary');
466
+ this.banner.setAttribute('aria-label', 'Advertisement');
467
+
468
+ // Create content container
469
+ this.contentContainer = document.createElement('div');
470
+ this.contentContainer.className = 'myetv-iframe-banner-content';
471
+
472
+ // Create ad container
473
+ this.adContainer = document.createElement('div');
474
+ this.adContainer.className = 'myetv-iframe-banner-ad-container';
475
+
476
+ // Load ad based on mode
477
+ if (this.mode === 'iframe') {
478
+ this.loadDirectIframe();
479
+ } else if (this.mode === 'script') {
480
+ this.loadAdScript();
481
+ }
482
+
483
+ this.contentContainer.appendChild(this.adContainer);
484
+
485
+ // Create close button (only if closeable option is true)
486
+ if (this.options.closeable) {
487
+ this.closeButton = document.createElement('div');
488
+ this.closeButton.className = 'myetv-iframe-banner-close';
489
+ this.closeButton.innerHTML = '&times;';
490
+ this.closeButton.setAttribute('role', 'button');
491
+ this.closeButton.setAttribute('aria-label', 'Close advertisement');
492
+ this.closeButton.addEventListener('click', (e) => {
493
+ e.stopPropagation();
494
+ this.hideBanner();
495
+ });
496
+
497
+ this.contentContainer.appendChild(this.closeButton);
498
+ }
499
+
500
+ // Create timer element
501
+ this.timerElement = document.createElement('div');
502
+ this.timerElement.className = 'myetv-iframe-banner-timer';
503
+ this.updateTimerText();
504
+
505
+ // Assemble banner
506
+ this.banner.appendChild(this.contentContainer);
507
+ this.banner.appendChild(this.timerElement);
508
+
509
+ // Append to player container
510
+ if (this.player.container) {
511
+ this.player.container.appendChild(this.banner);
512
+ } else {
513
+ console.error('[IframeBannerAds] Player container not found');
514
+ }
515
+
516
+ if (this.options.debug) {
517
+ console.log('[IframeBannerAds] Banner created');
518
+ }
519
+ }
520
+
521
+ /**
522
+ * Load direct iframe
523
+ */
524
+ loadDirectIframe() {
525
+ const iframe = document.createElement('iframe');
526
+ iframe.src = this.options.url;
527
+ iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-forms');
528
+ iframe.setAttribute('loading', 'lazy');
529
+ iframe.setAttribute('title', 'Advertisement');
530
+
531
+ this.adContainer.appendChild(iframe);
532
+
533
+ if (this.options.debug) {
534
+ console.log('[IframeBannerAds] Direct iframe loaded:', this.options.url);
535
+ }
536
+ }
537
+
538
+ /**
539
+ * Load external ad script with parameters
540
+ */
541
+ loadAdScript() {
542
+ // Create script element
543
+ const script = document.createElement('script');
544
+ script.src = this.options.adScript;
545
+
546
+ // Apply data-* attributes from adParams
547
+ if (this.options.adParams && typeof this.options.adParams === 'object') {
548
+ for (const [key, value] of Object.entries(this.options.adParams)) {
549
+ script.setAttribute(key, value);
550
+ }
551
+ }
552
+
553
+ // Append script to ad container
554
+ this.adContainer.appendChild(script);
555
+
556
+ if (this.options.debug) {
557
+ console.log('[IframeBannerAds] Ad script loaded:', this.options.adScript);
558
+ console.log('[IframeBannerAds] Ad params:', this.options.adParams);
559
+ }
560
+ }
561
+
562
+ /**
563
+ * Bind player events
564
+ */
565
+ bindEvents() {
566
+ if (this.options.showOnPlay) {
567
+ this.player.addEventListener('played', () => {
568
+ if (this.options.debug) {
569
+ console.log('[IframeBannerAds] Video played - checking if can show banner');
570
+ }
571
+
572
+ if (this.canShowAd()) {
573
+ this.show();
574
+ } else {
575
+ if (this.options.debug) {
576
+ console.log('[IframeBannerAds] Ad blocked by minTimeBetweenAds');
577
+ }
578
+ }
579
+ });
580
+ }
581
+
582
+ if (this.options.showOnPause) {
583
+ this.player.addEventListener('paused', () => {
584
+ if (this.options.debug) {
585
+ console.log('[IframeBannerAds] Video paused - checking if can show banner');
586
+ }
587
+
588
+ if (this.canShowAd()) {
589
+ this.show();
590
+ } else {
591
+ if (this.options.debug) {
592
+ console.log('[IframeBannerAds] Ad blocked by minTimeBetweenAds');
593
+ }
594
+ }
595
+ });
596
+ }
597
+
598
+ // Hide banner when video ends and stop repeat interval
599
+ this.player.addEventListener('ended', () => {
600
+ if (this.options.debug) {
601
+ console.log('[IframeBannerAds] Video ended - hiding banner and stopping repeat');
602
+ }
603
+ this.hide();
604
+ this.stopRepeatInterval();
605
+ });
606
+
607
+ if (this.options.debug) {
608
+ console.log('[IframeBannerAds] Events bound');
609
+ }
610
+ }
611
+
612
+ /**
613
+ * Update timer text display
614
+ */
615
+ updateTimerText() {
616
+ if (this.timerElement) {
617
+ if (this.countdown > 0) {
618
+ this.timerElement.textContent = `Close in ${this.countdown} second${this.countdown !== 1 ? 's' : ''}`;
619
+ } else {
620
+ this.timerElement.textContent = 'Closing...';
621
+ }
622
+ }
623
+ }
624
+
625
+ /**
626
+ * Start countdown timer
627
+ */
628
+ startCountdown() {
629
+ // Clear any existing timers
630
+ this.stopCountdown();
631
+
632
+ this.countdown = this.options.duration;
633
+ this.updateTimerText();
634
+
635
+ // Countdown interval (updates every second)
636
+ this.countdownInterval = setInterval(() => {
637
+ this.countdown--;
638
+ this.updateTimerText();
639
+
640
+ if (this.countdown <= 0) {
641
+ clearInterval(this.countdownInterval);
642
+ this.countdownInterval = null;
643
+ }
644
+ }, 1000);
645
+
646
+ // Auto-close timeout
647
+ this.autoCloseTimeout = setTimeout(() => {
648
+ if (this.options.debug) {
649
+ console.log('[IframeBannerAds] Auto-closing banner');
650
+ }
651
+
652
+ // Clear interval first
653
+ if (this.countdownInterval) {
654
+ clearInterval(this.countdownInterval);
655
+ this.countdownInterval = null;
656
+ }
657
+
658
+ // Then hide
659
+ this.hideBanner();
660
+
661
+ }, this.options.duration * 1000);
662
+
663
+ if (this.options.debug) {
664
+ console.log('[IframeBannerAds] Countdown started:', this.options.duration, 'seconds');
665
+ }
666
+ }
667
+
668
+ /**
669
+ * Stop countdown timer
670
+ */
671
+ stopCountdown() {
672
+ if (this.countdownInterval) {
673
+ clearInterval(this.countdownInterval);
674
+ this.countdownInterval = null;
675
+ }
676
+
677
+ if (this.autoCloseTimeout) {
678
+ clearTimeout(this.autoCloseTimeout);
679
+ this.autoCloseTimeout = null;
680
+ }
681
+
682
+ if (this.options.debug) {
683
+ console.log('[IframeBannerAds] Countdown stopped');
684
+ }
685
+ }
686
+
687
+ /**
688
+ * Show banner
689
+ */
690
+ show() {
691
+ if (this.isVisible || !this.banner) {
692
+ return;
693
+ }
694
+
695
+ // Check again if we can show the ad
696
+ if (!this.canShowAd()) {
697
+ if (this.options.debug) {
698
+ console.log('[IframeBannerAds] Cannot show ad - time restriction');
699
+ }
700
+ return;
701
+ }
702
+
703
+ this.banner.style.display = 'flex';
704
+ this.banner.style.opacity = '1';
705
+ this.banner.classList.remove('hidden');
706
+ this.banner.classList.add('visible');
707
+ this.isVisible = true;
708
+ this.adShownCount++;
709
+
710
+ // Update last ad timestamp
711
+ this.updateLastAdTimestamp();
712
+
713
+ // Start countdown
714
+ this.startCountdown();
715
+
716
+ // Start repeat interval if configured
717
+ if (this.options.repeatInterval > 0) {
718
+ this.startRepeatInterval();
719
+ }
720
+
721
+ if (this.options.debug) {
722
+ console.log('[IframeBannerAds] Banner shown (count:', this.adShownCount + ')');
723
+ }
724
+ }
725
+
726
+ /**
727
+ * Hide banner - SINGLE METHOD used by both X button and auto-close
728
+ */
729
+ hideBanner() {
730
+ if (!this.banner) {
731
+ return;
732
+ }
733
+
734
+ // Stop all timers
735
+ if (this.countdownInterval) {
736
+ clearInterval(this.countdownInterval);
737
+ this.countdownInterval = null;
738
+ }
739
+
740
+ if (this.autoCloseTimeout) {
741
+ clearTimeout(this.autoCloseTimeout);
742
+ this.autoCloseTimeout = null;
743
+ }
744
+
745
+ // Set flag immediately
746
+ this.isVisible = false;
747
+
748
+ // Fadeout animation via JavaScript
749
+ let opacity = 1;
750
+ const fadeInterval = setInterval(() => {
751
+ opacity -= 0.05;
752
+
753
+ if (opacity <= 0) {
754
+ clearInterval(fadeInterval);
755
+
756
+ // Hide completely after fadeout
757
+ if (this.banner) {
758
+ this.banner.style.display = 'none';
759
+ this.banner.style.opacity = '1';
760
+ this.banner.classList.remove('visible');
761
+ this.banner.classList.remove('hidden');
762
+ }
763
+
764
+ if (this.options.debug) {
765
+ console.log('[IframeBannerAds] Banner hidden');
766
+ }
767
+ } else {
768
+ if (this.banner) {
769
+ this.banner.style.opacity = opacity;
770
+ }
771
+ }
772
+ }, 25);
773
+
774
+ // Add hidden class for other animations
775
+ this.banner.classList.add('hidden');
776
+ this.banner.classList.remove('visible');
777
+ }
778
+
779
+ /**
780
+ * Hide banner
781
+ */
782
+ hide() {
783
+ this.hideBanner();
784
+ // Don't stop repeat interval here - it continues until video ends
785
+ }
786
+
787
+ /**
788
+ * Toggle banner visibility
789
+ */
790
+ toggle() {
791
+ if (this.isVisible) {
792
+ this.hide();
793
+ } else {
794
+ this.show();
795
+ }
796
+ }
797
+
798
+ /**
799
+ * Update iframe URL (only for direct iframe mode)
800
+ */
801
+ setUrl(newUrl) {
802
+ if (this.mode !== 'iframe') {
803
+ console.warn('[IframeBannerAds] setUrl() only works in iframe mode');
804
+ return;
805
+ }
806
+
807
+ if (newUrl) {
808
+ this.options.url = newUrl;
809
+
810
+ // Clear and reload
811
+ this.adContainer.innerHTML = '';
812
+ this.loadDirectIframe();
813
+
814
+ if (this.options.debug) {
815
+ console.log('[IframeBannerAds] URL updated:', newUrl);
816
+ }
817
+ }
818
+ }
819
+
820
+ /**
821
+ * Update ad script parameters (only for script mode)
822
+ */
823
+ setAdParams(newParams) {
824
+ if (this.mode !== 'script') {
825
+ console.warn('[IframeBannerAds] setAdParams() only works in script mode');
826
+ return;
827
+ }
828
+
829
+ if (newParams && typeof newParams === 'object') {
830
+ this.options.adParams = { ...this.options.adParams, ...newParams };
831
+
832
+ // Clear and reload
833
+ this.adContainer.innerHTML = '';
834
+ this.loadAdScript();
835
+
836
+ if (this.options.debug) {
837
+ console.log('[IframeBannerAds] Ad params updated:', this.options.adParams);
838
+ }
839
+ }
840
+ }
841
+
842
+ /**
843
+ * Update banner duration
844
+ */
845
+ setDuration(seconds) {
846
+ if (typeof seconds === 'number' && seconds > 0) {
847
+ this.options.duration = seconds;
848
+
849
+ if (this.options.debug) {
850
+ console.log('[IframeBannerAds] Duration updated:', seconds);
851
+ }
852
+ }
853
+ }
854
+
855
+ /**
856
+ * Update banner opacity
857
+ */
858
+ setOpacity(opacity) {
859
+ if (typeof opacity === 'number' && opacity >= 0 && opacity <= 1) {
860
+ this.options.opacity = opacity;
861
+
862
+ if (this.banner) {
863
+ this.banner.style.background = `rgba(0, 0, 0, ${opacity})`;
864
+ }
865
+
866
+ if (this.options.debug) {
867
+ console.log('[IframeBannerAds] Opacity updated:', opacity);
868
+ }
869
+ }
870
+ }
871
+
872
+ /**
873
+ * Get plugin state
874
+ */
875
+ getState() {
876
+ return {
877
+ mode: this.mode,
878
+ isVisible: this.isVisible,
879
+ countdown: this.countdown,
880
+ url: this.options.url,
881
+ adScript: this.options.adScript,
882
+ adParams: this.options.adParams,
883
+ duration: this.options.duration,
884
+ opacity: this.options.opacity,
885
+ minTimeBetweenAds: this.options.minTimeBetweenAds,
886
+ repeatInterval: this.options.repeatInterval,
887
+ adShownCount: this.adShownCount,
888
+ lastAdTimestamp: this.getCookie(this.options.cookieName)
889
+ };
890
+ }
891
+
892
+ /**
893
+ * Dispose plugin - cleanup
894
+ */
895
+ dispose() {
896
+ if (this.options.debug) {
897
+ console.log('[IframeBannerAds] Disposing plugin');
898
+ }
899
+
900
+ // Stop timers
901
+ this.stopCountdown();
902
+ this.stopRepeatInterval();
903
+
904
+ // Remove DOM elements
905
+ if (this.banner && this.banner.parentNode) {
906
+ this.banner.parentNode.removeChild(this.banner);
907
+ }
908
+
909
+ // Clear references
910
+ this.banner = null;
911
+ this.contentContainer = null;
912
+ this.adContainer = null;
913
+ this.timerElement = null;
914
+ this.closeButton = null;
915
+ }
916
+ }
917
+
918
+ // Register plugin globally
919
+ if (typeof window !== 'undefined' && typeof window.registerMYETVPlugin === 'function') {
920
+ window.registerMYETVPlugin('iframe-banner-ads', IframeBannerAds);
921
+ console.log('[MyeTV] Iframe Banner Ads plugin registered');
922
+ } else {
923
+ console.error('[IframeBannerAds] Plugin registration failed - registerMYETVPlugin not available');
924
+ }
925
+
926
+ })();