public/js/app.js
Rama main · réplica de lectura del repositorio original.
'use strict'
// State-changing admin actions are sent as POST requests carrying the CSRF token
// exposed in the <meta name="csrf-token"> tag of the layout.
function postTo(url, params = {}) {
const token = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
const form = document.createElement('form');
form.method = 'post';
form.action = url;
form.classList.add('d-none');
const fields = Object.assign({ _token: token }, params);
Object.keys(fields).forEach(name => {
const input = document.createElement('input');
input.type = 'hidden';
input.name = name;
input.value = fields[name];
form.appendChild(input);
});
document.body.appendChild(form);
form.submit();
}
// Any element with a data-post-href attribute triggers a POST to that URL when clicked
document.addEventListener('click', event => {
const trigger = event.target.closest('[data-post-href]');
if (!trigger) {
return;
}
event.preventDefault();
postTo(trigger.getAttribute('data-post-href'));
});
// Clicking an element with data-clear-target empties the input with that id.
// Native date inputs have no good way to clear the value.
document.addEventListener('click', event => {
const trigger = event.target.closest('[data-clear-target]');
if (!trigger) {
return;
}
const input = document.getElementById(trigger.getAttribute('data-clear-target'));
if (input) {
input.value = '';
input.dispatchEvent(new Event('change', { bubbles: true }));
}
});
// Calendar share modal
const shareModal = document.getElementById('shareModal')
if (shareModal) {
const addShareeButton = document.getElementById('shareModal-addSharee');
// Bound once: the target url is refreshed each time the modal is shown
addShareeButton.addEventListener("click", function(e) {
const writeAccess = document.getElementById('shareModal-writeAccess').checked ? 'true' : 'false';
const principalId = document.getElementById('shareModal-member').value;
e.preventDefault()
postTo(addShareeButton.getAttribute('data-href'), { principalId: principalId, write: writeAccess })
});
shareModal.addEventListener('show.bs.modal', event => {
// Button that triggered the modal
const button = event.relatedTarget
// Grab calendar shares url and add url
let shareesUrl = button.getAttribute('data-sharees-href');
addShareeButton.setAttribute('data-href', button.getAttribute('data-href'));
const noneElement = document.getElementById('shareModal-none')
// Shares list
const shares = document.getElementById('shareModal-shares')
shares.innerHTML = ''
// Get calendar shares
fetch(shareesUrl)
.then((response) => response.json())
.then((data) => {
// No sharee
if (data.length === 0) {
noneElement.classList.remove("d-none");
return
}
noneElement.classList.add('d-none')
// Share list item template
const template = document.getElementById("shareModal-shareeTemplate");
data.forEach(element => {
const clone = template.content.cloneNode(true);
let name = clone.querySelectorAll("span.name");
name[0].textContent = element.displayName;
let badge = clone.querySelectorAll("span.badge");
badge[0].textContent = element.accessText;
if (element.isWriteAccess) {
badge[0].classList.add('bg-success')
badge[0].classList.remove('bg-info')
}
let revokeButton = clone.querySelectorAll(".revoke");
revokeButton[0].setAttribute('data-post-href', element.revokeUrl);
shares.appendChild(clone);
});
});
})
}
// Delete modals (all kind of entities, so we use the rel, not the id)
document.querySelectorAll('[rel="deleteModal"]').forEach(element => {
element.addEventListener('show.bs.modal', event => {
// Button that triggered the modal
const button = event.relatedTarget
// Grab real target url for deletion
let targetUrl = button.getAttribute('data-href');
let modalFlavour = button.getAttribute('data-flavour');
// Put it into the modal's OK button (which POSTs it on click)
const deleteCTA = document.getElementById(`deleteModal-${modalFlavour}-cta`);
deleteCTA.setAttribute('data-post-href', targetUrl);
})
})
// Global account delegation modal
const addDelegateModal = document.getElementById('addDelegateModal')
if (addDelegateModal) {
const addDelegateButton = document.getElementById('addDelegateModal-cta');
addDelegateButton.addEventListener("click", function(e) {
const targetUrl = addDelegateButton.getAttribute('data-href');
const writeAccess = document.getElementById('addDelegateModal-writeAccess').checked ? 'true' : 'false';
const principalId = document.getElementById('addDelegateModal-member').value;
e.preventDefault()
postTo(targetUrl, { principalId: principalId, write: writeAccess })
});
}
// Color swatch: update it live (not working in IE ¯\_(ツ)_/¯ but it's just a nice to have)
const colorPicker = document.getElementById('calendar_instance_calendarColor');
if (colorPicker) {
colorPicker.addEventListener('keyup', event => {
document.body.style.setProperty('--calendar-color', event.target.value);
})
document.body.style.setProperty('--calendar-color', colorPicker.value);
}
// Bootstrap 5 popovers
//
// The setup popovers show a calendar or address book uri, which its owner chooses over DAV. The
// content is built here as DOM rather than handed to Bootstrap as an HTML string, so those values
// go through textContent and are never parsed as markup.
const buildPopoverContent = element => {
const content = document.createDocumentFragment();
[['URI', element.dataset.uri], ['Absolute path', element.dataset.davUri]].forEach(([label, value], index) => {
if (!value) {
return;
}
if (index > 0) {
content.appendChild(document.createElement('br'));
}
content.appendChild(document.createTextNode(`${label}: `));
const code = document.createElement('code');
code.textContent = value;
content.appendChild(code);
})
return content;
}
const popoverTriggerList = document.querySelectorAll('[data-bs-toggle="popover"]')
if (popoverTriggerList) {
[...popoverTriggerList].map(popoverTriggerEl => new bootstrap.Popover(
popoverTriggerEl,
// Popovers that carry no uri keep their own static `data-bs-content`
popoverTriggerEl.dataset.uri
? { html: true, content: buildPopoverContent(popoverTriggerEl) }
: {}
))
}
// Bootstrap 5 toasts
const toastElList = document.querySelectorAll('.toast')
if (toastElList) {
[...toastElList].map(toastEl => {
const toast = new bootstrap.Toast(toastEl)
toast.show()
})
}