diff --git a/submissions/Catover/Catover/background.js b/submissions/Catover/Catover/background.js new file mode 100644 index 00000000..a6712662 --- /dev/null +++ b/submissions/Catover/Catover/background.js @@ -0,0 +1,178 @@ +const browser = chrome || browser; +let blockingEnabled = true; +let catReplacementEnabled = true; +let positivityBubbleEnabled = true; +let adsBlocked = 0; + +// Load saved settings +chrome.storage.local.get( + ['blockingEnabled', 'catReplacementEnabled', 'positivityBubbleEnabled', 'adsBlocked'], + function(result) { + blockingEnabled = result.blockingEnabled !== undefined ? result.blockingEnabled : true; + catReplacementEnabled = result.catReplacementEnabled !== undefined ? result.catReplacementEnabled : true; + positivityBubbleEnabled = result.positivityBubbleEnabled !== undefined ? result.positivityBubbleEnabled : true; + adsBlocked = result.adsBlocked || 0; + + // Only initialize rules if blocking is enabled + if (blockingEnabled) { + updateRules(); + } + } +); + +// Define rule for blocking ads +const adBlockingRule = { + id: 1, + priority: 1, + action: { type: "block" }, + condition: { + urlFilter: "||*", + domains: [ + "*://*.doubleclick.net/*", + "*://*.googlesyndication.com/*", + "*://*.googleadservices.com/*", + "*://*.adsafeprotected.com/*", + "*://*.adnxs.com/*", + "*://*.adsrvr.org/*", + "*://*.adform.net/*", + "*://*.advertising.com/*", + "*://*.adtech.de/*", + "*://*.exponential.com/*", + "*://*.media.net/*", + "*://*.zedo.com/*", + "*://*.yieldmanager.com/*", + "*://*.pubmatic.com/*", + "*://*.openx.net/*", + "*://*.rubiconproject.com/*", + "*://*.criteo.com/*", + "*://*.outbrain.com/*", + "*://*.taboola.com/*", + "*://*.revcontent.com/*", + "*://*.mgid.com/*", + "*://*.adblade.com/*", + "*://*.adroll.com/*", + "*://*.quantserve.com/*", + "*://*.scorecardresearch.com/*", + "*://*.bluekai.com/*", + "*://*.mathtag.com/*", + "*://*.tradedoubler.com/*", + "*://*.adbrite.com/*", + "*://*.admob.com/*", + "*://*.adcolony.com/*", + "*://*.adf.ly/*", + "*://*.adfly.com/*", + "*://*.adk2.com/*", + "*://*.admarketplace.net/*", + "*://*.adscale.de/*", + "*://*.adserverplus.com/*", + "*://*.adtechus.com/*", + "*://*.advertserve.com/*", + "*://*.adzerk.*/*" // adzerk regex + ], + resourceTypes: [ + "main_frame", + "sub_frame", + "stylesheet", + "script", + "image", + "font", + "object", + "xmlhttprequest", + "ping", + "csp_report", + "media", + "websocket", + "other" + ] + } +}; + +// Function to update blocking rules +async function updateRules() { + try { + if (blockingEnabled) { + await chrome.declarativeNetRequest.updateDynamicRules({ + removeRuleIds: [1], + addRules: [adBlockingRule] + }); + } else { + await chrome.declarativeNetRequest.updateDynamicRules({ + removeRuleIds: [1] + }); + } + } catch (error) { + console.error('Error updating rules:', error); + } +} + +// Handle messages from popup with conditional execution +chrome.runtime.onMessage.addListener( + function(request, sender, sendResponse) { + switch (request.action) { + case 'toggleBlocking': + blockingEnabled = request.enabled; + chrome.storage.local.set({ blockingEnabled }); + if (blockingEnabled) { + updateRules(); + } else { + chrome.declarativeNetRequest.updateDynamicRules({ removeRuleIds: [1] }); + } + sendResponse({ enabled: blockingEnabled }); + break; + + case 'toggleCatReplacement': + catReplacementEnabled = request.enabled; + chrome.storage.local.set({ catReplacementEnabled }); + // Only notify content script if enabled + if (catReplacementEnabled) { + chrome.tabs.query({active: true, currentWindow: true}, function(tabs) { + if (tabs[0]) { + chrome.tabs.sendMessage(tabs[0].id, { + action: 'enableCatReplacement' + }); + } + }); + } + sendResponse({ enabled: catReplacementEnabled }); + break; + + case 'togglePositivityBubble': + positivityBubbleEnabled = request.enabled; + chrome.storage.local.set({ positivityBubbleEnabled }); + // Only notify content script if enabled + if (positivityBubbleEnabled) { + chrome.tabs.query({active: true, currentWindow: true}, function(tabs) { + if (tabs[0]) { + chrome.tabs.sendMessage(tabs[0].id, { + action: 'enablePositivityBubble' + }); + } + }); + } + sendResponse({ enabled: positivityBubbleEnabled }); + break; + + case 'getStatus': + sendResponse({ + adsBlocked, + blockingEnabled, + catReplacementEnabled, + positivityBubbleEnabled + }); + break; + } + return true; + } +); + +// Track blocked requests using declarativeNetRequest.onRuleMatchedDebug +chrome.declarativeNetRequest.onRuleMatchedDebug?.addListener( + (info) => { + if (info.rule.id === 1) { + adsBlocked++; + chrome.storage.local.set({ adsBlocked: adsBlocked }); + } + } +); + +console.log('Background script loaded'); \ No newline at end of file diff --git a/submissions/Catover/Catover/content.js b/submissions/Catover/Catover/content.js new file mode 100644 index 00000000..f8e0f211 --- /dev/null +++ b/submissions/Catover/Catover/content.js @@ -0,0 +1,384 @@ +let catReplacementEnabled = true; +let positivityBubbleEnabled = true; +let observer; +let positivityBubbleTimer; + +const EXCLUDED_DOMAINS = [ + 'cataas.com', + 'api.cataas.com' +]; + +// Check if current site is in excluded domains +const isExcludedDomain = EXCLUDED_DOMAINS.some(domain => + window.location.hostname === domain || + window.location.hostname.endsWith('.' + domain) +); + +if (isExcludedDomain) { + console.log('CATAAS or related site detected, deactivating extension features.'); + // Set state variables + catReplacementEnabled = false; + positivityBubbleEnabled = false; + + // Clean up any existing observers and timers + if (typeof observer !== 'undefined') { + observer.disconnect(); + } + if (typeof positivityBubbleTimer !== 'undefined') { + clearTimeout(positivityBubbleTimer); + } + + // Early exit + console.log('Extension deactivated on excluded site'); + throw new Error('Extension disabled on excluded site'); +} + +const BORDER_COLOR = '#4b2e83'; // Purple border color +const BATCH_SIZE = 15; // Increased batch size +const MOTIVATIONAL_QUOTES = [ +"Keep going, you're closer than you think.", +"You've got this, just keep pushing!", +"Believe in yourself and all that you are.", +"Every step forward is a step closer.", +"Don't stop now, you're almost there.", +"The only limit is the one you set for yourself.", +"You’re stronger than you think.", +"The journey is tough, but so are you.", +"Stay focused, stay strong.", +"Success is just around the corner.", +"One day at a time, you’ll get there.", +"You have everything you need to succeed.", +"Don’t give up, you’re on the right path.", +"Push through the challenges, you're capable.", +"Your hard work will pay off.", +"Don’t wait for opportunity, create it.", +"Believe in the power of your dreams.", +"You can conquer anything you set your mind to.", +"The best is yet to come.", +"Trust the process and keep going.", +"Progress, not perfection.", +"Keep your head high and your spirits higher.", +"Keep fighting, you're stronger than you know.", +"You are capable of more than you think.", +"Every setback is a setup for a comeback.", +"Don’t stop until you're proud.", +"You've come so far, don't quit now.", +"Make today your masterpiece.", +"There’s no limit to what you can achieve.", +"Stay brave, stay bold, stay you.", +"Nothing is impossible unless you believe it is.", +"Dream big and work hard.", +"You’re on the path to greatness.", +"Keep your dreams alive, you’re almost there.", +"Your potential is endless.", +"Believe in the magic of your dreams.", +"Keep striving, you’re closer than you think.", +"You have the power to make it happen.", +"No challenge is too big for you.", +"You’re capable of overcoming anything.", +"Great things are coming your way.", +"Don’t doubt yourself, trust your journey.", +"Success starts with the belief that you can.", +"You have what it takes to succeed.", +"Today is the perfect day to begin.", +"Keep pushing, the finish line is in sight.", +"Rise up and keep going.", +"Your future is as bright as your determination.", +"Don’t just dream it, do it.", +"The sky’s the limit when you keep going!" +]; + +function generateUniqueSeed() { + return Math.random().toString(36).substring(2) + Date.now().toString(36); +} + +function isCatImage(img) { + return img.style.borderColor === BORDER_COLOR || + img.hasAttribute('data-cat-replaced') || + img.closest('[data-cat-circle]'); // Skip images in positivity circle +} + +function getRandomQuote() { + const randomIndex = Math.floor(Math.random() * MOTIVATIONAL_QUOTES.length); + return MOTIVATIONAL_QUOTES[randomIndex]; +} + +async function preloadCatImage(width, height) { + const uniqueSeed = generateUniqueSeed(); + const newSrc = `https://cataas.com/cat/cute?width=${width}&height=${height}&seed=${uniqueSeed}`; + + return new Promise((resolve, reject) => { + const timeout = setTimeout(() => reject('Timeout'), 3000); + const img = new Image(); + img.onload = () => { + clearTimeout(timeout); + resolve(newSrc); + }; + img.onerror = () => { + clearTimeout(timeout); + reject('Failed to load'); + }; + img.src = newSrc; + }); +} + +// Modified image replacement function +async function replaceImage(img) { + if (isCatImage(img) || img.hasAttribute('data-processing')) return; + + img.setAttribute('data-processing', 'true'); + + try { + const width = Math.max(img.naturalWidth || img.width || 300, 100); + const height = Math.max(img.naturalHeight || img.height || 300, 100); + const newSrc = await preloadCatImage(width, height); + + if (catReplacementEnabled && !isCatImage(img)) { + img.setAttribute('data-original-src', img.src); + img.src = newSrc; + img.setAttribute('data-cat-replaced', 'true'); + img.style.border = `3px solid ${BORDER_COLOR}`; + img.style.borderRadius = '8px'; + } + } catch (error) { + console.error('Failed to replace image:', error); + } finally { + img.removeAttribute('data-processing'); + } +} + +async function processBatch(images) { + const currentBatch = Array.from(images).slice(0, BATCH_SIZE); + + await Promise.all(currentBatch.map(replaceImage)); + + if (images.length > BATCH_SIZE) { + requestAnimationFrame(() => processBatch(images.slice(BATCH_SIZE))); + } +} + +// Initialize the mutation observer with error handling +function initializeObserver() { + try { + if (observer) { + observer.disconnect(); + } + + observer = new MutationObserver((mutations) => { + if (!catReplacementEnabled) return; + + const newImages = new Set(); + mutations.forEach(mutation => { + mutation.addedNodes.forEach(node => { + if (node.tagName === 'IMG' && !isCatImage(node)) { + newImages.add(node); + } else if (node.getElementsByTagName) { + const images = node.getElementsByTagName('img'); + Array.from(images) + .filter(img => !isCatImage(img)) + .forEach(img => newImages.add(img)); + } + }); + + if (mutation.type === 'attributes' && + mutation.attributeName === 'src' && + mutation.target.tagName === 'IMG' && + !isCatImage(mutation.target)) { + newImages.add(mutation.target); + } + }); + + if (newImages.size > 0) { + processBatch(Array.from(newImages)); + } + }); + + observer.observe(document.body, { + childList: true, + subtree: true, + attributes: true, + attributeFilter: ['src'] + }); + } catch (error) { + console.error('Failed to initialize observer:', error); + } +} + +function initializeImages() { + const images = document.getElementsByTagName('img'); + processBatch(Array.from(images)); +} + +// Initialize extension state +async function initializeExtension() { + try { + const result = await chrome.storage.local.get([ + 'catReplacementEnabled', + 'positivityBubbleEnabled' + ]); + + catReplacementEnabled = result.catReplacementEnabled ?? true; + positivityBubbleEnabled = result.positivityBubbleEnabled ?? true; + + if (catReplacementEnabled) { + initializeImages(); + } + if (positivityBubbleEnabled) { + schedulePositivityCircle(); + } + } catch (error) { + console.error('Failed to initialize extension:', error); + } +} + +// Update message listener to use async/await +chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { + const handleMessage = async () => { + switch (request.action) { + case 'toggleCatReplacement': + catReplacementEnabled = request.enabled; + if (catReplacementEnabled) { + await initializeImages(); + } else { + restoreOriginalImages(); + } + return { success: true }; + + case 'togglePositivityBubble': + positivityBubbleEnabled = request.enabled; + if (positivityBubbleEnabled) { + schedulePositivityCircle(); + } else { + clearTimeout(positivityBubbleTimer); + } + return { success: true }; + + case 'getStatus': + return { + catReplacementEnabled, + positivityBubbleEnabled + }; + } + }; + + // Handle async response + handleMessage().then(sendResponse); + return true; // Keep message channel open for async response +}); + +// Function to restore original images +function restoreOriginalImages() { + document.querySelectorAll('img[data-cat-replaced]').forEach(img => { + if (img.hasAttribute('data-original-src')) { + img.src = img.getAttribute('data-original-src'); + img.removeAttribute('data-cat-replaced'); + img.removeAttribute('data-original-src'); + img.style.border = ''; + img.style.borderRadius = ''; + } + }); +} + +// Load initial state +chrome.storage.local.get(['positivityBubbleEnabled'], function(result) { + positivityBubbleEnabled = result.positivityBubbleEnabled !== undefined ? + result.positivityBubbleEnabled : true; + if (positivityBubbleEnabled) { + schedulePositivityCircle(); + } +}); + +function showPositivityCircle() { + if (!positivityBubbleEnabled) return; + + const uniqueSeed = generateUniqueSeed(); + const quote = getRandomQuote().replace(/\s+/g, '%20'); + const quoteUrl = `https://cataas.com/cat/says/${quote}?width=1280&height=720&fontSize=32&seed=${uniqueSeed}`; // Added size parameter + + const circle = document.createElement('div'); + circle.style.position = 'fixed'; + circle.style.bottom = '20px'; + circle.style.right = '20px'; + circle.style.width = '120px'; + circle.style.height = '120px'; + circle.style.borderRadius = '50%'; + circle.style.backgroundColor = '#4b2e83'; + circle.style.display = 'flex'; + circle.style.justifyContent = 'center'; + circle.style.alignItems = 'center'; + circle.style.cursor = 'pointer'; + circle.style.zIndex = '999999'; + circle.style.boxShadow = '0 4px 8px rgba(0,0,0,0.3)'; + circle.style.transition = 'all 0.3s ease-in-out'; + circle.style.transform = 'scale(0)'; + circle.style.opacity = '0'; + circle.setAttribute('data-cat-circle', 'true'); // Prevent cat replacement + + const link = document.createElement('a'); + link.href = quoteUrl; + link.target = '_blank'; + link.style.color = 'white'; + link.style.textDecoration = 'none'; + link.style.fontSize = '14px'; // Smaller font size + link.style.textAlign = 'center'; + link.style.padding = '10px'; + link.innerText = '😺\nClick me!'; + link.style.whiteSpace = 'pre-line'; + link.style.lineHeight = '1.2'; // Better spacing for smaller text + + circle.appendChild(link); + document.body.appendChild(circle); + + // Animate in + requestAnimationFrame(() => { + circle.style.transform = 'scale(1)'; + circle.style.opacity = '1'; + }); + + // Hover effect + circle.addEventListener('mouseover', () => { + circle.style.transform = 'scale(1.1)'; + }); + + circle.addEventListener('mouseout', () => { + circle.style.transform = 'scale(1)'; + }); + + // Remove after delay with animation + setTimeout(() => { + circle.style.transform = 'scale(0)'; + circle.style.opacity = '0'; + setTimeout(() => { + if (document.body.contains(circle)) { + document.body.removeChild(circle); + } + }, 300); + }, 10000); +} + +function schedulePositivityCircle() { + // Random time between 10-20 minutes (in milliseconds) + const minTime = 10 * 60 * 1000 + const maxTime = 20 * 60 * 1000 + const randomTime = Math.floor(Math.random() * (maxTime - minTime + 1)) + minTime; + + positivityBubbleTimer = setTimeout(() => { + showPositivityCircle(); + schedulePositivityCircle(); + }, randomTime); +} + +// Start the circle scheduling +if (positivityBubbleEnabled) { + schedulePositivityCircle(); +} + +// Check for excluded domains first +if (!isExcludedDomain) { + initializeExtension(); + initializeObserver(); +} else { + console.log('Extension deactivated on excluded site'); +} + +console.log('Content script loaded'); \ No newline at end of file diff --git a/submissions/Catover/Catover/icons/logo_128x128.png b/submissions/Catover/Catover/icons/logo_128x128.png new file mode 100644 index 00000000..0a11b411 Binary files /dev/null and b/submissions/Catover/Catover/icons/logo_128x128.png differ diff --git a/submissions/Catover/Catover/icons/logo_16x16.png b/submissions/Catover/Catover/icons/logo_16x16.png new file mode 100644 index 00000000..b429fd73 Binary files /dev/null and b/submissions/Catover/Catover/icons/logo_16x16.png differ diff --git a/submissions/Catover/Catover/icons/logo_48x48.png b/submissions/Catover/Catover/icons/logo_48x48.png new file mode 100644 index 00000000..9df5f2c6 Binary files /dev/null and b/submissions/Catover/Catover/icons/logo_48x48.png differ diff --git a/submissions/Catover/Catover/manifest.json b/submissions/Catover/Catover/manifest.json new file mode 100644 index 00000000..285317b7 --- /dev/null +++ b/submissions/Catover/Catover/manifest.json @@ -0,0 +1,39 @@ +{ + "name": "Catover", + "version": "2.0", + "description": "Catover is a browser extension that replaces all images with cat images and blocks ads.", + "manifest_version": 3, + "permissions": [ + "storage", + "declarativeNetRequest", + "declarativeNetRequestFeedback", + "tabs", + "activeTab" + ], + "host_permissions": [ + "" + ], + "background": { + "service_worker": "background.js", + "scripts": ["background.js"] + }, + "icons": { + "16": "icons/logo_16x16.png", + "48": "icons/logo_48x48.png", + "128": "icons/logo_128x128.png" + }, + "content_scripts": [ + { + "matches": [""], + "exclude_matches": ["*://*.cataas.com/*"], + "js": ["content.js"], + "run_at": "document_end" + } + ], + "action": { + "default_popup": "popup.html", + "16": "icons/logo_16x16.png", + "48": "icons/logo_48x48.png", + "128": "icons/logo_128x128.png" + } +} \ No newline at end of file diff --git a/submissions/Catover/Catover/popup.html b/submissions/Catover/Catover/popup.html new file mode 100644 index 00000000..696e6d18 --- /dev/null +++ b/submissions/Catover/Catover/popup.html @@ -0,0 +1,95 @@ + + + + Catover + + + +

