diff --git a/README.md b/README.md index e297b29..7ce1f36 100644 --- a/README.md +++ b/README.md @@ -1,83 +1,172 @@ # PacCrypt WebApp -**PacCrypt** is a web-based application designed to provide secure encoding, encryption, and password generation. It allows users to easily encrypt and decrypt text and files, with both basic and advanced encryption options. It also features a password generator and a simple Pac-Man game as an Easter egg! +**PacCrypt** is a web-based platform that allows you to securely encrypt/decrypt text and files, generate passwords, and even enjoy a hidden Pac-Man game! +Built using Python (Flask), JavaScript, and AES-GCM encryption. -## Features +Official Website: [paccrypt.unnaturalll.dev](http://paccrypt.unnaturalll.dev) -- **Basic and Advanced Encryption**: Choose between simple encryption (Caesar Cipher) or more secure AES-GCM encryption. -- **File Encryption/Decryption**: Encrypt or decrypt files with a password. -- **Password Generator**: Generate secure random passwords with customizable length and complexity. -- **Pac-Man Game**: A fun Easter egg! Play a Pac-Man game when you type "pacman" in the text area. -- **Copy to Clipboard**: Copy generated passwords or encrypted results with one click. -- **Responsive Design**: Fully responsive web design that works across different screen sizes. +--- -## Installation +## ✨ Features -### Prerequisites +- 🔒 **Basic and Advanced Encryption** (Text and Files) +- 🔑 **Password Generator** +- đŸ„šī¸ **Pac-Man Easter Egg** (Type `pacman` to unlock!) +- 📱 **Responsive Design** (Mobile Friendly) +- ⚡ **One-Click Start Scripts** (Dev and Production modes) +- 🎨 **Modern Animated UI** (Dark Mode + Green Neon Theme) + +--- + +## 👨‍đŸ’ģ Installation + +### 📋 Prerequisites - **Python 3.7+** -- **Nginx** (for reverse proxy and SSL configuration for hosting) +- **Flask 3+** +- **Cryptography 42+** +- **Waitress 2.1+** +- **Nginx** (Recommended for production) -Official PacCrypt website: paccrypt.unnaturalll.dev +--- -### Steps to Set Up Locally: +### ⚡ Quick Setup 1. Clone the repository: + + ```bash git clone https://github.com/TySP-Dev/PacCrypt.git - cd paccrypt-webapp + cd paccrypt-webapp-final + ``` 2. Create and activate a virtual environment: - python3 -m venv venv - source venv/bin/activate # On Windows, use `venv\Scripts\activate` -3. Install the required Python dependencies: + ```bash + python -m venv venv + source venv/bin/activate # Windows: venv\Scripts\activate + ``` + +3. Install required Python packages: + + ```bash pip install -r requirements.txt + ``` -4. Run the Flask app: - python app.py +4. Start the app: -5. Open http://127.0.0.1:5000 to access the app locally. + **Windows**: + ```bash + start_dev.bat # For Development + start_prod.bat # For Production + ``` -## Usage + **Linux / Mac**: + ```bash + chmod +x start_dev.sh start_prod.sh + ./start_dev.sh # For Development + ./start_prod.sh # For Production + ``` -### Encryption and Decryption +5. Access the app at: + [http://127.0.0.1:5000](http://127.0.0.1:5000) -#### For text encryption/decryption: +--- --- Select the encryption type (Basic or Advanced). +## 🚀 Usage Guide --- Choose whether to Encrypt or Decrypt. +### 🔒 Text Encryption/Decryption --- Enter text in the Input Text area. +- Select **Encryption Type** (Basic or Advanced) +- Enter text +- Provide password (Advanced only) +- Choose **Encrypt** or **Decrypt** +- Click **Submit** --- Enter a password (if using advanced encryption). +### 📁 File Encryption/Decryption --- Click submit. +- Select **Advanced** encryption +- Upload a file +- Provide password +- Choose **Encrypt** or **Decrypt** +- Click **Submit** -#### For file encryption/decryption: +### 🔑 Password Generator --- Select encryption type **Advanced.** +- Click **Generate** to create a secure password +- Click **Copy** to save it to clipboard --- Choose whether to Encrypt or Decrypt. +### 🎮 Pac-Man Easter Egg --- Upload a file. +- Type **`pacman`** into the input box to unlock the hidden Pac-Man game! --- Enter a password for encryption/decryption. +--- --- Click submit. +## đŸ›Ąī¸ Hosting with Nginx (optional) -### Password Generation: +Recommended for secure public deployment. -Click the Generate button to create a random password, then use the Copy button to copy it to your clipboard. +Example minimal Nginx config: -### Pac-Man Game (Easter Egg): +```nginx +server { + listen 80; + server_name yourdomain.com; -Type the word "pacman" in the input box to unlock the Pac-Man game! + location / { + proxy_pass http://127.0.0.1:5000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } +} +``` -### Contributing: +> Tip: Set up SSL with Let's Encrypt for HTTPS security! 🔐 -Feel free to open an issue or submit a pull request for improvements, bug fixes, or new features! +--- -### License +## 📂 Project Structure -This project is open source and available under the MIT License. +``` +paccrypt-webapp-final/ +├── app.py +├── requirements.txt +├── templates/ +│ ├── index.html +│ ├── 404.html +│ └── 403.html +│ └── 500.html +├── static/ +│ ├── css/ +│ │ └── styles.css +│ ├── js/ +│ │ └── script.js +│ ├── img/ +│ │ └── PacCrypt.png +│ └── audio/ +│ └── chomp.mp3 +├── start_dev.bat +├── start_prod.bat +├── start_dev.sh +├── start_prod.sh +├── README.md +``` + +--- + +## 🤝 Contributing + +Contributions are welcome! + +- Add new features +- Fix bugs +- Improve performance +- Expand the Pac-Man Easter Egg 🎮 + +--- + +## 📄 License + +This project is licensed under the **MIT License**. + +--- \ No newline at end of file diff --git a/app.py b/app.py index a14fbb5..b91ccf4 100644 --- a/app.py +++ b/app.py @@ -1,15 +1,17 @@ +## DEV DEV DEV + +import os from flask import Flask, render_template, request, jsonify import html -import os import base64 from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC from cryptography.hazmat.primitives.hashes import SHA256 from cryptography.hazmat.primitives.ciphers.aead import AESGCM -from waitress import serve app = Flask(__name__) -# Basic Encoder/Decoder +# ====== Your App Code ====== + ALPHABET = list('abcdefghijklmnopqrstuvwxyz') def simple_encode(text: str) -> str: @@ -24,7 +26,6 @@ def simple_decode(text: str) -> str: for c in text.lower() ) -# Advanced Encrypt/Decrypt using AES-GCM def derive_key(password: str, salt: bytes) -> bytes: kdf = PBKDF2HMAC( algorithm=SHA256(), @@ -56,7 +57,6 @@ def advanced_decrypt(token_b64: str, password: str) -> str: except Exception: return "[Error] Invalid password or corrupted data!" -# Combined Route for Page & AJAX @app.route("/", methods=["GET", "POST"]) def index(): if request.method == 'POST': @@ -83,6 +83,30 @@ def index(): encryption_type="advanced" ) +# ====== Smart Server Startup ====== + +@app.errorhandler(404) +def page_not_found(e): + return render_template('404.html'), 404 + +@app.errorhandler(500) +def server_error(e): + return render_template('500.html'), 500 + +@app.errorhandler(403) +def forbidden(e): + return render_template('403.html'), 403 + if __name__ == "__main__": - # Use Waitress to serve the app in production - serve(app, host="0.0.0.0", port=5000) + PRODUCTION = os.getenv("PRODUCTION", "false").lower() == "true" + + if PRODUCTION: + from waitress import serve + print("[INFO] Running in PRODUCTION mode with Waitress.") + serve(app, host="0.0.0.0", port=5000) + else: + print("[INFO] Running in DEVELOPMENT mode with Flask server.") + app.run(debug=True, host="0.0.0.0", port=5000) + + +## DEV DEV DEV diff --git a/requirements.txt b/requirements.txt index 1d89f7f..f1d587b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,8 @@ ### **requirements.txt** -Flask==2.1.2 -cryptography==3.4.8 -nginx==1.21.0 # Only needed for Nginx integration, not installed via pip \ No newline at end of file +flask==3.0.3 +cryptography==42.0.5 +waitress==2.1.2 + +# nginx - Only needed for Nginx integration, not installed via pip +# Run pip install -r requirements.txt \ No newline at end of file diff --git a/start_dev.bat b/start_dev.bat new file mode 100644 index 0000000..8214467 --- /dev/null +++ b/start_dev.bat @@ -0,0 +1,5 @@ +@echo off +echo Starting PacCrypt in DEVELOPMENT mode... +set PRODUCTION=false +python app.py +pause diff --git a/start_dev.sh b/start_dev.sh new file mode 100644 index 0000000..6fe5464 --- /dev/null +++ b/start_dev.sh @@ -0,0 +1,4 @@ +#!/bin/bash +echo "Starting PacCrypt in DEVELOPMENT mode..." +export PRODUCTION=false +python3 app.py diff --git a/start_prod.bat b/start_prod.bat new file mode 100644 index 0000000..2902d46 --- /dev/null +++ b/start_prod.bat @@ -0,0 +1,5 @@ +@echo off +echo Starting PacCrypt in PRODUCTION mode... +set PRODUCTION=true +python app.py +pause diff --git a/start_prod.sh b/start_prod.sh new file mode 100644 index 0000000..50b9bac --- /dev/null +++ b/start_prod.sh @@ -0,0 +1,4 @@ +#!/bin/bash +echo "Starting PacCrypt in PRODUCTION mode..." +export PRODUCTION=true +python3 app.py \ No newline at end of file diff --git a/static/audio/chomp.mp3 b/static/audio/chomp.mp3 new file mode 100644 index 0000000..f55a949 Binary files /dev/null and b/static/audio/chomp.mp3 differ diff --git a/static/css/styles.css b/static/css/styles.css index 5e32031..9b36441 100644 --- a/static/css/styles.css +++ b/static/css/styles.css @@ -9,23 +9,25 @@ body { font-family: 'Poppins', sans-serif; background-color: #121212; - color: #f0f0f0; + color: #00ff99; display: flex; flex-direction: column; min-height: 100vh; - justify-content: center; /* Vertically center content */ - align-items: center; /* Horizontally center content */ + justify-content: center; + align-items: center; padding: 20px; } /* ===== Header ===== */ header { text-align: center; - padding: 30px 20px; + padding: 25px; background-color: #1c1c1c; - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.5); + border-radius: 12px; + box-shadow: 0 0 15px rgba(0, 255, 153, 0.4); width: 100%; max-width: 800px; + margin-bottom: 30px; } header h1 { @@ -35,7 +37,7 @@ header { } header p { - font-size: 1.1em; + font-size: 1.2em; color: #00ff99; } @@ -49,13 +51,12 @@ main { max-width: 800px; padding: 20px; gap: 30px; - justify-content: center; } /* ===== Section Card Styling ===== */ .card { background-color: #1e1e1e; - padding: 20px 25px; + padding: 25px; width: 100%; max-width: 800px; border-radius: 12px; @@ -68,7 +69,7 @@ main { display: flex; flex-direction: column; align-items: center; - gap: 15px; + gap: 20px; width: 100%; } @@ -92,7 +93,8 @@ textarea { resize: none; } -input[type="password"] { +input[type="password"], +#password { min-height: 50px; } @@ -123,7 +125,7 @@ select:focus { box-shadow: 0 0 8px rgba(0, 255, 153, 0.8); } -/* ===== Match input and output textarea sizes ===== */ +/* ===== Match input and output sizes ===== */ #input-text, #output-text { width: 80%; @@ -138,8 +140,8 @@ select:focus { display: flex; flex-wrap: wrap; justify-content: center; - gap: 8px; - margin-top: 8px; + gap: 15px; + margin-top: 15px; width: 100%; } @@ -152,8 +154,8 @@ button { font-size: 1em; cursor: pointer; transition: 0.3s; - width: 100%; /* Makes buttons stretch to fill container */ - max-width: 200px; /* Restricts button width */ + width: 100%; + max-width: 200px; } button:hover { @@ -161,7 +163,7 @@ button { color: #121212; } -/* ===== Toggle Buttons ===== */ +/* ===== Toggle Buttons (Encode/Decode, Encrypt/Decrypt) ===== */ .radio-group { display: flex; justify-content: center; @@ -174,13 +176,15 @@ button { display: inline-flex; align-items: center; justify-content: center; - padding: 8px 18px; + padding: 1px 1px; border: 2px solid #00ff99; border-radius: 8px; background-color: #2c2f33; color: #00ff99; cursor: pointer; transition: 0.3s; + position: relative; + box-shadow: none; } .radio-button:hover { @@ -188,21 +192,27 @@ button { color: #121212; } + /* Hide the actual radio input */ .radio-button input { display: none; } + /* When selected, make the ENTIRE BUTTON glow */ .radio-button input:checked + span { - background-color: #00ff99; - color: #121212; - padding: 8px 18px; + background-color: #2c2f33; + color: #00ff99; + box-shadow: 0 0 15px rgba(0, 255, 153, 0.7); border-radius: 8px; - display: inline-block; + padding: 8px 18px; + display: inline-flex; + align-items: center; + justify-content: center; } + /* ===== Remove File Button ===== */ #remove-file-btn { - display: none; /* only shows when a file is selected */ + display: none; margin-top: 8px; padding: 8px 16px; border: 2px solid #ff5555; @@ -221,14 +231,19 @@ button { /* ===== Toast Notifications ===== */ .toast { visibility: hidden; - min-width: 250px; + width: 80%; + max-width: 500px; + min-height: 50px; background-color: #333; color: #00ff99; text-align: center; - border-radius: 6px; - padding: 10px; - margin-top: 8px; - font-size: 0.9em; + border-radius: 8px; + padding: 14px; + margin: 10px auto 0 auto; + font-size: 1em; + display: flex; + align-items: center; + justify-content: center; animation: fadein 0.5s, fadeout 0.5s 2.5s; } @@ -274,11 +289,14 @@ button { /* ===== Footer ===== */ footer { text-align: center; - padding: 18px; + padding: 25px; background-color: #1c1c1c; color: #00ff99; - margin-top: auto; + border-radius: 12px; + box-shadow: 0 0 15px rgba(0, 255, 153, 0.4); + margin-top: 30px; width: 100%; + max-width: 800px; } footer a { @@ -290,22 +308,17 @@ footer { color: #ff0066; } -/* ===== Password Input Field ===== */ -#password-input { - display: flex; /* Password input is visible by default */ - margin-top: 15px; - flex-direction: column; - gap: 10px; - width: 100%; - max-width: 500px; -} - - #password-input input { - padding: 12px; - font-size: 1em; - border: 2px solid #00ff99; - border-radius: 8px; - background-color: #2c2f33; - color: #00ff99; - width: 100%; /* Ensure the password field takes full width */ +/* ===== Responsive Tweaks ===== */ +@media (max-width: 600px) { + input, + textarea, + select, + #input-text, + #output-text, + #password-field, + #password, + #file-password { + width: 100%; + max-width: 90%; } +} diff --git a/static/js/script.js b/static/js/script.js index 2785547..3628aa1 100644 --- a/static/js/script.js +++ b/static/js/script.js @@ -1,41 +1,31 @@ -// ===== AES Encryption ===== +// ===== AES Advanced Encryption ===== async function encryptAdvanced(message, password) { - // Create a random salt for key derivation const salt = crypto.getRandomValues(new Uint8Array(16)); - - // Derive a key from the password using PBKDF2 and the salt const key = await deriveKey(password, salt); - - // Create a random initialization vector (IV) const iv = crypto.getRandomValues(new Uint8Array(12)); - // Encode the message as a Uint8Array const encoder = new TextEncoder(); const encodedMessage = encoder.encode(message); - // Encrypt the message using AES-GCM const encryptedMessage = await crypto.subtle.encrypt( { name: 'AES-GCM', iv: iv }, key, encodedMessage ); - // Combine salt, IV, and encrypted message const encryptedArray = new Uint8Array(salt.length + iv.length + encryptedMessage.byteLength); encryptedArray.set(salt); encryptedArray.set(iv, salt.length); encryptedArray.set(new Uint8Array(encryptedMessage), salt.length + iv.length); - // Convert the result to base64 to send to the server - return btoa(String.fromCharCode.apply(null, encryptedArray)); + return btoa(String.fromCharCode(...encryptedArray)); } -// Derive a key from the password using PBKDF2 async function deriveKey(password, salt) { const encoder = new TextEncoder(); const passwordBuffer = encoder.encode(password); - const key = await crypto.subtle.importKey( + const keyMaterial = await crypto.subtle.importKey( 'raw', passwordBuffer, { name: 'PBKDF2' }, @@ -47,37 +37,31 @@ async function deriveKey(password, salt) { { name: 'PBKDF2', salt: salt, - iterations: 100000, + iterations: 200000, hash: 'SHA-256', }, - key, + keyMaterial, { name: 'AES-GCM', length: 256 }, false, ['encrypt', 'decrypt'] ); } -// ===== AES Decryption ===== async function decryptAdvanced(encryptedData, password) { - // Decode the base64-encoded encrypted data - const encryptedArray = new Uint8Array(atob(encryptedData).split("").map(char => char.charCodeAt(0))); + const encryptedArray = new Uint8Array(atob(encryptedData).split("").map(c => c.charCodeAt(0))); - // Extract salt, IV, and encrypted message from the encrypted data const salt = encryptedArray.slice(0, 16); const iv = encryptedArray.slice(16, 28); const encryptedMessage = encryptedArray.slice(28); - // Derive the key from the password and salt const key = await deriveKey(password, salt); - // Decrypt the message using AES-GCM const decryptedMessage = await crypto.subtle.decrypt( { name: 'AES-GCM', iv: iv }, key, encryptedMessage ); - // Decode the decrypted message to text const decoder = new TextDecoder(); return decoder.decode(decryptedMessage); } @@ -85,81 +69,54 @@ async function decryptAdvanced(encryptedData, password) { // ===== UI Toggles ===== function toggleEncryptionOptions() { const type = document.getElementById("encryption-type").value; - const pwdContainer = document.getElementById("password-input"); - pwdContainer.style.display = (type === 'advanced') ? 'flex' : 'none'; + document.getElementById("password-input").style.display = (type === 'advanced') ? 'flex' : 'none'; + if (type === 'basic') removeFile(); + toggleInputMode(); - document.getElementById("encrypt-label").textContent = - (type === 'basic') ? "Encode" : "Encrypt"; - document.getElementById("decrypt-label").textContent = - (type === 'basic') ? "Decode" : "Decrypt"; + document.getElementById("encrypt-label").textContent = (type === 'basic') ? "Encode" : "Encrypt"; + document.getElementById("decrypt-label").textContent = (type === 'basic') ? "Decode" : "Decrypt"; } -// ===== Remove File Button ===== function removeFile() { - document.getElementById("file-input").value = ""; // Clear the file input - document.getElementById("remove-file-btn").style.display = 'none'; // Hide the remove file button - toggleInputMode(); // Reapply the input mode logic - document.getElementById("file-password-input").style.display = 'none'; // Hide the file password input + document.getElementById("file-input").value = ""; + document.getElementById("remove-file-btn").style.display = 'none'; + toggleInputMode(); } -// ===== Input vs. File Toggle ===== function toggleInputMode() { const textValue = document.getElementById("input-text").value.trim(); const fileSelected = document.getElementById("file-input").files.length > 0; const isAdvanced = document.getElementById("encryption-type").value === 'advanced'; - // Show/hide text area based on file selection - document.getElementById("text-section").style.display = - fileSelected ? 'none' : 'flex'; + document.getElementById("text-section").style.display = fileSelected ? 'none' : 'flex'; + document.getElementById("file-section").style.display = (isAdvanced && !textValue) ? 'flex' : 'none'; + document.getElementById("remove-file-btn").style.display = fileSelected ? 'inline-block' : 'none'; - // Show/hide file input section when in advanced mode and no text input is given - document.getElementById("file-section").style.display = - (isAdvanced && !textValue) ? 'flex' : 'none'; - - // Show/hide the remove file button - document.getElementById("remove-file-btn").style.display = - fileSelected ? 'inline-block' : 'none'; - - // ALWAYS show the password input in advanced mode if (isAdvanced) { document.getElementById("password-input").style.display = 'flex'; - } else { - document.getElementById("password-input").style.display = 'none'; - } - - // Show the dedicated password input for file encryption if a file is selected - if (fileSelected) { - document.getElementById("file-password-input").style.display = 'flex'; // Show password input for files - } else { - document.getElementById("file-password-input").style.display = 'none'; // Hide when no file is selected } } -// ===== Validate and Submit Form ===== +// ===== Form Submission ===== async function handleSubmit(event) { event.preventDefault(); - // If the encryption type is advanced, ensure password is provided const password = document.getElementById("password").value; - const filePassword = document.getElementById("file-password") ? document.getElementById("file-password").value : ''; const encryptionType = document.getElementById("encryption-type").value; - if (encryptionType === 'advanced' && !password && !filePassword) { + if (encryptionType === 'advanced' && !password) { alert("Password is required for advanced encryption."); return; } - // Prepare the form data const payload = { "encryption-type": encryptionType, operation: document.querySelector('input[name="operation"]:checked').value, message: document.getElementById("input-text").value, - password: password, - "file-password": filePassword + password: password }; - // Handle file upload encryption/decryption const fileInput = document.getElementById("file-input"); if (fileInput.files.length > 0) { const op = document.querySelector('input[name="operation"]:checked').value; @@ -168,7 +125,6 @@ async function handleSubmit(event) { return; } - // Handle text encryption/decryption try { const resp = await fetch("/", { method: "POST", @@ -185,30 +141,35 @@ async function handleSubmit(event) { // ===== File Encryption / Decryption ===== function encryptFile() { const f = document.getElementById("file-input"); - const pwd = document.getElementById("file-password").value; + const pwd = document.getElementById("password").value; if (!pwd) return alert("Please enter a password!"); if (!f.files.length) return alert("Please select a file!"); + const reader = new FileReader(); reader.onload = async (e) => { - const raw = e.target.result; - let encryptedMessage = await encryptAdvanced(raw, pwd); - downloadFile(encryptedMessage, f.files[0].name + ".enc"); + const raw = new Uint8Array(e.target.result); + const base64Raw = btoa(String.fromCharCode(...raw)); + const encrypted = await encryptAdvanced(base64Raw, pwd); + downloadFile(encrypted, f.files[0].name + ".enc"); }; - reader.readAsText(f.files[0]); + reader.readAsArrayBuffer(f.files[0]); } function decryptFile() { const f = document.getElementById("file-input"); - const pwd = document.getElementById("file-password").value; + const pwd = document.getElementById("password").value; if (!pwd) return alert("Please enter a password!"); if (!f.files.length) return alert("Please select a file!"); + const reader = new FileReader(); reader.onload = async (e) => { try { - const enc = e.target.result; - const decryptedMessage = await decryptAdvanced(enc, pwd); - downloadFile(decryptedMessage, f.files[0].name.replace(/\.enc$/, '')); - } catch { + const encryptedText = e.target.result; + const decryptedBase64 = await decryptAdvanced(encryptedText, pwd); + const byteString = atob(decryptedBase64); + const byteArray = new Uint8Array([...byteString].map(c => c.charCodeAt(0))); + downloadFileBinary(byteArray, f.files[0].name.replace(/\.enc$/, '')); + } catch (err) { alert("Decryption failed: wrong password or corrupted file."); } }; @@ -225,6 +186,16 @@ function downloadFile(content, filename) { URL.revokeObjectURL(url); } +function downloadFileBinary(byteArray, filename) { + const blob = new Blob([byteArray], { type: "application/octet-stream" }); + const url = URL.createObjectURL(blob); + const a = document.createElement("a"); + a.href = url; + a.download = filename; + a.click(); + URL.revokeObjectURL(url); +} + // ===== Password Generator ===== function generateRandomPassword() { const length = 30; @@ -240,13 +211,12 @@ function generateRandomPassword() { function copyToClipboard(elementId, toastId) { const copyText = document.getElementById(elementId); copyText.select(); - copyText.setSelectionRange(0, 99999); // For mobile devices + copyText.setSelectionRange(0, 99999); document.execCommand("copy"); - // Show toast notification const toast = document.getElementById(toastId); toast.classList.add("show"); - setTimeout(() => toast.classList.remove("show"), 2000); // Remove toast after 2 seconds + setTimeout(() => toast.classList.remove("show"), 2000); } // ===== Pacman Easter Egg ===== @@ -277,6 +247,28 @@ function resetGame() { startPacman(); } +// ===== Clear All ===== +function clearAll() { + document.getElementById("input-text").value = ""; + document.getElementById("output-text").value = ""; + document.getElementById("file-input").value = ""; + document.getElementById("password").value = ""; + + document.getElementById("pacman-section").style.display = "none"; + document.getElementById("encoding-section").style.display = "block"; + + removeFile(); + toggleInputMode(); +} + +// ===== Initialize ===== +document.addEventListener("DOMContentLoaded", () => { + toggleEncryptionOptions(); + toggleInputMode(); + document.getElementById("input-text").addEventListener("input", checkForPacman); +}); + + // ===== Pacman Game Variables & Logic ===== let canvas, ctx, pacman, enemy, walls, dots, score; let pacmanSpeed = 40, enemySpeed = 20, cellSize = 40, dotSize = 5; @@ -441,18 +433,27 @@ function drawChar(ch,color) { } function eatDots() { - dots = dots.filter(d=>{ - const dx = d.c*cellSize+cellSize/2, dy = d.r*cellSize+cellSize/2; - if (Math.abs(pacman.x-dx) { + const dx = d.c * cellSize + cellSize / 2; + const dy = d.r * cellSize + cellSize / 2; + if (Math.abs(pacman.x - dx) < pacman.size && Math.abs(pacman.y - dy) < pacman.size) { score++; - return false; + if (chompSound) { + chompSound.currentTime = 0; // Reset sound + chompSound.volume = 0.4; + chompSound.play(); + } + return false; // Remove dot } return true; }); - ctx.fillStyle="white"; - dots.forEach(d=>{ + + ctx.fillStyle = "white"; + dots.forEach(d => { ctx.beginPath(); - ctx.arc(d.c*cellSize+cellSize/2, d.r*cellSize+cellSize/2, dotSize,0,Math.PI*2); + ctx.arc(d.c * cellSize + cellSize / 2, d.r * cellSize + cellSize / 2, dotSize, 0, Math.PI * 2); ctx.fill(); }); } @@ -471,26 +472,4 @@ function checkGameOver() { ctx.fillText("Game Over!", canvas.width/2, canvas.height/2); clearInterval(gameInterval); } -} - -// ===== Clear All Functionality ===== -function clearAll() { - document.getElementById("input-text").value = ""; - document.getElementById("output-text").value = ""; - document.getElementById("file-input").value = ""; - document.getElementById("password").value = ""; - document.getElementById("file-password").value = ""; - - document.getElementById("pacman-section").style.display = "none"; - document.getElementById("encoding-section").style.display = "block"; - - removeFile(); - toggleInputMode(); -} - -// ===== Initialize ===== -document.addEventListener("DOMContentLoaded", () => { - toggleEncryptionOptions(); - toggleInputMode(); - document.getElementById("input-text").addEventListener("input", checkForPacman); -}); +} \ No newline at end of file diff --git a/templates/403.html b/templates/403.html new file mode 100644 index 0000000..7b5ca2b --- /dev/null +++ b/templates/403.html @@ -0,0 +1,42 @@ + + + + + + 403 - PacCrypt + + + + + + +
+

