bunkerweb 1.4.0

This commit is contained in:
bunkerity
2022-06-03 17:24:14 +02:00
parent 3a078326c5
commit a9f886804a
5245 changed files with 1432051 additions and 27894 deletions

1780
ui/static/js/939.js Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

27
ui/static/js/cache.js Normal file
View File

@@ -0,0 +1,27 @@
var editor_path = "";
$(document).ready(function () {
$("textarea").numberedtextarea({ allowTabChar: true });
$("#modal-see-file").on("show.bs.modal", function (event) {
var button = $(event.relatedTarget);
var path = button.data("path");
var content = button.data("content");
$("#modal-see-file-label").html(`File: ${path}`);
if (editor_path != path) {
$("#editor").html(atob(content));
}
editor_path = path;
});
$(".download-button").click(function () {
var filepath = $(this).attr("data-path");
windows.open(`${location.href}/download?path=${filepath}`, "_blank");
});
$(".collapse-div").click(function () {
$(this).find(".rotate-icon").toggleClass("down");
});
});

96
ui/static/js/configs.js Normal file
View File

@@ -0,0 +1,96 @@
var editor_path = "";
var decoder = new TextDecoder("utf-8");
$(document).ready(function () {
$("textarea").numberedtextarea({ allowTabChar: true });
$("#modal-edit-new-file").on("show.bs.modal", function (event) {
var button = $(event.relatedTarget);
var path = button.data("path");
$("#file-path").val(path);
var new_path = path.split("/");
var name = new_path.pop().replace(".conf", "");
var action = button.data("action");
$("#file-operation").val(action);
var content = button.data("content");
$("#modal-edit-new-file-label").html(
`<div class="d-flex align-items-center"><div class="flex-grow-1">${
action == "edit" ? "Editing" : "New"
} file: <span class="d-md-inline d-none">${
action == "edit" ? new_path.join("/") : path
}</span><span class="d-md-none">.../${
action == "edit" ? new_path.pop() : path.split("/").pop()
}</span>/</div><div class="d-sm-flex align-items-center"><input type="text" class="form-control" id="new-file-name" name="name" value="${
action == "edit" ? name : ""
}" placeholder="File name" required="" pattern="^[a-zA-Z0-9_-]{1,64}$" title="File name can only contain numbers, letters, underscores and hyphens (min 1 character and max 64)" />.conf</div></div>`
);
var editor = $("#editor");
if (action == "edit") {
if (editor_path != path) {
editor.html(atob(content));
}
} else {
editor.html("");
}
editor.keyup();
editor_path = path;
});
$("#modal-edit-new-folder").on("show.bs.modal", function (event) {
var button = $(event.relatedTarget);
var path = button.data("path");
$("#folder-path").val(path);
var action = button.data("action");
$("#folder-operation").val(action);
var foldername = path.split("/").pop();
$("#modal-edit-new-folder-label").html(
`<div class="d-flex align-items-center"><div class="flex-grow-1">${
action == "edit" ? "Editing" : "New"
} folder: <span class="d-md-inline d-none">${path}</span><span class="d-md-none">.../${path
.split("/")
.pop()}</span>/</div><div class="d-sm-flex align-items-center"><input type="text" class="form-control" id="new-folder-name" name="name" placeholder="Folder name" value="${
action == "edit" ? foldername : ""
}" required="" pattern="^[a-zA-Z0-9_-]{1,64}$" title="Folder name can only contain numbers, letters, underscores and hyphens (min 1 character and max 64)" /></div></div>`
);
});
$("#modal-delete").on("show.bs.modal", function (event) {
var button = $(event.relatedTarget);
var path = button.data("path");
$("#delete-path").val(path);
var name = path.split("/").pop();
$("#modal-delete-label").html(
`Deleting ${name.includes(".") ? "file" : "folder"}`
);
$("#modal-delete-body").html(
`Are you sure you want to delete <b>${path}</b> ?`
);
});
$(".collapse-div").click(function () {
$(this).find(".rotate-icon").toggleClass("down");
});
$("form").on("focus", ".form-control", function () {
if ($(this).attr("type") == "text" && $(this).prop("validity").valid) {
$(this).addClass("is-valid");
}
});
$("form").on("focusout", ".form-control", function () {
if ($(this).attr("type") == "text") {
$(this).removeClass("is-valid");
}
});
$("form").on("change", ".form-control", function () {
if ($(this).attr("type") == "text" && !$(this).prop("validity").valid) {
$(this).addClass("is-invalid");
} else {
$(this).removeClass("is-invalid");
}
});
});

