mirror of
https://github.com/caiwx86/small-packages.git
synced 2026-10-31 22:45:13 +08:00
update 2026-08-08 20:35:50
This commit is contained in:
1 parent
74e7dc08d2
commit
2a09d6ce42
1982 files changed
+712
-1523905
No files matched your search
@@ -8,6 +8,22 @@ function arraysEqual(a, b) {
|
||||
return true;
|
||||
}
|
||||
|
||||
function decodeIfBase64(str) {
|
||||
try {
|
||||
let s = str.replace(/-/g, '+').replace(/_/g, '/');
|
||||
while (s.length % 4) s += '=';
|
||||
const decoded = decodeURIComponent(
|
||||
atob(s).split('').map(c =>
|
||||
'%' + c.charCodeAt(0).toString(16).padStart(2, '0')
|
||||
).join('')
|
||||
);
|
||||
if (btoa(unescape(encodeURIComponent(decoded))).replace(/=+$/, '') === s.replace(/=+$/, '')) {
|
||||
return decoded;
|
||||
}
|
||||
} catch (e) {}
|
||||
return str;
|
||||
}
|
||||
|
||||
function waitForElement(selector, callback) {
|
||||
const el = document.querySelector(selector);
|
||||
if (el) return callback(el);
|
||||
|
||||
@@ -51,14 +51,14 @@ function index()
|
||||
entry({"admin", "services", appname, "socks_config"}, cbi(appname .. "/client/socks_config")).leaf = true
|
||||
entry({"admin", "services", appname, "acl"}, cbi(appname .. "/client/acl"), _("Access control"), 98).leaf = true
|
||||
entry({"admin", "services", appname, "acl_config"}, cbi(appname .. "/client/acl_config")).leaf = true
|
||||
entry({"admin", "services", appname, "log"}, form(appname .. "/client/log"), _("Runtime Logs"), 999).leaf = true
|
||||
entry({"admin", "services", appname, "log"}, template(appname .. "/log/log"), _("Runtime Logs"), 999).leaf = true
|
||||
|
||||
--[[ Server ]]
|
||||
entry({"admin", "services", appname, "server"}, cbi(appname .. "/server/index"), _("Server-Side"), 99).leaf = true
|
||||
entry({"admin", "services", appname, "server_user"}, cbi(appname .. "/server/user")).leaf = true
|
||||
|
||||
--[[ API ]]
|
||||
entry({"admin", "services", appname, "server_user_update"}, call("server_user_update")).leaf = true
|
||||
entry({"admin", "services", appname, "server_update_config"}, call("server_update_config")).leaf = true
|
||||
entry({"admin", "services", appname, "server_user_status"}, call("server_user_status")).leaf = true
|
||||
entry({"admin", "services", appname, "server_user_log"}, call("server_user_log")).leaf = true
|
||||
entry({"admin", "services", appname, "server_get_log"}, call("server_get_log")).leaf = true
|
||||
@@ -652,7 +652,7 @@ function rollback_rules()
|
||||
http_write_json_ok()
|
||||
end
|
||||
|
||||
function server_user_update()
|
||||
function server_update_config()
|
||||
local id = http.formvalue("id") -- Node id
|
||||
local data = http.formvalue("data") -- json new Data
|
||||
if id and data then
|
||||
|
||||
@@ -114,7 +114,7 @@ if api.is_finded("geoview") then
|
||||
.. "</ul>"
|
||||
end
|
||||
|
||||
shunt_group = add_option(ListValue, "shunt_group", translate("Group Name"))
|
||||
shunt_group = add_option(ListValue, "shunt_group", translate("Shunt Rule Group"))
|
||||
shunt_group:value("", translate("default"))
|
||||
for k, v in pairs(groups) do
|
||||
shunt_group:value(k)
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
local appname = "passwall2"
|
||||
|
||||
f = SimpleForm(appname)
|
||||
f.reset = false
|
||||
f.submit = false
|
||||
f:append(Template(appname .. "/log/log"))
|
||||
|
||||
return f
|
||||
@@ -30,9 +30,6 @@ o:value("https://connectivitycheck.platform.hicloud.com/generate_204", "HiCloud
|
||||
o:value("https://wifi.vivo.com.cn/generate_204", "VIVO (CN)")
|
||||
o.default = o.keylist[3]
|
||||
|
||||
-- [[ Add the node via the link ]]--
|
||||
s:append(Template(appname .. "/node_list/link_add_node"))
|
||||
|
||||
m:append(Template(appname .. "/node_list/node_list"))
|
||||
|
||||
return api.return_map(m)
|
||||
@@ -88,6 +88,6 @@ end
|
||||
|
||||
s:append(Template(appname .. "/rule/rule_version"))
|
||||
|
||||
s:append(Template(appname .. "/rule/shunt_rule_list"))
|
||||
m:append(Template(appname .. "/rule/shunt_rule_list"))
|
||||
|
||||
return api.return_map(m)
|
||||
@@ -76,7 +76,7 @@ remarks.validate = function(self, value, section)
|
||||
return value
|
||||
end
|
||||
|
||||
o = s:option(Value, "group", translate("Group Name"))
|
||||
o = s:option(Value, "group", translate("Shunt Rule Group"))
|
||||
o.default = ""
|
||||
o:value("", translate("default"))
|
||||
for k, v in pairs(groups) do
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<%
|
||||
local api = require "luci.passwall2.api"
|
||||
-%>
|
||||
<%+header%>
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
var log_textarea = null;
|
||||
@@ -42,3 +43,4 @@ local api = require "luci.passwall2.api"
|
||||
<input class="btn cbi-button cbi-button-remove" type="button" onclick="clearlog()" value="<%:Clear logs%>" />
|
||||
<textarea id="log_textarea" class="cbi-input-textarea" style="width: 100%;margin-top: 10px;" data-update="change" rows="40" wrap="off" readonly="readonly"></textarea>
|
||||
</fieldset>
|
||||
<%+footer%>
|
||||
@@ -1,556 +0,0 @@
|
||||
<%
|
||||
local api = require "luci.passwall2.api"
|
||||
-%>
|
||||
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
function ajax_add_node(link, group) {
|
||||
const dom = document.getElementById('modal-mask');
|
||||
dom.style.display = 'block';
|
||||
dom.innerHTML = '<div style="position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);color:#fff;"><%:Processing, please wait…%></div>';
|
||||
|
||||
const chunkSize = 1000; // Fragmented sending to overcome uhttpd's limitations, with each fragment containing 1000 characters.
|
||||
const totalChunks = Math.ceil(link.length / chunkSize);
|
||||
let currentChunk = 0;
|
||||
|
||||
function sendNextChunk() {
|
||||
if (currentChunk < totalChunks) {
|
||||
const chunk = link.substring(currentChunk * chunkSize, (currentChunk + 1) * chunkSize);
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', '<%=api.url("link_add_node")%>', true);
|
||||
xhr.onload = function () {
|
||||
if (xhr.status === 200) {
|
||||
currentChunk++;
|
||||
sendNextChunk();
|
||||
} else {
|
||||
dom.innerHTML = '';
|
||||
dom.style.display = 'none';
|
||||
alert("<%:Error%>");
|
||||
return;
|
||||
}
|
||||
};
|
||||
xhr.onerror = function () {
|
||||
dom.innerHTML = '';
|
||||
dom.style.display = 'none';
|
||||
alert("<%:Network Error%>");
|
||||
return;
|
||||
};
|
||||
const formData = new FormData();
|
||||
formData.append("chunk", chunk);
|
||||
formData.append("chunk_index", currentChunk);
|
||||
formData.append("total_chunks", totalChunks);
|
||||
formData.append("group", group);
|
||||
xhr.send(formData);
|
||||
} else {
|
||||
window.location.href = '<%=api.url("node_list")%>';
|
||||
}
|
||||
}
|
||||
sendNextChunk();
|
||||
}
|
||||
|
||||
function decodeIfBase64(str) {
|
||||
try {
|
||||
let s = str.replace(/-/g, '+').replace(/_/g, '/');
|
||||
while (s.length % 4) s += '=';
|
||||
const decoded = decodeURIComponent(
|
||||
atob(s).split('').map(c =>
|
||||
'%' + c.charCodeAt(0).toString(16).padStart(2, '0')
|
||||
).join('')
|
||||
);
|
||||
if (btoa(unescape(encodeURIComponent(decoded))).replace(/=+$/, '') === s.replace(/=+$/, '')) {
|
||||
return decoded;
|
||||
}
|
||||
} catch (e) {}
|
||||
return str;
|
||||
}
|
||||
|
||||
function getBg(el) {
|
||||
if (!el) return null;
|
||||
const style = getComputedStyle(el);
|
||||
const bgImage = style.backgroundImage;
|
||||
const bgColor = style.backgroundColor;
|
||||
return (bgImage !== 'none' || !/rgba\([^,]+,[^,]+,[^,]+,\s*0\)/.test(bgColor) && bgColor !== 'transparent')
|
||||
? style.background
|
||||
: null;
|
||||
};
|
||||
|
||||
function open_add_link_div() {
|
||||
document.body.classList.add('modal-open');
|
||||
document.getElementById('modal-mask').style.display = 'block';
|
||||
const addLinkDiv = document.getElementById("add_link_div");
|
||||
addLinkDiv.style.background = getBg(document.querySelector('.cbi-section')) || getBg(document.body) || '';
|
||||
addLinkDiv.style.display = "block";
|
||||
if (!addLinkDiv._dropdown_inited) {
|
||||
addLinkDiv._dropdown_inited = true;
|
||||
dropdown_list_fun("addlink_group_custom");
|
||||
}
|
||||
document.getElementById("nodes_link").focus();
|
||||
}
|
||||
|
||||
function close_add_link_div() {
|
||||
document.getElementById('modal-mask').style.display = 'none';
|
||||
document.getElementById("add_link_div").style.display = "none";
|
||||
document.body.classList.remove('modal-open');
|
||||
}
|
||||
|
||||
function add_node() {
|
||||
let nodes_link = document.getElementById("nodes_link").value;
|
||||
const group = (document.querySelector('#addlink_group_custom input[type="hidden"]')?.value || "default");
|
||||
nodes_link = nodes_link.replace(/\t/g, "").replace(/\r\n|\r/g, "\n").replace(/<[^>]*>/g, '').trim();
|
||||
if (nodes_link != "") {
|
||||
nodes_link = decodeIfBase64(nodes_link);
|
||||
let s = nodes_link.split('://');
|
||||
if (s.length > 1) {
|
||||
close_add_link_div();
|
||||
ajax_add_node(nodes_link, group);
|
||||
} else {
|
||||
alert("<%:Please enter the correct link.%>");
|
||||
}
|
||||
} else {
|
||||
document.getElementById("nodes_link").focus();
|
||||
}
|
||||
}
|
||||
|
||||
function clear_all_nodes() {
|
||||
if (confirm('<%:Are you sure to clear all nodes?%>') == true){
|
||||
XHR.get('<%=api.url("clear_all_nodes")%>', null,
|
||||
function(x, data) {
|
||||
if(x && x.status == 200) {
|
||||
window.location.href = '<%=api.url("node_list")%>';
|
||||
} else {
|
||||
alert("<%:Error%>");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function open_reassign_group_div() {
|
||||
const ids = [];
|
||||
const visibleContainer = document.querySelector('#cbi-passwall2-nodes > .cbi-tabcontainer[style*="display:block"], #cbi-passwall2-nodes > .cbi-tabcontainer[style*="display: block"]');
|
||||
if (!visibleContainer) return;
|
||||
const doms = visibleContainer.getElementsByClassName("nodes_select");
|
||||
if (doms && doms.length > 0) {
|
||||
for (let i = 0 ; i < doms.length; i++) {
|
||||
if (doms[i].checked) {
|
||||
ids.push(doms[i].getAttribute("cbid"))
|
||||
}
|
||||
}
|
||||
if (ids.length > 0) {
|
||||
document.body.classList.add('modal-open');
|
||||
document.getElementById('modal-mask').style.display = 'block';
|
||||
const reassignGroupDiv = document.getElementById("reassign_group_div");
|
||||
reassignGroupDiv.style.background = getBg(document.querySelector('.cbi-section')) || getBg(document.body) || '';
|
||||
reassignGroupDiv.style.display = "block";
|
||||
if (!reassignGroupDiv._dropdown_inited) {
|
||||
reassignGroupDiv._dropdown_inited = true;
|
||||
dropdown_list_fun("reassign_group_custom");
|
||||
}
|
||||
} else {
|
||||
alert("<%:You no select nodes !%>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function close_reassign_group_div() {
|
||||
document.getElementById('modal-mask').style.display = 'none';
|
||||
document.getElementById("reassign_group_div").style.display = "none";
|
||||
document.body.classList.remove('modal-open');
|
||||
}
|
||||
|
||||
function reassign_group() {
|
||||
const ids = [];
|
||||
const group = (document.querySelector('#reassign_group_custom input[type="hidden"]')?.value || "default");
|
||||
const visibleContainer = document.querySelector('#cbi-passwall2-nodes > .cbi-tabcontainer[style*="display:block"], #cbi-passwall2-nodes > .cbi-tabcontainer[style*="display: block"]');
|
||||
if (!visibleContainer) return;
|
||||
const doms = visibleContainer.getElementsByClassName("nodes_select");
|
||||
if (doms && doms.length > 0) {
|
||||
for (let i = 0 ; i < doms.length; i++) {
|
||||
if (doms[i].checked) {
|
||||
ids.push(doms[i].getAttribute("cbid"))
|
||||
}
|
||||
}
|
||||
if (ids.length > 0) {
|
||||
XHR.get('<%=api.url("reassign_group")%>', {
|
||||
group: group,
|
||||
ids: ids.join(",")
|
||||
},
|
||||
function(x, data) {
|
||||
if (x && x.status == 200) {
|
||||
window.location.href = '<%=api.url("node_list")%>';
|
||||
} else {
|
||||
alert("<%:Error%>");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Custom group dropdown list events
|
||||
function dropdown_list_fun(div_id) {
|
||||
const dropdown = document.getElementById(div_id);
|
||||
if (!dropdown) return;
|
||||
|
||||
const display = dropdown.querySelector(".selected-display");
|
||||
const displayText = display.querySelector(".text");
|
||||
const list = dropdown.querySelector(".dropdown-list");
|
||||
const hidden = dropdown.querySelector('input[type="hidden"]');
|
||||
const input = dropdown.querySelector(".create-item-input");
|
||||
|
||||
display.addEventListener("click", function() {
|
||||
list.style.display = list.style.display === "none" ? "block" : "none";
|
||||
input.value = "";
|
||||
//input.focus();
|
||||
});
|
||||
|
||||
function selectItem(li) {
|
||||
list.querySelectorAll(".dropdown-item").forEach(function(el){
|
||||
el.classList.remove("selected");
|
||||
});
|
||||
li.classList.add("selected");
|
||||
hidden.value = li.dataset.value;
|
||||
displayText.textContent = li.dataset.value || "<%:default%>";
|
||||
list.style.display = "none";
|
||||
}
|
||||
|
||||
list.addEventListener("click", function(e){
|
||||
const li = e.target.closest(".dropdown-item");
|
||||
if (!li || li.classList.contains("custom-input")) return;
|
||||
selectItem(li);
|
||||
});
|
||||
|
||||
input.addEventListener("keydown", function(e){
|
||||
const isEnter = e.key === "Enter" || e.keyCode === 13;
|
||||
if (!isEnter) return;
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
const val = input.value.trim();
|
||||
if (!val) return;
|
||||
|
||||
if (val.toLowerCase() === "default") {
|
||||
const emptyLi = Array.from(list.querySelectorAll(".dropdown-item"))
|
||||
.find(function(el){ return !el.dataset.value; });
|
||||
if (emptyLi) selectItem(emptyLi);
|
||||
input.value = "";
|
||||
return;
|
||||
}
|
||||
|
||||
let li = Array.from(list.querySelectorAll(".dropdown-item")).find(function(el){
|
||||
return el.dataset.value && el.dataset.value.toLowerCase() === val.toLowerCase();
|
||||
});
|
||||
if (!li) {
|
||||
li = document.createElement("li");
|
||||
li.className = "dropdown-item";
|
||||
li.dataset.value = val;
|
||||
li.textContent = val;
|
||||
list.insertBefore(li, input.parentNode);
|
||||
}
|
||||
|
||||
input.value = "";
|
||||
selectItem(li);
|
||||
});
|
||||
// Read group names from tab
|
||||
function InsertGroup(list, input) {
|
||||
const tabs = document.querySelectorAll(".cbi-tabmenu li");
|
||||
if (!tabs.length) {
|
||||
setTimeout(() => InsertGroup(list, input), 50);
|
||||
return;
|
||||
}
|
||||
|
||||
tabs.forEach(function(li){
|
||||
const group = li.id.split('.').pop();
|
||||
if(group === "default") return;
|
||||
if(Array.from(list.querySelectorAll(".dropdown-item")).some(el => el.dataset.value === group)) return;
|
||||
const newLi = document.createElement("li");
|
||||
newLi.className = "dropdown-item";
|
||||
newLi.dataset.value = group;
|
||||
newLi.textContent = group;
|
||||
list.insertBefore(newLi, input.parentNode);
|
||||
});
|
||||
}
|
||||
InsertGroup(list, input);
|
||||
|
||||
// Automatically collapses when clicking on the outside.
|
||||
document.addEventListener("click", function(e) {
|
||||
if (!dropdown.contains(e.target)) {
|
||||
list.style.display = "none";
|
||||
}
|
||||
});
|
||||
}
|
||||
//]]>
|
||||
</script>
|
||||
|
||||
<div id="modal-mask"></div>
|
||||
|
||||
<div id="add_link_div">
|
||||
<div id="add_link_modal_container">
|
||||
<h3><%:Add the node via the link%></h3>
|
||||
<div class="value-custom">
|
||||
<textarea id="nodes_link" rows="10"></textarea>
|
||||
<p id="nodes_link_text"><%:Enter share links, one per line. Subscription links are not supported!%></p>
|
||||
</div>
|
||||
<div class="value-custom">
|
||||
<div class="value-field-custom">
|
||||
<label class="value-title-custom" for="addlink_group_custom"><%:Group Name%></label>
|
||||
<div id="addlink_group_custom" class="custom-dropdown">
|
||||
<div class="selected-display">
|
||||
<span class="text"><%:default%></span>
|
||||
<span class="arrow-down-small"></span>
|
||||
</div>
|
||||
<ul class="dropdown-list" style="display:none;">
|
||||
<li class="dropdown-item selected" data-value=""><%:default%></li>
|
||||
<li class="dropdown-item custom-input">
|
||||
<input type="text" placeholder="-- <%:custom%> --" class="create-item-input" inputmode="text" enterkeyhint="done">
|
||||
</li>
|
||||
</ul>
|
||||
<input type="hidden" name="addlink_group" value="">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="add_link_button_container">
|
||||
<input class="btn cbi-button cbi-button-add" type="button" onclick="add_node()" value="<%:Add%>" />
|
||||
<input class="btn cbi-button cbi-button-remove" type="button" onclick="close_add_link_div()" value="<%:Close%>" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="reassign_group_div">
|
||||
<div id="reassign_group_modal_container">
|
||||
<h3><%:Reassign Node Group%></h3>
|
||||
<div class="value-custom">
|
||||
<div class="value-field-custom">
|
||||
<label class="value-title-custom" for="reassign_group_custom"><%:Group Name%></label>
|
||||
<div id="reassign_group_custom" class="custom-dropdown">
|
||||
<div class="selected-display">
|
||||
<span class="text"><%:default%></span>
|
||||
<span class="arrow-down-small"></span>
|
||||
</div>
|
||||
<ul class="dropdown-list" style="display:none;">
|
||||
<li class="dropdown-item selected" data-value=""><%:default%></li>
|
||||
<li class="dropdown-item custom-input">
|
||||
<input type="text" placeholder="-- <%:custom%> --" class="create-item-input" inputmode="text" enterkeyhint="done">
|
||||
</li>
|
||||
</ul>
|
||||
<input type="hidden" name="to_group" value="">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="reassign_group_button_container">
|
||||
<input class="btn cbi-button cbi-button-edit" type="button" onclick="reassign_group()" value="<%:Save%>" />
|
||||
<input class="btn cbi-button cbi-button-remove" type="button" onclick="close_reassign_group_div()" value="<%:Close%>" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="pw-toolbar">
|
||||
<div class="pw-toolbar-field">
|
||||
<input class="btn cbi-button cbi-button-add" type="button" onclick="to_add_node()" value="<%:Add%>" />
|
||||
<input class="btn cbi-button cbi-button-add" type="button" onclick="open_add_link_div()" value="<%:Add the node via the link%>" />
|
||||
<input class="btn cbi-button cbi-button-remove" type="button" onclick="clear_all_nodes()" value="<%:Clear all nodes%>" />
|
||||
<input class="btn cbi-button cbi-button-remove" type="button" onclick="delete_select_nodes()" value="<%:Delete select nodes%>" />
|
||||
<input class="btn cbi-button cbi-button-edit" type="button" id="select_all_btn" onclick="checked_all_node(this)" value="<%:Select all%>" />
|
||||
<input class="btn cbi-button cbi-button-edit" type="button" onclick="open_reassign_group_div()" value="<%:Reassign Group%>" />
|
||||
<input class="btn cbi-button cbi-button-apply" type="submit" name="cbi.apply" value="<%:Save & Apply%>" />
|
||||
<!--<input class="btn cbi-button cbi-button-save" type="submit" name="cbi.save" value="<%:Save%>" />-->
|
||||
<!--<input class="btn cbi-button cbi-button-reset" type="button" value="<%:Reset%>" onclick="location.href='<%=REQUEST_URI%>'" />-->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.pw-toolbar {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.pw-toolbar-field {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
padding: 5px 0 5px;
|
||||
}
|
||||
|
||||
#modal-mask {
|
||||
display: none;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background: rgba(0,0,0,0.4);
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
body.modal-open {
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
body.modal-open #add_link_div,
|
||||
body.modal-open #reassign_group_div {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
#add_link_div, #reassign_group_div {
|
||||
display: none;
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border: 2px solid #ccc;
|
||||
box-shadow: 0 0 10px rgba(0,0,0,0.5);
|
||||
z-index: 1000;
|
||||
width: 90%;
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
#add_link_modal_container, #reassign_group_modal_container {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
#nodes_link {
|
||||
width: 100%;
|
||||
height: 180px;
|
||||
resize: vertical;
|
||||
font-family: monospace;
|
||||
padding: 5px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
#add_link_div h3,
|
||||
#reassign_group_div h3 {
|
||||
background: inherit;
|
||||
}
|
||||
|
||||
#nodes_link_text {
|
||||
color: red;
|
||||
font-size: 14px;
|
||||
margin-top: 5px;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#add_link_button_container, #reassign_group_button_container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.value-custom {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
margin: 10px 0;
|
||||
padding: 0px 5px 0px 5px;
|
||||
}
|
||||
|
||||
.value-field-custom {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.value-title-custom {
|
||||
font-size: 13px;
|
||||
line-height: 28px;
|
||||
white-space: nowrap;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.custom-dropdown {
|
||||
position: relative;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 2px;
|
||||
width: 180px;
|
||||
height: 28px;
|
||||
font-size: 13px;
|
||||
background: #fff;
|
||||
cursor: pointer;
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.selected-display {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 8px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.selected-display:hover {
|
||||
background-color: #f7f7f7;
|
||||
}
|
||||
|
||||
.dropdown-list {
|
||||
position: absolute;
|
||||
top: calc(100% + 2px);
|
||||
left: -1px;
|
||||
width: calc(100% + 2px);
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 2px;
|
||||
border-top: none;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.15);
|
||||
background: #fff;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
z-index: 100;
|
||||
box-sizing: border-box;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.dropdown-item {
|
||||
padding: 4px 8px;
|
||||
line-height: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.dropdown-item.selected {
|
||||
background-color: #1e90ff;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.create-item-input::placeholder {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.dropdown-item.custom-input input {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 3px;
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
border: 1px solid #ccc;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.arrow-down-small {
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 4px solid transparent;
|
||||
border-right: 4px solid transparent;
|
||||
border-top: 5px solid #555;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
</style>
|
||||
@@ -9,10 +9,11 @@ local node = api.uci_get_type("global", "node")
|
||||
if node then
|
||||
local node_type = api.uci_get_type_id(node, "type")
|
||||
local node_protocol = api.uci_get_type_id(node, "protocol")
|
||||
if node_type == "Xray" and node_protocol == "_shunt" then
|
||||
if (node_type == "Xray" or node_type == "sing-box") and node_protocol == "_shunt" then
|
||||
default_node_type = node_protocol
|
||||
local node_shunt_group = api.uci_get_type_id(node, "shunt_group")
|
||||
uci:foreach(appname, "shunt_rules", function(e)
|
||||
if e[".name"] and e.remarks then
|
||||
if e[".name"] and e.remarks and e.group == node_shunt_group then
|
||||
shunt_rule_list[#shunt_rule_list + 1] = e
|
||||
end
|
||||
end)
|
||||
@@ -36,12 +37,13 @@ table td, .table .td {
|
||||
display: none;
|
||||
width: 30rem;
|
||||
position: fixed;
|
||||
top:50%;
|
||||
padding-top: 30px;
|
||||
top:45%;
|
||||
z-index: 99;
|
||||
text-align: center;
|
||||
background: white;
|
||||
box-shadow: darkgrey 10px 10px 30px 5px;
|
||||
text-align: center;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
._now_use_bg {
|
||||
@@ -183,8 +185,205 @@ table td, .table .td {
|
||||
}
|
||||
</style>
|
||||
|
||||
<% if api.is_js_luci() then -%>
|
||||
<style>
|
||||
.pw-toolbar {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.pw-toolbar-field {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
padding: 5px 0 5px;
|
||||
}
|
||||
|
||||
#modal-mask {
|
||||
display: none;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background: rgba(0,0,0,0.4);
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
body.modal-open {
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
body.modal-open #add_link_div,
|
||||
body.modal-open #reassign_group_div {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
#add_link_div, #reassign_group_div {
|
||||
display: none;
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border: 2px solid #ccc;
|
||||
box-shadow: 0 0 10px rgba(0,0,0,0.5);
|
||||
z-index: 1000;
|
||||
width: 90%;
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
#add_link_modal_container, #reassign_group_modal_container {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
#nodes_link {
|
||||
width: 100%;
|
||||
height: 180px;
|
||||
resize: vertical;
|
||||
font-family: monospace;
|
||||
padding: 5px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
#add_link_div h3,
|
||||
#reassign_group_div h3 {
|
||||
background: inherit;
|
||||
}
|
||||
|
||||
#nodes_link_text {
|
||||
color: red;
|
||||
font-size: 14px;
|
||||
margin-top: 5px;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#add_link_button_container, #reassign_group_button_container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.value-custom {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
margin: 10px 0;
|
||||
padding: 0px 5px 0px 5px;
|
||||
}
|
||||
|
||||
.value-field-custom {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.value-title-custom {
|
||||
font-size: 13px;
|
||||
line-height: 28px;
|
||||
white-space: nowrap;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.custom-dropdown {
|
||||
position: relative;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 2px;
|
||||
width: 180px;
|
||||
height: 28px;
|
||||
font-size: 13px;
|
||||
background: #fff;
|
||||
cursor: pointer;
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.selected-display {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 8px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.selected-display:hover {
|
||||
background-color: #f7f7f7;
|
||||
}
|
||||
|
||||
.dropdown-list {
|
||||
position: absolute;
|
||||
top: calc(100% + 2px);
|
||||
left: -1px;
|
||||
width: calc(100% + 2px);
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 2px;
|
||||
border-top: none;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.15);
|
||||
background: #fff;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
z-index: 100;
|
||||
box-sizing: border-box;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.dropdown-item {
|
||||
padding: 4px 8px;
|
||||
line-height: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.dropdown-item.selected {
|
||||
background-color: #1e90ff;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.create-item-input::placeholder {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.dropdown-item.custom-input input {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 3px;
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
border: 1px solid #ccc;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.arrow-down-small {
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 4px solid transparent;
|
||||
border-right: 4px solid transparent;
|
||||
border-top: 5px solid #555;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
<% if api.is_js_luci() then -%>
|
||||
var cbi_t = [];
|
||||
function cbi_t_add(section, tab) {
|
||||
var t = document.getElementById('tab.' + section + '.' + tab);
|
||||
@@ -217,9 +416,7 @@ table td, .table .td {
|
||||
}
|
||||
return false
|
||||
}
|
||||
</script>
|
||||
<%- else %>
|
||||
<script type="text/javascript">
|
||||
(function() {
|
||||
if (typeof(cbi_t_switch) === "function") {
|
||||
var old_switch = cbi_t_switch;
|
||||
@@ -229,16 +426,16 @@ table td, .table .td {
|
||||
};
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
<%- end %>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
let auto_detection_time = "<%=api.uci_get_type("global_other", "auto_detection_time", "0")%>"
|
||||
let show_node_info = "<%=api.uci_get_type("global_other", "show_node_info", "0")%>"
|
||||
var ajaxList = [];
|
||||
|
||||
var ajax = {
|
||||
list: [],
|
||||
post: function(url, data, fn_success, timeout, fn_timeout) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
var code = ajax.encode(data);
|
||||
@@ -267,7 +464,7 @@ table td, .table .td {
|
||||
fn_success(xhr, json);
|
||||
}
|
||||
};
|
||||
ajaxList.push(xhr);
|
||||
this.list.push(xhr);
|
||||
xhr.send(code);
|
||||
},
|
||||
encode: function(obj) {
|
||||
@@ -287,65 +484,63 @@ table td, .table .td {
|
||||
}
|
||||
|
||||
return obj;
|
||||
},
|
||||
abortAll: function() {
|
||||
this.list.forEach(xhr => xhr.abort());
|
||||
this.list = [];
|
||||
}
|
||||
}
|
||||
|
||||
function ajaxAbortAll() {
|
||||
ajaxList.forEach(xhr => xhr.abort());
|
||||
ajaxList = [];
|
||||
}
|
||||
|
||||
function to_add_node() {
|
||||
ajaxAbortAll();
|
||||
const dom = document.getElementsByClassName("cbi-tab")[0];
|
||||
const current_group = dom ? (dom.getAttribute("group_name") || "default") : "default";
|
||||
window.location.href='<%=api.url("add_node")%>?redirect=1&group=' + current_group;
|
||||
}
|
||||
|
||||
function to_edit_node(cbi_id) {
|
||||
ajaxAbortAll();
|
||||
location.href='<%=api.url("node_config")%>/' + cbi_id;
|
||||
}
|
||||
|
||||
function copy_node(cbi_id) {
|
||||
ajaxAbortAll();
|
||||
window.location.href = '<%=api.url("copy_node")%>' + "?section=" + cbi_id;
|
||||
}
|
||||
|
||||
function del_node(cbi_id) {
|
||||
ajaxAbortAll();
|
||||
window.location.href = '<%=api.url("delete_select_nodes")%>' + "?redirect=1&ids=" + cbi_id;
|
||||
}
|
||||
|
||||
var section = "";
|
||||
function open_set_node_div(cbi_id) {
|
||||
section = cbi_id;
|
||||
document.getElementById("set_node_div").style.display="block";
|
||||
var node_name = document.getElementById("cbid.<%=appname%>." + cbi_id + ".remarks").value;
|
||||
document.getElementById("set_node_name").innerHTML = node_name;
|
||||
}
|
||||
|
||||
function close_set_node_div() {
|
||||
document.getElementById("set_node_div").style.display="none";
|
||||
document.getElementById("set_node_name").innerHTML = "";
|
||||
}
|
||||
|
||||
function row_top(btn, group) {
|
||||
const row = btn.closest("tr");
|
||||
if (!row) return;
|
||||
const parent = row.parentNode;
|
||||
let firstDataRow = parent.querySelector("tr:not(.cbi-section-table-titles)");
|
||||
if (firstDataRow && firstDataRow !== row) {
|
||||
parent.insertBefore(row, firstDataRow);
|
||||
let save_order_btn = document.getElementById("save_order_btn_" + group);
|
||||
if (save_order_btn) {
|
||||
const new_order = get_node_order(group);
|
||||
if (!arraysEqual(new_order, origin_group_node_order[group])) {
|
||||
save_order_btn.style.display = null;
|
||||
} else {
|
||||
save_order_btn.style.display = "none";
|
||||
function node_action(action, cbi_id, a) {
|
||||
ajax.abortAll();
|
||||
switch (action) {
|
||||
case "add":
|
||||
const dom = document.getElementsByClassName("cbi-tab")[0];
|
||||
const current_group = dom ? (dom.getAttribute("group_name") || "default") : "default";
|
||||
window.location.href='<%=api.url("add_node")%>?redirect=1&group=' + current_group;
|
||||
break;
|
||||
case "copy":
|
||||
window.location.href = '<%=api.url("copy_node")%>' + "?section=" + cbi_id;
|
||||
break;
|
||||
case "edit":
|
||||
window.location.href='<%=api.url("node_config")%>/' + cbi_id;
|
||||
break;
|
||||
case "del":
|
||||
window.location.href = '<%=api.url("delete_select_nodes")%>' + "?redirect=1&ids=" + cbi_id;
|
||||
break;
|
||||
case "row_top":
|
||||
let btn = a.btn;
|
||||
let group = a.group;
|
||||
const row = btn.closest("tr");
|
||||
if (!row) return;
|
||||
const parent = row.parentNode;
|
||||
let firstDataRow = parent.querySelector("tr:not(.cbi-section-table-titles)");
|
||||
if (firstDataRow && firstDataRow !== row) {
|
||||
parent.insertBefore(row, firstDataRow);
|
||||
let save_order_btn = document.getElementById("save_order_btn_" + group);
|
||||
if (save_order_btn) {
|
||||
const new_order = get_node_order(group);
|
||||
if (!arraysEqual(new_order, origin_group_node_order[group])) {
|
||||
save_order_btn.style.display = null;
|
||||
} else {
|
||||
save_order_btn.style.display = "none";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "del_all":
|
||||
if (confirm('<%:Are you sure to clear all nodes?%>') == true){
|
||||
XHR.get('<%=api.url("clear_all_nodes")%>', null,
|
||||
function(x, data) {
|
||||
if(x && x.status == 200) {
|
||||
window.location.href = '<%=api.url("node_list")%>';
|
||||
} else {
|
||||
alert("<%:Error%>");
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
@@ -420,6 +615,7 @@ table td, .table .td {
|
||||
if (confirm('<%:Are you sure to delete select nodes?%>') == true) {
|
||||
dom.style.display = 'block';
|
||||
dom.innerHTML = '<div style="position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);color:#fff;"><%:Processing, please wait…%></div>';
|
||||
ajax.abortAll();
|
||||
XHR.get('<%=api.url("delete_select_nodes")%>', {
|
||||
ids: ids.join()
|
||||
},
|
||||
@@ -433,7 +629,7 @@ table td, .table .td {
|
||||
box.remove();
|
||||
}
|
||||
*/
|
||||
ajaxAbortAll();
|
||||
ajax.abortAll();
|
||||
window.location.href = '<%=api.url("node_list")%>';
|
||||
} else {
|
||||
alert("<%:Error%>");
|
||||
@@ -446,14 +642,7 @@ table td, .table .td {
|
||||
alert("<%:You no select nodes !%>");
|
||||
}
|
||||
}
|
||||
|
||||
function set_node(type, config) {
|
||||
if (confirm('<%:Are you sure set this server?%>')==true){
|
||||
ajaxAbortAll();
|
||||
window.location.href = '<%=api.url("set_node")%>?type=' + type + '&config=' + config + '§ion=' + section;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function get_address_full(id) {
|
||||
var address = (document.getElementById("cbid.<%=appname%>." + id + ".address") || {}).value || "";
|
||||
var port = (document.getElementById("cbid.<%=appname%>." + id + ".port") || {}).value || "";
|
||||
@@ -501,6 +690,7 @@ table td, .table .td {
|
||||
var id = row.id.replace("cbi-<%=appname%>-", "");
|
||||
ids.push(id);
|
||||
});
|
||||
ajax.abortAll();
|
||||
XHR.get('<%=api.url("save_node_order")%>', {
|
||||
group: group,
|
||||
ids: ids.join(",")
|
||||
@@ -741,16 +931,6 @@ table td, .table .td {
|
||||
}
|
||||
}
|
||||
|
||||
function arraysEqual(a, b) {
|
||||
if (a === b) return true;
|
||||
if (a == null || b == null) return false;
|
||||
if (a.length !== b.length) return false;
|
||||
for (let i = 0; i < a.length; i++) {
|
||||
if (a[i] !== b[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// List drag and rearrange
|
||||
function initSortableForTable(table) {
|
||||
if (!table) return null;
|
||||
@@ -821,6 +1001,291 @@ table td, .table .td {
|
||||
}
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
//Pop-up logic
|
||||
|
||||
// Custom group dropdown list events
|
||||
function dropdown_list_fun(div_id) {
|
||||
const dropdown = document.getElementById(div_id);
|
||||
if (!dropdown) return;
|
||||
|
||||
const display = dropdown.querySelector(".selected-display");
|
||||
const displayText = display.querySelector(".text");
|
||||
const list = dropdown.querySelector(".dropdown-list");
|
||||
const hidden = dropdown.querySelector('input[type="hidden"]');
|
||||
const input = dropdown.querySelector(".create-item-input");
|
||||
|
||||
display.addEventListener("click", function() {
|
||||
list.style.display = list.style.display === "none" ? "block" : "none";
|
||||
input.value = "";
|
||||
//input.focus();
|
||||
});
|
||||
|
||||
function selectItem(li) {
|
||||
list.querySelectorAll(".dropdown-item").forEach(function(el){
|
||||
el.classList.remove("selected");
|
||||
});
|
||||
li.classList.add("selected");
|
||||
hidden.value = li.dataset.value;
|
||||
displayText.textContent = li.dataset.value || "<%:default%>";
|
||||
list.style.display = "none";
|
||||
}
|
||||
|
||||
list.addEventListener("click", function(e){
|
||||
const li = e.target.closest(".dropdown-item");
|
||||
if (!li || li.classList.contains("custom-input")) return;
|
||||
selectItem(li);
|
||||
});
|
||||
|
||||
input.addEventListener("keydown", function(e){
|
||||
const isEnter = e.key === "Enter" || e.keyCode === 13;
|
||||
if (!isEnter) return;
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
const val = input.value.trim();
|
||||
if (!val) return;
|
||||
|
||||
if (val.toLowerCase() === "default") {
|
||||
const emptyLi = Array.from(list.querySelectorAll(".dropdown-item"))
|
||||
.find(function(el){ return !el.dataset.value; });
|
||||
if (emptyLi) selectItem(emptyLi);
|
||||
input.value = "";
|
||||
return;
|
||||
}
|
||||
|
||||
let li = Array.from(list.querySelectorAll(".dropdown-item")).find(function(el){
|
||||
return el.dataset.value && el.dataset.value.toLowerCase() === val.toLowerCase();
|
||||
});
|
||||
if (!li) {
|
||||
li = document.createElement("li");
|
||||
li.className = "dropdown-item";
|
||||
li.dataset.value = val;
|
||||
li.textContent = val;
|
||||
list.insertBefore(li, input.parentNode);
|
||||
}
|
||||
|
||||
input.value = "";
|
||||
selectItem(li);
|
||||
});
|
||||
// Read group names from tab
|
||||
function InsertGroup(list, input) {
|
||||
const tabs = document.querySelectorAll(".cbi-tabmenu li");
|
||||
if (!tabs.length) {
|
||||
setTimeout(() => InsertGroup(list, input), 50);
|
||||
return;
|
||||
}
|
||||
|
||||
tabs.forEach(function(li){
|
||||
const group = li.id.split('.').pop();
|
||||
if(group === "default") return;
|
||||
if(Array.from(list.querySelectorAll(".dropdown-item")).some(el => el.dataset.value === group)) return;
|
||||
const newLi = document.createElement("li");
|
||||
newLi.className = "dropdown-item";
|
||||
newLi.dataset.value = group;
|
||||
newLi.textContent = group;
|
||||
list.insertBefore(newLi, input.parentNode);
|
||||
});
|
||||
}
|
||||
InsertGroup(list, input);
|
||||
|
||||
// Automatically collapses when clicking on the outside.
|
||||
document.addEventListener("click", function(e) {
|
||||
if (!dropdown.contains(e.target)) {
|
||||
list.style.display = "none";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getBg(el) {
|
||||
if (!el) return null;
|
||||
const style = getComputedStyle(el);
|
||||
const bgImage = style.backgroundImage;
|
||||
const bgColor = style.backgroundColor;
|
||||
return (bgImage !== 'none' || !/rgba\([^,]+,[^,]+,[^,]+,\s*0\)/.test(bgColor) && bgColor !== 'transparent')
|
||||
? style.background
|
||||
: null;
|
||||
}
|
||||
|
||||
const add_link_div = {
|
||||
open() {
|
||||
document.body.classList.add('modal-open');
|
||||
document.getElementById('modal-mask').style.display = 'block';
|
||||
const addLinkDiv = document.getElementById("add_link_div");
|
||||
addLinkDiv.style.background = getBg(document.querySelector('.cbi-section')) || getBg(document.body) || '';
|
||||
addLinkDiv.style.display = "block";
|
||||
if (!addLinkDiv._dropdown_inited) {
|
||||
addLinkDiv._dropdown_inited = true;
|
||||
dropdown_list_fun("addlink_group_custom");
|
||||
}
|
||||
document.getElementById("nodes_link").focus();
|
||||
},
|
||||
close() {
|
||||
document.getElementById('modal-mask').style.display = 'none';
|
||||
document.getElementById("add_link_div").style.display = "none";
|
||||
document.body.classList.remove('modal-open');
|
||||
},
|
||||
add_node() {
|
||||
let nodes_link = document.getElementById("nodes_link").value;
|
||||
const group = (document.querySelector('#addlink_group_custom input[type="hidden"]')?.value || "default");
|
||||
nodes_link = nodes_link.replace(/\t/g, "").replace(/\r\n|\r/g, "\n").replace(/<[^>]*>/g, '').trim();
|
||||
if (nodes_link != "") {
|
||||
nodes_link = decodeIfBase64(nodes_link);
|
||||
let s = nodes_link.split('://');
|
||||
if (s.length > 1) {
|
||||
this.close();
|
||||
this.ajax_add_node(nodes_link, group);
|
||||
} else {
|
||||
alert("<%:Please enter the correct link.%>");
|
||||
}
|
||||
} else {
|
||||
document.getElementById("nodes_link").focus();
|
||||
}
|
||||
},
|
||||
ajax_add_node(link, group) {
|
||||
const dom = document.getElementById('modal-mask');
|
||||
dom.style.display = 'block';
|
||||
dom.innerHTML = '<div style="position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);color:#fff;"><%:Processing, please wait…%></div>';
|
||||
|
||||
const chunkSize = 1000; // Fragmented sending to overcome uhttpd's limitations, with each fragment containing 1000 characters.
|
||||
const totalChunks = Math.ceil(link.length / chunkSize);
|
||||
let currentChunk = 0;
|
||||
|
||||
function sendNextChunk() {
|
||||
if (currentChunk < totalChunks) {
|
||||
const chunk = link.substring(currentChunk * chunkSize, (currentChunk + 1) * chunkSize);
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', '<%=api.url("link_add_node")%>', true);
|
||||
xhr.onload = function () {
|
||||
if (xhr.status === 200) {
|
||||
currentChunk++;
|
||||
sendNextChunk();
|
||||
} else {
|
||||
dom.innerHTML = '';
|
||||
dom.style.display = 'none';
|
||||
alert("<%:Error%>");
|
||||
return;
|
||||
}
|
||||
};
|
||||
xhr.onerror = function () {
|
||||
dom.innerHTML = '';
|
||||
dom.style.display = 'none';
|
||||
alert("<%:Network Error%>");
|
||||
return;
|
||||
};
|
||||
const formData = new FormData();
|
||||
formData.append("chunk", chunk);
|
||||
formData.append("chunk_index", currentChunk);
|
||||
formData.append("total_chunks", totalChunks);
|
||||
formData.append("group", group);
|
||||
ajax.abortAll();
|
||||
xhr.send(formData);
|
||||
} else {
|
||||
window.location.href = '<%=api.url("node_list")%>';
|
||||
}
|
||||
}
|
||||
sendNextChunk();
|
||||
}
|
||||
}
|
||||
|
||||
const reassign_group_div = {
|
||||
open() {
|
||||
const ids = [];
|
||||
const visibleContainer = document.querySelector('#cbi-passwall2-nodes > .cbi-tabcontainer[style*="display:block"], #cbi-passwall2-nodes > .cbi-tabcontainer[style*="display: block"]');
|
||||
if (!visibleContainer) return;
|
||||
const doms = visibleContainer.getElementsByClassName("nodes_select");
|
||||
if (doms && doms.length > 0) {
|
||||
for (let i = 0 ; i < doms.length; i++) {
|
||||
if (doms[i].checked) {
|
||||
ids.push(doms[i].getAttribute("cbid"))
|
||||
}
|
||||
}
|
||||
if (ids.length > 0) {
|
||||
document.body.classList.add('modal-open');
|
||||
document.getElementById('modal-mask').style.display = 'block';
|
||||
const reassignGroupDiv = document.getElementById("reassign_group_div");
|
||||
reassignGroupDiv.style.background = getBg(document.querySelector('.cbi-section')) || getBg(document.body) || '';
|
||||
reassignGroupDiv.style.display = "block";
|
||||
if (!reassignGroupDiv._dropdown_inited) {
|
||||
reassignGroupDiv._dropdown_inited = true;
|
||||
dropdown_list_fun("reassign_group_custom");
|
||||
}
|
||||
} else {
|
||||
alert("<%:You no select nodes !%>");
|
||||
}
|
||||
}
|
||||
},
|
||||
close() {
|
||||
document.getElementById('modal-mask').style.display = 'none';
|
||||
document.getElementById("reassign_group_div").style.display = "none";
|
||||
document.body.classList.remove('modal-open');
|
||||
},
|
||||
action() {
|
||||
const ids = [];
|
||||
const group = (document.querySelector('#reassign_group_custom input[type="hidden"]')?.value || "default");
|
||||
const visibleContainer = document.querySelector('#cbi-passwall2-nodes > .cbi-tabcontainer[style*="display:block"], #cbi-passwall2-nodes > .cbi-tabcontainer[style*="display: block"]');
|
||||
if (!visibleContainer) return;
|
||||
const doms = visibleContainer.getElementsByClassName("nodes_select");
|
||||
if (doms && doms.length > 0) {
|
||||
for (let i = 0 ; i < doms.length; i++) {
|
||||
if (doms[i].checked) {
|
||||
ids.push(doms[i].getAttribute("cbid"))
|
||||
}
|
||||
}
|
||||
if (ids.length > 0) {
|
||||
ajax.abortAll();
|
||||
XHR.get('<%=api.url("reassign_group")%>', {
|
||||
group: group,
|
||||
ids: ids.join(",")
|
||||
},
|
||||
function(x, data) {
|
||||
if (x && x.status == 200) {
|
||||
window.location.href = '<%=api.url("node_list")%>';
|
||||
} else {
|
||||
alert("<%:Error%>");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const set_node_div = {
|
||||
section: '',
|
||||
open(cbi_id) {
|
||||
this.section = cbi_id;
|
||||
document.getElementById("set_node_div").style.display="block";
|
||||
const node_name = document.getElementById("cbid.<%=appname%>." + cbi_id + ".remarks").value;
|
||||
document.getElementById("set_node_name").innerHTML = node_name;
|
||||
},
|
||||
close() {
|
||||
document.getElementById("set_node_div").style.display="none";
|
||||
document.getElementById("set_node_name").innerHTML = "";
|
||||
},
|
||||
set(type, config) {
|
||||
if (confirm('<%:Are you sure set this node?%>') == true){
|
||||
ajax.abortAll();
|
||||
window.location.href = '<%=api.url("set_node")%>?type=' + type + '&config=' + config + '§ion=' + section;
|
||||
}
|
||||
}
|
||||
}
|
||||
//]]>
|
||||
</script>
|
||||
|
||||
<div class="pw-toolbar">
|
||||
<div class="pw-toolbar-field">
|
||||
<input class="btn cbi-button cbi-button-add" type="button" onclick="node_action('add')" value="<%:Add%>" />
|
||||
<input class="btn cbi-button cbi-button-add" type="button" onclick="add_link_div.open()" value="<%:Add the node via the link%>" />
|
||||
<input class="btn cbi-button cbi-button-remove" type="button" onclick="node_action('del_all')" value="<%:Clear all nodes%>" />
|
||||
<input class="btn cbi-button cbi-button-remove" type="button" onclick="delete_select_nodes()" value="<%:Delete select nodes%>" />
|
||||
<input class="btn cbi-button cbi-button-edit" type="button" id="select_all_btn" onclick="checked_all_node(this)" value="<%:Select all%>" />
|
||||
<input class="btn cbi-button cbi-button-edit" type="button" onclick="reassign_group_div.open()" value="<%:Reassign Group%>" />
|
||||
<input class="btn cbi-button cbi-button-apply" type="submit" name="cbi.apply" value="<%:Save & Apply%>" />
|
||||
<!--<input class="btn cbi-button cbi-button-save" type="submit" name="cbi.save" value="<%:Save%>" />-->
|
||||
<!--<input class="btn cbi-button cbi-button-reset" type="button" value="<%:Reset%>" onclick="location.href='<%=REQUEST_URI%>'" />-->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/template" id="nodes-table-template">
|
||||
<fieldset class="cbi-section cbi-tblsection" id="cbi-<%=appname%>-nodes-{{group}}-fieldset">
|
||||
@@ -838,7 +1303,7 @@ table td, .table .td {
|
||||
{{node-tr}}
|
||||
</table>
|
||||
<div class="cbi-section-create cbi-tblsection-create">
|
||||
<input class="cbi-button cbi-button-add" type="button" value="<%:Add%>" onclick="to_add_node()">
|
||||
<input class="cbi-button cbi-button-add" type="button" value="<%:Add%>" onclick="node_action('add')">
|
||||
<input class="cbi-button cbi-button-apply" style="display: none" type="button" id="save_order_btn_{{group}}" value="<%:Save Order%>" onclick="save_current_page_order('{{group}}')">
|
||||
</div>
|
||||
</fieldset>
|
||||
@@ -858,11 +1323,11 @@ table td, .table .td {
|
||||
<td class="td cbi-value-field pw-urltest">{{url_test}}</td>
|
||||
<td class="td cbi-section-table-cell nowrap cbi-section-actions pw-actions">
|
||||
<div class="node-wrapper">
|
||||
<input class="btn cbi-button cbi-button-edit" type="button" value="<%:To Top%>" onclick="row_top(this, '{{group}}')" title="<%:To Top%>"/>
|
||||
<input class="btn cbi-button cbi-button-apply" type="button" value="<%:Use%>" id="apply_{{id}}" onclick="open_set_node_div('{{id}}')"/>
|
||||
<input class="btn cbi-button cbi-button-add" type="button" value="<%:Copy%>" onclick="copy_node('{{id}}')"/>
|
||||
<input class="btn cbi-button cbi-button-edit" type="button" value="<%:Edit%>" onclick="to_edit_node('{{id}}')" alt="<%:Edit%>" title="<%:Edit%>">
|
||||
<input class="btn cbi-button cbi-button-remove" type="button" value="<%:Delete%>" onclick="del_node('{{id}}')" alt="<%:Delete%>" title="<%:Delete%>">
|
||||
<input class="btn cbi-button cbi-button-edit" type="button" value="<%:To Top%>" onclick="node_action('row_top', null, {btn:this, group:'{{group}}'})" title="<%:To Top%>"/>
|
||||
<input class="btn cbi-button cbi-button-apply" type="button" value="<%:Use%>" id="apply_{{id}}" onclick="set_node_div.open('{{id}}')"/>
|
||||
<input class="btn cbi-button cbi-button-add" type="button" value="<%:Copy%>" onclick="node_action('copy', '{{id}}')"/>
|
||||
<input class="btn cbi-button cbi-button-edit" type="button" value="<%:Edit%>" onclick="node_action('edit', '{{id}}')" alt="<%:Edit%>" title="<%:Edit%>">
|
||||
<input class="btn cbi-button cbi-button-remove" type="button" value="<%:Delete%>" onclick="node_action('del', '{{id}}')" alt="<%:Delete%>" title="<%:Delete%>">
|
||||
<span class="drag-handle center" title="<%:Drag to reorder%>">⠿</span>
|
||||
</div>
|
||||
</td>
|
||||
@@ -873,6 +1338,8 @@ table td, .table .td {
|
||||
</fieldset>
|
||||
|
||||
<script type="text/javascript">
|
||||
//init logic
|
||||
|
||||
function get_remarks_name(o) {
|
||||
let str = "";
|
||||
let remarks = o["remarks"] || "";
|
||||
@@ -949,9 +1416,8 @@ table td, .table .td {
|
||||
group_nodes[_node.group].push(_node)
|
||||
}
|
||||
|
||||
var tab_ul_html = '<ul class="cbi-tabmenu">'
|
||||
var tab_ul_li_html = ''
|
||||
var tab_content_html = '<fieldset class="cbi-section-node cbi-section-node-tabbed" id="cbi-<%=appname%>-nodes">'
|
||||
var tab_content_html = ''
|
||||
var nodes_table_template = document.getElementById("nodes-table-template");
|
||||
var node_template = document.getElementById("node-tr-template");
|
||||
var default_group = null
|
||||
@@ -1025,11 +1491,21 @@ table td, .table .td {
|
||||
'</div>'
|
||||
}
|
||||
|
||||
tab_ul_html += tab_ul_li_html + '</ul>'
|
||||
tab_content_html += '</fieldset>'
|
||||
var tab_html = tab_ul_html + tab_content_html
|
||||
function addTabDOM(li, content) {
|
||||
let tab_ul_html = '<ul class="cbi-tabmenu">';
|
||||
let tab_content_html = '<fieldset class="cbi-section-shunt_rule cbi-section-shunt_rule-tabbed" id="cbi-<%=appname%>-nodes">';
|
||||
if (li) {
|
||||
tab_ul_html += li;
|
||||
}
|
||||
if (content) {
|
||||
tab_content_html += content;
|
||||
}
|
||||
tab_ul_html += '</ul>';
|
||||
tab_content_html += '</fieldset>';
|
||||
return tab_ul_html + tab_content_html;
|
||||
}
|
||||
|
||||
document.getElementById("node_list").innerHTML = tab_html
|
||||
document.getElementById("node_list").innerHTML = addTabDOM(tab_ul_li_html, tab_content_html);
|
||||
|
||||
for (let group in group_nodes) {
|
||||
cbi_t_add("<%=appname%>.nodes", group)
|
||||
@@ -1038,7 +1514,7 @@ table td, .table .td {
|
||||
if (default_group) {
|
||||
cbi_t_switch("<%=appname%>.nodes", default_group)
|
||||
}
|
||||
|
||||
|
||||
origin_group_node_order = {};
|
||||
for (let group in group_nodes) {
|
||||
origin_group_node_order[group] = get_node_order(group);
|
||||
@@ -1071,20 +1547,8 @@ table td, .table .td {
|
||||
|
||||
//Node list option saving logic
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
function waitForElement(selector, callback) {
|
||||
const el = document.querySelector(selector);
|
||||
if (el) return callback(el);
|
||||
const observer = new MutationObserver(() => {
|
||||
const el = document.querySelector(selector);
|
||||
if (el) {
|
||||
observer.disconnect();
|
||||
callback(el);
|
||||
}
|
||||
});
|
||||
observer.observe(document.body, { childList: true, subtree: true });
|
||||
}
|
||||
|
||||
function onChange(option, value) {
|
||||
ajax.abortAll();
|
||||
XHR.get('<%=api.url("save_node_list_opt")%>', {
|
||||
option: option,
|
||||
value: value
|
||||
@@ -1118,31 +1582,96 @@ table td, .table .td {
|
||||
links.forEach(link => {
|
||||
link.addEventListener('click', (e) => {
|
||||
if (link.getAttribute('target') !== '_blank') {
|
||||
ajaxAbortAll();
|
||||
ajax.abortAll();
|
||||
}
|
||||
});
|
||||
});
|
||||
window.addEventListener('beforeunload', (event) => {
|
||||
ajax.abortAll();
|
||||
});
|
||||
});
|
||||
//]]>
|
||||
</script>
|
||||
|
||||
<div style="display: -webkit-flex; display: flex; -webkit-align-items: center; align-items: center; -webkit-justify-content: center; justify-content: center;">
|
||||
<div id="set_node_div" style="text-align: center;">
|
||||
<div class="cbi-value" style="display: flex; justify-content: center; align-items: center; margin: 8px 0;">
|
||||
<font color=#666><%:You choose node is:%></font><a style="color: red" id="set_node_name"></a>
|
||||
<div id="modal-mask"></div>
|
||||
|
||||
<div id="add_link_div">
|
||||
<div id="add_link_modal_container">
|
||||
<h3><%:Add the node via the link%></h3>
|
||||
<div class="value-custom">
|
||||
<textarea id="nodes_link" rows="10"></textarea>
|
||||
<p id="nodes_link_text"><%:Enter share links, one per line. Subscription links are not supported!%></p>
|
||||
</div>
|
||||
<div class="cbi-value" style="display: flex; justify-content: center; gap: 8px; margin: 8px 0;">
|
||||
<div class="value-custom">
|
||||
<div class="value-field-custom">
|
||||
<label class="value-title-custom" for="addlink_group_custom"><%:Group Name%></label>
|
||||
<div id="addlink_group_custom" class="custom-dropdown">
|
||||
<div class="selected-display">
|
||||
<span class="text"><%:default%></span>
|
||||
<span class="arrow-down-small"></span>
|
||||
</div>
|
||||
<ul class="dropdown-list" style="display:none;">
|
||||
<li class="dropdown-item selected" data-value=""><%:default%></li>
|
||||
<li class="dropdown-item custom-input">
|
||||
<input type="text" placeholder="-- <%:custom%> --" class="create-item-input" inputmode="text" enterkeyhint="done">
|
||||
</li>
|
||||
</ul>
|
||||
<input type="hidden" name="addlink_group" value="">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="add_link_button_container">
|
||||
<input class="btn cbi-button cbi-button-add" type="button" onclick="add_link_div.add_node()" value="<%:Add%>" />
|
||||
<input class="btn cbi-button cbi-button-remove" type="button" onclick="add_link_div.close()" value="<%:Close%>" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="reassign_group_div">
|
||||
<div id="reassign_group_modal_container">
|
||||
<h3><%:Reassign Node Group%></h3>
|
||||
<div class="value-custom">
|
||||
<div class="value-field-custom">
|
||||
<label class="value-title-custom" for="reassign_group_custom"><%:Group Name%></label>
|
||||
<div id="reassign_group_custom" class="custom-dropdown">
|
||||
<div class="selected-display">
|
||||
<span class="text"><%:default%></span>
|
||||
<span class="arrow-down-small"></span>
|
||||
</div>
|
||||
<ul class="dropdown-list" style="display:none;">
|
||||
<li class="dropdown-item selected" data-value=""><%:default%></li>
|
||||
<li class="dropdown-item custom-input">
|
||||
<input type="text" placeholder="-- <%:custom%> --" class="create-item-input" inputmode="text" enterkeyhint="done">
|
||||
</li>
|
||||
</ul>
|
||||
<input type="hidden" name="to_group" value="">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="reassign_group_button_container">
|
||||
<input class="btn cbi-button cbi-button-edit" type="button" onclick="reassign_group_div.action()" value="<%:Save%>" />
|
||||
<input class="btn cbi-button cbi-button-remove" type="button" onclick="reassign_group_div.close()" value="<%:Close%>" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: -webkit-flex; display: flex; -webkit-align-items: center; align-items: center; -webkit-justify-content: center; justify-content: center;">
|
||||
<div id="set_node_div">
|
||||
<div class="cbi-value" style="display: flex; justify-content: center; align-items: center; margin: 1em 0; height: 2em;">
|
||||
<font color=#666><%:You choose node is:%></font><a style="color: red" id="set_node_name"></a>
|
||||
<div style="position:absolute; right:0.5em; top: 0.5em;"><input class="btn cbi-button" type="button" onclick="set_node_div.close()" value="X" /></div>
|
||||
</div>
|
||||
<div class="cbi-value" style="justify-content: center; gap: 8px; margin: 1em 0;">
|
||||
<%- if default_node_type == "_shunt" then
|
||||
for i, v in ipairs(shunt_rule_list) do
|
||||
-%>
|
||||
<input class="btn cbi-button cbi-button-edit" type="button" onclick="set_node('<%=node%>', '<%=v[".name"]%>')" value="<%=v.remarks%>" />
|
||||
<input class="btn cbi-button" type="button" onclick="set_node_div.set('<%=node%>', '<%=v[".name"]%>')" value="<%=v.remarks%>" />
|
||||
<%-
|
||||
end
|
||||
-%>
|
||||
<% else %>
|
||||
<input class="btn cbi-button cbi-button-edit" type="button" onclick="set_node('@global[0]', 'node')" value="<%:Node%>" />
|
||||
<input class="btn cbi-button" type="button" onclick="set_node_div.set('@global[0]', 'node')" value="<%:Node%>" />
|
||||
<% end %>
|
||||
<input class="btn cbi-button cbi-button-remove" type="button" onclick="close_set_node_div()" value="<%:Close%>" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -254,16 +254,6 @@ table td, .table .td {
|
||||
);
|
||||
}
|
||||
|
||||
function arraysEqual(a, b) {
|
||||
if (a === b) return true;
|
||||
if (a == null || b == null) return false;
|
||||
if (a.length !== b.length) return false;
|
||||
for (let i = 0; i < a.length; i++) {
|
||||
if (a[i] !== b[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// List drag and rearrange
|
||||
function initSortableForTable(table) {
|
||||
if (!table) return null;
|
||||
@@ -355,7 +345,7 @@ table td, .table .td {
|
||||
|
||||
<script type="text/template" id="shunt_rule-tr-template">
|
||||
<tr class="tr cbi-section-table-row" id="cbi-<%=appname%>-{{id}}">
|
||||
<td class="td cbi-value-field"><h3>{{id}}</h3></td>
|
||||
<td class="td cbi-value-field"><b>{{id}}</b></td>
|
||||
<td class="td cbi-value-field">{{remarks}}</td>
|
||||
<td class="td cbi-section-table-cell nowrap cbi-section-actions td-actions">
|
||||
<div class="shunt_rule-wrapper">
|
||||
@@ -368,11 +358,11 @@ table td, .table .td {
|
||||
</tr>
|
||||
</script>
|
||||
|
||||
<fieldset class="cbi-section">
|
||||
<legend>Sing-Box/Xray <%:Shunt Rule%></legend>
|
||||
<div class="cbi-section-descr"><a style="color: red"><%:Please note attention to the priority, the higher the order, the higher the priority.%></a></div>
|
||||
<div class="cbi-section">
|
||||
<h3>Sing-Box/Xray <%:Shunt Rule%></h3>
|
||||
<div class="cbi-section-descr"><a style="color: red;text-decoration: none"><%:Please note attention to the priority, the higher the order, the higher the priority.%></a></div>
|
||||
<div id="shunt_rule_list"></div>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
function loadNodeList() {
|
||||
|
||||
@@ -3,6 +3,9 @@ local api = self.api
|
||||
-%>
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
let node_id = "<%=self.section%>";
|
||||
let node_config_url = '<%=api.url("server_user")%>/' + node_id;
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
waitForElement('select[name*="<%=self.config%>"][name*="type"]', function(el) {
|
||||
let o_val = el.value;
|
||||
@@ -12,12 +15,12 @@ local api = self.api
|
||||
let save = true;
|
||||
if (save) {
|
||||
//document.getElementsByClassName('cbi-button-save')[0].click();
|
||||
update_node({
|
||||
enable: getOption("enable").value,
|
||||
remarks: getOption("remarks").value,
|
||||
type: getOption("type").value,
|
||||
server_update_config(node_id, {
|
||||
enable: getOption("<%=self.config%>", node_id, "enable").value,
|
||||
remarks: getOption("<%=self.config%>", node_id, "remarks").value,
|
||||
type: getOption("<%=self.config%>", node_id, "type").value,
|
||||
protocol: ''
|
||||
});
|
||||
}, node_config_url);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,51 +1,15 @@
|
||||
<%
|
||||
local api = self.api
|
||||
-%>
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
let node_id = "<%=self.section%>";
|
||||
let node_config_url = '<%=api.url("server_user")%>/' + node_id;
|
||||
|
||||
function waitForElement(selector, callback) {
|
||||
const el = document.querySelector(selector);
|
||||
if (el) return callback(el);
|
||||
const observer = new MutationObserver(() => {
|
||||
const el = document.querySelector(selector);
|
||||
if (el) {
|
||||
observer.disconnect();
|
||||
callback(el);
|
||||
}
|
||||
});
|
||||
observer.observe(document.body, { childList: true, subtree: true });
|
||||
}
|
||||
|
||||
function getOption(opt) {
|
||||
var obj;
|
||||
var id = 'cbid.<%=self.config%>.' + node_id + '.' + opt;
|
||||
obj = document.getElementsByName(id)[0] || document.getElementById(id);
|
||||
if (obj) {
|
||||
var combobox = document.getElementById('cbi.combobox.' + id);
|
||||
if (combobox) {
|
||||
obj.combobox = combobox;
|
||||
}
|
||||
var div = document.getElementById(id);
|
||||
if (div && div.getElementsByTagName("li").length > 0) {
|
||||
obj = div;
|
||||
}
|
||||
return obj;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function update_node(data) {
|
||||
XHR.get('<%=api.url("server_user_update")%>', {
|
||||
id: node_id,
|
||||
function server_update_config(cfgid, data, redirect_url) {
|
||||
XHR.get('<%=self.api.url("server_update_config")%>', {
|
||||
id: cfgid,
|
||||
data: JSON.stringify(data)
|
||||
},
|
||||
function(x, data) {
|
||||
if (x && x.status == 200 && data.code == 1) {
|
||||
window.location.href = node_config_url;
|
||||
if (redirect_url) {
|
||||
window.location.href = redirect_url;
|
||||
}
|
||||
}
|
||||
else {
|
||||
alert("<%:Error%>");
|
||||
|
||||
@@ -337,8 +337,8 @@ msgstr "آیا از حذف گرههای انتخاب شده اطمینان د
|
||||
msgid "You no select nodes !"
|
||||
msgstr "شما هیچ گرهای انتخاب نکردهاید!"
|
||||
|
||||
msgid "Are you sure set to"
|
||||
msgstr "آیا مطمئن هستید که تنظیم شود به"
|
||||
msgid "Are you sure set this node?"
|
||||
msgstr "مطمئنی این گره رو تنظیم کردی؟"
|
||||
|
||||
msgid "the server?"
|
||||
msgstr "سرور؟"
|
||||
@@ -970,6 +970,9 @@ msgstr "توجه: مصرف منابع را افزایش میدهد."
|
||||
msgid "Shunt Rule"
|
||||
msgstr "قانون شنت"
|
||||
|
||||
msgid "Shunt Rule Group"
|
||||
msgstr "گروه قوانین شنت"
|
||||
|
||||
msgid "Please note attention to the priority, the higher the order, the higher the priority."
|
||||
msgstr "لطفاً به اولویت توجه کنید، هرچه ترتیب بالاتر باشد، اولویت بیشتر است."
|
||||
|
||||
|
||||
@@ -334,8 +334,8 @@ msgstr "Вы действительно хотите удалить выбран
|
||||
msgid "You no select nodes !"
|
||||
msgstr "Вы не выбрали ни одного узла!"
|
||||
|
||||
msgid "Are you sure set to"
|
||||
msgstr "Вы действительно хотите назначить"
|
||||
msgid "Are you sure set this node?"
|
||||
msgstr "Вы уверены, что задали этот узел?"
|
||||
|
||||
msgid "the server?"
|
||||
msgstr "активным сервером?"
|
||||
@@ -971,6 +971,9 @@ msgstr "Примечание: увеличивает нагрузку на си
|
||||
msgid "Shunt Rule"
|
||||
msgstr "Правила разделения трафика"
|
||||
|
||||
msgid "Shunt Rule Group"
|
||||
msgstr "Группа правил шунтирования"
|
||||
|
||||
msgid "Please note attention to the priority, the higher the order, the higher the priority."
|
||||
msgstr "Обратите внимание: правила выше в списке имеют более высокий приоритет."
|
||||
|
||||
|
||||
@@ -334,8 +334,8 @@ msgstr "你确定要删除选择的节点吗?"
|
||||
msgid "You no select nodes !"
|
||||
msgstr "你没有选择任何节点!"
|
||||
|
||||
msgid "Are you sure set to"
|
||||
msgstr "你确定要设为"
|
||||
msgid "Are you sure set this node?"
|
||||
msgstr "你确定要设置这个节点吗?"
|
||||
|
||||
msgid "the server?"
|
||||
msgstr "服务器吗?"
|
||||
@@ -962,6 +962,9 @@ msgstr "注:会增加一些系统资源的开销。"
|
||||
msgid "Shunt Rule"
|
||||
msgstr "分流规则"
|
||||
|
||||
msgid "Shunt Rule Group"
|
||||
msgstr "分流规则组"
|
||||
|
||||
msgid "Please note attention to the priority, the higher the order, the higher the priority."
|
||||
msgstr "请注意优先级问题,排序越上面优先级越高。"
|
||||
|
||||
|
||||
@@ -334,8 +334,8 @@ msgstr "你確定要刪除選择的節點吗?"
|
||||
msgid "You no select nodes !"
|
||||
msgstr "你沒有選择任何節點!"
|
||||
|
||||
msgid "Are you sure set to"
|
||||
msgstr "你確定要設為"
|
||||
msgid "Are you sure set this node?"
|
||||
msgstr "你確定要設定這個節點嗎?"
|
||||
|
||||
msgid "the server?"
|
||||
msgstr "服務器吗?"
|
||||
@@ -962,6 +962,9 @@ msgstr "注:会增加一些系統资源的開销。"
|
||||
msgid "Shunt Rule"
|
||||
msgstr "分流規則"
|
||||
|
||||
msgid "Shunt Rule Group"
|
||||
msgstr "分流規則組"
|
||||
|
||||
msgid "Please note attention to the priority, the higher the order, the higher the priority."
|
||||
msgstr "請注意優先級問題,排序越上面優先級越高。"
|
||||
|
||||
|
||||
Reference in new issue
Block a user