diff --git a/intelmq_manager/static/js/configs.js b/intelmq_manager/static/js/configs.js index 6f95f10..f1a4c1c 100644 --- a/intelmq_manager/static/js/configs.js +++ b/intelmq_manager/static/js/configs.js @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2020 IntelMQ Team , 2020 Edvard Rejthar , 2021 Mikk Margus Möll // // SPDX-License-Identifier: AGPL-3.0-or-later +'use strict'; var NETWORK_OPTIONS = NETWORK_OPTIONS || {}; @@ -47,9 +48,11 @@ var BORDER_TYPES = { var draggedElement = null; +var warn_on_close_tab = false; + $(window).on('hashchange', location.reload); -$(window).on('unload', () => "If you have not saved your work you'll loose the changes you have made. Do you want to continue?"); +$(window).on('beforeunload', () => warn_on_close_tab ? "If you have not saved your work you'll lose the changes you have made. Do you want to continue?" : undefined); function resize() { // Resize body @@ -154,8 +157,8 @@ function load_bots(config) { } $('#side-menu').metisMenu({restart: true}); - $EDIT_DEFAULT_BUTTON.click(function () { - create_form('Edit Defaults', $(this).attr("id"), undefined); + $EDIT_DEFAULT_BUTTON.click(e => { + create_form('Edit Defaults', $(e.target).attr("id"), undefined); fill_editDefault(app.defaults); }); @@ -164,14 +167,14 @@ function load_bots(config) { } else { draw(); resize(); - $saveButton.blinking(); + set_pending_change(); } } function fill_editDefault(data) { table.innerHTML = ''; insertBorder(BORDER_TYPES.DEFAULT); - for (let [key, value] in Object.entries(data)) { + for (let [key, value] of Object.entries(data)) { insertKeyValue(key, value, BORDER_TYPES.DEFAULT, true); } @@ -246,7 +249,7 @@ function save_data_on_files() { .fail(() => alert_error('positions', ...arguments) ) ) // all files were correctly saved - .then(() => $saveButton.unblinking()); + .then(unset_pending_change); } @@ -301,7 +304,7 @@ function convert_nodes(nodes, includePositions) { new_node.x = x; new_node.y = y; } catch (err) { - console.error('positions in file are ignored:', err, index); + console.error('positions in file are ignored:', err, node); show_error('Saved positions are not valid or not complete. The configuration has possibly been modified outside of the IntelMQ-Manager.'); includePositions = false; } @@ -314,7 +317,7 @@ function convert_nodes(nodes, includePositions) { } function fill_bot(id, group, name) { - let bot = {}; + let bot; table.innerHTML = ''; if (id === undefined) { @@ -322,7 +325,7 @@ function fill_bot(id, group, name) { name = bot.name.replace(/\ /g, '-').replace(/[^A-Za-z0-9-]/g, ''); group = bot.group.replace(/\ /g, '-'); - default_id = gen_new_id(`${name}-${group}`); + let default_id = gen_new_id(`${name}-${group}`); bot.bot_id = bot.id = default_id; bot.defaults = {}; @@ -381,13 +384,7 @@ function insertBorder(border_type) { } function insertKeyValue(key, value, section, allowXButtons, insertAt) { - let new_row = null; - - if (insertAt === undefined) { - new_row = table.insertRow(-1); - } else { - new_row = table.insertRow(insertAt); - } + let new_row = table.insertRow(insertAt === undefined ? -1 : insertAt); let keyCell = new_row.insertCell(0); let valueCell = new_row.insertCell(1); @@ -480,6 +477,7 @@ function addNewDefaultKey() { $(document).keydown(function (event) { if (event.keyCode === 27) { + let $el; if (($el = $("body > .modal:not([data-hiding])")).length) { // close the most recent modal $el.last().attr("data-hiding", true).modal('hide'); @@ -498,7 +496,7 @@ $(document).keydown(function (event) { function saveDefaults_tmp(data, callback) { app.defaults = {}; saveFormData(); - $saveButton.blinking(); + set_pending_change(); clearPopUp(data, callback); } @@ -558,57 +556,65 @@ function saveData(data, callback) { } let current_id = node.bot_id, old_id = app.bot_before_altering.bot_id; - if (current_id !== old_id && old_id in app.nodes) { - if (!confirm("You have edited the bot's ID. Proceed with the operation?")) { + + let old_bot = app.nodes[old_id]; + node.parameters.destination_queues = old_bot.parameters.destination_queues; + + if (current_id !== old_id) { + if (current_id in app.nodes) { + alert("A bot with this ID already exists, please select a different ID"); return; } - let old_bot = app.nodes[old_id]; - node.parameters.destination_queues = old_bot.parameters.destination_queues; + if (old_id in app.nodes) { + if (!confirm("You have edited the bot's ID. Proceed with the operation?")) { + return; + } - app.positions[current_id] = app.positions[old_id]; - app.nodes[current_id] = node; - delete app.positions[old_id]; + app.positions[current_id] = app.positions[old_id]; + app.nodes[current_id] = node; + delete app.positions[old_id]; - app.network_data.nodes.add(convert_nodes([node], true)); + app.network_data.nodes.add(convert_nodes([node], true)); - // recreate reverse edges - for (let edge_id of get_reverse_edges(old_id)) { - let [from, to, path] = from_edge_id(edge_id); - let list = app.nodes[from].parameters.destination_queues[path]; - let to_index = list.indexOf(`${old_id}-queue`); + // recreate reverse edges + for (let edge_id of get_reverse_edges(old_id)) { + let [from, to, path] = from_edge_id(edge_id); + let list = app.nodes[from].parameters.destination_queues[path]; + let to_index = list.indexOf(`${old_id}-queue`); - list[to_index] = `${current_id}-queue`; + list[to_index] = `${current_id}-queue`; - let new_edge_id = to_edge_id(from, current_id, path); - if (path === '_default') { - path = undefined; - } + let new_edge_id = to_edge_id(from, current_id, path); + if (path === '_default') { + path = undefined; + } - app.network_data.edges.remove({id: edge_id}); - app.network_data.edges.add({id: new_edge_id, from, to: current_id, label: path}); - } + app.network_data.edges.remove({id: edge_id}); + app.network_data.edges.add({id: new_edge_id, from, to: current_id, label: path}); + } - // recreate forward edges - for (let [path, path_l] of Object.entries(node.parameters.destination_queues)) { - for (let to of path_l) { - app.network_data.edges.add({ - id: to_edge_id(current_id, to, path), - from: current_id, - to: to.replace(/-queue$/, ''), - label: path === '_default' ? undefined : path - }); + // recreate forward edges + for (let [path, path_l] of Object.entries(node.parameters.destination_queues)) { + for (let to of path_l) { + app.network_data.edges.add({ + id: to_edge_id(current_id, to, path), + from: current_id, + to: to.replace(/-queue$/, ''), + label: path === '_default' ? undefined : path + }); + } } - } - delete app.nodes[old_id]; - app.network_data.nodes.remove(old_id); + delete app.nodes[old_id]; + app.network_data.nodes.remove(old_id); + } } // switch parameters and defaults if ('parameters' in node) { - for (parameterKey in node.parameters) { + for (let parameterKey in node.parameters) { if ( node.parameters[parameterKey] !== app.bot_before_altering.parameters[parameterKey] && parameterKey in app.defaults @@ -620,7 +626,7 @@ function saveData(data, callback) { } if ('defaults' in node) { - for (defaultsKey in node.defaults) { + for (let defaultsKey in node.defaults) { if (node.defaults[defaultsKey] !== app.defaults[defaultsKey]) { swapToParameters(node, defaultsKey); } @@ -636,7 +642,7 @@ function saveData(data, callback) { app.nodes[node.bot_id] = node; - $saveButton.blinking(); + set_pending_change(); clearPopUp(data, callback); } @@ -656,17 +662,18 @@ function swapToDefaults(node, key) { * @example popupModal("Title", $input, () => {$input.val();}) */ function popupModal(title, body, callback) { - $el = $("#templates > .modal").clone().appendTo("body"); + let $el = $("#templates > .modal").clone().appendTo("body"); $(".modal-title", $el).text(title); $(".modal-body", $el).html(body); - $el.modal({keyboard: false}).on('shown.bs.modal', function () { - if (($ee = $('input,textarea,button', $(".modal-body", this)).first())) { + $el.modal({keyboard: false}).on('shown.bs.modal', e => { + let $ee; + if (($ee = $('input,textarea,button', $(".modal-body", e.target)).first())) { $ee.focus(); } }); - return $el.on('submit', 'form', function () { + return $el.on('submit', 'form', e => { if (callback() !== false) { - $(this).closest(".modal").modal('hide'); + $(e.target).closest(".modal").modal('hide'); } return false; }); @@ -679,12 +686,12 @@ function create_form(title, data, callback) { let cancelButton = document.getElementById('network-popUp-cancel'); if (data === $EDIT_DEFAULT_BUTTON.attr("id")) { - okButton.onclick = saveDefaults_tmp.bind(this, data, callback); + okButton.onclick = saveDefaults_tmp.bind(window, data, callback); } else { - okButton.onclick = saveData.bind(this, data, callback); + okButton.onclick = saveData.bind(window, data, callback); } - cancelButton.onclick = clearPopUp.bind(this, data, callback); + cancelButton.onclick = clearPopUp.bind(window, data, callback); table.innerHTML = "

