mirror of
https://github.com/arcan1s/ahriman.git
synced 2025-04-24 15:27:17 +00:00
feat: get rid of jquery (#133)
This commit is contained in:
parent
f43ee2fd1d
commit
c4f4e37731
@ -44,28 +44,28 @@
|
|||||||
</button>
|
</button>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li>
|
<li>
|
||||||
<button id="package-add-button" class="btn dropdown-item" data-bs-toggle="modal" data-bs-target="#package-add-modal" hidden>
|
<button id="package-add-button" class="btn dropdown-item" data-bs-toggle="modal" data-bs-target="#package-add-modal">
|
||||||
<i class="bi bi-plus"></i> add
|
<i class="bi bi-plus"></i> add
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<button id="package-update-button" class="btn dropdown-item" onclick="packagesUpdate()" hidden>
|
<button id="package-update-button" class="btn dropdown-item" onclick="packagesUpdate()">
|
||||||
<i class="bi bi-play"></i> update
|
<i class="bi bi-play"></i> update
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<button id="package-rebuild-button" class="btn dropdown-item" data-bs-toggle="modal" data-bs-target="#package-rebuild-modal" hidden>
|
<button id="package-rebuild-button" class="btn dropdown-item" data-bs-toggle="modal" data-bs-target="#package-rebuild-modal">
|
||||||
<i class="bi bi-arrow-clockwise"></i> rebuild
|
<i class="bi bi-arrow-clockwise"></i> rebuild
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<button id="package-remove-button" class="btn dropdown-item" onclick="packagesRemove()" disabled hidden>
|
<button id="package-remove-button" class="btn dropdown-item" onclick="packagesRemove()" disabled>
|
||||||
<i class="bi bi-trash"></i> remove
|
<i class="bi bi-trash"></i> remove
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<button id="key-import-button" type="button" class="btn btn-info" data-bs-toggle="modal" data-bs-target="#key-import-modal" hidden>
|
<button id="key-import-button" type="button" class="btn btn-info" data-bs-toggle="modal" data-bs-target="#key-import-modal">
|
||||||
<i class="bi bi-key"></i><span class="d-none d-sm-inline"> import key</span>
|
<i class="bi bi-key"></i><span class="d-none d-sm-inline"> import key</span>
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
<script>
|
<script>
|
||||||
const alertPlaceholder = $("#alert-placeholder");
|
const alertPlaceholder = document.getElementById("alert-placeholder");
|
||||||
|
|
||||||
function createAlert(title, message, clz, action, id) {
|
function createAlert(title, message, clz, action, id) {
|
||||||
if (!id) id = $.md5(title + message); // MD5 id from the content
|
id ??= md5(title + message); // MD5 id from the content
|
||||||
if (alertPlaceholder.find(`#${id}`).length > 0) return; // check if there are duplicates
|
if (alertPlaceholder.querySelector(`#alert-${id}`)) return; // check if there are duplicates
|
||||||
|
|
||||||
const wrapper = document.createElement("div");
|
const wrapper = document.createElement("div");
|
||||||
wrapper.id = id;
|
wrapper.id = `alert-${id}`;
|
||||||
wrapper.classList.add("toast", clz);
|
wrapper.classList.add("toast", clz);
|
||||||
wrapper.role = "alert";
|
wrapper.role = "alert";
|
||||||
wrapper.ariaLive = "assertive";
|
wrapper.ariaLive = "assertive";
|
||||||
@ -23,7 +23,7 @@
|
|||||||
body.innerText = message;
|
body.innerText = message;
|
||||||
wrapper.appendChild(body);
|
wrapper.appendChild(body);
|
||||||
|
|
||||||
alertPlaceholder.append(wrapper);
|
alertPlaceholder.appendChild(wrapper);
|
||||||
const toast = new bootstrap.Toast(wrapper);
|
const toast = new bootstrap.Toast(wrapper);
|
||||||
wrapper.addEventListener("hidden.bs.toast", _ => {
|
wrapper.addEventListener("hidden.bs.toast", _ => {
|
||||||
wrapper.remove(); // bootstrap doesn't remove elements
|
wrapper.remove(); // bootstrap doesn't remove elements
|
||||||
@ -32,12 +32,12 @@
|
|||||||
toast.show();
|
toast.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
function showFailure(title, description, jqXHR, errorThrown) {
|
function showFailure(title, description, error) {
|
||||||
let details;
|
let details;
|
||||||
try {
|
try {
|
||||||
details = $.parseJSON(jqXHR.responseText).error; // execution handler json error response
|
details = JSON.parse(error.text).error; // execution handler json error response
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
details = errorThrown;
|
details = error.text ?? error.message ?? error;
|
||||||
}
|
}
|
||||||
createAlert(title, description(details), "text-bg-danger");
|
createAlert(title, description(details), "text-bg-danger");
|
||||||
}
|
}
|
||||||
|
@ -36,61 +36,69 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const keyImportModal = $("#key-import-modal");
|
const keyImportModal = document.getElementById("key-import-modal");
|
||||||
const keyImportForm = $("#key-import-form");
|
const keyImportForm = document.getElementById("key-import-form");
|
||||||
|
|
||||||
const keyImportBodyInput = $("#key-import-body-input");
|
const keyImportBodyInput = document.getElementById("key-import-body-input");
|
||||||
const keyImportCopyButton = $("#key-import-copy-button");
|
const keyImportCopyButton = document.getElementById("key-import-copy-button");
|
||||||
|
|
||||||
const keyImportFingerprintInput = $("#key-import-fingerprint-input");
|
const keyImportFingerprintInput = document.getElementById("key-import-fingerprint-input");
|
||||||
const keyImportServerInput = $("#key-import-server-input");
|
const keyImportServerInput = document.getElementById("key-import-server-input");
|
||||||
|
|
||||||
async function copyPgpKey() {
|
async function copyPgpKey() {
|
||||||
const logs = keyImportBodyInput.text();
|
const key = keyImportBodyInput.textContent;
|
||||||
await copyToClipboard(logs, keyImportCopyButton);
|
await copyToClipboard(key, keyImportCopyButton);
|
||||||
}
|
}
|
||||||
|
|
||||||
function fetchPgpKey() {
|
function fetchPgpKey() {
|
||||||
const key = keyImportFingerprintInput.val();
|
const key = keyImportFingerprintInput.value;
|
||||||
const server = keyImportServerInput.val();
|
const server = keyImportServerInput.value;
|
||||||
|
|
||||||
if (key && server) {
|
if (key && server) {
|
||||||
$.ajax({
|
makeRequest(
|
||||||
url: "/api/v1/service/pgp",
|
"/api/v1/service/pgp",
|
||||||
data: {"key": key, "server": server},
|
{
|
||||||
type: "GET",
|
query: {
|
||||||
dataType: "json",
|
key: key,
|
||||||
success: response => { keyImportBodyInput.text(response.key); },
|
server: server,
|
||||||
});
|
},
|
||||||
|
convert: response => response.json(),
|
||||||
|
},
|
||||||
|
data => { keyImportBodyInput.textContent = data.key; },
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function importPgpKey() {
|
function importPgpKey() {
|
||||||
const key = keyImportFingerprintInput.val();
|
const key = keyImportFingerprintInput.value;
|
||||||
const server = keyImportServerInput.val();
|
const server = keyImportServerInput.value;
|
||||||
|
|
||||||
if (key && server) {
|
if (key && server) {
|
||||||
$.ajax({
|
makeRequest(
|
||||||
url: "/api/v1/service/pgp",
|
"/api/v1/service/pgp",
|
||||||
data: JSON.stringify({key: key, server: server}),
|
{
|
||||||
type: "POST",
|
method: "POST",
|
||||||
contentType: "application/json",
|
json: {
|
||||||
success: _ => {
|
key: key,
|
||||||
keyImportModal.modal("hide");
|
server: server,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
bootstrap.Modal.getOrCreateInstance(keyImportModal).hide();
|
||||||
showSuccess("Success", `Key ${key} has been imported`);
|
showSuccess("Success", `Key ${key} has been imported`);
|
||||||
},
|
},
|
||||||
error: (jqXHR, _, errorThrown) => {
|
error => {
|
||||||
const message = _ => `Could not import key ${key} from ${server}`;
|
const message = _ => `Could not import key ${key} from ${server}`;
|
||||||
showFailure("Action failed", message, jqXHR, errorThrown);
|
showFailure("Action failed", message, error);
|
||||||
},
|
},
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$(_ => {
|
ready(_ => {
|
||||||
keyImportModal.on("hidden.bs.modal", _ => {
|
keyImportModal.addEventListener("hidden.bs.modal", _ => {
|
||||||
keyImportBodyInput.text("");
|
keyImportBodyInput.textContent = "";
|
||||||
keyImportForm.trigger("reset");
|
keyImportForm.reset();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -34,53 +34,57 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const loginModal = $("#login-modal");
|
const loginModal = document.getElementById("login-modal");
|
||||||
const loginForm = $("#login-form");
|
const loginForm = document.getElementById("login-form");
|
||||||
|
|
||||||
const loginPasswordInput = $("#login-password");
|
const loginPasswordInput = document.getElementById("login-password");
|
||||||
const loginUsernameInput = $("#login-username");
|
const loginUsernameInput = document.getElementById("login-username");
|
||||||
const showHidePasswordButton = $("#login-show-hide-password-button");
|
const showHidePasswordButton = document.getElementById("login-show-hide-password-button");
|
||||||
|
|
||||||
function login() {
|
function login() {
|
||||||
const password = loginPasswordInput.val();
|
const password = loginPasswordInput.value;
|
||||||
const username = loginUsernameInput.val();
|
const username = loginUsernameInput.value;
|
||||||
|
|
||||||
if (username && password) {
|
if (username && password) {
|
||||||
$.ajax({
|
makeRequest(
|
||||||
url: "/api/v1/login",
|
"/api/v1/login",
|
||||||
data: JSON.stringify({username: username, password: password}),
|
{
|
||||||
type: "POST",
|
method: "POST",
|
||||||
contentType: "application/json",
|
json: {
|
||||||
success: _ => {
|
username: username,
|
||||||
loginModal.modal("hide");
|
password: password,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
bootstrap.Modal.getOrCreateInstance(loginModal).hide();
|
||||||
showSuccess("Logged in", `Successfully logged in as ${username}`, _ => location.href = "/");
|
showSuccess("Logged in", `Successfully logged in as ${username}`, _ => location.href = "/");
|
||||||
},
|
},
|
||||||
error: (jqXHR, _, errorThrown) => {
|
error => {
|
||||||
const message = _ =>
|
const message = _ =>
|
||||||
username === "admin" && password === "admin"
|
username === "admin" && password === "admin"
|
||||||
? "You've entered a password for user \"root\", did you make a typo in username?"
|
? "You've entered a password for user \"root\", did you make a typo in username?"
|
||||||
: `Could not login as ${username}`;
|
: `Could not login as ${username}`;
|
||||||
showFailure("Login error", message, jqXHR, errorThrown);
|
showFailure("Login error", message, error);
|
||||||
},
|
},
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function showPassword() {
|
function showPassword() {
|
||||||
if (loginPasswordInput.attr("type") === "password") {
|
if (loginPasswordInput.getAttribute("type") === "password") {
|
||||||
loginPasswordInput.attr("type", "text");
|
loginPasswordInput.setAttribute("type", "text");
|
||||||
showHidePasswordButton.removeClass("bi-eye");
|
showHidePasswordButton.classList.remove("bi-eye");
|
||||||
showHidePasswordButton.addClass("bi-eye-slash");
|
showHidePasswordButton.classList.add("bi-eye-slash");
|
||||||
} else {
|
} else {
|
||||||
loginPasswordInput.attr("type", "password");
|
loginPasswordInput.setAttribute("type", "password");
|
||||||
showHidePasswordButton.removeClass("bi-eye-slash");
|
showHidePasswordButton.classList.remove("bi-eye-slash");
|
||||||
showHidePasswordButton.addClass("bi-eye");
|
showHidePasswordButton.classList.add("bi-eye");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$(_ => {
|
ready(_ => {
|
||||||
loginModal.on("hidden.bs.modal", _ => {
|
loginModal.addEventListener("hidden.bs.modal", _ => {
|
||||||
loginForm.trigger("reset");
|
loginForm.reset();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -41,14 +41,14 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const packageAddModal = $("#package-add-modal");
|
const packageAddModal = document.getElementById("package-add-modal");
|
||||||
const packageAddForm = $("#package-add-form");
|
const packageAddForm = document.getElementById("package-add-form");
|
||||||
|
|
||||||
const packageAddInput = $("#package-add-input");
|
const packageAddInput = document.getElementById("package-add-input");
|
||||||
const packageAddRepositoryInput = $("#package-add-repository-input");
|
const packageAddRepositoryInput = document.getElementById("package-add-repository-input");
|
||||||
const packageAddKnownPackagesList = $("#package-add-known-packages-dlist");
|
const packageAddKnownPackagesList = document.getElementById("package-add-known-packages-dlist");
|
||||||
|
|
||||||
const packageAddVariablesDiv = $("#package-add-variables-div");
|
const packageAddVariablesDiv = document.getElementById("package-add-variables-div");
|
||||||
|
|
||||||
function packageAddVariableInputCreate() {
|
function packageAddVariableInputCreate() {
|
||||||
const variableInput = document.createElement("div");
|
const variableInput = document.createElement("div");
|
||||||
@ -78,7 +78,7 @@
|
|||||||
variableButtonRemove.classList.add("btn");
|
variableButtonRemove.classList.add("btn");
|
||||||
variableButtonRemove.classList.add("btn-outline-danger");
|
variableButtonRemove.classList.add("btn-outline-danger");
|
||||||
variableButtonRemove.innerHTML = "<i class=\"bi bi-trash\"></i>";
|
variableButtonRemove.innerHTML = "<i class=\"bi bi-trash\"></i>";
|
||||||
variableButtonRemove.onclick = _ => { return variableInput.remove(); };
|
variableButtonRemove.onclick = _ => { variableInput.remove(); };
|
||||||
|
|
||||||
// bring them together
|
// bring them together
|
||||||
variableInput.appendChild(variableNameInput);
|
variableInput.appendChild(variableNameInput);
|
||||||
@ -86,27 +86,26 @@
|
|||||||
variableInput.appendChild(variableValueInput);
|
variableInput.appendChild(variableValueInput);
|
||||||
variableInput.appendChild(variableButtonRemove);
|
variableInput.appendChild(variableButtonRemove);
|
||||||
|
|
||||||
packageAddVariablesDiv.append(variableInput);
|
packageAddVariablesDiv.appendChild(variableInput);
|
||||||
}
|
}
|
||||||
|
|
||||||
function patchesParse() {
|
function patchesParse() {
|
||||||
const patches = packageAddVariablesDiv.find(".package-add-variable").map((_, element) => {
|
const patches = Array.from(packageAddVariablesDiv.getElementsByClassName("package-add-variable")).map(element => {
|
||||||
const richElement = $(element);
|
|
||||||
return {
|
return {
|
||||||
key: richElement.find(".package-add-variable-name").val(),
|
key: element.querySelector(".package-add-variable-name").value,
|
||||||
value: richElement.find(".package-add-variable-value").val(),
|
value: element.querySelector(".package-add-variable-value").value,
|
||||||
};
|
};
|
||||||
}).filter((_, patch) => patch.key).get();
|
}).filter(patch => patch.key);
|
||||||
return {patches: patches};
|
return {patches: patches};
|
||||||
}
|
}
|
||||||
|
|
||||||
function packagesAdd(packages, patches, repository) {
|
function packagesAdd(packages, patches, repository) {
|
||||||
packages = packages ?? packageAddInput.val();
|
packages = packages ?? packageAddInput.value;
|
||||||
patches = patches ?? patchesParse();
|
patches = patches ?? patchesParse();
|
||||||
repository = repository ?? getRepositorySelector(packageAddRepositoryInput);
|
repository = repository ?? getRepositorySelector(packageAddRepositoryInput);
|
||||||
|
|
||||||
if (packages) {
|
if (packages) {
|
||||||
packageAddModal.modal("hide");
|
bootstrap.Modal.getOrCreateInstance(packageAddModal).hide();
|
||||||
const onSuccess = update => `Packages ${update} have been added`;
|
const onSuccess = update => `Packages ${update} have been added`;
|
||||||
const onFailure = error => `Package addition failed: ${error}`;
|
const onFailure = error => `Package addition failed: ${error}`;
|
||||||
doPackageAction("/api/v1/service/add", [packages], repository, onSuccess, onFailure, patches);
|
doPackageAction("/api/v1/service/add", [packages], repository, onSuccess, onFailure, patches);
|
||||||
@ -114,50 +113,54 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function packagesRequest(packages, patches) {
|
function packagesRequest(packages, patches) {
|
||||||
packages = packages ?? packageAddInput.val();
|
packages = packages ?? packageAddInput.value;
|
||||||
patches = patches ?? patchesParse();
|
patches = patches ?? patchesParse();
|
||||||
const repository = getRepositorySelector(packageAddRepositoryInput);
|
const repository = getRepositorySelector(packageAddRepositoryInput);
|
||||||
|
|
||||||
if (packages) {
|
if (packages) {
|
||||||
packageAddModal.modal("hide");
|
bootstrap.Modal.getOrCreateInstance(packageAddModal).hide();
|
||||||
const onSuccess = update => `Packages ${update} have been requested`;
|
const onSuccess = update => `Packages ${update} have been requested`;
|
||||||
const onFailure = error => `Package request failed: ${error}`;
|
const onFailure = error => `Package request failed: ${error}`;
|
||||||
doPackageAction("/api/v1/service/request", [packages], repository, onSuccess, onFailure, patches);
|
doPackageAction("/api/v1/service/request", [packages], repository, onSuccess, onFailure, patches);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$(_ => {
|
ready(_ => {
|
||||||
packageAddModal.on("shown.bs.modal", _ => {
|
packageAddModal.addEventListener("shown.bs.modal", _ => {
|
||||||
$(`#package-add-repository-input option[value="${repository.architecture}-${repository.repository}"]`).prop("selected", true);
|
const option = packageAddRepositoryInput.querySelector(`option[value="${repository.architecture}-${repository.repository}"]`);
|
||||||
|
option.selected = "selected";
|
||||||
});
|
});
|
||||||
packageAddModal.on("hidden.bs.modal", _ => {
|
packageAddModal.addEventListener("hidden.bs.modal", _ => {
|
||||||
packageAddVariablesDiv.empty();
|
packageAddVariablesDiv.replaceChildren();
|
||||||
packageAddForm.trigger("reset");
|
packageAddForm.reset();
|
||||||
});
|
});
|
||||||
|
|
||||||
packageAddInput.keyup(_ => {
|
packageAddInput.addEventListener("keyup", _ => {
|
||||||
clearTimeout(packageAddInput.data("timeout"));
|
clearTimeout(packageAddInput.requestTimeout);
|
||||||
packageAddInput.data("timeout", setTimeout($.proxy(_ => {
|
packageAddInput.requestTimeout = setTimeout(_ => {
|
||||||
const value = packageAddInput.val();
|
const value = packageAddInput.value;
|
||||||
|
|
||||||
if (value.length >= 3) {
|
if (value.length >= 3) {
|
||||||
$.ajax({
|
makeRequest(
|
||||||
url: "/api/v1/service/search",
|
"/api/v1/service/search",
|
||||||
data: {"for": value},
|
{
|
||||||
type: "GET",
|
query: {
|
||||||
dataType: "json",
|
for: value,
|
||||||
success: response => {
|
},
|
||||||
const options = response.map(pkg => {
|
convert: response => response.json(),
|
||||||
|
},
|
||||||
|
data => {
|
||||||
|
const options = data.map(pkg => {
|
||||||
const option = document.createElement("option");
|
const option = document.createElement("option");
|
||||||
option.value = pkg.package;
|
option.value = pkg.package;
|
||||||
option.innerText = `${pkg.package} (${pkg.description})`;
|
option.innerText = `${pkg.package} (${pkg.description})`;
|
||||||
return option;
|
return option;
|
||||||
});
|
});
|
||||||
packageAddKnownPackagesList.empty().append(options);
|
packageAddKnownPackagesList.replaceChildren(...options);
|
||||||
},
|
},
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
}, this), 500));
|
}, 500);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -58,7 +58,7 @@
|
|||||||
<pre class="language-diff"><code id="package-info-changes-input" class="pre-scrollable language-diff"></code><button id="package-info-changes-copy-button" type="button" class="btn language-diff" onclick="copyChanges()"><i class="bi bi-clipboard"></i> copy</button></pre>
|
<pre class="language-diff"><code id="package-info-changes-input" class="pre-scrollable language-diff"></code><button id="package-info-changes-copy-button" type="button" class="btn language-diff" onclick="copyChanges()"><i class="bi bi-clipboard"></i> copy</button></pre>
|
||||||
</div>
|
</div>
|
||||||
<div id="package-info-events" class="tab-pane fade" role="tabpanel" aria-labelledby="package-info-events-button" tabindex="0">
|
<div id="package-info-events" class="tab-pane fade" role="tabpanel" aria-labelledby="package-info-events-button" tabindex="0">
|
||||||
<canvas id="package-info-events-update-chart"></canvas>
|
<canvas id="package-info-events-update-chart" hidden></canvas>
|
||||||
<table id="package-info-events-table"
|
<table id="package-info-events-table"
|
||||||
data-classes="table table-hover"
|
data-classes="table table-hover"
|
||||||
data-sortable="true"
|
data-sortable="true"
|
||||||
@ -77,8 +77,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button id="package-info-update-button" type="submit" class="btn btn-success" onclick="packageInfoUpdate()" data-bs-dismiss="modal" hidden><i class="bi bi-play"></i><span class="d-none d-sm-inline"> update</span></button>
|
{% if not auth.enabled or auth.username is not none %}
|
||||||
<button id="package-info-remove-button" type="submit" class="btn btn-danger" onclick="packageInfoRemove()" data-bs-dismiss="modal" hidden><i class="bi bi-trash"></i><span class="d-none d-sm-inline"> remove</span></button>
|
<button id="package-info-update-button" type="submit" class="btn btn-success" onclick="packageInfoUpdate()" data-bs-dismiss="modal"><i class="bi bi-play"></i><span class="d-none d-sm-inline"> update</span></button>
|
||||||
|
<button id="package-info-remove-button" type="submit" class="btn btn-danger" onclick="packageInfoRemove()" data-bs-dismiss="modal"><i class="bi bi-trash"></i><span class="d-none d-sm-inline"> remove</span></button>
|
||||||
|
{% endif %}
|
||||||
<button type="button" class="btn btn-secondary" onclick="showPackageInfo()"><i class="bi bi-arrow-clockwise"></i><span class="d-none d-sm-inline"> reload</span></button>
|
<button type="button" class="btn btn-secondary" onclick="showPackageInfo()"><i class="bi bi-arrow-clockwise"></i><span class="d-none d-sm-inline"> reload</span></button>
|
||||||
<button type="button" class="btn btn-primary" data-bs-dismiss="modal"><i class="bi bi-x"></i><span class="d-none d-sm-inline"> close</span></button>
|
<button type="button" class="btn btn-primary" data-bs-dismiss="modal"><i class="bi bi-x"></i><span class="d-none d-sm-inline"> close</span></button>
|
||||||
</div>
|
</div>
|
||||||
@ -87,33 +89,35 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const packageInfoModal = $("#package-info-modal");
|
const packageInfoModal = document.getElementById("package-info-modal");
|
||||||
const packageInfoModalHeader = $("#package-info-modal-header");
|
const packageInfoModalHeader = document.getElementById("package-info-modal-header");
|
||||||
const packageInfo = $("#package-info");
|
const packageInfo = document.getElementById("package-info");
|
||||||
|
|
||||||
const packageInfoLogsInput = $("#package-info-logs-input");
|
const packageInfoLogsInput = document.getElementById("package-info-logs-input");
|
||||||
const packageInfoLogsCopyButton = $("#package-info-logs-copy-button");
|
const packageInfoLogsCopyButton = document.getElementById("package-info-logs-copy-button");
|
||||||
|
|
||||||
const packageInfoChangesInput = $("#package-info-changes-input");
|
const packageInfoChangesInput = document.getElementById("package-info-changes-input");
|
||||||
const packageInfoChangesCopyButton = $("#package-info-changes-copy-button");
|
const packageInfoChangesCopyButton = document.getElementById("package-info-changes-copy-button");
|
||||||
|
|
||||||
const packageInfoEventsTable = $("#package-info-events-table");
|
// so far bootstrap-table only operates with jquery elements
|
||||||
|
const packageInfoEventsTable = $(document.getElementById("package-info-events-table"));
|
||||||
const packageInfoEventsUpdateChartCanvas = document.getElementById("package-info-events-update-chart");
|
const packageInfoEventsUpdateChartCanvas = document.getElementById("package-info-events-update-chart");
|
||||||
let packageInfoEventsUpdateChart = null;
|
let packageInfoEventsUpdateChart = null;
|
||||||
|
|
||||||
const packageInfoAurUrl = $("#package-info-aur-url");
|
const packageInfoAurUrl = document.getElementById("package-info-aur-url");
|
||||||
const packageInfoDepends = $("#package-info-depends");
|
const packageInfoDepends = document.getElementById("package-info-depends");
|
||||||
const packageInfoGroups = $("#package-info-groups");
|
const packageInfoGroups = document.getElementById("package-info-groups");
|
||||||
const packageInfoLicenses = $("#package-info-licenses");
|
const packageInfoLicenses = document.getElementById("package-info-licenses");
|
||||||
const packageInfoPackager = $("#package-info-packager");
|
const packageInfoPackager = document.getElementById("package-info-packager");
|
||||||
const packageInfoPackages = $("#package-info-packages");
|
const packageInfoPackages = document.getElementById("package-info-packages");
|
||||||
const packageInfoUpstreamUrl = $("#package-info-upstream-url");
|
const packageInfoUpstreamUrl = document.getElementById("package-info-upstream-url");
|
||||||
const packageInfoVersion = $("#package-info-version");
|
const packageInfoVersion = document.getElementById("package-info-version");
|
||||||
|
|
||||||
const packageInfoVariablesBlock = $("#package-info-variables-block");
|
const packageInfoVariablesBlock = document.getElementById("package-info-variables-block");
|
||||||
const packageInfoVariablesDiv = $("#package-info-variables-div");
|
const packageInfoVariablesDiv = document.getElementById("package-info-variables-div");
|
||||||
|
|
||||||
function clearChart() {
|
function clearChart() {
|
||||||
|
packageInfoEventsUpdateChartCanvas.hidden = true;
|
||||||
if (packageInfoEventsUpdateChart) {
|
if (packageInfoEventsUpdateChart) {
|
||||||
packageInfoEventsUpdateChart.data = {};
|
packageInfoEventsUpdateChart.data = {};
|
||||||
packageInfoEventsUpdateChart.update();
|
packageInfoEventsUpdateChart.update();
|
||||||
@ -121,20 +125,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function copyChanges() {
|
async function copyChanges() {
|
||||||
const changes = packageInfoChangesInput.text();
|
const changes = packageInfoChangesInput.textContent;
|
||||||
await copyToClipboard(changes, packageInfoChangesCopyButton);
|
await copyToClipboard(changes, packageInfoChangesCopyButton);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function copyLogs() {
|
async function copyLogs() {
|
||||||
const logs = packageInfoLogsInput.text();
|
const logs = packageInfoLogsInput.textContent;
|
||||||
await copyToClipboard(logs, packageInfoLogsCopyButton);
|
await copyToClipboard(logs, packageInfoLogsCopyButton);
|
||||||
}
|
}
|
||||||
|
|
||||||
function hideInfoControls(hidden) {
|
|
||||||
packageInfoRemoveButton.attr("hidden", hidden);
|
|
||||||
packageInfoUpdateButton.attr("hidden", hidden);
|
|
||||||
}
|
|
||||||
|
|
||||||
function highlight(element) {
|
function highlight(element) {
|
||||||
delete element.dataset.highlighted;
|
delete element.dataset.highlighted;
|
||||||
hljs.highlightElement(element);
|
hljs.highlightElement(element);
|
||||||
@ -164,12 +163,13 @@
|
|||||||
variableButtonRemove.classList.add("btn-outline-danger");
|
variableButtonRemove.classList.add("btn-outline-danger");
|
||||||
variableButtonRemove.innerHTML = "<i class=\"bi bi-trash\"></i>";
|
variableButtonRemove.innerHTML = "<i class=\"bi bi-trash\"></i>";
|
||||||
variableButtonRemove.onclick = _ => {
|
variableButtonRemove.onclick = _ => {
|
||||||
$.ajax({
|
makeRequest(
|
||||||
url: `/api/v1/packages/${packageBase}/patches/${variable.key}`,
|
`/api/v1/packages/${packageBase}/patches/${variable.key}`,
|
||||||
type: "DELETE",
|
{
|
||||||
dataType: "json",
|
method: "DELETE",
|
||||||
success: _ => variableInput.remove(),
|
},
|
||||||
});
|
_ => variableInput.remove(),
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// bring them together
|
// bring them together
|
||||||
@ -178,52 +178,57 @@
|
|||||||
variableInput.appendChild(variableValueInput);
|
variableInput.appendChild(variableValueInput);
|
||||||
variableInput.appendChild(variableButtonRemove);
|
variableInput.appendChild(variableButtonRemove);
|
||||||
|
|
||||||
packageInfoVariablesDiv.append(variableInput);
|
packageInfoVariablesDiv.appendChild(variableInput);
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadChanges(packageBase, onFailure) {
|
function loadChanges(packageBase, onFailure) {
|
||||||
$.ajax({
|
makeRequest(
|
||||||
url: `/api/v1/packages/${packageBase}/changes`,
|
`/api/v1/packages/${packageBase}/changes`,
|
||||||
data: {
|
{
|
||||||
architecture: repository.architecture,
|
query: {
|
||||||
repository: repository.repository,
|
architecture: repository.architecture,
|
||||||
|
repository: repository.repository,
|
||||||
|
},
|
||||||
|
convert: response => response.json(),
|
||||||
},
|
},
|
||||||
type: "GET",
|
data => {
|
||||||
dataType: "json",
|
const changes = data.changes;
|
||||||
success: response => {
|
packageInfoChangesInput.textContent = changes ?? "";
|
||||||
const changes = response.changes;
|
highlight(packageInfoChangesInput);
|
||||||
packageInfoChangesInput.text(changes || "");
|
|
||||||
packageInfoChangesInput.map((_, el) => highlight(el));
|
|
||||||
},
|
},
|
||||||
error: onFailure,
|
onFailure,
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadEvents(packageBase, onFailure) {
|
function loadEvents(packageBase, onFailure) {
|
||||||
packageInfoEventsTable.bootstrapTable("showLoading");
|
packageInfoEventsTable.bootstrapTable("showLoading");
|
||||||
clearChart();
|
clearChart();
|
||||||
|
|
||||||
$.ajax({
|
makeRequest(
|
||||||
url: `/api/v1/events`,
|
"/api/v1/events",
|
||||||
data: {
|
{
|
||||||
architecture: repository.architecture,
|
query: {
|
||||||
repository: repository.repository,
|
architecture: repository.architecture,
|
||||||
object_id: packageBase,
|
repository: repository.repository,
|
||||||
limit: 30,
|
object_id: packageBase,
|
||||||
|
limit: 30,
|
||||||
|
},
|
||||||
|
convert: response => response.json(),
|
||||||
},
|
},
|
||||||
type: "GET",
|
data => {
|
||||||
dataType: "json",
|
const events = data.map(event => {
|
||||||
success: response => {
|
|
||||||
const events = response.map(event => {
|
|
||||||
return {
|
return {
|
||||||
timestamp: new Date(1000 * event.created).toISOStringShort(),
|
timestamp: new Date(1000 * event.created).toISOStringShort(),
|
||||||
event: event.event,
|
event: event.event,
|
||||||
message: event.message || "",
|
message: event.message || "",
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
const chart = data.filter(event => event.event === "package-updated");
|
||||||
|
|
||||||
|
packageInfoEventsTable.bootstrapTable("load", events);
|
||||||
|
packageInfoEventsTable.bootstrapTable("hideLoading");
|
||||||
|
|
||||||
if (packageInfoEventsUpdateChart) {
|
if (packageInfoEventsUpdateChart) {
|
||||||
const chart = response.filter(event => event.event === "package-updated");
|
|
||||||
packageInfoEventsUpdateChart.config.data = {
|
packageInfoEventsUpdateChart.config.data = {
|
||||||
labels: chart.map(event => new Date(1000 * event.created).toISOStringShort()),
|
labels: chart.map(event => new Date(1000 * event.created).toISOStringShort()),
|
||||||
datasets: [{
|
datasets: [{
|
||||||
@ -235,32 +240,31 @@
|
|||||||
};
|
};
|
||||||
packageInfoEventsUpdateChart.update();
|
packageInfoEventsUpdateChart.update();
|
||||||
}
|
}
|
||||||
|
packageInfoEventsUpdateChartCanvas.hidden = !chart.length;
|
||||||
packageInfoEventsTable.bootstrapTable("load", events);
|
|
||||||
packageInfoEventsTable.bootstrapTable("hideLoading");
|
|
||||||
},
|
},
|
||||||
error: onFailure,
|
onFailure,
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadLogs(packageBase, onFailure) {
|
function loadLogs(packageBase, onFailure) {
|
||||||
$.ajax({
|
makeRequest(
|
||||||
url: `/api/v2/packages/${packageBase}/logs`,
|
`/api/v2/packages/${packageBase}/logs`,
|
||||||
data: {
|
{
|
||||||
architecture: repository.architecture,
|
query: {
|
||||||
repository: repository.repository,
|
architecture: repository.architecture,
|
||||||
|
repository: repository.repository,
|
||||||
|
},
|
||||||
|
convert: response => response.json(),
|
||||||
},
|
},
|
||||||
type: "GET",
|
data => {
|
||||||
dataType: "json",
|
const logs = data.map(log_record => {
|
||||||
success: response => {
|
|
||||||
const logs = response.map(log_record => {
|
|
||||||
return `[${new Date(1000 * log_record.created).toISOString()}] ${log_record.message}`;
|
return `[${new Date(1000 * log_record.created).toISOString()}] ${log_record.message}`;
|
||||||
});
|
});
|
||||||
packageInfoLogsInput.text(logs.join("\n"));
|
packageInfoLogsInput.textContent = logs.join("\n");
|
||||||
packageInfoLogsInput.map((_, el) => highlight(el));
|
highlight(packageInfoLogsInput);
|
||||||
},
|
},
|
||||||
error: onFailure,
|
onFailure,
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadPackage(packageBase, onFailure) {
|
function loadPackage(packageBase, onFailure) {
|
||||||
@ -272,16 +276,17 @@
|
|||||||
return ["bg-secondary", "text-white"];
|
return ["bg-secondary", "text-white"];
|
||||||
};
|
};
|
||||||
|
|
||||||
$.ajax({
|
makeRequest(
|
||||||
url: `/api/v1/packages/${packageBase}`,
|
`/api/v1/packages/${packageBase}`,
|
||||||
data: {
|
{
|
||||||
architecture: repository.architecture,
|
query: {
|
||||||
repository: repository.repository,
|
architecture: repository.architecture,
|
||||||
|
repository: repository.repository,
|
||||||
|
},
|
||||||
|
convert: response => response.json(),
|
||||||
},
|
},
|
||||||
type: "GET",
|
data => {
|
||||||
dataType: "json",
|
const description = data.find(Boolean);
|
||||||
success: response => {
|
|
||||||
const description = response.find(Boolean);
|
|
||||||
const packages = Object.keys(description.package.packages);
|
const packages = Object.keys(description.package.packages);
|
||||||
const aurUrl = description.package.remote.web_url;
|
const aurUrl = description.package.remote.web_url;
|
||||||
const upstreamUrls = Array.from(
|
const upstreamUrls = Array.from(
|
||||||
@ -291,72 +296,71 @@
|
|||||||
)
|
)
|
||||||
).sort();
|
).sort();
|
||||||
|
|
||||||
packageInfo.text(`${description.package.base} ${description.status.status} at ${new Date(1000 * description.status.timestamp).toISOStringShort()}`);
|
packageInfo.textContent = `${description.package.base} ${description.status.status} at ${new Date(1000 * description.status.timestamp).toISOStringShort()}`;
|
||||||
|
|
||||||
packageInfoModalHeader.removeClass();
|
packageInfoModalHeader.classList.remove(...packageInfoModalHeader.classList);
|
||||||
packageInfoModalHeader.addClass("modal-header");
|
packageInfoModalHeader.classList.add("modal-header");
|
||||||
headerClass(description.status.status).forEach(clz => packageInfoModalHeader.addClass(clz));
|
headerClass(description.status.status).forEach(clz => packageInfoModalHeader.classList.add(clz));
|
||||||
|
|
||||||
packageInfoAurUrl.html(aurUrl ? safeLink(aurUrl, aurUrl, "AUR link").outerHTML : "");
|
packageInfoAurUrl.innerHTML = aurUrl ? safeLink(aurUrl, aurUrl, "AUR link").outerHTML : "";
|
||||||
packageInfoDepends.html(listToTable(
|
packageInfoDepends.innerHTML = listToTable(
|
||||||
Object.values(description.package.packages)
|
Object.values(description.package.packages)
|
||||||
.reduce((accumulator, currentValue) => {
|
.reduce((accumulator, currentValue) => {
|
||||||
return accumulator.concat(currentValue.depends.filter(v => packages.indexOf(v) === -1))
|
return accumulator.concat(currentValue.depends.filter(v => packages.indexOf(v) === -1))
|
||||||
.concat(currentValue.make_depends.filter(v => packages.indexOf(v) === -1).map(v => `${v} (make)`))
|
.concat(currentValue.make_depends.filter(v => packages.indexOf(v) === -1).map(v => `${v} (make)`))
|
||||||
.concat(currentValue.opt_depends.filter(v => packages.indexOf(v) === -1).map(v => `${v} (optional)`));
|
.concat(currentValue.opt_depends.filter(v => packages.indexOf(v) === -1).map(v => `${v} (optional)`));
|
||||||
}, [])
|
}, [])
|
||||||
));
|
);
|
||||||
packageInfoGroups.html(listToTable(extractListProperties(description.package, "groups")));
|
packageInfoGroups.innerHTML = listToTable(extractListProperties(description.package, "groups"));
|
||||||
packageInfoLicenses.html(listToTable(extractListProperties(description.package, "licenses")));
|
packageInfoLicenses.innerHTML = listToTable(extractListProperties(description.package, "licenses"));
|
||||||
packageInfoPackager.text(description.package.packager);
|
packageInfoPackager.textContent = description.package.packager;
|
||||||
packageInfoPackages.html(listToTable(packages));
|
packageInfoPackages.innerHTML = listToTable(packages);
|
||||||
packageInfoUpstreamUrl.html(upstreamUrls.map(url => safeLink(url, url, "upstream link").outerHTML).join("<br>"));
|
packageInfoUpstreamUrl.innerHTML = upstreamUrls.map(url => safeLink(url, url, "upstream link").outerHTML).join("<br>");
|
||||||
packageInfoVersion.text(description.package.version);
|
packageInfoVersion.textContent = description.package.version;
|
||||||
|
|
||||||
hideInfoControls(false);
|
|
||||||
},
|
},
|
||||||
error: (jqXHR, _, errorThrown) => {
|
onFailure,
|
||||||
hideInfoControls(true);
|
);
|
||||||
onFailure(jqXHR, null, errorThrown);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadPatches(packageBase, onFailure) {
|
function loadPatches(packageBase, onFailure) {
|
||||||
$.ajax({
|
makeRequest(
|
||||||
url: `/api/v1/packages/${packageBase}/patches`,
|
`/api/v1/packages/${packageBase}/patches`,
|
||||||
type: "GET",
|
{
|
||||||
dataType: "json",
|
convert: response => response.json(),
|
||||||
success: response => {
|
|
||||||
packageInfoVariablesDiv.empty();
|
|
||||||
response.map(patch => insertVariable(packageBase, patch));
|
|
||||||
packageInfoVariablesBlock.attr("hidden", response.length === 0);
|
|
||||||
},
|
},
|
||||||
error: onFailure,
|
data => {
|
||||||
});
|
packageInfoVariablesDiv.replaceChildren();
|
||||||
|
data.map(patch => insertVariable(packageBase, patch));
|
||||||
|
packageInfoVariablesBlock.hidden = !data.length;
|
||||||
|
},
|
||||||
|
onFailure,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function packageInfoRemove() {
|
function packageInfoRemove() {
|
||||||
const packageBase = packageInfoModal.data("package");
|
const packageBase = packageInfoModal.package;
|
||||||
if (packageBase) return packagesRemove([packageBase]);
|
packagesRemove([packageBase]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function packageInfoUpdate() {
|
function packageInfoUpdate() {
|
||||||
const packageBase = packageInfoModal.data("package");
|
const packageBase = packageInfoModal.package;
|
||||||
if (packageBase) return packagesAdd(packageBase, [], repository);
|
packagesAdd(packageBase, [], repository);
|
||||||
}
|
}
|
||||||
|
|
||||||
function showPackageInfo(packageBase) {
|
function showPackageInfo(packageBase) {
|
||||||
const isPackageBaseSet = packageBase !== undefined;
|
const isPackageBaseSet = packageBase !== undefined;
|
||||||
if (isPackageBaseSet)
|
if (isPackageBaseSet) {
|
||||||
packageInfoModal.data("package", packageBase); // set package base as currently used
|
// set package base as currently used
|
||||||
else
|
packageInfoModal.package = packageBase;
|
||||||
packageBase = packageInfoModal.data("package"); // read package base from the current window attribute
|
} else {
|
||||||
|
// read package base from the current window attribute
|
||||||
|
packageBase = packageInfoModal.package;
|
||||||
|
}
|
||||||
|
|
||||||
const onFailure = (jqXHR, _, errorThrown) => {
|
const onFailure = error => {
|
||||||
if (isPackageBaseSet) {
|
if (isPackageBaseSet) {
|
||||||
const message = error => `Could not load package ${packageBase} info: ${error}`;
|
const message = details => `Could not load package ${packageBase} info: ${details}`;
|
||||||
showFailure("Load failure", message, jqXHR, errorThrown);
|
showFailure("Load failure", message, error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -366,10 +370,12 @@
|
|||||||
loadChanges(packageBase, onFailure);
|
loadChanges(packageBase, onFailure);
|
||||||
loadEvents(packageBase, onFailure);
|
loadEvents(packageBase, onFailure);
|
||||||
|
|
||||||
if (isPackageBaseSet) packageInfoModal.modal("show");
|
if (isPackageBaseSet) {
|
||||||
|
bootstrap.Modal.getOrCreateInstance(packageInfoModal).show();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$(_ => {
|
ready(_ => {
|
||||||
packageInfoEventsUpdateChart = new Chart(packageInfoEventsUpdateChartCanvas, {
|
packageInfoEventsUpdateChart = new Chart(packageInfoEventsUpdateChartCanvas, {
|
||||||
type: "line",
|
type: "line",
|
||||||
data: {},
|
data: {},
|
||||||
@ -378,27 +384,23 @@
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
packageInfoModal.on("hidden.bs.modal", _ => {
|
packageInfoModal.addEventListener("hidden.bs.modal", _ => {
|
||||||
packageInfoAurUrl.empty();
|
packageInfoAurUrl.textContent = "";
|
||||||
packageInfoDepends.empty();
|
packageInfoDepends.textContent = "";
|
||||||
packageInfoGroups.empty();
|
packageInfoGroups.textContent = "";
|
||||||
packageInfoLicenses.empty();
|
packageInfoLicenses.textContent = "";
|
||||||
packageInfoPackager.empty();
|
packageInfoPackager.textContent = "";
|
||||||
packageInfoPackages.empty();
|
packageInfoPackages.textContent = "";
|
||||||
packageInfoUpstreamUrl.empty();
|
packageInfoUpstreamUrl.textContent = "";
|
||||||
packageInfoVersion.empty();
|
packageInfoVersion.textContent = "";
|
||||||
|
|
||||||
packageInfoVariablesBlock.attr("hidden", true);
|
packageInfoVariablesBlock.hidden = true;
|
||||||
packageInfoVariablesDiv.empty();
|
packageInfoVariablesDiv.replaceChildren();
|
||||||
|
|
||||||
packageInfoLogsInput.empty();
|
packageInfoLogsInput.textContent = "";
|
||||||
packageInfoChangesInput.empty();
|
packageInfoChangesInput.textContent = "";
|
||||||
packageInfoEventsTable.bootstrapTable("load", []);
|
packageInfoEventsTable.bootstrapTable("load", []);
|
||||||
clearChart();
|
clearChart();
|
||||||
|
|
||||||
packageInfoModal.trigger("reset");
|
|
||||||
|
|
||||||
hideInfoControls(true);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -33,28 +33,31 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const packageRebuildModal = $("#package-rebuild-modal");
|
const packageRebuildModal = document.getElementById("package-rebuild-modal");
|
||||||
const packageRebuildForm = $("#package-rebuild-form");
|
const packageRebuildForm = document.getElementById("package-rebuild-form");
|
||||||
|
|
||||||
const packageRebuildDependencyInput = $("#package-rebuild-dependency-input");
|
const packageRebuildDependencyInput = document.getElementById("package-rebuild-dependency-input");
|
||||||
const packageRebuildRepositoryInput = $("#package-rebuild-repository-input");
|
const packageRebuildRepositoryInput = document.getElementById("package-rebuild-repository-input");
|
||||||
|
|
||||||
function packagesRebuild() {
|
function packagesRebuild() {
|
||||||
const packages = packageRebuildDependencyInput.val();
|
const packages = packageRebuildDependencyInput.value;
|
||||||
const repository = getRepositorySelector(packageRebuildRepositoryInput);
|
const repository = getRepositorySelector(packageRebuildRepositoryInput);
|
||||||
if (packages) {
|
if (packages) {
|
||||||
packageRebuildModal.modal("hide");
|
bootstrap.Modal.getOrCreateInstance(packageRebuildModal).hide();
|
||||||
const onSuccess = update => `Repository rebuild has been run for packages which depend on ${update}`;
|
const onSuccess = update => `Repository rebuild has been run for packages which depend on ${update}`;
|
||||||
const onFailure = error => `Repository rebuild failed: ${error}`;
|
const onFailure = error => `Repository rebuild failed: ${error}`;
|
||||||
doPackageAction("/api/v1/service/rebuild", [packages], repository, onSuccess, onFailure);
|
doPackageAction("/api/v1/service/rebuild", [packages], repository, onSuccess, onFailure);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$(_ => {
|
ready(_ => {
|
||||||
packageRebuildModal.on("shown.bs.modal", _ => {
|
packageRebuildModal.addEventListener("shown.bs.modal", _ => {
|
||||||
$(`#package-rebuild-repository-input option[value="${repository.architecture}-${repository.repository}"]`).prop("selected", true);
|
const option = packageRebuildRepositoryInput.querySelector(`option[value="${repository.architecture}-${repository.repository}"]`);
|
||||||
|
option.selected = "selected";
|
||||||
|
|
||||||
});
|
});
|
||||||
packageRebuildModal.on("hidden.bs.modal", _ => { packageRebuildForm.trigger("reset"); });
|
packageRebuildModal.addEventListener("hidden.bs.modal", _ => {
|
||||||
|
packageRebuildForm.reset();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,39 +1,34 @@
|
|||||||
<script>
|
<script>
|
||||||
const keyImportButton = $("#key-import-button");
|
const packageRemoveButton = document.getElementById("package-remove-button");
|
||||||
const packageAddButton = $("#package-add-button");
|
const packageUpdateButton = document.getElementById("package-update-button");
|
||||||
const packageRebuildButton = $("#package-rebuild-button");
|
|
||||||
const packageRemoveButton = $("#package-remove-button");
|
|
||||||
const packageUpdateButton = $("#package-update-button");
|
|
||||||
|
|
||||||
const packageInfoRemoveButton = $("#package-info-remove-button");
|
|
||||||
const packageInfoUpdateButton = $("#package-info-update-button");
|
|
||||||
|
|
||||||
let repository = null;
|
let repository = null;
|
||||||
|
|
||||||
const table = $("#packages");
|
// so far bootstrap-table only operates with jquery elements
|
||||||
|
const table = $(document.getElementById("packages"));
|
||||||
|
|
||||||
const statusBadge = $("#badge-status");
|
const statusBadge = document.getElementById("badge-status");
|
||||||
const versionBadge = $("#badge-version");
|
const versionBadge = document.getElementById("badge-version");
|
||||||
|
|
||||||
function doPackageAction(uri, packages, repository, successText, failureText, data) {
|
function doPackageAction(uri, packages, repository, successText, failureText, data) {
|
||||||
const queryParams = $.param({
|
makeRequest(
|
||||||
architecture: repository.architecture,
|
uri,
|
||||||
repository: repository.repository,
|
{
|
||||||
}); // it will never be empty btw
|
method: "POST",
|
||||||
|
query: {
|
||||||
$.ajax({
|
architecture: repository.architecture,
|
||||||
url: `${uri}?${queryParams}`,
|
repository: repository.repository,
|
||||||
data: JSON.stringify(Object.assign({}, {packages: packages}, data || {})),
|
},
|
||||||
type: "POST",
|
json: Object.assign({}, {packages: packages}, data || {}),
|
||||||
contentType: "application/json",
|
},
|
||||||
success: _ => {
|
_ => {
|
||||||
const message = successText(packages.join(", "));
|
const message = successText(packages.join(", "));
|
||||||
showSuccess("Success", message);
|
showSuccess("Success", message);
|
||||||
},
|
},
|
||||||
error: (jqXHR, _, errorThrown) => {
|
error => {
|
||||||
showFailure("Action failed", failureText, jqXHR, errorThrown);
|
showFailure("Action failed", failureText, error);
|
||||||
},
|
},
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function filterListGroups() {
|
function filterListGroups() {
|
||||||
@ -49,10 +44,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getRepositorySelector(selector) {
|
function getRepositorySelector(selector) {
|
||||||
const selected = selector.find(":selected");
|
const selected = selector.options[selector.selectedIndex];
|
||||||
return {
|
return {
|
||||||
architecture: selected.data("architecture"),
|
architecture: selected.getAttribute("data-architecture"),
|
||||||
repository: selected.data("repository"),
|
repository: selected.getAttribute("data-repository"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,14 +55,6 @@
|
|||||||
return table.bootstrapTable("getSelections").map(row => row.id);
|
return table.bootstrapTable("getSelections").map(row => row.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
function hideControls(hidden) {
|
|
||||||
keyImportButton.attr("hidden", hidden);
|
|
||||||
packageAddButton.attr("hidden", hidden);
|
|
||||||
packageRebuildButton.attr("hidden", hidden);
|
|
||||||
packageRemoveButton.attr("hidden", hidden);
|
|
||||||
packageUpdateButton.attr("hidden", hidden);
|
|
||||||
}
|
|
||||||
|
|
||||||
function packagesRemove(packages) {
|
function packagesRemove(packages) {
|
||||||
packages = packages ?? getSelection();
|
packages = packages ?? getSelection();
|
||||||
const onSuccess = update => `Packages ${update} have been removed`;
|
const onSuccess = update => `Packages ${update} have been removed`;
|
||||||
@ -97,16 +84,17 @@
|
|||||||
return "btn-outline-secondary";
|
return "btn-outline-secondary";
|
||||||
};
|
};
|
||||||
|
|
||||||
$.ajax({
|
makeRequest(
|
||||||
url: "/api/v1/packages",
|
"/api/v1/packages",
|
||||||
data: {
|
{
|
||||||
architecture: repository.architecture,
|
query: {
|
||||||
repository: repository.repository,
|
architecture: repository.architecture,
|
||||||
|
repository: repository.repository,
|
||||||
|
},
|
||||||
|
convert: response => response.json(),
|
||||||
},
|
},
|
||||||
type: "GET",
|
data => {
|
||||||
dataType: "json",
|
const payload = data.map(description => {
|
||||||
success: response => {
|
|
||||||
const payload = response.map(description => {
|
|
||||||
const package_base = description.package.base;
|
const package_base = description.package.base;
|
||||||
const web_url = description.package.remote.web_url;
|
const web_url = description.package.remote.web_url;
|
||||||
return {
|
return {
|
||||||
@ -125,10 +113,9 @@
|
|||||||
table.bootstrapTable("load", payload);
|
table.bootstrapTable("load", payload);
|
||||||
table.bootstrapTable("uncheckAll");
|
table.bootstrapTable("uncheckAll");
|
||||||
table.bootstrapTable("hideLoading");
|
table.bootstrapTable("hideLoading");
|
||||||
hideControls(false);
|
|
||||||
},
|
},
|
||||||
error: (jqXHR, _, errorThrown) => {
|
error => {
|
||||||
if ((jqXHR.status === 401) || (jqXHR.status === 403)) {
|
if ((error.status === 401) || (error.status === 403)) {
|
||||||
// authorization error
|
// authorization error
|
||||||
const text = "In order to see statuses you must login first.";
|
const text = "In order to see statuses you must login first.";
|
||||||
table.find("tr.unauthorized").remove();
|
table.find("tr.unauthorized").remove();
|
||||||
@ -136,39 +123,39 @@
|
|||||||
table.bootstrapTable("hideLoading");
|
table.bootstrapTable("hideLoading");
|
||||||
} else {
|
} else {
|
||||||
// other errors
|
// other errors
|
||||||
const message = error => `Could not load list of packages: ${error}`;
|
const message = details => `Could not load list of packages: ${details}`;
|
||||||
showFailure("Load failure", message, jqXHR, errorThrown);
|
showFailure("Load failure", message, error);
|
||||||
}
|
}
|
||||||
hideControls(true);
|
|
||||||
},
|
},
|
||||||
});
|
);
|
||||||
|
|
||||||
$.ajax({
|
makeRequest(
|
||||||
url: "/api/v1/status",
|
"/api/v1/status",
|
||||||
data: {
|
{
|
||||||
architecture: repository.architecture,
|
query: {
|
||||||
repository: repository.repository,
|
architecture: repository.architecture,
|
||||||
|
repository: repository.repository,
|
||||||
|
},
|
||||||
|
convert: response => response.json(),
|
||||||
},
|
},
|
||||||
type: "GET",
|
data => {
|
||||||
dataType: "json",
|
versionBadge.innerHTML = `<i class="bi bi-github"></i> ahriman ${safe(data.version)}`;
|
||||||
success: response => {
|
|
||||||
versionBadge.html(`<i class="bi bi-github"></i> ahriman ${safe(response.version)}`);
|
|
||||||
|
|
||||||
statusBadge
|
statusBadge.classList.remove(...statusBadge.classList);
|
||||||
.popover("dispose")
|
statusBadge.classList.add("btn");
|
||||||
.attr("data-bs-content", `${response.status.status} at ${new Date(1000 * response.status.timestamp).toISOStringShort()}`)
|
statusBadge.classList.add(badgeClass(data.status.status));
|
||||||
.popover();
|
|
||||||
statusBadge.removeClass();
|
const popover = bootstrap.Popover.getOrCreateInstance(statusBadge);
|
||||||
statusBadge.addClass("btn");
|
popover.dispose();
|
||||||
statusBadge.addClass(badgeClass(response.status.status));
|
statusBadge.dataset.bsContent = `${data.status.status} at ${new Date(1000 * data.status.timestamp).toISOStringShort()}`;
|
||||||
|
bootstrap.Popover.getOrCreateInstance(statusBadge);
|
||||||
},
|
},
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectRepository() {
|
function selectRepository() {
|
||||||
const fragment = window.location.hash.replace("#", "") || "{{ repositories[0].id }}";
|
const fragment = window.location.hash.replace("#", "") || "{{ repositories[0].id }}";
|
||||||
const element = $(`#${fragment}-link`);
|
document.getElementById(`${fragment}-link`).click();
|
||||||
element.click();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function statusFormat(value) {
|
function statusFormat(value) {
|
||||||
@ -182,20 +169,25 @@
|
|||||||
return {classes: cellClass(value)};
|
return {classes: cellClass(value)};
|
||||||
}
|
}
|
||||||
|
|
||||||
$(_ => {
|
ready(_ => {
|
||||||
$("#repositories a").on("click", event => {
|
document.querySelectorAll("#repositories a").forEach(element => {
|
||||||
const element = event.target;
|
element.onclick = _ => {
|
||||||
repository = {
|
repository = {
|
||||||
architecture: element.dataset.architecture,
|
architecture: element.dataset.architecture,
|
||||||
repository: element.dataset.repository,
|
repository: element.dataset.repository,
|
||||||
|
};
|
||||||
|
if (packageUpdateButton) {
|
||||||
|
packageUpdateButton.innerHTML = `<i class="bi bi-play"></i> update<span class="d-none d-sm-inline"> ${safe(repository.repository)} (${safe(repository.architecture)})</span>`;
|
||||||
|
}
|
||||||
|
bootstrap.Tab.getOrCreateInstance(document.getElementById(element.id)).show();
|
||||||
|
reload();
|
||||||
};
|
};
|
||||||
packageUpdateButton.html(`<i class="bi bi-play"></i> update<span class="d-none d-sm-inline"> ${safe(repository.repository)} (${safe(repository.architecture)})</span>`);
|
|
||||||
$(`#${element.id}`).tab("show");
|
|
||||||
reload();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
table.on("check.bs.table uncheck.bs.table check-all.bs.table uncheck-all.bs.table", _ => {
|
table.on("check.bs.table uncheck.bs.table check-all.bs.table uncheck-all.bs.table", _ => {
|
||||||
packageRemoveButton.prop("disabled", !table.bootstrapTable("getSelections").length);
|
if (packageRemoveButton) {
|
||||||
|
packageRemoveButton.disabled = !table.bootstrapTable("getSelections").length;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
table.on("click-row.bs.table", (self, data, row, cell) => {
|
table.on("click-row.bs.table", (self, data, row, cell) => {
|
||||||
if (0 === cell || "base" === cell) {
|
if (0 === cell || "base" === cell) {
|
||||||
@ -204,26 +196,38 @@
|
|||||||
} else showPackageInfo(data.id);
|
} else showPackageInfo(data.id);
|
||||||
});
|
});
|
||||||
table.on("created-controls.bs.table", _ => {
|
table.on("created-controls.bs.table", _ => {
|
||||||
const pickerInput = $(".bootstrap-table-filter-control-timestamp");
|
new easepick.create({
|
||||||
pickerInput.daterangepicker({
|
element: document.querySelector(".bootstrap-table-filter-control-timestamp"),
|
||||||
autoUpdateInput: false,
|
css: [
|
||||||
|
"https://cdn.jsdelivr.net/npm/@easepick/bundle@1.2.1/dist/index.css",
|
||||||
|
],
|
||||||
|
grid: 2,
|
||||||
|
calendars: 2,
|
||||||
|
autoApply: false,
|
||||||
locale: {
|
locale: {
|
||||||
cancelLabel: "Clear",
|
cancel: "Clear",
|
||||||
|
},
|
||||||
|
RangePlugin: {
|
||||||
|
tooltip: false,
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
"RangePlugin",
|
||||||
|
],
|
||||||
|
setup: picker => {
|
||||||
|
picker.on("select", _ => { table.bootstrapTable("triggerSearch"); });
|
||||||
|
// replace "Cancel" behaviour to "Clear"
|
||||||
|
picker.onClickCancelButton = element => {
|
||||||
|
if (picker.isCancelButton(element)) {
|
||||||
|
picker.clear();
|
||||||
|
picker.hide();
|
||||||
|
table.bootstrapTable("triggerSearch");
|
||||||
|
}
|
||||||
|
};
|
||||||
},
|
},
|
||||||
});
|
|
||||||
|
|
||||||
pickerInput.on("apply.daterangepicker", (event, picker) => {
|
|
||||||
pickerInput.val(`${picker.startDate.format("YYYY-MM-DD")} - ${picker.endDate.format("YYYY-MM-DD")}`);
|
|
||||||
table.bootstrapTable("triggerSearch");
|
|
||||||
});
|
|
||||||
|
|
||||||
pickerInput.on("cancel.daterangepicker", _ => {
|
|
||||||
pickerInput.val("");
|
|
||||||
table.bootstrapTable("triggerSearch");
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
statusBadge.popover();
|
bootstrap.Popover.getOrCreateInstance(statusBadge);
|
||||||
selectRepository();
|
selectRepository();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -105,13 +105,13 @@ SigLevel = Database{% if has_repo_signed %}Required{% else %}Never{% endif %} Pa
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const table = $("#packages");
|
const table = $(document.getElementById("packages"));
|
||||||
|
|
||||||
const pacmanConf = $("#pacman-conf");
|
const pacmanConf = document.getElementById("pacman-conf");
|
||||||
const pacmanConfCopyButton = $("#copy-btn");
|
const pacmanConfCopyButton = document.getElementById("copy-btn");
|
||||||
|
|
||||||
async function copyPacmanConf() {
|
async function copyPacmanConf() {
|
||||||
const conf = pacmanConf.text();
|
const conf = pacmanConf.textContent;
|
||||||
await copyToClipboard(conf, pacmanConfCopyButton);
|
await copyToClipboard(conf, pacmanConfCopyButton);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,24 +127,36 @@ SigLevel = Database{% if has_repo_signed %}Required{% else %}Never{% endif %} Pa
|
|||||||
return extractDataList(table.bootstrapTable("getData"), "licenses");
|
return extractDataList(table.bootstrapTable("getData"), "licenses");
|
||||||
}
|
}
|
||||||
|
|
||||||
$(_ => {
|
ready(_ => {
|
||||||
table.on("created-controls.bs.table", _ => {
|
table.on("created-controls.bs.table", _ => {
|
||||||
const pickerInput = $(".bootstrap-table-filter-control-timestamp");
|
new easepick.create({
|
||||||
pickerInput.daterangepicker({
|
element: document.querySelector(".bootstrap-table-filter-control-timestamp"),
|
||||||
autoUpdateInput: false,
|
css: [
|
||||||
|
"https://cdn.jsdelivr.net/npm/@easepick/bundle@1.2.1/dist/index.css",
|
||||||
|
],
|
||||||
|
grid: 2,
|
||||||
|
calendars: 2,
|
||||||
|
autoApply: false,
|
||||||
locale: {
|
locale: {
|
||||||
cancelLabel: "Clear",
|
cancel: "Clear",
|
||||||
|
},
|
||||||
|
RangePlugin: {
|
||||||
|
tooltip: false,
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
"RangePlugin",
|
||||||
|
],
|
||||||
|
setup: picker => {
|
||||||
|
picker.on("select", _ => { table.bootstrapTable("triggerSearch"); });
|
||||||
|
// replace "Cancel" behaviour to "Clear"
|
||||||
|
picker.onClickCancelButton = element => {
|
||||||
|
if (picker.isCancelButton(element)) {
|
||||||
|
picker.clear();
|
||||||
|
picker.hide();
|
||||||
|
table.bootstrapTable("triggerSearch");
|
||||||
|
}
|
||||||
|
};
|
||||||
},
|
},
|
||||||
});
|
|
||||||
|
|
||||||
pickerInput.on("apply.daterangepicker", (event, picker) => {
|
|
||||||
pickerInput.val(`${picker.startDate.format("YYYY-MM-DD")} - ${picker.endDate.format("YYYY-MM-DD")}`);
|
|
||||||
table.bootstrapTable("triggerSearch");
|
|
||||||
});
|
|
||||||
|
|
||||||
pickerInput.on("cancel.daterangepicker", _ => {
|
|
||||||
pickerInput.val("");
|
|
||||||
table.bootstrapTable("triggerSearch");
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,41 +1,30 @@
|
|||||||
<script src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
<script src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/jquery.md5@1.0.2/index.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
<script src="https://cdn.jsdelivr.net/npm/js-md5@0.8.3/src/md5.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/moment@2.29.4/moment.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
<script src="https://cdn.jsdelivr.net/npm/tableexport.jquery.plugin@1.30.0/tableExport.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/daterangepicker@3.1.0/daterangepicker.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/tableexport.jquery.plugin@1.28.0/tableExport.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/jquery-resizable-columns@0.2.3/dist/jquery.resizableColumns.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
<script src="https://cdn.jsdelivr.net/npm/jquery-resizable-columns@0.2.3/dist/jquery.resizableColumns.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap-table@1.22.1/dist/bootstrap-table.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap-table@1.23.2/dist/bootstrap-table.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap-table@1.22.1/dist/extensions/export/bootstrap-table-export.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap-table@1.23.2/dist/extensions/export/bootstrap-table-export.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap-table@1.22.1/dist/extensions/resizable/bootstrap-table-resizable.js" crossorigin="anonymous" type="application/javascript"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap-table@1.23.2/dist/extensions/resizable/bootstrap-table-resizable.js" crossorigin="anonymous" type="application/javascript"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap-table@1.22.1/dist/extensions/filter-control/bootstrap-table-filter-control.js" crossorigin="anonymous" type="application/javascript"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap-table@1.23.2/dist/extensions/filter-control/bootstrap-table-filter-control.js" crossorigin="anonymous" type="application/javascript"></script>
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/highlight.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
<script src="https://cdn.jsdelivr.net/npm/@easepick/bundle@1.2.1/dist/index.umd.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.10.0/build/highlight.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.4/dist/chart.umd.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.4/dist/chart.umd.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
async function copyToClipboard(text, button) {
|
async function copyToClipboard(text, button) {
|
||||||
if (navigator.clipboard === undefined) {
|
await navigator.clipboard.writeText(text);
|
||||||
const input = document.createElement("textarea");
|
button.innerHTML = "<i class=\"bi bi-clipboard-check\"></i> copied";
|
||||||
input.innerHTML = text;
|
setTimeout(_ => {
|
||||||
document.body.appendChild(input);
|
button.innerHTML = "<i class=\"bi bi-clipboard\"></i> copy";
|
||||||
input.select();
|
|
||||||
document.execCommand("copy");
|
|
||||||
document.body.removeChild(input);
|
|
||||||
} else {
|
|
||||||
await navigator.clipboard.writeText(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
button.html("<i class=\"bi bi-clipboard-check\"></i> copied");
|
|
||||||
setTimeout(()=> {
|
|
||||||
button.html("<i class=\"bi bi-clipboard\"></i> copy");
|
|
||||||
}, 2000);
|
}, 2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,6 +65,47 @@
|
|||||||
.join("<br>");
|
.join("<br>");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function makeRequest(url, params, onSuccess, onFailure) {
|
||||||
|
const requestParams = {
|
||||||
|
method: params.method,
|
||||||
|
body: params.json ? JSON.stringify(params.json) : params.json,
|
||||||
|
headers: {
|
||||||
|
"Accept": "application/json",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
if (params.query) {
|
||||||
|
const query = new URLSearchParams(params.query);
|
||||||
|
url += `?${query.toString()}`;
|
||||||
|
}
|
||||||
|
const convert = params.convert ?? (response => response.text());
|
||||||
|
|
||||||
|
return fetch(url, requestParams)
|
||||||
|
.then(response => {
|
||||||
|
if (response.ok) {
|
||||||
|
return convert(response);
|
||||||
|
} else {
|
||||||
|
const error = new Error("Network request error");
|
||||||
|
error.status = response.status;
|
||||||
|
error.statusText = response.statusText;
|
||||||
|
return response.text().then(text => {
|
||||||
|
error.text = text;
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(data => onSuccess && onSuccess(data))
|
||||||
|
.catch(error => onFailure && onFailure(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
function ready(fn) {
|
||||||
|
if (document.readyState === "complete" || document.readyState === "interactive") {
|
||||||
|
setTimeout(fn, 1);
|
||||||
|
} else {
|
||||||
|
document.addEventListener("DOMContentLoaded", fn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function safe(string) {
|
function safe(string) {
|
||||||
return String(string)
|
return String(string)
|
||||||
.replace(/&/g, "&")
|
.replace(/&/g, "&")
|
||||||
@ -89,7 +119,9 @@
|
|||||||
const element = document.createElement("a");
|
const element = document.createElement("a");
|
||||||
element.href = url;
|
element.href = url;
|
||||||
element.innerText = text;
|
element.innerText = text;
|
||||||
if (title) element.title = title;
|
if (title) {
|
||||||
|
element.title = title;
|
||||||
|
}
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,17 +1,15 @@
|
|||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" crossorigin="anonymous" type="text/css">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" crossorigin="anonymous" type="text/css">
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css" crossorigin="anonymous" type="text/css">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css" crossorigin="anonymous" type="text/css">
|
||||||
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-table@1.22.1/dist/bootstrap-table.min.css" crossorigin="anonymous" type="text/css">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-table@1.23.2/dist/bootstrap-table.min.css" crossorigin="anonymous" type="text/css">
|
||||||
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/jquery-resizable-columns@0.2.3/dist/jquery.resizableColumns.css" crossorigin="anonymous" type="text/css">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/jquery-resizable-columns@0.2.3/dist/jquery.resizableColumns.css" crossorigin="anonymous" type="text/css">
|
||||||
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-table@1.22.1/dist/extensions/filter-control/bootstrap-table-filter-control.css" crossorigin="anonymous" type="text/css">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-table@1.23.2/dist/extensions/filter-control/bootstrap-table-filter-control.css" crossorigin="anonymous" type="text/css">
|
||||||
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootswatch@5.3.2/dist/cosmo/bootstrap.min.css" crossorigin="anonymous" type="text/css">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootswatch@5.3.3/dist/cosmo/bootstrap.min.css" crossorigin="anonymous" type="text/css">
|
||||||
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/daterangepicker@3.1.0/daterangepicker.css" crossorigin="anonymous" type="text/css">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.10.0/build/styles/github.min.css" crossorigin="anonymous" type="text/css">
|
||||||
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/styles/github.min.css" crossorigin="anonymous" type="text/css">
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.pre-scrollable {
|
.pre-scrollable {
|
||||||
|
Loading…
Reference in New Issue
Block a user