Removes ads, enables text copying, and adds download buttons for question sections
Size
12.0 KB
Version
2.1.3
Created
Oct 29, 2025
Updated
about 2 months ago
1// ==UserScript==
2// @name Examveda Ad Remover & Question Downloader
3// @description Removes ads, enables text copying, and adds download buttons for question sections
4// @version 2.1.3
5// @match https://*.examveda.com/*
6// @icon https://www.examveda.com/favicon.ico
7// @require https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js
8// ==/UserScript==
9(function() {
10 'use strict';
11
12 console.log('=== Examveda Extension Starting ===');
13
14 // Add CSS to hide ads, banners, and enable text selection
15 TM_addStyle(`
16 /* Remove all ads including floating and banner ads */
17 ins.adsbygoogle,
18 .adsbygoogle,
19 [data-ad-slot],
20 [id*="google_ads"],
21 [id*="google-auto-placed"],
22 [class*="advertisement"],
23 [class*="google-auto-placed"],
24 iframe[src*="doubleclick"],
25 iframe[src*="googlesyndication"],
26 div[style*="z-index: 2147483647"],
27 div[style*="position: fixed"][style*="bottom"],
28 div[style*="position: fixed"][style*="top"],
29 .banner-ad,
30 #banner-ad,
31 [class*="sticky-ad"],
32 [id*="sticky-ad"] {
33 display: none !important;
34 visibility: hidden !important;
35 height: 0 !important;
36 width: 0 !important;
37 opacity: 0 !important;
38 pointer-events: none !important;
39 }
40
41 /* Enable text selection everywhere */
42 * {
43 -webkit-user-select: text !important;
44 -moz-user-select: text !important;
45 -ms-user-select: text !important;
46 user-select: text !important;
47 }
48
49 /* Download button styles */
50 .examveda-download-btn {
51 background: #4CAF50 !important;
52 color: white !important;
53 border: none !important;
54 padding: 12px 24px !important;
55 margin: 20px 0 !important;
56 border-radius: 5px !important;
57 cursor: pointer !important;
58 font-size: 16px !important;
59 font-weight: bold !important;
60 box-shadow: 0 2px 5px rgba(0,0,0,0.2) !important;
61 transition: all 0.3s ease !important;
62 display: block !important;
63 width: 100% !important;
64 max-width: 300px !important;
65 }
66
67 .examveda-download-btn:hover {
68 background: #45a049 !important;
69 transform: scale(1.02) !important;
70 }
71 `);
72
73 console.log('CSS styles added');
74
75 // Remove copy protection event listeners
76 ['copy', 'cut', 'contextmenu', 'selectstart'].forEach(eventType => {
77 document.addEventListener(eventType, function(e) {
78 e.stopPropagation();
79 }, true);
80 });
81
82 // Override copy protection properties
83 document.oncopy = null;
84 document.oncut = null;
85 document.onselectstart = null;
86 document.oncontextmenu = null;
87
88 TM_runBody(() => {
89 document.body.oncopy = null;
90 document.body.oncut = null;
91 document.body.onselectstart = null;
92 document.body.oncontextmenu = null;
93 console.log('Copy protection removed');
94 });
95
96 // Function to aggressively remove ad elements
97 function removeAdElements() {
98 const adSelectors = [
99 'ins.adsbygoogle',
100 '.adsbygoogle',
101 '[data-ad-slot]',
102 '[id*="google_ads"]',
103 '[id*="google-auto-placed"]',
104 '[class*="google-auto-placed"]',
105 'iframe[src*="doubleclick"]',
106 'iframe[src*="googlesyndication"]',
107 '.banner-ad',
108 '#banner-ad'
109 ];
110
111 let removedCount = 0;
112 adSelectors.forEach(selector => {
113 const elements = document.querySelectorAll(selector);
114 elements.forEach(el => {
115 el.remove();
116 removedCount++;
117 });
118 });
119
120 // Remove floating/fixed positioned elements that look like ads
121 const allElements = document.querySelectorAll('*');
122 allElements.forEach(el => {
123 const style = window.getComputedStyle(el);
124 if ((style.position === 'fixed' || style.position === 'sticky') &&
125 (el.innerHTML.includes('google') || el.innerHTML.includes('ad') ||
126 el.className.includes('ad') || el.id.includes('ad'))) {
127 el.remove();
128 removedCount++;
129 }
130 });
131
132 if (removedCount > 0) {
133 console.log(`Removed ${removedCount} ad elements`);
134 }
135 }
136
137 // Function to download entire page content as PDF
138 async function downloadPageAsPDF() {
139 console.log('Starting PDF download...');
140
141 try {
142 const { jsPDF } = window.jspdf;
143 const pdf = new jsPDF('p', 'pt', 'a4');
144
145 // Get page title
146 const pageTitle = document.title || 'Examveda Content';
147
148 // Get main content
149 const mainContent = document.querySelector('.main-content') || document.body;
150
151 // Extract all text content from questions and answers
152 let contentText = `${pageTitle}\n\n`;
153 contentText += `URL: ${window.location.href}\n`;
154 contentText += `Downloaded: ${new Date().toLocaleString()}\n\n`;
155 contentText += '='.repeat(80) + '\n\n';
156
157 // Get all questions
158 const questions = mainContent.querySelectorAll('.question.single-question');
159
160 if (questions.length > 0) {
161 questions.forEach((question, index) => {
162 // Get question text
163 const questionText = question.querySelector('.question-main');
164 if (questionText) {
165 contentText += `Question ${index + 1}: ${questionText.textContent.trim()}\n\n`;
166 }
167
168 // Get options
169 const options = question.querySelectorAll('.question-options p');
170 options.forEach(option => {
171 const optionText = option.textContent.trim();
172 if (optionText) {
173 contentText += ` ${optionText}\n`;
174 }
175 });
176
177 // Get answer if visible
178 const answer = question.querySelector('.question-answer');
179 if (answer) {
180 contentText += `\nAnswer: ${answer.textContent.trim()}\n`;
181 }
182
183 // Get explanation if visible
184 const explanation = question.querySelector('.question-explanation');
185 if (explanation) {
186 contentText += `Explanation: ${explanation.textContent.trim()}\n`;
187 }
188
189 contentText += '\n' + '-'.repeat(80) + '\n\n';
190 });
191 } else {
192 // If no questions found, get all text content
193 contentText += mainContent.innerText || mainContent.textContent;
194 }
195
196 // Add text to PDF with proper formatting
197 const pageWidth = pdf.internal.pageSize.getWidth();
198 const pageHeight = pdf.internal.pageSize.getHeight();
199 const margin = 40;
200 const maxWidth = pageWidth - (margin * 2);
201 const lineHeight = 15;
202 let yPosition = margin;
203
204 pdf.setFontSize(10);
205
206 const lines = pdf.splitTextToSize(contentText, maxWidth);
207
208 lines.forEach(line => {
209 if (yPosition > pageHeight - margin) {
210 pdf.addPage();
211 yPosition = margin;
212 }
213 pdf.text(line, margin, yPosition);
214 yPosition += lineHeight;
215 });
216
217 // Save the PDF
218 const fileName = pageTitle.replace(/[^a-z0-9]/gi, '_') + '.pdf';
219 pdf.save(fileName);
220
221 console.log('PDF download complete:', fileName);
222 alert('PDF downloaded successfully!');
223
224 } catch (error) {
225 console.error('PDF download error:', error);
226 alert('Error creating PDF. Falling back to text download.');
227 downloadPageAsText();
228 }
229 }
230
231 // Fallback function to download as text
232 function downloadPageAsText() {
233 console.log('Downloading as text...');
234
235 const mainContent = document.querySelector('.main-content') || document.body;
236 const content = mainContent.innerText || mainContent.textContent;
237
238 const pageTitle = document.title || 'Examveda_Content';
239 const fullContent = `${pageTitle}\n\nURL: ${window.location.href}\n\nDownloaded: ${new Date().toLocaleString()}\n\n${'='.repeat(80)}\n\n${content}`;
240
241 const blob = new Blob([fullContent], { type: 'text/plain;charset=utf-8' });
242 const url = URL.createObjectURL(blob);
243
244 const a = document.createElement('a');
245 a.href = url;
246 a.download = `${pageTitle.replace(/[^a-z0-9]/gi, '_')}.txt`;
247 document.body.appendChild(a);
248 a.click();
249 document.body.removeChild(a);
250 URL.revokeObjectURL(url);
251
252 console.log('Text download complete');
253 }
254
255 // Function to add download button at the end of content
256 function addDownloadButton() {
257 console.log('Adding download button...');
258
259 // Remove any existing download buttons
260 const existingButtons = document.querySelectorAll('.examveda-download-btn');
261 existingButtons.forEach(btn => btn.remove());
262
263 // Find the main content area
264 const mainContent = document.querySelector('.main-content .col-md-8');
265
266 if (!mainContent) {
267 console.log('Main content area not found');
268 return;
269 }
270
271 // Create download button
272 const downloadBtn = document.createElement('button');
273 downloadBtn.className = 'examveda-download-btn';
274 downloadBtn.textContent = '📥 Download This Page as PDF';
275 downloadBtn.type = 'button';
276
277 // Add click handler
278 downloadBtn.onclick = function(e) {
279 e.preventDefault();
280 e.stopPropagation();
281 downloadPageAsPDF();
282 };
283
284 // Insert button at the end of content
285 mainContent.appendChild(downloadBtn);
286 console.log('Download button added at the end of content');
287 }
288
289 // Debounce function to prevent excessive calls
290 function debounce(func, wait) {
291 let timeout;
292 return function executedFunction(...args) {
293 const later = () => {
294 clearTimeout(timeout);
295 func(...args);
296 };
297 clearTimeout(timeout);
298 timeout = setTimeout(later, wait);
299 };
300 }
301
302 // Main initialization function
303 function init() {
304 console.log('Initializing extension...');
305
306 // Remove ads immediately
307 removeAdElements();
308
309 // Add download button after a short delay to ensure content is loaded
310 setTimeout(() => {
311 addDownloadButton();
312 }, 1000);
313
314 // Set up observer for dynamic content with debouncing
315 const debouncedRemoveAds = debounce(removeAdElements, 500);
316 const debouncedAddButton = debounce(addDownloadButton, 1000);
317
318 const observer = new MutationObserver(function() {
319 debouncedRemoveAds();
320 debouncedAddButton();
321 });
322
323 observer.observe(document.body, {
324 childList: true,
325 subtree: true
326 });
327
328 // Periodically remove ads
329 setInterval(removeAdElements, 2000);
330
331 console.log('Extension initialized successfully!');
332 }
333
334 // Wait for page to load
335 if (document.readyState === 'loading') {
336 document.addEventListener('DOMContentLoaded', init);
337 } else {
338 init();
339 }
340
341})();