`: * * - opening moves focus to the element the caller names (Cancel, or the first * field), and remembers the control that opened it; * - everything else on the page is `inert` while it is open; * - Tab and Shift+Tab cycle inside it; Escape cancels; * - closing puts focus back on the control that opened it. * * It decides nothing about what a dialog does. The caller still owns Cancel, * Import, Download and what Confirm submits. */ if (!defined('ABSPATH')) exit; /** * Print the shared dialog script and the in-page confirm dialog, once per page. * * Buttons opt in with data attributes (all translated by the caller): * `data-wpbr-confirm` (the question), `data-wpbr-confirm-title`, * `data-wpbr-confirm-label` (the Confirm button). Pressing one asks first; Confirm * then submits the button's own form with that button as the submitter, so the * posted `wpbr_sub` is exactly what a direct click would have posted. */ function wpbr_dialog_support_print() { static $printed = false; if ($printed) return; $printed = true; echo "
"; echo "
"; echo "

"; echo "

"; echo "
"; echo ""; echo ""; echo "
"; echo "
"; echo "
"; wpbr_inline_open('script'); ?> (function(){ var open = null; function visible(el){ return !el.disabled && el.type !== 'hidden' && (el.offsetWidth || el.offsetHeight || el.getClientRects().length); } function focusables(panel){ return Array.prototype.filter.call(panel.querySelectorAll('a[href],button,input,select,textarea,[tabindex]'), function(el){ return el.tabIndex >= 0 && visible(el); }); } function panelOf(overlay){ return overlay.querySelector('[role="dialog"]') || overlay.firstElementChild; } function makeInert(overlay){ var changed = [], node = overlay; while (node && node !== document.body && node.parentNode) { Array.prototype.forEach.call(node.parentNode.children, function(sib){ if (sib !== node && sib.tagName !== 'SCRIPT' && sib.tagName !== 'STYLE' && !sib.hasAttribute('inert')) { sib.setAttribute('inert', ''); changed.push(sib); } }); node = node.parentNode; } return changed; } /* opts.opener: the control to return focus to (default: whatever had focus). opts.initial: the element that takes focus (default: the first focusable). opts.cancel: what Escape and a click on the overlay do (default: close). */ function show(overlay, opts){ opts = opts || {}; var panel = panelOf(overlay); var state = { overlay: overlay, opener: opts.opener || document.activeElement, inert: makeInert(overlay), cancel: opts.cancel }; overlay.style.display = 'block'; open = state; var first = opts.initial || focusables(panel)[0] || panel; first.focus(); return state; } function hide(overlay, returnFocus){ if (!open || open.overlay !== overlay) { overlay.style.display = 'none'; return; } overlay.style.display = 'none'; open.inert.forEach(function(el){ el.removeAttribute('inert'); }); var opener = open.opener; open = null; if (returnFocus !== false && opener && document.contains(opener) && opener.focus) { opener.focus(); } } document.addEventListener('keydown', function(e){ if (!open) return; if (e.key === 'Escape') { e.preventDefault(); e.stopPropagation(); if (open.cancel) { open.cancel(); } else { hide(open.overlay); } return; } if (e.key !== 'Tab') return; var items = focusables(panelOf(open.overlay)); if (!items.length) { e.preventDefault(); return; } var first = items[0], last = items[items.length - 1], active = document.activeElement; if (!open.overlay.contains(active)) { e.preventDefault(); first.focus(); } else if (e.shiftKey && active === first) { e.preventDefault(); last.focus(); } else if (!e.shiftKey && active === last) { e.preventDefault(); first.focus(); } }, true); window.wpbrDialog = { show: show, hide: hide }; /* The in-page replacement for confirm(). */ var overlay = document.getElementById('wpbr_confirm_dialog'); var titleEl = document.getElementById('wpbr_confirm_title'); var bodyEl = document.getElementById('wpbr_confirm_body'); var cancelBtn = document.getElementById('wpbr_confirm_cancel'); var okBtn = document.getElementById('wpbr_confirm_ok'); var pending = null; function cancel(){ hide(overlay); pending = null; } cancelBtn.addEventListener('click', cancel); overlay.addEventListener('click', function(e){ if (e.target === overlay) { cancel(); } }); okBtn.addEventListener('click', function(){ var btn = pending; pending = null; hide(overlay, false); if (!btn) return; var form = btn.form; if (!form) return; /* The server only carries out a confirmed action: this field names the one the person just confirmed. */ var mark = form.querySelector('input[name="wpbr_confirmed"]'); if (!mark) { mark = document.createElement('input'); mark.type = 'hidden'; mark.name = 'wpbr_confirmed'; form.appendChild(mark); } mark.value = btn.value || ''; if (form.requestSubmit) { form.requestSubmit(btn); mark.value = ''; return; } if (btn.name) { var h = document.createElement('input'); h.type = 'hidden'; h.name = btn.name; h.value = btn.value; form.appendChild(h); } form.submit(); }); document.addEventListener('click', function(e){ var btn = e.target.closest ? e.target.closest('[data-wpbr-confirm]') : null; if (!btn || btn.disabled) return; e.preventDefault(); pending = btn; titleEl.textContent = btn.getAttribute('data-wpbr-confirm-title') || ''; bodyEl.textContent = btn.getAttribute('data-wpbr-confirm') || ''; okBtn.textContent = btn.getAttribute('data-wpbr-confirm-label') || ''; show(overlay, { opener: btn, initial: cancelBtn, cancel: cancel }); }); })();