where as Django >= 4.0 generates s
let parents = document.querySelectorAll('ul.has-select-all,div.has-select-all');
for (const parent of parents) {
let childElementType = 'div';
if (parent.tagName.toLowerCase() == 'ul')
childElementType = 'li';
let selectAllItem = document.createElement(childElementType);
let label = document.createElement('label');
label.for = "select_all";
label.setAttribute('class', 'select-all-label');
let checkbox = document.createElement('input');
checkbox.type = "checkbox";
checkbox.setAttribute('class', 'select-all');
label.appendChild(checkbox);
selectAllItem.appendChild(label);
parent.insertBefore(selectAllItem, parent.childNodes[0]);
setSelectAllValue(parent);
checkbox.addEventListener('change', onSelectAllChanged);
options = parent.querySelectorAll('input.has-select-all');
for (const option of options) {
option.addEventListener('change', onSelectAllOptionsChanged);
}
}
});
// When there is a change on the "select all" checkbox, set the checked property
// of all the checkboxes to the value of the "select all" checkbox
function onSelectAllChanged(event) {
const selectAllCheckbox = event.currentTarget;
const parent = selectAllCheckbox.parentElement.parentElement.parentElement;
const options = parent.querySelectorAll('input.has-select-all');
for (const option of options) {
option.checked = selectAllCheckbox.checked;
}
}
// When there is a change on a checkbox controlled by a select all checkbox,
// update the value of checkbox.
function onSelectAllOptionsChanged(event) {
const parent = event.currentTarget.parentElement.parentElement.parentElement;
setSelectAllValue(parent);
}
// Set/reset the checked property of "select all" checkbox based on whether all
// checkboxes it controls are checked.
function setSelectAllValue(parent) {
const options = parent.querySelectorAll('input.has-select-all');
let enableSelectAll = true;
for (const option of options) {
if (!option.checked) {
enableSelectAll = false;
break;
}
}
parent.querySelector('.select-all').checked = enableSelectAll;
}
/*
* Check whether an app is available on its setup page.
*/
document.addEventListener('DOMContentLoaded', async () => {
const checkingElement = document.querySelector('.app-checking-availability');
if (!checkingElement)
return;
// App does not need setup, it likely needs upgrade
const setupState = checkingElement.getAttribute('data-setup-state');
if (setupState !== 'needs-setup')
return;
const appId = checkingElement.getAttribute('data-app-id');
checkingElement.classList.remove('d-none');
function setInstallButtonState(enable) {
const installButton = document.querySelector('.install-button');
if (enable)
installButton?.removeAttribute('disabled');
else
installButton?.setAttribute('disabled', 'disabled');
}
function error() {
const element = document.querySelector('.app-checking-availability-error');
element.classList.remove('d-none');
checkingElement.classList.add('d-none');
setInstallButtonState(true); // Allow trying installation
}
try {
setInstallButtonState(false);
const response = await fetch(`/plinth/is-available/${appId}/`, {
timeout: 2 * 60 * 1000 // 2 minutes
});
checkingElement.classList.add('d-none');
if (response.ok) {
const data = await response.json();
if (data.is_available === true) {
setInstallButtonState(true);
} else if (data.is_available === false) {
document.querySelector('.app-unavailable').classList.remove('d-none');
setInstallButtonState(false);
} else {
error();
}
} else {
error();
}
} catch {
error();
}
});
/*
* Text areas showing log lines have special behavior.
*/
document.addEventListener('DOMContentLoaded', function (event) {
const logElements = document.querySelectorAll('textarea.log');
// Scroll the textarea to the bottom so that last lines are visible.
for (const element of logElements) {
element.scrollTop = element.scrollHeight;
}
});
/*
* Close notifications dropdown when clicking outside, like the other dropdowns.
*/
document.addEventListener('click', function (event) {
// Ignore if notifications dropdown is not open
const notifications = document.querySelector('.notifications');
if (!notifications?.classList.contains('show')) {
return;
}
// Ignore if the click happened inside the notifications area or on the toggle button
const toggles = document.querySelectorAll('[data-bs-target=".notifications"]');
const clickedInsideToggle = Array.from(toggles).some(toggle => toggle.contains(event.target));
if (notifications.contains(event.target) || clickedInsideToggle) {
return;
}
if (typeof bootstrap !== 'undefined' && bootstrap.Collapse) {
let bsCollapse = bootstrap.Collapse.getInstance(notifications);
bsCollapse.hide();
}
});