<!DOCTYPE html> <html lang="zh-CN" class="mdui-theme-auto"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>sub2sing-box</title> <link rel="stylesheet" href="https://unpkg.com/mdui@2/mdui.css" /> <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" /> <link href="https://fonts.googleapis.com/icon?family=Material+Icons+Outlined" rel="stylesheet" /> <script src="https://unpkg.com/mdui@2/mdui.global.js"></script> <style> h3 { margin-top: 0; } .section { margin: 20px 0; padding: 15px; border-radius: 8px; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); background-color: rgb(var(--mdui-color-surface-container)); } .section>* { margin-bottom: 10px; } .mdui-container { border-radius: 10px; max-width: 1200px; margin: 30px auto; } </style> </head> <body> <div class="mdui-container"> <div style=" display: flex; align-items: center; justify-content: space-between; "> <h2> <a href="https://github.com/nitezs/sub2sing-box" target="_blank">sub2sing-box</a> </h2> <mdui-dropdown trigger="hover"> <mdui-icon slot="trigger" name="dark_mode"></mdui-icon> <mdui-menu id="theme" selects="single" value="dark"> <mdui-menu-item value="light">亮色</mdui-menu-item> <mdui-menu-item value="dark">暗色</mdui-menu-item> <mdui-divider></mdui-divider> <mdui-menu-item value="auto">跟随系统</mdui-menu-item> </mdui-menu> </mdui-dropdown> </div> <!-- Template Section --> <div class=" section"> <h3>模板</h3> <mdui-text-field label="模板" type="text" id="template" name="template" /> </div> <div id="form"> <!-- Nodes Section --> <div class="section"> <h3>节点</h3> <div> <mdui-text-field label="订阅链接" autosize min-rows="3" max-rows="5" id="subscription" name="subscription" placeholder="一行一个"></mdui-text-field> </div> <div> <mdui-text-field label="节点分享链接" autosize min-rows="3" max-rows="5" id="proxy" name="proxy" placeholder="一行一个"></mdui-text-field> </div> <div> <mdui-text-field label="删除节点" type="text" id="delete" name="delete" placeholder="支持正则表达式" /> </div> <div> <mdui-list id="list"> <mdui-list-item> 重命名节点 <mdui-button slot="end-icon" type="button" onclick="addRenameField()">+ </mdui-button> </mdui-list-item> </mdui-list> <div id="renameContainer"></div> </div> </div> <!-- Policy Group Section --> <div class="section"> <h3>策略组</h3> <div> <label><mdui-checkbox type="checkbox" id="group" name="group" />启用</label> </div> <div> <mdui-text-field label="类型" type="text" id="group-type" name="group-type" value="selector" /> </div> <div> <mdui-select label="排序依据" name="sort" id="sort" value="tag"> <mdui-menu-item value="tag" selected>节点名</mdui-menu-item> <mdui-menu-item value="num">节点数量</mdui-menu-item> </mdui-select> </div> <div> <mdui-select label="排序方式" name="sort-type" id="sort-type" value="asc"> <mdui-menu-item value="asc" selected>升序</mdui-menu-item> <mdui-menu-item value="desc">降序</mdui-menu-item> </mdui-select> </div> </div> <!-- Result Section --> <div class="section"> <mdui-text-field label="生成链接" autosize min-rows="2" max-rows="5" id="output" name="output"></mdui-text-field> </div> </div> </div> <script> init(); function init() { theme(); listenInput(); } function theme() { const theme = window.localStorage.getItem("theme"); if (theme) { document.querySelector("html").className = "mdui-theme-" + theme; document.getElementById("theme").value = theme; } else { document.querySelector("html").className = "mdui-theme-auto"; document.getElementById("theme").value = "auto"; window.localStorage.setItem("theme", "auto"); } const icon = document.getElementById("theme"); const html = document.querySelector("html"); icon.addEventListener("change", function (e) { switch (icon.value) { case "light": html.className = "mdui-theme-light"; window.localStorage.setItem("theme", "light"); break; case "dark": html.className = "mdui-theme-dark"; window.localStorage.setItem("theme", "dark"); break; case "auto": html.className = "mdui-theme-auto"; window.localStorage.setItem("theme", "auto"); break; } }); } function encodeBase64(str) { return btoa( encodeURIComponent(str).replace( /%([0-9A-F]{2})/g, function (match, p1) { return String.fromCharCode("0x" + p1); } ) ) .replace(/\+/g, "-") .replace(/\//g, "_"); } function decodeBase64(str) { return decodeURIComponent( Array.prototype.map .call(atob(str), function (c) { return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2); }) .join("") ); } function listenInput() { const inputs = document.querySelectorAll( "mdui-checkbox, mdui-text-field" ); for (let input of inputs) { input.addEventListener("input", generateLink); } const selects = document.querySelectorAll("mdui-select"); for (let select of selects) { select.addEventListener("change", generateLink); } } function cleanLisnter() { const inputs = document.querySelectorAll("#form input, #form textarea"); for (let input of inputs) { input.removeEventListener("input", generateLink); } const selects = document.querySelectorAll("#form select"); for (let select of selects) { select.removeEventListener("change", generateLink); } } function addRenameField() { cleanLisnter(); const container = document.getElementById("list"); const fieldHTML = `<mdui-list-item class="rename-group"> <mdui-text-field slot="icon" type="text" name="rename_from[]" placeholder="支持正则表达式" label="原字符"></mdui-text-field> <mdui-text-field type="text" name="rename_to[]" label="替换字符"></mdui-text-field> <mdui-button slot="end-icon" type="button" class="btn btn-danger" onclick="removeThisField(this)">-</mdui-button> </mdui-list-item>`; container.insertAdjacentHTML("beforeend", fieldHTML); listenInput(); } function removeThisField(button) { cleanLisnter(); button.parentElement.remove(); generateLink(); listenInput(); } function generateLink() { const subscription = document .getElementById("subscription") .value.split("\n") .filter((i) => i); const proxy = document .getElementById("proxy") .value.split("\n") .filter((i) => i); const deleteRule = document.getElementById("delete").value; const template = document.getElementById("template").value; const renameFrom = Array.from( document.getElementsByName("rename_from[]") ).map((input) => input.value); const renameTo = Array.from( document.getElementsByName("rename_to[]") ).map((input) => input.value); const output = document.getElementById("output"); const group = document.getElementById("group").checked; const groupType = document.getElementById("group-type").value; const sort = document.getElementById("sort").value; const sortType = document.getElementById("sort-type").value; let rename = {}; for (let i = 0; i < renameFrom.length; i++) { if (renameFrom[i] && renameTo[i]) { rename[renameFrom[i]] = renameTo[i]; } } const data = { subscription, proxy, delete: deleteRule, template, rename, group, "group-type": groupType, sort, "sort-type": sortType, }; output.value = `${window.location.origin}/convert?data=${encodeBase64( JSON.stringify(data) )}`; } </script> </body> </html>