diff --git a/submissions/Toonify/README.md b/submissions/Toonify/README.md new file mode 100644 index 00000000..8491ce1e --- /dev/null +++ b/submissions/Toonify/README.md @@ -0,0 +1,77 @@ +# Toonify + +Transform any boring webpage into a comic book adventure with animations, games, and fun interactive elements! + +
+ Cartoonify Banner +
+ + +## Description + +Cartoonify is a Chrome extension that adds comic book styling and fun interactive elements to any webpage. Turn your mundane web browsing into an exciting, cartoon-like experience complete with custom fonts, sound effects, animations, and mini-games. + +## Demo + +https://github.com/user-attachments/assets/b271ff2e-835b-4d6d-90ae-abfb96835ae9 + +## Features + +- Changes fonts, colors, and adds cartoon-like effects to webpages +- Comic book style "POW!", "BOOM!", "ZAP!" sounds with every click +- A friendly turtle that moves across the bottom of your screen +- Random comic-style speech bubbles appear on headings and links +- Play a mini Pong game in the corner of your browser +- Cycle through rainbow colors on the entire page +- Invert all colors on the page for a negative effect +- A cute cat that follows your cursor around the page +- Get a humorous "roast" about the page you're viewing +- Get a nerdy, technical summary of the current page content + +## Installation + +1. Download or clone this repository +2. Open Chrome and navigate to `chrome://extensions` +3. Enable "Developer mode" in the top-right corner +4. Click "Load unpacked" and select the Cartoonify directory +5. The Cartoonify extension icon should now appear in your Chrome toolbar + +## Usage + +1. Click the Cartoonify extension icon in your toolbar to activate +2. Click it again to deactivate +3. Access settings by right-clicking the extension icon and selecting "Options" + +### Interactive Features: +- **Pong Game**: Use the Up/Down arrow keys and Enter to play +- **Pig Roaster**: Hover over the pig icon on the right side and click for a roast +- **Nerd Summarizer**: Hover over the nerd icon on the left side and click for a summary +- **Cat Cursor**: Watch the cat follow your cursor movements + +## Configuration + +You can customize various features through the extension's options page: + +- **Main Color**: Choose your preferred accent color +- **Enable/Disable Features**: Toggle individual features on or off: + - Sound Effects + - Speech Bubbles + - Moving Turtle + - Pong Game + - Color Inversion + - Pig Roaster + - Nerd Summarizer + - RGB Mode + - Cat Cursor + +## Requirements + +- Google Chrome browser (version 88 or higher recommended) +- An appreciation for fun and silliness 😄 + + +## Credits + +- Sound effects from various royalty-free sources +- Font: Bangers by Vernon Adams (Google Fonts) +- API: Google Gemini for text generation diff --git a/submissions/Toonify/background.js b/submissions/Toonify/background.js new file mode 100644 index 00000000..e69de29b diff --git a/submissions/Toonify/content.js b/submissions/Toonify/content.js new file mode 100644 index 00000000..1e9eb421 --- /dev/null +++ b/submissions/Toonify/content.js @@ -0,0 +1,1293 @@ +var myInteger = 0; +let styleEl = null; +let turtleDiv = null; +let pongGame = null; +let speechBubbles = []; +let mainColor = '#ef5350'; +let enableSoundEffects = true; +let enableSpeechBubbles = true; +let enableTurtle = true; +let enablePong = true; +let giantImageElement = null; +let enableinvert = true; +let enablePigRoaster = true; +let enableNerdSummarizer = true; +let pigElement = null; +let roastBubble = null; +let nerdElement = null; +let summaryBubble = null; +let enableRGB = true; +let j = 0; +// Add new variables for cat and cursor tracking +let catElement = null; +let lastCursorX = 0; +let lastCursorY = 0; +let isCatFacingRight = false; +let cursorStyleElement = null; +let enableCat = true; + +const audio = { + "chill": new Audio(chrome.runtime.getURL("media/Chill.mp3")), + "kachow": new Audio(chrome.runtime.getURL("media/kachow.mp3")), + "boing": new Audio(chrome.runtime.getURL("media/Boing.mp3")), + "pow": new Audio(chrome.runtime.getURL("media/Pow.mp3")), + "boom": new Audio(chrome.runtime.getURL("media/Boom.mp3")), + "zap": new Audio(chrome.runtime.getURL("media/Zap.mp3")) +} + +audio.chill.preload = "auto"; +audio.kachow.preload = "auto"; +audio.boing.preload = "auto"; +audio.pow.preload = "auto"; +audio.boom.preload = "auto"; +audio.zap.preload = "auto"; + +audio.chill.volume = 0.4; +audio.kachow.volume = 0.8; +audio.boing.volume = 0.1; +audio.pow.volume = 0.8; +audio.boom.volume = 0.8; +audio.zap.volume = 0.2; + + +audio.chill.loop = true; +audio.kachow.loop = false; +audio.boing.loop = true; +audio.pow.loop = false; +audio.boom.loop = false; +audio.zap.loop = false; + +audio.chill.currentTime = 0; +audio.kachow.currentTime = 0; +audio.boing.currentTime = 0.8; +audio.pow.currentTime = 0; +audio.boom.currentTime = 0; +audio.zap.currentTime = 0; + +audio.chill.load(); +audio.kachow.load(); +audio.boing.load(); +audio.pow.load(); +audio.boom.load(); +audio.zap.load(); + + +// Add this helper function to pause all audio elements +function pauseAllAudio() { + Object.values(audio).forEach(audioElement => { + audioElement.pause(); + if (audioElement.loop) { + audioElement.currentTime = 0; + } + }); +} + +chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { + if (message.action === "applyStyles") { + audio.chill.play(); + + mainColor = message.prefs.mainColor || mainColor; + + const wasSoundEnabled = enableSoundEffects; + enableSoundEffects = message.prefs.enableSoundEffects; + + // If sound is being disabled, pause all audio + if (wasSoundEnabled && !enableSoundEffects) { + pauseAllAudio(); + } + + // Store previous state of all features + const hasAnyFeatureEnabled = + message.prefs.enableSoundEffects || + message.prefs.enableSpeechBubbles || + message.prefs.enableTurtle || + message.prefs.enablePong || + message.prefs.enableinvert || + (message.prefs.enablePigRoaster !== undefined ? message.prefs.enablePigRoaster : true) || + (message.prefs.enableNerdSummarizer !== undefined ? message.prefs.enableNerdSummarizer : true) || + message.prefs.enableRGB; + + enableSoundEffects = message.prefs.enableSoundEffects; + enableSpeechBubbles = message.prefs.enableSpeechBubbles; + enableTurtle = message.prefs.enableTurtle; + enablePong = message.prefs.enablePong; + enableinvert = message.prefs.enableinvert; + enablePigRoaster = message.prefs.enablePigRoaster !== undefined ? message.prefs.enablePigRoaster : true; + enableNerdSummarizer = message.prefs.enableNerdSummarizer !== undefined ? message.prefs.enableNerdSummarizer : true; + enableRGB = message.prefs.enableRGB; + enableCat = message.prefs.enableCat !== undefined ? message.prefs.enableCat : true; + + // Toggle logic - flip the state each time a toggle request comes in + if (message.toggle === undefined) { + // If not explicitly requested, toggle based on current state + myInteger = (myInteger === 0) ? 1 : 0; + } else { + // If explicitly requested, set to the requested state + myInteger = message.toggle ? 1 : 0; + } + + const shouldApplyStyles = (myInteger === 1); + + if (shouldApplyStyles) { + // Even if no features are enabled, we should still create the basic style + // to show that the extension is active + styleEl = applyStyles(); + showGiantImage(); + + // Only apply specific features if they're enabled + if (enableSoundEffects) audio.chill.play(); + if (enableTurtle) applyStyles2(); + if (enablePong) applyStyles3(); + if (enableSpeechBubbles) addSpeechBubbles(); + if (enableinvert) applyInvert(); + if (enablePigRoaster) addPigRoaster(); + if (enableNerdSummarizer) addNerdSummarizer(); + if (enableRGB) applyRGB(); + + // Add cat chase functionality + if (enableCat) addCatChase(); + + sendResponse({status: "Cartoonify activated! POW!", active: true}); + } else { + unapplyStyles(); + sendResponse({status: "Back to reality! ZOOM!", active: false}); + } + } + return true; +}); + +function applyStyles() { + styleEl = document.createElement('style'); + + const doodlesURL = chrome.runtime.getURL('media/doodles.jpg'); + const cursorURL = chrome.runtime.getURL('media/mousecursor.png'); + const clickerURL = chrome.runtime.getURL('media/mouseclicker.png'); + + styleEl.textContent = ` + * { + font-family: 'Bangers', 'Comic Sans MS', cursive, sans-serif !important; + color: ${mainColor}; + font-weight: bold; + cursor: url('${cursorURL}') 16 16, auto !important; + } + + a, button, [role="button"], input[type="submit"], input[type="button"], input[type="reset"], + [onclick], [onmousedown], select, details, summary, [aria-haspopup="true"], + [class*="btn"], [class*="button"], [id*="btn"], [id*="button"] { + cursor: url('${clickerURL}'), pointer !important; + } + + body { + background: white !important; + background-image: url('${doodlesURL}') !important; + background-repeat: repeat; + background-size: auto; + } + + img { + filter: contrast(1000%) brightness(100%); + } + + /* Make content containers semi-transparent to show background */ + p, div, span, section, article, main, header, footer, aside, nav { + background-color: rgba(255, 255, 255, 0.2) !important; + } + + /* Specific elements with fully transparent backgrounds */ + #moving-turtle, #moving-turtle *, .speech-bubble, #pong-container, #pong-canvas { + background: transparent !important; + } + + *[style*="border"], *[class*="border"], *[id*="border"], + *[border], *[style*="outline"], table, td, th { + border-width: 5px !important; + border-style: solid !important; + border-color: ${mainColor} !important; + } + + @keyframes wobble { + 0% { transform: rotate(2deg); } + 50% { transform: rotate(-2deg); } + 100% { transform: rotate(2deg); } + } + + h1, h2, h3 { + display: inline-block; + animation: wobble 0.2s infinite alternate ease-in-out; + background-color: rgba(255, 255, 255, 0.3) !important; + padding: 5px !important; + } + + body::before { + content: ''; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + pointer-events: none; + z-index: -1; + background-repeat: repeat; + background-size: auto; + opacity: 0.5; + } + + /* Comic book style action words */ + .comic-effect { + position: fixed; + z-index: 9998; + font-size: 40px; + color: ${mainColor}; + text-shadow: 2px 2px 0 black; + pointer-events: none; + user-select: none; + transform: rotate(-10deg) scale(0); + animation: popIn 0.5s forwards; + } + + @keyframes popIn { + 0% { transform: rotate(-10deg) scale(0); } + 70% { transform: rotate(-10deg) scale(1.2); } + 100% { transform: rotate(-10deg) scale(1); } + } + + /* Speech bubble style */ + .speech-bubble { + position: absolute; + background: white !important; + border-radius: 15px !important; + padding: 10px !important; + box-shadow: 3px 3px 0 rgba(0,0,0,0.2) !important; + border: 3px solid ${mainColor} !important; + max-width: 200px; + z-index: 9997; + pointer-events: none; + } + + .speech-bubble:before { + content: ''; + position: absolute; + bottom: -20px; + left: 20px; + border: 10px solid transparent; + border-top-color: ${mainColor}; + border-bottom: 0; + } + + /* Hover effects for images and links */ + img:hover { + transform: scale(1.5); + transition: transform 0.3s ease; + z-index: 9996; + position: relative; + } + + a:hover { + transform: scale(1.5); + display: inline-block; + transition: transform 0.2s ease; + z-index: 9996; + position: relative; + } + `; + + document.head.appendChild(styleEl); + + try { + const fontLink = document.createElement('link'); + fontLink.href = 'https://fonts.googleapis.com/css2?family=Bangers&display=swap'; + fontLink.rel = 'stylesheet'; + fontLink.onerror = () => { + console.log('Google Fonts blocked by CSP'); + }; + document.head.appendChild(fontLink); + } catch (error) { + console.log('Could not load Google Fonts'); + } + + + document.addEventListener('click', function(e) { + if (Math.random() > 0.5) { + addComicEffect(e.clientX, e.clientY); + } + }); + + // Note: Removed the addCatChase() call from here since it will be conditionally called in the message listener +} + +// Function to add the cat that follows the cursor +function addCatChase() { + // Remove any existing cat + if (catElement && catElement.parentNode) { + catElement.parentNode.removeChild(catElement); + } + + catElement = document.createElement('div'); + catElement.id = 'cursor-cat'; + + const catImg = document.createElement('img'); + catImg.src = chrome.runtime.getURL('media/cat.png'); + catImg.style.width = '80px'; // Resize cat + catImg.style.height = 'auto'; + catElement.appendChild(catImg); + + catElement.style.cssText = ` + position: fixed; + z-index: 9999; + pointer-events: none; + transform: scaleX(1); + transition: left 0.3s ease-out, top 0.3s ease-out; + background: transparent !important; + will-change: transform; + `; + + catElement.style.left = '-100px'; + catElement.style.top = '-100px'; + + document.body.appendChild(catElement); + + document.addEventListener('mousemove', updateCatPosition); +} + +function updateCatPosition(e) { + if (!catElement) return; + + const cursorX = e.clientX; + const cursorY = e.clientY; + + if (cursorX > lastCursorX && !isCatFacingRight) { + catElement.style.transform = 'scaleX(-1)'; + isCatFacingRight = true; + } else if (cursorX < lastCursorX && isCatFacingRight) { + catElement.style.transform = 'scaleX(1)'; + isCatFacingRight = false; + } + + const catX = cursorX - (isCatFacingRight ? -20 : 80); // Position cat to left or right of cursor + const catY = cursorY - 30; // Position cat slightly above cursor + + setTimeout(() => { + if (catElement) { + catElement.style.left = `${catX}px`; + catElement.style.top = `${catY}px`; + } + }, 100); + + lastCursorX = cursorX; + lastCursorY = cursorY; +} + +function addComicEffect(x, y) { + const effects = ['POW!', 'KACHOW!', 'BOOM!', 'ZAP!']; + const effect = effects[Math.floor(Math.random() * effects.length)]; + + if (enableSoundEffects) { + if (effect === 'KACHOW!'){ + audio.kachow.play(); + } else if (effect === "POW!"){ + audio.pow.play(); + } else if (effect === "BOOM!"){ + audio.boom.play(); + } else if (effect === "ZAP!"){ + audio.zap.play(); + } + } + + const comicEffect = document.createElement('div'); + comicEffect.className = 'comic-effect'; + comicEffect.textContent = effect; + comicEffect.style.left = `${x - 30}px`; + comicEffect.style.top = `${y - 30}px`; + + document.body.appendChild(comicEffect); + + setTimeout(() => { + comicEffect.parentNode.removeChild(comicEffect); + }, 980); +} + +function addSpeechBubbles() { + + speechBubbles.forEach(bubble => { + if (bubble && bubble.parentNode) { + bubble.parentNode.removeChild(bubble); + } + }); + speechBubbles = []; + + + const headlines = document.querySelectorAll('h1, h2, h3'); + const interactiveElements = document.querySelectorAll('a, button'); + + + const randomHeadlines = Array.from(headlines).slice(0, Math.min(3, headlines.length)); + const randomInteractiveElements = Array.from(interactiveElements) + .sort(() => 0.5 - Math.random()) + .slice(0, Math.min(5, interactiveElements.length)); + + const elements = [...randomHeadlines, ...randomInteractiveElements]; + + + const phrases = [ + 'Click me!', + 'Comic power!', + 'Wow!', + 'Amazing!', + 'Look at this!', + 'Incredible!', + 'Super!', + 'Fantastic!', + 'Holy moly!' + ]; + + elements.forEach(element => { + if (element.getBoundingClientRect().width > 0 && isElementVisible(element)) { + const bubble = document.createElement('div'); + bubble.className = 'speech-bubble'; + bubble.textContent = phrases[Math.floor(Math.random() * phrases.length)]; + + const rect = element.getBoundingClientRect(); + bubble.style.left = `${rect.left + window.scrollX}px`; + bubble.style.top = `${rect.top + window.scrollY - 50}px`; + + document.body.appendChild(bubble); + speechBubbles.push(bubble); + } + }); +} + +function isElementVisible(element) { + const rect = element.getBoundingClientRect(); + return rect.top >= 0 && rect.left >= 0 && + rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && + rect.right <= (window.innerWidth || document.documentElement.clientWidth); +} + +function applyRGB() { + rgbInterval = setInterval(rotate, 100); + j = 0; + function rotate() { + j += 10; + document.body.style.filter = `hue-rotate(${j}deg)`; + } +} + +function applyStyles2() { + turtleDiv = document.createElement('div'); + turtleDiv.id = 'moving-turtle'; + turtleDiv.style.cssText = 'position: sticky; bottom: -6px; z-index: 9999; pointer-events: none; background: transparent !important;'; + + const turtleImg = document.createElement('img'); + turtleImg.src = chrome.runtime.getURL('media/turtle.gif'); + turtleImg.style.height = '200px'; + turtleImg.style.background = 'transparent'; + turtleDiv.appendChild(turtleImg); + + document.body.appendChild(turtleDiv); + const turtleStyleEl = document.createElement('style'); + + // Only play audio if sound effects are enabled + if (enableSoundEffects) { + audio.boing.play(); + } + + turtleStyleEl.textContent = ` + @keyframes moveLeftToRight { + from { right: -100px; } + to { right: calc(100% + 100px); } + } + + #moving-turtle { + animation: moveLeftToRight 15s linear infinite reverse; + } + `; + + document.head.appendChild(turtleStyleEl); + + if (!styleEl) styleEl = turtleStyleEl; +} + +function unapplyStyles(){ + const styles = document.querySelectorAll('style'); + + // Always pause audio when unapplying styles + pauseAllAudio(); + + styles.forEach(style => { + if (style.textContent.includes('Bangers') || + style.textContent.includes('moveLeftToRight') || + style.textContent.includes('pong-container') || + style.textContent.includes('filter') || + style.textContent.includes('cursor')) { + style.parentNode.removeChild(style); + } + }); + if (styleEl && styleEl.parentNode) { + styleEl.parentNode.removeChild(styleEl); + styleEl = null; + } + if (turtleDiv && turtleDiv.parentNode) { + turtleDiv.parentNode.removeChild(turtleDiv); + turtleDiv = null; + } + + if (rgbInterval) { + clearInterval(rgbInterval); + document.body.style.filter = ''; // Reset filter properly + } + + if (pongGame && pongGame.parentNode) { + pongGame.parentNode.removeChild(pongGame); + pongGame = null; + } + if (giantImageElement && giantImageElement.parentNode) { + giantImageElement.parentNode.removeChild(giantImageElement); + giantImageElement = null; + } + + removePigRoaster(); + removeNerdSummarizer(); + + speechBubbles.forEach(bubble => { + if (bubble && bubble.parentNode) { + bubble.parentNode.removeChild(bubble); + } + }); + speechBubbles = []; + + + const comicEffects = document.querySelectorAll('.comic-effect'); + comicEffects.forEach(effect => { + effect.parentNode.removeChild(effect); + }); + + styleEl = null; + + // Remove cat element + if (catElement && catElement.parentNode) { + catElement.parentNode.removeChild(catElement); + catElement = null; + } + + // Remove cursor tracking event listener + document.removeEventListener('mousemove', updateCatPosition); +} + +function applyStyles3() { + addPongGame(); + + const styleEl = document.createElement('style'); + + document.head.appendChild(styleEl); +} + +function showGiantImage() { + if (giantImageElement && giantImageElement.parentNode) { + giantImageElement.parentNode.removeChild(giantImageElement); + } + + giantImageElement = document.createElement('div'); + giantImageElement.id = 'giant-image'; + + const imageUrl = chrome.runtime.getURL('media/chillguy.png'); + + giantImageElement.style.cssText = ` + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-image: url('${imageUrl}'); + background-size: contain; + background-position: center; + background-repeat: no-repeat; + z-index: 9990; + opacity: 0; + pointer-events: none; + animation: fadeInOut 10s linear forwards; + background-color: transparent !important; +`; + +const fadeStyle = document.createElement('style'); +fadeStyle.textContent = ` + @keyframes fadeInOut { + 0% { opacity: 0; } + 25% { opacity: 0.5; } /* Slower fade-in */ + 75% { opacity: 0.5; } /* Hold visibility */ + 100% { opacity: 0; } /* Smooth fade-out */ + } +`; + + document.head.appendChild(fadeStyle); + document.body.appendChild(giantImageElement); + + // Remove the element after animation completes + setTimeout(() => { + if (giantImageElement && giantImageElement.parentNode) { + giantImageElement.parentNode.removeChild(giantImageElement); + giantImageElement = null; + } + }, 14100); // Slightly longer than 5s to ensure animation completes +} + +function applyInvert() { + const styleEl = document.createElement('style'); + + styleEl.textContent = ` + /* Additional styles can be added here if needed */ + html{ + filter: invert(1); + } + `; + document.head.appendChild(styleEl); +} + +function addPongGame() { + pongGame = document.createElement('div'); + pongGame.id = 'pong-container'; + pongGame.style.cssText = ` + position: fixed; + top: 20px; + right: 20px; + width: 320px; + height: 240px; + background: black; + border: 3px solid ${mainColor}; + z-index: 10000; + overflow: hidden; + border-radius: 5px; + box-shadow: 0 0 10px rgba(0,0,0,0.5); + `; + + const canvas = document.createElement('canvas'); + canvas.id = 'pong-canvas'; + canvas.width = 320; + canvas.height = 240; + canvas.style.cssText = ` + display: block; + background: black; + `; + + pongGame.appendChild(canvas); + document.body.appendChild(pongGame); + + const ctx = canvas.getContext('2d'); + let gameState = 'start'; + let score = { player: 0, computer: 0 }; + + const ball = { + x: canvas.width / 2, + y: canvas.height / 2, + radius: 5, + velocityX: 5, + velocityY: 5, + speed: 3, + color: "white" + }; + + const paddleHeight = 50; + const paddleWidth = 10; + + const player = { + x: canvas.width - paddleWidth - 10, + y: (canvas.height - paddleHeight) / 2, + width: paddleWidth, + height: paddleHeight, + score: 0, + color: mainColor, + speed: 8 + }; + + const computer = { + x: 10, + y: (canvas.height - paddleHeight) / 2, + width: paddleWidth, + height: paddleHeight, + score: 0, + color: mainColor, + speed: 5 + }; + + let upArrowPressed = false; + let downArrowPressed = false; + let enterPressed = false; + + document.addEventListener('keydown', keyDownHandler); + document.addEventListener('keyup', keyUpHandler); + + function keyDownHandler(e) { + if (e.key === 'ArrowUp' || e.key === 'Up') { + upArrowPressed = true; + } + if (e.key === 'ArrowDown' || e.key === 'Down') { + downArrowPressed = true; + } + if (e.key === 'Enter') { + enterPressed = true; + if (gameState === 'start' || gameState === 'gameOver') { + resetGame(); + gameState = 'playing'; + } + } + } + + function keyUpHandler(e) { + if (e.key === 'ArrowUp' || e.key === 'Up') { + upArrowPressed = false; + } + if (e.key === 'ArrowDown' || e.key === 'Down') { + downArrowPressed = false; + } + if (e.key === 'Enter') { + enterPressed = false; + } + } + + function resetGame() { + ball.x = canvas.width / 2; + ball.y = canvas.height / 2; + ball.velocityX = -ball.velocityX; + ball.speed = 7; + score.player = 0; + score.computer = 0; + } + + function resetBall() { + ball.x = canvas.width / 2; + ball.y = canvas.height / 2; + ball.velocityX = -ball.velocityX; + ball.speed = 7; + } + + function drawRect(x, y, w, h, color) { + ctx.fillStyle = color; + ctx.fillRect(x, y, w, h); + } + + function drawCircle(x, y, r, color) { + ctx.fillStyle = color; + ctx.beginPath(); + ctx.arc(x, y, r, 0, Math.PI * 2, false); + ctx.closePath(); + ctx.fill(); + } + + function drawText(text, x, y, color, fontSize) { + ctx.fillStyle = color; + ctx.font = `${fontSize}px Bangers`; + ctx.textAlign = 'center'; + ctx.fillText(text, x, y); + } + + function drawNet() { + for (let i = 0; i <= canvas.height; i += 15) { + drawRect(canvas.width / 2 - 1, i, 2, 10, "white"); + } + } + + function collision(b, p) { + + b.top = b.y - b.radius; + b.bottom = b.y + b.radius; + b.left = b.x - b.radius; + b.right = b.x + b.radius; + + p.top = p.y; + p.bottom = p.y + p.height; + p.left = p.x; + p.right = p.x + p.width; + + return p.left < b.right && p.top < b.bottom && p.right > b.left && p.bottom > b.top; + } + + function update() { + if (upArrowPressed && player.y > 0) { + player.y -= player.speed; + } else if (downArrowPressed && player.y < canvas.height - player.height) { + player.y += player.speed; + } + + let computerCenter = computer.y + computer.height / 2; + let ballCenter = ball.y; + + if (computerCenter < ballCenter - 35) { + computer.y += computer.speed; + } else if (computerCenter > ballCenter + 35) { + computer.y -= computer.speed; + } + + ball.x += ball.velocityX; + ball.y += ball.velocityY; + + if (ball.y - ball.radius < 0 || ball.y + ball.radius > canvas.height) { + ball.velocityY = -ball.velocityY; + } + + let paddle = (ball.x + ball.radius < canvas.width / 2) ? computer : player; + + if (collision(ball, paddle)) { + let collidePoint = (ball.y - (paddle.y + paddle.height / 2)); + collidePoint = collidePoint / (paddle.height / 2); + + let angleRad = (Math.PI / 4) * collidePoint; + + let direction = (ball.x + ball.radius < canvas.width / 2) ? 1 : -1; + + ball.velocityX = direction * ball.speed * Math.cos(angleRad); + ball.velocityY = ball.speed * Math.sin(angleRad); + + ball.speed += 0.1; + + + addComicEffect(paddle.x, ball.y); + } + + if (ball.x - ball.radius < 0) { + score.player++; + resetBall(); + if (score.player >= 5) { + gameState = 'gameOver'; + } + } else if (ball.x + ball.radius > canvas.width) { + score.computer++; + resetBall(); + if (score.computer >= 5) { + gameState = 'gameOver'; + } + } + } + + function render() { + drawRect(0, 0, canvas.width, canvas.height, "BLACK"); + + if (gameState === 'start') { + drawText("PONG", canvas.width / 2, canvas.height / 2 - 30, mainColor, 30); + drawText("Press ENTER to Start", canvas.width / 2, canvas.height / 2 + 30, "white", 15); + } + else if (gameState === 'playing') { + drawNet(); + + drawText(score.computer.toString(), canvas.width / 4, 30, "white", 24); + drawText(score.player.toString(), 3 * canvas.width / 4, 30, "white", 24); + + drawRect(player.x, player.y, player.width, player.height, player.color); + drawRect(computer.x, computer.y, computer.width, computer.height, computer.color); + + drawCircle(ball.x, ball.y, ball.radius, ball.color); + } + else if (gameState === 'gameOver') { + let winner = score.player > score.computer ? "YOU WIN!" : "COMPUTER WINS!"; + drawText(winner, canvas.width / 2, canvas.height / 2 - 30, mainColor, 24); + drawText(`${score.computer} - ${score.player}`, canvas.width / 2, canvas.height / 2, "white", 20); + drawText("Press ENTER to Restart", canvas.width / 2, canvas.height / 2 + 30, "white", 15); + } + } + + function gameLoop() { + if (gameState === 'playing') { + update(); + } + render(); + requestAnimationFrame(gameLoop); + } + + gameLoop(); +} + +function addPigRoaster() { + + removePigRoaster(); + + + pigElement = document.createElement('div'); + pigElement.id = 'roast-pig'; + pigElement.style.cssText = ` + position: fixed; + right: -80px; + bottom: 100px; /* Moved lower down instead of vertically centered */ + width: 170px; + height: 200px; + background-image: url('${chrome.runtime.getURL('media/pig.png')}'); + background-size: contain; + background-position: left; + background-repeat: no-repeat; + z-index: 10000; + cursor: pointer; + transition: right 0.3s ease; + background-color: transparent !important; /* Ensure transparent background */ + `; + + + pigElement.addEventListener('mouseenter', () => { + pigElement.style.right = '0'; + }); + + pigElement.addEventListener('mouseleave', () => { + pigElement.style.right = '-80px'; + }); + + + pigElement.addEventListener('click', generateRoast); + + document.body.appendChild(pigElement); + + + const pigStyle = document.createElement('style'); + pigStyle.textContent = ` + #roast-pig { + background-color: transparent !important; + } + + /* Roast bubble styling */ + .roast-bubble { + background-color: white !important; + box-shadow: 3px 3px 5px rgba(0,0,0,0.3) !important; + font-family: 'Bangers', cursive !important; + font-size: 16px !important; + color: ${mainColor} !important; + border: 3px solid ${mainColor} !important; + border-radius: 15px !important; + padding: 15px !important; + } + + .roast-bubble-tail { + border-left-color: ${mainColor} !important; + background-color: transparent !important; + } + `; + document.head.appendChild(pigStyle); +} + + +async function generateRoast() { + + const pageText = document.body.innerText.substring(0, 1000); + const pageTitle = document.title; + + + showRoastBubble("Cooking up a roast... 🔥"); + + try { + + const fallbackRoasts = [ + "Oink oink! Spending your time on this page? Your productivity just went to the slaughterhouse!", + "Squeal! Reading this when you should be working? That's what I call hogwashing your responsibilities!", + "Oink! Nice try opening this page. Your browser history is messier than a pig pen!", + "OINK! This page again? You're really wallowing in mediocrity today!", + "Snort snort! If your boss saw this page, you'd be bacon by morning!", + "Oink oink! Your interest in this content is as questionable as my table manners!" + ]; + + const GEMINI_API_KEY = "AIzaSyARBgeRS6EQS4tsdO9yQvYh_biYNbOVJeU"; + + + const timeoutPromise = new Promise((_, reject) => + setTimeout(() => reject(new Error('Request timeout')), 5000) + ); + + const fetchPromise = fetch(`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${GEMINI_API_KEY}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + contents: [{ + parts:[{text: `Based on this webpage title: "${pageTitle}" and content: "${pageText}", create a short, funny, pig-themed roast of the user for browsing this page. Make it witty, sarcastic and comically insulting, but not mean-spirited. Keep it under 150 characters. Start with "Oink oink!" or similar pig sounds.`}] + }] + }) + }); + + const response = await Promise.race([fetchPromise, timeoutPromise]); + const data = await response.json(); + + if (data && data.candidates && data.candidates[0] && + data.candidates[0].content && data.candidates[0].content.parts && + data.candidates[0].content.parts[0]) { + const roast = data.candidates[0].content.parts[0].text; + showRoastBubble(roast); + } else { + const fallbackRoast = fallbackRoasts[Math.floor(Math.random() * fallbackRoasts.length)]; + showRoastBubble(fallbackRoast); + } + } catch (error) { + console.error("Roast generation failed:", error); + + const fallbackRoasts = [ + "Oink oink! Spending your time on this page? Your productivity just went to the slaughterhouse!", + "Squeal! Reading this when you should be working? That's what I call hogwashing your responsibilities!", + "Oink! Nice try opening this page. Your browser history is messier than a pig pen!", + "OINK! This page again? You're really wallowing in mediocrity today!", + "Snort snort! If your boss saw this page, you'd be bacon by morning!", + "Oink oink! Your interest in this content is as questionable as my table manners!" + ]; + + const fallbackRoast = fallbackRoasts[Math.floor(Math.random() * fallbackRoasts.length)]; + showRoastBubble(fallbackRoast); + } +} + +function showRoastBubble(text) { + + if (roastBubble && roastBubble.parentNode) { + roastBubble.parentNode.removeChild(roastBubble); + } + + + roastBubble = document.createElement('div'); + roastBubble.className = 'roast-bubble'; + roastBubble.style.cssText = ` + position: fixed; + right: 120px; + bottom: 160px; /* Adjusted to align with pig */ + background: white !important; + border: 3px solid ${mainColor}; + border-radius: 15px; + padding: 15px; + max-width: 300px; + z-index: 10001; + box-shadow: 3px 3px 5px rgba(0,0,0,0.3); + font-family: 'Bangers', cursive; + font-size: 16px; + color: ${mainColor}; + `; + + roastBubble.textContent = text; + + + const tail = document.createElement('div'); + tail.className = 'roast-bubble-tail'; + tail.style.cssText = ` + position: absolute; + right: -15px; + top: 50%; + transform: translateY(-50%); + width: 0; + height: 0; + border-top: 10px solid transparent; + border-bottom: 10px solid transparent; + border-left: 15px solid ${mainColor}; + background-color: transparent !important; + `; + + roastBubble.appendChild(tail); + document.body.appendChild(roastBubble); + + + setTimeout(() => { + if (roastBubble && roastBubble.parentNode) { + roastBubble.parentNode.removeChild(roastBubble); + roastBubble = null; + } + }, 10000); +} + + +function removePigRoaster() { + if (pigElement && pigElement.parentNode) { + pigElement.parentNode.removeChild(pigElement); + pigElement = null; + } + + if (roastBubble && roastBubble.parentNode) { + roastBubble.parentNode.removeChild(roastBubble); + roastBubble = null; + } +} + + +function addNerdSummarizer() { + + removeNerdSummarizer(); + + + nerdElement = document.createElement('div'); + nerdElement.id = 'nerd-summarizer'; + nerdElement.style.cssText = ` + position: fixed; + left: -80px; + bottom: 100px; + width: 170px; + height: 200px; + background-image: url('${chrome.runtime.getURL('media/spongebob.png')}'); + background-size: contain; + background-position: right; + background-repeat: no-repeat; + z-index: 10000; + cursor: pointer; + transition: left 0.3s ease; + background-color: transparent !important; + `; + + + nerdElement.addEventListener('mouseenter', () => { + nerdElement.style.left = '0'; + }); + + nerdElement.addEventListener('mouseleave', () => { + nerdElement.style.left = '-80px'; + }); + + nerdElement.addEventListener('click', generateSummary); + + document.body.appendChild(nerdElement); + + const nerdStyle = document.createElement('style'); + nerdStyle.textContent = ` + #nerd-summarizer { + background-color: transparent !重要; + } + + /* Summary bubble styling */ + .summary-bubble { + background-color: white !important; + box-shadow: 3px 3px 5px rgba(0,0,0,0.3) !重要; + font-family: 'Bangers', cursive !重要; + font-size: 16px !重要; + color: ${mainColor} !重要; + border: 3px solid ${mainColor} !重要; + border-radius: 15px !重要; + padding: 15px !重要; + } + + .summary-bubble-tail { + border-right-color: ${mainColor} !重要; + background-color: transparent !重要; + } + `; + document.head.appendChild(nerdStyle); +} + +function removeNerdSummarizer() { + if (nerdElement && nerdElement.parentNode) { + nerdElement.parentNode.removeChild(nerdElement); + nerdElement = null; + } + + if (summaryBubble && summaryBubble.parentNode) { + summaryBubble.parentNode.removeChild(summaryBubble); + summaryBubble = null; + } +} + +async function generateSummary() { + const pageText = document.body.innerText.substring(0, 1500); + const pageTitle = document.title; + const metaDesc = document.querySelector('meta[name="description"]')?.content || ''; + const h1Text = Array.from(document.querySelectorAll('h1')).map(h => h.innerText).join('. '); + + + showSummaryBubble("Erm, analyzing page content... *adjusts glasses*"); + + try { + + const fallbackSummaries = [ + "Erm, actually this appears to be a webpage about general internet content. *adjusts glasses* The information architecture could be improved by approximately 37.2%.", + "Well, technically speaking, this site contains HTML, CSS, and Javascript. *snorts* I could have coded it better in 1/3 the time.", + "Um, according to my calculations, this page has a complexity rating of 3.7 on the Flesch-Kincaid scale. *pushes up glasses* Not that impressive really.", + "Erm, I've analyzed this content and found exactly 42 semantic errors. *cleans glasses nervously* The developer clearly didn't validate their markup.", + "Actually, this page loads in 2.3 seconds, which is approximately 0.7 seconds slower than optimal. *adjusts bowtie* I could optimize it with a few algorithmic adjustments.", + "Based on my extensive research, this content has a correlation coefficient of 0.86 with similar websites. *pushes glasses up* Not statistically significant enough." + ]; + + + const GEMINI_API_KEY = "AIzaSyCL_N3hmVIlEE5jRY7-stVzAgD4H2JVkG0"; + + const timeoutPromise = new Promise((_, reject) => + setTimeout(() => reject(new Error('Request timeout')), 5000) + ); + + const fetchPromise = fetch(`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${GEMINI_API_KEY}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + contents: [{ + parts:[{text: `Analyze this webpage with title: "${pageTitle}", description: "${metaDesc}", main heading: "${h1Text}" and content: "${pageText}". Create a brief, nerdy summary of what this page is about. Make it sound like a stereotypical nerd - include phrases like "erm, actually", "technically speaking", "*adjusts glasses*", or "*pushes up glasses*". Keep it under 150 characters. Be a bit condescending but factually accurate.`}] + }] + }) + }); + + const response = await Promise.race([fetchPromise, timeoutPromise]); + const data = await response.json(); + + if (data && data.candidates && data.candidates[0] && + data.candidates[0].content && data.candidates[0].content.parts && + data.candidates[0].content.parts[0]) { + const summary = data.candidates[0].content.parts[0].text; + showSummaryBubble(summary); + } else { + const fallbackSummary = fallbackSummaries[Math.floor(Math.random() * fallbackSummaries.length)]; + showSummaryBubble(fallbackSummary); + } + } catch (error) { + console.error("Summary generation failed:", error); + + const fallbackSummaries = [ + "Erm, actually this appears to be a webpage about general internet content. *adjusts glasses* The information architecture could be improved by approximately 37.2%.", + "Well, technically speaking, this site contains HTML, CSS, and Javascript. *snorts* I could have coded it better in 1/3 the time.", + "Um, according to my calculations, this page has a complexity rating of 3.7 on the Flesch-Kincaid scale. *pushes up glasses* Not that impressive really.", + "Erm, I've analyzed this content and found exactly 42 semantic errors. *cleans glasses nervously* The developer clearly didn't validate their markup.", + "Actually, this page loads in 2.3 seconds, which is approximately 0.7 seconds slower than optimal. *adjusts bowtie* I could optimize it with a few algorithmic adjustments.", + "Based on my extensive research, this content has a correlation coefficient of 0.86 with similar websites. *pushes glasses up* Not statistically significant enough." + ]; + + const fallbackSummary = fallbackSummaries[Math.floor(Math.random() * fallbackSummaries.length)]; + showSummaryBubble(fallbackSummary); + } +} + +function showSummaryBubble(text) { + + if (summaryBubble && summaryBubble.parentNode) { + summaryBubble.parentNode.removeChild(summaryBubble); + } + + + summaryBubble = document.createElement('div'); + summaryBubble.className = 'summary-bubble'; + summaryBubble.style.cssText = ` + position: fixed; + left: 120px; + bottom: 160px; /* Aligned with the nerd character */ + background: white !important; + border: 3px solid ${mainColor}; + border-radius: 15px; + padding: 15px; + max-width: 300px; + z-index: 10001; + box-shadow: 3px 3px 5px rgba(0,0,0,0.3); + font-family: 'Bangers', cursive; + font-size: 16px; + color: ${mainColor}; + `; + + summaryBubble.textContent = text; + + + const tail = document.createElement('div'); + tail.className = 'summary-bubble-tail'; + tail.style.cssText = ` + position: absolute; + left: -15px; + top: 50%; + transform: translateY(-50%); + width: 0; + height: 0; + border-top: 10px solid transparent; + border-bottom: 10px solid transparent; + border-right: 15px solid ${mainColor}; + background-color: transparent !important; + `; + + summaryBubble.appendChild(tail); + document.body.appendChild(summaryBubble); + + + setTimeout(() => { + if (summaryBubble && summaryBubble.parentNode) { + summaryBubble.parentNode.removeChild(summaryBubble); + summaryBubble = null; + } + }, 15000); +} diff --git a/submissions/Toonify/manifest.json b/submissions/Toonify/manifest.json new file mode 100644 index 00000000..9145df0d --- /dev/null +++ b/submissions/Toonify/manifest.json @@ -0,0 +1,34 @@ +{ + "manifest_version": 3, + "name": "Toonify", + "version": "1.0", + "description": "A Chrome extension that cartoonifies web pages", + "icons": { + "16": "media/icon16.jpg", + "48": "media/icon48.jpg", + "128": "media/icon128.jpg" + }, + "permissions": [ + "activeTab", + "storage" + ], + "content_scripts": [ + { + "matches": [""], + "js": ["content.js"] + } + ], + "action": { + "default_popup": "popup.html", + "default_icon": { + "16": "media/icon16.jpg", + "48": "media/icon48.jpg", + "128": "media/icon128.jpg" + } + }, + "web_accessible_resources": [{ + "resources": ["media/doodles.jpg", "media/turtle.gif", "media/Boing.mp3", "media/Resonance.mp3", "media/kachow.mp3", "media/Boom.mp3", "media/Pow.mp3" , "media/Zap.mp3", "media/Chill.mp3", "media/spongebob.png", "media/pig.png", "media/icon.jpg", "media/mousecursor.png", "media/mouseclicker.png", "media/cat.png", "media/chillguy.png", "media/peppapig.png"], + "matches": [""] + + }] +} \ No newline at end of file diff --git a/submissions/Toonify/media/Boing.mp3 b/submissions/Toonify/media/Boing.mp3 new file mode 100644 index 00000000..5b466b93 Binary files /dev/null and b/submissions/Toonify/media/Boing.mp3 differ diff --git a/submissions/Toonify/media/Boom.mp3 b/submissions/Toonify/media/Boom.mp3 new file mode 100644 index 00000000..9695b198 Binary files /dev/null and b/submissions/Toonify/media/Boom.mp3 differ diff --git a/submissions/Toonify/media/Chill.mp3 b/submissions/Toonify/media/Chill.mp3 new file mode 100644 index 00000000..6d50a662 Binary files /dev/null and b/submissions/Toonify/media/Chill.mp3 differ diff --git a/submissions/Toonify/media/Pow.mp3 b/submissions/Toonify/media/Pow.mp3 new file mode 100644 index 00000000..e8d398d6 Binary files /dev/null and b/submissions/Toonify/media/Pow.mp3 differ diff --git a/submissions/Toonify/media/Resonance.mp3 b/submissions/Toonify/media/Resonance.mp3 new file mode 100644 index 00000000..f7a5ee08 Binary files /dev/null and b/submissions/Toonify/media/Resonance.mp3 differ diff --git a/submissions/Toonify/media/Zap.mp3 b/submissions/Toonify/media/Zap.mp3 new file mode 100644 index 00000000..dfe44d29 Binary files /dev/null and b/submissions/Toonify/media/Zap.mp3 differ diff --git a/submissions/Toonify/media/cat.png b/submissions/Toonify/media/cat.png new file mode 100644 index 00000000..2a07a3c1 Binary files /dev/null and b/submissions/Toonify/media/cat.png differ diff --git a/submissions/Toonify/media/chillguy.png b/submissions/Toonify/media/chillguy.png new file mode 100644 index 00000000..247f94e1 Binary files /dev/null and b/submissions/Toonify/media/chillguy.png differ diff --git a/submissions/Toonify/media/doodles.jpg b/submissions/Toonify/media/doodles.jpg new file mode 100644 index 00000000..22f4a404 Binary files /dev/null and b/submissions/Toonify/media/doodles.jpg differ diff --git a/submissions/Toonify/media/icon.jpg b/submissions/Toonify/media/icon.jpg new file mode 100644 index 00000000..824c2eaf Binary files /dev/null and b/submissions/Toonify/media/icon.jpg differ diff --git a/submissions/Toonify/media/icon128.jpg b/submissions/Toonify/media/icon128.jpg new file mode 100644 index 00000000..30647737 Binary files /dev/null and b/submissions/Toonify/media/icon128.jpg differ diff --git a/submissions/Toonify/media/icon16.jpg b/submissions/Toonify/media/icon16.jpg new file mode 100644 index 00000000..e65e7c3a Binary files /dev/null and b/submissions/Toonify/media/icon16.jpg differ diff --git a/submissions/Toonify/media/icon48.jpg b/submissions/Toonify/media/icon48.jpg new file mode 100644 index 00000000..1ee7b0cc Binary files /dev/null and b/submissions/Toonify/media/icon48.jpg differ diff --git a/submissions/Toonify/media/kachow.mp3 b/submissions/Toonify/media/kachow.mp3 new file mode 100644 index 00000000..84daf1e2 Binary files /dev/null and b/submissions/Toonify/media/kachow.mp3 differ diff --git a/submissions/Toonify/media/mouseclicker.png b/submissions/Toonify/media/mouseclicker.png new file mode 100644 index 00000000..c0e2cb89 Binary files /dev/null and b/submissions/Toonify/media/mouseclicker.png differ diff --git a/submissions/Toonify/media/mousecursor.png b/submissions/Toonify/media/mousecursor.png new file mode 100644 index 00000000..13899cf6 Binary files /dev/null and b/submissions/Toonify/media/mousecursor.png differ diff --git a/submissions/Toonify/media/peppapig.png b/submissions/Toonify/media/peppapig.png new file mode 100644 index 00000000..79098759 Binary files /dev/null and b/submissions/Toonify/media/peppapig.png differ diff --git a/submissions/Toonify/media/pig.png b/submissions/Toonify/media/pig.png new file mode 100644 index 00000000..b083c17b Binary files /dev/null and b/submissions/Toonify/media/pig.png differ diff --git a/submissions/Toonify/media/spongebob.png b/submissions/Toonify/media/spongebob.png new file mode 100644 index 00000000..c08db86b Binary files /dev/null and b/submissions/Toonify/media/spongebob.png differ diff --git a/submissions/Toonify/media/turtle.gif b/submissions/Toonify/media/turtle.gif new file mode 100644 index 00000000..4e439f04 Binary files /dev/null and b/submissions/Toonify/media/turtle.gif differ diff --git a/submissions/Toonify/popup.css b/submissions/Toonify/popup.css new file mode 100644 index 00000000..7eed8344 --- /dev/null +++ b/submissions/Toonify/popup.css @@ -0,0 +1,158 @@ +body { + font-family: 'Bangers', cursive; + background-color: #ffd54f; + width: 300px; + margin: 0; + padding: 15px; + box-sizing: border-box; + color: #333; + background-image: url('data:image/svg+xml;utf8,'); +} + +.container { + display: flex; + flex-direction: column; + gap: 15px; + background-color: white; + padding: 15px; + border-radius: 10px; + border: 3px solid #333; + box-shadow: 5px 5px 0 rgba(0,0,0,0.3); +} + +h1 { + margin: 0; + text-align: center; + color: #ef5350; + letter-spacing: 1px; + text-shadow: 2px 2px 0 #333; + font-size: 32px; + transform: rotate(-2deg); +} + +.color-picker { + display: flex; + align-items: center; + justify-content: space-between; +} + +.effects-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 8px; + margin: 10px 0; +} + +.effect-btn { + background-color: #ffd54f; + color: #333; + border: 3px solid #333; + padding: 8px 12px; + border-radius: 8px; + font-family: 'Bangers', cursive; + font-size: 14px; + cursor: pointer; + box-shadow: 3px 3px 0 rgba(0,0,0,0.3); + transition: all 0.2s ease; + letter-spacing: 0.5px; + text-align: center; + position: relative; + overflow: hidden; +} + +.effect-btn:hover { + transform: translate(-1px, -1px); + box-shadow: 4px 4px 0 rgba(0,0,0,0.3); + background-color: #ffecb3; +} + +.effect-btn:active { + transform: translate(1px, 1px); + box-shadow: 2px 2px 0 rgba(0,0,0,0.3); +} + +.effect-btn.active { + background-color: #4caf50; + color: white; + text-shadow: 1px 1px 0 #333; +} + +.effect-btn.active:hover { + background-color: #66bb6a; +} + +.effect-btn::before { + content: ''; + position: absolute; + top: -2px; + left: -2px; + right: -2px; + bottom: -2px; + background: linear-gradient(45deg, #ff9800, #ffc107, #ffeb3b, #8bc34a, #4caf50, #2196f3, #9c27b0, #e91e63); + border-radius: 10px; + z-index: -1; + opacity: 0; + transition: opacity 0.3s ease; +} + +.effect-btn.active::before { + opacity: 0.3; + animation: rainbow-border 2s linear infinite; +} + +@keyframes rainbow-border { + 0% { background-position: 0% 50%; } + 50% { background-position: 100% 50%; } + 100% { background-position: 0% 50%; } +} + +button { + background-color: #ef5350; + color: white; + border: none; + padding: 10px; + border-radius: 5px; + font-family: 'Bangers', cursive; + font-size: 18px; + cursor: pointer; + box-shadow: 3px 3px 0 #333; + transition: transform 0.1s, box-shadow 0.1s; + letter-spacing: 1px; +} + +button:hover { + transform: translate(-1px, -1px); + box-shadow: 4px 4px 0 #333; +} + +button:active { + transform: translate(1px, 1px); + box-shadow: 2px 2px 0 #333; +} + +.status { + text-align: center; + font-style: italic; +} + +#outputText { + margin: 0; +} + +.peppa-section { + text-align: center; + margin-bottom: 10px; +} + +.peppa-img { + width: 60px; + height: 60px; + border-radius: 50%; + border: 3px solid #ef5350; + box-shadow: 2px 2px 0 #333; + transition: transform 0.3s ease; +} + +.peppa-img:hover { + transform: rotate(10deg) scale(1.1); +} diff --git a/submissions/Toonify/popup.html b/submissions/Toonify/popup.html new file mode 100644 index 00000000..95eb7c66 --- /dev/null +++ b/submissions/Toonify/popup.html @@ -0,0 +1,40 @@ + + + + + + Toonify + + + + +
+
+ Peppa Pig +
+

