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