Blocks ads, removes floating elements and login prompts, and adds PDF download functionality to ExamVeda pages
Size
11.5 KB
Version
1.0.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.0.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 // Find the main content area
245 const contentSelectors = [
246 '.page-content',
247 'article',
248 'main',
249 '.content',
250 '#content',
251 '.post-content',
252 '.entry-content'
253 ];
254
255 let contentElement = null;
256 for (const selector of contentSelectors) {
257 contentElement = document.querySelector(selector);
258 if (contentElement) {
259 console.log('Found content element:', selector);
260 break;
261 }
262 }
263
264 // If no specific content found, use body but clone it to remove unwanted elements
265 if (!contentElement) {
266 console.log('No specific content element found, using body');
267 contentElement = document.body;
268 }
269
270 // Clone the content to avoid modifying the original page
271 const clonedContent = contentElement.cloneNode(true);
272
273 // Remove unwanted elements from the clone
274 const unwantedSelectors = [
275 '#examveda-pdf-download-btn',
276 '.adsbygoogle',
277 'ins.adsbygoogle',
278 '.google-auto-placed',
279 '[data-ad-slot]',
280 'script',
281 'iframe',
282 '.whatsapp-wrapper',
283 'div[id*="clever"]',
284 'nav',
285 'header',
286 'footer',
287 '.sidebar',
288 '.comments',
289 '.social-share'
290 ];
291
292 unwantedSelectors.forEach(selector => {
293 const elements = clonedContent.querySelectorAll(selector);
294 elements.forEach(el => el.remove());
295 });
296
297 // Get page title for filename
298 const pageTitle = document.title.replace(/[^a-z0-9]/gi, '_').substring(0, 50) || 'examveda_page';
299
300 // Configure html2pdf options
301 const opt = {
302 margin: [10, 10, 10, 10],
303 filename: `${pageTitle}.pdf`,
304 image: { type: 'jpeg', quality: 0.98 },
305 html2canvas: {
306 scale: 2,
307 useCORS: true,
308 logging: false,
309 letterRendering: true
310 },
311 jsPDF: {
312 unit: 'mm',
313 format: 'a4',
314 orientation: 'portrait'
315 },
316 pagebreak: { mode: ['avoid-all', 'css', 'legacy'] }
317 };
318
319 // Generate PDF
320 console.log('Generating PDF with html2pdf...');
321 await html2pdf().set(opt).from(clonedContent).save();
322 console.log('PDF generated successfully');
323 }
324
325 // Initialize the extension
326 function init() {
327 console.log('Initializing ExamVeda extension...');
328
329 // Add blocking styles immediately
330 addBlockingStyles();
331
332 // Remove ads and floating elements
333 removeAdsAndFloatingElements();
334
335 // Create PDF download button when DOM is ready
336 if (document.readyState === 'loading') {
337 document.addEventListener('DOMContentLoaded', createPDFDownloadButton);
338 } else {
339 createPDFDownloadButton();
340 }
341
342 // Use MutationObserver to catch dynamically loaded ads
343 const debouncedRemoval = debounce(removeAdsAndFloatingElements, 500);
344
345 const observer = new MutationObserver(debouncedRemoval);
346
347 observer.observe(document.body, {
348 childList: true,
349 subtree: true
350 });
351
352 console.log('MutationObserver started to watch for dynamic ads');
353
354 // Also check periodically for any ads that might slip through
355 setInterval(removeAdsAndFloatingElements, 3000);
356 }
357
358 // Start the extension
359 if (document.readyState === 'loading') {
360 document.addEventListener('DOMContentLoaded', init);
361 } else {
362 init();
363 }
364
365})();