View File

@@ -1,122 +0,0 @@
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl, { container: 'body' })
})
function post(operation, url, data) {
var form = document.createElement("form");
form.method = "POST";
form.action = url;
for (var key in data) {
var field = document.createElement("input");
field.type = "hidden";
field.name = key;
field.value = data[key];
form.appendChild(field);
}
var field = document.createElement("input");
field.type = "hidden";
field.name = "operation";
field.value = operation;
form.appendChild(field);
document.body.appendChild(form);
form.submit();
}
function getData(id) {
var elements = document.getElementById(id).elements;
var data = {};
for (var i = 0; i < elements.length; i++) {
element = elements[i];
if (element["type"] === "checkbox") {
if (element["checked"]) {
data[element["name"]] = "yes";
}
else {
data[element["name"]] = "no";
}
}
else {
data[element["name"]] = element["value"];
}
}
return data;
}
function newService() {
post("new", "services", getData('form-new'));
}
function editService(id) {
post("edit", "services", getData('form-edit-' + id));
}
function deleteService(id) {
post("delete", "services", getData('form-delete-' + id));
}
function reloadInstance(id) {
post("reload", "instances", getData('form-instance-' + id));
return false;
}
function startInstance(id) {
post("start", "instances", getData('form-instance-' + id));
return false;
}
function stopInstance(id) {
post("stop", "instances", getData('form-instance-' + id));
return false;
}
function restartInstance(id) {
post("restart", "instances", getData('form-instance-' + id));
return false;
}
function deleteInstance(id) {
post("delete", "instances", getData('form-instance-' + id));
return false;
}
var multiples = {};
function addMultiple(id, paramsEnc) {
var params = JSON.parse(paramsEnc);
var div = document.getElementById(id);
if (!multiples.hasOwnProperty(id)) {
multiples[id] = 0;
}
multiples[id]++;
for (var i = 0; i < params.length; i++) {
var input = "";
var input_id = id + "-" + params[i]["id"] + "-" + multiples[id].toString();
var input_name = params[i]["env"] + "_" + multiples[id].toString();
var input_label = params[i]["label"] + " #" + multiples[id].toString();
var input_value = params[i]["default"];
var pt = "";
if (params[i]["type"] == "text") {
input = `<input type="text" class="form-control" id="${input_id}" value="${input_value}" name="${input_name}">`;
}
else if (params[i]["type"] == "checkbox") {
if (input_value == "yes") {
input_value = "checked";
}
input = `<div class="form-check form-switch"><input type="checkbox" class="form-check-input" id="${input_id}" name="${input_name}" ${input_value}></div>`;
pt = "pt-0";
}
div.insertAdjacentHTML('beforeend', `<label for="${input_id}" class="col-4 col-form-label ${pt} mb-3" id="label-${input_id}">${input_label}</label><div class="col-8 mb-3" id="input-${input_id}">${input}</div>`);
}
}
function delMultiple(id, paramsEnc) {
if (multiples.hasOwnProperty(id) && multiples[id] > 0) {
var params = JSON.parse(paramsEnc);
for (var i = 0; i < params.length; i++) {
var input_id = id + "-" + params[i]["id"] + "-" + multiples[id].toString();
document.getElementById("label-" + input_id).remove();
document.getElementById("input-" + input_id).remove();
}
multiples[id]--;
}
}

45
ui/static/js/darkmode.js Normal file
View File

@@ -0,0 +1,45 @@
window.onload = init;
var darkMode = document.getElementById("dark-mode-switch");
var darkModeIcon = "darkModeIcon";
function init() {
if (darkMode) {
initTheme();
darkMode.addEventListener("change", function () {
resetTheme();
});
}
}
function initTheme() {
var darkThemeSelected =
window.matchMedia("(prefers-color-scheme: dark)").matches ||
(localStorage.getItem("dark-mode") !== null &&
localStorage.getItem("dark-mode") === "dark");
darkMode.checked = darkThemeSelected;
darkThemeSelected
? document.body.setAttribute("data-theme", "dark")
: document.body.removeAttribute("data-theme");
darkThemeSelected
? document.getElementById(darkModeIcon).classList.add("bi-moon")
: document.getElementById(darkModeIcon).classList.add("bi-sun");
}
function resetTheme() {
if (darkMode.checked) {
document.body.setAttribute("data-theme", "dark");
localStorage.setItem("dark-mode", "dark");
document.getElementById(darkModeIcon).classList.remove("bi-sun");
document.getElementById(darkModeIcon).classList.add("bi-moon");
} else {
document.body.removeAttribute("data-theme");
localStorage.removeItem("dark-mode");
document.getElementById(darkModeIcon).classList.remove("bi-moon");
document.getElementById(darkModeIcon).classList.add("bi-sun");
}
}

