LUCI_TITLE:=LuCI support for Adblock
LUCI_DEPENDS:=+luci-base +luci-lib-uqr +adblock
-PKG_VERSION:=4.5.2
-PKG_RELEASE:=3
+PKG_VERSION:=4.5.3
+PKG_RELEASE:=1
PKG_LICENSE:=Apache-2.0
PKG_MAINTAINER:=Dirk Brenken <dev@brenken.org>
'require ui';
const localFile = '/etc/adblock/adblock.allowlist';
-let notMsg = false, errMsg = false;
+const maxSize = 100000;
+let notMsg = false;
const resetScroll = () => {
document.body.scrollTop = document.documentElement.scrollTop = 0;
return view.extend({
load: function () {
- return L.resolveDefault(fs.stat(localFile), "")
+ return L.resolveDefault(fs.stat(localFile), null)
.then(function (stat) {
- if (!stat) {
- return fs.write(localFile, "");
- }
- return Promise.all([
- L.resolveDefault(fs.stat(localFile), ""),
- L.resolveDefault(fs.read_direct(localFile), "")
- ]);
- });
+ if (!stat) {
+ return fs.write(localFile, "").then(() => [{ size: 0 }, ""]);
+ }
+ return Promise.all([
+ Promise.resolve(stat),
+ L.resolveDefault(fs.read_direct(localFile), "")
+ ]);
+ });
},
+
render: function (allowlist) {
- if (allowlist[0] && allowlist[0].size >= 100000) {
+ const size = allowlist[0] ? allowlist[0].size : 0;
+ const content = allowlist[1] != null ? allowlist[1] : '';
+ const tooBig = size >= maxSize;
+
+ if (tooBig) {
resetScroll();
ui.addNotification(null, E('p', _('The allowlist is too big, unable to save modifications.')), 'error');
}
'style': 'width: 100% !important; padding: 5px; font-family: monospace; margin-top: .4em',
'spellcheck': 'false',
'wrap': 'off',
- 'rows': 25
- }, [allowlist[1] != null ? allowlist[1] : ''])
+ 'rows': 25,
+ 'readonly': tooBig ? 'readonly' : null,
+ 'input': function () { notMsg = false; }
+ }, [content])
]);
},
- handleSave: function (ev) {
- let value = ((document.querySelector('textarea').value || '').trim().toLowerCase().replace(/[^a-z0-9\.\-# \r\n]/g, '').replace(/\r\n?/g, '\n'));
+
+ handleSave: function (_ev) {
+ const value = ((document.querySelector('textarea').value || '').trim().toLowerCase().replace(/[^a-z0-9.\-# \r\n]/g, '').replace(/\r\n?/g, '\n'));
return fs.write(localFile, value + "\n")
- .then(function () {
- document.querySelector('textarea').value = value;
- resetScroll();
- if (!notMsg) {
- ui.addNotification(null, E('p', _('Allowlist modifications have been saved, reload adblock that changes take effect.')), 'info');
- notMsg = true;
- }
- }).catch(function (e) {
- resetScroll();
- if (!errMsg) {
+ .then(function () {
+ document.querySelector('textarea').value = value + "\n";
+ resetScroll();
+ if (!notMsg) {
+ notMsg = true;
+ ui.addNotification(null, E('p', _('Allowlist modifications have been saved, reload adblock that changes take effect.')), 'info');
+ }
+ }).catch(function (e) {
+ resetScroll();
ui.addNotification(null, E('p', _('Unable to save modifications: %s').format(e.message)), 'error');
- errMsg = true;
- }
- });
+ });
},
+
handleSaveApply: null,
handleReset: null
-});
+});
\ No newline at end of file
'require ui';
const localFile = '/etc/adblock/adblock.blocklist';
-let notMsg = false, errMsg = false;
+const maxSize = 100000;
+let notMsg = false;
const resetScroll = () => {
document.body.scrollTop = document.documentElement.scrollTop = 0;
return view.extend({
load: function () {
- return L.resolveDefault(fs.stat(localFile), "")
+ return L.resolveDefault(fs.stat(localFile), null)
.then(function (stat) {
- if (!stat) {
- return fs.write(localFile, "");
- }
- return Promise.all([
- L.resolveDefault(fs.stat(localFile), ""),
- L.resolveDefault(fs.read_direct(localFile), "")
- ]);
- });
+ if (!stat) {
+ return fs.write(localFile, "").then(() => [{ size: 0 }, ""]);
+ }
+ return Promise.all([
+ Promise.resolve(stat),
+ L.resolveDefault(fs.read_direct(localFile), "")
+ ]);
+ });
},
+
render: function (blocklist) {
- if (blocklist[0] && blocklist[0].size >= 100000) {
+ const size = blocklist[0] ? blocklist[0].size : 0;
+ const content = blocklist[1] != null ? blocklist[1] : '';
+ const tooBig = size >= maxSize;
+
+ if (tooBig) {
resetScroll();
ui.addNotification(null, E('p', _('The blocklist is too big, unable to save modifications.')), 'error');
}
E('p', _('This is the local adblock blocklist to always-block certain domains.<br /> \
<em><b>Please note:</b></em> add only one domain per line. Comments introduced with \'#\' are allowed - ip addresses, wildcards and regex are not.')),
E('textarea', {
- 'style': 'min-height: 500px; max-height: 90vh; width: 100%; padding: 5px; font-family: monospace; resize: vertical;',
+ 'style': 'width: 100% !important; padding: 5px; font-family: monospace; margin-top: .4em',
'spellcheck': 'false',
'wrap': 'off',
- 'rows': 25
- }, [blocklist[1] != null ? blocklist[1] : ''])
+ 'rows': 25,
+ 'readonly': tooBig ? 'readonly' : null,
+ 'input': function () { notMsg = false; }
+ }, [content])
]);
},
- handleSave: function (ev) {
- let value = ((document.querySelector('textarea').value || '').trim().toLowerCase().replace(/[^a-z0-9\.\-# \r\n]/g, '').replace(/\r\n?/g, '\n'));
+
+ handleSave: function (_ev) {
+ const value = ((document.querySelector('textarea').value || '').trim().toLowerCase().replace(/[^a-z0-9.\-# \r\n]/g, '').replace(/\r\n?/g, '\n'));
return fs.write(localFile, value + "\n")
- .then(function () {
- document.querySelector('textarea').value = value;
- resetScroll();
- if (!notMsg) {
- ui.addNotification(null, E('p', _('Blocklist modifications have been saved, reload adblock that changes take effect.')), 'info');
- notMsg = true;
- }
- }).catch(function (e) {
- resetScroll();
- if (!errMsg) {
+ .then(function () {
+ document.querySelector('textarea').value = value + "\n";
+ resetScroll();
+ if (!notMsg) {
+ notMsg = true;
+ ui.addNotification(null, E('p', _('Blocklist modifications have been saved, reload adblock that changes take effect.')), 'info');
+ }
+ }).catch(function (e) {
+ resetScroll();
ui.addNotification(null, E('p', _('Unable to save modifications: %s').format(e.message)), 'error');
- errMsg = true;
- }
- });
+ });
},
+
handleSaveApply: null,
handleReset: null
-});
+});
\ No newline at end of file
'require ui';
'require uci';
-var notMsg = false, errMsg = false;
+/* separate flags per notification context */
+let listNotMsg = false;
+let mapNotMsg = false;
/*
button handling
'click': ui.createHandlerFn(this, function (ev) {
L.resolveDefault(fs.read_direct('/etc/adblock/adblock.blocklist'), '')
.then(function (res) {
- var domain = document.getElementById('blocklist').value.trim().toLowerCase().replace(/[^a-z0-9\.\-]/g, '');
- var pattern = new RegExp('^' + domain.replace(/[\.]/g, '\\.') + '$', 'm');
+ const domain = document.getElementById('blocklist').value.trim().toLowerCase().replace(/[^a-z0-9.\-]/g, '');
+ const pattern = new RegExp('^' + domain.replace(/[.]/g, '\\.') + '$', 'm');
if (res.search(pattern) === -1) {
- var blocklist = res + domain + '\n';
+ const blocklist = res + domain + '\n';
fs.write('/etc/adblock/adblock.blocklist', blocklist);
- if (!notMsg) {
- notMsg = true;
+ if (!listNotMsg) {
+ listNotMsg = true;
ui.addNotification(null, E('p', _('Blocklist modifications have been saved, reload adblock that changes take effect.')), 'info');
}
}
'click': ui.createHandlerFn(this, function (ev) {
L.resolveDefault(fs.read_direct('/etc/adblock/adblock.allowlist'), '')
.then(function (res) {
- var domain = document.getElementById('allowlist').value.trim().toLowerCase().replace(/[^a-z0-9\.\-]/g, '');
- var pattern = new RegExp('^' + domain.replace(/[\.]/g, '\\.') + '$', 'm');
+ const domain = document.getElementById('allowlist').value.trim().toLowerCase().replace(/[^a-z0-9.\-]/g, '');
+ const pattern = new RegExp('^' + domain.replace(/[.]/g, '\\.') + '$', 'm');
if (res.search(pattern) === -1) {
- var allowlist = res + domain + '\n';
+ const allowlist = res + domain + '\n';
fs.write('/etc/adblock/adblock.allowlist', allowlist);
- if (!notMsg) {
- notMsg = true;
+ if (!listNotMsg) {
+ listNotMsg = true;
ui.addNotification(null, E('p', _('Allowlist modifications have been saved, reload adblock that changes take effect.')), 'info');
}
}
E('button', {
'class': 'btn cbi-button-action',
'click': ui.createHandlerFn(this, function (ev) {
- const domain = document.getElementById('search').value.trim().toLowerCase().replace(/[^a-z0-9\.\-]/g, '');
+ const domain = document.getElementById('search').value.trim().toLowerCase().replace(/[^a-z0-9.\-]/g, '');
if (domain) {
document.getElementById('run').classList.add("spinning");
document.getElementById('search').value = domain;
}
document.getElementById('run').classList.remove("spinning");
document.getElementById('search').value = '';
- })
+ });
}
document.getElementById('search').focus();
})
])
]),
E('label', { 'class': 'cbi-input-text', 'style': 'padding-top:.5em' }, [
- E('input', { 'class': 'cbi-input-text', 'spellcheck': 'false', 'id': 'search' }, [
- ]),
+ E('input', { 'class': 'cbi-input-text', 'spellcheck': 'false', 'id': 'search' }, []),
'\xa0\xa0\xa0',
_('Filter criteria like date, domain or client (optional)')
]),
'click': function () {
document.querySelectorAll('.cbi-page-actions button').forEach(function (btn) {
btn.disabled = true;
- })
+ });
this.blur();
this.classList.add('spinning');
const top_count = document.getElementById('top_count').value;
const res_count = document.getElementById('res_count').value;
- const search = document.getElementById('search').value.trim().replace(/[^\w\.\-\:]/g, '') || '+';
+ const search = document.getElementById('search').value.trim().replace(/[^\w.\-:]/g, '') || '+';
L.resolveDefault(fs.exec_direct('/etc/init.d/adblock', ['report', 'gen', top_count, res_count, search]), '')
.then(function () {
location.reload();
- })
+ });
}
}, _('Refresh'))
])
let a_cnt = '\xa0', a_addr = '\xa0', b_cnt = '\xa0', b_addr = '\xa0', c_cnt = '\xa0', c_addr = '\xa0';
if (content[0].top_clients[i]) {
a_cnt = content[0].top_clients[i].count;
- }
- if (content[0].top_clients[i]) {
a_addr = content[0].top_clients[i].address;
}
if (content[0].top_domains[i]) {
b_cnt = content[0].top_domains[i].count;
- }
- if (content[0].top_domains[i]) {
b_addr = '<a href="https://ip-api.com/#' + encodeURIComponent(content[0].top_domains[i].address) + '" target="_blank" rel="noreferrer noopener" title="Domain Lookup">' + content[0].top_domains[i].address + '</a>';
}
if (content[0].top_blocked[i]) {
c_cnt = content[0].top_blocked[i].count;
- }
- if (content[0].top_blocked[i]) {
c_addr = '<a href="https://ip-api.com/#' + encodeURIComponent(content[0].top_blocked[i].address) + '" target="_blank" rel="noreferrer noopener" title="Domain Lookup">' + content[0].top_blocked[i].address + '</a>';
}
- rows_top.push([
- a_cnt,
- a_addr,
- b_cnt,
- b_addr,
- c_cnt,
- c_addr
- ]);
+ rows_top.push([a_cnt, a_addr, b_cnt, b_addr, c_cnt, c_addr]);
}
cbi_update_table(tbl_top, rows_top);
])
]);
- max = 0;
if (content[0].requests) {
- let button;
max = content[0].requests.length;
for (let i = 0; i < max; i++) {
+ let button;
if (content[0].requests[i].rc === 'NX') {
button = E('button', {
'class': 'btn cbi-button cbi-button-positive',
const page = E('div', { 'class': 'cbi-map', 'id': 'map' }, [
E('div', { 'class': 'cbi-section' }, [
- E('p', _('This tab displays the most recently generated DNS report. Use the \‘Refresh\’ button to update it.')),
+ E('p', _('This tab displays the most recently generated DNS report. Use the \'Refresh\' button to update it.')),
E('div', { 'class': 'cbi-value', 'style': 'position:relative;min-height:220px' }, [
E('div', {
'style': 'position:absolute; top:0; right:0; text-align:center'
'style': 'float:none;margin-right:.4em;',
'id': 'btnTest',
'title': 'Adblock Test',
- 'click': function() {
+ 'click': function () {
window.open('https://adblock.turtlecute.org/', '_blank', 'noopener,noreferrer');
}
}, [_('Adblock Test')]),
'title': 'Map',
'disabled': 'disabled',
'click': ui.createHandlerFn(this, function () {
- if (content[1] && content[1].length > 1) {
+ if (Array.isArray(content[1]) && content[1].length > 1) {
sessionStorage.setItem('mapData', JSON.stringify(content[1]));
return handleAction('map');
- }
- else {
- if (!notMsg) {
- notMsg = true;
+ } else {
+ if (!mapNotMsg) {
+ mapNotMsg = true;
return ui.addNotification(null, E('p', _('No GeoIP Map data!')), 'info');
}
}
}, [_('Refresh...')])
])
]);
+
if (uci.get('adblock', 'global', 'adb_map') === '1') {
const btn = page.querySelector('#btnMap');
if (btn) {
btn.removeAttribute('disabled');
}
}
+
/* Draw Pie Chart with Tooltip */
- const tooltip = E('div', {
+ const tooltipEl = E('div', {
id: 'dnsPieTooltip',
style: 'position:absolute; padding:6px 10px; background:#333; color:#fff; border-radius:4px; font-size:12px; pointer-events:none; opacity:0; transition:opacity .15s; z-index:9999'
});
- document.body.appendChild(tooltip);
- setTimeout(function() {
+ document.body.appendChild(tooltipEl);
+
+ setTimeout(function () {
const total = Number(content[0].total || 0);
const blocked = Number(content[0].blocked || 0);
const allowed = Math.max(total - blocked, 0);
const canvas = document.getElementById('dnsPie');
- if (!canvas || total <= 0)
- return;
+ if (!canvas || total <= 0) return;
const ctx = canvas.getContext('2d');
- const colors = {
- blocked: '#b04a4a',
- allowed: '#6a8f6a'
- };
+ const colors = { blocked: '#b04a4a', allowed: '#6a8f6a' };
+ let finalRot = 0;
- function drawPie(rotation = 0) {
+ function drawPie(rotation) {
const w = canvas.clientWidth;
canvas.width = w;
canvas.height = w;
- const cx = w / 2;
- const cy = w / 2;
- const r = (w / 2) - 4;
+ const cx = w / 2, cy = w / 2, r = (w / 2) - 4;
const blockedAngle = (blocked / total) * 2 * Math.PI;
const allowedAngle = (allowed / total) * 2 * Math.PI;
ctx.lineWidth = 2;
ctx.stroke();
}
+
let rot = 0;
function animate() {
rot += 0.10;
drawPie(rot);
- if (rot < Math.PI * 2)
+ if (rot < Math.PI * 2) {
requestAnimationFrame(animate);
+ } else {
+ finalRot = rot % (2 * Math.PI);
+ drawPie(finalRot);
+ }
}
animate();
- window.addEventListener('resize', function() {
- drawPie(rot);
+
+ window.addEventListener('resize', function () {
+ drawPie(finalRot);
});
+
const tooltip = document.getElementById('dnsPieTooltip');
- canvas.addEventListener('mousemove', function(ev) {
+ const blockedAngle = (blocked / total) * 2 * Math.PI;
+
+ canvas.addEventListener('mousemove', function (ev) {
const rect = canvas.getBoundingClientRect();
const x = ev.clientX - rect.left;
const y = ev.clientY - rect.top;
- const cx = canvas.width / 2;
- const cy = canvas.height / 2;
- const dx = x - cx;
- const dy = y - cy;
- const dist = Math.sqrt(dx*dx + dy*dy);
- if (dist > canvas.width/2 - 4) {
+ const cx = canvas.width / 2, cy = canvas.height / 2;
+ const dx = x - cx, dy = y - cy;
+ const dist = Math.sqrt(dx * dx + dy * dy);
+ if (dist > canvas.width / 2 - 4) {
tooltip.style.opacity = 0;
return;
}
- let angle = Math.atan2(dy, dx);
+ /* normalise angle relative to finalRot so it matches the drawn slices */
+ let angle = Math.atan2(dy, dx) - finalRot;
if (angle < 0) angle += 2 * Math.PI;
- const blockedAngle = (blocked / total) * 2 * Math.PI;
let label, abs, pct;
if (angle < blockedAngle) {
- label = 'Blocked';
- abs = blocked;
+ label = 'Blocked'; abs = blocked;
pct = ((blocked / total) * 100).toFixed(1) + '%';
} else {
- label = 'Allowed';
- abs = allowed;
+ label = 'Allowed'; abs = allowed;
pct = ((allowed / total) * 100).toFixed(1) + '%';
}
tooltip.textContent = `${label}: ${abs} (${pct})`;
tooltip.style.top = ev.pageY + 12 + 'px';
tooltip.style.opacity = 1;
});
- canvas.addEventListener('mouseleave', function() {
+
+ canvas.addEventListener('mouseleave', function () {
tooltip.style.opacity = 0;
});
}, 0);
+
return page;
},
handleSaveApply: null,
handleSave: null,
handleReset: null
-});
+});
\ No newline at end of file
'href': L.resource('view/adblock/custom.css')
}));
+/*
+ module-level file size set during render, read by observer
+*/
+let fileSize = 0;
+
+/*
+ button state helper
+*/
+function updateButtons() {
+ const buttons = document.querySelectorAll('#btnClear, #btnCreate, #btnSave, #btnUpload, #btnDownload');
+ if (fileSize === 0) {
+ if (buttons[1]) buttons[1].removeAttribute('disabled');
+ if (buttons[2]) buttons[2].removeAttribute('disabled');
+ } else {
+ if (buttons[0]) buttons[0].removeAttribute('disabled');
+ if (buttons[3]) buttons[3].removeAttribute('disabled');
+ if (buttons[4]) buttons[4].removeAttribute('disabled');
+ }
+}
+
/*
observe DOM changes
*/
labels.forEach(function (label) {
label.setAttribute("style", "font-weight: bold !important; color: #595 !important;");
})
- L.resolveDefault(fs.stat('/etc/adblock/adblock.custom.feeds'), '').then(function (stat) {
- const buttons = document.querySelectorAll('#btnClear, #btnCreate, #btnSave, #btnUpload, #btnDownload');
- if (buttons[1] && buttons[2] && stat.size === 0) {
- buttons[1].removeAttribute('disabled');
- buttons[2].removeAttribute('disabled');
- } else if (buttons[0] && buttons[3] && buttons[4] && stat.size > 0) {
- buttons[0].removeAttribute('disabled');
- buttons[3].removeAttribute('disabled');
- buttons[4].removeAttribute('disabled');
- }
- });
+ updateButtons();
}
});
if (ev === 'upload') {
return ui.uploadFile('/etc/adblock/adblock.custom.feeds').then(function () {
L.resolveDefault(fs.read_direct('/etc/adblock/adblock.custom.feeds', 'json'), "").then(function (data) {
- if (data) {
- let dataLength = Object.keys(data).length || 0;
- if (dataLength > 0) {
- for (let i = 0; i < dataLength; i++) {
- let feed = Object.keys(data)[i];
- let descr = data[feed].descr;
- if (feed && descr) {
- continue;
- }
- fs.write('/etc/adblock/adblock.custom.feeds', null).then(function () {
- return ui.addNotification(null, E('p', _('Upload of the custom feed file failed.')), 'error');
- });
+ if (data && Object.keys(data).length > 0) {
+ for (let i = 0; i < Object.keys(data).length; i++) {
+ let feed = Object.keys(data)[i];
+ let descr = data[feed].descr;
+ if (feed && descr) {
+ continue;
}
- } else {
- fs.write('/etc/adblock/adblock.custom.feeds', null).then(function () {
- return ui.addNotification(null, E('p', _('Upload of the custom feed file failed.')), 'error');
+ return fs.write('/etc/adblock/adblock.custom.feeds', null).then(function () {
+ ui.addNotification(null, E('p', _('Upload of the custom feed file failed.')), 'error');
});
}
location.reload();
} else {
- fs.write('/etc/adblock/adblock.custom.feeds', null).then(function () {
- return ui.addNotification(null, E('p', _('Upload of the custom feed file failed.')), 'error');
+ return fs.write('/etc/adblock/adblock.custom.feeds', null).then(function () {
+ ui.addNotification(null, E('p', _('Upload of the custom feed file failed.')), 'error');
});
}
});
}
}
/*
- gather all input data
+ gather all input data and fall through from 'save'
*/
- let sumSubElements = [];
+ const exportObj = {};
const nodeKeys = document.querySelectorAll('[id^="widget.cbid.json"][id$="name"]');
for (const keyNode of nodeKeys) {
const keyValue = keyNode.value?.trim();
sub[key] = value;
}
}
- if (sub.descr) {
- sumSubElements.push(keyValue, sub);
+ /* require at least descr and url to produce a valid feed entry */
+ if (sub.descr && sub.url) {
+ exportObj[keyValue] = sub;
}
}
- /*
- construct json object
- */
- let exportObj = {};
- for (let i = 0; i < sumSubElements.length; i += 2) {
- const key = sumSubElements[i];
- const value = sumSubElements[i + 1];
- exportObj[key] = value;
- }
- const exportJson = JSON.stringify(exportObj, null, 4);
/*
save to file and reload
*/
+ const exportJson = JSON.stringify(exportObj, null, 4);
return fs.write('/etc/adblock/adblock.custom.feeds', exportJson)
.then(() => location.reload());
}
return view.extend({
load: function () {
- return L.resolveDefault(fs.stat('/etc/adblock/adblock.custom.feeds'), "")
+ return L.resolveDefault(fs.stat('/etc/adblock/adblock.custom.feeds'), null)
.then(function (stat) {
- if (!stat) {
- return fs.write('/etc/adblock/adblock.custom.feeds', "");
- }
- return L.resolveDefault(fs.read_direct('/etc/adblock/adblock.custom.feeds', 'json'), "");
- });
+ if (!stat) {
+ return fs.write('/etc/adblock/adblock.custom.feeds', "").then(function () {
+ return { size: 0, data: null };
+ });
+ }
+ return L.resolveDefault(fs.read_direct('/etc/adblock/adblock.custom.feeds', 'json'), "")
+ .then(function (data) {
+ return { size: stat.size, data: data };
+ });
+ });
},
- render: function (data) {
+ render: function (result) {
let m, s, o, feed, url, rule, size, descr;
+ const data = result.data;
+ fileSize = result.size;
m = new form.JSONMap(data, null, _('With this editor you can upload your local custom feed file or fill up an initial one (a 1:1 copy of the version shipped with the package). \
The file is located at \'/etc/adblock/adblock.custom.feeds\'. \
if (!value) {
return true;
}
- if (!value.match(/^(http:\/\/|https:\/\/)[A-Za-z0-9\/\.\-\?\&\+_@%=:~#]+$/)) {
+ if (!value.match(/^https?:\/\/[A-Za-z0-9\[\]\/.\-?&+_@%=:~#]+$/)) {
return _('Protocol/URL format not supported');
}
return true;
o.value('feed 1', _('<Domain>'));
o.value('feed 127.0.0.1 2', _('127.0.0.1 <Domain>'));
o.value('feed 0.0.0.0 2', _('0.0.0.0 <Domain>'));
- o.value('feed 3 [|^]', _('<Adblock Plus Syntax>'));
+ o.value('feed || 3 [|^]', _('<Adblock Plus Syntax>'));
o.optional = true;
o.rmempty = true;
return handleEdit('save');
})
}, [_('Save')]),
- ])
+ ]);
});
return m.render();
},
handleSaveApply: null,
handleSave: null,
handleReset: null
-});
+});
\ No newline at end of file
return L.view.extend({
load: () => Promise.resolve(),
- render: () => {
- L.Poll.add(() => {
+ render: function () {
+ const pollFn = () => {
return callLogRead(1000, false, true).then(res => {
const logEl = document.getElementById('logfile');
if (!logEl) return;
const filtered = (res?.log ?? [])
- .filter(entry => !logtag || entry.msg.includes(logtag))
- .map(entry => {
- const d = new Date(entry.time);
- const date = d.toLocaleDateString([], {
- year: 'numeric',
- month: '2-digit',
- day: '2-digit'
+ .filter(entry => !logtag || entry.msg.includes(logtag))
+ .map(entry => {
+ const d = new Date(entry.time * 1000);
+ const date = d.toLocaleDateString([], {
+ year: 'numeric',
+ month: '2-digit',
+ day: '2-digit'
+ });
+ const time = d.toLocaleTimeString([], {
+ hour: '2-digit',
+ minute: '2-digit',
+ second: '2-digit',
+ hour12: false
+ });
+ return `[${date}-${time}] ${entry.msg}`;
});
- const time = d.toLocaleTimeString([], {
- hour: '2-digit',
- minute: '2-digit',
- second: '2-digit',
- hour12: false
- });
- return `[${date}-${time}] ${entry.msg}`;
- });
- if (filtered.length > 0) {
- logEl.value = filtered.join('\n');
- } else {
- logEl.value = _('No %s related logs yet!').format(name);
- }
+
+ logEl.value = filtered.length > 0
+ ? filtered.join('\n')
+ : _('No %s related logs yet!').format(name);
logEl.scrollTop = logEl.scrollHeight;
});
- });
+ };
+
+ this._pollFn = pollFn;
+ L.Poll.add(pollFn);
return E('div', { class: 'cbi-map' }, [
E('div', { class: 'cbi-section' }, [
]);
},
+ unload: function () {
+ if (this._pollFn) {
+ L.Poll.remove(this._pollFn);
+ this._pollFn = null;
+ }
+ },
+
handleSaveApply: null,
handleSave: null,
handleReset: null
});
}
-return L.Class.extend({ Logview });
+return L.Class.extend({ Logview });
\ No newline at end of file
btn.blur();
});
return fs.exec_direct('/etc/init.d/adblock', [ev]);
- })
+ });
} else {
if (ev !== 'stop') {
document.querySelectorAll('.cbi-page-actions button').forEach(function (btn) {
`https://${window.location.hostname}/cgi-bin/adblock`
]);
},
+
render: function (result) {
/*
config check
/*
poll runtime information
*/
+ let parseErrCount = 0;
poll.add(function () {
return L.resolveDefault(fs.stat('/var/run/adb_runtime.json'), null).then(function (stat) {
if (!stat) {
let info = null;
try {
info = JSON.parse(res);
+ parseErrCount = 0;
} catch (e) {
info = null;
+ parseErrCount++;
if (status) {
status.textContent = '-';
- if (status.classList.contains('spinning')) {
- buttons.forEach(function (btn) {
- btn.disabled = false;
- })
- status.classList.remove('spinning');
+ buttons.forEach(function (btn) {
+ btn.disabled = false;
+ });
+ status.classList.remove('spinning');
+ if (parseErrCount >= 3) {
ui.addNotification(null, E('p', _('Unable to parse the adblock runtime information!')), 'error');
poll.stop();
}
buttons.forEach(function (btn) {
btn.disabled = true;
btn.blur();
- })
+ });
if (!status.classList.contains("spinning")) {
status.classList.add("spinning");
}
}
buttons.forEach(function (btn) {
btn.disabled = false;
- })
+ });
}
}
if (info) {
]),
E('div', { 'class': 'cbi-value' }, [
E('label', { 'class': 'cbi-value-title', 'style': 'margin-bottom:-5px;padding-top:0rem;' }, _('Last Run')),
- E('div', { 'class': 'cbi-value-field', 'id': 'last', 'style': 'margin-bottom:- 5px; color:#37c; ' }, ' - ')
+ E('div', { 'class': 'cbi-value-field', 'id': 'last', 'style': 'margin-bottom:-5px;color:#37c;' }, '-')
]),
E('div', { 'class': 'cbi-value' }, [
E('label', { 'class': 'cbi-value-title', 'style': 'margin-bottom:-5px;padding-top:0rem;' }, _('System Info')),
o.rmempty = true;
o = s.taboption('general', form.Flag, 'adb_tld', _('TLD Compression'), _('The top level domain compression removes thousands of needless host entries from the final DNS blocklist.'));
- o.default = 1
+ o.default = 1;
o.rmempty = true;
o = s.taboption('general', form.Flag, 'adb_safesearch', _('Enable SafeSearch'), _('Enforcing SafeSearch for google, bing, brave, duckduckgo, yandex, youtube and pixabay.'));
o.rmempty = true;
o = s.taboption('additional', form.Flag, 'adb_fetchinsecure', _('Download Insecure'), _('Don\'t check SSL server certificates during download.'));
- o.default = 0
+ o.default = 0;
o.rmempty = true;
/*
firewall settings tab
*/
- o = s.taboption('firewall', form.DummyValue, '_sub');
+ o = s.taboption('firewall', form.DummyValue, '_fw_sub1');
o.rawhtml = true;
o.default = '<em style="color:#37c;font-weight:bold;">' + _('Changes on this tab needs an adblock service restart to take effect.') + '</em>'
+ '<hr style="width: 200px; height: 1px;" />'
o.default = '2a13:1001::86:54:11:100';
o.rmempty = true;
- o = s.taboption('firewall', form.DummyValue, '_sub');
+ o = s.taboption('firewall', form.DummyValue, '_fw_sub2');
o.rawhtml = true;
o.default = '<hr style="width: 200px; height: 1px;" /><em style="color:#37c;font-weight:bold;">' + _('External Filtered DNS Policy (MAC-/Interface‑based DNS bypass)') + '</em>';
o.default = '2a13:1001::86:54:11:13';
o.rmempty = true;
- o = s.taboption('firewall', form.DummyValue, '_sub');
+ o = s.taboption('firewall', form.DummyValue, '_fw_sub3');
o.rawhtml = true;
o.default = '<hr style="width: 200px; height: 1px;" /><em style="color:#37c;font-weight:bold;">' + _('External Remote DNS Policy (temporary MAC‑based remote DNS bypass)') + '</em>';
blackColor: 'black'
};
const svg = uqr.renderSVG(url, options);
- o = s.taboption('firewall', form.DummyValue, '_sub', _('QRCode for Remote Access'));
+ o = s.taboption('firewall', form.DummyValue, '_fw_qr', _('QRCode for Remote Access'));
o.rawhtml = true;
o.default = svg;
}
- o = s.taboption('firewall', form.DummyValue, '_sub');
+ o = s.taboption('firewall', form.DummyValue, '_fw_sub4');
o.rawhtml = true;
o.default = '<hr style="width: 200px; height: 1px;" /><em style="color:#37c;font-weight:bold;">' + _('External DNS Bridge (Zero‑Downtime during DNS Restarts)') + '</em>';
o.default = '2a13:1001::86:54:11:13';
o.rmempty = true;
- o = s.taboption('firewall', form.DummyValue, '_sub');
+ o = s.taboption('firewall', form.DummyValue, '_fw_sub5');
o.rawhtml = true;
o.default = '<hr style="width: 200px; height: 1px;" /><em style="color:#37c;font-weight:bold;">' + _('Local DNS Enforcement') + '</em>';
/*
prepare category data
*/
- var code, category, list, path, categories = [];
- if (result[2]) {
- categories = result[2].trim().split('\n');
- }
+ const categories = result[2] ? result[2].trim().split('\n') : [];
- o = s.taboption('feeds', form.DummyValue, '_sub');
+ o = s.taboption('feeds', form.DummyValue, '_feeds1');
o.rawhtml = true;
o.default = '<hr style="width: 200px; height: 1px;" /><em style="color:#37c;font-weight:bold;">' + _('1Hosts List Selection') + '</em>';
for (let i = 0; i < categories.length; i++) {
const cat = categories[i].match(/^(\w+);(.*?);(.*)$/);
if (!cat) continue;
-
- const code = cat[1].trim();
- const list = cat[2].trim();
- const path = cat[3].trim();
-
- if (code === 'hst') {
- o.value(path, list);
+ if (cat[1].trim() === 'hst') {
+ o.value(cat[3].trim(), cat[2].trim());
}
}
o.optional = true;
o.rmempty = true;
- o = s.taboption('feeds', form.DummyValue, '_sub');
+ o = s.taboption('feeds', form.DummyValue, '_feeds2');
o.rawhtml = true;
o.default = '<hr style="width: 200px; height: 1px;" /><em style="color:#37c;font-weight:bold;">' + _('Hagezi List Selection') + '</em>';
for (let i = 0; i < categories.length; i++) {
const cat = categories[i].match(/^(\w+);(.*?);(.*)$/);
if (!cat) continue;
-
- const code = cat[1].trim();
- const list = cat[2].trim();
- const path = cat[3].trim();
-
- if (code === 'hag') {
- o.value(path, list);
+ if (cat[1].trim() === 'hag') {
+ o.value(cat[3].trim(), cat[2].trim());
}
}
o.optional = true;
o.rmempty = true;
- o = s.taboption('feeds', form.DummyValue, '_sub');
+ o = s.taboption('feeds', form.DummyValue, '_feeds3');
o.rawhtml = true;
o.default = '<hr style="width: 200px; height: 1px;" /><em style="color:#37c;font-weight:bold;">' + _('IPFire List Selection') + '</em>';
for (let i = 0; i < categories.length; i++) {
const cat = categories[i].match(/^(\w+);(.*?);(.*)$/);
if (!cat) continue;
-
- const code = cat[1].trim();
- const list = cat[2].trim();
- const path = cat[3].trim();
-
- if (code === 'ipf') {
- o.value(path, list);
+ if (cat[1].trim() === 'ipf') {
+ o.value(cat[3].trim(), cat[2].trim());
}
}
o.optional = true;
o.rmempty = true;
- o = s.taboption('feeds', form.DummyValue, '_sub');
+ o = s.taboption('feeds', form.DummyValue, '_feeds4');
o.rawhtml = true;
o.default = '<hr style="width: 200px; height: 1px;" /><em style="color:#37c;font-weight:bold;">' + _('StevenBlack List Selection') + '</em>';
for (let i = 0; i < categories.length; i++) {
const cat = categories[i].match(/^(\w+);(.*?);(.*)$/);
if (!cat) continue;
-
- const code = cat[1].trim();
- const list = cat[2].trim();
- const path = cat[3].trim();
-
- if (code === 'stb') {
- o.value(path, list);
+ if (cat[1].trim() === 'stb') {
+ o.value(cat[3].trim(), cat[2].trim());
}
}
o.optional = true;
o.rmempty = true;
- o = s.taboption('feeds', form.DummyValue, '_sub');
+ o = s.taboption('feeds', form.DummyValue, '_feeds5');
o.rawhtml = true;
o.default = '<hr style="width: 200px; height: 1px;" /><em style="color:#37c;font-weight:bold;">' + _('UTCapitole Archive Selection') + '</em>';
for (let i = 0; i < categories.length; i++) {
const cat = categories[i].match(/^(\w+);(.*)$/);
if (!cat) continue;
-
- const code = cat[1].trim();
- const category = cat[2].trim();
-
- if (code === 'utc') {
- o.value(category);
+ if (cat[1].trim() === 'utc') {
+ o.value(cat[2].trim());
}
}
o.optional = true;
'style': 'float:none',
'title': 'Save & Restart',
'click': function () {
- handleAction('restart');
+ return handleAction('restart');
}
}, [_('Save & Restart')])
- ])
+ ]);
});
return m.render();
},
handleSaveApply: null,
handleSave: null,
handleReset: null
-});
+});
\ No newline at end of file