ExamVeda Ad Blocker & PDF Downloader

Blocks ads, removes floating elements and login prompts, and adds PDF download functionality to ExamVeda pages

Size

14.2 KB

Version

1.1.1

Created

Dec 29, 2025

Updated

2 months ago

1// ==UserScript==
2// @name		ExamVeda Ad Blocker & PDF Downloader
3// @description		Blocks ads, removes floating elements and login prompts, and adds PDF download functionality to ExamVeda pages
4// @version		1.1.1
5// @match		https://*.examveda.com/*
6// @icon		https://www.examveda.com/favicon.ico
7// @require		https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js
8// ==/UserScript==
9(function() {
10    'use strict';
11
12    console.log('ExamVeda Ad Blocker & PDF Downloader initialized');
13
14    // Utility function to debounce
15    function debounce(func, wait) {
16        let timeout;
17        return function executedFunction(...args) {
18            const later = () => {
19                clearTimeout(timeout);
20                func(...args);
21            };
22            clearTimeout(timeout);
23            timeout = setTimeout(later, wait);
24        };
25    }
26
27    // Function to remove ads and unwanted elements
28    function removeAdsAndFloatingElements() {
29        console.log('Removing ads and floating elements...');
30
31        // Remove Google AdSense ads
32        const adsSelectors = [
33            '.adsbygoogle',
34            'ins.adsbygoogle',
35            '.google-auto-placed',
36            '[data-ad-slot]',
37            '[data-ad-format]',
38            'div[id*="aswift"]',
39            'iframe[id*="aswift"]',
40            '.goog-rtopics',
41            '.google-anno-skip',
42            '.google-anno-sc'
43        ];
44
45        adsSelectors.forEach(selector => {
46            const elements = document.querySelectorAll(selector);
47            elements.forEach(el => {
48                console.log('Removing ad element:', selector);
49                el.remove();
50            });
51        });
52
53        // Remove floating elements
54        const floatingSelectors = [
55            'div[id*="clever"]',
56            'div[id*="top-scroll"]',
57            '.whatsapp-wrapper',
58            '.whatsapp-float',
59            '.fire-ring',
60            '[class*="floating"]',
61            '[class*="sticky-ad"]',
62            '[id*="sticky"]',
63            '.popup-overlay',
64            '.modal-overlay'
65        ];
66
67        floatingSelectors.forEach(selector => {
68            const elements = document.querySelectorAll(selector);
69            elements.forEach(el => {
70                console.log('Removing floating element:', selector);
71                el.remove();
72            });
73        });
74
75        // Remove login modals and popups
76        const loginSelectors = [
77            '.login-modal',
78            '.login-popup',
79            '#login-modal',
80            '[class*="login-overlay"]',
81            '[id*="login-popup"]',
82            '.signin-modal',
83            '.auth-modal'
84        ];
85
86        loginSelectors.forEach(selector => {
87            const elements = document.querySelectorAll(selector);
88            elements.forEach(el => {
89                console.log('Removing login element:', selector);
90                el.remove();
91            });
92        });
93
94        // Remove any overlay backgrounds
95        const overlays = document.querySelectorAll('[class*="overlay"], [id*="overlay"]');
96        overlays.forEach(overlay => {
97            if (overlay.style.position === 'fixed' || overlay.style.position === 'absolute') {
98                console.log('Removing overlay');
99                overlay.remove();
100            }
101        });
102
103        // Restore body scroll if it was disabled
104        document.body.style.overflow = 'auto';
105        document.documentElement.style.overflow = 'auto';
106    }
107
108    // Function to add CSS to hide ads and floating elements
109    function addBlockingStyles() {
110        const style = document.createElement('style');
111        style.textContent = `
112            /* Hide ads */
113            .adsbygoogle,
114            ins.adsbygoogle,
115            .google-auto-placed,
116            [data-ad-slot],
117            [data-ad-format],
118            div[id*="aswift"],
119            iframe[id*="aswift"],
120            .goog-rtopics,
121            .google-anno-skip,
122            .google-anno-sc {
123                display: none !important;
124                visibility: hidden !important;
125                opacity: 0 !important;
126                height: 0 !important;
127                width: 0 !important;
128            }
129
130            /* Hide floating elements */
131            div[id*="clever"],
132            div[id*="top-scroll"],
133            .whatsapp-wrapper,
134            .whatsapp-float,
135            .fire-ring,
136            [class*="floating"],
137            [class*="sticky-ad"],
138            [id*="sticky"] {
139                display: none !important;
140            }
141
142            /* Hide login modals */
143            .login-modal,
144            .login-popup,
145            #login-modal,
146            [class*="login-overlay"],
147            [id*="login-popup"],
148            .signin-modal,
149            .auth-modal {
150                display: none !important;
151            }
152
153            /* PDF Download Button Styles */
154            #examveda-pdf-download-btn {
155                position: fixed;
156                top: 100px;
157                right: 20px;
158                z-index: 9999;
159                background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
160                color: white;
161                border: none;
162                padding: 12px 24px;
163                border-radius: 8px;
164                cursor: pointer;
165                font-size: 16px;
166                font-weight: bold;
167                box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
168                transition: all 0.3s ease;
169                display: flex;
170                align-items: center;
171                gap: 8px;
172            }
173
174            #examveda-pdf-download-btn:hover {
175                transform: translateY(-2px);
176                box-shadow: 0 6px 20px rgba(102, 126, 234, 0.6);
177                background: linear-gradient(135deg, #764ba2 0%, #667eea 100%);
178            }
179
180            #examveda-pdf-download-btn:active {
181                transform: translateY(0);
182            }
183
184            #examveda-pdf-download-btn.downloading {
185                background: #6c757d;
186                cursor: wait;
187            }
188
189            /* Ensure body is scrollable */
190            body, html {
191                overflow: auto !important;
192            }
193        `;
194        document.head.appendChild(style);
195        console.log('Blocking styles added');
196    }
197
198    // Function to create PDF download button
199    function createPDFDownloadButton() {
200        // Check if button already exists
201        if (document.getElementById('examveda-pdf-download-btn')) {
202            console.log('PDF download button already exists');
203            return;
204        }
205
206        const button = document.createElement('button');
207        button.id = 'examveda-pdf-download-btn';
208        button.innerHTML = '📄 Download PDF';
209        button.title = 'Download this page as PDF';
210
211        button.addEventListener('click', async function() {
212            if (button.classList.contains('downloading')) {
213                return;
214            }
215
216            button.classList.add('downloading');
217            button.innerHTML = '⏳ Generating PDF...';
218
219            try {
220                await generatePDF();
221                button.innerHTML = '✅ Downloaded!';
222                setTimeout(() => {
223                    button.innerHTML = '📄 Download PDF';
224                    button.classList.remove('downloading');
225                }, 3000);
226            } catch (error) {
227                console.error('PDF generation failed:', error);
228                button.innerHTML = '❌ Failed';
229                setTimeout(() => {
230                    button.innerHTML = '📄 Download PDF';
231                    button.classList.remove('downloading');
232                }, 3000);
233            }
234        });
235
236        document.body.appendChild(button);
237        console.log('PDF download button created');
238    }
239
240    // Function to generate PDF
241    async function generatePDF() {
242        console.log('Starting PDF generation...');
243
244        try {
245            // Check if html2pdf is available
246            if (typeof html2pdf === 'undefined') {
247                console.error('html2pdf library not loaded');
248                throw new Error('PDF library not available');
249            }
250
251            // Find all question articles
252            const questions = document.querySelectorAll('article.question');
253            console.log('Found questions:', questions.length);
254
255            if (questions.length === 0) {
256                throw new Error('No questions found on page');
257            }
258
259            // Create a clean container for PDF content
260            const pdfContainer = document.createElement('div');
261            pdfContainer.style.cssText = `
262                width: 100%;
263                max-width: 800px;
264                margin: 0 auto;
265                padding: 30px;
266                background: white;
267                color: black;
268                font-family: Arial, sans-serif;
269                font-size: 14px;
270                line-height: 1.6;
271            `;
272
273            // Add title
274            const titleElement = document.createElement('h1');
275            titleElement.textContent = document.title;
276            titleElement.style.cssText = `
277                color: #333;
278                font-size: 24px;
279                margin-bottom: 30px;
280                text-align: center;
281                border-bottom: 2px solid #333;
282                padding-bottom: 15px;
283            `;
284            pdfContainer.appendChild(titleElement);
285
286            // Process each question
287            questions.forEach((question, index) => {
288                // Skip ad containers
289                if (question.classList.contains('center') || question.querySelector('.adsbygoogle')) {
290                    return;
291                }
292
293                // Create question container
294                const questionDiv = document.createElement('div');
295                questionDiv.style.cssText = `
296                    margin-bottom: 30px;
297                    page-break-inside: avoid;
298                    border: 1px solid #ddd;
299                    padding: 15px;
300                    border-radius: 5px;
301                `;
302
303                // Get question text
304                const questionText = question.querySelector('.question-main');
305                if (questionText) {
306                    const qText = document.createElement('div');
307                    qText.innerHTML = `<strong>Q${index + 1}. ${questionText.textContent.trim()}</strong>`;
308                    qText.style.cssText = `
309                        color: #000;
310                        font-size: 16px;
311                        margin-bottom: 15px;
312                        font-weight: bold;
313                    `;
314                    questionDiv.appendChild(qText);
315                }
316
317                // Get options
318                const options = question.querySelectorAll('.question-options p');
319                if (options.length > 0) {
320                    const optionsDiv = document.createElement('div');
321                    optionsDiv.style.cssText = 'margin-left: 20px;';
322                    
323                    options.forEach(option => {
324                        const optionText = option.textContent.trim();
325                        if (optionText) {
326                            const optDiv = document.createElement('div');
327                            optDiv.textContent = optionText;
328                            optDiv.style.cssText = `
329                                color: #333;
330                                margin-bottom: 8px;
331                                font-size: 14px;
332                            `;
333                            optionsDiv.appendChild(optDiv);
334                        }
335                    });
336                    
337                    questionDiv.appendChild(optionsDiv);
338                }
339
340                pdfContainer.appendChild(questionDiv);
341            });
342
343            console.log('PDF content prepared, generating PDF...');
344
345            // Get page title for filename
346            const pageTitle = document.title.replace(/[^a-z0-9]/gi, '_').substring(0, 50) || 'examveda_page';
347
348            // Configure html2pdf options with better settings
349            const opt = {
350                margin: [15, 15, 15, 15],
351                filename: `${pageTitle}.pdf`,
352                image: { type: 'jpeg', quality: 0.95 },
353                html2canvas: { 
354                    scale: 2,
355                    useCORS: true,
356                    logging: false,
357                    backgroundColor: '#ffffff',
358                    windowWidth: 800
359                },
360                jsPDF: { 
361                    unit: 'mm', 
362                    format: 'a4', 
363                    orientation: 'portrait',
364                    compress: true
365                },
366                pagebreak: { mode: ['avoid-all', 'css', 'legacy'] }
367            };
368
369            console.log('Calling html2pdf with options:', opt);
370            
371            // Generate and save PDF
372            await html2pdf().set(opt).from(pdfContainer).save();
373            
374            console.log('PDF generated and downloaded successfully');
375        } catch (error) {
376            console.error('PDF generation error:', error);
377            console.error('Error details:', error.message);
378            console.error('Error stack:', error.stack);
379            throw error;
380        }
381    }
382
383    // Initialize the extension
384    function init() {
385        console.log('Initializing ExamVeda extension...');
386
387        // Add blocking styles immediately
388        addBlockingStyles();
389
390        // Remove ads and floating elements
391        removeAdsAndFloatingElements();
392
393        // Create PDF download button when DOM is ready
394        if (document.readyState === 'loading') {
395            document.addEventListener('DOMContentLoaded', createPDFDownloadButton);
396        } else {
397            createPDFDownloadButton();
398        }
399
400        // Use MutationObserver to catch dynamically loaded ads
401        const debouncedRemoval = debounce(removeAdsAndFloatingElements, 500);
402        
403        const observer = new MutationObserver(debouncedRemoval);
404        
405        observer.observe(document.body, {
406            childList: true,
407            subtree: true
408        });
409
410        console.log('MutationObserver started to watch for dynamic ads');
411
412        // Also check periodically for any ads that might slip through
413        setInterval(removeAdsAndFloatingElements, 3000);
414    }
415
416    // Start the extension
417    if (document.readyState === 'loading') {
418        document.addEventListener('DOMContentLoaded', init);
419    } else {
420        init();
421    }
422
423})();