6
ui/static/js/fa.all.min.js vendored Normal file

File diff suppressed because one or more lines are too long

2
ui/static/js/jquery.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,146 @@
/*
* NumberedTextarea - jQuery Plugin
* Textarea with line numbering
*
* Copyright (c) 2015 Dariusz Arciszewski
*
* Requires: jQuery v2.0+
*
* Licensed under the GPL licenses:
* http://www.gnu.org/licenses/gpl.html
*/
(function ($) {
$.fn.numberedtextarea = function(options) {
var settings = $.extend({
color: null, // Font color
borderColor: null, // Border color
class: null, // Add class to the 'numberedtextarea-wrapper'
allowTabChar: false, // If true Tab key creates indentation
}, options);
this.each(function() {
if(this.nodeName.toLowerCase() !== "textarea") {
console.log('This is not a <textarea>, so no way Jose...');
return false;
}
addWrapper(this, settings);
addLineNumbers(this, settings);
if(settings.allowTabChar) {
$(this).allowTabChar();
}
});
return this;
};
$.fn.allowTabChar = function() {
if (this.jquery) {
this.each(function() {
if (this.nodeType == 1) {
var nodeName = this.nodeName.toLowerCase();
if (nodeName == "textarea" || (nodeName == "input" && this.type == "text")) {
allowTabChar(this);
}
}
})
}
return this;
}
function addWrapper(element, settings) {
var wrapper = $('<div class="numberedtextarea-wrapper"></div>').insertAfter(element);
$(element).detach().appendTo(wrapper);
}
function addLineNumbers(element, settings) {
element = $(element);
var wrapper = element.parents('.numberedtextarea-wrapper');
// Get textarea styles to implement it on line numbers div
var paddingLeft = parseFloat(element.css('padding-left'));
var paddingTop = parseFloat(element.css('padding-top'));
var paddingBottom = parseFloat(element.css('padding-bottom'));
var lineNumbers = $('<div class="numberedtextarea-line-numbers"></div>').insertAfter(element);
element.css({
paddingLeft: paddingLeft + lineNumbers.width() + 'px'
}).on('input propertychange change keyup paste', function() {
renderLineNumbers(element, settings);
}).on('scroll', function() {
scrollLineNumbers(element, settings);
});
lineNumbers.css({
paddingLeft: paddingLeft + 'px',
paddingTop: paddingTop + 'px',
lineHeight: element.css('line-height'),
fontFamily: element.css('font-family'),
width: lineNumbers.width() - paddingLeft + 'px',
});
element.trigger('change');
}
function renderLineNumbers(element, settings) {
element = $(element);
var linesDiv = element.parent().find('.numberedtextarea-line-numbers');
var count = element.val().split("\n").length;
var paddingBottom = parseFloat(element.css('padding-bottom'));
linesDiv.find('.numberedtextarea-number').remove();
for(i = 1; i<=count; i++) {
var line = $('<div class="numberedtextarea-number numberedtextarea-number-' + i + '">' + i + '</div>').appendTo(linesDiv);
if(i === count) {
line.css('margin-bottom', paddingBottom + 'px');
}
}
}
function scrollLineNumbers(element, settings) {
element = $(element);
var linesDiv = element.parent().find('.numberedtextarea-line-numbers');
linesDiv.scrollTop(element.scrollTop());
}
function pasteIntoInput(el, text) {
el.focus();
if (typeof el.selectionStart == "number") {
var val = el.value;
var selStart = el.selectionStart;
el.value = val.slice(0, selStart) + text + val.slice(el.selectionEnd);
el.selectionEnd = el.selectionStart = selStart + text.length;
} else if (typeof document.selection != "undefined") {
var textRange = document.selection.createRange();
textRange.text = text;
textRange.collapse(false);
textRange.select();
}
}
function allowTabChar(el) {
$(el).keydown(function(e) {
if (e.which == 9) {
pasteIntoInput(this, "\t");
return false;
}
});
// For Opera, which only allows suppression of keypress events, not keydown
$(el).keypress(function(e) {
if (e.which == 9) {
return false;
}
});
}
}(jQuery));

