mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-02 09:13:56 +08:00
🔥 Sync 2026-10-24 23:19:23
This commit is contained in:
1 parent
9886197604
commit
37e1475e87
59 files changed
+5167
-1483
No files matched your search
@@ -0,0 +1,773 @@
|
||||
/*
|
||||
* Click-to-sort headers and a per-group filter bar for the node list.
|
||||
*
|
||||
* Sort reorders rows inside one group table and calls set_order(), the same
|
||||
* path as drag-and-drop and To Top. Nothing is written until Save & Apply.
|
||||
*
|
||||
* A filter only toggles the pw-row-hidden class. Rows stay in the form, so
|
||||
* Save & Apply still posts every node and the full group.order value.
|
||||
*
|
||||
* Translated strings are read from #pw-nodesort-i18n (the node list template).
|
||||
* The template calls init() once, then enhanceAll() after loadNodeList()
|
||||
* has built the group tables.
|
||||
*
|
||||
* auto_detection_time is a top-level let in the template, not a window
|
||||
* property. It is read by name so rows are not moved while automatic Ping
|
||||
* or TCPing is still writing results by row position.
|
||||
*/
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var COLUMNS = {
|
||||
'pw-remark': 'name',
|
||||
'pw-ping': 'ping',
|
||||
'pw-tcping': 'tcping',
|
||||
'pw-urltest': 'urltest'
|
||||
};
|
||||
/* sub controls drawn inside the Remarks header: [sort key, label key or symbol, title key] */
|
||||
var NAME_EXTRA = [
|
||||
['type', 'type', 'typeHint'],
|
||||
['addr', 'addr', 'addrHint'],
|
||||
['orig', '\u21BA', 'origHint']
|
||||
];
|
||||
|
||||
var state = {
|
||||
app: 'passwall',
|
||||
root: null,
|
||||
text: {},
|
||||
bound: false
|
||||
};
|
||||
var searchTimer = null;
|
||||
|
||||
function T(key) {
|
||||
var value = state.text[key];
|
||||
return (value == null || value === '') ? key : value;
|
||||
}
|
||||
|
||||
function readText(box) {
|
||||
var text = {};
|
||||
var spans = box.querySelectorAll('[data-k]');
|
||||
for (var i = 0; i < spans.length; i++)
|
||||
text[spans[i].getAttribute('data-k')] = spans[i].textContent;
|
||||
return text;
|
||||
}
|
||||
|
||||
var collator = null;
|
||||
function prepareCollator() {
|
||||
var lang = document.documentElement.lang || (navigator.language || '');
|
||||
try {
|
||||
collator = (window.Intl && Intl.Collator) ? new Intl.Collator(lang || undefined, { numeric: true, sensitivity: 'base' }) : null;
|
||||
} catch (e) {
|
||||
collator = null;
|
||||
}
|
||||
}
|
||||
|
||||
function cmpText(a, b) {
|
||||
if (collator)
|
||||
return collator.compare(a, b);
|
||||
return a < b ? -1 : (a > b ? 1 : 0);
|
||||
}
|
||||
|
||||
/* "123 ms", "12.5 ms", "1.2 s" -> milliseconds. Timeout, Error, ---, Test stay null. */
|
||||
function parseLatency(text) {
|
||||
var t = String(text || '').replace(/\s+/g, ' ').trim();
|
||||
var m = /^(\d+(?:[.,]\d+)?)\s*(ms|s)?$/i.exec(t);
|
||||
if (!m)
|
||||
return null;
|
||||
var v = parseFloat(m[1].replace(',', '.'));
|
||||
if (isNaN(v))
|
||||
return null;
|
||||
if (m[2] && /^s$/i.test(m[2]))
|
||||
v *= 1000;
|
||||
return v;
|
||||
}
|
||||
|
||||
function hiddenVal(id, opt) {
|
||||
var el = document.getElementById('cbid.' + state.app + '.' + id + '.' + opt);
|
||||
return el && el.value != null ? String(el.value).trim() : '';
|
||||
}
|
||||
|
||||
function cellsOf(tr) {
|
||||
var out = [];
|
||||
for (var i = 0; i < tr.children.length; i++) {
|
||||
var tag = tr.children[i].tagName;
|
||||
if (tag === 'TD' || tag === 'TH')
|
||||
out.push(tr.children[i]);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
var wordSplit;
|
||||
try {
|
||||
wordSplit = new RegExp('[^\\p{L}\\p{N}]+', 'u');
|
||||
} catch (e) {
|
||||
wordSplit = /[\s.,;:!?|\/\\()\[\]{}<>"'`~@#$%^&*=+_-]+/;
|
||||
}
|
||||
|
||||
function wordsOf(text) {
|
||||
var seen = {};
|
||||
var out = [];
|
||||
var parts = String(text || '').split(wordSplit);
|
||||
for (var i = 0; i < parts.length; i++) {
|
||||
var w = parts[i];
|
||||
if (w.length < 2 || /^\d+$/.test(w))
|
||||
continue;
|
||||
var k = w.toLowerCase();
|
||||
if (!seen[k]) {
|
||||
seen[k] = 1;
|
||||
out.push(w);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function infoOf(table) {
|
||||
var prefix = 'cbi-' + state.app + '-nodes-';
|
||||
var id = table && table.id ? table.id : '';
|
||||
if (id.indexOf(prefix) !== 0 || id.slice(-6) !== '-table')
|
||||
return null;
|
||||
return { group: id.slice(prefix.length, -6) };
|
||||
}
|
||||
|
||||
function rowId(tr) {
|
||||
var prefix = 'cbi-' + state.app + '-';
|
||||
return tr.id.indexOf(prefix) === 0 ? tr.id.substring(prefix.length) : tr.id;
|
||||
}
|
||||
|
||||
function bodyOf(table) {
|
||||
return table.querySelector('tbody') || table;
|
||||
}
|
||||
|
||||
function dataRows(table) {
|
||||
var body = bodyOf(table);
|
||||
var out = [];
|
||||
for (var i = 0; i < body.children.length; i++) {
|
||||
var tr = body.children[i];
|
||||
if (tr.tagName === 'TR' && tr.classList.contains('cbi-section-table-row') && !tr.classList.contains('placeholder'))
|
||||
out.push(tr);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function colIndexMap(table) {
|
||||
var rows = dataRows(table);
|
||||
var map = {};
|
||||
if (!rows.length)
|
||||
return map;
|
||||
var cells = cellsOf(rows[0]);
|
||||
for (var i = 0; i < cells.length; i++) {
|
||||
for (var cls in COLUMNS) {
|
||||
if (cells[i].classList.contains(cls))
|
||||
map[COLUMNS[cls]] = i;
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
function typeOf(tr, cols) {
|
||||
var cell = cellsOf(tr)[cols.name];
|
||||
var text = cell ? cell.textContent.replace(/\s+/g, ' ').trim() : '';
|
||||
var i = text.indexOf('\uFF1A');
|
||||
if (i < 0)
|
||||
i = text.indexOf(':');
|
||||
return (i < 0 ? '' : text.slice(0, i)).replace(/\s+Realm$/, '').trim();
|
||||
}
|
||||
|
||||
function sortKey(tr, key, cols) {
|
||||
var id = rowId(tr);
|
||||
var cell;
|
||||
if (key === 'name')
|
||||
return hiddenVal(id, 'remarks') || ((cellsOf(tr)[cols.name] || {}).textContent || '').trim();
|
||||
if (key === 'type')
|
||||
return typeOf(tr, cols) + '\u0000' + hiddenVal(id, 'remarks');
|
||||
if (key === 'addr') {
|
||||
var address = hiddenVal(id, 'address');
|
||||
if (!address)
|
||||
return null;
|
||||
return address + '\u0000' + ('00000' + hiddenVal(id, 'port')).slice(-5);
|
||||
}
|
||||
cell = cellsOf(tr)[cols[key]];
|
||||
return cell ? parseLatency(cell.textContent) : null;
|
||||
}
|
||||
|
||||
function detectionMode() {
|
||||
try {
|
||||
if (typeof auto_detection_time === 'undefined')
|
||||
return null;
|
||||
return auto_detection_time;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/* { inflight: requests still running, left: cells still waiting for a result } */
|
||||
function pingBusy() {
|
||||
var mode = detectionMode();
|
||||
var inflight = 0;
|
||||
var left = 0;
|
||||
if (mode !== 'icmp' && mode !== 'tcping')
|
||||
return { inflight: 0, left: 0 };
|
||||
var list = window.ajax && window.ajax.list;
|
||||
if (list && list.length) {
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
if (list[i].readyState > 0 && list[i].readyState < 4)
|
||||
inflight++;
|
||||
}
|
||||
}
|
||||
if (!state.root)
|
||||
return { inflight: inflight, left: 0 };
|
||||
var spans = state.root.getElementsByClassName(mode === 'tcping' ? 'tcping_value' : 'ping_value');
|
||||
for (var s = 0; s < spans.length; s++) {
|
||||
var id = spans[s].getAttribute('cbiid');
|
||||
if (!id || spans[s].textContent.trim() !== '---')
|
||||
continue;
|
||||
var full = null;
|
||||
if (typeof window.get_address_full === 'function')
|
||||
full = window.get_address_full(id);
|
||||
if (!full)
|
||||
full = { address: hiddenVal(id, 'address'), port: hiddenVal(id, 'port') };
|
||||
if (full.address && (mode === 'icmp' || full.port))
|
||||
left++;
|
||||
}
|
||||
return { inflight: inflight, left: left };
|
||||
}
|
||||
|
||||
function note(table, text) {
|
||||
var id = 'pw-note-' + table.id;
|
||||
var el = document.getElementById(id);
|
||||
if (!text) {
|
||||
if (el && el.parentNode)
|
||||
el.parentNode.removeChild(el);
|
||||
return;
|
||||
}
|
||||
if (!el) {
|
||||
el = document.createElement('div');
|
||||
el.id = id;
|
||||
el.className = 'pw-sort-note';
|
||||
table.parentNode.insertBefore(el, table);
|
||||
}
|
||||
el.textContent = text;
|
||||
}
|
||||
|
||||
function setIndicator(table, th, sub, key, dir) {
|
||||
var arrows = table.querySelectorAll('.pw-sort-arrow');
|
||||
var subs = table.querySelectorAll('.pw-sort-sub');
|
||||
var headers = table.querySelectorAll('th[data-pw-sort]');
|
||||
var i;
|
||||
for (i = 0; i < arrows.length; i++)
|
||||
arrows[i].textContent = '';
|
||||
for (i = 0; i < subs.length; i++)
|
||||
subs[i].classList.remove('pw-sort-active');
|
||||
for (i = 0; i < headers.length; i++) {
|
||||
if (headers[i] !== th) {
|
||||
delete headers[i].dataset.pwDir;
|
||||
delete headers[i].dataset.pwKey;
|
||||
}
|
||||
}
|
||||
var arrow = th.querySelector('.pw-sort-arrow');
|
||||
if (arrow && key !== 'orig' && !sub)
|
||||
arrow.textContent = dir === 'desc' ? '\u25BC' : '\u25B2';
|
||||
if (sub && key !== 'orig')
|
||||
sub.classList.add('pw-sort-active');
|
||||
}
|
||||
|
||||
function applyOrder(table, info, rows) {
|
||||
var body = bodyOf(table);
|
||||
var frag = document.createDocumentFragment();
|
||||
for (var i = 0; i < rows.length; i++) {
|
||||
rows[i].classList.remove('cbi-rowstyle-1', 'cbi-rowstyle-2');
|
||||
rows[i].classList.add('cbi-rowstyle-' + (i % 2 + 1));
|
||||
frag.appendChild(rows[i]);
|
||||
}
|
||||
body.appendChild(frag);
|
||||
if (typeof window.set_order === 'function')
|
||||
window.set_order(info.group);
|
||||
}
|
||||
|
||||
function doSort(table, th, sub, key) {
|
||||
var info = infoOf(table);
|
||||
var rows = dataRows(table);
|
||||
var dir = 'asc';
|
||||
if (!info || rows.length < 2)
|
||||
return;
|
||||
if (key === 'orig') {
|
||||
var pos = {};
|
||||
var orig = table.__pwOrig || [];
|
||||
for (var n = 0; n < orig.length; n++)
|
||||
pos[orig[n]] = n;
|
||||
rows.sort(function (a, b) {
|
||||
var pa = pos[rowId(a)];
|
||||
var pb = pos[rowId(b)];
|
||||
return (pa == null ? 1e9 : pa) - (pb == null ? 1e9 : pb);
|
||||
});
|
||||
delete th.dataset.pwDir;
|
||||
delete th.dataset.pwKey;
|
||||
} else {
|
||||
dir = (th.dataset.pwKey === key && th.dataset.pwDir === 'asc') ? 'desc' : 'asc';
|
||||
th.dataset.pwKey = key;
|
||||
th.dataset.pwDir = dir;
|
||||
var cols = colIndexMap(table);
|
||||
var sign = dir === 'desc' ? -1 : 1;
|
||||
var list = [];
|
||||
for (var i = 0; i < rows.length; i++)
|
||||
list.push({ tr: rows[i], i: i, k: sortKey(rows[i], key, cols) });
|
||||
list.sort(function (a, b) {
|
||||
var emptyA = (a.k === null || a.k === '');
|
||||
var emptyB = (b.k === null || b.k === '');
|
||||
if (emptyA || emptyB)
|
||||
return emptyA === emptyB ? a.i - b.i : (emptyA ? 1 : -1);
|
||||
var cmp = (typeof a.k === 'number' && typeof b.k === 'number') ? a.k - b.k : cmpText(String(a.k), String(b.k));
|
||||
return cmp !== 0 ? sign * cmp : a.i - b.i;
|
||||
});
|
||||
rows = [];
|
||||
for (var j = 0; j < list.length; j++)
|
||||
rows.push(list[j].tr);
|
||||
}
|
||||
applyOrder(table, info, rows);
|
||||
refreshFilter(table);
|
||||
setIndicator(table, th, sub, key, dir);
|
||||
note(table, key === 'orig' ? T('restored') : T('changed'));
|
||||
}
|
||||
|
||||
function requestSort(table, th, sub, key) {
|
||||
if (table.__pwWait) {
|
||||
clearInterval(table.__pwWait);
|
||||
table.__pwWait = null;
|
||||
}
|
||||
var st = pingBusy();
|
||||
if (!st.inflight && !st.left) {
|
||||
doSort(table, th, sub, key);
|
||||
return;
|
||||
}
|
||||
var started = Date.now();
|
||||
var idle = 0;
|
||||
note(table, T('wait').replace('%d', st.left || st.inflight));
|
||||
table.__pwWait = setInterval(function () {
|
||||
st = pingBusy();
|
||||
idle = st.inflight ? 0 : idle + 1;
|
||||
if ((!st.inflight && !st.left) || idle >= 6) {
|
||||
clearInterval(table.__pwWait);
|
||||
table.__pwWait = null;
|
||||
if (document.body.contains(table))
|
||||
doSort(table, th, sub, key);
|
||||
return;
|
||||
}
|
||||
if (Date.now() - started > 120000) {
|
||||
clearInterval(table.__pwWait);
|
||||
table.__pwWait = null;
|
||||
note(table, T('waitFail'));
|
||||
return;
|
||||
}
|
||||
note(table, T('wait').replace('%d', st.left || st.inflight));
|
||||
}, 500);
|
||||
}
|
||||
|
||||
function enhance(table) {
|
||||
var info = infoOf(table);
|
||||
if (!info || table.__pwReady)
|
||||
return;
|
||||
var titles = table.querySelector('tr.cbi-section-table-titles');
|
||||
var rows = dataRows(table);
|
||||
if (!titles || !rows.length)
|
||||
return;
|
||||
var cols = colIndexMap(table);
|
||||
var headers = cellsOf(titles);
|
||||
table.__pwReady = true;
|
||||
table.__pwOrig = [];
|
||||
for (var r = 0; r < rows.length; r++)
|
||||
table.__pwOrig.push(rowId(rows[r]));
|
||||
for (var key in cols) {
|
||||
if (!Object.prototype.hasOwnProperty.call(cols, key))
|
||||
continue;
|
||||
var th = headers[cols[key]];
|
||||
if (!th || th.classList.contains('cbi-section-actions'))
|
||||
continue;
|
||||
th.setAttribute('data-pw-sort', key);
|
||||
th.title = T('hint');
|
||||
var arrow = document.createElement('span');
|
||||
arrow.className = 'pw-sort-arrow';
|
||||
th.appendChild(arrow);
|
||||
if (key !== 'name')
|
||||
continue;
|
||||
for (var s = 0; s < NAME_EXTRA.length; s++) {
|
||||
var spec = NAME_EXTRA[s];
|
||||
var sp = document.createElement('span');
|
||||
sp.className = 'pw-sort-sub';
|
||||
sp.setAttribute('data-pw-sort', spec[0]);
|
||||
sp.textContent = T(spec[1]);
|
||||
sp.title = T(spec[2]);
|
||||
th.appendChild(sp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function rowVisible(el) {
|
||||
var row = el && el.closest ? el.closest('tr') : null;
|
||||
return !row || !row.classList.contains('pw-row-hidden');
|
||||
}
|
||||
|
||||
function filterDefs(rows, cols) {
|
||||
return [
|
||||
{
|
||||
key: 'type',
|
||||
label: T('type'),
|
||||
multi: false,
|
||||
get: function (tr) { return typeOf(tr, cols); }
|
||||
},
|
||||
{
|
||||
key: 'word',
|
||||
label: T('fWord'),
|
||||
multi: true,
|
||||
minCount: 2,
|
||||
max: 40,
|
||||
get: function (tr) { return wordsOf(hiddenVal(rowId(tr), 'remarks')); }
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
function searchText(tr, cols) {
|
||||
var id = rowId(tr);
|
||||
return [
|
||||
typeOf(tr, cols),
|
||||
hiddenVal(id, 'remarks'),
|
||||
hiddenVal(id, 'address') + ':' + hiddenVal(id, 'port')
|
||||
].join(' ');
|
||||
}
|
||||
|
||||
function collect(table) {
|
||||
var rows = dataRows(table);
|
||||
var cols = colIndexMap(table);
|
||||
var defs = rows.length >= 2 ? filterDefs(rows, cols) : [];
|
||||
var shown = [];
|
||||
for (var d = 0; d < defs.length; d++) {
|
||||
var def = defs[d];
|
||||
var counts = {};
|
||||
var labels = {};
|
||||
var vals = [];
|
||||
for (var r = 0; r < rows.length; r++) {
|
||||
var raw = def.get(rows[r]);
|
||||
var list = def.multi ? (raw || []) : [raw == null ? '' : String(raw)];
|
||||
var keys = [];
|
||||
for (var n = 0; n < list.length; n++) {
|
||||
var item = list[n];
|
||||
var k = def.multi ? String(item).toLowerCase() : String(item);
|
||||
counts[k] = (counts[k] || 0) + 1;
|
||||
if (!labels[k])
|
||||
labels[k] = {};
|
||||
labels[k][item] = (labels[k][item] || 0) + 1;
|
||||
keys.push(k);
|
||||
}
|
||||
vals.push(keys);
|
||||
}
|
||||
var opts = [];
|
||||
for (var k in counts) {
|
||||
if (!Object.prototype.hasOwnProperty.call(counts, k))
|
||||
continue;
|
||||
if (def.multi && (counts[k] < (def.minCount || 1) || counts[k] >= rows.length))
|
||||
continue;
|
||||
var best = '';
|
||||
var bestN = -1;
|
||||
for (var label in labels[k]) {
|
||||
if (labels[k][label] > bestN) {
|
||||
bestN = labels[k][label];
|
||||
best = label;
|
||||
}
|
||||
}
|
||||
opts.push({ v: k, label: best === '' ? T('empty') : best, n: counts[k] });
|
||||
}
|
||||
opts.sort(function (a, b) {
|
||||
if (def.multi && a.n !== b.n)
|
||||
return b.n - a.n;
|
||||
return cmpText(a.label, b.label);
|
||||
});
|
||||
if (def.max && opts.length > def.max)
|
||||
opts = opts.slice(0, def.max);
|
||||
var enough = def.multi ? opts.length >= 1 : opts.length >= 2;
|
||||
if (!enough)
|
||||
continue;
|
||||
def.vals = vals;
|
||||
def.opts = opts;
|
||||
shown.push(def);
|
||||
}
|
||||
var search = rows.length >= 2;
|
||||
var sig = String(rows.length) + (search ? 's' : '');
|
||||
for (var i = 0; i < shown.length; i++) {
|
||||
sig += '|' + shown[i].key + '=';
|
||||
for (var o = 0; o < shown[i].opts.length; o++)
|
||||
sig += shown[i].opts[o].v + ':' + shown[i].opts[o].n + ',';
|
||||
}
|
||||
return { rows: rows, cols: cols, defs: shown, search: search, sig: sig };
|
||||
}
|
||||
|
||||
function barId(table) {
|
||||
return 'pw-filter-' + table.id;
|
||||
}
|
||||
|
||||
function buildBar(table, collected) {
|
||||
var st = table.__pwF || (table.__pwF = { sel: {}, q: '' });
|
||||
var id = barId(table);
|
||||
var bar = document.getElementById(id);
|
||||
if (!bar) {
|
||||
bar = document.createElement('div');
|
||||
bar.id = id;
|
||||
bar.className = 'pw-filter';
|
||||
bar.title = T('filterHint');
|
||||
table.parentNode.insertBefore(bar, table);
|
||||
bar.__pwTable = table;
|
||||
}
|
||||
while (bar.firstChild)
|
||||
bar.removeChild(bar.firstChild);
|
||||
var head = document.createElement('span');
|
||||
head.textContent = T('filter');
|
||||
bar.appendChild(head);
|
||||
var keep = {};
|
||||
for (var d = 0; d < collected.defs.length; d++) {
|
||||
var def = collected.defs[d];
|
||||
var lab = document.createElement('label');
|
||||
var sel = document.createElement('select');
|
||||
lab.appendChild(document.createTextNode(def.label));
|
||||
sel.className = 'cbi-input-select';
|
||||
sel.setAttribute('data-pw-filter', def.key);
|
||||
var all = document.createElement('option');
|
||||
all.value = '';
|
||||
all.textContent = T('all') + ' (' + collected.rows.length + ')';
|
||||
sel.appendChild(all);
|
||||
for (var o = 0; o < def.opts.length; o++) {
|
||||
var op = document.createElement('option');
|
||||
op.value = 'v:' + def.opts[o].v;
|
||||
op.textContent = def.opts[o].label + ' (' + def.opts[o].n + ')';
|
||||
sel.appendChild(op);
|
||||
}
|
||||
var cur = st.sel[def.key] || '';
|
||||
var known = false;
|
||||
if (cur) {
|
||||
for (var c = 0; c < def.opts.length; c++) {
|
||||
if (('v:' + def.opts[c].v) === cur) {
|
||||
known = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!known)
|
||||
cur = '';
|
||||
sel.value = cur;
|
||||
if (cur)
|
||||
keep[def.key] = cur;
|
||||
lab.appendChild(sel);
|
||||
bar.appendChild(lab);
|
||||
}
|
||||
st.sel = keep;
|
||||
if (collected.search) {
|
||||
var input = document.createElement('input');
|
||||
input.type = 'search';
|
||||
input.className = 'cbi-input-text';
|
||||
input.placeholder = T('search');
|
||||
input.title = T('searchHint');
|
||||
input.autocomplete = 'off';
|
||||
input.setAttribute('data-pw-search', '1');
|
||||
input.value = st.q;
|
||||
bar.appendChild(input);
|
||||
} else {
|
||||
st.q = '';
|
||||
}
|
||||
var count = document.createElement('span');
|
||||
count.className = 'pw-filter-count';
|
||||
bar.appendChild(count);
|
||||
var reset = document.createElement('button');
|
||||
reset.type = 'button';
|
||||
reset.className = 'btn cbi-button';
|
||||
reset.setAttribute('data-pw-reset', '1');
|
||||
reset.textContent = T('reset');
|
||||
reset.title = T('resetHint');
|
||||
bar.appendChild(reset);
|
||||
return bar;
|
||||
}
|
||||
|
||||
function listHas(list, value) {
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
if (list[i] === value)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function applyFilter(table, collected, bar) {
|
||||
var st = table.__pwF || { sel: {}, q: '' };
|
||||
var q = String(st.q || '').toLowerCase().trim();
|
||||
var active = !!q;
|
||||
var shown = 0;
|
||||
var d;
|
||||
for (d = 0; d < collected.defs.length; d++) {
|
||||
if (st.sel[collected.defs[d].key])
|
||||
active = true;
|
||||
}
|
||||
for (var i = 0; i < collected.rows.length; i++) {
|
||||
var tr = collected.rows[i];
|
||||
var ok = true;
|
||||
for (d = 0; d < collected.defs.length; d++) {
|
||||
var sel = st.sel[collected.defs[d].key];
|
||||
if (ok && sel && !listHas(collected.defs[d].vals[i], sel.slice(2)))
|
||||
ok = false;
|
||||
}
|
||||
if (ok && q && searchText(tr, collected.cols).toLowerCase().indexOf(q) < 0)
|
||||
ok = false;
|
||||
if (ok !== !tr.classList.contains('pw-row-hidden'))
|
||||
tr.classList.toggle('pw-row-hidden', !ok);
|
||||
if (ok) {
|
||||
shown++;
|
||||
var want = 'cbi-rowstyle-' + ((shown - 1) % 2 + 1);
|
||||
var other = want === 'cbi-rowstyle-1' ? 'cbi-rowstyle-2' : 'cbi-rowstyle-1';
|
||||
if (!tr.classList.contains(want))
|
||||
tr.classList.add(want);
|
||||
if (tr.classList.contains(other))
|
||||
tr.classList.remove(other);
|
||||
} else {
|
||||
var cb = tr.querySelector('input.nodes_select');
|
||||
if (cb && cb.checked) {
|
||||
cb.checked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (typeof window.update_select_state === 'function')
|
||||
window.update_select_state();
|
||||
var count = bar.querySelector('.pw-filter-count');
|
||||
var txt = T('shown').replace('%s', shown).replace('%t', collected.rows.length);
|
||||
if (count && count.textContent !== txt)
|
||||
count.textContent = txt;
|
||||
if (bar.classList.contains('pw-filter-on') !== active)
|
||||
bar.classList.toggle('pw-filter-on', active);
|
||||
}
|
||||
|
||||
function removeBar(table) {
|
||||
var bar = document.getElementById(barId(table));
|
||||
if (bar && bar.parentNode)
|
||||
bar.parentNode.removeChild(bar);
|
||||
var rows = dataRows(table);
|
||||
for (var i = 0; i < rows.length; i++) {
|
||||
if (rows[i].classList.contains('pw-row-hidden'))
|
||||
rows[i].classList.remove('pw-row-hidden');
|
||||
}
|
||||
table.__pwSig = null;
|
||||
}
|
||||
|
||||
function refreshFilter(table) {
|
||||
if (!infoOf(table))
|
||||
return;
|
||||
var collected = collect(table);
|
||||
if (!collected.defs.length && !collected.search) {
|
||||
if (table.__pwSig)
|
||||
removeBar(table);
|
||||
return;
|
||||
}
|
||||
var bar = document.getElementById(barId(table));
|
||||
if (!bar || collected.sig !== table.__pwSig) {
|
||||
bar = buildBar(table, collected);
|
||||
table.__pwSig = collected.sig;
|
||||
}
|
||||
applyFilter(table, collected, bar);
|
||||
}
|
||||
|
||||
function barTable(el) {
|
||||
var bar = el && el.closest ? el.closest('.pw-filter') : null;
|
||||
return bar && bar.__pwTable ? bar.__pwTable : null;
|
||||
}
|
||||
|
||||
function eventTarget(ev) {
|
||||
var t = ev.target;
|
||||
if (t && t.nodeType !== 1)
|
||||
t = t.parentNode;
|
||||
return t;
|
||||
}
|
||||
|
||||
function bind(root) {
|
||||
root.addEventListener('click', function (ev) {
|
||||
var t = eventTarget(ev);
|
||||
if (t && t.getAttribute && t.getAttribute('data-pw-reset')) {
|
||||
var resetTable = barTable(t);
|
||||
if (!resetTable)
|
||||
return;
|
||||
ev.preventDefault();
|
||||
resetTable.__pwF = { sel: {}, q: '' };
|
||||
resetTable.__pwSig = null;
|
||||
refreshFilter(resetTable);
|
||||
return;
|
||||
}
|
||||
var el = t && t.closest ? t.closest('[data-pw-sort]') : null;
|
||||
if (!el || !root.contains(el))
|
||||
return;
|
||||
if (t.closest && t.closest('input, a, button, select'))
|
||||
return;
|
||||
var th = el.closest('th');
|
||||
var table = el.closest('table');
|
||||
if (!th || !table || !infoOf(table))
|
||||
return;
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
requestSort(table, th, el === th ? null : el, el.getAttribute('data-pw-sort'));
|
||||
});
|
||||
root.addEventListener('change', function (ev) {
|
||||
var t = eventTarget(ev);
|
||||
if (!t || !t.getAttribute || !t.getAttribute('data-pw-filter'))
|
||||
return;
|
||||
var table = barTable(t);
|
||||
if (!table)
|
||||
return;
|
||||
(table.__pwF = table.__pwF || { sel: {}, q: '' }).sel[t.getAttribute('data-pw-filter')] = t.value;
|
||||
refreshFilter(table);
|
||||
});
|
||||
root.addEventListener('input', function (ev) {
|
||||
var t = eventTarget(ev);
|
||||
if (!t || !t.getAttribute || !t.getAttribute('data-pw-search'))
|
||||
return;
|
||||
var table = barTable(t);
|
||||
if (!table)
|
||||
return;
|
||||
clearTimeout(searchTimer);
|
||||
searchTimer = setTimeout(function () {
|
||||
(table.__pwF = table.__pwF || { sel: {}, q: '' }).q = t.value;
|
||||
refreshFilter(table);
|
||||
}, 150);
|
||||
});
|
||||
root.addEventListener('keydown', function (ev) {
|
||||
var t = eventTarget(ev);
|
||||
if (ev.key === 'Enter' && t && t.getAttribute && t.getAttribute('data-pw-search'))
|
||||
ev.preventDefault();
|
||||
});
|
||||
}
|
||||
|
||||
function init() {
|
||||
var box = document.getElementById('pw-nodesort-i18n');
|
||||
var root = document.getElementById('node_list');
|
||||
if (!box || !root)
|
||||
return;
|
||||
state.app = box.getAttribute('data-app') || 'passwall';
|
||||
state.text = readText(box);
|
||||
state.root = root;
|
||||
prepareCollator();
|
||||
if (!state.bound) {
|
||||
bind(root);
|
||||
state.bound = true;
|
||||
}
|
||||
}
|
||||
|
||||
function enhanceAll() {
|
||||
if (!state.root)
|
||||
return;
|
||||
var tables = state.root.querySelectorAll('table.cbi-section-table');
|
||||
for (var i = 0; i < tables.length; i++) {
|
||||
try {
|
||||
enhance(tables[i]);
|
||||
refreshFilter(tables[i]);
|
||||
} catch (e) {
|
||||
if (window.console)
|
||||
console.warn('passwall node list sort:', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.pwNodeSort = {
|
||||
init: init,
|
||||
enhanceAll: enhanceAll,
|
||||
rowVisible: rowVisible
|
||||
};
|
||||
})();
|
||||
Reference in new issue
Block a user