PacCrypt

+

Secure Encoding, Encryption and Password Generation

+
+ +
+
+

403 - Forbidden

+

+ Looks like this area is locked behind a secret ghost door! đŸ›Ąī¸đŸ‘ģ +

+ + +
+
+ + + + + diff --git a/templates/404.html b/templates/404.html new file mode 100644 index 0000000..2eb2f41 --- /dev/null +++ b/templates/404.html @@ -0,0 +1,42 @@ + + + + + + 404 - PacCrypt + + + + + + +
+

PacCrypt

+

Secure Encoding, Encryption and Password Generation

+
+ +
+ +
+

404 - Page Not Found

+

Oops! The page you're looking for doesn't exist.

+ + +
+ +
+ + + + + diff --git a/templates/500.html b/templates/500.html new file mode 100644 index 0000000..a2dc67c --- /dev/null +++ b/templates/500.html @@ -0,0 +1,42 @@ + + + + + + 500 - PacCrypt + + + + + + +
+

PacCrypt

+

Secure Encoding, Encryption and Password Generation

+
+ +
+
+

500 - Server Error

+

+ Uh oh! The ghosts chomped the server. đŸ§Ÿâ€â™‚ī¸ +

+ + +
+
+ + + + + diff --git a/templates/index.html b/templates/index.html index 88a2754..b733739 100644 --- a/templates/index.html +++ b/templates/index.html @@ -4,29 +4,31 @@ PacCrypt - - + + + + + + +

PacCrypt

Secure Encoding, Encryption and Password Generation

+ +

Password Generator

- +
@@ -35,6 +37,7 @@
+ +

Text Encoder / Decoder & File Encryption

+ + +
+
- -
- -
+
+ +
+ +
+ + -
+
+ +