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})();