clean-paste-10 / index.html
C50BARZ's picture
Add 2 files
ba607be verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Clean Paste - Remove and Format Text</title>
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
darkMode: 'class',
theme: {
extend: {
animation: {
'fade-in': 'fadeIn 0.3s ease-in-out',
'fade-out': 'fadeOut 0.3s ease-in-out',
'pulse-slow': 'pulse 3s infinite',
},
keyframes: {
fadeIn: {
'0%': { opacity: '0' },
'100%': { opacity: '1' },
},
fadeOut: {
'0%': { opacity: '1' },
'100%': { opacity: '0' },
}
}
}
}
}
</script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
.text-area-container {
position: relative;
transition: all 0.3s ease;
}
.text-area-container:focus-within {
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.5);
}
.paste-btn {
transition: all 0.2s ease;
}
.paste-btn:hover {
transform: translateY(-1px);
}
.copy-btn {
transition: all 0.2s ease;
}
.copy-btn:hover {
transform: translateY(-1px);
}
.format-btn {
transition: all 0.2s ease;
}
.format-btn:hover {
transform: translateY(-1px);
}
.character-count {
font-variant-numeric: tabular-nums;
}
.tooltip {
position: absolute;
top: -40px;
left: 50%;
transform: translateX(-50%);
background-color: #333;
color: white;
padding: 5px 10px;
border-radius: 4px;
font-size: 12px;
opacity: 0;
transition: opacity 0.3s;
pointer-events: none;
white-space: nowrap;
}
.tooltip:after {
content: "";
position: absolute;
top: 100%;
left: 50%;
margin-left: -5px;
border-width: 5px;
border-style: solid;
border-color: #333 transparent transparent transparent;
}
.show-tooltip {
opacity: 1;
}
.active-format {
background-color: #3b82f6 !important;
color: white !important;
}
.dark .text-area-container {
background-color: #1e293b;
border-color: #334155;
}
.dark .text-area-container textarea {
background-color: #1e293b;
border-color: #334155;
color: #f8fafc;
}
.dark .text-area-container textarea::placeholder {
color: #64748b;
}
.dark .format-btn {
background-color: #334155;
color: #e2e8f0;
}
.dark .format-btn:hover {
background-color: #475569;
}
.dark .bg-gray-50 {
background-color: #1e293b;
}
.dark .bg-white {
background-color: #0f172a;
}
.dark .text-gray-600 {
color: #94a3b8;
}
.dark .text-gray-500 {
color: #94a3b8;
}
.dark .text-gray-700 {
color: #e2e8f0;
}
.dark .border-gray-200 {
border-color: #334155;
}
.code-btn {
background-color: #f59e0b;
color: white;
}
.code-btn:hover {
background-color: #d97706;
}
.dark .code-btn {
background-color: #92400e;
}
.dark .code-btn:hover {
background-color: #7c2d12;
}
.analysis-panel {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease-out;
}
.analysis-panel.open {
max-height: 300px;
}
.word-frequency-item {
transition: all 0.2s ease;
}
.word-frequency-item:hover {
transform: translateX(3px);
}
.find-replace-panel {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease-out;
}
.find-replace-panel.open {
max-height: 200px;
}
.highlight {
background-color: rgba(255, 255, 0, 0.4);
}
.current-highlight {
background-color: rgba(255, 165, 0, 0.6);
animation: pulse 1.5s infinite;
}
@keyframes pulse {
0% { background-color: rgba(255, 165, 0, 0.6); }
50% { background-color: rgba(255, 165, 0, 0.3); }
100% { background-color: rgba(255, 165, 0, 0.6); }
}
.speech-controls {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease-out;
}
.speech-controls.open {
max-height: 120px;
}
.voice-active {
animation: pulse-slow 2s infinite;
}
.progress-bar {
height: 4px;
background-color: #e5e7eb;
border-radius: 2px;
overflow: hidden;
}
.progress-bar-fill {
height: 100%;
background-color: #3b82f6;
transition: width 0.1s linear;
}
</style>
</head>
<body class="bg-gray-100 dark:bg-gray-900 min-h-screen flex flex-col items-center justify-center p-4 transition-colors duration-200">
<div class="w-full max-w-3xl bg-white dark:bg-slate-800 rounded-xl shadow-lg overflow-hidden transition-colors duration-200">
<div class="bg-blue-600 p-4">
<div class="flex items-center justify-between">
<div class="flex items-center space-x-2">
<i class="fas fa-broom text-white text-2xl"></i>
<h1 class="text-white text-2xl font-bold">Clean Paste</h1>
</div>
<div class="flex items-center space-x-4">
<span class="text-blue-100 text-sm hidden md:block">Paste → Clean → Format → Copy</span>
<button id="theme-toggle" class="text-blue-100 hover:text-white focus:outline-none">
<i class="fas fa-moon" id="theme-icon"></i>
</button>
</div>
</div>
<p class="text-blue-100 mt-1 text-sm">Remove formatting and apply new styles to your text</p>
</div>
<div class="p-6">
<div class="text-area-container bg-gray-50 dark:bg-slate-700 rounded-lg border border-gray-200 dark:border-slate-600 p-4">
<div class="flex justify-between items-center mb-2">
<label for="clean-text" class="text-gray-600 dark:text-gray-300 font-medium">Paste your formatted text here:</label>
<div class="flex items-center space-x-2">
<span class="text-xs text-gray-500 dark:text-gray-400 character-count">0 characters</span>
<button id="paste-btn" class="paste-btn bg-blue-100 hover:bg-blue-200 dark:bg-blue-900 dark:hover:bg-blue-800 text-blue-700 dark:text-blue-200 px-3 py-1 rounded-md text-sm flex items-center">
<i class="fas fa-paste mr-1"></i> Paste
</button>
</div>
</div>
<textarea
id="clean-text"
class="w-full h-64 p-3 bg-white dark:bg-slate-700 border border-gray-300 dark:border-slate-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent resize-none dark:text-white"
placeholder="Paste your formatted text here (or click the Paste button above)..."
spellcheck="false"
></textarea>
</div>
<!-- Find & Replace Panel -->
<div id="find-replace-panel" class="find-replace-panel bg-gray-50 dark:bg-slate-700 rounded-lg mt-4 p-4 border border-gray-200 dark:border-slate-600">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Find:</label>
<div class="flex">
<input type="text" id="find-input" class="flex-1 p-2 border border-gray-300 dark:border-slate-600 rounded-l-md dark:bg-slate-600 dark:text-white focus:outline-none focus:ring-2 focus:ring-blue-500">
<button id="find-prev-btn" class="bg-gray-200 dark:bg-slate-600 px-3 border-t border-b border-gray-300 dark:border-slate-600">
<i class="fas fa-chevron-up"></i>
</button>
<button id="find-next-btn" class="bg-gray-200 dark:bg-slate-600 px-3 border border-gray-300 dark:border-slate-600 rounded-r-md">
<i class="fas fa-chevron-down"></i>
</button>
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Replace with:</label>
<div class="flex">
<input type="text" id="replace-input" class="flex-1 p-2 border border-gray-300 dark:border-slate-600 rounded-l-md dark:bg-slate-600 dark:text-white focus:outline-none focus:ring-2 focus:ring-blue-500">
<button id="replace-btn" class="bg-blue-500 text-white px-3 border border-blue-500 rounded-r-md hover:bg-blue-600">
Replace
</button>
</div>
</div>
</div>
<div class="flex justify-between items-center mt-3">
<div class="flex items-center space-x-2">
<input type="checkbox" id="case-sensitive" class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded dark:bg-slate-600 dark:border-slate-500">
<label for="case-sensitive" class="text-sm text-gray-700 dark:text-gray-300">Case sensitive</label>
</div>
<div class="text-sm text-gray-500 dark:text-gray-400">
<span id="find-results">0 matches</span>
</div>
</div>
</div>
<!-- Text to Speech Panel -->
<div id="speech-panel" class="speech-controls bg-gray-50 dark:bg-slate-700 rounded-lg mt-4 p-4 border border-gray-200 dark:border-slate-600">
<div class="flex flex-col md:flex-row md:items-center md:justify-between gap-4">
<div class="flex-1">
<div class="flex items-center space-x-3 mb-2">
<label class="text-sm font-medium text-gray-700 dark:text-gray-300">Voice:</label>
<select id="voice-select" class="flex-1 p-2 border border-gray-300 dark:border-slate-600 rounded-md dark:bg-slate-600 dark:text-white focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm">
<option value="">Loading voices...</option>
</select>
</div>
<div class="grid grid-cols-2 gap-3">
<div>
<label class="text-sm font-medium text-gray-700 dark:text-gray-300 block mb-1">Rate:</label>
<input type="range" id="rate-control" min="0.5" max="2" step="0.1" value="1" class="w-full">
<div class="text-xs text-gray-500 dark:text-gray-400 text-center" id="rate-value">1.0</div>
</div>
<div>
<label class="text-sm font-medium text-gray-700 dark:text-gray-300 block mb-1">Pitch:</label>
<input type="range" id="pitch-control" min="0.5" max="2" step="0.1" value="1" class="w-full">
<div class="text-xs text-gray-500 dark:text-gray-400 text-center" id="pitch-value">1.0</div>
</div>
</div>
</div>
<div class="flex flex-col items-center justify-center space-y-2">
<div class="flex space-x-3">
<button id="speak-btn" class="bg-green-500 hover:bg-green-600 text-white px-4 py-2 rounded-full flex items-center">
<i class="fas fa-play mr-2"></i> Speak
</button>
<button id="pause-btn" class="bg-yellow-500 hover:bg-yellow-600 text-white px-4 py-2 rounded-full flex items-center">
<i class="fas fa-pause mr-2"></i> Pause
</button>
<button id="stop-btn" class="bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-full flex items-center">
<i class="fas fa-stop mr-2"></i> Stop
</button>
</div>
<div class="progress-bar w-full max-w-xs">
<div id="progress-bar-fill" class="progress-bar-fill" style="width: 0%"></div>
</div>
</div>
</div>
</div>
<!-- Text Analysis Panel -->
<div id="analysis-panel" class="analysis-panel bg-gray-50 dark:bg-slate-700 rounded-lg mt-4 p-4 border border-gray-200 dark:border-slate-600 overflow-y-auto">
<h3 class="font-medium text-gray-700 dark:text-gray-300 mb-3">Text Analysis</h3>
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-4">
<div class="bg-white dark:bg-slate-600 p-3 rounded-lg shadow">
<div class="text-sm text-gray-500 dark:text-gray-400">Characters</div>
<div class="text-xl font-bold text-gray-800 dark:text-white" id="analysis-chars">0</div>
</div>
<div class="bg-white dark:bg-slate-600 p-3 rounded-lg shadow">
<div class="text-sm text-gray-500 dark:text-gray-400">Words</div>
<div class="text-xl font-bold text-gray-800 dark:text-white" id="analysis-words">0</div>
</div>
<div class="bg-white dark:bg-slate-600 p-3 rounded-lg shadow">
<div class="text-sm text-gray-500 dark:text-gray-400">Sentences</div>
<div class="text-xl font-bold text-gray-800 dark:text-white" id="analysis-sentences">0</div>
</div>
<div class="bg-white dark:bg-slate-600 p-3 rounded-lg shadow">
<div class="text-sm text-gray-500 dark:text-gray-400">Reading Time</div>
<div class="text-xl font-bold text-gray-800 dark:text-white" id="analysis-reading-time">0 min</div>
</div>
</div>
<div>
<h4 class="font-medium text-gray-700 dark:text-gray-300 mb-2">Word Frequency</h4>
<div class="max-h-32 overflow-y-auto" id="word-frequency-list">
<div class="text-center text-gray-500 dark:text-gray-400 py-2">No words to analyze</div>
</div>
</div>
</div>
<div class="mt-4">
<div class="flex flex-wrap gap-2 mb-4">
<button id="lowercase-btn" class="format-btn bg-gray-200 hover:bg-gray-300 dark:bg-slate-600 dark:hover:bg-slate-500 text-gray-700 dark:text-gray-200 px-3 py-1 rounded-md text-sm flex items-center">
<i class="fas fa-text-height mr-1"></i> lowercase
</button>
<button id="uppercase-btn" class="format-btn bg-gray-200 hover:bg-gray-300 dark:bg-slate-600 dark:hover:bg-slate-500 text-gray-700 dark:text-gray-200 px-3 py-1 rounded-md text-sm flex items-center">
<i class="fas fa-text-height mr-1 transform rotate-180"></i> UPPERCASE
</button>
<button id="bold-btn" class="format-btn bg-gray-200 hover:bg-gray-300 dark:bg-slate-600 dark:hover:bg-slate-500 text-gray-700 dark:text-gray-200 px-3 py-1 rounded-md text-sm flex items-center">
<i class="fas fa-bold mr-1"></i> Bold
</button>
<button id="italic-btn" class="format-btn bg-gray-200 hover:bg-gray-300 dark:bg-slate-600 dark:hover:bg-slate-500 text-gray-700 dark:text-gray-200 px-3 py-1 rounded-md text-sm flex items-center">
<i class="fas fa-italic mr-1"></i> Italic
</button>
<button id="underline-btn" class="format-btn bg-gray-200 hover:bg-gray-300 dark:bg-slate-600 dark:hover:bg-slate-500 text-gray-700 dark:text-gray-200 px-3 py-1 rounded-md text-sm flex items-center">
<i class="fas fa-underline mr-1"></i> Underline
</button>
<button id="capitalize-btn" class="format-btn bg-gray-200 hover:bg-gray-300 dark:bg-slate-600 dark:hover:bg-slate-500 text-gray-700 dark:text-gray-200 px-3 py-1 rounded-md text-sm flex items-center">
<i class="fas fa-paragraph mr-1"></i> Capitalize
</button>
<button id="reverse-btn" class="format-btn bg-gray-200 hover:bg-gray-300 dark:bg-slate-600 dark:hover:bg-slate-500 text-gray-700 dark:text-gray-200 px-3 py-1 rounded-md text-sm flex items-center">
<i class="fas fa-exchange-alt mr-1"></i> Reverse
</button>
<button id="encode-btn" class="format-btn code-btn px-3 py-1 rounded-md text-sm flex items-center">
<i class="fas fa-lock mr-1"></i> Encode
</button>
<button id="decode-btn" class="format-btn code-btn px-3 py-1 rounded-md text-sm flex items-center">
<i class="fas fa-lock-open mr-1"></i> Decode
</button>
<button id="remove-format-btn" class="format-btn bg-red-100 hover:bg-red-200 dark:bg-red-900 dark:hover:bg-red-800 text-red-700 dark:text-red-200 px-3 py-1 rounded-md text-sm flex items-center">
<i class="fas fa-eraser mr-1"></i> Remove Format
</button>
<button id="find-replace-btn" class="format-btn bg-purple-100 hover:bg-purple-200 dark:bg-purple-900 dark:hover:bg-purple-800 text-purple-700 dark:text-purple-200 px-3 py-1 rounded-md text-sm flex items-center">
<i class="fas fa-search mr-1"></i> Find & Replace
</button>
<button id="analyze-btn" class="format-btn bg-green-100 hover:bg-green-200 dark:bg-green-900 dark:hover:bg-green-800 text-green-700 dark:text-green-200 px-3 py-1 rounded-md text-sm flex items-center">
<i class="fas fa-chart-bar mr-1"></i> Analyze Text
</button>
<button id="speak-toggle-btn" class="format-btn bg-indigo-100 hover:bg-indigo-200 dark:bg-indigo-900 dark:hover:bg-indigo-800 text-indigo-700 dark:text-indigo-200 px-3 py-1 rounded-md text-sm flex items-center">
<i class="fas fa-volume-up mr-1"></i> Text to Speech
</button>
</div>
<div class="flex justify-between items-center">
<div class="flex items-center space-x-2">
<button id="clear-btn" class="bg-gray-200 hover:bg-gray-300 dark:bg-slate-600 dark:hover:bg-slate-500 text-gray-700 dark:text-gray-200 px-4 py-2 rounded-md flex items-center">
<i class="fas fa-trash-alt mr-2"></i> Clear
</button>
<button id="copy-btn" class="copy-btn bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-md flex items-center relative">
<i class="fas fa-copy mr-2"></i> Copy Text
<span class="tooltip">Copied to clipboard!</span>
</button>
</div>
<div class="text-xs text-gray-500 dark:text-gray-400">
<span id="word-count">0 words</span>
</div>
</div>
</div>
</div>
<div class="bg-gray-50 dark:bg-slate-700 p-4 border-t border-gray-200 dark:border-slate-600">
<div class="flex flex-wrap items-center justify-center gap-4 text-sm text-gray-600 dark:text-gray-300">
<div class="flex items-center">
<i class="fas fa-info-circle mr-1"></i>
<span>Formats removed: bold, italic, colors, fonts, etc.</span>
</div>
<div class="flex items-center">
<i class="fas fa-keyboard mr-1"></i>
<span>Shortcut: Ctrl+V to paste, Ctrl+C to copy</span>
</div>
</div>
</div>
</div>
<div class="mt-8 text-center text-gray-500 dark:text-gray-400 text-sm">
<p>Clean Paste - Remove formatting and apply new styles to your text</p>
</div>
<audio id="click-sound" preload="auto">
<source src="https://electronicenergysource.com/entirelibofsounds/Salvaged%20Files/1001%20Sound%20Effects/Video%20Game%20Sounds/Arcade%20Beep%2001.wav" type="audio/wav">
</audio>
<script>
document.addEventListener('DOMContentLoaded', function() {
const cleanTextArea = document.getElementById('clean-text');
const pasteBtn = document.getElementById('paste-btn');
const copyBtn = document.getElementById('copy-btn');
const clearBtn = document.getElementById('clear-btn');
const charCount = document.querySelector('.character-count');
const wordCount = document.getElementById('word-count');
const tooltip = document.querySelector('.tooltip');
const clickSound = document.getElementById('click-sound');
const themeToggle = document.getElementById('theme-toggle');
const themeIcon = document.getElementById('theme-icon');
// Format buttons
const lowercaseBtn = document.getElementById('lowercase-btn');
const uppercaseBtn = document.getElementById('uppercase-btn');
const boldBtn = document.getElementById('bold-btn');
const italicBtn = document.getElementById('italic-btn');
const underlineBtn = document.getElementById('underline-btn');
const capitalizeBtn = document.getElementById('capitalize-btn');
const reverseBtn = document.getElementById('reverse-btn');
const encodeBtn = document.getElementById('encode-btn');
const decodeBtn = document.getElementById('decode-btn');
const removeFormatBtn = document.getElementById('remove-format-btn');
// New feature buttons
const findReplaceBtn = document.getElementById('find-replace-btn');
const analyzeBtn = document.getElementById('analyze-btn');
const speakToggleBtn = document.getElementById('speak-toggle-btn');
const findReplacePanel = document.getElementById('find-replace-panel');
const analysisPanel = document.getElementById('analysis-panel');
const speechPanel = document.getElementById('speech-panel');
// Find & Replace elements
const findInput = document.getElementById('find-input');
const replaceInput = document.getElementById('replace-input');
const findNextBtn = document.getElementById('find-next-btn');
const findPrevBtn = document.getElementById('find-prev-btn');
const replaceBtn = document.getElementById('replace-btn');
const caseSensitiveCheckbox = document.getElementById('case-sensitive');
const findResults = document.getElementById('find-results');
// Analysis elements
const analysisChars = document.getElementById('analysis-chars');
const analysisWords = document.getElementById('analysis-words');
const analysisSentences = document.getElementById('analysis-sentences');
const analysisReadingTime = document.getElementById('analysis-reading-time');
const wordFrequencyList = document.getElementById('word-frequency-list');
// Speech synthesis elements
const voiceSelect = document.getElementById('voice-select');
const rateControl = document.getElementById('rate-control');
const pitchControl = document.getElementById('pitch-control');
const rateValue = document.getElementById('rate-value');
const pitchValue = document.getElementById('pitch-value');
const speakBtn = document.getElementById('speak-btn');
const pauseBtn = document.getElementById('pause-btn');
const stopBtn = document.getElementById('stop-btn');
const progressBarFill = document.getElementById('progress-bar-fill');
// Variables for find & replace
let currentFindIndex = -1;
let findMatches = [];
// Variables for speech synthesis
let speechSynthesis = window.speechSynthesis;
let speechUtterance = null;
let voices = [];
let isSpeaking = false;
let isPaused = false;
let speechProgressInterval;
// Play click sound
function playClickSound() {
clickSound.currentTime = 0;
clickSound.play().catch(e => console.log("Audio play failed:", e));
}
// Theme toggle
function toggleTheme() {
if (document.documentElement.classList.contains('dark')) {
document.documentElement.classList.remove('dark');
localStorage.setItem('theme', 'light');
themeIcon.classList.remove('fa-sun');
themeIcon.classList.add('fa-moon');
} else {
document.documentElement.classList.add('dark');
localStorage.setItem('theme', 'dark');
themeIcon.classList.remove('fa-moon');
themeIcon.classList.add('fa-sun');
}
playClickSound();
}
// Check for saved theme preference
if (localStorage.getItem('theme') === 'dark' ||
(!localStorage.getItem('theme') && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.documentElement.classList.add('dark');
themeIcon.classList.remove('fa-moon');
themeIcon.classList.add('fa-sun');
}
themeToggle.addEventListener('click', toggleTheme);
// Update character and word count
function updateCounts() {
const text = cleanTextArea.value;
charCount.textContent = `${text.length} characters`;
const words = text.trim() === '' ? 0 : text.trim().split(/\s+/).length;
wordCount.textContent = `${words} words`;
}
// Handle paste from button
pasteBtn.addEventListener('click', async function() {
playClickSound();
try {
const text = await navigator.clipboard.readText();
cleanTextArea.value = text;
updateCounts();
// Show success feedback
pasteBtn.innerHTML = '<i class="fas fa-check mr-1"></i> Pasted!';
pasteBtn.classList.remove('bg-blue-100', 'text-blue-700', 'dark:bg-blue-900', 'dark:text-blue-200');
pasteBtn.classList.add('bg-green-100', 'text-green-700', 'dark:bg-green-900', 'dark:text-green-200');
setTimeout(() => {
pasteBtn.innerHTML = '<i class="fas fa-paste mr-1"></i> Paste';
pasteBtn.classList.remove('bg-green-100', 'text-green-700', 'dark:bg-green-900', 'dark:text-green-200');
pasteBtn.classList.add('bg-blue-100', 'text-blue-700', 'dark:bg-blue-900', 'dark:text-blue-200');
}, 1500);
} catch (err) {
alert('Failed to read clipboard. Please paste manually into the text area.');
console.error('Failed to read clipboard contents:', err);
}
});
// Handle copy button
copyBtn.addEventListener('click', function() {
playClickSound();
if (cleanTextArea.value.trim() === '') {
alert('Nothing to copy! Please paste some text first.');
return;
}
navigator.clipboard.writeText(cleanTextArea.value)
.then(() => {
// Show tooltip
tooltip.classList.add('show-tooltip');
setTimeout(() => {
tooltip.classList.remove('show-tooltip');
}, 2000);
})
.catch(err => {
console.error('Failed to copy text: ', err);
alert('Failed to copy text. Please try again.');
});
});
// Handle clear button
clearBtn.addEventListener('click', function() {
playClickSound();
cleanTextArea.value = '';
updateCounts();
clearHighlights();
findMatches = [];
currentFindIndex = -1;
findResults.textContent = '0 matches';
// Stop any ongoing speech
stopSpeech();
});
// Formatting functions
function applyFormat(formatFn) {
if (cleanTextArea.value.trim() === '') {
alert('No text to format! Please paste some text first.');
return;
}
const startPos = cleanTextArea.selectionStart;
const endPos = cleanTextArea.selectionEnd;
if (startPos === endPos) {
// No selection - format entire text
cleanTextArea.value = formatFn(cleanTextArea.value);
} else {
// Format only selected text
const selectedText = cleanTextArea.value.substring(startPos, endPos);
const formattedText = formatFn(selectedText);
cleanTextArea.value = cleanTextArea.value.substring(0, startPos) +
formattedText +
cleanTextArea.value.substring(endPos);
}
updateCounts();
}
// Base64 encode function
function encodeBase64(text) {
return btoa(unescape(encodeURIComponent(text)));
}
// Base64 decode function
function decodeBase64(text) {
try {
return decodeURIComponent(escape(atob(text)));
} catch (e) {
alert("Invalid Base64 encoded string!");
return text;
}
}
// Format button handlers
lowercaseBtn.addEventListener('click', function() {
playClickSound();
applyFormat(text => text.toLowerCase());
highlightActiveButton(lowercaseBtn);
});
uppercaseBtn.addEventListener('click', function() {
playClickSound();
applyFormat(text => text.toUpperCase());
highlightActiveButton(uppercaseBtn);
});
boldBtn.addEventListener('click', function() {
playClickSound();
applyFormat(text => {
const startPos = cleanTextArea.selectionStart;
const endPos = cleanTextArea.selectionEnd;
if (startPos === endPos) {
return text.replace(/([^\n])([^\n]+)?/g, function(match, p1, p2) {
return p1 + (p2 ? '**' + p2 + '**' : '');
});
} else {
return '**' + text + '**';
}
});
highlightActiveButton(boldBtn);
});
italicBtn.addEventListener('click', function() {
playClickSound();
applyFormat(text => {
const startPos = cleanTextArea.selectionStart;
const endPos = cleanTextArea.selectionEnd;
if (startPos === endPos) {
return text.replace(/([^\n])([^\n]+)?/g, function(match, p1, p2) {
return p1 + (p2 ? '_' + p2 + '_' : '');
});
} else {
return '_' + text + '_';
}
});
highlightActiveButton(italicBtn);
});
underlineBtn.addEventListener('click', function() {
playClickSound();
applyFormat(text => {
const startPos = cleanTextArea.selectionStart;
const endPos = cleanTextArea.selectionEnd;
if (startPos === endPos) {
return text.replace(/([^\n])([^\n]+)?/g, function(match, p1, p2) {
return p1 + (p2 ? '<u>' + p2 + '</u>' : '');
});
} else {
return '<u>' + text + '</u>';
}
});
highlightActiveButton(underlineBtn);
});
capitalizeBtn.addEventListener('click', function() {
playClickSound();
applyFormat(text => {
return text.toLowerCase().replace(/(^|\s)\S/g, function(firstLetter) {
return firstLetter.toUpperCase();
});
});
highlightActiveButton(capitalizeBtn);
});
reverseBtn.addEventListener('click', function() {
playClickSound();
applyFormat(text => {
return text.split('').reverse().join('');
});
highlightActiveButton(reverseBtn);
});
encodeBtn.addEventListener('click', function() {
playClickSound();
applyFormat(text => {
return encodeBase64(text);
});
highlightActiveButton(encodeBtn);
});
decodeBtn.addEventListener('click', function() {
playClickSound();
applyFormat(text => {
return decodeBase64(text);
});
highlightActiveButton(decodeBtn);
});
removeFormatBtn.addEventListener('click', function() {
playClickSound();
applyFormat(text => {
// Remove markdown formatting
let cleaned = text.replace(/(\*\*|__)(.*?)\1/g, '$2'); // bold
cleaned = cleaned.replace(/(\*|_)(.*?)\1/g, '$2'); // italic
cleaned = cleaned.replace(/<u>(.*?)<\/u>/g, '$1'); // underline
cleaned = cleaned.replace(/<[^>]+>/g, ''); // any HTML tags
return cleaned;
});
highlightActiveButton(removeFormatBtn);
});
// Highlight active format button
function highlightActiveButton(button) {
// Remove active class from all format buttons
document.querySelectorAll('.format-btn').forEach(btn => {
if (!btn.classList.contains('code-btn') &&
!btn.classList.contains('bg-purple-100') &&
!btn.classList.contains('bg-green-100') &&
!btn.classList.contains('bg-indigo-100')) {
btn.classList.remove('active-format');
btn.classList.add('bg-gray-200', 'text-gray-700', 'dark:bg-slate-600', 'dark:text-gray-200');
btn.classList.remove('bg-blue-600', 'text-white');
}
});
// Add active class to clicked button
if (button.classList.contains('code-btn')) {
button.classList.add('bg-yellow-600', 'dark:bg-yellow-700');
} else if (button.classList.contains('bg-purple-100')) {
button.classList.add('bg-purple-600', 'text-white');
} else if (button.classList.contains('bg-green-100')) {
button.classList.add('bg-green-600', 'text-white');
} else if (button.classList.contains('bg-indigo-100')) {
button.classList.add('bg-indigo-600', 'text-white');
} else {
button.classList.add('active-format');
button.classList.remove('bg-gray-200', 'text-gray-700', 'dark:bg-slate-600', 'dark:text-gray-200');
button.classList.add('bg-blue-600', 'text-white');
}
// Remove highlight after 1.5 seconds
setTimeout(() => {
if (button.classList.contains('code-btn')) {
button.classList.remove('bg-yellow-600', 'dark:bg-yellow-700');
} else if (button.classList.contains('bg-purple-100')) {
button.classList.remove('bg-purple-600', 'text-white');
} else if (button.classList.contains('bg-green-100')) {
button.classList.remove('bg-green-600', 'text-white');
} else if (button.classList.contains('bg-indigo-100')) {
button.classList.remove('bg-indigo-600', 'text-white');
} else {
button.classList.remove('active-format');
button.classList.remove('bg-blue-600', 'text-white');
button.classList.add('bg-gray-200', 'text-gray-700', 'dark:bg-slate-600', 'dark:text-gray-200');
}
}, 1500);
}
// Toggle find & replace panel
findReplaceBtn.addEventListener('click', function() {
playClickSound();
analysisPanel.classList.remove('open');
speechPanel.classList.remove('open');
findReplacePanel.classList.toggle('open');
if (findReplacePanel.classList.contains('open')) {
highlightActiveButton(findReplaceBtn);
findInput.focus();
}
});
// Toggle analysis panel
analyzeBtn.addEventListener('click', function() {
playClickSound();
findReplacePanel.classList.remove('open');
speechPanel.classList.remove('open');
analysisPanel.classList.toggle('open');
if (analysisPanel.classList.contains('open')) {
highlightActiveButton(analyzeBtn);
analyzeText();
}
});
// Toggle speech panel
speakToggleBtn.addEventListener('click', function() {
playClickSound();
findReplacePanel.classList.remove('open');
analysisPanel.classList.remove('open');
speechPanel.classList.toggle('open');
if (speechPanel.classList.contains('open')) {
highlightActiveButton(speakToggleBtn);
loadVoices();
}
});
// Analyze text function
function analyzeText() {
const text = cleanTextArea.value;
// Basic stats
analysisChars.textContent = text.length;
const words = text.trim() === '' ? 0 : text.trim().split(/\s+/).length;
analysisWords.textContent = words;
// Count sentences (very basic implementation)
const sentences = text.split(/[.!?]+/).filter(s => s.trim().length > 0).length;
analysisSentences.textContent = sentences;
// Calculate reading time (average reading speed: 200 words per minute)
const readingTime = Math.ceil(words / 200);
analysisReadingTime.textContent = readingTime <= 1 ? '1 min' : `${readingTime} mins`;
// Word frequency analysis
if (text.trim() === '') {
wordFrequencyList.innerHTML = '<div class="text-center text-gray-500 dark:text-gray-400 py-2">No words to analyze</div>';
return;
}
// Process words
const wordsArray = text.toLowerCase()
.replace(/[^\w\s]/g, '') // Remove punctuation
.split(/\s+/)
.filter(word => word.length > 0);
// Count word frequency
const wordFrequency = {};
wordsArray.forEach(word => {
wordFrequency[word] = (wordFrequency[word] || 0) + 1;
});
// Sort by frequency
const sortedWords = Object.keys(wordFrequency).sort((a, b) => wordFrequency[b] - wordFrequency[a]);
// Display top 20 words
wordFrequencyList.innerHTML = '';
sortedWords.slice(0, 20).forEach(word => {
const frequency = wordFrequency[word];
const percentage = Math.round((frequency / wordsArray.length) * 100);
const item = document.createElement('div');
item.className = 'word-frequency-item flex justify-between items-center mb-1 p-2 bg-white dark:bg-slate-600 rounded';
const wordSpan = document.createElement('span');
wordSpan.className = 'font-medium dark:text-white';
wordSpan.textContent = word;
const freqSpan = document.createElement('span');
freqSpan.className = 'text-sm text-gray-500 dark:text-gray-300';
freqSpan.textContent = `${frequency} (${percentage}%)`;
item.appendChild(wordSpan);
item.appendChild(freqSpan);
wordFrequencyList.appendChild(item);
});
}
// Find text function
function findText(direction = 'next') {
const searchText = findInput.value;
if (!searchText) {
alert('Please enter text to find');
return;
}
const text = cleanTextArea.value;
const caseSensitive = caseSensitiveCheckbox.checked;
const flags = caseSensitive ? 'g' : 'gi';
// Clear previous highlights
clearHighlights();
// Find all matches
const regex = new RegExp(escapeRegExp(searchText), flags);
findMatches = [];
let match;
while ((match = regex.exec(text)) !== null) {
findMatches.push({
start: match.index,
end: match.index + match[0].length
});
}
findResults.textContent = `${findMatches.length} matches`;
if (findMatches.length === 0) {
alert('No matches found');
return;
}
// Navigate to next/previous match
if (direction === 'next') {
currentFindIndex = (currentFindIndex + 1) % findMatches.length;
} else {
currentFindIndex = (currentFindIndex - 1 + findMatches.length) % findMatches.length;
}
// Highlight all matches and scroll to current one
highlightMatches();
// Scroll to current match
const currentMatch = findMatches[currentFindIndex];
scrollToMatch(currentMatch.start, currentMatch.end);
}
// Helper function to escape regex special characters
function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
// Highlight all matches
function highlightMatches() {
const text = cleanTextArea.value;
let highlightedText = '';
let lastIndex = 0;
findMatches.forEach((match, index) => {
// Add text before match
highlightedText += text.substring(lastIndex, match.start);
// Add highlighted match
const matchClass = index === currentFindIndex ? 'current-highlight': 'highlight';
highlightedText += `<span class="${matchClass}">${text.substring(match.start, match.end)}</span>`;
lastIndex = match.end;
});
// Add remaining text
highlightedText += text.substring(lastIndex);
// Update textarea with highlighted HTML (using a hidden div)
const hiddenDiv = document.createElement('div');
hiddenDiv.style.display = 'none';
hiddenDiv.innerHTML = highlightedText;
document.body.appendChild(hiddenDiv);
// Get plain text with markers for highlights
let plainText = '';
const walker = document.createTreeWalker(hiddenDiv, NodeFilter.SHOW_TEXT, null, false);
let node;
while (node = walker.nextNode()) {
plainText += node.nodeValue;
}
// Remove the hidden div
document.body.removeChild(hiddenDiv);
// Update textarea with plain text
cleanTextArea.value = plainText;
}
// Clear all highlights
function clearHighlights() {
if (findMatches.length > 0) {
const text = cleanTextArea.value;
let plainText = '';
let lastIndex = 0;
findMatches.forEach(match => {
plainText += text.substring(lastIndex, match.start);
plainText += text.substring(match.start, match.end);
lastIndex = match.end;
});
plainText += text.substring(lastIndex);
cleanTextArea.value = plainText;
findMatches = [];
currentFindIndex = -1;
findResults.textContent = '0 matches';
}
}
// Scroll to match position
function scrollToMatch(start, end) {
// Calculate line height (approximate)
const lineHeight = 20; // px
// Calculate number of lines to scroll
const textBefore = cleanTextArea.value.substring(0, start);
const linesBefore = textBefore.split('\n').length - 1;
// Scroll to position
cleanTextArea.scrollTop = linesBefore * lineHeight;
// Set selection
cleanTextArea.focus();
cleanTextArea.setSelectionRange(start, end);
}
// Replace text function
function replaceText() {
if (findMatches.length === 0) {
findText();
return;
}
const replaceText = replaceInput.value;
const currentMatch = findMatches[currentFindIndex];
// Replace current match
const text = cleanTextArea.value;
const newText = text.substring(0, currentMatch.start) + replaceText + text.substring(currentMatch.end);
cleanTextArea.value = newText;
// Adjust positions of remaining matches
const lengthDiff = replaceText.length - (currentMatch.end - currentMatch.start);
findMatches.forEach((match, index) => {
if (match.start > currentMatch.start) {
match.start += lengthDiff;
match.end += lengthDiff;
}
});
// Remove current match from array
findMatches.splice(currentFindIndex, 1);
if (findMatches.length === 0) {
findResults.textContent = '0 matches';
currentFindIndex = -1;
return;
}
// Update current index and highlight
currentFindIndex = Math.min(currentFindIndex, findMatches.length - 1);
findResults.textContent = `${findMatches.length} matches`;
highlightMatches();
}
// Speech Synthesis Functions
function loadVoices() {
// Chrome loads voices asynchronously
voices = speechSynthesis.getVoices();
voiceSelect.innerHTML = '';
if (voices.length === 0) {
voiceSelect.innerHTML = '<option value="">Loading voices...</option>';
// Try again in 1 second if voices aren't loaded yet
setTimeout(loadVoices, 1000);
return;
}
// Filter voices to only include ones with localService (usually system voices)
const systemVoices = voices.filter(voice => voice.localService);
systemVoices.forEach((voice, index) => {
const option = document.createElement('option');
option.value = index;
option.textContent = `${voice.name} (${voice.lang})`;
if (voice.default) {
option.textContent += ' [Default]';
}
voiceSelect.appendChild(option);
});
// Try to select a reasonable default voice
const defaultVoice = systemVoices.find(voice =>
voice.lang.startsWith('en-') && voice.default
) || systemVoices.find(voice => voice.default) || systemVoices[0];
if (defaultVoice) {
const defaultIndex = systemVoices.indexOf(defaultVoice);
voiceSelect.value = defaultIndex;
}
}
function speakText() {
if (isSpeaking) {
pauseSpeech();
return;
}
if (isPaused) {
resumeSpeech();
return;
}
const text = cleanTextArea.value;
if (!text.trim()) {
alert('No text to speak! Please paste some text first.');
return;
}
// Stop any ongoing speech
stopSpeech();
// Create a new utterance
speechUtterance = new SpeechSynthesisUtterance(text);
// Set voice
const selectedVoiceIndex = parseInt(voiceSelect.value);
if (!isNaN(selectedVoiceIndex) && voices[selectedVoiceIndex]) {
speechUtterance.voice = voices[selectedVoiceIndex];
}
// Set rate and pitch
speechUtterance.rate = parseFloat(rateControl.value);
speechUtterance.pitch = parseFloat(pitchControl.value);
// Update UI
isSpeaking = true;
speakBtn.innerHTML = '<i class="fas fa-pause mr-2"></i> Pause';
speakBtn.classList.remove('bg-green-500', 'hover:bg-green-600');
speakBtn.classList.add('bg-yellow-500', 'hover:bg-yellow-600');
speakToggleBtn.classList.add('voice-active');
// Reset progress bar
progressBarFill.style.width = '0%';
// Start progress tracking
const textLength = text.length;
let currentPosition = 0;
speechProgressInterval = setInterval(() => {
if (speechSynthesis.speaking && !isPaused) {
// Estimate position based on time elapsed
const elapsed = (speechUtterance.elapsedTime || 0) * 1000; // ms
const totalDuration = (textLength / (speechUtterance.rate * 8)) * 1000; // rough estimate
const progress = Math.min(100, (elapsed / totalDuration) * 100);
progressBarFill.style.width = `${progress}%`;
}
}, 100);
// Event handlers
speechUtterance.onend = function() {
stopSpeech();
};
speechUtterance.onerror = function(event) {
console.error('SpeechSynthesis error:', event);
stopSpeech();
alert('Error occurred during speech synthesis: ' + event.error);
};
speechUtterance.onboundary = function(event) {
if (event.name === 'word') {
currentPosition = event.charIndex;
}
};
// Speak the text
speechSynthesis.speak(speechUtterance);
}
function pauseSpeech() {
if (isSpeaking && !isPaused) {
speechSynthesis.pause();
isSpeaking = false;
isPaused = true;
speakBtn.innerHTML = '<i class="fas fa-play mr-2"></i> Resume';
speakBtn.classList.remove('bg-yellow-500', 'hover:bg-yellow-600');
speakBtn.classList.add('bg-green-500', 'hover:bg-green-600');
}
}
function resumeSpeech() {
if (isPaused) {
speechSynthesis.resume();
isSpeaking = true;
isPaused = false;
speakBtn.innerHTML = '<i class="fas fa-pause mr-2"></i> Pause';
speakBtn.classList.remove('bg-green-500', 'hover:bg-green-600');
speakBtn.classList.add('bg-yellow-500', 'hover:bg-yellow-600');
}
}
function stopSpeech() {
speechSynthesis.cancel();
isSpeaking = false;
isPaused = false;
// Clear interval
if (speechProgressInterval) {
clearInterval(speechProgressInterval);
speechProgressInterval = null;
}
// Reset UI
speakBtn.innerHTML = '<i class="fas fa-play mr-2"></i> Speak';
speakBtn.classList.remove('bg-yellow-500', 'hover:bg-yellow-600');
speakBtn.classList.add('bg-green-500', 'hover:bg-green-600');
speakToggleBtn.classList.remove('voice-active');
progressBarFill.style.width = '0%';
}
// Initialize speech synthesis
if ('speechSynthesis' in window) {
// Chrome loads voices asynchronously
speechSynthesis.onvoiceschanged = loadVoices;
// Load voices immediately if they're already available
if (speechSynthesis.getVoices().length > 0) {
loadVoices();
}
} else {
// Disable speech features if not supported
speakToggleBtn.disabled = true;
speakToggleBtn.title = 'Text-to-speech not supported in your browser';
}
// Find & Replace event listeners
findNextBtn.addEventListener('click', function() {
playClickSound();
findText('next');
});
findPrevBtn.addEventListener('click', function() {
playClickSound();
findText('prev');
});
replaceBtn.addEventListener('click', function() {
playClickSound();
replaceText();
});
findInput.addEventListener('keydown', function(e) {
if (e.key === 'Enter') {
playClickSound();
findText('next');
}
});
// Speech synthesis event listeners
speakBtn.addEventListener('click', function() {
playClickSound();
speakText();
});
pauseBtn.addEventListener('click', function() {
playClickSound();
pauseSpeech();
});
stopBtn.addEventListener('click', function() {
playClickSound();
stopSpeech();
});
rateControl.addEventListener('input', function() {
rateValue.textContent = this.value;
if (speechUtterance) {
speechUtterance.rate = parseFloat(this.value);
}
});
pitchControl.addEventListener('input', function() {
pitchValue.textContent = this.value;
if (speechUtterance) {
speechUtterance.pitch = parseFloat(this.value);
}
});
// Update counts when typing
cleanTextArea.addEventListener('input', function() {
updateCounts();
// Clear find highlights if text changes
if (findMatches.length > 0) {
clearHighlights();
}
// Stop speech if text changes while speaking
if (isSpeaking || isPaused) {
stopSpeech();
}
});
// Handle direct paste into textarea
cleanTextArea.addEventListener('paste', function(e) {
playClickSound();
// Let the paste happen first
setTimeout(() => {
updateCounts();
// Show feedback
const originalPlaceholder = cleanTextArea.placeholder;
cleanTextArea.placeholder = "✓ Text pasted!";
setTimeout(() => {
cleanTextArea.placeholder = originalPlaceholder;
}, 1500);
}, 10);
});
// Keyboard shortcuts
document.addEventListener('keydown', function(e) {
if ((e.ctrlKey || e.metaKey) && e.key === 'v' && document.activeElement !== cleanTextArea) {
e.preventDefault();
pasteBtn.click();
}
if ((e.ctrlKey || e.metaKey) && e.key === 'c' && document.activeElement === cleanTextArea) {
e.preventDefault();
copyBtn.click();
}
// Formatting shortcuts
if ((e.ctrlKey || e.metaKey) && document.activeElement === cleanTextArea) {
switch(e.key.toLowerCase()) {
case 'b':
e.preventDefault();
boldBtn.click();
break;
case 'i':
e.preventDefault();
italicBtn.click();
break;
case 'u':
e.preventDefault();
underlineBtn.click();
break;
case 'l':
e.preventDefault();
lowercaseBtn.click();
break;
case 'u':
if (e.shiftKey) {
e.preventDefault();
uppercaseBtn.click();
}
break;
case 'r':
e.preventDefault();
reverseBtn.click();
break;
case 'e':
e.preventDefault();
encodeBtn.click();
break;
case 'd':
e.preventDefault();
decodeBtn.click();
break;
case 'f':
e.preventDefault();
findReplaceBtn.click();
break;
case 'h':
e.preventDefault();
analyzeBtn.click();
break;
case 's':
e.preventDefault();
speakToggleBtn.click();
break;
}
}
// Find next/previous with F3/Shift+F3
if (e.key === 'F3') {
e.preventDefault();
if (findReplacePanel.classList.contains('open')) {
if (e.shiftKey) {
findPrevBtn.click();
} else {
findNextBtn.click();
}
}
}
});
// Initialize counts
updateCounts();
});
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=C50BARZ/clean-paste-10" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>