mirror of
https://github.com/pi-hole/web.git
synced 2025-12-24 12:48:29 +00:00
Use proper Object methods (#3441)
This commit is contained in:
@@ -76,16 +76,16 @@ function parseLines(ta, str) {
|
||||
// Splitting the text on "\r"
|
||||
const lines = str.split(/(?=\r)/g);
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
if (lines[i][0] === "\r") {
|
||||
for (let line of lines) {
|
||||
if (line[0] === "\r") {
|
||||
// This line starts with the "OVER" sequence. Replace them with "\n" before print
|
||||
lines[i] = lines[i].replaceAll("\r[K", "\n").replaceAll("\r", "\n");
|
||||
line = line.replaceAll("\r[K", "\n").replaceAll("\r", "\n");
|
||||
|
||||
// Last line from the textarea will be overwritten, so we remove it
|
||||
ta.text(ta.text().substring(0, ta.text().lastIndexOf("\n")));
|
||||
}
|
||||
|
||||
// Append the new text to the end of the output
|
||||
ta.append(lines[i]);
|
||||
ta.append(line);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,8 +29,7 @@ function reloadClientSuggestions() {
|
||||
sel.append($("<option />"));
|
||||
|
||||
// Add data obtained from API
|
||||
for (let i = 0; i < data.clients.length; i++) {
|
||||
const client = data.clients[i];
|
||||
for (const client of data.clients) {
|
||||
let mockDevice = false;
|
||||
let text = client.hwaddr.toUpperCase();
|
||||
let key = text;
|
||||
@@ -370,15 +369,11 @@ function addClient() {
|
||||
// - IPv6 address (with and without CIDR)
|
||||
// - MAC address (in the form AA:BB:CC:DD:EE:FF)
|
||||
// - host name (arbitrary form, we're only checking against some reserved characters)
|
||||
for (let i = 0; i < ips.length; i++) {
|
||||
if (
|
||||
utils.validateIPv4CIDR(ips[i]) ||
|
||||
utils.validateIPv6CIDR(ips[i]) ||
|
||||
utils.validateMAC(ips[i])
|
||||
) {
|
||||
for (const [index, ip] of ips.entries()) {
|
||||
if (utils.validateIPv4CIDR(ip) || utils.validateIPv6CIDR(ip) || utils.validateMAC(ip)) {
|
||||
// Convert input to upper case (important for MAC addresses)
|
||||
ips[i] = ips[i].toUpperCase();
|
||||
} else if (!utils.validateHostname(ips[i])) {
|
||||
ips[index] = ip.toUpperCase();
|
||||
} else if (!utils.validateHostname(ip)) {
|
||||
utils.showAlert(
|
||||
"warning",
|
||||
"",
|
||||
|
||||
@@ -503,12 +503,12 @@ function addDomain() {
|
||||
|
||||
// Check if the wildcard checkbox was marked and transform the domains into regex
|
||||
if (kind === "exact" && wildcardChecked) {
|
||||
for (let i = 0; i < domains.length; i++) {
|
||||
for (const [index, domain] of domains.entries()) {
|
||||
// Strip leading "*." if specified by user in wildcard mode
|
||||
if (domains[i].startsWith("*.")) domains[i] = domains[i].substr(2);
|
||||
if (domain.startsWith("*.")) domains[index] = domain.substr(2);
|
||||
|
||||
// Transform domain into a wildcard regex
|
||||
domains[i] = "(\\.|^)" + domains[i].replaceAll(".", "\\.") + "$";
|
||||
domains[index] = "(\\.|^)" + domains[index].replaceAll(".", "\\.") + "$";
|
||||
}
|
||||
|
||||
kind = "regex";
|
||||
|
||||
@@ -120,14 +120,14 @@ function updateQueryTypesPie() {
|
||||
let sum = 0;
|
||||
|
||||
// Compute total number of queries
|
||||
for (const item of Object.keys(data.types)) {
|
||||
sum += data.types[item];
|
||||
for (const value of Object.values(data.types)) {
|
||||
sum += value;
|
||||
}
|
||||
|
||||
// Fill chart with data (only include query types which appeared recently)
|
||||
for (const item of Object.keys(data.types)) {
|
||||
if (data.types[item] > 0) {
|
||||
v.push((100 * data.types[item]) / sum);
|
||||
for (const [item, value] of Object.entries(data.types)) {
|
||||
if (value > 0) {
|
||||
v.push((100 * value) / sum);
|
||||
c.push(THEME_COLORS[i % THEME_COLORS.length]);
|
||||
k.push(item);
|
||||
}
|
||||
@@ -165,9 +165,9 @@ function updateClientsOverTime() {
|
||||
let numClients = 0;
|
||||
const labels = [];
|
||||
const clients = {};
|
||||
for (const ip of Object.keys(data.clients)) {
|
||||
for (const [ip, clientData] of Object.entries(data.clients)) {
|
||||
clients[ip] = numClients++;
|
||||
labels.push(data.clients[ip].name !== null ? data.clients[ip].name : ip);
|
||||
labels.push(clientData.name !== null ? clientData.name : ip);
|
||||
}
|
||||
|
||||
// Remove possibly already existing data
|
||||
@@ -192,15 +192,11 @@ function updateClientsOverTime() {
|
||||
|
||||
// Add data for each dataset that is available
|
||||
// We need to iterate over all time slots and fill in the data for each client
|
||||
for (const item of Object.keys(data.history)) {
|
||||
for (const client of Object.keys(clients)) {
|
||||
if (data.history[item].data[client] === undefined) {
|
||||
// If there is no data for this client in this timeslot, we push 0
|
||||
clientsChart.data.datasets[clients[client]].data.push(0);
|
||||
} else {
|
||||
// Otherwise, we push the data
|
||||
clientsChart.data.datasets[clients[client]].data.push(data.history[item].data[client]);
|
||||
}
|
||||
for (const item of Object.values(data.history)) {
|
||||
for (const [client, index] of Object.entries(clients)) {
|
||||
const clientData = item.data[client];
|
||||
// If there is no data for this client in this timeslot, we push 0, otherwise the data
|
||||
clientsChart.data.datasets[index].data.push(clientData === undefined ? 0 : clientData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -525,10 +521,10 @@ function labelWithPercentage(tooltipLabel, skipZero = false) {
|
||||
// Sum all queries for the current time by iterating over all keys in the
|
||||
// current dataset
|
||||
let sum = 0;
|
||||
const keys = Object.keys(tooltipLabel.parsed._stacks.y);
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
if (tooltipLabel.parsed._stacks.y[i] === undefined) continue;
|
||||
sum += Number.parseInt(tooltipLabel.parsed._stacks.y[i], 10);
|
||||
for (const value of Object.values(tooltipLabel.parsed._stacks.y)) {
|
||||
if (value === undefined) continue;
|
||||
const num = Number.parseInt(value, 10);
|
||||
if (num) sum += num;
|
||||
}
|
||||
|
||||
let percentage = 0;
|
||||
|
||||
@@ -96,7 +96,6 @@ $(() => {
|
||||
tableApi = $("#network-entries").DataTable({
|
||||
rowCallback(row, data) {
|
||||
let color;
|
||||
let index;
|
||||
let iconClasses;
|
||||
const lastQuery = Number.parseInt(data.lastQuery, 10);
|
||||
const diff = getTimestamp() - lastQuery;
|
||||
@@ -153,19 +152,18 @@ $(() => {
|
||||
return a.ip.localeCompare(b.ip);
|
||||
});
|
||||
|
||||
for (index = 0; index < data.ips.length; index++) {
|
||||
const ip = data.ips[index];
|
||||
let iptext = ip.ip;
|
||||
for (const { ip, name } of data.ips) {
|
||||
let iptext = ip;
|
||||
|
||||
if (ip.name !== null && ip.name.length > 0) {
|
||||
iptext = iptext + " (" + ip.name + ")";
|
||||
if (name !== null && name.length > 0) {
|
||||
iptext = `${iptext} (${name})`;
|
||||
}
|
||||
|
||||
iptitles.push(iptext);
|
||||
|
||||
// Only add IPs to the table if we have not reached the maximum
|
||||
if (index < MAXIPDISPLAY) {
|
||||
ips.push('<a href="queries?client_ip=' + ip.ip + '">' + iptext + "</a>");
|
||||
if (ips.length < MAXIPDISPLAY) {
|
||||
ips.push(`<a href="queries?client_ip=${ip}">${iptext}</a>`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -415,12 +415,7 @@ function addSelectSuggestion(name, dict, data) {
|
||||
}
|
||||
|
||||
// Add data obtained from API
|
||||
for (const key in data) {
|
||||
if (!Object.hasOwn(data, key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const text = data[key];
|
||||
for (const text of Object.values(data)) {
|
||||
obj.append($("<option />").val(text).text(text));
|
||||
}
|
||||
|
||||
@@ -434,11 +429,8 @@ function getSuggestions(dict) {
|
||||
$.get(
|
||||
document.body.dataset.apiurl + "/queries/suggestions",
|
||||
data => {
|
||||
for (const key in filters) {
|
||||
if (Object.hasOwn(filters, key)) {
|
||||
const f = filters[key];
|
||||
addSelectSuggestion(f, dict, data.suggestions[f]);
|
||||
}
|
||||
for (const filter of Object.values(filters)) {
|
||||
addSelectSuggestion(filter, dict, data.suggestions[filter]);
|
||||
}
|
||||
},
|
||||
"json"
|
||||
@@ -446,15 +438,7 @@ function getSuggestions(dict) {
|
||||
}
|
||||
|
||||
function parseFilters() {
|
||||
const filter = {};
|
||||
for (const key in filters) {
|
||||
if (Object.hasOwn(filters, key)) {
|
||||
const f = filters[key];
|
||||
filter[f] = $("#" + f + "_filter").val();
|
||||
}
|
||||
}
|
||||
|
||||
return filter;
|
||||
return Object.fromEntries(filters.map(filter => [filter, $(`#${filter}_filter`).val()]));
|
||||
}
|
||||
|
||||
function filterOn(param, dict) {
|
||||
@@ -464,13 +448,10 @@ function filterOn(param, dict) {
|
||||
|
||||
function getAPIURL(filters) {
|
||||
let apiurl = document.body.dataset.apiurl + "/queries?";
|
||||
for (const key in filters) {
|
||||
if (Object.hasOwn(filters, key)) {
|
||||
const filter = filters[key];
|
||||
if (filterOn(key, filters)) {
|
||||
if (!apiurl.endsWith("?")) apiurl += "&";
|
||||
apiurl += key + "=" + encodeURIComponent(filter);
|
||||
}
|
||||
for (const [key, filter] of Object.entries(filters)) {
|
||||
if (filterOn(key, filters)) {
|
||||
if (!apiurl.endsWith("?")) apiurl += "&";
|
||||
apiurl += `${key}=${encodeURIComponent(filter)}`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -502,16 +483,13 @@ $(() => {
|
||||
// Do we want to filter queries?
|
||||
const GETDict = utils.parseQueryString();
|
||||
|
||||
for (const sel in filters) {
|
||||
if (Object.hasOwn(filters, sel)) {
|
||||
const element = filters[sel];
|
||||
$("#" + element + "_filter").select2({
|
||||
width: "100%",
|
||||
tags: sel < 4, // Only the first four (client(IP/name), domain, upstream) are allowed to freely specify input
|
||||
placeholder: "Select...",
|
||||
allowClear: true,
|
||||
});
|
||||
}
|
||||
for (const [sel, element] of Object.entries(filters)) {
|
||||
$(`#${element}_filter`).select2({
|
||||
width: "100%",
|
||||
tags: sel < 4, // Only the first four (client(IP/name), domain, upstream) are allowed to freely specify input
|
||||
placeholder: "Select...",
|
||||
allowClear: true,
|
||||
});
|
||||
}
|
||||
|
||||
getSuggestions(GETDict);
|
||||
|
||||
@@ -264,8 +264,7 @@ function valueDetails(key, value) {
|
||||
function generateRow(topic, key, value) {
|
||||
// If the value is an object, we need to recurse
|
||||
if (!("description" in value)) {
|
||||
for (const subkey of Object.keys(value)) {
|
||||
const subvalue = value[subkey];
|
||||
for (const [subkey, subvalue] of Object.entries(value)) {
|
||||
generateRow(topic, key + "." + subkey, subvalue);
|
||||
}
|
||||
|
||||
@@ -303,9 +302,7 @@ function createDynamicConfigTabs() {
|
||||
})
|
||||
.done(data => {
|
||||
// Create the tabs for the advanced dynamic config topics
|
||||
for (const n of Object.keys(data.topics)) {
|
||||
const topic = data.topics[n];
|
||||
|
||||
for (const topic of Object.values(data.topics)) {
|
||||
$("#advanced-settings-tabs").append(`
|
||||
<div id="advanced-content-${topic.name}" role="tabpanel" class="tab-pane fade">
|
||||
<h3 class="page-header">${topic.description} (<code>${topic.name}</code>)</h3>
|
||||
@@ -324,8 +321,7 @@ function createDynamicConfigTabs() {
|
||||
}
|
||||
|
||||
// Dynamically fill the tabs with config topics
|
||||
for (const topic of Object.keys(data.config)) {
|
||||
const value = data.config[topic];
|
||||
for (const [topic, value] of Object.entries(data.config)) {
|
||||
generateRow(topic, topic, value);
|
||||
}
|
||||
|
||||
|
||||
@@ -60,16 +60,16 @@ function updateCachePie(data) {
|
||||
data.empty.valid = cacheSize - cacheEntries;
|
||||
|
||||
// Fill chart with data
|
||||
for (const item of Object.keys(data)) {
|
||||
if (data[item].valid > 0) {
|
||||
v.push((100 * data[item].valid) / cacheSize);
|
||||
for (const [item, value] of Object.entries(data)) {
|
||||
if (value.valid > 0) {
|
||||
v.push((100 * value.valid) / cacheSize);
|
||||
c.push(item !== "empty" ? THEME_COLORS[i++ % THEME_COLORS.length] : "#80808040");
|
||||
k.push(item);
|
||||
}
|
||||
|
||||
if (data[item].stale > 0) {
|
||||
if (value.stale > 0) {
|
||||
// There are no stale empty entries
|
||||
v.push((100 * data[item].stale) / cacheSize);
|
||||
v.push((100 * value.stale) / cacheSize);
|
||||
c.push(THEME_COLORS[i++ % THEME_COLORS.length]);
|
||||
k.push(item + " (stale)");
|
||||
}
|
||||
|
||||
@@ -59,8 +59,8 @@ function importZIP() {
|
||||
$("#modal-import-success").show();
|
||||
$("#modal-import-success-title").text("Import successful");
|
||||
let text = "<p>Processed files:</p><ul>";
|
||||
for (let i = 0; i < data.files.length; i++) {
|
||||
text += "<li>" + utils.escapeHtml(data.files[i]) + "</li>";
|
||||
for (const file of data.files) {
|
||||
text += "<li>" + utils.escapeHtml(file) + "</li>";
|
||||
}
|
||||
|
||||
text += "</ul>";
|
||||
|
||||
@@ -28,8 +28,7 @@ $(() => {
|
||||
function setConfigValues(topic, key, value) {
|
||||
// If the value is an object, we need to recurse
|
||||
if (!("description" in value)) {
|
||||
for (const subkey of Object.keys(value)) {
|
||||
const subvalue = value[subkey];
|
||||
for (const [subkey, subvalue] of Object.entries(value)) {
|
||||
// If the key is empty, we are at the top level
|
||||
const newKey = key === "" ? subkey : key + "." + subkey;
|
||||
setConfigValues(topic, newKey, subvalue);
|
||||
|
||||
@@ -274,9 +274,8 @@ function stateLoadCallback(itemName) {
|
||||
data = JSON.parse(data);
|
||||
|
||||
// Clear possible filtering settings
|
||||
// TODO Maybe Object.values() is meant to be used here?
|
||||
for (const [index, _value] of data.columns.entries()) {
|
||||
data.columns[index].search.search = "";
|
||||
for (const column of Object.values(data.columns)) {
|
||||
column.search.search = "";
|
||||
}
|
||||
|
||||
// Always start on the first page to show most recent queries
|
||||
@@ -520,12 +519,9 @@ function parseQueryString() {
|
||||
|
||||
// https://stackoverflow.com/q/21647928
|
||||
function hexEncode(string) {
|
||||
let hex;
|
||||
let i;
|
||||
|
||||
let result = "";
|
||||
for (i = 0; i < string.length; i++) {
|
||||
hex = string.codePointAt(i).toString(16);
|
||||
for (let i = 0; i < string.length; i++) {
|
||||
const hex = string.codePointAt(i).toString(16);
|
||||
result += ("000" + hex).slice(-4);
|
||||
}
|
||||
|
||||
@@ -534,11 +530,10 @@ function hexEncode(string) {
|
||||
|
||||
// https://stackoverflow.com/q/21647928
|
||||
function hexDecode(string) {
|
||||
let j;
|
||||
const hexes = string.match(/.{1,4}/g) || [];
|
||||
let back = "";
|
||||
for (j = 0; j < hexes.length; j++) {
|
||||
back += String.fromCodePoint(Number.parseInt(hexes[j], 16));
|
||||
for (const hex of hexes) {
|
||||
back += String.fromCodePoint(Number.parseInt(hex, 16));
|
||||
}
|
||||
|
||||
return back;
|
||||
@@ -547,7 +542,10 @@ function hexDecode(string) {
|
||||
function listsAlert(type, items, data) {
|
||||
// Show simple success message if there is no "processed" object in "data" or
|
||||
// if all items were processed successfully
|
||||
if (data.processed === undefined || data.processed.success.length === items.length) {
|
||||
const successLength = data.processed.success.length;
|
||||
const errorsLength = data.processed.errors.length;
|
||||
|
||||
if (data.processed === undefined || successLength === items.length) {
|
||||
showAlert(
|
||||
"success",
|
||||
"fas fa-plus",
|
||||
@@ -562,54 +560,40 @@ function listsAlert(type, items, data) {
|
||||
let message = "";
|
||||
|
||||
// Show a list of successful items if there are any
|
||||
if (data.processed.success.length > 0) {
|
||||
if (successLength > 0) {
|
||||
message +=
|
||||
"Successfully added " +
|
||||
data.processed.success.length +
|
||||
" " +
|
||||
type +
|
||||
(data.processed.success.length !== 1 ? "s" : "") +
|
||||
":";
|
||||
"Successfully added " + successLength + " " + type + (successLength !== 1 ? "s" : "") + ":";
|
||||
|
||||
// Loop over data.processed.success and print "item"
|
||||
for (const item in data.processed.success) {
|
||||
if (Object.hasOwn(data.processed.success, item)) {
|
||||
message += "\n- " + data.processed.success[item].item;
|
||||
}
|
||||
for (const item of Object.values(data.processed.success)) {
|
||||
message += "\n- " + item.item;
|
||||
}
|
||||
}
|
||||
|
||||
// Add a line break if there are both successful and failed items
|
||||
if (data.processed.success.length > 0 && data.processed.errors.length > 0) {
|
||||
if (successLength > 0 && errorsLength > 0) {
|
||||
message += "\n\n";
|
||||
}
|
||||
|
||||
// Show a list of failed items if there are any
|
||||
if (data.processed.errors.length > 0) {
|
||||
if (errorsLength > 0) {
|
||||
message +=
|
||||
"Failed to add " +
|
||||
data.processed.errors.length +
|
||||
" " +
|
||||
type +
|
||||
(data.processed.errors.length !== 1 ? "s" : "") +
|
||||
":\n";
|
||||
"Failed to add " + errorsLength + " " + type + (errorsLength !== 1 ? "s" : "") + ":\n";
|
||||
|
||||
// Loop over data.processed.errors and print "item: error"
|
||||
for (const item in data.processed.errors) {
|
||||
if (Object.hasOwn(data.processed.errors, item)) {
|
||||
let error = data.processed.errors[item].error;
|
||||
// Replace some error messages with a more user-friendly text
|
||||
if (error.includes("UNIQUE constraint failed")) {
|
||||
error = "Already present";
|
||||
}
|
||||
|
||||
message += "\n- " + data.processed.errors[item].item + ": " + error;
|
||||
for (const errorItem of Object.values(data.processed.errors)) {
|
||||
let error = errorItem.error;
|
||||
// Replace some error messages with a more user-friendly text
|
||||
if (error.includes("UNIQUE constraint failed")) {
|
||||
error = "Already present";
|
||||
}
|
||||
|
||||
message += `\n- ${errorItem.item}: ${error}`;
|
||||
}
|
||||
}
|
||||
|
||||
// Show the warning message
|
||||
const total = data.processed.success.length + data.processed.errors.length;
|
||||
const total = successLength + errorsLength;
|
||||
const processed = "(" + total + " " + type + (total !== 1 ? "s" : "") + " processed)";
|
||||
showAlert(
|
||||
"warning",
|
||||
|
||||
Reference in New Issue
Block a user