MikroTikPatch/index.html
ErrorX b6721ffd7d Redesign index.html
Replaces legacy CSS and table-based layout with modernizes the UI with responsive cards, details/summary accordions, and improved theme/language switching. Adds dynamic loading, new download tables, and enhanced accessibility. Removes old inline styles and scripts, streamlining the code for maintainability and better user experience.
2025-09-16 15:08:43 +06:00

596 lines
No EOL
39 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="en" class="scroll-smooth">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MikroTik RouterOS Patched Versions</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://code.iconify.design/3/3.1.1/iconify.min.js"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preconnect" href="https://fonts.loli.net">
<link rel="preconnect" href="https://gstatic.loli.net" crossorigin>
<link id="webfont-css" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Fira+Code&display=swap" rel="stylesheet">
<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';
function isLoaded(name){
if (!('fonts' in document)) return false;
try { return document.fonts.check('1em "' + name + '"'); } catch (e) { return false; }
}
function switchToMirror(){
if (linkEl.href !== mirrorHref) { linkEl.href = mirrorHref; }
}
if (!('fonts' in document)) { switchToMirror(); return; }
var decided = false;
var timer = setTimeout(function(){
if (!decided && (!isLoaded('Inter') || !isLoaded('Fira Code'))) {
decided = true; switchToMirror();
}
}, 2500);
document.fonts.ready.then(function(){
if (!decided && (!isLoaded('Inter') || !isLoaded('Fira Code'))) {
decided = true; switchToMirror();
} else {
decided = true; clearTimeout(timer);
}
});
})();
</script>
<style type="text/tailwindcss">
body {
font-family: 'Inter', sans-serif;
background-image: radial-gradient(circle at top, #dde3ee 0%, #f1f5f9 60%);
}
html.dark body {
background-image: radial-gradient(circle at top, #1e293b 0%, #0f172a 60%);
}
#command pre, #command code {
font-family: 'Fira Code', monospace;
}
.custom-scrollbar::-webkit-scrollbar {
width: 8px;
}
.custom-scrollbar::-webkit-scrollbar-track {
background: transparent;
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background: #94a3b8;
border-radius: 10px;
}
html.dark .custom-scrollbar::-webkit-scrollbar-thumb {
background: #475569;
}
#version-buttons button.active {
@apply bg-blue-600 text-white shadow-md;
}
summary::-webkit-details-marker {
display: none;
}
.content-card {
@apply transition-all duration-700;
}
.loading .content-card {
@apply opacity-0 translate-y-4;
}
</style>
<script>
tailwind.config = {
darkMode: 'class',
theme: {
extend: {
fontFamily: {
sans: ['Inter', 'sans-serif'],
mono: ['Fira Code', 'monospace'],
},
},
},
}
</script>
</head>
<body class="bg-slate-100 dark:bg-gray-900 text-slate-700 dark:text-slate-300 transition-colors duration-300 loading">
<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>
<div class="container mx-auto max-w-7xl px-4 py-8 md:py-16">
<header class="text-center mb-12 content-card" style="transition-delay: 100ms;">
<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>
<div class="mt-6 flex justify-center items-center gap-4 flex-wrap">
<a href="https://github.com/elseif/MikroTikPatch/actions/workflows/mikrotik_patch_6.yml" target="_blank"><img src="https://mikrotik.ltd/badge/mikrotik_patch_6.yml" alt="Patch Mikrotik RouterOS 6.x"></a>
<a href="https://github.com/elseif/MikroTikPatch/actions/workflows/mikrotik_patch_7.yml" target="_blank"><img src="https://mikrotik.ltd/badge/mikrotik_patch_7.yml" alt="Patch Mikrotik RouterOS 7.x"></a>
<img src="https://img.shields.io/endpoint?logo=icloud&url=https://mikrotik.ltd/status/cloud" alt="Cloud Status">
<img src="https://img.shields.io/endpoint?logo=googlecloudstorage&url=https://mikrotik.ltd/status/dartnode" alt="VPS Status">
</div>
<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>
<main class="space-y-12">
<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">
<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>
<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">
<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>
<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>
<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>
<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>
<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>
<div class="flex flex-wrap items-center gap-2 mb-4">
<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>
<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>
</div>
<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>
</div>
<div id="download-rows" class="space-y-8 content-card" style="transition-delay: 400ms;">
<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>
<footer class="mt-16 pt-8 border-t border-slate-200 dark:border-gray-700/50 text-center text-sm text-slate-500 dark:text-slate-400">
<div class="flex justify-center items-center gap-6">
<a href="https://t.me/mikrotikpatch" target="_blank" title="Telegram" class="text-slate-500 hover:text-blue-600 dark:hover:text-blue-400 transition-colors">
<span class="iconify text-3xl" data-icon="ph:telegram-logo-bold"></span>
</a>
<a href="https://github.com/elseif/MikroTikPatch" target="_blank" title="GitHub" class="text-slate-500 hover:text-blue-600 dark:hover:text-blue-400 transition-colors">
<span class="iconify text-3xl" data-icon="ph:github-logo-bold"></span>
</a>
</div>
</footer>
</div>
<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>
<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>
<script>
document.addEventListener("DOMContentLoaded", async () => {
const ICONS = {
download: 'ph:download-simple-bold',
changelog: 'ph:newspaper-clipping-bold',
trash: 'ph:trash-bold',
copyAndOpen: 'ph:copy-simple-bold'
};
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();
return text.split(" ")[0];
}
} catch (e) {
console.warn("fetch failed or timeout:", e);
}
return defaultValue;
}
const i18n = {
en: {
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",
},
zh: {
language: "语言",
infoLabel1:`如果你已经在运行<b>Patch</b>过的RouterOS可以通过WebFig或WinBox中的<b>QuickSet或System > Packages</b>菜单点击<b>"Check For Updates"</b>来升级到最新版本。`,
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:"主题",
}
};
function setLanguage(lang) {
document.querySelectorAll("[data-i18n]").forEach(el => {
const key = el.getAttribute("data-i18n");
if (i18n[lang] && i18n[lang][key]) {
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];
}
}
});
localStorage.setItem("lang", lang);
}
function initLanguage() {
let savedLang = localStorage.getItem("lang");
if (!savedLang) {
savedLang = navigator.language.startsWith("zh") ? "zh" : "en";
}
document.getElementById("lang-switcher").value = savedLang;
setLanguage(savedLang);
}
document.getElementById("lang-switcher").addEventListener("change", e => {
setLanguage(e.target.value);
});
const themeBtn = document.getElementById("theme-toggle-btn");
const htmlEl = document.documentElement;
const sunIcon = themeBtn.querySelector('.sun-icon');
const moonIcon = themeBtn.querySelector('.moon-icon');
function updateThemeUI() {
if (htmlEl.classList.contains('dark')) {
sunIcon.classList.remove('hidden');
moonIcon.classList.add('hidden');
} else {
sunIcon.classList.add('hidden');
moonIcon.classList.remove('hidden');
}
}
function initializeTheme() {
const savedTheme = localStorage.getItem("theme");
if (savedTheme) {
htmlEl.classList.toggle("dark", savedTheme === "dark");
} else {
htmlEl.classList.toggle("dark", window.matchMedia('(prefers-color-scheme: dark)').matches);
}
updateThemeUI();
}
themeBtn.addEventListener("click", () => {
htmlEl.classList.toggle("dark");
localStorage.setItem("theme", htmlEl.classList.contains("dark") ? "dark" : "light");
updateThemeUI();
});
initializeTheme();
initLanguage();
const loadingOverlay = document.getElementById("loading");
const [routeros7_stable, routeros7_testing, routeros6_longterm, routeros6_stable] = await Promise.all([
fetch_latest_versions("https://upgrade.mikrotik.ltd/routeros/NEWESTa7.stable", "7.19.4"),
fetch_latest_versions("https://upgrade.mikrotik.ltd/routeros/NEWESTa7.testing", "7.20rc1"),
fetch_latest_versions("https://upgrade.mikrotik.ltd/routeros/NEWEST6.long-term", "6.49.18"),
fetch_latest_versions("https://upgrade.mikrotik.ltd/routeros/NEWEST6.stable", "6.49.19"),
]);
loadingOverlay.style.display = "none";
document.body.classList.remove('loading');
const downloads =[
{
title:"RouterOS v7",
versions:[ { title:"Stable", version:routeros7_stable }, { title:"Testing", version:routeros7_testing } ],
groups: [
{ 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" } ] }
]
},
{
title:"RouterOS v6",
versions:[ { title:"Long-Term", version:routeros6_longterm }, { title:"Stable", version:routeros6_stable } ],
groups: [
{ title:"X86", arch:"x86", downloads:[ { title:"Extra packages", type:"extra" }, { title:"CD Image", type:"iso" }, { title:"Install image", type:"install" }, { title:"Changelog", type:"changlog" } ] }
]
},
{
title:"Cloud Hosted Router",
versions:[ { title:"Long-Term", version:routeros6_longterm }, { title:"Stable", version:routeros6_stable }, { title:"Stable", version:routeros7_stable }, { title:"Testing", version:routeros7_testing } ],
groups:[
{ 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" } ] }
]
},
];
const fileNames = (version, arch, type) => {
const files = [];
if (version.charAt(0) === "6" && arch !== "x86") return files;
if (type === "main" && version.charAt(0) === "7") files.push(`routeros-${version}${arch === "x86" ? "" : "-" + arch}.npk`);
if (type === "iso") files.push(`mikrotik-${version}${arch === "x86" ? "" : "-" + arch}.iso`);
if (type === "extra") files.push(`all_packages-${arch}-${version}.zip`);
if (type === "install") files.push(`install-image-${version}${arch === "x86" ? "" : "-" + arch}.zip`);
if (type in { vhdx: 1, vmdk: 1, vdi: 1, vhd: 1, img: 1 }) {
files.push(`chr-${version}${arch === "x86" ? "" : "-" + arch}.${type}.zip`);
if (arch === "x86" && version.charAt(0) !== "6") files.push(`chr-${version}-legacy-bios.${type}.zip`);
}
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`);
}
if (type === "netinstall_windows") files.push(`netinstall-${version}.zip`);
if (type === "netinstall_linux_cli") files.push(`netinstall-${version}.tar.gz`);
return files;
};
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}/`;
const container = document.getElementById("download-rows");
container.innerHTML = '';
downloads.forEach((download, index) => {
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";
accordion.open = index === 0;
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";
table.innerHTML = `
<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>
</tr>
</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>
${download.versions.map(v => {
if (d.type === "changlog") {
return `<td class="px-6 py-4 text-center"><a href="https://upgrade.mikrotik.ltd/routeros/${v.version}/CHANGELOG" class="inline-flex items-center gap-1.5 text-blue-500 dark:text-blue-400 hover:underline" title="Changelog" target="_blank"><span class="iconify" data-icon="${ICONS.changelog}"></span> View</a></td>`;
}
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')) {
label = file.includes("legacy-bios") ? "Legacy" : "UEFI";
}
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>`;
}).join("<br>");
return `<td class="px-6 py-4 text-center space-y-2">${links}</td>`;
}).join("")}
<td></td>
</tr>
`).join("")}
`).join("")}
</tbody>
</table>
`;
content.appendChild(table);
accordion.appendChild(summary);
accordion.appendChild(content);
container.appendChild(accordion);
});
const ghProxyCheckbox = document.getElementById("gh-proxy");
const clearCacheBtn = document.getElementById("clear-cache");
ghProxyCheckbox.checked = localStorage.getItem("gh-proxy-enabled") === "true";
clearCacheBtn.style.display = ghProxyCheckbox.checked ? "block" : "none";
function updateAllDownloadLinks() {
document.querySelectorAll('a[href*="github.com/elseif/MikroTikPatch"]').forEach(link => {
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/", "");
}
});
const activeBtn = document.querySelector("#version-buttons button.active");
if (activeBtn) updateCommand(activeBtn.dataset.version);
}
ghProxyCheckbox.addEventListener("change", () => {
localStorage.setItem("gh-proxy-enabled", ghProxyCheckbox.checked ? "true" : "false");
clearCacheBtn.style.display = ghProxyCheckbox.checked ? "block" : "none";
updateAllDownloadLinks();
});
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);
});
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);
});
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();
});
const versionBtnsContainer = document.getElementById("version-buttons");
const commandDiv = document.getElementById("command");
const copyBtn = document.getElementById("copy-btn");
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>
`;
function updateCommand(version) {
const tool = document.querySelector('input[name="tool"]:checked').value;
const proxy = localStorage.getItem("gh-proxy-enabled") === "true" ? " | sed 's#https://github.com#https://gh-proxy.com/https://github.com#g'" : "";
const cmd = tool === "curl"
? `<pre class="text-slate-300"><code><span class="text-pink-400">curl</span> <span class="text-cyan-400">https://mikrotik.ltd/chr.sh</span>${proxy} | <span class="text-pink-400">bash</span> -s <span class="text-green-400">${version}</span></code></pre>`
: `<pre class="text-slate-300"><code><span class="text-pink-400">wget</span> -O - <span class="text-cyan-400">https://mikrotik.ltd/chr.sh</span>${proxy} | <span class="text-pink-400">bash</span> -s <span class="text-green-400">${version}</span></code></pre>`;
commandDiv.innerHTML = cmd;
}
document.querySelectorAll('input[name="tool"]').forEach(radio => {
radio.addEventListener("change", () => {
const activeBtn = document.querySelector("#version-buttons button.active");
if (activeBtn) updateCommand(activeBtn.dataset.version);
});
});
const buttons = document.querySelectorAll("#version-buttons button");
buttons.forEach(btn => {
btn.addEventListener("click", (e) => {
e.preventDefault();
updateCommand(btn.dataset.version);
buttons.forEach(b => b.classList.remove("active"));
btn.classList.add("active");
});
});
buttons[0].click();
copyBtn.addEventListener("click", () => {
const code = commandDiv.querySelector("code").innerText;
navigator.clipboard.writeText(code).then(() => {
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));
});
});
</script>
</body>
</html>