178 lines
8.4 KiB
HTML
178 lines
8.4 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="ru">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<title>Code Formatter — WA Dev Tools</title>
|
|
<script src="/vendor/tailwind.js"></script>
|
|
<script>window.WA_TOOL_ID = 'formatter';</script>
|
|
<script src="/shared.js?v=2"></script>
|
|
<link href="/vendor/fonts.css" rel="stylesheet">
|
|
<link href="/shared.css" rel="stylesheet">
|
|
<style>
|
|
@keyframes fadeUp{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}.fade-up{animation:fadeUp .4s ease-out forwards}.fade-up-delay{animation:fadeUp .4s ease-out .1s forwards;opacity:0}
|
|
.tool-btn{transition:all .2s}.tool-btn.active{color:#0054e6;background:rgba(0,84,230,.08);border-color:rgba(0,84,230,.3)}
|
|
textarea{font-family:'JetBrains Mono',monospace;font-size:13px;resize:vertical}
|
|
</style>
|
|
</head>
|
|
<body class="noise dark:text-gray-200 text-gray-700 font-sans antialiased">
|
|
|
|
<div class="main-content">
|
|
<div class="tool-container relative z-10 max-w-3xl mx-auto px-4 py-8 sm:py-12">
|
|
|
|
<div class="page-header text-center mb-8 fade-up">
|
|
<div class="inline-flex items-center gap-2 mb-3 px-3 py-1 rounded-full border dark:border-surface-600 border-gray-300 dark:bg-surface-800/60 bg-white/60 text-xs font-mono dark:text-gray-400 text-gray-500 tracking-wide">
|
|
<span class="w-1.5 h-1.5 rounded-full bg-accent animate-pulse"></span>
|
|
CSS · JavaScript
|
|
</div>
|
|
<h1 class="text-3xl sm:text-4xl font-extrabold tracking-tight dark:text-white text-gray-900">
|
|
Code <span class="text-accent">Formatter</span>
|
|
</h1>
|
|
<p class="description mt-2 text-sm dark:text-gray-500 text-gray-400 font-mono">форматирование и beautify кода</p>
|
|
</div>
|
|
|
|
<!-- Tool Toggle -->
|
|
<div class="flex gap-2 mb-5 fade-up-delay">
|
|
<button class="tool-btn active flex-1 px-4 py-3 rounded-xl text-center border dark:border-surface-600 border-gray-200 dark:text-gray-400 text-gray-500 cursor-pointer" data-tool="css">
|
|
<div class="text-sm font-semibold">CSS</div>
|
|
</button>
|
|
<button class="tool-btn flex-1 px-4 py-3 rounded-xl text-center border dark:border-surface-600 border-gray-200 dark:text-gray-400 text-gray-500 cursor-pointer" data-tool="js">
|
|
<div class="text-sm font-semibold">JavaScript</div>
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Processing Card -->
|
|
<div class="dark:bg-surface-800/80 bg-white/85 border dark:border-surface-600 border-gray-200 rounded-2xl p-5 sm:p-7 backdrop-blur-sm fade-up-delay shadow-sm dark:shadow-none">
|
|
|
|
<div class="mb-4">
|
|
<label class="block text-xs font-medium dark:text-gray-500 text-gray-400 mb-1.5 font-mono uppercase tracking-wider">Input</label>
|
|
<textarea id="inputArea" rows="10" placeholder="Вставьте CSS..."
|
|
class="w-full px-4 py-3 rounded-xl dark:bg-surface-700 bg-gray-50 border dark:border-surface-600 border-gray-200 dark:text-gray-200 text-gray-700 focus:outline-none focus:border-accent/50 transition-colors"></textarea>
|
|
</div>
|
|
|
|
<button id="processBtn"
|
|
class="w-full py-3 rounded-xl font-semibold text-sm tracking-wide transition-all duration-300
|
|
bg-accent text-white border border-accent cursor-pointer
|
|
hover:bg-accent-bright hover:shadow-lg hover:shadow-accent/20 active:scale-[0.98]">
|
|
Форматировать
|
|
</button>
|
|
|
|
<div id="outputBlock" class="mt-5 hidden">
|
|
<div class="flex items-center justify-between mb-1.5">
|
|
<label class="text-xs font-medium dark:text-gray-500 text-gray-400 font-mono uppercase tracking-wider">Output</label>
|
|
<div class="flex items-center gap-2">
|
|
<span id="stats" class="text-[10px] font-mono dark:text-gray-600 text-gray-400"></span>
|
|
<button id="copyBtn" class="px-3 py-1 rounded-lg text-xs font-medium bg-accent/10 text-accent hover:bg-accent/20 transition-colors border border-accent/20">Копировать</button>
|
|
<button id="clearBtn" class="px-3 py-1 rounded-lg text-xs font-medium dark:bg-surface-700 bg-gray-100 dark:text-gray-400 text-gray-500 hover:text-danger transition-colors border dark:border-surface-600 border-gray-200">Очистить</button>
|
|
</div>
|
|
</div>
|
|
<textarea id="outputArea" rows="14" readonly
|
|
class="w-full px-4 py-3 rounded-xl dark:bg-surface-700 bg-gray-50 border dark:border-surface-600 border-gray-200 dark:text-gray-200 text-gray-700 focus:outline-none transition-colors"></textarea>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
let activeTool = 'css';
|
|
const $ = id => document.getElementById(id);
|
|
|
|
document.querySelectorAll('.tool-btn[data-tool]').forEach(btn => {
|
|
btn.addEventListener('click', () => {
|
|
document.querySelectorAll('.tool-btn[data-tool]').forEach(b => b.classList.remove('active'));
|
|
btn.classList.add('active');
|
|
activeTool = btn.dataset.tool;
|
|
$('inputArea').placeholder = activeTool === 'css' ? 'Вставьте CSS...' : 'Вставьте JavaScript...';
|
|
});
|
|
});
|
|
|
|
$('processBtn').addEventListener('click', () => {
|
|
const input = $('inputArea').value;
|
|
if (!input.trim()) return;
|
|
let output = '';
|
|
try {
|
|
output = activeTool === 'css' ? formatCss(input) : formatJs(input);
|
|
} catch (e) { output = 'Error: ' + e.message; }
|
|
$('outputArea').value = output;
|
|
$('outputBlock').classList.remove('hidden');
|
|
const diff = output.length - input.length;
|
|
$('stats').textContent = `${input.length} \u2192 ${output.length} (${diff > 0 ? '+' : ''}${diff})`;
|
|
});
|
|
|
|
$('copyBtn').addEventListener('click', () => {
|
|
navigator.clipboard.writeText($('outputArea').value).then(() => {
|
|
const b = $('copyBtn'); const o = b.textContent;
|
|
b.textContent = '\u0421\u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u043e!'; setTimeout(() => { b.textContent = o; }, 1500);
|
|
});
|
|
});
|
|
|
|
$('clearBtn').addEventListener('click', () => {
|
|
$('inputArea').value = ''; $('outputArea').value = ''; $('outputBlock').classList.add('hidden');
|
|
});
|
|
|
|
// ==================== CSS Formatter ====================
|
|
function formatCss(input) {
|
|
const preserved = [];
|
|
const hold = m => { preserved.push(m); return '\x00' + (preserved.length - 1) + '\x00'; };
|
|
|
|
let css = input;
|
|
css = css.replace(/(["'])(?:(?!\1|\\).|\\.)*\1/g, hold);
|
|
css = css.replace(/\/\*[\s\S]*?\*\//g, hold);
|
|
|
|
css = css.replace(/\s+/g, ' ').trim();
|
|
css = css.replace(/\s*\{\s*/g, ' {\n');
|
|
css = css.replace(/\s*\}\s*/g, '\n}\n\n');
|
|
css = css.replace(/;\s*/g, ';\n');
|
|
|
|
let indent = 0;
|
|
const lines = css.split('\n');
|
|
const formatted = [];
|
|
for (const raw of lines) {
|
|
const line = raw.trim();
|
|
if (!line) { if (formatted.length && formatted[formatted.length - 1] !== '') formatted.push(''); continue; }
|
|
if (line.startsWith('}')) indent = Math.max(0, indent - 1);
|
|
formatted.push(' '.repeat(indent) + line);
|
|
if (line.endsWith('{')) indent++;
|
|
}
|
|
|
|
let result = formatted.join('\n').replace(/\n{3,}/g, '\n\n').trim();
|
|
result = result.replace(/\x00(\d+)\x00/g, (_, i) => preserved[parseInt(i)]);
|
|
return result;
|
|
}
|
|
|
|
// ==================== JS Formatter ====================
|
|
function formatJs(input) {
|
|
const preserved = [];
|
|
const hold = m => { preserved.push(m); return '\x00' + (preserved.length - 1) + '\x00'; };
|
|
|
|
let js = input;
|
|
js = js.replace(/`(?:[^`\\]|\\.)*`/gs, hold);
|
|
js = js.replace(/(["'])(?:(?!\1|\\).|\\.)*\1/g, hold);
|
|
js = js.replace(/\/\*[\s\S]*?\*\//g, hold);
|
|
js = js.replace(/\/\/[^\n]*/g, hold);
|
|
|
|
js = js.replace(/\s+/g, ' ').trim();
|
|
js = js.replace(/\s*\{\s*/g, ' {\n');
|
|
js = js.replace(/\s*\}\s*/g, '\n}\n');
|
|
js = js.replace(/;\s*/g, ';\n');
|
|
|
|
let indent = 0;
|
|
const lines = js.split('\n');
|
|
const formatted = [];
|
|
for (const raw of lines) {
|
|
const line = raw.trim();
|
|
if (!line) { if (formatted.length && formatted[formatted.length - 1] !== '') formatted.push(''); continue; }
|
|
if (line.startsWith('}')) indent = Math.max(0, indent - 1);
|
|
formatted.push(' '.repeat(indent) + line);
|
|
if (line.endsWith('{')) indent++;
|
|
}
|
|
|
|
let result = formatted.join('\n').replace(/\n{3,}/g, '\n\n').trim();
|
|
result = result.replace(/\x00(\d+)\x00/g, (_, i) => preserved[parseInt(i)]);
|
|
return result;
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|