Cartoonify

+
+
+ + +
+
+
+
+ + + + + + + + +
+ +
+

Ready to cartoonify!

+
+
+ + + diff --git a/submissions/Toonify/popup.js b/submissions/Toonify/popup.js new file mode 100644 index 00000000..50879e6f --- /dev/null +++ b/submissions/Toonify/popup.js @@ -0,0 +1,143 @@ +const inputText = document.getElementById('inputText'); +const saveButton = document.getElementById('saveButton'); +const outputText = document.getElementById('outputText'); +const applyStylesButton = document.getElementById('applyStyles'); +const mainColorInput = document.getElementById('mainColor'); +const presetColors = document.getElementById('presetColors'); +const effectButtons = document.querySelectorAll('.effect-btn'); +var i = 0; + +// Object to track effect states +const effectStates = { + enableSoundEffects: true, + enableSpeechBubbles: true, + enableTurtle: true, + enablePong: true, + enableinvert: true, + enablePigRoaster: true, + enableNerdSummarizer: true, + enableRGB: true +}; + + +chrome.storage.sync.get(['savedText'], function (result) { + if (result.savedText) { + outputText.textContent = `Saved: ${result.savedText}`; + } +}); + + +chrome.storage.sync.get(['cartoonifyPrefs'], function (result) { + if (result.cartoonifyPrefs) { + const prefs = result.cartoonifyPrefs; + mainColorInput.value = prefs.mainColor || '#ef5350'; + + // Update effect states and button appearances + Object.keys(effectStates).forEach(effectKey => { + effectStates[effectKey] = prefs[effectKey] !== false; + const button = document.querySelector(`[data-effect="${effectKey}"]`); + if (button) { + if (effectStates[effectKey]) { + button.classList.add('active'); + } else { + button.classList.remove('active'); + } + } + }); + + updateColorPreview(mainColorInput.value); + } +}); + + + + +function updateColorPreview(color) { + document.documentElement.style.setProperty('--preview-color', color); +} + + +function savePreferences() { + const prefs = { + mainColor: mainColorInput.value, + ...effectStates + }; + + chrome.storage.sync.set({ cartoonifyPrefs: prefs }); + updateColorPreview(prefs.mainColor); +} + + +const colorPresets = ['#ef5350', '#42a5f5', '#66bb6a', '#ffca28', '#ab47bc', '#26c6da']; +colorPresets.forEach(color => { + const colorButton = document.createElement('button'); + colorButton.className = 'color-preset'; + colorButton.style.backgroundColor = color; + colorButton.title = color; + colorButton.addEventListener('click', () => { + mainColorInput.value = color; + updateColorPreview(color); + savePreferences(); + }); + presetColors.appendChild(colorButton); +}); + +// Add event listeners for effect buttons +effectButtons.forEach(button => { + button.addEventListener('click', () => { + const effect = button.getAttribute('data-effect'); + effectStates[effect] = !effectStates[effect]; + + if (effectStates[effect]) { + button.classList.add('active'); + } else { + button.classList.remove('active'); + } + + savePreferences(); + }); +}); + +mainColorInput.addEventListener('change', savePreferences); + +updateColorPreview(mainColorInput.value); + + +applyStylesButton.addEventListener('click', () => { + i++; + if (i%2==1){ + applyStylesButton.innerHTML = "🛑 UNTOGGLE COMIC MODE 🛑" + } + else{ + applyStylesButton.innerHTML = "🚀 TOGGLE COMIC MODE 🚀" + } + const prefs = { + mainColor: mainColorInput.value, + ...effectStates + }; + chrome.storage.sync.set({ userPrefs: prefs }, () => { + console.log("Preferences saved."); + }); + + chrome.tabs.query({active: true, currentWindow: true}, function(tabs) { + chrome.tabs.sendMessage(tabs[0].id, { + action: "applyStyles", + prefs: prefs + }, function(response) { + if (chrome.runtime.lastError) { + console.error("Connection error:", chrome.runtime.lastError.message); + outputText.textContent = "❌ Error: Please refresh the page and try again"; + return; + } + + if (response && response.status) { + outputText.textContent = "🎉 " + response.status + " 🎉"; + + document.body.classList.add('activated'); + setTimeout(() => { + document.body.classList.remove('activated'); + }, 500); + } + }); + }); +}); diff --git a/submissions/Toonify/styles.css b/submissions/Toonify/styles.css new file mode 100644 index 00000000..23c66858 --- /dev/null +++ b/submissions/Toonify/styles.css @@ -0,0 +1,40 @@ +* { + font-family: 'Bangers', cursive !important; + color: #ef5350; + font-weight: bold; + } + body, html * { + background: white !important; + background-image: none !important; + } + + + @keyframes wobble { + 0% { transform: rotate(2deg); } + 50% { transform: rotate(-2deg); } + 100% { transform: rotate(2deg); } + } + + h1, h2, h3 { + display: inline-block; + animation: wobble 0.2s infinite alternate ease-in-out; + } + + img { + filter: contrast(1000%) brightness(100%); + animation: wobble 0.2s infinite alternate ease-in-out + } + + /* * { + border: 2px solid black; + box-shadow: 1px 1px 0 black; + } */ + + @keyframes bounce { + 0%, 100% { transform: translateY(0); } + 50% { transform: translateY(-5px); } + } + + button { + animation: bounce 0.3s infinite alternate ease-in-out; + } \ No newline at end of file