165
ui/static/js/logs.js Normal file
View File

@@ -0,0 +1,165 @@
var current_container_id = null;
var last_logs_update = null;
var filter = "";
var date_filter1 = "";
var date_filter2 = "";
$(document).ready(function () {
$("#date-picker").datepicker({ format: "yyyy/mm/dd" });
current_container_id = $("#active-nav").data("container-id");
const logs_interval = setInterval(load_logs, 1000);
function clear_filters() {
filter = "";
date_filter1 = "";
date_filter2 = "";
$("#logs-list li").filter(function () {
$(this).toggle(true);
});
}
async function get_logs(container_id, last_update) {
const response = await fetch(
`${location.href}/${container_id}` +
(last_update ? `?last_update=${last_update}` : "")
);
if (response.status === 200) {
return await response.json();
} else {
console.log(`Error: ${response.status}`);
clearInterval(logs_interval);
}
return null;
}
async function load_logs() {
logs = await get_logs(current_container_id, last_logs_update);
if (logs) {
last_logs_update = logs.last_update;
logs.logs.forEach((log) => {
const log_element = document.createElement("li");
log_element.className = "list-group-item";
if (log.type === "error") {
log_element.classList.add("list-group-item-danger");
} else if (log.type === "warning") {
log_element.classList.add("list-group-item-warning");
} else if (log.type === "info") {
log_element.classList.add("list-group-item-info");
}
log_element.innerHTML = log.content;
if (
!(
log.content.toLowerCase().indexOf(filter) > -1 &&
(log.content.toLowerCase().indexOf(date_filter1) > -1 ||
log.content.toLowerCase().indexOf(date_filter2) > -1)
)
) {
log_element.style = "display: none;";
}
if (log.separator) log_element.classList.add("pt-1");
document.getElementById("logs-list").appendChild(log_element);
});
}
}
$("#date-picker").on("changeDate", function () {
let date = $(this).datepicker("getFormattedDate");
if (date) {
date_filter1 = date;
date_filter2 = date.replaceAll("/", "-");
$("#date-input").val(date);
$("#date-clear").show();
$("#logs-list li").filter(function () {
$(this).toggle(
$(this).text().toLowerCase().indexOf(filter) > -1 &&
($(this).text().toLowerCase().indexOf(date_filter1) > -1 ||
$(this).text().toLowerCase().indexOf(date_filter2) > -1)
);
});
}
});
$("#filter-input").on("keyup", function () {
var val = $.trim(this.value);
if (!val) {
val = "";
if (!$("#date-picker").datepicker("getFormattedDate")) {
$("#date-clear").hide();
}
} else {
$("#date-clear").show();
}
val = val.toLowerCase();
filter = val;
$("#logs-list li").filter(function () {
$(this).toggle(
$(this).text().toLowerCase().indexOf(val) > -1 &&
($(this).text().toLowerCase().indexOf(date_filter1) > -1 ||
$(this).text().toLowerCase().indexOf(date_filter2) > -1)
);
});
});
$("#date-clear").click(function () {
$("#filter-input").val("");
$("#date-input").val("");
$(this).hide();
clear_filters();
});
$("#refresh-logs").click(function () {
if ($(this).find("#rotate-icon").hasClass("rotate")) {
$(this).find("#rotate-icon").removeClass("rotate");
clearInterval(logs_interval);
} else if (!$(this).find("#rotate-icon").hasClass("rotate")) {
$(this).find("#rotate-icon").addClass("rotate");
logs_interval = setInterval(load_logs, 1000);
}
});
$(".container-selector").click(function () {
clearInterval(logs_interval);
current_container_id = $(this).data("container-id");
$("#logs-list").empty();
clear_filters();
last_logs_update = null;
let old_selector = $("#active-nav");
old_selector.removeClass("active");
old_selector.removeAttr("aria-current");
old_selector.removeAttr("id");
$(this).addClass("active");
$(this).attr("aria-current", "page");
$(this).attr("id", "active-nav");
if (current_container_id == "linux") {
$("#date-picker").prop("disabled", true);
} else {
$("#date-picker").prop("disabled", false);
}
load_logs();
setTimeout(function () {
logs_interval = setInterval(load_logs, 1000);
}, 1000);
});
$("#filter-input").on("keypress", function (e) {
var code = e.keyCode || e.which;
if (code == 13) {
e.preventDefault();
return false;
}
});
});

