Spaces:
Running
Running
| <html lang="id"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Beautify HTML - Versi Gabungan</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.14.9/beautify-html.min.js"></script> | |
| </head> | |
| <body class="bg-gray-100 min-h-screen flex items-center justify-center p-6 font-sans"> | |
| <div class="w-full max-w-6xl bg-white rounded-xl shadow-xl border border-gray-200"> | |
| <!-- Header --> | |
| <div class="bg-gradient-to-r from-blue-600 to-blue-500 text-white px-6 py-4 rounded-t-xl"> | |
| <h1 class="text-2xl font-bold">🤖 HTML Beautifier & Cleaner</h1> | |
| <p class="text-sm opacity-90">Versi gabungan Tailwind + gaya ala BeautifyTools</p> | |
| </div> | |
| <!-- Tombol aksi --> | |
| <div class="p-4 flex flex-wrap gap-3 border-b border-gray-200 bg-gray-50"> | |
| <button onclick="perbaikiHtml()" | |
| class="px-4 py-2 bg-blue-600 text-white rounded-lg shadow hover:bg-blue-700 transition"> | |
| Perbaiki | |
| </button> | |
| <button onclick="beautifyHtml()" | |
| class="px-4 py-2 bg-purple-600 text-white rounded-lg shadow hover:bg-purple-700 transition"> | |
| Beautify | |
| </button> | |
| <button onclick="copyOutput()" | |
| class="px-4 py-2 bg-green-600 text-white rounded-lg shadow hover:bg-green-700 transition"> | |
| Copy | |
| </button> | |
| <button onclick="reviewOutput()" | |
| class="px-4 py-2 bg-yellow-500 text-white rounded-lg shadow hover:bg-yellow-600 transition"> | |
| Preview | |
| </button> | |
| <button onclick="clearInput()" | |
| class="px-4 py-2 bg-red-600 text-white rounded-lg shadow hover:bg-red-700 transition"> | |
| Clear | |
| </button> | |
| </div> | |
| <!-- Tabs --> | |
| <div class="px-6 pt-4"> | |
| <div class="flex space-x-2 border-b border-gray-200"> | |
| <button id="tab-input" | |
| class="tab-btn px-4 py-2 font-medium border-b-2 border-blue-600 text-blue-600 bg-white rounded-t-lg"> | |
| Input | |
| </button> | |
| <button id="tab-output" | |
| class="tab-btn px-4 py-2 font-medium border-b-2 border-transparent text-gray-600 hover:text-blue-600"> | |
| Hasil Script | |
| </button> | |
| <button id="tab-preview" | |
| class="tab-btn px-4 py-2 font-medium border-b-2 border-transparent text-gray-600 hover:text-blue-600"> | |
| Live Preview | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Panels --> | |
| <div class="p-6"> | |
| <!-- Input --> | |
| <div id="panel-input" class="tab-panel"> | |
| <textarea id="inputHtml" | |
| class="w-full h-72 p-3 border rounded-lg shadow-inner font-mono text-sm focus:ring focus:ring-blue-300" | |
| placeholder="<h1>Halo Dunia"></textarea> | |
| </div> | |
| <!-- Output --> | |
| <div id="panel-output" class="tab-panel hidden"> | |
| <textarea id="outputHtml" | |
| class="w-full h-96 p-3 border rounded-lg shadow-inner bg-gray-50 font-mono text-sm" | |
| readonly></textarea> | |
| </div> | |
| <!-- Preview --> | |
| <div id="panel-preview" class="tab-panel hidden"> | |
| <iframe id="previewFrame" | |
| class="w-full h-96 border rounded-lg shadow bg-white"></iframe> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // Tab system | |
| const tabs = ["input", "output", "preview"]; | |
| function switchTab(name) { | |
| tabs.forEach(t => { | |
| const btn = document.getElementById(`tab-${t}`); | |
| const panel = document.getElementById(`panel-${t}`); | |
| if (t === name) { | |
| btn.classList.add("border-blue-600","text-blue-600","bg-white","rounded-t-lg"); | |
| btn.classList.remove("border-transparent","text-gray-600"); | |
| panel.classList.remove("hidden"); | |
| } else { | |
| btn.classList.remove("border-blue-600","text-blue-600","bg-white","rounded-t-lg"); | |
| btn.classList.add("border-transparent","text-gray-600"); | |
| panel.classList.add("hidden"); | |
| } | |
| }); | |
| } | |
| switchTab("input"); | |
| document.getElementById("tab-input").addEventListener("click", () => switchTab("input")); | |
| document.getElementById("tab-output").addEventListener("click", () => switchTab("output")); | |
| document.getElementById("tab-preview").addEventListener("click", () => { ensurePreview(); switchTab("preview"); }); | |
| // Logic utama | |
| function perbaikiHtml() { | |
| const input = document.getElementById("inputHtml").value.trim(); | |
| const parser = new DOMParser(); | |
| const doc = parser.parseFromString(input || "<!DOCTYPE html><html><head></head><body></body></html>", "text/html"); | |
| let cleaned = "<!DOCTYPE html>\n" + doc.documentElement.outerHTML; | |
| const needs = []; | |
| if (!doc.querySelector("meta[charset]")) needs.push(" <meta charset='UTF-8'>"); | |
| if (!doc.querySelector("meta[name='viewport']")) needs.push(" <meta name='viewport' content='width=device-width, initial-scale=1.0'>"); | |
| if (!doc.querySelector("title")) needs.push(" <title>DokumenBaru</title>"); | |
| if (needs.length) cleaned = cleaned.replace("</head>", needs.join("\n") + "\n</head>"); | |
| document.getElementById("outputHtml").value = html_beautify(cleaned, { | |
| indent_size: 2, | |
| wrap_line_length: 100, | |
| preserve_newlines: true | |
| }); | |
| switchTab("output"); | |
| } | |
| function beautifyHtml() { | |
| const output = document.getElementById("outputHtml"); | |
| if (!output.value.trim()) { | |
| alert("⚠️ Tidak ada hasil untuk Beautify. Klik Perbaiki dulu!"); | |
| return; | |
| } | |
| const beautified = html_beautify(output.value, { | |
| indent_size: 2, | |
| wrap_line_length: 100, | |
| preserve_newlines: true | |
| }); | |
| output.value = beautified; | |
| switchTab("output"); | |
| } | |
| function copyOutput() { | |
| const output = document.getElementById("outputHtml"); | |
| if (!output.value.trim()) { | |
| alert("⚠️ Tidak ada hasil untuk dicopy. Klik Perbaiki dulu!"); | |
| return; | |
| } | |
| navigator.clipboard.writeText(output.value) | |
| .then(() => alert("✅ Script HTML berhasil dicopy!")) | |
| .catch(() => { | |
| output.select(); | |
| document.execCommand("copy"); | |
| alert("✅ Script HTML berhasil dicopy (fallback)!"); | |
| }); | |
| } | |
| function ensurePreview() { | |
| const output = document.getElementById("outputHtml").value.trim(); | |
| const iframe = document.getElementById("previewFrame"); | |
| if (output) { | |
| iframe.srcdoc = output; | |
| } else { | |
| const raw = document.getElementById("inputHtml").value.trim(); | |
| iframe.srcdoc = raw || "<h3 style='font-family:sans-serif'>Tidak ada konten untuk dipreview</h3>"; | |
| } | |
| } | |
| function reviewOutput() { | |
| ensurePreview(); | |
| switchTab("preview"); | |
| } | |
| function clearInput() { | |
| document.getElementById("inputHtml").value = ""; | |
| document.getElementById("outputHtml").value = ""; | |
| switchTab("input"); | |
| } | |
| function downloadOutput() { | |
| const output = document.getElementById("outputHtml").value; | |
| if (!output.trim()) { | |
| alert("⚠️ Tidak ada hasil untuk diunduh."); | |
| return; | |
| } | |
| const blob = new Blob([output], { type: "text/html" }); | |
| const url = URL.createObjectURL(blob); | |
| const a = document.createElement("a"); | |
| a.href = url; | |
| a.download = "hasil.html"; | |
| a.click(); | |
| URL.revokeObjectURL(url); | |
| } | |
| // Auto focus ke textarea input saat load | |
| window.onload = () => document.getElementById("inputHtml").focus(); | |
| </script> | |
| </body> | |
| </html> | |