Catover

+

Ads Blocked: 0

+
+ + +
+
+ + +
+
+ + +
+ + + \ No newline at end of file diff --git a/submissions/Catover/Catover/popup.js b/submissions/Catover/Catover/popup.js new file mode 100644 index 00000000..db583d2b --- /dev/null +++ b/submissions/Catover/Catover/popup.js @@ -0,0 +1,92 @@ +document.addEventListener('DOMContentLoaded', async function() { + const adsBlockedElement = document.getElementById('adsBlocked'); + const toggleBlockingCheckbox = document.getElementById('toggleBlocking'); + const toggleCatReplacementCheckbox = document.getElementById('toggleCatReplacement'); + const togglePositivityBubbleCheckbox = document.getElementById('togglePositivityBubble'); + + // Check if we're on a CATAAS site + chrome.tabs.query({active: true, currentWindow: true}, function(tabs) { + const currentUrl = tabs[0].url; + if (currentUrl.includes('cataas.com')) { + // Disable all toggles and show message + toggleBlockingCheckbox.disabled = true; + toggleCatReplacementCheckbox.disabled = true; + togglePositivityBubbleCheckbox.disabled = true; + + // Add message to popup + const messageDiv = document.createElement('div'); + messageDiv.style.color = 'red'; + messageDiv.style.padding = '10px'; + messageDiv.style.textAlign = 'center'; + messageDiv.textContent = 'Extension disabled on CATAAS sites'; + document.body.insertBefore(messageDiv, document.body.firstChild); + + return; // Don't proceed with normal initialization + } + + // Normal initialization for non-CATAAS sites + (async function() { + try { + const settings = await chrome.storage.local.get([ + 'blockingEnabled', + 'catReplacementEnabled', + 'positivityBubbleEnabled', + 'adsBlocked' + ]); + + toggleBlockingCheckbox.checked = settings.blockingEnabled ?? true; + toggleCatReplacementCheckbox.checked = settings.catReplacementEnabled ?? true; + togglePositivityBubbleCheckbox.checked = settings.positivityBubbleEnabled ?? true; + adsBlockedElement.textContent = `Ads Blocked: ${settings.adsBlocked || 0}`; + } catch (error) { + console.error('Failed to load settings:', error); + } + })(); + + // Add event listeners for all toggles + toggleBlockingCheckbox.addEventListener('change', async function() { + try { + await Promise.all([ + chrome.storage.local.set({ blockingEnabled: this.checked }), + chrome.runtime.sendMessage({ + action: 'toggleBlocking', + enabled: this.checked + }) + ]); + } catch (error) { + console.error('Failed to save blocking setting:', error); + this.checked = !this.checked; // Revert on error + } + }); + + toggleCatReplacementCheckbox.addEventListener('change', async function() { + try { + await Promise.all([ + chrome.storage.local.set({ catReplacementEnabled: this.checked }), + chrome.runtime.sendMessage({ + action: 'toggleCatReplacement', + enabled: this.checked + }) + ]); + } catch (error) { + console.error('Failed to save cat replacement setting:', error); + this.checked = !this.checked; + } + }); + + togglePositivityBubbleCheckbox.addEventListener('change', async function() { + try { + await Promise.all([ + chrome.storage.local.set({ positivityBubbleEnabled: this.checked }), + chrome.runtime.sendMessage({ + action: 'togglePositivityBubble', + enabled: this.checked + }) + ]); + } catch (error) { + console.error('Failed to save positivity bubble setting:', error); + this.checked = !this.checked; + } + }); + }); +}); \ No newline at end of file diff --git a/submissions/Catover/Catover/rules.json b/submissions/Catover/Catover/rules.json new file mode 100644 index 00000000..ab13f35a --- /dev/null +++ b/submissions/Catover/Catover/rules.json @@ -0,0 +1,33 @@ +{ + "id": 1, + "priority": 1, + "action": { + "type": "block" + }, + "condition": { + "urlFilter": "||*", + "domains": [ + "doubleclick.net", + "googlesyndication.com", + "googleadservices.com", + "adsafeprotected.com", + "adnxs.com", + "adsrvr.org" + ], + "resourceTypes": [ + "main_frame", + "sub_frame", + "stylesheet", + "script", + "image", + "font", + "object", + "xmlhttprequest", + "ping", + "csp_report", + "media", + "websocket", + "other" + ] + } +} \ No newline at end of file diff --git a/submissions/Catover/README.md b/submissions/Catover/README.md new file mode 100644 index 00000000..445ef619 --- /dev/null +++ b/submissions/Catover/README.md @@ -0,0 +1,22 @@ +# Catover + +This Web Extension removes ads and replaces images with cat images so you stay away from all the boring and dangerous stuff. (Works for Both Firefox and Chromium based browsers) + +## Installation + +### Chrome +1. Copy this repository or download just the Catover folder. +2. Go to Extensions > Manage Extensions. +3. Enable Developer mode and then press the newly appeared "Load unpacked" button. +4. Choose the Catover folder. +5. You're good to go! + +### Firefox +1. Copy this repository or download just the AdBlock folder. +2. Search "about:debugging" in the search bar. +3. Go to "This Firefox". +4. Load Temporary Extension. +5. Choose the `manifest.json` file instead of the folder. +6. You're good to go! + +*Note:* This extension will be applied automatically to each website. To turn it off, you need to either disable or delete the extension.