46
ui/static/js/navbar.js Normal file
View File

@@ -0,0 +1,46 @@
(function ($) {
"use strict";
$(function () {
var header = $(".start-style");
$(window).scroll(function () {
var scroll = $(window).scrollTop();
if (scroll >= 10) {
header.removeClass("start-style").addClass("scroll-on");
} else {
header.removeClass("scroll-on").addClass("start-style");
}
});
});
//Animation
$(document).ready(function () {
$("body.hero-anime").removeClass("hero-anime");
});
//Menu On Hover
$("body").on("mouseenter mouseleave", ".nav-item", function (e) {
if ($(window).width() > 750) {
var _d = $(e.target).closest(".nav-item");
_d.addClass("show");
setTimeout(function () {
_d[_d.is(":hover") ? "addClass" : "removeClass"]("show");
}, 1);
}
});
//Switch light/dark
$("#switch").on("click", function () {
if ($("body").hasClass("dark")) {
$("body").removeClass("dark");
$("#switch").removeClass("switched");
} else {
$("body").addClass("dark");
$("#switch").addClass("switched");
}
});
})(jQuery);

124
ui/static/js/plugins.js Normal file
View File

@@ -0,0 +1,124 @@
$(document).ready(function () {
$("#modal-delete").on("show.bs.modal", function (event) {
var button = $(event.relatedTarget);
var path = button.data("path");
$("#delete-path").val(path);
var name = path.split("/").pop();
$("#modal-delete-label").html("Deleting plugin");
$("#modal-delete-body").html(
`Are you sure you want to delete <b>${path}</b> ?`
);
});
$(".collapse-div").click(function () {
$(this).find(".rotate-icon").toggleClass("down");
});
});
const form = document.querySelector("form"),
dropZoneElement = document.querySelector(".drop-zone"),
fileInput = document.querySelector(".file-input"),
progressArea = document.querySelector(".progress-area"),
uploadedArea = document.querySelector(".uploaded-area");
form.addEventListener("click", () => {
fileInput.click();
});
fileInput.onchange = ({ target }) => {
timeout = 500;
for (let i = 0; i < target.files.length; i++) {
setTimeout(() => uploadFile(target.files[i]), timeout * i);
}
};
dropZoneElement.addEventListener("dragover", (e) => {
e.preventDefault();
dropZoneElement.classList.add("drop-zone--over");
});
["dragleave", "dragend"].forEach((type) => {
dropZoneElement.addEventListener(type, (e) => {
dropZoneElement.classList.remove("drop-zone--over");
});
});
dropZoneElement.addEventListener("drop", (e) => {
e.preventDefault();
fileInput.files = e.dataTransfer.files;
fileInput.dispatchEvent(new Event("change"));
dropZoneElement.classList.remove("drop-zone--over");
});
function uploadFile(file) {
let name = file.name;
if (name.length >= 12) {
let splitName = name.split(".");
name = splitName[0].substring(0, 13) + "... ." + splitName[1];
}
let xhr = new XMLHttpRequest();
xhr.open("POST", "plugins/upload");
let fileSize;
xhr.upload.addEventListener("progress", ({ loaded, total }) => {
let fileLoaded = Math.floor((loaded / total) * 100);
let fileTotal = Math.floor(total / 1000);
fileTotal < 1024
? (fileSize = fileTotal + " KB")
: (fileSize = (loaded / (1024 * 1024)).toFixed(2) + " MB");
let progressHTML = `<li class="row">
<span class="fa-solid fa-file-zipper"></span>
<span class="content">
<div class="details">
<span class="name">${name} • Uploading</span>
<span class="percent">${fileLoaded}%</span>
</div>
<div class="progress-bar">
<div class="progress" style="width: ${fileLoaded}%"></div>
</div>
</span>
</li>`;
uploadedArea.classList.add("onprogress");
progressArea.innerHTML = progressHTML;
});
xhr.onreadystatechange = function () {
if (xhr.readyState === XMLHttpRequest.DONE) {
progressArea.innerHTML = "";
let uploadedHTML =
xhr.status == 201
? `<li class="row">
<div class="content upload">
<i class="fa-solid fa-file-zipper"></i>
<div class="details">
<span class="name">${name} • Uploaded</span>
<span class="size">${fileSize}</span>
</div>
</div>
<i class="fa-solid fa-check"></i>
</li>`
: `<li class="row failed">
<div class="content upload">
<i class="fa-solid fa-file-zipper"></i>
<div class="details">
<span class="name">${name} • Failed</span>
<span class="size">${fileSize}</span>
</div>
</div>
<i class="fa-solid fa-xmark"></i>
</li>`;
uploadedArea.classList.remove("onprogress");
uploadedArea.insertAdjacentHTML("afterbegin", uploadedHTML);
}
};
let data = new FormData();
data.set("file", file);
data.set("csrf_token", $("#csrf_token").val());
xhr.send(data);
}

