/** * Alert and Confirm modals. * * Usage: * * modalAlert({message: "Hello world!"}).then(callback); * modalConfirm({message: "Are you sure?"}).then(callback); * * Available options for modalAlert: * - message * - title: Alert * * Available options for modalConfirm: * - message * - title: Confirm * - buttons: ["Ok", "Cancel"] * - event (pass `event` for easy inline onclick handlers) * - element (pass `this` for easy inline onclick handlers) * * Example onclick for modalConfirm: * * * * The `element` is used to find the nearest
and submit it on OK. * The `event` is used to cancel the submit button's default. */ document.addEventListener('DOMContentLoaded', () => { const $modal = document.querySelector("#nonshy-alert-modal"), $ok = $modal.querySelector("button.nonshy-alert-ok-button"), $cancel = $modal.querySelector("button.nonshy-alert-cancel-button"), $title = $modal.querySelector("#nonshy-alert-modal-title"), $body = $modal.querySelector("#nonshy-alert-modal-body"), alertIcon = ``, confirmIcon = ``, cls = 'is-active'; // Current caller's promise. var currentPromise = null; const hideModal = () => { currentPromise = null; $modal.classList.remove(cls); }; const showModal = ({ message, title="Alert", isConfirm=false, buttons=["Ok", "Cancel"], }) => { $ok.innerHTML = buttons[0]; $cancel.innerHTML = buttons[1]; $cancel.style.display = isConfirm ? "" : "none"; // Strip HTML from message but allow line breaks. message = message.replace(//g, ">"); message = message.replace(/\n/g, "
"); $title.innerHTML = (isConfirm ? confirmIcon : alertIcon) + title; $body.innerHTML = message; // Show the modal. $modal.classList.add(cls); // Focus the OK button, e.g. so hitting Enter doesn't accidentally (re)click the same // link/button which prompted the alert box in the first place. window.requestAnimationFrame(() => { $ok.focus(); }); // Return as a promise. return new Promise((resolve, reject) => { currentPromise = resolve; }); }; // Click events for the modal buttons. $ok.addEventListener('click', (e) => { if (currentPromise !== null) { currentPromise(); } hideModal(); }); $cancel.addEventListener('click', (e) => { hideModal(); }); // Key bindings to dismiss the modal. window.addEventListener('keydown', (e) => { if ($modal.classList.contains(cls)) { if (e.key == 'Enter') { $ok.click(); } else if (e.key == 'Escape') { $cancel.click(); } } }); // Inline submit button confirmation prompts, e.g.: many submit buttons have name="intent" // and want the user to confirm before submitting, and had inline onclick handlers. (document.querySelectorAll('.nonshy-confirm-submit') || []).forEach(button => { const message = button.dataset.confirm; if (!message) return; const onclick = (e) => { e.preventDefault(); modalConfirm({ message: message.replace(/\\n/g, '\n'), }).then(() => { button.removeEventListener('click', onclick); window.requestAnimationFrame(() => { button.click(); }); }); } button.addEventListener('click', onclick); }); // Exported global functions to invoke the modal. window.modalAlert = async ({ message, title="Alert" }) => { return showModal({ message, title, isConfirm: false, }); }; window.modalConfirm = async ({ message, title="Confirm", buttons=["Ok", "Cancel"] }) => { return showModal({ message, title, isConfirm: true, buttons, }); }; });