PageSourceSearch

https://vidiqs.com/js/popunder_new.js?v=1790905659

js vidiqs.com collected 2026-10-02 01:47:44 UTC 27,610 bytes, 726 lines download raw bytes

1/**
2 * LHFree Popunder Ads System - Complete Redesign
3 * Working popunder system with Dual-Action Folder Follow-Through
4 * (Opens ad in popup tab + opens the clicked folder/card simultaneously)
5 */
6
7(function() {
8    'use strict';
9    
10    // Configuration defaults
11    let config = {
12        cooldownKey: 'LHFreePopunder_cooldown',
13        cooldownSeconds: 30,
14        maxPerSession: 5,
15        adCode: '',
16        adName: '',
17        isInitialized: false,
18        lastPopunderTime: 0,
19        sessionCount: 0
20    };
21    
22    // Excluded pages
23    const excludedPages = [
24        'video_player.php',
25        'login.php',
26        'register.php',
27        'forgot_password.php',
28        'reset_password.php',
29        'verify_email.php'
30    ];
31    
32    // Excluded selectors
33    const excludedSelectors = [
34        'a[href*="video_player"]',
35        'a[href*="admin"]',
36        'button[data-bs-toggle]',
37        '.btn[data-bs-toggle]',
38        '.video-controls',
39        '.playlist-item',
40        '.custom-ad-overlay',
41        '[data-ad]',
42        '.ad-container',
43        '.popup-ad',
44        '.overlay-ad',
45        '.banner-ad-container',
46        '.banner-ad-wrapper',
47        '.integrated-ad-container',
48        '.integrated-ad-wrapper',
49        '.download-btn',
50        '.filmyfly-download-button'
51    ];
52
53    // Tracking state for click/touch follow-through
54    let lastTouchPos = null;
55    let intendedTargetAtDown = null;
56    let lastNavigationTime = 0;
57    let lastNavigationUrl = '';
58    let fallbackAdTriggeredTime = 0;
59
60    /**
61     * Dual-Action Engine: Determine if an element belongs to an ad overlay or ad container
62     */
63    function isAdElement(el) {
64        if (!el || !el.tagName) return false;
65        
66        // 1. Check if it's an external link (Adsterra transparent overlay links to bendspecimen or external ad URL)
67        try {
68            const anchor = el.tagName === 'A' ? el : el.closest('a');
69            if (anchor && anchor.hostname) {
70                const currentHost = window.location.hostname.toLowerCase();
71                const linkHost = anchor.hostname.toLowerCase();
72                if (linkHost && linkHost !== currentHost && !linkHost.endsWith('.' + currentHost)) {
73                    return true;
74                }
75            }
76        } catch (e) {}
77
78        // 2. Exclude specific known ad classes and IDs
79        const className = typeof el.className === 'string' ? el.className.toLowerCase() : '';
80        const idName = typeof el.id === 'string' ? el.id.toLowerCase() : '';
81        
82        const adKeywords = [
83            'banner-ad', 'integrated-ad', 'popup-ad', 'overlay-ad', 
84            'custom-ad', 'social-bar', 'adsterra', 'bendspecimen', 'highperformance'
85        ];
86        for (let i = 0; i < adKeywords.length; i++) {
87            if (className.includes(adKeywords[i]) || idName.includes(adKeywords[i])) {
88                return true;
89            }
90        }
91        
92        if (el.hasAttribute && el.hasAttribute('data-ad')) {
93            return true;
94        }
95
96        // 3. Check if element is a fixed full-screen overlay outside our main site containers
97        try {
98            if (typeof window !== 'undefined' && window.getComputedStyle) {
99                const style = window.getComputedStyle(el);
100                if (style && (style.position === 'fixed' || style.position === 'absolute')) {
101                    const zIndex = parseInt(style.zIndex, 10);
102                    if (!isNaN(zIndex) && zIndex >= 1000) {
103                        // If it is NOT inside our site's legitimate containers
104                        if (!el.closest('.modal, .header, .navbar, .mobile-menu, nav, .main-container, .cards-grid')) {
105                            return true;
106                        }
107                    }
108                }
109            }
110        } catch (e) {}
111
112        return false;
113    }
114
115    /**
116     * Extract actionable destination or click action from an element
117     */
118    function extractTarget(el) {
119        if (!el || !el.tagName) return null;
120
121        // Ignore clicks inside form controls
122        if (el.closest('input, textarea, select, form')) {
123            return null;
124        }
125
126        // 1. Check for card element (.content-card, .category-card, .batch-card, .course-card, .subject-card)
127        const card = el.closest('.content-card, .category-card, .batch-card, .course-card, .subject-card');
128        if (card && !isAdElement(card)) {
129            // Check for video card (e.g. onclick="openVideo(id, catId)")
130            const onclickAttr = card.getAttribute('onclick') || '';
131            const match = onclickAttr.match(/openVideo\s*\(\s*(\d+)(?:\s*,\s*(\d+))?/);
132            if (match) {
133                const videoId = parseInt(match[1], 10);
134                const catId = match[2] ? parseInt(match[2], 10) : 0;
135                let videoUrl = 'video_player.php?video_id=' + videoId;
136                if (catId) {
137                    videoUrl += '&category_id=' + catId;
138                }
139                return {
140                    type: 'video',
141                    element: card,
142                    videoUrl: videoUrl,
143                    onclickAttr: onclickAttr
144                };
145            }
146
147            // Check for folder link (e.g. <a href="subjects.php?..." class="content-card">)
148            const link = card.tagName === 'A' ? card : (card.closest('a[href]') || card.querySelector('a[href]'));
149            if (link && link.href) {
150                const href = link.getAttribute('href');
151                if (href && href !== '#' && !href.startsWith('javascript:')) {
152                    try {
153                        const currentHost = window.location.hostname.toLowerCase();
154                        const linkHost = (link.hostname || '').toLowerCase();
155                        if (!linkHost || linkHost === currentHost || linkHost.endsWith('.' + currentHost)) {
156                            return {
157                                type: 'link',
158                                element: card,
159                                href: link.href,
160                                target: link.getAttribute('target') || '_self'
161                            };
162                        }
163                    } catch (e) {}
164                }
165            }
166
167            // Check for clickable card with onclick
168            if (onclickAttr || typeof card.onclick === 'function') {
169                return {
170                    type: 'clickable',
171                    element: card,
172                    onclickAttr: onclickAttr
173                };
174            }
175        }
176
177        // 2. Check for general internal site links (breadcrumbs, navbar, logos, explore buttons)
178        const link = el.closest('a[href]');
179        if (link && link.href && !isAdElement(link)) {
180            const href = link.getAttribute('href');
181            if (href && href !== '#' && !href.startsWith('javascript:')) {
182                try {
183                    const currentHost = window.location.hostname.toLowerCase();
184                    const linkHost = (link.hostname || '').toLowerCase();
185                    if (!linkHost || linkHost === currentHost || linkHost.endsWith('.' + currentHost)) {
186                        return {
187                            type: 'link',
188                            element: link,
189                            href: link.href,
190                            target: link.getAttribute('target') || '_self'
191                        };
192                    }
193                } catch (e) {}
194            }
195        }
196
197        // 3. Check for general clickable buttons or controls
198        const clickable = el.closest('button, .view-btn, [role="button"]');
199        if (clickable && !isAdElement(clickable)) {
200            const onclickAttr = clickable.getAttribute('onclick');
201            if (onclickAttr || typeof clickable.onclick === 'function') {
202                return {
203                    type: 'clickable',
204                    element: clickable,
205                    onclickAttr: onclickAttr
206                };
207            }
208        }
209
210        return null;
211    }
212
213    /**
214     * Inspect all DOM elements at (x, y) looking through transparent ad overlays
215     */
216    function getUnderlyingTarget(x, y) {
217        if (typeof x !== 'number' || typeof y !== 'number' || isNaN(x) || isNaN(y)) return null;
218        
219        if (typeof document !== 'undefined' && typeof document.elementsFromPoint === 'function') {
220            const elements = document.elementsFromPoint(x, y);
221            if (elements && elements.length > 0) {
222                for (let i = 0; i < elements.length; i++) {
223                    const el = elements[i];
224                    if (!el || el === document.body || el === document.documentElement) continue;
225                    if (isAdElement(el)) continue;
226                    
227                    const target = extractTarget(el);
228                    if (target) return target;
229                }
230            }
231        } else if (typeof document !== 'undefined' && typeof document.elementFromPoint === 'function') {
232            const el = document.elementFromPoint(x, y);
233            if (el && !isAdElement(el)) {
234                return extractTarget(el);
235            }
236        }
237
238        return null;
239    }
240
241    /**
242     * Open the clicked folder / video / card with a micro-delay so the popup ad window opens first
243     */
244    function triggerContentOpen(target) {
245        if (!target) return;
246
247        const now = Date.now();
248        const destUrl = target.videoUrl || target.href;
249
250        if (target.type === 'link' || target.type === 'video') {
251            if (now - lastNavigationTime < 1000 && lastNavigationUrl === destUrl) {
252                return;
253            }
254            lastNavigationTime = now;
255            lastNavigationUrl = destUrl;
256
257            console.log('LHFree Popunder: Navigating to folder/video alongside ad:', destUrl);
258
259            // 60ms delay ensures the ad click event initiates opening its new tab cleanly,
260            // then the current window navigates to the folder/video!
261            setTimeout(function() {
262                try {
263                    window.location.href = destUrl;
264                } catch (err) {
265                    window.location.assign(destUrl);
266                }
267            }, 60);
268        } else if (target.type === 'clickable') {
269            if (now - lastNavigationTime < 800) return;
270            lastNavigationTime = now;
271
272            setTimeout(function() {
273                try {
274                    if (typeof target.element.click === 'function') {
275                        target.element.click();
276                    } else if (target.onclickAttr) {
277                        new Function(target.onclickAttr).call(target.element);
278                    }
279                } catch (err) {}
280            }, 60);
281        }
282    }
283
284    /**
285     * Setup global capture listeners for Pointer / Touch / Click
286     */
287    function attachDualActionListeners() {
288        if (typeof document === 'undefined') return;
289
290        // Track touch coordinates and detect intended target on mobile devices
291        const recordTouch = function(e) {
292            if (e.touches && e.touches[0]) {
293                lastTouchPos = { x: e.touches[0].clientX, y: e.touches[0].clientY, time: Date.now() };
294                recordDown(e);
295            }
296        };
297        document.addEventListener('touchstart', recordTouch, { capture: true, passive: true });
298
299        document.addEventListener('touchend', function(e) {
300            if (e.changedTouches && e.changedTouches[0]) {
301                lastTouchPos = { x: e.changedTouches[0].clientX, y: e.changedTouches[0].clientY, time: Date.now() };
302            }
303        }, { capture: true, passive: true });
304
305        // Record the intended element at pointer/mouse/touch down
306        const recordDown = function(e) {
307            if (e.button !== undefined && e.button !== 0) return;
308            let x = e.clientX;
309            let y = e.clientY;
310            if (e.touches && e.touches[0]) {
311                x = e.touches[0].clientX;
312                y = e.touches[0].clientY;
313            }
314            if ((!x && !y) && lastTouchPos && (Date.now() - lastTouchPos.time < 1000)) {
315                x = lastTouchPos.x;
316                y = lastTouchPos.y;
317            }
318
319            const target = getUnderlyingTarget(x, y);
320            if (target) {
321                const hitDirectly = target.element.contains(e.target) || e.target === target.element;
322                intendedTargetAtDown = {
323                    target: target,
324                    wasOverlay: !hitDirectly,
325                    time: Date.now()
326                };
327            } else {
328                intendedTargetAtDown = null;
329            }
330        };
331
332        document.addEventListener('pointerdown', recordDown, { capture: true, passive: true });
333        document.addEventListener('mousedown', recordDown, { capture: true, passive: true });
334
335        // Capture phase click handler
336        document.addEventListener('click', function(e) {
337            if (e.button !== undefined && e.button !== 0) return;
338
339            let x = e.clientX;
340            let y = e.clientY;
341            if ((!x && !y) && lastTouchPos && (Date.now() - lastTouchPos.time < 1000)) {
342                x = lastTouchPos.x;
343                y = lastTouchPos.y;
344            }
345
346            let target = null;
347            let wasOverlay = false;
348
349            if (intendedTargetAtDown && (Date.now() - intendedTargetAtDown.time < 1500)) {
350                target = intendedTargetAtDown.target;
351                wasOverlay = intendedTargetAtDown.wasOverlay;
352            }
353
354            if (!target) {
355                target = getUnderlyingTarget(x, y);
356            }
357
358            if (target && !wasOverlay) {
359                wasOverlay = !target.element.contains(e.target) && e.target !== target.element;
360            }
361
362            console.log('LHFree Click:', { target: target, wasOverlay: wasOverlay, targetEl: e.target });
363
364            if (target && wasOverlay) {
365                triggerContentOpen(target);
366            }
367
368            intendedTargetAtDown = null;
369        }, true); // Important: capture phase
370    }
371
372    /**
373     * Clear all Adsterra client-side frequency cookies and localStorage markers
374     * so that ads can trigger across all subpages automatically.
375     */
376    function clearAdsterraCapping() {
377        try {
378            // Clear localStorage keys created by Adsterra
379            if (typeof localStorage !== 'undefined') {
380                const keysToRemove = [];
381                for (let i = 0; i < localStorage.length; i++) {
382                    const key = localStorage.key(i);
383                    if (key && (
384                        key.startsWith('pp_') || 
385                        key.startsWith('_') || 
386                        key.includes('hu89') || 
387                        key.includes('bbrp') || 
388                        key.includes('ged') || 
389                        key.includes('cesb') || 
390                        key.includes('showOn') || 
391                        key.includes('cookie') ||
392                        key.includes('adsterra') ||
393                        key.includes('LHFreePopunder')
394                    )) {
395                        keysToRemove.push(key);
396                    }
397                }
398                keysToRemove.forEach(k => {
399                    try { localStorage.removeItem(k); } catch(e) {}
400                });
401            }
402
403            // Clear Adsterra cookies on this domain
404            if (typeof document !== 'undefined' && document.cookie) {
405                const cookies = document.cookie.split(';');
406                for (let i = 0; i < cookies.length; i++) {
407                    const cookie = cookies[i].trim();
408                    const eqPos = cookie.indexOf('=');
409                    const name = eqPos > -1 ? cookie.substr(0, eqPos) : cookie;
410                    if (name && (
411                        name.startsWith('pp_') || 
412                        name.startsWith('_') || 
413                        name.includes('cesb') || 
414                        name.includes('ged') || 
415                        name.includes('bbrp') ||
416                        name.includes('show') ||
417                        name.includes('cookie')
418                    )) {
419                        document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/;';
420                        document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; domain=' + window.location.hostname + ';';
421                        document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT;';
422                    }
423                }
424            }
425        } catch (e) {}
426    }
427
428    function init() {
429        if (config.isInitialized) return;
430        
431        // Attach the Dual-Action Follow-Through listeners once
432        attachDualActionListeners();
433
434        if (typeof window === 'undefined') return;
435
436        // Premium users check
437        if (window.LHFreePremiumUser === true) {
438            console.log('LHFree Popunder: Premium user - skipping');
439            return;
440        }
441        
442        // Page exclusion check
443        const currentPage = window.location.pathname.split('/').pop();
444        if (excludedPages.includes(currentPage)) {
445            console.log('LHFree Popunder: Page excluded -', currentPage);
446            return;
447        }
448        
449        // Load config
450        if (window.LHFreePopunderConfig) {
451            config.cooldownSeconds = parseInt(window.LHFreePopunderConfig.cooldownSeconds) || 30;
452            config.maxPerSession = parseInt(window.LHFreePopunderConfig.maxPerSession) || 5;
453            config.adCode = window.LHFreePopunderConfig.adCode || '';
454            config.adName = window.LHFreePopunderConfig.adName || '';
455        }
456        
457        // Load tracking data
458        loadTrackingData();
459        
460        // Parse script source if present in adCode
461        let scriptSrc = '';
462        if (config.adCode) {
463            const match = config.adCode.match(/src=["']([^"']+)["']/i);
464            if (match) {
465                scriptSrc = match[1];
466            }
467        }
468        
469        // If it is a script-based popunder (Type 1 like Adsterra/RevenueCPM)
470        if (scriptSrc) {
471            console.log('LHFree Popunder: Script-based ad detected:', scriptSrc);
472            
473            // Clear Adsterra capping cookies and localStorage markers to allow ads across all subpages
474            clearAdsterraCapping();
475
476            // Always inject ad script on every page so it is active across all subpages
477            setAdCursor(true);
478            injectScript(scriptSrc);
479            
480            // Listen for click to update tracking
481            const clickHandler = function(e) {
482                // Ignore download buttons and exclusions
483                if (e.target && e.target.closest) {
484                    let isExcluded = false;
485                    for (let selector of excludedSelectors) {
486                        if (e.target.closest(selector)) {
487                            isExcluded = true;
488                            break;
489                        }
490                    }
491                    if (isExcluded) return;
492                }
493                
494                console.log('LHFree Popunder: Script popunder clicked - updating tracking');
495                updateTracking();
496                clearAdsterraCapping();
497                setAdCursor(false);
498                document.removeEventListener('click', clickHandler, true);
499            };
500            document.addEventListener('click', clickHandler, true);
501
502            // Ensure next page load starts clean
503            window.addEventListener('beforeunload', clearAdsterraCapping, { passive: true });
504            window.addEventListener('pagehide', clearAdsterraCapping, { passive: true });
505
506            config.isInitialized = true;
507            return;
508        }
509        
510        // If it is NOT a script-based ad (direct URL or custom HTML fallback)
511        if (config.adCode) {
512            console.log('LHFree Popunder: Fallback ad detected');
513            attachFallbackEventListeners();
514            config.isInitialized = true;
515        }
516    }
517
518    function setAdCursor(state) {
519        try {
520            if (state) {
521                document.documentElement.style.setProperty('cursor', 'pointer', 'important');
522                document.body.style.setProperty('cursor', 'pointer', 'important');
523            } else {
524                document.documentElement.style.removeProperty('cursor');
525                document.body.style.removeProperty('cursor');
526            }
527        } catch (e) {}
528    }
529
530    function injectScript(src) {
531        try {
532            let absoluteSrc = src;
533            if (absoluteSrc.indexOf('//') === 0) {
534                absoluteSrc = window.location.protocol + absoluteSrc;
535            } else if (absoluteSrc.indexOf('http') !== 0) {
536                const tempAnchor = document.createElement('a');
537                tempAnchor.href = absoluteSrc;
538                absoluteSrc = tempAnchor.href;
539            }
540            
541            let alreadyExists = false;
542            const scripts = document.getElementsByTagName('script');
543            for (let i = 0; i < scripts.length; i++) {
544                if (scripts[i].src === absoluteSrc) {
545                    alreadyExists = true;
546                    break;
547                }
548            }
549            
550            if (!alreadyExists) {
551                const script = document.createElement('script');
552                script.src = absoluteSrc;
553                script.type = 'text/javascript';
554                document.body.appendChild(script);
555                console.log('LHFree Popunder: Script injected:', absoluteSrc);
556            } else {
557                console.log('LHFree Popunder: Script already exists, skipped duplicate injection:', absoluteSrc);
558            }
559        } catch (e) {}
560    }
561
562    function shouldTrigger() {
563        if (window.LHFreePremiumUser === true) return false;
564        if (!config.adCode) return false;
565        
566        // Check session limit
567        if (config.sessionCount >= config.maxPerSession) {
568            console.log('LHFree Popunder: Session limit reached');
569            return false;
570        }
571        
572        // Check cooldown
573        const now = Date.now();
574        const timeDiff = (now - config.lastPopunderTime) / 1000;
575        if (timeDiff < config.cooldownSeconds) {
576            return false;
577        }
578        return true;
579    }
580
581    function scheduleCooldownEnd() {
582        const now = Date.now();
583        const timeDiff = now - config.lastPopunderTime;
584        const cooldownMs = config.cooldownSeconds * 1000;
585        const remainingMs = cooldownMs - timeDiff;
586        
587        if (remainingMs > 0) {
588            setTimeout(function() {
589                console.log('LHFree Popunder: Cooldown ended - reinitializing');
590                config.isInitialized = false;
591                init();
592            }, remainingMs + 500);
593        }
594    }
595
596    function loadTrackingData() {
597        // Automatically remove legacy permanent localStorage locks that blocked users forever
598        try {
599            if (localStorage.getItem('LHFreePopunder_sessionCount')) {
600                localStorage.removeItem('LHFreePopunder_sessionCount');
601            }
602            if (localStorage.getItem('LHFreePopunder_lastTime')) {
603                localStorage.removeItem('LHFreePopunder_lastTime');
604            }
605        } catch (e) {}
606
607        try {
608            const lastTime = sessionStorage.getItem('LHFreePopunder_lastTime');
609            const sessionCount = sessionStorage.getItem('LHFreePopunder_sessionCount');
610            if (lastTime) {
611                config.lastPopunderTime = parseInt(lastTime, 10);
612            }
613            if (sessionCount) {
614                config.sessionCount = parseInt(sessionCount, 10);
615            }
616        } catch (e) {}
617    }
618
619    function updateTracking() {
620        config.lastPopunderTime = Date.now();
621        config.sessionCount++;
622        try {
623            sessionStorage.setItem('LHFreePopunder_lastTime', config.lastPopunderTime.toString());
624            sessionStorage.setItem('LHFreePopunder_sessionCount', config.sessionCount.toString());
625        } catch (e) {}
626    }
627
628    // Fallback system for direct URL or Custom HTML
629    function attachFallbackEventListeners() {
630        document.addEventListener('click', handleFallbackClick, { passive: true });
631    }
632
633    function handleFallbackClick(e) {
634        if (window.LHFreePremiumUser === true) return;
635        
636        // Check exclusions
637        if (e.target && e.target.closest) {
638            let isExcluded = false;
639            for (let selector of excludedSelectors) {
640                if (e.target.closest(selector)) {
641                    isExcluded = true;
642                    break;
643                }
644            }
645            if (isExcluded) return;
646        }
647        
648        if (shouldTrigger()) {
649            fallbackAdTriggeredTime = Date.now();
650            triggerFallbackAd();
651        }
652    }
653
654    function triggerFallbackAd() {
655        const adCode = String(config.adCode).trim();
656        
657        // If it starts with http or //, treat it as a direct link
658        if (adCode.startsWith('http') || adCode.startsWith('//')) {
659            let url = adCode;
660            if (url.startsWith('//')) {
661                url = window.location.protocol + url;
662            }
663            try {
664                const popup = window.open(url, '_blank');
665                if (popup) {
666                    popup.blur();
667                    window.focus();
668                    updateTracking();
669                    scheduleCooldownEnd();
670                }
671            } catch (err) {}
672            return;
673        }
674        
675        // Otherwise, open about:blank popup and write HTML (safe fallback)
676        try {
677            const windowName = 'LHFreePopunder_' + Date.now();
678            const popup = window.open('about:blank', windowName, 'width=800,height=600,scrollbars=yes,resizable=yes');
679            if (popup) {
680                popup.document.write(`
681                    <!DOCTYPE html>
682                    <html>
683                    <head><title>Advertisement</title></head>
684                    <body>
685                        ${adCode}
686                        <script>
687                            window.onload = function() {
688                                if (window.opener) window.opener.focus();
689                            };
690                        </script>
691                    </body>
692                    </html>
693                `);
694                popup.document.close();
695                popup.blur();
696                window.focus();
697                updateTracking();
698                scheduleCooldownEnd();
699            }
700        } catch (e) {}
701    }
702
703    // Public API
704    if (typeof window !== 'undefined') {
705        window.LHFreePopunder = {
706            init: init,
707            resetSession: function() {
708                config.lastPopunderTime = 0;
709                config.sessionCount = 0;
710                try {
711                    localStorage.removeItem('LHFreePopunder_lastTime');
712                    localStorage.removeItem('LHFreePopunder_sessionCount');
713                    sessionStorage.removeItem('LHFreePopunder_lastTime');
714                    sessionStorage.removeItem('LHFreePopunder_sessionCount');
715                } catch (e) {}
716            }
717        };
718
719        // Auto-init
720        if (document.readyState === 'loading') {
721            document.addEventListener('DOMContentLoaded', init);
722        } else {
723            init();
724        }
725    }
726})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.