Please select one of the bots on the left

"; popup.style.display = 'block'; @@ -700,7 +707,7 @@ function clearPopUp(data, callback) { popup.style.display = 'none'; span.innerHTML = ""; - for (i = table.rows.length - 1; i >= 0; i--) { + for (let i = table.rows.length - 1; i >= 0; i--) { let position = table.rows[i].rowIndex; if (position >= CORE_FIELDS) { @@ -721,7 +728,7 @@ function redrawNetwork() { app.network.destroy(); app.network = null; initNetwork(false); - $saveButton.blinking(); + set_pending_change(); } function draw() { @@ -777,13 +784,13 @@ function initNetwork(includePositions = true) { $("#templates .network-right-menu").clone().insertAfter($manipulation); let $nc = $("#network-container"); - $(".vis-live-toggle", $nc).click(function () { - $(this).toggleClass("running", !reload_queues.running); + $(".vis-live-toggle", $nc).click(e => { + $(e.target).toggleClass("running", !reload_queues.running); reload_queues.toggle(!reload_queues.running); }).click(); let physics_running = true; - $(".vis-physics-toggle", $nc).click(function () { - $(this).toggleClass("running"); + $(".vis-physics-toggle", $nc).click(e => { + $(e.target).toggleClass("running"); app.network.setOptions({physics: (physics_running = !physics_running)}); }); @@ -792,19 +799,19 @@ function initNetwork(includePositions = true) { $saveButton.children().on('click', save_data_on_files); $saveButton.data("reloadables", []); $saveButton.blinkOnce = function() { - $(this).addClass('blinking-once'); - setTimeout(() => $(this).removeClass('blinking-once'), 2000); + $($saveButton).addClass('blinking-once'); + setTimeout(() => $($saveButton).removeClass('blinking-once'), 2000); } $saveButton.blinking = function (bot_id = null) { - $(this).addClass('vis-save-blinking') + $($saveButton).addClass('vis-save-blinking') if (bot_id) { - $(this).data("reloadables").push(bot_id); + $($saveButton).data("reloadables").push(bot_id); } }; $saveButton.unblinking = function () { - $(this).removeClass('vis-save-blinking'); + $($saveButton).removeClass('vis-save-blinking'); let promises = []; - let bots = $.unique($(this).data("reloadables")); + let bots = $.unique($($saveButton).data("reloadables")); for (let bot_id of bots) { let url = managementUrl("bot", `action=reload&id=${bot_id}`); promises.push(authenticatedGetJson(url)); @@ -819,16 +826,15 @@ function initNetwork(includePositions = true) { let allow_blinking_once = false; // Save Configuration button will not blink when a button is clicked now automatically // list of button callbacks in form ["button/settings name"] => function called when clicked receives true/false according to the clicked state - let callbacks = [["live", (val) => { - reload_queues[val ? "start" : "stop"](); - }], ["physics", (val) => { - app.network.setOptions({physics: val}); - }]]; + let callbacks = [ + ["live", val => reload_queues[val ? "start" : "stop"]()], + ["physics", val => app.network.setOptions({physics: val})], + ]; for (let [name, fn] of callbacks) { - let $el = $(`.vis-${name}-toggle`, $nc).click(function () { + let $el = $(`.vis-${name}-toggle`, $nc).click(e => { // button click will callback and blinks Save Configuration button few times fn(settings[name] = !settings[name]); - $(this).toggleClass("running", settings[name]); + $(e.target).toggleClass("running", settings[name]); if (allow_blinking_once) { $saveButton.blinkOnce(); @@ -854,7 +860,7 @@ function initNetwork(includePositions = true) { app.network.manipulation._showManipulatorToolbar = app.network.manipulation.showManipulatorToolbar; app.network.manipulation.showManipulatorToolbar = function () { // call the parent function that builds the default menu - app.network.manipulation._showManipulatorToolbar.call(this); + app.network.manipulation._showManipulatorToolbar.call(app.network.manipulation); // enable 'Edit defaults' button $EDIT_DEFAULT_BUTTON.prop('disabled', false); @@ -893,13 +899,17 @@ function initNetwork(includePositions = true) { // refresh shortcuts // (it is so hard to click on the 'Add Node' button we rather register click event) // We use 't' for 'Add bot' and 'Duplicate' because that's a common letter. - $(".vis-add .vis-label", $manipulation).attr("data-accesskey", "t").click(app.network.addNodeMode); - - $(".vis-connect .vis-label", $manipulation).attr("data-accesskey", "q").click(app.network.addEdgeMode); - $(".vis-delete .vis-label", $manipulation).attr("data-accesskey", "d").click(app.network.deleteSelected); + let shortcuts = [ + ['t', 'add', 'addNodeMode'], + ['q', 'connect', 'addEdgeMode'], + ['d', 'delete', 'deleteSelected'], + ['e', 'edit', 'editNode'], + ]; - $(".vis-edit .vis-label", $manipulation).attr("data-accesskey", "e").click(app.network.editNode); + for (let [letter, tag, callback_name] in shortcuts) { + $(`.vis-${tag} .vis-label`, $manipulation).attr('data-accesskey', letter).click(app.network[callback_name]); + } accesskeyfie(); }; @@ -909,7 +919,7 @@ function initNetwork(includePositions = true) { // double click action trigger editation app.network.on("doubleClick", active => { if (active.nodes.length === 1) { - let ev = document.createEvent('MouseEvent');// vis-js button need to be clicked this hard way + let ev = document.createEvent('MouseEvent'); // vis-js button need to be clicked this hard way ev.initEvent("pointerdown", true, true); $(".vis-edit", $manipulation).get()[0].dispatchEvent(ev); } @@ -974,7 +984,7 @@ function refresh_color(bot) { function load_live_info() { $(".navbar").addClass('waiting'); return authenticatedGetJson(managementUrl('queues-and-status')) - .done(function (data) { + .done(data => { let bot_queues; [bot_queues, bot_status] = data; @@ -1007,3 +1017,13 @@ function load_live_info() { this.blocking = false; }); } + +function set_pending_change(bot_id = null) { + $saveButton.blinking(bot_id); + warn_on_close_tab = true; +} + +function unset_pending_change() { + $saveButton.unblinking(); + warn_on_close_tab = false; +} diff --git a/intelmq_manager/static/js/monitor.js b/intelmq_manager/static/js/monitor.js index 11bac2f..9b5f738 100644 --- a/intelmq_manager/static/js/monitor.js +++ b/intelmq_manager/static/js/monitor.js @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2020 IntelMQ Team , 2020 Edvard Rejthar , 2021 Mikk Margus Möll // // SPDX-License-Identifier: AGPL-3.0-or-later +'use strict'; var ALL_BOTS = 'All Bots'; var bot_logs = {}; @@ -250,7 +251,9 @@ function load_bot_log() { .fail(ajax_fail_callback('Error loading bot log information')) .always(() => { $('#logs-panel-title').removeClass('waiting'); - this.blocking = false; + if (this instanceof Interval) { + this.blocking = false; + } }); } @@ -264,7 +267,9 @@ function load_bot_queues() { }) .fail(ajax_fail_callback('Error loading bot queues information')) .always(() => { - this.blocking = false; + if (this instanceof Interval) { + this.blocking = false; + } }); } @@ -321,14 +326,15 @@ function select_bot(bot_id, history_push = false) { } function refresh_path_names() { + let parent = $dq.parent(); if ($.isEmptyObject(path_names)) { // expand the columns - //$dq.parent().find("col:eq(1)").css("visibility", "collapse"); - $dq.parent().find("col:eq(1)").css("display", "none"); - $("td:nth-child(2), th:nth-child(2)", $dq.parent()).css("display", "none"); + //parent.find("col:eq(1)").css("visibility", "collapse"); + parent.find("col:eq(1)").css("display", "none"); + $("td:nth-child(2), th:nth-child(2)", parent).css("display", "none"); - $dq.parent().find("th:eq(0)").removeClass().addClass("width-80"); - $dq.parent().find("th:eq(1)").removeClass(); + parent.find("th:eq(0)").removeClass().addClass("width-80"); + parent.find("th:eq(1)").removeClass(); if ($("#destination-queues-table-div").hasClass('col-md-12')) { // in full width display of all bots, there is no need of another hassling return; @@ -339,13 +345,13 @@ function refresh_path_names() { } // fold the columns to make more space on the line due to the Path column - //$dq.parent().find("col:eq(1)").css("visibility", "inherit"); - //$dq.parent().find("col:eq(1)").css("display", "inherit"); - $("td:nth-child(2), th:nth-child(2)", $dq.parent()).css("display", "revert"); + //parent.find("col:eq(1)").css("visibility", "inherit"); + //parent.find("col:eq(1)").css("display", "inherit"); + $("td:nth-child(2), th:nth-child(2)", parent).css("display", "revert"); - $dq.parent().find("th:eq(0)").removeClass().addClass("width-60"); - $dq.parent().find("th:eq(1)").addClass("width-20"); + parent.find("th:eq(0)").removeClass().addClass("width-60"); + parent.find("th:eq(1)").addClass("width-20"); $("#destination-queues-table-div").removeClass("col-md-4").addClass("col-md-5"); $("#internal-queue-table-div").removeClass("col-md-4").addClass("col-md-3"); diff --git a/intelmq_manager/static/js/network-configuration.js b/intelmq_manager/static/js/network-configuration.js index 88afcb7..4361cc1 100644 --- a/intelmq_manager/static/js/network-configuration.js +++ b/intelmq_manager/static/js/network-configuration.js @@ -96,7 +96,7 @@ var NETWORK_OPTIONS = { for (let node_name of data.nodes) { delete app.nodes[node_name]; } - $saveButton.blinking(); + set_pending_change(); }, addEdge: function (data, callback) { if (data.from === data.to) { @@ -158,7 +158,7 @@ var NETWORK_OPTIONS = { add_edge(data.from, data.to, data.path); - $saveButton.blinking(data.from); + set_pending_change(data.from); if (edit_needed) { editPath(app, data.id, true); } @@ -168,7 +168,7 @@ var NETWORK_OPTIONS = { let queue = app.nodes[from].parameters.destination_queues[path]; remove_edge(from, to, path); - $saveButton.blinking(from); + set_pending_change(from); callback(data); } }, @@ -202,7 +202,7 @@ function editPath(app, edge, adding=false) { } ok_clicked = true; - $saveButton.blinking(); + set_pending_change(); }).on("hide.bs.modal", () => { let from_queues = app.nodes[from].parameters.destination_queues[new_path] ?? []; let duplicate_edge = from_queues.includes(to); @@ -239,25 +239,27 @@ function duplicateNode(app, bot) { // deep copy old bot information let node = $.extend(true, {}, app.nodes[bot]); + app.positions[new_id] = app.positions[bot]; node.id = new_id; node.bot_id = new_id; app.nodes[new_id] = node; // add to the Vis and focus - app.network_data.nodes.add(convert_nodes([node])); + app.network_data.nodes.add(convert_nodes([node], true)); for (let edge of app.network.getConnectedEdges(bot).map(edge => app.network_data.edges.get(edge))) { - delete edge.id; + let [old_from, old_to, path] = from_edge_id(edge.id); if (edge.from === bot) { edge.from = new_id; } - if (edge.to === bot) { + else if (edge.to === bot) { edge.to = new_id; } + edge.id = to_edge_id(edge.from, edge.to, path); app.network_data.edges.add(edge); } app.network.selectNodes([new_id]); app.network.focus(new_id); - $saveButton.blinking(); + set_pending_change(); } function remove_edge(from, to, path) { diff --git a/intelmq_manager/static/js/static.js b/intelmq_manager/static/js/static.js index 9f97a46..19dccfd 100644 --- a/intelmq_manager/static/js/static.js +++ b/intelmq_manager/static/js/static.js @@ -105,28 +105,28 @@ if (!String.prototype.format) { let lw_tips = new Set(); $(function () { let $lw = $("#log-window"); - let closeFn = function () { + let closeFn = () => { $lw.hide(); $(".contents", $lw).html(""); lw_tips.clear(); // no tips displayed return false; }; - $lw - .on("click", function () { // clicking enlarges but not shrinks so that we may copy the text - if (!$(this).hasClass("extended")) { - $(this).toggleClass("extended"); + $lw.on("click", e => { // clicking enlarges but not shrinks so that we may copy the text + let btn = $(e.target); + if (!btn.hasClass("extended")) { + btn.toggleClass("extended"); - //$(".alert", this).prependTo($(this)); + //$(".alert", this).prependTo(btn); - $(document).on('keydown.close-log-window', function (event) { - if (event.key == "Escape") { - $(document).off('keydown.close-log-window'); - $lw.removeClass("extended"); - } - }); - } - }); + $(document).on('keydown.close-log-window', event => { + if (event.key == "Escape") { + $(document).off('keydown.close-log-window'); + $lw.removeClass("extended"); + } + }); + } + }); $("#log-window [role=close]").click(closeFn); }); @@ -140,22 +140,23 @@ function show_error(string, permit_html=false) { let $lwc = $("#log-window .contents"); let $el = $(`

