Blocks all ads, enables text copying, and adds PDF download with A4 formatting
Size
15.7 KB
Version
1.0.1
Created
Nov 18, 2025
Updated
25 days ago
1// ==UserScript==
2// @name Panacea Concept Ad Blocker & PDF Downloader
3// @description Blocks all ads, enables text copying, and adds PDF download with A4 formatting
4// @version 1.0.1
5// @match https://*.panaceaconcept.in/*
6// @grant GM.getValue
7// @grant GM.setValue
8// @require https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js
9// @require https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js
10// ==/UserScript==
11(function() {
12 'use strict';
13
14 console.log('Panacea Concept Ad Blocker & PDF Downloader - Extension loaded');
15
16 // Initialize the extension
17 function init() {
18 blockAllAds();
19 enableTextCopying();
20 addPDFDownloadButton();
21 observeDOMForNewAds();
22 console.log('Extension initialized successfully');
23 }
24
25 // Block all ads on the page
26 function blockAllAds() {
27 console.log('Blocking ads...');
28
29 // CSS to hide all ad-related elements
30 const adBlockingCSS = `
31 /* Google AdSense */
32 .adsbygoogle,
33 ins.adsbygoogle,
34 .google-auto-placed,
35 #aswift_1_host,
36 #aswift_2_host,
37 [id^="aswift_"],
38 [id^="google_ads_"],
39 .google-anno-skip,
40 .goog-rtopics,
41
42 /* Common ad containers */
43 .ad, .ads, .advertisement, .banner-ad,
44 [class*="ad-container"],
45 [class*="ad-banner"],
46 [class*="ad-wrapper"],
47 [class*="advertisement"],
48 [id*="ad-container"],
49 [id*="ad-banner"],
50 [id*="advertisement"],
51
52 /* Floating and popup ads */
53 [class*="floating-ad"],
54 [class*="popup-ad"],
55 [class*="overlay-ad"],
56 [style*="position: fixed"][style*="z-index"],
57
58 /* Ad scripts and iframes */
59 iframe[src*="doubleclick"],
60 iframe[src*="googlesyndication"],
61 iframe[src*="googleadservices"],
62 iframe[src*="ads"],
63
64 /* Additional ad patterns */
65 div[id*="google_ads"],
66 div[class*="google-ad"],
67 .sponsored,
68 [data-ad-slot],
69 [data-ad-format] {
70 display: none !important;
71 visibility: hidden !important;
72 opacity: 0 !important;
73 height: 0 !important;
74 width: 0 !important;
75 position: absolute !important;
76 left: -9999px !important;
77 }
78
79 /* Remove ad spacing */
80 body {
81 margin: 0 !important;
82 }
83 `;
84
85 TM_addStyle(adBlockingCSS);
86
87 // Remove ad elements from DOM
88 const adSelectors = [
89 '.adsbygoogle',
90 'ins.adsbygoogle',
91 '.google-auto-placed',
92 '[id^="aswift_"]',
93 '[id^="google_ads_"]',
94 '.google-anno-skip',
95 '.goog-rtopics',
96 'iframe[src*="doubleclick"]',
97 'iframe[src*="googlesyndication"]',
98 'iframe[src*="googleadservices"]',
99 '[data-ad-slot]',
100 '[data-ad-format]'
101 ];
102
103 adSelectors.forEach(selector => {
104 const elements = document.querySelectorAll(selector);
105 elements.forEach(el => {
106 console.log('Removing ad element:', selector);
107 el.remove();
108 });
109 });
110
111 console.log('Ads blocked successfully');
112 }
113
114 // Enable text copying on the website
115 function enableTextCopying() {
116 console.log('Enabling text copying...');
117
118 // CSS to enable text selection
119 const copyEnableCSS = `
120 * {
121 -webkit-user-select: text !important;
122 -moz-user-select: text !important;
123 -ms-user-select: text !important;
124 user-select: text !important;
125 }
126
127 body {
128 -webkit-touch-callout: default !important;
129 }
130 `;
131
132 TM_addStyle(copyEnableCSS);
133
134 // Remove copy protection event listeners
135 const events = ['copy', 'cut', 'paste', 'contextmenu', 'selectstart', 'mousedown', 'mouseup', 'mousemove', 'keydown', 'keypress', 'keyup'];
136
137 events.forEach(event => {
138 document.addEventListener(event, function(e) {
139 e.stopPropagation();
140 }, true);
141 });
142
143 // Remove oncopy, oncut, onpaste attributes
144 document.body.oncopy = null;
145 document.body.oncut = null;
146 document.body.onpaste = null;
147 document.body.onselectstart = null;
148 document.body.oncontextmenu = null;
149
150 console.log('Text copying enabled successfully');
151 }
152
153 // Add PDF download button
154 function addPDFDownloadButton() {
155 console.log('Adding PDF download button...');
156
157 // Create download button
158 const downloadButton = document.createElement('button');
159 downloadButton.id = 'pdf-download-btn';
160 downloadButton.innerHTML = '📄 Download PDF';
161 downloadButton.title = 'Download page as PDF (A4 format)';
162
163 // Style the button
164 const buttonCSS = `
165 #pdf-download-btn {
166 position: fixed;
167 bottom: 30px;
168 right: 30px;
169 z-index: 999999;
170 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
171 color: white;
172 border: none;
173 padding: 15px 25px;
174 font-size: 16px;
175 font-weight: bold;
176 border-radius: 50px;
177 cursor: pointer;
178 box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
179 transition: all 0.3s ease;
180 font-family: Arial, sans-serif;
181 }
182
183 #pdf-download-btn:hover {
184 transform: translateY(-3px);
185 box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4);
186 background: linear-gradient(135deg, #764ba2 0%, #667eea 100%);
187 }
188
189 #pdf-download-btn:active {
190 transform: translateY(-1px);
191 }
192
193 #pdf-loading-overlay {
194 position: fixed;
195 top: 0;
196 left: 0;
197 width: 100%;
198 height: 100%;
199 background: rgba(0, 0, 0, 0.8);
200 z-index: 9999999;
201 display: flex;
202 flex-direction: column;
203 justify-content: center;
204 align-items: center;
205 color: white;
206 font-family: Arial, sans-serif;
207 }
208
209 #pdf-loading-overlay .spinner {
210 border: 5px solid #f3f3f3;
211 border-top: 5px solid #667eea;
212 border-radius: 50%;
213 width: 60px;
214 height: 60px;
215 animation: spin 1s linear infinite;
216 margin-bottom: 20px;
217 }
218
219 @keyframes spin {
220 0% { transform: rotate(0deg); }
221 100% { transform: rotate(360deg); }
222 }
223
224 #pdf-loading-overlay .message {
225 font-size: 20px;
226 font-weight: bold;
227 margin-top: 10px;
228 }
229 `;
230
231 TM_addStyle(buttonCSS);
232
233 // Add click event to download PDF
234 downloadButton.addEventListener('click', async function() {
235 console.log('PDF download initiated');
236 await generatePDF();
237 });
238
239 // Add button to page
240 document.body.appendChild(downloadButton);
241 console.log('PDF download button added successfully');
242 }
243
244 // Generate and download PDF
245 async function generatePDF() {
246 try {
247 // Show loading overlay
248 const loadingOverlay = document.createElement('div');
249 loadingOverlay.id = 'pdf-loading-overlay';
250 loadingOverlay.innerHTML = `
251 <div class="spinner"></div>
252 <div class="message">Generating PDF...</div>
253 <div style="font-size: 14px; margin-top: 10px;">Please wait, this may take a moment</div>
254 `;
255 document.body.appendChild(loadingOverlay);
256
257 console.log('Starting PDF generation...');
258
259 // Get the main content area
260 const contentArea = document.querySelector('#primary.content-area') ||
261 document.querySelector('main') ||
262 document.querySelector('article') ||
263 document.body;
264
265 console.log('Content area selected:', contentArea);
266
267 // Temporarily hide ads and unwanted elements for PDF
268 const elementsToHide = document.querySelectorAll('.adsbygoogle, .google-auto-placed, #pdf-download-btn, header, nav, footer, .sidebar, [class*="ad-"], [id*="ad-"]');
269 elementsToHide.forEach(el => {
270 el.style.display = 'none';
271 });
272
273 // Create canvas from content
274 const canvas = await html2canvas(contentArea, {
275 scale: 2,
276 useCORS: true,
277 logging: false,
278 backgroundColor: '#ffffff',
279 windowWidth: 1200,
280 windowHeight: document.documentElement.scrollHeight
281 });
282
283 console.log('Canvas created successfully');
284
285 // Restore hidden elements
286 elementsToHide.forEach(el => {
287 el.style.display = '';
288 });
289
290 // A4 dimensions in mm
291 const a4Width = 210;
292 const a4Height = 297;
293
294 // Create PDF with A4 size
295 const { jsPDF } = window.jspdf;
296 const pdf = new jsPDF({
297 orientation: 'portrait',
298 unit: 'mm',
299 format: 'a4',
300 compress: true
301 });
302
303 // Calculate dimensions to fit A4
304 const imgWidth = a4Width - 20; // 10mm margin on each side
305 const imgHeight = (canvas.height * imgWidth) / canvas.width;
306
307 let heightLeft = imgHeight;
308 let position = 10; // Top margin
309
310 // Add image to PDF
311 const imgData = canvas.toDataURL('image/jpeg', 0.95);
312
313 // Add first page
314 pdf.addImage(imgData, 'JPEG', 10, position, imgWidth, imgHeight);
315 heightLeft -= (a4Height - 20);
316
317 // Add additional pages if content is longer than one page
318 while (heightLeft > 0) {
319 position = heightLeft - imgHeight + 10;
320 pdf.addPage();
321 pdf.addImage(imgData, 'JPEG', 10, position, imgWidth, imgHeight);
322 heightLeft -= (a4Height - 20);
323 }
324
325 // Generate filename from page title
326 const pageTitle = document.title.replace(/[^a-z0-9]/gi, '_').toLowerCase();
327 const filename = `${pageTitle}_${Date.now()}.pdf`;
328
329 // Save PDF
330 pdf.save(filename);
331
332 console.log('PDF generated and downloaded successfully:', filename);
333
334 // Remove loading overlay
335 loadingOverlay.remove();
336
337 // Show success message
338 showNotification('PDF downloaded successfully!', 'success');
339
340 } catch (error) {
341 console.error('Error generating PDF:', error);
342
343 // Remove loading overlay
344 const overlay = document.getElementById('pdf-loading-overlay');
345 if (overlay) overlay.remove();
346
347 showNotification('Error generating PDF. Please try again.', 'error');
348 }
349 }
350
351 // Show notification
352 function showNotification(message, type) {
353 const notification = document.createElement('div');
354 notification.style.cssText = `
355 position: fixed;
356 top: 20px;
357 right: 20px;
358 z-index: 99999999;
359 background: ${type === 'success' ? '#4CAF50' : '#f44336'};
360 color: white;
361 padding: 15px 25px;
362 border-radius: 5px;
363 font-family: Arial, sans-serif;
364 font-size: 16px;
365 box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
366 animation: slideIn 0.3s ease;
367 `;
368 notification.textContent = message;
369
370 const style = document.createElement('style');
371 style.textContent = `
372 @keyframes slideIn {
373 from {
374 transform: translateX(400px);
375 opacity: 0;
376 }
377 to {
378 transform: translateX(0);
379 opacity: 1;
380 }
381 }
382 `;
383 document.head.appendChild(style);
384
385 document.body.appendChild(notification);
386
387 setTimeout(() => {
388 notification.style.animation = 'slideIn 0.3s ease reverse';
389 setTimeout(() => notification.remove(), 300);
390 }, 3000);
391 }
392
393 // Observe DOM for dynamically loaded ads
394 function observeDOMForNewAds() {
395 console.log('Setting up MutationObserver for dynamic ads...');
396
397 const observer = new MutationObserver(debounce(function(mutations) {
398 mutations.forEach(function(mutation) {
399 mutation.addedNodes.forEach(function(node) {
400 if (node.nodeType === 1) { // Element node
401 // Check if the added node is an ad
402 if (node.classList && (
403 node.classList.contains('adsbygoogle') ||
404 node.classList.contains('google-auto-placed') ||
405 node.id && (node.id.includes('aswift_') || node.id.includes('google_ads_'))
406 )) {
407 console.log('Removing dynamically added ad:', node);
408 node.remove();
409 }
410
411 // Check child nodes for ads
412 const adElements = node.querySelectorAll('.adsbygoogle, .google-auto-placed, [id^="aswift_"], [id^="google_ads_"]');
413 adElements.forEach(ad => {
414 console.log('Removing dynamically added ad (child):', ad);
415 ad.remove();
416 });
417 }
418 });
419 });
420 }, 500));
421
422 observer.observe(document.body, {
423 childList: true,
424 subtree: true
425 });
426
427 console.log('MutationObserver set up successfully');
428 }
429
430 // Debounce function to limit observer callback frequency
431 function debounce(func, wait) {
432 let timeout;
433 return function executedFunction(...args) {
434 const later = () => {
435 clearTimeout(timeout);
436 func(...args);
437 };
438 clearTimeout(timeout);
439 timeout = setTimeout(later, wait);
440 };
441 }
442
443 // Wait for page to load and initialize
444 if (document.readyState === 'loading') {
445 document.addEventListener('DOMContentLoaded', init);
446 } else {
447 init();
448 }
449
450 // Also run after a short delay to catch late-loading ads
451 setTimeout(() => {
452 blockAllAds();
453 console.log('Secondary ad blocking pass completed');
454 }, 2000);
455
456})();