6
ui/static/js/popper.min.js vendored Normal file

File diff suppressed because one or more lines are too long

103
ui/static/js/services.js Normal file
View File

@@ -0,0 +1,103 @@
var multiples = {};
function addMultiple(id, paramsEnc) {
var params = JSON.parse(paramsEnc);
var div = document.getElementById(id);
if (!multiples.hasOwnProperty(id)) {
multiples[id] = 0;
}
multiples[id]++;
x = 0;
for (const param in params) {
var input = "";
var input_id =
id + "-" + params[param]["id"] + "-" + multiples[id].toString();
var input_name =
params[param]["env"] +
(multiples[id] - 1 > 0 ? "_" + (multiples[id] - 1).toString() : "");
var input_label = params[param]["label"] + " #" + multiples[id].toString();
var input_value = params[param]["default"];
var input_help = params[param]["help"];
var input_selects = params[param]["select"];
var pt = "";
var padding_bottom = "";
if (params[param]["type"] == "text" || params[param]["type"] == "number") {
input = `<input type="${params[param]["type"]}" class="form-control" id="${input_id}" value="${input_value}" name="${input_name}">`;
} else if (params[param]["type"] == "check") {
if (input_value == "yes") {
input_value = "checked";
} else {
input_value = "";
}
input = `<div class="form-check form-switch"><input type="checkbox" class="form-check-input" role="switch" id="${input_id}" name="${input_name}" ${input_value}><input type="hidden" id="${input_id}-hidden" name="${input_name}" value="off"></div>`;
pt = "pt-0";
} else if (params[param]["type"] == "select") {
input = `<select type="form-select" class="form-control form-select" id="${input_id}" name="${input_name}">`;
for (const select in input_selects) {
selected = "";
if (input_value == select) {
selected = "selected";
}
input += `<option value="${select}" ${selected}>${select}</option>`;
}
input += `</select>`;
}
if (x === 0 && multiples[id] > 1) {
padding_bottom = "pb-3";
}
div.insertAdjacentHTML(
"afterend",
`<div class="d-flex flex-row justify-content-between align-items-center mb-3 ${padding_bottom}" id="${input_id}"><div class="px-2 d-sm-inline" data-bs-toggle="tooltip" data-bs-placement="bottom" title="${input_help}"><i class="fas fa-question-circle"></i></div><label for="${input_id}" class="flex-grow-1 d-sm-inline ${pt}" id="${input_id}">${input_label}</label><div class="d-sm-inline" id="${input_id}">${input}</div></div>`
);
x++;
}
}
function delMultiple(id, paramsEnc) {
if (multiples.hasOwnProperty(id) && multiples[id] > 0) {
var params = JSON.parse(paramsEnc);
for (const param in params) {
var input_id =
id + "-" + params[param]["id"] + "-" + multiples[id].toString();
document.getElementById(input_id).remove();
}
multiples[id]--;
}
}
$(document).ready(function () {
$("form").on("focus", ".form-control", function () {
if (
["text", "number"].includes($(this).attr("type")) &&
$(this).prop("validity").valid
) {
$(this).addClass("is-valid");
}
});
$("form").on("focusout", ".form-control", function () {
if (["text", "number"].includes($(this).attr("type"))) {
$(this).removeClass("is-valid");
}
});
$("form").on("change", ".form-control", function () {
if (["text", "number"].includes($(this).attr("type"))) {
if (!$(this).prop("validity").valid) {
$("#pills-tab a").addClass("disabled");
$(this).addClass("is-invalid");
} else {
$("#pills-tab a").removeClass("disabled");
$(this).removeClass("is-invalid");
$(this).addClass("is-valid");
}
}
});
});

File diff suppressed because one or more lines are too long