${time} ${string}

`); let found = false; - $("p", $lwc).each(function () { - if ($("span:eq(2)", $(this)).text() === $("span:eq(2)", $el).text()) { + $("p", $lwc).each((i, v) => { + if ($("span:eq(2)", $(v)).text() === $("span:eq(2)", $el).text()) { // we've seen this message before found = true; // put it in front of the other errors // only if the error window is not expanded (so that it does not shuffle when the user read the details) - if (!$(this).closest("#log-window").hasClass("extended")) { - $(this).prependTo($lwc); + if (!$(v).closest("#log-window").hasClass("extended")) { + $(v).prependTo($lwc); } //blink - $("span:eq(0)", $(this)).text(time).stop().animate({opacity: 0.1}, 100, function () { - $(this).animate({opacity: 1}, 100); + let blink_e = v.children[0]; + $(blink_e, $(v)).text(time).stop().animate({opacity: 0.1}, 100, () => { + $(blink_e).animate({opacity: 1}, 100); }); // increment 'seen' counter - let counter = parseInt($("span:eq(1)", $(this)).text()) || 1; - $("span:eq(1)", $(this)).text(`${counter + 1}×`); + let counter = parseInt($("span:eq(1)", $(v)).text()) || 1; + $("span:eq(1)", $(v)).text(`${counter + 1}×`); return false; } }); @@ -349,43 +350,52 @@ var BOT_STATUS_DEFINITION = { var botnet_status = {}; // {group | true (for whole botnet) : BOT_STATUS_DEFINITION} var bot_status = {}; // {bot-id : BOT_STATUS_DEFINITION} var bot_status_previous = {}; // we need a shallow copy of bot_status, it's too slow to ask `app` every time -var bot_definition = {};// {bot-id : runtime information (group, ...)}; only management.js uses this in time +var bot_definition = {}; // {bot-id : runtime information (group, ...)}; only management.js uses this in time + +$(document).on("click", ".control-buttons button", e => { + let btn = $(e.target); -$(document).on("click", ".control-buttons button", function () { - let bot = $(this).parent().attr("data-bot-id"); - let botnet = $(this).parent().attr("data-botnet-group"); - let callback_fn = $(this).parent().data("callback_fn"); + let parent = btn.parent(); + if (parent.hasClass('btn')) { // clicked on glyphicon, shift up by one level + btn = parent; + parent = parent.parent(); + } + + let bot = parent.attr("data-bot-id"); + let botnet = parent.attr("data-botnet-group"); + let callback_fn = parent.data("callback_fn"); let url; if (bot) { - bot_status[bot] = $(this).attr("data-status-definition"); - url = managementUrl("bot", `action=${$(this).attr("data-url")}&id=${bot}`); + bot_status[bot] = btn.attr("data-status-definition"); + url = managementUrl("bot", `action=${btn.attr("data-url")}&id=${bot}`); } else { - botnet_status[botnet] = $(this).attr("data-status-definition"); - url = managementUrl('botnet', `action=${$(this).attr("data-url")}&group=${botnet}`); + botnet_status[botnet] = btn.attr("data-status-definition"); + url = managementUrl('botnet', `action=${btn.attr("data-url")}&group=${botnet}`); for (let bot_d of Object.values(bot_definition)) { if (bot_d.groupname === botnet) { - bot_status[bot_d.bot_id] = $(this).attr("data-status-definition"); + bot_status[bot_d.bot_id] = btn.attr("data-status-definition"); } } } - callback_fn.call(this, bot || botnet, 0); - $(this).siblings("[data-role=control-status]").trigger("update"); + + callback_fn.call(e.target, bot || botnet, 0); + btn.siblings("[data-role=control-status]").trigger("update"); authenticatedGetJson(url) - .done(function (data) { + .done(data => { if (bot) { // only restarting action returns an array of two values, the latter is important; otherwise, this is a string bot_status[bot] = Array.isArray(data) ? data.slice(-1)[0] : data; } else { // we received a {bot => status} object Object.assign(bot_status, data); // merge to current list } }) - .fail(function () { - ajax_fail_callback('Error {0} bot{1}'.format(bot_status[bot] || botnet_status[botnet], (!bot ? "net" : ""))).apply(null, arguments); + .fail(() => { + ajax_fail_callback(`Error ${bot_status[bot] || botnet_status[botnet]} bot${!bot ? "net" : ""}`).apply(null, arguments); bot_status[bot] = BOT_STATUS_DEFINITION.error; }).always(() => { - $(this).siblings("[data-role=control-status]").trigger("update"); - callback_fn.call(this, bot || botnet, 1); + btn.siblings("[data-role=control-status]").trigger("update"); + callback_fn.call(e.target, bot || botnet, 1); }); }); @@ -409,11 +419,12 @@ function generate_control_buttons(bot = null, botnet = null, callback_fn = null, $el.attr("data-botnet-group", botnet); } if (status_info) { - $("", {"data-role": "control-status"}).bind("update", function () { - let bot = $(this).closest(".control-buttons").attr("data-bot-id"); - let botnet = $(this).closest(".control-buttons").attr("data-botnet-group"); + $("", {"data-role": "control-status"}).bind("update", e => { + let btn = $(e.target); + let bot = btn.closest(".control-buttons").attr("data-bot-id"); + let botnet = btn.closest(".control-buttons").attr("data-botnet-group"); let status = bot ? bot_status[bot] : botnet_status[botnet]; - $(this).text(status).removeClass().addClass(`bg-${BOT_CLASS_DEFINITION[status]}`); + btn.text(status).removeClass().addClass(`bg-${BOT_CLASS_DEFINITION[status]}`); }).prependTo($el).trigger("update"); } return $el; @@ -439,18 +450,19 @@ function getUrlParameter(sParam) { function accesskeyfie() { let seen = new Set(); $("[data-accesskey]").attr("accesskey", ""); // reset all accesskeys. In Chrome, there might be only one accesskey 'e' on page. - $("[data-accesskey]:visible").each(() => { - let key = $(this).attr("data-accesskey"); + $("[data-accesskey]:visible").each((i, v) => { + let btn = $(v); + let key = btn.attr("data-accesskey"); if (seen.has(key)) { return false; // already defined at current page state } seen.add(key); - $(this).attr("accesskey", key); + btn.attr("accesskey", key); // add underscore to the accesskeyed letter if possible (can work badly with elements having nested DOM children) - let t1 = $(this).text(); - let t2 = t1.replace(new RegExp(key, "i"), match => `${escape_html(match)}`); - if (t1 != t2) { - $(this).html(t2); + let t1 = escape_html(btn.text()); + let t2 = t1.replace(new RegExp(key, "i"), match => `${match}`); + if (t1 !== t2) { + btn.html(t2); } }); } @@ -514,7 +526,7 @@ $(document).ready(function() { // finished. (after success and error callbacks are executed) complete: () => $('#loginForm #password').val(""), // Executes this if the request was successful. - }).done(function(data) { + }).done(data => { // Check if login_token and username came back and store them in // sessionStorage. if (typeof data.login_token !== 'undefined' &&