2025-09-26 13:10:28 +08:00
2025-08-27 18:35:01 +08:00
<!doctype html>
2025-09-16 15:08:43 +06:00
< html lang = "en" class = "scroll-smooth" >
2025-08-27 18:35:01 +08:00
< head >
< meta charset = "utf-8" / >
2025-09-16 15:08:43 +06:00
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" >
2025-08-27 18:35:01 +08:00
< title > MikroTik RouterOS Patched Versions< / title >
2025-09-19 11:32:05 +06:00
<!-- Tailwind CSS framework for styling -->
< script src = "https://cdn.tailwindcss.com" > < / script >
<!-- Iconify icon library for icons -->
2025-09-16 15:08:43 +06:00
< script src = "https://code.iconify.design/3/3.1.1/iconify.min.js" > < / script >
2025-09-19 11:32:05 +06:00
<!-- Google Fonts preconnect for performance optimization -->
2025-09-16 15:08:43 +06:00
< link rel = "preconnect" href = "https://fonts.googleapis.com" >
< link rel = "preconnect" href = "https://fonts.gstatic.com" crossorigin >
2025-09-19 11:32:05 +06:00
<!-- Mirror fonts for Chinese users (fonts.loli.net) -->
2025-09-16 15:08:43 +06:00
< link rel = "preconnect" href = "https://fonts.loli.net" >
< link rel = "preconnect" href = "https://gstatic.loli.net" crossorigin >
2025-09-19 11:32:05 +06:00
<!-- Main font stylesheet with Inter and Fira Code fonts -->
2025-09-16 15:08:43 +06:00
< link id = "webfont-css" href = "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Fira+Code&display=swap" rel = "stylesheet" >
2025-09-19 11:32:05 +06:00
<!-- Font loading fallback script for Chinese users -->
2025-09-16 15:08:43 +06:00
< script >
(function(){
var linkEl = document.getElementById('webfont-css');
if (!linkEl) return;
var mirrorHref = 'https://fonts.loli.net/css2?family=Inter:wght@400;500;600;700&family=Fira+Code&display=swap';
2025-09-19 11:32:05 +06:00
// Check if a specific font is loaded
2025-09-16 15:08:43 +06:00
function isLoaded(name){
if (!('fonts' in document)) return false;
try { return document.fonts.check('1em "' + name + '"'); } catch (e) { return false; }
}
2025-09-19 11:32:05 +06:00
// Switch to mirror font server if Google Fonts fails
2025-09-16 15:08:43 +06:00
function switchToMirror(){
if (linkEl.href !== mirrorHref) { linkEl.href = mirrorHref; }
}
if (!('fonts' in document)) { switchToMirror(); return; }
var decided = false;
2025-09-19 11:32:05 +06:00
// Timeout fallback - switch to mirror after 2.5 seconds if fonts not loaded
2025-09-16 15:08:43 +06:00
var timer = setTimeout(function(){
if (!decided & & (!isLoaded('Inter') || !isLoaded('Fira Code'))) {
decided = true; switchToMirror();
}
}, 2500);
2025-09-19 11:32:05 +06:00
// Check when fonts are ready
2025-09-16 15:08:43 +06:00
document.fonts.ready.then(function(){
if (!decided & & (!isLoaded('Inter') || !isLoaded('Fira Code'))) {
decided = true; switchToMirror();
} else {
decided = true; clearTimeout(timer);
}
});
})();
< / script >
2025-08-27 18:35:01 +08:00
2025-09-16 15:08:43 +06:00
2025-09-19 11:32:05 +06:00
<!-- Custom CSS styles using Tailwind CSS -->
2025-09-16 15:08:43 +06:00
< style type = "text/tailwindcss" >
2025-09-19 11:32:05 +06:00
/* Main body styling with gradient background */
2025-09-16 15:08:43 +06:00
body {
font-family: 'Inter', sans-serif;
background-image: radial-gradient(circle at top, #dde3ee 0%, #f1f5f9 60%);
2025-08-27 18:35:01 +08:00
}
2025-09-19 11:32:05 +06:00
/* Dark mode gradient background */
2025-09-16 15:08:43 +06:00
html.dark body {
background-image: radial-gradient(circle at top, #1e293b 0%, #0f172a 60%);
2025-08-27 18:35:01 +08:00
}
2025-09-19 11:32:05 +06:00
/* Monospace font for command code blocks */
2025-09-16 15:08:43 +06:00
#command pre, #command code {
font-family: 'Fira Code', monospace;
2025-08-27 18:35:01 +08:00
}
2025-09-19 11:32:05 +06:00
/* Custom scrollbar styling */
2025-09-16 15:08:43 +06:00
.custom-scrollbar::-webkit-scrollbar {
width: 8px;
2025-08-27 18:35:01 +08:00
}
2025-09-16 15:08:43 +06:00
.custom-scrollbar::-webkit-scrollbar-track {
background: transparent;
2025-08-27 18:35:01 +08:00
}
2025-09-16 15:08:43 +06:00
.custom-scrollbar::-webkit-scrollbar-thumb {
background: #94a3b8;
border-radius: 10px;
2025-08-27 18:35:01 +08:00
}
2025-09-19 11:32:05 +06:00
/* Dark mode scrollbar */
2025-09-16 15:08:43 +06:00
html.dark .custom-scrollbar::-webkit-scrollbar-thumb {
background: #475569;
2025-08-27 18:35:01 +08:00
}
2025-09-19 11:32:05 +06:00
/* Active state for version selection buttons */
2025-09-16 15:08:43 +06:00
#version-buttons button.active {
@apply bg-blue-600 text-white shadow-md;
2025-08-27 18:35:01 +08:00
}
2025-09-19 11:32:05 +06:00
/* Hide default details marker for custom styling */
2025-09-16 15:08:43 +06:00
summary::-webkit-details-marker {
display: none;
2025-08-27 18:35:01 +08:00
}
2025-09-19 11:32:05 +06:00
/* Content card animation classes */
2025-09-16 15:08:43 +06:00
.content-card {
@apply transition-all duration-700;
2025-09-08 10:22:15 +08:00
}
2025-09-19 11:32:05 +06:00
/* Loading state animation */
2025-09-16 15:08:43 +06:00
.loading .content-card {
@apply opacity-0 translate-y-4;
2025-09-08 10:22:15 +08:00
}
2025-08-27 18:35:01 +08:00
< / style >
2025-09-19 11:32:05 +06:00
<!-- Tailwind CSS configuration -->
2025-08-27 18:35:01 +08:00
< script >
2025-09-16 15:08:43 +06:00
tailwind.config = {
2025-09-19 11:32:05 +06:00
darkMode: 'class', // Enable class-based dark mode
2025-09-16 15:08:43 +06:00
theme: {
extend: {
fontFamily: {
2025-09-19 11:32:05 +06:00
sans: ['Inter', 'sans-serif'], // Default sans-serif font
mono: ['Fira Code', 'monospace'], // Monospace font for code
2025-09-16 15:08:43 +06:00
},
},
},
}
< / script >
< / head >
2025-09-19 11:32:05 +06:00
<!-- Main body with dark mode support and loading animation -->
2025-09-16 15:08:43 +06:00
< body class = "bg-slate-100 dark:bg-gray-900 text-slate-700 dark:text-slate-300 transition-colors duration-300 loading" >
2025-09-19 11:32:05 +06:00
<!-- GitHub corner ribbon (top - right corner) -->
2025-09-16 15:08:43 +06:00
< a href = "https://github.com/elseif/MikroTikPatch" class = "github-corner fixed top-0 right-0 z-50" aria-label = "View source on GitHub" target = "_blank" >
< svg width = "80" height = "80" viewBox = "0 0 250 250" class = "fill-slate-800 dark:fill-slate-50" style = "position: absolute; top: 0; border: 0; right: 0;" >
< path d = "M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z" > < / path >
< path d = "M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill = "currentColor" style = "transform-origin: 130px 106px;" class = "octo-arm text-slate-50 dark:text-gray-900" > < / path >
< path d = "M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.6,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill = "currentColor" class = "octo-body text-slate-50 dark:text-gray-900" > < / path >
< / svg >
< / a >
2025-09-19 11:32:05 +06:00
<!-- Main container with responsive padding -->
2025-09-16 15:08:43 +06:00
< div class = "container mx-auto max-w-7xl px-4 py-8 md:py-16" >
2025-09-19 11:32:05 +06:00
<!-- Page header section -->
2025-09-16 15:08:43 +06:00
< header class = "text-center mb-12 content-card" style = "transition-delay: 100ms;" >
2025-09-19 11:32:05 +06:00
<!-- Main title with gradient text effect -->
2025-09-16 15:08:43 +06:00
< h1 class = "text-4xl md:text-5xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-blue-600 to-sky-400 dark:from-blue-400 dark:to-sky-300 mb-3" > MikroTik RouterOS< / h1 >
< p class = "text-xl md:text-2xl text-slate-600 dark:text-slate-400" > Patched Versions< / p >
2025-09-19 11:32:05 +06:00
<!-- Status badges for build workflows and services -->
2025-09-16 15:08:43 +06:00
< div class = "mt-6 flex justify-center items-center gap-4 flex-wrap" >
2026-07-15 09:43:20 +08:00
< a href = "https://github.com/elseif/MikroTikPatch/actions/workflows/main.yml" target = "_blank" > < img src = "https://github.com/elseif/MikroTikPatch/actions/workflows/main.yml/badge.svg" alt = "Patch Mikrotik RouterOS" > < / a >
2025-09-16 15:08:43 +06:00
< img src = "https://img.shields.io/endpoint?logo=icloud&url=https://mikrotik.ltd/status/cloud" alt = "Cloud Status" >
< / div >
2025-09-19 11:32:05 +06:00
<!-- GitHub social buttons -->
2025-09-16 15:08:43 +06:00
< div class = "mt-4 flex justify-center items-center gap-2" >
< script async defer src = "https://buttons.github.io/buttons.js" > < / script >
< a class = "github-button" href = "https://github.com/elseif/MikroTikPatch" data-icon = "octicon-star" data-size = "large" data-show-count = "true" aria-label = "Star elseif/MikroTikPatch on GitHub" > Star< / a >
< a class = "github-button" href = "https://github.com/elseif/MikroTikPatch/fork" data-icon = "octicon-repo-forked" data-size = "large" data-show-count = "true" aria-label = "Fork elseif/MikroTikPatch on GitHub" > Fork< / a >
< / div >
< / header >
2025-09-19 11:32:05 +06:00
<!-- Main content area -->
2025-09-16 15:08:43 +06:00
< main class = "space-y-12" >
2025-09-19 11:32:05 +06:00
<!-- Information and settings card -->
2025-09-16 15:08:43 +06:00
< div class = "bg-white/60 dark:bg-gray-800/60 rounded-2xl shadow-lg p-6 md:p-8 ring-1 ring-black/5 backdrop-blur-xl content-card" style = "transition-delay: 200ms;" >
< div class = "grid md:grid-cols-2 gap-8" >
2025-09-19 11:32:05 +06:00
<!-- Quick information section -->
2025-09-16 15:08:43 +06:00
< div >
< h2 class = "text-lg font-semibold text-slate-800 dark:text-white mb-4 flex items-center" > < span class = "iconify mr-2 text-blue-500" data-icon = "ph:info-bold" > < / span > < span data-i18n = "quickInformation" > < / span > < / h2 >
< div class = "space-y-3 text-sm" >
< p data-i18n = "infoLabel1" > < / p >
< p data-i18n = "infoLabel2" > < / p >
< / div >
< / div >
2025-09-19 11:32:05 +06:00
<!-- Settings section -->
2025-09-16 15:08:43 +06:00
< div class = "border-t md:border-t-0 md:border-l border-slate-200 dark:border-gray-700 pt-6 md:pt-0 md:pl-8" >
< h2 class = "text-lg font-semibold text-slate-800 dark:text-white mb-4 flex items-center" > < span class = "iconify mr-2 text-blue-500" data-icon = "ph:gear-six-bold" > < / span > < span data-i18n = "settings" > < / span > < / h2 >
< div class = "space-y-4" >
2025-09-19 11:32:05 +06:00
<!-- Theme toggle switch -->
2025-09-16 15:08:43 +06:00
< div class = "flex items-center justify-between" >
< label for = "theme-toggle-btn" class = "font-medium" data-i18n = "theme" > < / label >
< button id = "theme-toggle-btn" class = "p-2 rounded-full text-slate-500 hover:bg-slate-200 dark:text-slate-400 dark:hover:bg-slate-700 transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 focus:ring-offset-white dark:focus:ring-offset-gray-900" >
< span class = "sr-only" > Toggle theme< / span >
< span class = "iconify text-xl sun-icon" data-icon = "ph:sun-bold" > < / span >
< span class = "iconify text-xl moon-icon" data-icon = "ph:moon-bold" > < / span >
< / button >
< / div >
2025-09-19 11:32:05 +06:00
<!-- Language selector -->
2025-09-16 15:08:43 +06:00
< div class = "flex items-center justify-between" >
< label for = "lang-switcher" data-i18n = "language" class = "font-medium" > < / label >
< select id = "lang-switcher" class = "bg-slate-100 dark:bg-gray-700 border border-slate-300 dark:border-gray-600 rounded-md p-1.5 text-sm focus:ring-blue-500 focus:border-blue-500" >
< option value = "en" > English< / option >
< option value = "zh" > 中文< / option >
< / select >
< / div >
2025-09-19 11:32:05 +06:00
<!-- GitHub proxy toggle -->
2025-09-16 15:08:43 +06:00
< div class = "flex items-center justify-between" >
< label for = "gh-proxy" data-i18n = "proxyLabel" class = "font-medium text-sm" > < / label >
< label class = "relative inline-flex items-center cursor-pointer" >
< input type = "checkbox" id = "gh-proxy" name = "gh-proxy" class = "sr-only peer" >
< div class = "w-11 h-6 bg-slate-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600" > < / div >
< / label >
< / div >
2025-09-19 11:32:05 +06:00
<!-- Clear cache button -->
2025-09-16 15:08:43 +06:00
< div >
< button data-i18n = "clearCache" id = "clear-cache" class = "w-full text-sm font-semibold bg-slate-100 hover:bg-slate-200 dark:bg-gray-700 dark:hover:bg-gray-600 text-slate-700 dark:text-slate-200 py-2 px-4 rounded-lg transition-colors flex items-center justify-center gap-2" > < / button >
< / div >
< / div >
< / div >
< / div >
< / div >
2025-09-19 11:32:05 +06:00
<!-- Install command section -->
2025-09-16 15:08:43 +06:00
< div class = "bg-white/60 dark:bg-gray-800/60 rounded-2xl shadow-lg ring-1 ring-black/5 backdrop-blur-xl overflow-hidden content-card" style = "transition-delay: 300ms;" >
< div class = "p-6 md:p-8" >
< h2 class = "text-lg font-semibold text-slate-800 dark:text-white mb-4 flex items-center" data-i18n = "installCmdLabel" > < span class = "iconify mr-2 text-blue-500" data-icon = "ph:terminal-window-bold" > < / span > < / h2 >
2025-09-19 11:32:05 +06:00
<!-- Version and tool selection controls -->
2025-09-16 15:08:43 +06:00
< div class = "flex flex-wrap items-center gap-2 mb-4" >
2025-09-19 11:32:05 +06:00
<!-- Version selection buttons (populated by JavaScript) -->
2025-09-16 15:08:43 +06:00
< div id = "version-buttons" class = "flex flex-wrap items-center gap-2 p-1 bg-slate-100 dark:bg-gray-700/50 rounded-lg" >
< / div >
2025-09-19 11:32:05 +06:00
<!-- Tool selection (curl/wget) -->
2025-09-16 15:08:43 +06:00
< div class = "flex items-center gap-2 p-1 bg-slate-100 dark:bg-gray-700/50 rounded-lg" >
< label class = "flex items-center px-2 cursor-pointer" >
< input type = "radio" name = "tool" value = "curl" checked class = "w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600" >
< span class = "ml-2 text-sm font-medium" > curl< / span >
< / label >
< label class = "flex items-center px-2 cursor-pointer" >
< input type = "radio" name = "tool" value = "wget" class = "w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600" >
< span class = "ml-2 text-sm font-medium" > wget< / span >
< / label >
< / div >
< / div >
2025-09-26 13:29:16 +08:00
<!-- Command display area with copy button -->
< div id = "command-container" class = "relative bg-slate-900 dark:bg-black/50 p-4 text-sm" >
< button id = "copy-btn" class = "absolute top-3 right-3 p-1.5 bg-slate-700 hover:bg-slate-600 rounded-md text-slate-300 hover:text-white transition-colors" >
< span class = "sr-only" > Copy command< / span >
< span class = "iconify text-lg copy-icon" data-icon = "ph:copy-bold" > < / span >
< span class = "iconify text-lg check-icon hidden" data-icon = "ph:check-bold" > < / span >
< / button >
< div id = "command" > < / div >
< / div >
2025-09-16 15:08:43 +06:00
< / div >
< / div >
2025-09-08 10:04:22 +08:00
2025-09-16 15:08:43 +06:00
2025-09-19 11:32:05 +06:00
<!-- Download sections container (populated by JavaScript) -->
2025-09-16 15:08:43 +06:00
< div id = "download-rows" class = "space-y-8 content-card" style = "transition-delay: 400ms;" >
2025-09-19 11:32:05 +06:00
<!-- Loading overlay shown while fetching version data -->
2025-09-16 15:08:43 +06:00
< div class = "loading-overlay fixed inset-0 bg-slate-100/80 dark:bg-gray-900/80 flex flex-col items-center justify-center z-50" id = "loading" >
< span class = "iconify text-4xl text-blue-600 animate-spin" data-icon = "ph:spinner-gap-bold" > < / span >
< span data-i18n = "loading" class = "mt-4 text-lg font-medium" > < / span >
< / div >
< / div >
< / main >
2025-09-19 11:32:05 +06:00
<!-- Footer with banner - style social links -->
< footer class = "mt-16 pt-8 border-t border-slate-200 dark:border-gray-700/50" >
< div class = "text-center" >
<!-- Social links without container background -->
< div class = "flex justify-center items-center gap-4" >
<!-- Telegram link -->
< a href = "https://t.me/mikrotikpatch" target = "_blank"
class="group flex items-center gap-3 px-6 py-3 rounded-xl bg-gradient-to-r from-blue-500 to-blue-600 hover:from-blue-600 hover:to-blue-700 text-white shadow-md hover:shadow-lg transition-all duration-300 hover:scale-105">
< span class = "iconify text-xl transition-transform duration-300 group-hover:rotate-12" data-icon = "ph:telegram-logo-bold" > < / span >
< span class = "font-semibold text-sm" > Telegram< / span >
< / a >
<!-- GitHub link -->
< a href = "https://github.com/elseif/MikroTikPatch" target = "_blank"
class="group flex items-center gap-3 px-6 py-3 rounded-xl bg-gradient-to-r from-gray-800 to-gray-900 hover:from-gray-700 hover:to-gray-800 dark:from-slate-600 dark:to-slate-700 dark:hover:from-slate-500 dark:hover:to-slate-600 text-white shadow-md hover:shadow-lg transition-all duration-300 hover:scale-105">
< span class = "iconify text-xl transition-transform duration-300 group-hover:rotate-12" data-icon = "ph:github-logo-bold" > < / span >
< span class = "font-semibold text-sm" > GitHub< / span >
< / a >
2026-03-30 08:09:57 +08:00
<!-- Telegram Keyget Bot link -->
2026-03-30 08:29:36 +08:00
< a href = "https://t.me/ROS_Keygen_Bot" target = "_blank"
2026-03-30 08:09:57 +08:00
class="group flex items-center gap-3 px-6 py-3 rounded-xl bg-gradient-to-r from-blue-500 to-blue-600 hover:from-blue-600 hover:to-blue-700 text-white shadow-md hover:shadow-lg transition-all duration-300 hover:scale-105">
< span class = "iconify text-xl transition-transform duration-300 group-hover:rotate-12" data-icon = "ph:telegram-logo-bold" > < / span >
< span class = "font-semibold text-sm" > Keygen Bot< / span >
< / a >
2025-09-19 11:32:05 +06:00
< / div >
< / div >
2025-09-16 15:08:43 +06:00
< / footer >
< / div >
2025-09-08 10:22:15 +08:00
2025-09-16 15:08:43 +06:00
2025-09-19 11:32:05 +06:00
<!-- URLs modal for cache clearing -->
2025-09-16 15:08:43 +06:00
< div id = "urls-pannel" class = "fixed inset-0 bg-black/60 backdrop-blur-sm z-50 flex items-center justify-center p-4 hidden" tabindex = "-1" >
< div class = "bg-white dark:bg-gray-800 rounded-xl shadow-2xl w-full max-w-2xl max-h-[90vh] flex flex-col transform transition-all opacity-0 scale-95" id = "modal-content" >
< header class = "p-4 border-b border-slate-200 dark:border-gray-700 flex justify-between items-center" >
< h3 class = "text-lg font-semibold text-slate-800 dark:text-white" data-i18n = "copyUrlsClearCache" > < / h3 >
< button id = "urls-close" class = "p-1 rounded-full text-slate-400 hover:bg-slate-100 dark:hover:bg-gray-700" >
< span class = "sr-only" > Close modal< / span >
< span class = "iconify text-2xl" data-icon = "ph:x-bold" > < / span >
< / button >
< / header >
2025-09-19 11:32:05 +06:00
<!-- URLs list display area -->
2025-09-16 15:08:43 +06:00
< div id = "urls-list" class = "p-6 text-sm text-slate-600 dark:text-slate-400 overflow-y-auto custom-scrollbar flex-grow bg-slate-50 dark:bg-gray-900/50 rounded-b-xl whitespace-pre-wrap break-all" > < / div >
< footer class = "p-4 border-t border-slate-200 dark:border-gray-700" >
< button id = "copy-to-clear-cache" class = "w-full bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-lg transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 focus:ring-offset-white dark:focus:ring-offset-gray-800 flex items-center justify-center gap-2" data-i18n = "copyUrlsClearCache" href = "#" > < / button >
< / footer >
< / div >
< / div >
2025-09-08 10:04:22 +08:00
2025-09-19 11:32:05 +06:00
<!-- Changelog popup modal -->
< div id = "changelog-modal" class = "fixed inset-0 bg-black/60 backdrop-blur-sm z-50 flex items-center justify-center p-4 hidden" tabindex = "-1" >
< div class = "bg-white dark:bg-gray-800 rounded-xl shadow-2xl w-full max-w-4xl max-h-[90vh] flex flex-col transform transition-all opacity-0 scale-95" id = "changelog-modal-content" >
< header class = "p-4 border-b border-slate-200 dark:border-gray-700 flex justify-between items-center" >
< h3 class = "text-lg font-semibold text-slate-800 dark:text-white flex items-center gap-2" >
< span class = "iconify text-blue-500" data-icon = "ph:newspaper-clipping-bold" > < / span >
< span data-i18n = "changelog" > < / span >
< / h3 >
< button id = "changelog-close" class = "p-1 rounded-full text-slate-400 hover:bg-slate-100 dark:hover:bg-gray-700" >
< span class = "sr-only" > Close modal< / span >
< span class = "iconify text-2xl" data-icon = "ph:x-bold" > < / span >
< / button >
< / header >
<!-- Changelog content display area -->
< div id = "changelog-content" class = "p-6 text-sm text-slate-600 dark:text-slate-400 overflow-y-auto custom-scrollbar flex-grow bg-slate-50 dark:bg-gray-900/50 rounded-b-xl" >
<!-- Loading state for changelog -->
< div class = "flex items-center justify-center py-8" >
< span class = "iconify text-4xl text-blue-600 animate-spin" data-icon = "ph:spinner-gap-bold" > < / span >
< span class = "ml-4 text-lg font-medium" data-i18n = "loadingChangelog" > < / span >
< / div >
< / div >
< / div >
< / div >
2025-09-08 10:04:22 +08:00
2025-09-19 11:32:05 +06:00
<!-- Main JavaScript functionality -->
2025-09-16 15:08:43 +06:00
< script >
document.addEventListener("DOMContentLoaded", async () => {
2025-09-19 11:32:05 +06:00
// Icon definitions for consistent icon usage
2025-09-16 15:08:43 +06:00
const ICONS = {
download: 'ph:download-simple-bold',
changelog: 'ph:newspaper-clipping-bold',
trash: 'ph:trash-bold',
copyAndOpen: 'ph:copy-simple-bold'
};
2025-09-19 11:32:05 +06:00
// Function to fetch latest version numbers with timeout fallback
2025-09-16 15:08:43 +06:00
async function fetch_latest_versions(url, defaultValue, timeout = 3000) {
try {
const controller = new AbortController();
const id = setTimeout(() => controller.abort(), timeout);
const res = await fetch(url, { signal: controller.signal });
clearTimeout(id);
if (res.ok) {
const text = await res.text();
2025-09-19 11:32:05 +06:00
return text.split(" ")[0]; // Extract version number from response
2025-09-16 15:08:43 +06:00
}
} catch (e) {
console.warn("fetch failed or timeout:", e);
2025-08-27 18:35:01 +08:00
}
2025-09-19 11:32:05 +06:00
return defaultValue; // Return fallback version if fetch fails
2025-08-27 18:35:01 +08:00
}
2025-09-19 11:32:05 +06:00
// Internationalization (i18n) translations for English and Chinese
2025-09-08 10:22:15 +08:00
const i18n = {
en: {
2025-09-16 15:08:43 +06:00
language: "Language",
infoLabel1:`If you are already running < b > patched< / b > RouterOS, upgrading to the latest version can be done by clicking on < b > "Check For Updates"< / b > in < b > QuickSet or System > Packages< / b > menu in WebFig or WinBox.`,
infoLabel2:`For more information about < b > install and upgrade< / b > see the < a href = "https://help.mikrotik.com/docs/" target = "_blank" class = "text-blue-500 dark:text-blue-400 hover:underline" > Documentation< / a > . For more information about < b > patch< / b > see the < a href = "https://github.com/elseif/MikroTikPatch" target = "_blank" class = "text-blue-500 dark:text-blue-400 hover:underline" > MikroTikPatch< / a > .`,
installCmdLabel:"Install Command",
proxyLabel:"Enable GitHub Proxy Accelerator",
clearCache:"Clear gh-proxy Cache",
copyUrlsClearCache:"Copy URLs & Open Clear Cache Page",
loading:"Loading latest versions...",
quickInformation:"Quick Information",
settings:"Settings",
theme:"Theme",
2025-09-19 11:32:05 +06:00
changelog:"Changelog",
loadingChangelog:"Loading changelog...",
changelogError:"Failed to load changelog. Please try again later.",
2025-09-08 10:22:15 +08:00
},
zh: {
2025-09-16 15:08:43 +06:00
language: "语言",
2025-09-08 10:22:15 +08:00
infoLabel1:`如果你已经在运行< b > Patch< / b > 过的RouterOS, 可以通过WebFig或WinBox中的< b > QuickSet或System > Packages< / b > 菜单点击< b > "Check For Updates"< / b > 来升级到最新版本。`,
2025-09-16 15:08:43 +06:00
infoLabel2:`有关< b > 安装和升级< / b > 的更多信息,请参阅< a href = "https://help.mikrotik.com/docs/" target = "_blank" class = "text-blue-500 dark:text-blue-400 hover:underline" > 文档< / a > < br > 有关< b > Patch< / b > 的更多信息,请参阅< a href = "https://github.com/elseif/MikroTikPatch" target = "_blank" class = "text-blue-500 dark:text-blue-400 hover:underline" > MikroTikPatch< / a > `,
installCmdLabel:"安装命令",
proxyLabel:"启用GitHub代理加速",
clearCache:"清除gh-proxy缓存",
copyUrlsClearCache:"复制链接并打开缓存清除页面",
loading:"正在加载最新版本...",
quickInformation:"快速信息",
settings:"设置",
theme:"主题",
2025-09-19 11:32:05 +06:00
changelog:"更新日志",
loadingChangelog:"正在加载更新日志...",
changelogError:"加载更新日志失败,请稍后重试。",
2025-09-08 10:22:15 +08:00
}
};
2025-09-16 15:08:43 +06:00
2025-09-19 11:32:05 +06:00
// Function to set language and update all i18n elements
2025-09-08 10:22:15 +08:00
function setLanguage(lang) {
document.querySelectorAll("[data-i18n]").forEach(el => {
const key = el.getAttribute("data-i18n");
if (i18n[lang] & & i18n[lang][key]) {
2025-09-19 11:32:05 +06:00
// Special handling for buttons that need icons
2025-09-16 15:08:43 +06:00
if (key === 'clearCache') {
el.innerHTML = `< span class = "iconify" data-icon = "${ICONS.trash}" > < / span > ${i18n[lang][key]}`;
} else if (key === 'copyUrlsClearCache' & & el.id === 'copy-to-clear-cache') {
el.innerHTML = `< span class = "iconify" data-icon = "${ICONS.copyAndOpen}" > < / span > ${i18n[lang][key]}`;
} else if (key === 'installCmdLabel') {
el.innerHTML = `< span class = "iconify mr-2 text-blue-500" data-icon = "ph:terminal-window-bold" > < / span > ${i18n[lang][key]}`;
}
else {
el.innerHTML = i18n[lang][key];
}
2025-09-08 10:22:15 +08:00
}
});
2025-09-19 11:32:05 +06:00
localStorage.setItem("lang", lang); // Save language preference
2025-09-08 10:22:15 +08:00
}
2025-09-16 15:08:43 +06:00
2025-09-19 11:32:05 +06:00
// Initialize language based on saved preference or browser language
2025-09-08 10:22:15 +08:00
function initLanguage() {
let savedLang = localStorage.getItem("lang");
if (!savedLang) {
2025-09-19 11:32:05 +06:00
// Auto-detect Chinese language, default to English
2025-09-08 10:22:15 +08:00
savedLang = navigator.language.startsWith("zh") ? "zh" : "en";
}
document.getElementById("lang-switcher").value = savedLang;
setLanguage(savedLang);
}
2025-09-16 15:08:43 +06:00
2025-09-19 11:32:05 +06:00
// Language switcher event listener
2025-09-08 10:22:15 +08:00
document.getElementById("lang-switcher").addEventListener("change", e => {
2025-09-16 15:08:43 +06:00
setLanguage(e.target.value);
2025-09-08 10:22:15 +08:00
});
2025-09-19 11:32:05 +06:00
// Theme toggle functionality
2025-09-16 15:08:43 +06:00
const themeBtn = document.getElementById("theme-toggle-btn");
const htmlEl = document.documentElement;
const sunIcon = themeBtn.querySelector('.sun-icon');
const moonIcon = themeBtn.querySelector('.moon-icon');
2025-09-08 10:22:15 +08:00
2025-09-19 11:32:05 +06:00
// Update theme UI icons based on current theme
2025-09-16 15:08:43 +06:00
function updateThemeUI() {
if (htmlEl.classList.contains('dark')) {
sunIcon.classList.remove('hidden');
moonIcon.classList.add('hidden');
} else {
sunIcon.classList.add('hidden');
moonIcon.classList.remove('hidden');
}
}
2025-09-19 11:32:05 +06:00
// Initialize theme based on saved preference or system preference
2025-09-16 15:08:43 +06:00
function initializeTheme() {
const savedTheme = localStorage.getItem("theme");
if (savedTheme) {
htmlEl.classList.toggle("dark", savedTheme === "dark");
} else {
2025-09-19 11:32:05 +06:00
// Use system preference if no saved theme
2025-09-16 15:08:43 +06:00
htmlEl.classList.toggle("dark", window.matchMedia('(prefers-color-scheme: dark)').matches);
}
updateThemeUI();
}
2025-09-19 11:32:05 +06:00
// Theme toggle button event listener
2025-09-16 15:08:43 +06:00
themeBtn.addEventListener("click", () => {
htmlEl.classList.toggle("dark");
localStorage.setItem("theme", htmlEl.classList.contains("dark") ? "dark" : "light");
updateThemeUI();
});
2025-09-19 11:32:05 +06:00
// Initialize theme and language
2025-09-16 15:08:43 +06:00
initializeTheme();
initLanguage();
2025-09-19 11:32:05 +06:00
// Fetch latest version numbers from MikroTik servers
2025-09-16 15:08:43 +06:00
const loadingOverlay = document.getElementById("loading");
const [routeros7_stable, routeros7_testing, routeros6_longterm, routeros6_stable] = await Promise.all([
2025-10-22 14:12:58 +08:00
fetch_latest_versions(`https://upgrade.mikrotik.ltd/routeros/NEWESTa7.stable?ts=${Date.now()}`, "7.19.4"),
fetch_latest_versions(`https://upgrade.mikrotik.ltd/routeros/NEWESTa7.testing?ts=${Date.now()}`, "7.20rc1"),
fetch_latest_versions(`https://upgrade.mikrotik.ltd/routeros/NEWEST6.long-term?ts=${Date.now()}`, "6.49.18"),
fetch_latest_versions(`https://upgrade.mikrotik.ltd/routeros/NEWEST6.stable?ts=${Date.now()}`, "6.49.19"),
2025-09-16 15:08:43 +06:00
]);
2025-09-19 11:32:05 +06:00
// Hide loading overlay and remove loading class
2025-09-16 15:08:43 +06:00
loadingOverlay.style.display = "none";
document.body.classList.remove('loading');
2025-09-08 10:04:22 +08:00
2025-09-19 11:32:05 +06:00
// Download sections configuration with versions and file types
2025-09-08 10:04:22 +08:00
const downloads =[
{
title:"RouterOS v7",
2025-09-16 15:08:43 +06:00
versions:[ { title:"Stable", version:routeros7_stable }, { title:"Testing", version:routeros7_testing } ],
2025-09-08 10:04:22 +08:00
groups: [
2025-09-16 15:08:43 +06:00
{ title:"ARM64 / AMPERE", arch:"arm64", downloads:[ { title:"Main package", type:"main" }, { title:"Extra packages", type:"extra" }, { title:"ISO image for AMPERE", type:"iso" } ] },
{ title:"X86", arch:"x86", downloads:[ { title:"Main package", type:"main" }, { title:"Extra packages", type:"extra" }, { title:"CD Image", type:"iso" }, { title:"Install image", type:"install" } ] },
{ title:"GENERAL", arch:"general", downloads:[ { title:"Netinstall (Windows)", type:"netinstall_windows" }, { title:"Netinstall (CLI Linux)", type:"netinstall_linux_cli" }, { title:"Changelog", type:"changlog" } ] }
2025-09-08 10:04:22 +08:00
]
},
{
title:"RouterOS v6",
2025-09-16 15:08:43 +06:00
versions:[ { title:"Long-Term", version:routeros6_longterm }, { title:"Stable", version:routeros6_stable } ],
2025-09-08 10:04:22 +08:00
groups: [
2025-09-16 15:08:43 +06:00
{ title:"X86", arch:"x86", downloads:[ { title:"Extra packages", type:"extra" }, { title:"CD Image", type:"iso" }, { title:"Install image", type:"install" }, { title:"Changelog", type:"changlog" } ] }
2025-09-08 10:04:22 +08:00
]
},
{
title:"Cloud Hosted Router",
2025-09-16 15:08:43 +06:00
versions:[ { title:"Long-Term", version:routeros6_longterm }, { title:"Stable", version:routeros6_stable }, { title:"Stable", version:routeros7_stable }, { title:"Testing", version:routeros7_testing } ],
2025-09-08 10:04:22 +08:00
groups:[
2025-09-16 15:08:43 +06:00
{ title:"X86", arch:"x86", downloads:[ { title:"Main package", type:"main" }, { title:"Extra packages", type:"extra" }, { title:"VHDX image", type:"vhdx" }, { title:"VMDK image", type:"vmdk" }, { title:"VDI image", type:"vdi" }, { title:"VirtualPC image", type:"vhd" }, { title:"Raw disk image", type:"img" }, { title:"OVA template", type:"ova" } ] },
{ title:"ARM64 / AMPERE", arch:"arm64", downloads:[ { title:"Main package", type:"main" }, { title:"Extra packages", type:"extra" }, { title:"VHDX image", type:"vhdx" }, { title:"VMDK image", type:"vmdk" }, { title:"VDI image", type:"vdi" }, { title:"VirtualPC image", type:"vhd" }, { title:"Raw disk image", type:"img" } ] }
2025-09-08 10:04:22 +08:00
]
},
2025-09-16 15:08:43 +06:00
];
2025-09-08 10:04:22 +08:00
2025-09-19 11:32:05 +06:00
// Function to generate file names based on version, architecture, and type
2025-09-16 15:08:43 +06:00
const fileNames = (version, arch, type) => {
2025-09-08 10:04:22 +08:00
const files = [];
2025-09-19 11:32:05 +06:00
// RouterOS v6 only supports x86 architecture
2025-09-16 15:08:43 +06:00
if (version.charAt(0) === "6" & & arch !== "x86") return files;
2025-09-19 11:32:05 +06:00
// Main package for RouterOS v7
2025-09-16 15:08:43 +06:00
if (type === "main" & & version.charAt(0) === "7") files.push(`routeros-${version}${arch === "x86" ? "" : "-" + arch}.npk`);
2025-09-19 11:32:05 +06:00
// ISO images
2025-09-16 15:08:43 +06:00
if (type === "iso") files.push(`mikrotik-${version}${arch === "x86" ? "" : "-" + arch}.iso`);
2025-09-19 11:32:05 +06:00
// Extra packages
2025-09-16 15:08:43 +06:00
if (type === "extra") files.push(`all_packages-${arch}-${version}.zip`);
2025-09-19 11:32:05 +06:00
// Install images
2025-09-16 15:08:43 +06:00
if (type === "install") files.push(`install-image-${version}${arch === "x86" ? "" : "-" + arch}.zip`);
2025-09-19 11:32:05 +06:00
// Virtual machine images
2025-09-16 15:08:43 +06:00
if (type in { vhdx: 1, vmdk: 1, vdi: 1, vhd: 1, img: 1 }) {
files.push(`chr-${version}${arch === "x86" ? "" : "-" + arch}.${type}.zip`);
2025-09-19 11:32:05 +06:00
// Add legacy BIOS version for x86 RouterOS v7
2025-09-16 15:08:43 +06:00
if (arch === "x86" & & version.charAt(0) !== "6") files.push(`chr-${version}-legacy-bios.${type}.zip`);
2025-09-08 10:04:22 +08:00
}
2025-09-19 11:32:05 +06:00
// OVA templates (RouterOS v7 only)
2025-09-16 15:08:43 +06:00
if (type === "ova" & & version.charAt(0) !== "6") {
files.push(`chr-${version}${arch === "x86" ? "" : "-" + arch}.${type}.zip`);
if (arch === "x86") files.push(`chr-${version}-legacy-bios.${type}.zip`);
}
2025-09-19 11:32:05 +06:00
// Netinstall packages
2025-09-16 15:08:43 +06:00
if (type === "netinstall_windows") files.push(`netinstall-${version}.zip`);
if (type === "netinstall_linux_cli") files.push(`netinstall-${version}.tar.gz`);
2025-09-08 10:04:22 +08:00
return files;
};
2025-08-27 18:35:01 +08:00
2025-09-19 11:32:05 +06:00
// Generate base URL for downloads with optional GitHub proxy
2025-09-16 15:08:43 +06:00
const baseUrl = (version, arch) => `${localStorage.getItem("gh-proxy-enabled") === "true" ? "https://gh-proxy.com/" : ""}https://github.com/elseif/MikroTikPatch/releases/download/${version}${arch === "x86" || arch === "general" ? "" : "-" + arch}/`;
2025-09-19 11:32:05 +06:00
// Clear and populate download sections
2025-08-27 18:35:01 +08:00
const container = document.getElementById("download-rows");
2025-09-16 15:08:43 +06:00
container.innerHTML = '';
2025-09-19 11:32:05 +06:00
// Generate download sections dynamically
2025-08-27 18:35:01 +08:00
downloads.forEach((download, index) => {
2025-09-16 15:08:43 +06:00
const accordion = document.createElement("details");
accordion.className = "bg-white/60 dark:bg-gray-800/60 rounded-2xl shadow-lg ring-1 ring-black/5 backdrop-blur-xl overflow-hidden group";
2025-09-19 11:32:05 +06:00
accordion.open = index === 0; // Open first section by default
2025-09-16 15:08:43 +06:00
const summary = document.createElement("summary");
summary.className = "p-6 cursor-pointer text-xl font-bold text-slate-800 dark:text-white flex justify-between items-center";
summary.innerHTML = `< span > ${download.title}< / span > < span class = "iconify text-2xl transition-transform duration-300 group-open:rotate-180" data-icon = "ph:caret-down-bold" > < / span > `;
const content = document.createElement("div");
content.className = "px-6 pb-6";
const table = document.createElement("div");
table.className = "overflow-x-auto";
2025-08-27 18:35:01 +08:00
table.innerHTML = `
2025-09-16 15:08:43 +06:00
< table class = "w-full min-w-max text-sm text-left text-slate-500 dark:text-slate-400" >
< thead class = "text-xs text-slate-700 dark:text-slate-200 uppercase bg-slate-50/80 dark:bg-gray-700/80" >
< tr >
< th scope = "col" class = "px-6 py-3 rounded-l-lg" > Package / Architecture< / th >
${download.versions.map(v => `< th scope = "col" class = "px-6 py-3 text-center" > ${v.version}< br > < span class = "font-normal normal-case" > ${v.title}< / span > < / th > `).join("")}
< th class = "rounded-r-lg" > < / th >
2025-08-27 18:35:01 +08:00
< / tr >
2025-09-16 15:08:43 +06:00
< / thead >
< tbody >
${download.groups.map(g => `
< tr class = "bg-slate-100/80 dark:bg-gray-700/50" >
< th colspan = "${download.versions.length + 2}" class = "px-6 py-2 text-base font-semibold text-slate-800 dark:text-white" > ${g.title}< / th >
< / tr >
${g.downloads.map(d => `
< tr class = "bg-transparent border-b last:border-b-0 border-slate-200 dark:border-gray-700 hover:bg-slate-50/50 dark:hover:bg-gray-800/50 transition-colors" >
< td class = "px-6 py-4 font-medium text-slate-900 dark:text-white whitespace-nowrap" > ${d.title}< / td >
2025-08-27 18:35:01 +08:00
${download.versions.map(v => {
2025-09-16 15:08:43 +06:00
if (d.type === "changlog") {
2025-09-19 11:32:05 +06:00
return `< td class = "px-6 py-4 text-center" > < button onclick = "showChangelog('${v.version}')" class = "inline-flex items-center gap-1.5 text-blue-500 dark:text-blue-400 hover:underline cursor-pointer" title = "Changelog" > < span class = "iconify" data-icon = "${ICONS.changelog}" > < / span > View< / button > < / td > `;
2025-09-08 10:04:22 +08:00
}
2025-09-16 15:08:43 +06:00
const files = fileNames(v.version, g.arch, d.type);
if (!files || files.length === 0) return `< td class = "px-6 py-4" > < / td > `;
const links = files.map(file => {
const url = `${baseUrl(v.version, g.arch)}${file}`;
let label = "Download";
if (download.title === "Cloud Hosted Router" & & file.includes('chr')) {
2025-09-18 23:29:20 +08:00
if (v.version.charAt(0) === "6" ){
label = "Legacy";
} else {
label = file.includes("legacy-bios") ? "Legacy" : "UEFI";
}
2025-09-16 15:08:43 +06:00
}
return `< a href = "${url}" class = "inline-flex items-center gap-1.5 text-blue-500 dark:text-blue-400 hover:underline" title = "${file}" target = "_blank" > < span class = "iconify" data-icon = "${ICONS.download}" > < / span > ${label}< / a > `;
2025-09-19 12:05:41 +08:00
}).join("");
2025-09-19 12:07:24 +08:00
return `< td class = "px-6 py-4 text-center space-x-2" > ${links}< / td > `;
2025-08-27 18:35:01 +08:00
}).join("")}
2025-09-16 15:08:43 +06:00
< td > < / td >
2025-08-27 18:35:01 +08:00
< / tr >
2025-09-16 15:08:43 +06:00
`).join("")}
`).join("")}
< / tbody >
< / table >
`;
2025-08-27 18:35:01 +08:00
2025-09-16 15:08:43 +06:00
content.appendChild(table);
accordion.appendChild(summary);
accordion.appendChild(content);
container.appendChild(accordion);
});
2025-09-19 11:32:05 +06:00
// GitHub proxy functionality
2025-09-16 15:08:43 +06:00
const ghProxyCheckbox = document.getElementById("gh-proxy");
const clearCacheBtn = document.getElementById("clear-cache");
2025-09-26 13:15:28 +08:00
if (localStorage.getItem("gh-proxy-enabled") === null & & navigator.language.startsWith("zh")) {
// Enable GitHub proxy by default for Chinese users
localStorage.setItem("gh-proxy-enabled", "true");
}
2025-08-27 18:35:01 +08:00
ghProxyCheckbox.checked = localStorage.getItem("gh-proxy-enabled") === "true";
2025-09-19 12:27:07 +08:00
clearCacheBtn.style.display = ghProxyCheckbox.checked ? "inline-flex" : "none";
2025-09-19 11:32:05 +06:00
// Update all download links with/without GitHub proxy
2025-09-16 15:08:43 +06:00
function updateAllDownloadLinks() {
2025-09-19 11:32:05 +06:00
// Only apply proxy to download links (releases/download), not to other GitHub links
document.querySelectorAll('a[href*="github.com/elseif/MikroTikPatch/releases/download"]').forEach(link => {
2025-09-16 15:08:43 +06:00
const isProxyEnabled = localStorage.getItem("gh-proxy-enabled") === "true";
const hasProxy = link.href.includes('gh-proxy.com');
if (isProxyEnabled & & !hasProxy) {
link.href = link.href.replace("https://github.com/", "https://gh-proxy.com/https://github.com/");
} else if (!isProxyEnabled & & hasProxy) {
link.href = link.href.replace("https://gh-proxy.com/", "");
}
});
2025-09-19 11:32:05 +06:00
// Update command display if a version is selected
2025-08-27 18:35:01 +08:00
const activeBtn = document.querySelector("#version-buttons button.active");
2025-09-26 13:10:28 +08:00
activeBtn ? updateCommand(activeBtn.dataset.version) : updateCommand();
2025-09-16 15:08:43 +06:00
}
2025-08-27 18:35:01 +08:00
2025-09-19 11:32:05 +06:00
// GitHub proxy toggle event listener
2025-09-16 15:08:43 +06:00
ghProxyCheckbox.addEventListener("change", () => {
localStorage.setItem("gh-proxy-enabled", ghProxyCheckbox.checked ? "true" : "false");
2025-09-19 12:27:07 +08:00
clearCacheBtn.style.display = ghProxyCheckbox.checked ? "inline-flex" : "none";
2025-09-16 15:08:43 +06:00
updateAllDownloadLinks();
});
2025-09-19 11:32:05 +06:00
// Cache clearing modal functionality
2025-09-16 15:08:43 +06:00
const modal = document.getElementById("urls-pannel");
const modalContent = document.getElementById("modal-content");
clearCacheBtn.addEventListener("click", () => {
const links = document.querySelectorAll('a[href^="https://gh-proxy.com/https://github.com/elseif/MikroTikPatch/"]');
document.getElementById("urls-list").innerText = Array.from(links).map(a => a.href).filter(href => !href.includes(".yml")).join("\n");
modal.classList.remove('hidden');
setTimeout(() => { modalContent.classList.add('opacity-100', 'scale-100'); modalContent.classList.remove('opacity-0', 'scale-95'); }, 10);
});
2025-09-19 11:32:05 +06:00
// Modal close functionality
2025-09-16 15:08:43 +06:00
document.getElementById("urls-close").addEventListener("click", () => {
modalContent.classList.remove('opacity-100', 'scale-100');
modalContent.classList.add('opacity-0', 'scale-95');
setTimeout(() => modal.classList.add('hidden'), 200);
});
2025-08-27 18:35:01 +08:00
2025-09-19 11:32:05 +06:00
// Copy URLs and open cache clearing page
2025-09-16 15:08:43 +06:00
document.getElementById("copy-to-clear-cache").addEventListener("click", () => {
navigator.clipboard.writeText(document.getElementById("urls-list").innerText);
window.open("https://cache.gh-proxy.com/", "_blank");
document.getElementById("urls-close").click();
});
2025-08-27 18:35:01 +08:00
2025-09-19 11:32:05 +06:00
// Changelog modal functionality
const changelogModal = document.getElementById("changelog-modal");
const changelogModalContent = document.getElementById("changelog-modal-content");
const changelogContent = document.getElementById("changelog-content");
const changelogClose = document.getElementById("changelog-close");
// Fetch changelog content from MikroTik servers
async function fetchChangelog(version) {
try {
const response = await fetch(`https://upgrade.mikrotik.ltd/routeros/${version}/CHANGELOG`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const text = await response.text();
return text;
} catch (error) {
console.error("Failed to fetch changelog:", error);
return null;
}
}
// Global function to show changelog modal
window.showChangelog = function(version) {
changelogModal.classList.remove('hidden');
changelogContent.innerHTML = `
< div class = "flex items-center justify-center py-8" >
< span class = "iconify text-4xl text-blue-600 animate-spin" data-icon = "ph:spinner-gap-bold" > < / span >
< span class = "ml-4 text-lg font-medium" data-i18n = "loadingChangelog" > < / span >
< / div >
`;
setLanguage(document.getElementById("lang-switcher").value);
setTimeout(() => {
changelogModalContent.classList.add('opacity-100', 'scale-100');
changelogModalContent.classList.remove('opacity-0', 'scale-95');
}, 10);
// Fetch and display changelog content
fetchChangelog(version).then(content => {
if (content) {
changelogContent.innerHTML = `
< div class = "prose prose-slate dark:prose-invert max-w-none" >
< pre class = "whitespace-pre-wrap text-sm leading-relaxed" > ${content}< / pre >
< / div >
`;
} else {
// Show error state with retry button
changelogContent.innerHTML = `
< div class = "flex flex-col items-center justify-center py-8 text-center" >
< span class = "iconify text-6xl text-red-500 mb-4" data-icon = "ph:warning-circle-bold" > < / span >
< p class = "text-lg font-medium text-slate-700 dark:text-slate-300 mb-2" data-i18n = "changelogError" > < / p >
< button onclick = "showChangelog('${version}')" class = "mt-4 px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg transition-colors" >
< span class = "iconify mr-2" data-icon = "ph:arrow-clockwise-bold" > < / span >
Retry
< / button >
< / div >
`;
setLanguage(document.getElementById("lang-switcher").value);
}
});
};
// Hide changelog modal with animation
function hideChangelog() {
changelogModalContent.classList.remove('opacity-100', 'scale-100');
changelogModalContent.classList.add('opacity-0', 'scale-95');
setTimeout(() => changelogModal.classList.add('hidden'), 200);
}
changelogClose.addEventListener("click", hideChangelog);
// Close modal when clicking outside
changelogModal.addEventListener("click", (e) => {
if (e.target === changelogModal) {
hideChangelog();
}
});
// Close modal with Escape key
document.addEventListener("keydown", (e) => {
if (e.key === "Escape" & & !changelogModal.classList.contains("hidden")) {
hideChangelog();
}
});
// Command generation and version selection
2025-09-16 15:08:43 +06:00
const versionBtnsContainer = document.getElementById("version-buttons");
2025-08-27 18:35:01 +08:00
const commandDiv = document.getElementById("command");
const copyBtn = document.getElementById("copy-btn");
2025-09-19 11:32:05 +06:00
// Generate version selection buttons
2025-09-16 15:08:43 +06:00
versionBtnsContainer.innerHTML = `
< button data-version = "${routeros7_stable}" class = "px-3 py-1.5 text-sm font-medium rounded-md hover:bg-blue-200 dark:hover:bg-gray-600 transition-all" > v7 Stable< / button >
< button data-version = "${routeros7_testing}" class = "px-3 py-1.5 text-sm font-medium rounded-md hover:bg-blue-200 dark:hover:bg-gray-600 transition-all" > v7 Testing< / button >
< button data-version = "${routeros6_longterm}" class = "px-3 py-1.5 text-sm font-medium rounded-md hover:bg-blue-200 dark:hover:bg-gray-600 transition-all" > v6 Long-Term< / button >
< button data-version = "${routeros6_stable}" class = "px-3 py-1.5 text-sm font-medium rounded-md hover:bg-blue-200 dark:hover:bg-gray-600 transition-all" > v6 Stable< / button >
`;
2025-09-19 11:32:05 +06:00
// Update command display based on selected tool and version
2025-08-27 18:35:01 +08:00
function updateCommand(version) {
const tool = document.querySelector('input[name="tool"]:checked').value;
2025-09-26 13:42:03 +08:00
const _version = version===undefined?"": `< span class = "text-green-500 font-semibold" > VERSION< / span > =< span class = "text-yellow-400" > ${version}< / span > `
2025-09-16 15:08:43 +06:00
const cmd = tool === "curl"
2025-09-26 13:42:03 +08:00
? `< pre class = "text-slate-300" > < code > ${_version}< span class = "text-blue-400 font-bold" > bash< / span > < span class = "text-pink-400" > < (< / span > < span class = "text-blue-400 font-bold" > curl< / span > < span class = "text-teal-400" > https://mikrotik.ltd/chr.sh< / span > < span class = "text-pink-400" > )< / span > < / code > < / pre > `
: `< pre class = "text-slate-300" > < code > ${_version}< span class = "text-blue-400 font-bold" > bash< / span > < span class = "text-pink-400" > < (< / span > < span class = "text-blue-400 font-bold" > wget -O - < / span > < span class = "text-teal-400" > https://mikrotik.ltd/chr.sh< / span > < span class = "text-pink-400" > )< / span > < / code > < / pre > `;
2025-08-27 18:35:01 +08:00
commandDiv.innerHTML = cmd;
}
2025-09-16 15:08:43 +06:00
2025-09-19 11:32:05 +06:00
// Tool selection event listeners
2025-08-27 18:35:01 +08:00
document.querySelectorAll('input[name="tool"]').forEach(radio => {
radio.addEventListener("change", () => {
const activeBtn = document.querySelector("#version-buttons button.active");
2025-09-26 13:10:28 +08:00
activeBtn ? updateCommand(activeBtn.dataset.version) : updateCommand();
2025-08-27 18:35:01 +08:00
});
});
2025-09-19 11:32:05 +06:00
// Version button event listeners
2025-08-27 18:35:01 +08:00
const buttons = document.querySelectorAll("#version-buttons button");
buttons.forEach(btn => {
2025-09-16 15:08:43 +06:00
btn.addEventListener("click", (e) => {
e.preventDefault();
2025-09-26 13:10:28 +08:00
if (btn.classList.contains("active")) {
updateCommand();
btn.classList.remove("active");
}else{
updateCommand(btn.dataset.version);
buttons.forEach(b => b.classList.remove("active"));
btn.classList.add("active");
}
2025-08-27 18:35:01 +08:00
});
});
2025-09-26 13:10:28 +08:00
updateCommand();
2025-09-16 15:08:43 +06:00
2025-09-19 11:32:05 +06:00
// Copy command to clipboard functionality
2025-08-27 18:35:01 +08:00
copyBtn.addEventListener("click", () => {
2025-09-16 15:08:43 +06:00
const code = commandDiv.querySelector("code").innerText;
2025-08-27 18:35:01 +08:00
navigator.clipboard.writeText(code).then(() => {
2025-09-16 15:08:43 +06:00
copyBtn.querySelector('.copy-icon').classList.add('hidden');
copyBtn.querySelector('.check-icon').classList.remove('hidden');
setTimeout(() => {
copyBtn.querySelector('.copy-icon').classList.remove('hidden');
copyBtn.querySelector('.check-icon').classList.add('hidden');
}, 2000);
}).catch(err => console.error("Copy failed:", err));
2025-08-27 18:35:01 +08:00
});
2025-09-16 15:08:43 +06:00
});
2025-08-27 18:35:01 +08:00
< / script >
< / body >
2025-09-18 23:29:20 +08:00
< / html >