Size
8.0 KB
Version
1.0.1
Created
Nov 10, 2025
Updated
about 1 month ago
1// ==UserScript==
2// @name Testbook Ad Blocker & PDF Downloader
3// @description A new extension
4// @version 1.0.1
5// @match https://*.testbook.com/*
6// @icon https://testbook.com/assets/img/brand/logo-32x32.png
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('Testbook Ad Blocker & PDF Downloader initialized');
13
14 // Function to remove ads, banners, and login/signup elements
15 function removeAdsAndPromotions() {
16 console.log('Removing ads, banners, and login/signup elements...');
17
18 // Remove super banner ads
19 const superBanners = document.querySelectorAll('.super-banner, a[href*="super-pass-live-coaching"]');
20 superBanners.forEach(banner => {
21 console.log('Removing super banner:', banner);
22 banner.remove();
23 });
24
25 // Remove signup buttons
26 const signupButtons = document.querySelectorAll('.signup-button, .signup-button-link, a[href*="login?tile=signup"]');
27 signupButtons.forEach(button => {
28 console.log('Removing signup button:', button);
29 button.remove();
30 });
31
32 // Remove sticky signup component
33 const stickySignup = document.querySelectorAll('.sticky-signup__content, .sticky-signup');
34 stickySignup.forEach(element => {
35 console.log('Removing sticky signup:', element);
36 element.remove();
37 });
38
39 // Remove signup banner
40 const signupBanner = document.querySelectorAll('.signup-banner');
41 signupBanner.forEach(element => {
42 console.log('Removing signup banner:', element);
43 element.remove();
44 });
45
46 // Remove "Get Started" buttons
47 const getStartedButtons = document.querySelectorAll('a.pure-button[href*="login"]');
48 getStartedButtons.forEach(button => {
49 if (button.textContent.includes('Get Started') || button.textContent.includes('Sign Up')) {
50 console.log('Removing get started button:', button);
51 button.remove();
52 }
53 });
54
55 // Remove download app buttons
56 const downloadAppButtons = document.querySelectorAll('.download-app-btn');
57 downloadAppButtons.forEach(button => {
58 console.log('Removing download app button:', button);
59 button.remove();
60 });
61
62 // Remove the fake "Download Solution PDF" buttons that require login
63 const fakeDownloadButtons = document.querySelectorAll('a.solpdf-download-link[href*="login"]');
64 fakeDownloadButtons.forEach(button => {
65 console.log('Removing fake download button:', button);
66 button.remove();
67 });
68
69 // Remove promotional containers
70 const promoContainers = document.querySelectorAll('.buttons-container a[href*="login"]');
71 promoContainers.forEach(container => {
72 console.log('Removing promo container:', container);
73 container.remove();
74 });
75
76 console.log('Ad removal complete');
77 }
78
79 // Function to add PDF download button
80 function addPDFDownloadButton() {
81 console.log('Adding PDF download button...');
82
83 // Find the question header area
84 const questionHeader = document.querySelector('.question__header');
85 if (!questionHeader) {
86 console.error('Question header not found');
87 return;
88 }
89
90 // Create the download button
91 const downloadButton = document.createElement('button');
92 downloadButton.id = 'custom-pdf-download-btn';
93 downloadButton.className = 'pure-button pure-button-error';
94 downloadButton.innerHTML = '📄 Download as PDF';
95 downloadButton.style.cssText = `
96 background-color: #d9534f;
97 color: white;
98 padding: 10px 20px;
99 border: none;
100 border-radius: 4px;
101 cursor: pointer;
102 font-size: 14px;
103 font-weight: bold;
104 transition: background-color 0.3s;
105 `;
106
107 // Add hover effect
108 downloadButton.addEventListener('mouseenter', () => {
109 downloadButton.style.backgroundColor = '#c9302c';
110 });
111 downloadButton.addEventListener('mouseleave', () => {
112 downloadButton.style.backgroundColor = '#d9534f';
113 });
114
115 // Add click handler
116 downloadButton.addEventListener('click', generatePDF);
117
118 // Append button to header
119 questionHeader.appendChild(downloadButton);
120 console.log('PDF download button added successfully');
121 }
122
123 // Function to generate PDF
124 async function generatePDF() {
125 console.log('Generating PDF...');
126
127 const button = document.getElementById('custom-pdf-download-btn');
128 const originalText = button.innerHTML;
129 button.innerHTML = '⏳ Generating PDF...';
130 button.disabled = true;
131
132 try {
133 // Get the main content container
134 const mainContainer = document.querySelector('.ques-container');
135 if (!mainContainer) {
136 throw new Error('Content container not found');
137 }
138
139 // Clone the content to avoid modifying the original
140 const contentClone = mainContainer.cloneNode(true);
141
142 // Remove any remaining login/signup elements from the clone
143 const unwantedElements = contentClone.querySelectorAll('a[href*="login"], .signup-button, .solpdf-download-link');
144 unwantedElements.forEach(el => el.remove());
145
146 // Configure PDF options
147 const opt = {
148 margin: [10, 10, 10, 10],
149 filename: 'testbook-question-answer.pdf',
150 image: { type: 'jpeg', quality: 0.98 },
151 html2canvas: {
152 scale: 2,
153 useCORS: true,
154 logging: false,
155 letterRendering: true
156 },
157 jsPDF: {
158 unit: 'mm',
159 format: 'a4',
160 orientation: 'portrait'
161 }
162 };
163
164 // Generate PDF
165 await html2pdf().set(opt).from(contentClone).save();
166
167 console.log('PDF generated successfully');
168 button.innerHTML = '✅ PDF Downloaded!';
169
170 // Reset button after 3 seconds
171 setTimeout(() => {
172 button.innerHTML = originalText;
173 button.disabled = false;
174 }, 3000);
175
176 } catch (error) {
177 console.error('Error generating PDF:', error);
178 button.innerHTML = '❌ Error - Try Again';
179 button.disabled = false;
180
181 setTimeout(() => {
182 button.innerHTML = originalText;
183 }, 3000);
184 }
185 }
186
187 // Debounce function for MutationObserver
188 function debounce(func, wait) {
189 let timeout;
190 return function executedFunction(...args) {
191 const later = () => {
192 clearTimeout(timeout);
193 func(...args);
194 };
195 clearTimeout(timeout);
196 timeout = setTimeout(later, wait);
197 };
198 }
199
200 // Initialize the extension
201 function init() {
202 console.log('Initializing Testbook Ad Blocker & PDF Downloader...');
203
204 // Wait for the page to be fully loaded
205 if (document.readyState === 'loading') {
206 document.addEventListener('DOMContentLoaded', init);
207 return;
208 }
209
210 // Remove ads and add PDF button
211 removeAdsAndPromotions();
212 addPDFDownloadButton();
213
214 // Observe DOM changes to remove dynamically loaded ads
215 const debouncedRemoveAds = debounce(removeAdsAndPromotions, 500);
216 const observer = new MutationObserver(debouncedRemoveAds);
217
218 observer.observe(document.body, {
219 childList: true,
220 subtree: true
221 });
222
223 console.log('Initialization complete');
224 }
225
226 // Start the extension
227 init();
228
229})();