Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>APK Auto-Builder Studio</title> | |
| <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet"> | |
| <style> | |
| :root { | |
| --primary: #6366f1; | |
| --primary-hover: #4f46e5; | |
| --secondary: #ec4899; | |
| --bg-dark: #0f172a; | |
| --bg-panel: #1e293b; | |
| --text-main: #f8fafc; | |
| --text-muted: #94a3b8; | |
| --border: #334155; | |
| --success: #10b981; | |
| --terminal-bg: #0c0c0c; | |
| --font-main: 'Segoe UI', system-ui, sans-serif; | |
| --font-mono: 'Courier New', Courier, monospace; | |
| } | |
| * { | |
| box-sizing: border-box; | |
| margin: 0; | |
| padding: 0; | |
| } | |
| body { | |
| font-family: var(--font-main); | |
| background-color: var(--bg-dark); | |
| color: var(--text-main); | |
| min-height: 100vh; | |
| display: flex; | |
| flex-direction: column; | |
| overflow-x: hidden; | |
| } | |
| /* Header */ | |
| header { | |
| background: rgba(30, 41, 59, 0.8); | |
| backdrop-filter: blur(10px); | |
| border-bottom: 1px solid var(--border); | |
| padding: 1rem 2rem; | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| position: sticky; | |
| top: 0; | |
| z-index: 100; | |
| } | |
| .logo { | |
| font-size: 1.5rem; | |
| font-weight: 700; | |
| background: linear-gradient(135deg, var(--primary), var(--secondary)); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| display: flex; | |
| align-items: center; | |
| gap: 0.5rem; | |
| } | |
| .anycoder-link { | |
| color: var(--text-muted); | |
| text-decoration: none; | |
| font-size: 0.9rem; | |
| transition: color 0.3s; | |
| display: flex; | |
| align-items: center; | |
| gap: 0.5rem; | |
| } | |
| .anycoder-link:hover { | |
| color: var(--primary); | |
| } | |
| /* Main Layout */ | |
| main { | |
| flex: 1; | |
| display: grid; | |
| grid-template-columns: 350px 1fr; | |
| gap: 2rem; | |
| padding: 2rem; | |
| max-width: 1600px; | |
| margin: 0 auto; | |
| width: 100%; | |
| } | |
| @media (max-width: 900px) { | |
| main { | |
| grid-template-columns: 1fr; | |
| } | |
| } | |
| /* Panels */ | |
| .panel { | |
| background: var(--bg-panel); | |
| border: 1px solid var(--border); | |
| border-radius: 12px; | |
| padding: 1.5rem; | |
| box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); | |
| } | |
| h2 { | |
| font-size: 1.25rem; | |
| margin-bottom: 1rem; | |
| color: var(--text-main); | |
| display: flex; | |
| align-items: center; | |
| gap: 0.5rem; | |
| } | |
| /* File Upload Zone */ | |
| .upload-zone { | |
| border: 2px dashed var(--border); | |
| border-radius: 8px; | |
| padding: 3rem 1.5rem; | |
| text-align: center; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .upload-zone:hover, .upload-zone.dragover { | |
| border-color: var(--primary); | |
| background: rgba(99, 102, 241, 0.05); | |
| } | |
| .upload-icon { | |
| font-size: 3rem; | |
| color: var(--text-muted); | |
| margin-bottom: 1rem; | |
| transition: transform 0.3s; | |
| } | |
| .upload-zone:hover .upload-icon { | |
| transform: scale(1.1); | |
| color: var(--primary); | |
| } | |
| .file-info { | |
| display: none; | |
| margin-top: 1rem; | |
| background: rgba(0,0,0,0.2); | |
| padding: 1rem; | |
| border-radius: 6px; | |
| text-align: left; | |
| } | |
| .file-info.active { | |
| display: block; | |
| animation: fadeIn 0.5s ease; | |
| } | |
| .file-name { | |
| font-weight: 600; | |
| color: var(--primary); | |
| word-break: break-all; | |
| } | |
| .file-meta { | |
| font-size: 0.85rem; | |
| color: var(--text-muted); | |
| margin-top: 0.25rem; | |
| } | |
| /* Form Elements */ | |
| .form-group { | |
| margin-bottom: 1.25rem; | |
| } | |
| label { | |
| display: block; | |
| margin-bottom: 0.5rem; | |
| font-size: 0.9rem; | |
| color: var(--text-muted); | |
| } | |
| input, select { | |
| width: 100%; | |
| background: var(--bg-dark); | |
| border: 1px solid var(--border); | |
| color: var(--text-main); | |
| padding: 0.75rem; | |
| border-radius: 6px; | |
| font-family: var(--font-main); | |
| transition: border-color 0.3s; | |
| } | |
| input:focus, select:focus { | |
| outline: none; | |
| border-color: var(--primary); | |
| } | |
| /* Terminal / Console */ | |
| .terminal-container { | |
| display: flex; | |
| flex-direction: column; | |
| height: 100%; | |
| min-height: 400px; | |
| } | |
| .terminal-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| padding-bottom: 1rem; | |
| border-bottom: 1px solid var(--border); | |
| margin-bottom: 1rem; | |
| } | |
| .status-badge { | |
| font-size: 0.8rem; | |
| padding: 0.25rem 0.75rem; | |
| border-radius: 99px; | |
| background: var(--border); | |
| color: var(--text-muted); | |
| text-transform: uppercase; | |
| letter-spacing: 0.05em; | |
| } | |
| .status-badge.active { | |
| background: rgba(16, 185, 129, 0.2); | |
| color: var(--success); | |
| } | |
| .terminal-window { | |
| flex: 1; | |
| background: var(--terminal-bg); | |
| border-radius: 8px; | |
| padding: 1rem; | |
| font-family: var(--font-mono); | |
| font-size: 0.9rem; | |
| color: #d4d4d4; | |
| overflow-y: auto; | |
| height: 400px; | |
| border: 1px solid #333; | |
| box-shadow: inset 0 2px 10px rgba(0,0,0,0.5); | |
| } | |
| .log-line { | |
| margin-bottom: 0.25rem; | |
| line-height: 1.4; | |
| display: flex; | |
| gap: 0.5rem; | |
| } | |
| .log-time { | |
| color: #569cd6; | |
| } | |
| .log-info { color: #d4d4d4; } | |
| .log-warn { color: #ce9178; } | |
| .log-success { color: var(--success); } | |
| .log-cmd { color: var(--secondary); } | |
| /* Progress Bar */ | |
| .progress-container { | |
| margin-top: 1rem; | |
| height: 6px; | |
| background: var(--bg-dark); | |
| border-radius: 3px; | |
| overflow: hidden; | |
| display: none; | |
| } | |
| .progress-container.active { | |
| display: block; | |
| } | |
| .progress-bar { | |
| height: 100%; | |
| background: linear-gradient(90deg, var(--primary), var(--secondary)); | |
| width: 0%; | |
| transition: width 0.3s ease; | |
| } | |
| /* Buttons */ | |
| .btn { | |
| width: 100%; | |
| padding: 1rem; | |
| border: none; | |
| border-radius: 8px; | |
| font-size: 1rem; | |
| font-weight: 600; | |
| cursor: pointer; | |
| transition: all 0.3s; | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| gap: 0.5rem; | |
| } | |
| .btn-primary { | |
| background: var(--primary); | |
| color: white; | |
| box-shadow: 0 4px 15px rgba(99, 102, 241, 0.3); | |
| } | |
| .btn-primary:hover { | |
| background: var(--primary-hover); | |
| transform: translateY(-2px); | |
| } | |
| .btn-primary:disabled { | |
| background: var(--border); | |
| cursor: not-allowed; | |
| transform: none; | |
| box-shadow: none; | |
| } | |
| /* Toast Notification */ | |
| .toast { | |
| position: fixed; | |
| bottom: 2rem; | |
| right: 2rem; | |
| background: var(--bg-panel); | |
| border: 1px solid var(--border); | |
| padding: 1rem 1.5rem; | |
| border-radius: 8px; | |
| display: flex; | |
| align-items: center; | |
| gap: 1rem; | |
| box-shadow: 0 10px 25px rgba(0,0,0,0.5); | |
| transform: translateY(100px); | |
| opacity: 0; | |
| transition: all 0.3s cubic-bezier(0.68, -0.55, 0.27, 1.55); | |
| z-index: 1000; | |
| } | |
| .toast.show { | |
| transform: translateY(0); | |
| opacity: 1; | |
| } | |
| .toast-icon { | |
| font-size: 1.5rem; | |
| } | |
| .toast-success .toast-icon { color: var(--success); } | |
| .toast-error .toast-icon { color: #ef4444; } | |
| /* Animations */ | |
| @keyframes fadeIn { | |
| from { opacity: 0; transform: translateY(10px); } | |
| to { opacity: 1; transform: translateY(0); } | |
| } | |
| @keyframes pulse { | |
| 0% { opacity: 0.5; } | |
| 50% { opacity: 1; } | |
| 100% { opacity: 0.5; } | |
| } | |
| .cursor { | |
| display: inline-block; | |
| width: 8px; | |
| height: 15px; | |
| background: var(--success); | |
| animation: pulse 1s infinite; | |
| } | |
| /* Scrollbar */ | |
| ::-webkit-scrollbar { | |
| width: 8px; | |
| } | |
| ::-webkit-scrollbar-track { | |
| background: var(--bg-dark); | |
| } | |
| ::-webkit-scrollbar-thumb { | |
| background: var(--border); | |
| border-radius: 4px; | |
| } | |
| ::-webkit-scrollbar-thumb:hover { | |
| background: var(--text-muted); | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <header> | |
| <div class="logo"> | |
| <i class="fa-brands fa-android"></i> APK Forge | |
| </div> | |
| <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="anycoder-link"> | |
| <i class="fa-solid fa-code"></i> Built with anycoder | |
| </a> | |
| </header> | |
| <main> | |
| <!-- Left Column: Input & Config --> | |
| <section class="left-col"> | |
| <div class="panel" style="margin-bottom: 2rem;"> | |
| <h2><i class="fa-solid fa-file-import"></i> Source Code</h2> | |
| <div class="upload-zone" id="dropZone"> | |
| <input type="file" id="fileInput" hidden accept=".java,.jar,.js,.html,.cpp,.h"> | |
| <i class="fa-solid fa-cloud-arrow-up upload-icon"></i> | |
| <p>Drag & Drop your file here</p> | |
| <p style="font-size: 0.8rem; color: var(--text-muted); margin-top: 0.5rem;"> | |
| Supports .java, .jar, .js, .html, .cpp, .h | |
| </p> | |
| <div class="file-info" id="fileInfo"> | |
| <div class="file-name" id="fileName">script.js</div> | |
| <div class="file-meta" id="fileMeta">12 KB • JavaScript</div> | |
| <button id="removeFile" style="margin-top: 0.5rem; background: transparent; border: 1px solid var(--border); color: var(--text-muted); padding: 0.25rem 0.5rem; border-radius: 4px; cursor: pointer;">Remove</button> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="panel"> | |
| <h2><i class="fa-solid fa-sliders"></i> Configuration</h2> | |
| <form id="configForm"> | |
| <div class="form-group"> | |
| <label>Application Name</label> | |
| <input type="text" id="appName" placeholder="My Awesome App" value="My App"> | |
| </div> | |
| <div class="form-group"> | |
| <label>Package Name</label> | |
| <input type="text" id="packageName" placeholder="com.example.myapp" value="com.example.app"> | |
| </div> | |
| <div class="form-group"> | |
| <label>Target SDK</label> | |
| <select id="sdkVersion"> | |
| <option value="33">Android 13 (API 33)</option> | |
| <option value="32">Android 12L (API 32)</option> | |
| <option value="31">Android 12 (API 31)</option> | |
| <option value="30">Android 11 (API 30)</option> | |
| </select> | |
| </div> | |
| <div class="form-group"> | |
| <label>Build Mode</label> | |
| <select id="buildMode"> | |
| <option value="debug">Debug (Unigned)</option> | |
| <option value="release">Release (Signed)</option> | |
| </select> | |
| </div> | |
| <button type="button" id="buildBtn" class="btn btn-primary" disabled> | |
| <i class="fa-solid fa-hammer"></i> Build APK | |
| </button> | |
| </form> | |
| </div> | |
| </section> | |
| <!-- Right Column: Terminal & Output --> | |
| <section class="right-col"> | |
| <div class="panel terminal-container"> | |
| <div class="terminal-header"> | |
| <h2><i class="fa-solid fa-terminal"></i> Build Console</h2> | |
| <span class="status-badge" id="statusBadge">Idle</span> | |
| </div> | |
| <div class="terminal-window" id="terminal"> | |
| <div class="log-line"> | |
| <span class="log-time">[System]</span> | |
| <span class="log-info">Welcome to APK Forge. Ready to build.</span> | |
| </div> | |
| <div class="log-line"> | |
| <span class="log-time">[System]</span> | |
| <span class="log-info">Waiting for source file...</span> | |
| </div> | |
| <span class="cursor"></span> | |
| </div> | |
| <div class="progress-container" id="progressContainer"> | |
| <div class="progress-bar" id="progressBar"></div> | |
| </div> | |
| </div> | |
| </section> | |
| </main> | |
| <!-- Toast Notification --> | |
| <div class="toast" id="toast"> | |
| <div class="toast-icon"><i class="fa-solid fa-circle-check"></i></div> | |
| <div class="toast-content"> | |
| <h4 id="toastTitle">Success</h4> | |
| <p id="toastMessage" style="font-size: 0.9rem; color: var(--text-muted);">Build completed successfully.</p> | |
| </div> | |
| </div> | |
| <script> | |
| // DOM Elements | |
| const dropZone = document.getElementById('dropZone'); | |
| const fileInput = document.getElementById('fileInput'); | |
| const fileInfo = document.getElementById('fileInfo'); | |
| const fileName = document.getElementById('fileName'); | |
| const fileMeta = document.getElementById('fileMeta'); | |
| const removeFileBtn = document.getElementById('removeFile'); | |
| const buildBtn = document.getElementById('buildBtn'); | |
| const terminal = document.getElementById('terminal'); | |
| const progressBar = document.getElementById('progressBar'); | |
| const progressContainer = document.getElementById('progressContainer'); | |
| const statusBadge = document.getElementById('statusBadge'); | |
| const toast = document.getElementById('toast'); | |
| const packageNameInput = document.getElementById('packageName'); | |
| const appNameInput = document.getElementById('appName'); | |
| // State | |
| let currentFile = null; | |
| let isBuilding = false; | |
| // --- File Handling Logic --- | |
| dropZone.addEventListener('click', () => fileInput.click()); | |
| dropZone.addEventListener('dragover', (e) => { | |
| e.preventDefault(); | |
| dropZone.classList.add('dragover'); | |
| }); | |
| dropZone.addEventListener('dragleave', () => { | |
| dropZone.classList.remove('dragover'); | |
| }); | |
| dropZone.addEventListener('drop', (e) => { | |
| e.preventDefault(); | |
| dropZone.classList.remove('dragover'); | |
| if (e.dataTransfer.files.length) { | |
| handleFile(e.dataTransfer.files[0]); | |
| } | |
| }); | |
| fileInput.addEventListener('change', (e) => { | |
| if (e.target.files.length) { | |
| handleFile(e.target.files[0]); | |
| } | |
| }); | |
| removeFileBtn.addEventListener('click', (e) => { | |
| e.stopPropagation(); | |
| resetFile(); | |
| }); | |
| function handleFile(file) { | |
| const validExtensions = ['.java', '.jar', '.js', '.html', '.cpp', '.h']; | |
| const fileName = file.name; | |
| const ext = fileName.substring(fileName.lastIndexOf('.')).toLowerCase(); | |
| if (!validExtensions.includes(ext)) { | |
| showToast('error', 'Invalid File', 'Please upload .java, .jar, .js, .html, .cpp, or .h files.'); | |
| return; | |
| } | |
| currentFile = file; | |
| // Auto-configure based on file type | |
| autoConfigure(ext, fileName); | |
| // Update UI | |
| document.getElementById('fileName').textContent = fileName; | |
| document.getElementById('fileMeta').textContent = `${formatBytes(file.size)} • ${getFileType(ext)}`; | |
| dropZone.querySelector('.upload-icon').style.display = 'none'; | |
| dropZone.querySelector('p').style.display = 'none'; | |
| dropZone.querySelector('p:last-child').style.display = 'none'; | |
| fileInfo.classList.add('active'); | |
| buildBtn.disabled = false; | |
| log(`File loaded: ${fileName}`, 'info'); | |
| } | |
| function resetFile() { | |
| currentFile = null; | |
| fileInput.value = ''; | |
| dropZone.querySelector('.upload-icon').style.display = 'block'; | |
| dropZone.querySelector('p').style.display = 'block'; | |
| dropZone.querySelector('p:last-child').style.display = 'block'; | |
| fileInfo.classList.remove('active'); | |
| buildBtn.disabled = false; // Allow clicking build to show error | |
| log('File removed.', 'warn'); | |
| } | |
| function autoConfigure(ext, name) { | |
| let baseName = name.replace(ext, '').replace(/[^a-zA-Z0-9]/g, ' '); | |
| baseName = baseName.replace(/\b\w/g, l => l.toUpperCase()); // Title Case | |
| appNameInput.value = baseName.trim() || "My App"; | |
| // Smart package name generation | |
| const randomId = Math.floor(Math.random() * 1000); | |
| if (ext === '.html' || ext === '.js') { | |
| packageNameInput.value = `com.webwrapper.app${randomId}`; | |
| log('Detected Web Project. Configuring WebView Wrapper.', 'info'); | |
| } else if (ext === '.cpp' || ext === '.h') { | |
| packageNameInput.value = `com.native.ndk.app${randomId}`; | |
| log('Detected Native C++. Configuring NDK Build.', 'info'); | |
| } else { | |
| packageNameInput.value = `com.example.app${randomId}`; | |
| } | |
| } | |
| // --- Build Simulation Logic --- | |
| buildBtn.addEventListener('click', () => { | |
| if (!currentFile) { | |
| showToast('error', 'No File', 'Please upload a source file first.'); | |
| return; | |
| } | |
| if (isBuilding) return; | |
| startBuild(); | |
| }); | |
| function startBuild() { | |
| isBuilding = true; | |
| buildBtn.disabled = true; | |
| buildBtn.innerHTML = '<i class="fa-solid fa-circle-notch fa-spin"></i> Building...'; | |
| statusBadge.textContent = 'Compiling'; | |
| statusBadge.classList.add('active'); | |
| progressContainer.classList.add('active'); | |
| // Clear previous logs (keep header) | |
| const logs = terminal.querySelectorAll('.log-line:not(:first-child):not(:nth-child(2))'); | |
| logs.forEach(l => l.remove()); | |
| const steps = [ | |
| { msg: `Initializing build environment...`, delay: 500, type: 'info' }, | |
| { msg: `Reading source: ${currentFile.name}`, delay: 1200, type: 'cmd' }, | |
| { msg: `Auto-configuring AndroidManifest.xml`, delay: 1800, type: 'info' }, | |
| { msg: `Generating R.java files...`, delay: 2500, type: 'info' }, | |
| { msg: `Compiling resources with AAPT2`, delay: 3200, type: 'cmd' }, | |
| { msg: `Converting Java bytecode to DEX (dx/d8)`, delay: 4000, type: 'cmd' }, | |
| { msg: `Packaging APK (apkbuilder)`, delay: 5000, type: 'info' }, | |
| { msg: `Signing APK with debug key...`, delay: 6000, type: 'warn' }, | |
| { msg: `Build Successful! Output generated.`, delay: 7000, type: 'success' } | |
| ]; | |
| let totalDelay = 7000; | |
| steps.forEach(step => { | |
| setTimeout(() => { | |
| log(step.msg, step.type); | |
| // Update progress bar roughly | |
| const progress = (step.delay / totalDelay) * 100; | |
| progressBar.style.width = `${Math.min(progress, 100)}%`; | |
| }, step.delay); | |
| }); | |
| setTimeout(() => { | |
| finishBuild(); | |
| }, totalDelay + 500); | |
| } | |
| function finishBuild() { | |
| isBuilding = false; | |
| buildBtn.disabled = false; | |
| buildBtn.innerHTML = '<i class="fa-solid fa-hammer"></i> Build APK'; | |
| statusBadge.textContent = 'Completed'; | |
| statusBadge.classList.remove('active'); | |
| showToast('success', 'Build Complete', 'APK downloaded to your device.'); | |
| // Trigger Download | |
| downloadDummyApk(); | |
| } | |
| // --- Helper Functions --- | |
| function log(message, type = 'info') { | |
| const line = document.createElement('div'); | |
| line.className = 'log-line'; | |
| const time = new Date().toLocaleTimeString('en-US', { hour12: false }); | |
| line.innerHTML = ` | |
| <span class="log-time">[${time}]</span> | |
| <span class="log-${type}">${message}</span> | |
| `; | |
| // Insert before cursor | |
| const cursor = terminal.querySelector('.cursor'); | |
| terminal.insertBefore(line, cursor); | |
| // Auto scroll | |
| terminal.scrollTop = terminal.scrollHeight; | |
| } | |
| function formatBytes(bytes, decimals = 2) { | |
| if (!+bytes) return '0 Bytes'; | |
| const k = 1024; | |
| const dm = decimals < 0 ? 0 : decimals; | |
| const sizes = ['Bytes', 'KB', 'MB', 'GB']; | |
| const i = Math.floor(Math.log(bytes) / Math.log(k)); | |
| return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`; | |
| } | |
| function getFileType(ext) { | |
| const map = { | |
| '.java': 'Java Source', | |
| '.jar': 'Java Archive', | |
| '.js': 'JavaScript', | |
| '.html': 'HTML Document', | |
| '.cpp': 'C++ Source', | |
| '.h': 'C++ Header' | |
| }; | |
| return map[ext] || 'Unknown'; | |
| } | |
| function showToast(type, title, message) { | |
| const toastEl = document.getElementById('toast'); | |
| const icon = toastEl.querySelector('.toast-icon i'); | |
| const titleEl = document.getElementById('toastTitle'); | |
| const msgEl = document.getElementById('toastMessage'); | |
| toastEl.className = `toast toast-${type} show`; | |
| if (type === 'success') { | |
| icon.className = 'fa-solid fa-circle-check'; | |
| } else { | |
| icon.className = 'fa-solid fa-circle-exclamation'; | |
| } | |
| titleEl.textContent = title; | |
| msgEl.textContent = message; | |
| setTimeout(() => { | |
| toastEl.classList.remove('show'); | |
| }, 4000); | |
| } | |
| // --- Mock Download Logic --- | |
| // Since we cannot actually compile C++/Java in the browser without a backend, | |
| // we generate a dummy ZIP file renamed to .apk to simulate the experience. | |
| function downloadDummyApk() { | |
| const appName = appNameInput.value.replace(/\s+/g, '').toLowerCase() || 'app'; | |
| const filename = `${appName}.apk`; | |
| // Create a dummy text content that mimics a manifest | |
| const dummyContent = ` | |
| PK..... | |
| [AndroidManifest.xml] | |
| Package: ${packageNameInput.value} | |
| App Name: ${appNameInput.value} | |
| Source: ${currentFile.name} | |
| Type: Auto-generated by APK Forge | |
| `; | |
| const blob = new Blob([dummyContent], { type: 'application/vnd.android.package-archive' }); | |
| const url = window.URL.createObjectURL(blob); | |
| const a = document.createElement('a'); | |
| a.style.display = 'none'; | |
| a.href = url; | |
| a.download = filename; | |
| document.body.appendChild(a); | |
| a.click(); | |
| window.URL.revokeObjectURL(url); | |
| document.body.removeChild(a); | |
| log(`Triggered download for ${filename}`, 'success'); | |
| } | |
| </script> | |
| </body> | |
| </html> |