2 * =========================================================
3 * DBX CONFIRM SYSTEM (confirm.js)
4 * =========================================================
8 * confirm.js ist die universelle Bestätigungs-Lib von DBX.
10 * Die Lib ist bewusst getrennt von:
11 * - ajax.js → Transport / Response
12 * - form.js → Formular-/UI-Logik
14 * confirm.js kümmert sich ausschließlich um:
15 * - Bestätigungsdialoge
17 * - frei definierbare Beschriftungen
19 * - optionalen Hinweistext
24 * confirm.js arbeitet als DBX-Feature mit:
29 * - eine confirm-Instanz gilt nur für ihr Root-Element
30 * - plus dessen Children
31 * - niemals global als Event-Fänger
36 * confirm.js bietet zwei Nutzungsarten:
38 * 1. deklarativ über data-dbx + Klassen + data-confirm-*
39 * 2. programmatisch über dbx.confirm.open(...)
42 * =========================================================
43 * ROOT-KONFIGURATION (data-dbx)
44 * =========================================================
48 * <div data-dbx="lib=confirm|class=dbxConfirm|bind=link,button,form">
53 * Unterstützte Root-Parameter
54 * ---------------------------
57 * Pflicht. Registriert confirm.js auf diesem Root.
60 * Match-Class. Nur Elemente mit dieser Klasse werden
61 * von dieser confirm-Instanz behandelt.
65 * - class=dbxDeleteConfirm
68 * bind=link,button,form
69 * Welche Elementtypen bestätigt werden dürfen.
75 * - bind=link,button,form
79 * Default-Titel des Dialogs.
82 * Default-Fragetext des Dialogs.
85 * Optionaler Hinweistext unter der Frage.
86 * Wird nur angezeigt, wenn gesetzt und nicht leer.
88 * buttons=yesno|yesnocancel|cancel|cancelonly
95 * - Ja / Nein / Abbruch
97 * cancel / cancelonly:
104 * Steuert, ob die jeweiligen Inhalte als HTML interpretiert werden.
115 * Frei definierbare Beschriftungen.
116 * HTML ist erlaubt, wenn labelhtml=1.
119 * Darf der Dialog über "X" geschlossen werden?
122 * Darf Klick auf Backdrop den Dialog schließen?
125 * Darf Escape den Dialog schließen?
128 * =========================================================
129 * ELEMENT-OVERRIDES (data-confirm-*)
130 * =========================================================
132 * Ein Link, Button oder ein Form kann alle relevanten
133 * Einstellungen lokal überschreiben.
135 * Unterstützte data-confirm-* Parameter
136 * -------------------------------------
138 * data-confirm-title="..."
139 * data-confirm-question="..."
140 * data-confirm-hint="..."
141 * data-confirm-buttons="yesno|yesnocancel|cancel"
143 * data-confirm-titlehtml="0|1"
144 * data-confirm-questionhtml="0|1"
145 * data-confirm-hinthtml="0|1"
146 * data-confirm-labelhtml="0|1"
148 * data-confirm-labelyes="..."
149 * data-confirm-labelno="..."
150 * data-confirm-labelcancel="..."
152 * data-confirm-closable="0|1"
153 * data-confirm-backdropclose="0|1"
154 * data-confirm-escclose="0|1"
159 * Zusätzlich wird akzeptiert:
163 * Das wird als question behandelt, falls data-confirm-question
167 * =========================================================
168 * PROGRAMMATISCHE NUTZUNG
169 * =========================================================
174 * title: "<i class='bi bi-trash'></i> Löschen",
175 * question: "Datensatz wirklich löschen?",
176 * hint: "<small>Dieser Vorgang kann nicht rückgängig gemacht werden.</small>",
177 * buttons: "yesnocancel",
178 * labelyes: "<i class='bi bi-check-lg'></i> Ja",
179 * labelno: "<i class='bi bi-x-lg'></i> Nein",
180 * labelcancel: "<i class='bi bi-slash-circle'></i> Abbruch"
181 * }).then(result => {
182 * if (result.action === "yes") { ... }
186 * Rückgabe von open(...)
187 * ---------------------
188 * Promise → resolve({
190 * action, // yes | no | cancel | close
197 * =========================================================
199 * =========================================================
205 * buttons=yesnocancel
210 * buttons=cancel / cancelonly
214 * =========================================================
215 * AUTOMATISCHES FORTSETZEN DER AUSLÖSUNG
216 * =========================================================
218 * Wenn confirm.js deklarativ auf einem:
223 * dann wird die ursprüngliche Aktion nach erfolgreichem "yes"
224 * automatisch fortgesetzt.
227 * 1. Falls möglich direkt über ajax.js
228 * 2. sonst normaler Browser-Default
230 * confirm.js selbst macht keine Fachlogik.
231 * Es reicht die Auslösung an ajax.js oder den Browser weiter.
234 * =========================================================
236 * =========================================================
238 * confirm.js nutzt dbx.event.emit():
243 * Wenn ein id gesetzt ist, funktionieren zusätzlich die
244 * id-scoped Events von core.js:
246 * confirm:result:<id>
248 * =========================================================
251(function (window, document) {
254 if (!window.dbx || !window.dbx.feature) {
255 console.error("[dbx][confirm] dbx core missing");
259 const dbx = window.dbx;
261 dbx.confirm = dbx.confirm || {};
267 /* =========================================================
269 * ========================================================= */
271 function bool(v, def = false) {
272 if (v === undefined || v === null || v === "") return def;
273 if (v === true || v === 1 || v === "1" || v === "on" || v === "true") return true;
274 if (v === false || v === 0 || v === "0" || v === "off" || v === "false") return false;
278 function str(v, def = "") {
279 if (v === undefined || v === null) return def;
283 function defaultLabels() {
285 yes: '<i class="bi bi-check-lg"></i> ' + dbx.translate({
290 no: '<i class="bi bi-x-lg"></i> ' + dbx.translate({
295 cancel: '<i class="bi bi-slash-circle"></i> ' + dbx.translate({
303 function normalizeBind(v) {
304 if (v === undefined || v === null || v === "") return ["link", "button", "form"];
306 const raw = String(v).toLowerCase().trim();
308 if (raw === "*" || raw === "all") {
309 return ["link", "button", "form"];
312 return raw.split(",").map(s => s.trim()).filter(Boolean);
315 function normalizeClassFilter(v) {
316 if (v === undefined || v === null || v === "") return ["dbxConfirm"];
318 const raw = String(v).trim();
319 if (raw === "*") return ["*"];
321 return raw.split(",").map(s => s.trim()).filter(Boolean);
324 function normalizeButtons(v) {
325 const raw = String(v || "yesno").toLowerCase().trim();
327 if (raw === "yesnocancel") return "yesnocancel";
328 if (raw === "cancel" || raw === "cancelonly") return "cancel";
333 function elementType(el) {
334 if (!el || !el.tagName) return "";
336 const tag = el.tagName.toLowerCase();
338 if (tag === "form") return "form";
339 if (tag === "a") return "link";
340 if (tag === "button") return "button";
342 if (tag === "input") {
343 const type = str(el.getAttribute("type"), "").toLowerCase();
344 if (type === "button" || type === "submit" || type === "image") {
352 function bindMatches(type, bindList) {
353 if (!type) return false;
354 if (!Array.isArray(bindList) || !bindList.length) return false;
355 return bindList.includes(type);
358 function classMatches(el, classList) {
359 if (!el) return false;
360 if (!Array.isArray(classList) || !classList.length) return false;
361 if (classList.includes("*")) return true;
363 for (let i = 0; i < classList.length; i++) {
364 if (el.classList.contains(classList[i])) return true;
370 function readAttr(el, name) {
371 if (!el || !el.getAttribute) return "";
372 const v = el.getAttribute(name);
373 return v == null ? "" : String(v).trim();
376 function readConfirm(el, key) {
377 return readAttr(el, "data-confirm-" + key) || readAttr(el, "data-confirm_" + key);
380 function emit(name, data) {
381 if (dbx.event && typeof dbx.event.emit === "function") {
382 dbx.event.emit(name, data);
386 function htmlOrText(el, value, allowHtml) {
390 el.innerHTML = value || "";
392 el.textContent = value || "";
396 function iconFromTitle(title) {
397 const raw = String(title || "").toLowerCase();
398 if (raw.includes("trash") || raw.includes("loesch") || raw.includes("lösch")) {
401 if (raw.includes("copy") || raw.includes("kopier")) {
404 if (raw.includes("warn") || raw.includes("achtung")) {
405 return "bi-exclamation-triangle";
407 if (raw.includes("mail") || raw.includes("e-mail") || raw.includes("email")) {
408 return "bi-envelope-check";
410 return "bi-question-circle";
413 function ensureRoot(el) {
414 return el || document.body;
417 function getRootConfigs(root) {
419 if (!root) return [];
421 if (Array.isArray(root._dbxConfirmConfigs)) {
422 return root._dbxConfirmConfigs;
427 if (dbx.declare && dbx.declare.schemas && dbx.declare.schemas.confirm) {
428 out = dbx.declare.resolve("confirm", root);
430 const attr = readAttr(root, "data-dbx");
431 const list = dbx.parseData(attr).filter(cfg => cfg.lib === "confirm");
432 const labels = defaultLabels();
434 out = list.map((cfg, index) => {
437 class: normalizeClassFilter(cfg.class),
438 bind: normalizeBind(cfg.bind),
439 title: str(cfg.title, ""),
440 question: str(cfg.question, ""),
441 hint: str(cfg.hint, ""),
442 buttons: normalizeButtons(cfg.buttons),
443 titlehtml: bool(cfg.titlehtml, true),
444 questionhtml: bool(cfg.questionhtml, true),
445 hinthtml: bool(cfg.hinthtml, true),
446 labelhtml: bool(cfg.labelhtml, true),
447 labelyes: str(cfg.labelyes, labels.yes),
448 labelno: str(cfg.labelno, labels.no),
449 labelcancel: str(cfg.labelcancel, labels.cancel),
450 closable: bool(cfg.closable, true),
451 backdropclose: bool(cfg.backdropclose, false),
452 escclose: bool(cfg.escclose, true)
459 class: normalizeClassFilter("dbxConfirm"),
460 bind: normalizeBind("link,button,form"),
469 labelyes: labels.yes,
471 labelcancel: labels.cancel,
473 backdropclose: false,
479 root._dbxConfirmConfigs = out;
484 function findMatchingConfig(root, source, type) {
486 const configs = getRootConfigs(root);
488 for (let i = 0; i < configs.length; i++) {
489 const cfg = configs[i];
491 if (!bindMatches(type, cfg.bind)) continue;
492 if (!classMatches(source, cfg.class)) continue;
500 function readOptionsFromElement(source, cfg) {
503 id: readConfirm(source, "id") || "",
504 title: readConfirm(source, "title") || cfg.title,
505 question: readConfirm(source, "question") || readAttr(source, "data-confirm") || cfg.question,
506 hint: readConfirm(source, "hint") || cfg.hint,
507 buttons: normalizeButtons(readConfirm(source, "buttons") || cfg.buttons),
509 titlehtml: bool(readConfirm(source, "titlehtml"), cfg.titlehtml),
510 questionhtml: bool(readConfirm(source, "questionhtml"), cfg.questionhtml),
511 hinthtml: bool(readConfirm(source, "hinthtml"), cfg.hinthtml),
512 labelhtml: bool(readConfirm(source, "labelhtml"), cfg.labelhtml),
514 labelyes: readConfirm(source, "labelyes") || cfg.labelyes,
515 labelno: readConfirm(source, "labelno") || cfg.labelno,
516 labelcancel: readConfirm(source, "labelcancel") || cfg.labelcancel,
518 closable: bool(readConfirm(source, "closable"), cfg.closable),
519 backdropclose: bool(readConfirm(source, "backdropclose"), cfg.backdropclose),
520 escclose: bool(readConfirm(source, "escclose"), cfg.escclose),
529 function buildButtonsMarkup(opts) {
533 if (opts.buttons === "yesno") {
534 actions.push("yes", "no");
537 if (opts.buttons === "yesnocancel") {
538 actions.push("yes", "no", "cancel");
541 if (opts.buttons === "cancel") {
542 actions.push("cancel");
548 function getMountEl(root, opts) {
550 if (opts && opts.mountEl) {
554 return document.body;
557 function numericZIndex(el) {
558 if (!el || el.nodeType !== 1) return 0;
559 const z = parseInt(window.getComputedStyle(el).zIndex, 10);
560 return Number.isFinite(z) ? z : 0;
563 function maxAncestorZIndex(el) {
565 let cur = el && el.nodeType === 1 ? el : null;
566 while (cur && cur !== document.documentElement) {
567 max = Math.max(max, numericZIndex(cur));
568 cur = cur.parentElement;
573 function autoConfirmZIndex(root, opts) {
574 if (opts && opts.zIndex > 0) return opts.zIndex;
576 max = Math.max(max, maxAncestorZIndex(opts && opts.source));
577 max = Math.max(max, maxAncestorZIndex(opts && opts.callerEl));
578 max = Math.max(max, maxAncestorZIndex(root));
579 max = Math.max(max, maxAncestorZIndex(opts && opts.mountEl));
580 document.querySelectorAll(".dbx-window, .dbx-window-overlay, .dbx-confirm-overlay, .dbx-confirm-dialog").forEach(el => {
581 if (el.offsetParent !== null || el === document.body || el.classList.contains("dbx-confirm-overlay")) {
582 max = Math.max(max, numericZIndex(el));
585 return Math.min(2147483647, max + 20);
588 function createDialogElements(root, opts) {
590 const mountEl = getMountEl(root, opts);
592 const overlay = document.createElement("div");
593 overlay.className = "dbx-confirm-overlay";
594 overlay.style.position = "fixed";
595 overlay.style.inset = "0";
596 overlay.style.zIndex = String(autoConfirmZIndex(root, opts));
597 overlay.style.background = "rgba(0,0,0,0.35)";
598 overlay.style.backdropFilter = "blur(2px)";
599 overlay.style.display = "flex";
600 overlay.style.alignItems = "center";
601 overlay.style.justifyContent = "center";
602 overlay.style.padding = "1rem";
604 const dialog = document.createElement("div");
605 dialog.className = "dbx-confirm-dialog card shadow-lg";
606 dialog.style.width = "100%";
607 dialog.style.maxWidth = "640px";
608 dialog.style.border = "1px solid rgba(62, 129, 218, 0.28)";
609 dialog.style.borderRadius = "10px";
610 dialog.style.overflow = "hidden";
611 dialog.style.boxShadow = "0 22px 70px rgba(28, 57, 99, 0.28)";
613 const header = document.createElement("div");
614 header.className = "card-header dbx-confirm-header d-flex align-items-center justify-content-between gap-3";
615 header.style.background = "linear-gradient(180deg, #d8ebff 0%, #a9cffc 100%)";
616 header.style.borderBottom = "1px solid rgba(58, 123, 208, 0.34)";
617 header.style.color = "#132033";
618 header.style.minHeight = "70px";
619 header.style.padding = "12px 16px";
621 const titleWrap = document.createElement("div");
622 titleWrap.className = "dbx-confirm-titlewrap d-flex align-items-center gap-3";
623 titleWrap.style.minWidth = "0";
625 const titleIcon = document.createElement("span");
626 titleIcon.className = "dbx-confirm-title-icon";
627 titleIcon.innerHTML = "<i class=\"bi bi-question-circle\"></i>";
628 titleIcon.style.alignItems = "center";
629 titleIcon.style.background = "rgba(255,255,255,0.58)";
630 titleIcon.style.border = "1px solid rgba(35, 103, 194, 0.28)";
631 titleIcon.style.borderRadius = "8px";
632 titleIcon.style.color = "#0d6efd";
633 titleIcon.style.display = "inline-flex";
634 titleIcon.style.flex = "0 0 46px";
635 titleIcon.style.fontSize = "1.35rem";
636 titleIcon.style.height = "46px";
637 titleIcon.style.justifyContent = "center";
638 titleIcon.style.width = "46px";
640 const title = document.createElement("div");
641 title.className = "dbx-confirm-title fw-semibold";
642 title.style.fontSize = "1.08rem";
643 title.style.lineHeight = "1.25";
644 title.style.minWidth = "0";
646 const btnClose = document.createElement("button");
647 btnClose.type = "button";
648 btnClose.className = "btn btn-sm btn-outline-primary";
649 btnClose.innerHTML = "<i class=\"bi bi-x-lg\"></i>";
650 btnClose.style.background = "rgba(255,255,255,0.52)";
651 btnClose.style.borderColor = "rgba(35, 103, 194, 0.34)";
652 btnClose.style.flex = "0 0 auto";
654 const body = document.createElement("div");
655 body.className = "card-body dbx-confirm-body";
656 body.style.background = "linear-gradient(180deg, #ffffff 0%, #f6f9fd 100%)";
657 body.style.padding = "18px 20px";
659 const question = document.createElement("div");
660 question.className = "dbx-confirm-question mb-2";
661 question.style.color = "#182231";
662 question.style.fontSize = "1rem";
663 question.style.lineHeight = "1.45";
665 const hint = document.createElement("div");
666 hint.className = "dbx-confirm-hint text-muted small mb-3";
667 hint.style.background = "#f8fafc";
668 hint.style.border = "1px solid #e2e8f0";
669 hint.style.borderRadius = "8px";
670 hint.style.padding = "10px 12px";
672 const footer = document.createElement("div");
673 footer.className = "card-footer d-flex justify-content-end gap-2 flex-wrap";
674 footer.style.background = "#f8fbff";
675 footer.style.borderTop = "1px solid #dce8f7";
676 footer.style.padding = "12px 16px";
678 titleWrap.appendChild(titleIcon);
679 titleWrap.appendChild(title);
680 header.appendChild(titleWrap);
681 header.appendChild(btnClose);
683 body.appendChild(question);
684 body.appendChild(hint);
686 dialog.appendChild(header);
687 dialog.appendChild(body);
688 dialog.appendChild(footer);
690 overlay.appendChild(dialog);
691 mountEl.appendChild(overlay);
708 function applyDialogState(entry) {
710 const opts = entry.options;
713 htmlOrText(ui.title, opts.title, opts.titlehtml);
714 htmlOrText(ui.question, opts.question, opts.questionhtml);
715 ui.titleIcon.innerHTML = "<i class=\"bi " + iconFromTitle(opts.title) + "\"></i>";
718 ui.hint.style.display = "";
719 htmlOrText(ui.hint, opts.hint, opts.hinthtml);
721 ui.hint.style.display = "none";
722 ui.hint.innerHTML = "";
725 ui.btnClose.style.display = opts.closable ? "" : "none";
727 ui.footer.innerHTML = "";
729 const actions = buildButtonsMarkup(opts);
731 actions.forEach(action => {
733 const btn = document.createElement("button");
735 btn.className = "btn";
737 if (action === "yes") {
738 btn.className += " btn-primary";
739 btn.setAttribute("data-confirm-action", "yes");
740 htmlOrText(btn, opts.labelyes, opts.labelhtml);
743 if (action === "no") {
744 btn.className += " btn-outline-secondary";
745 btn.setAttribute("data-confirm-action", "no");
746 htmlOrText(btn, opts.labelno, opts.labelhtml);
749 if (action === "cancel") {
750 btn.className += " btn-outline-danger";
751 btn.setAttribute("data-confirm-action", "cancel");
752 htmlOrText(btn, opts.labelcancel, opts.labelhtml);
755 ui.footer.appendChild(btn);
759 function closeDialog(entry, result) {
761 if (!entry || entry.closed) return;
768 if (ui && ui.overlay && ui.overlay.parentNode) {
769 ui.overlay.parentNode.removeChild(ui.overlay);
774 emit("confirm:result", {
776 action: result.action,
777 source: entry.options.source || null,
778 root: entry.options.root || null,
782 entry.resolve(result);
785 function continueOriginalAction(entry) {
787 const source = entry.options.source;
790 const type = elementType(source);
793 if (type === "link") {
794 source.__dbxConfirmBypass = true;
799 if (type === "button") {
801 const btnType = str(source.getAttribute("type"), "submit").toLowerCase();
802 const form = source.form || source.closest("form");
804 if ((btnType === "submit" || btnType === "image") && form) {
806 * FormData(form) enthält Submit-Buttons nicht. Zusätzlich
807 * liefert requestSubmit() in älteren WebViews nicht immer ein
808 * SubmitEvent.submitter. Ein kurzlebiges Hidden-Feld erhält
809 * deshalb name/value, während der normale Submit-Pfad weiter
810 * selbst zwischen AJAX und Browsernavigation entscheidet.
812 const submitName = readAttr(source, "name");
813 let submitProxy = null;
816 submitProxy = document.createElement("input");
817 submitProxy.type = "hidden";
818 submitProxy.name = submitName;
819 submitProxy.value = readAttr(source, "value");
820 submitProxy.setAttribute("data-dbx-confirm-submitter", "1");
821 form.appendChild(submitProxy);
824 source.__dbxConfirmBypass = true;
825 form.__dbxConfirmBypass = true;
828 if (typeof form.requestSubmit === "function") {
829 form.requestSubmit(source);
834 if (submitProxy && submitProxy.parentNode) {
835 submitProxy.parentNode.removeChild(submitProxy);
842 source.__dbxConfirmBypass = true;
847 if (type === "form") {
848 source.__dbxConfirmBypass = true;
850 if (typeof source.requestSubmit === "function") {
851 source.requestSubmit();
858 function openDialog(rawOptions) {
859 const labels = defaultLabels();
862 id: str(rawOptions.id, "dbx-confirm-" + (++_uid)),
863 root: ensureRoot(rawOptions.root),
864 source: rawOptions.source || rawOptions.callerEl || rawOptions.caller || null,
865 callerEl: rawOptions.callerEl || rawOptions.caller || rawOptions.source || null,
866 mountEl: rawOptions.mountEl || null,
867 zIndex: parseInt(rawOptions.zIndex, 10) || 0,
869 title: str(rawOptions.title, ""),
870 question: str(rawOptions.question, ""),
871 hint: str(rawOptions.hint, ""),
872 buttons: normalizeButtons(rawOptions.buttons),
874 titlehtml: bool(rawOptions.titlehtml, true),
875 questionhtml: bool(rawOptions.questionhtml, true),
876 hinthtml: bool(rawOptions.hinthtml, true),
877 labelhtml: bool(rawOptions.labelhtml, true),
879 labelyes: str(rawOptions.labelyes, labels.yes),
880 labelno: str(rawOptions.labelno, labels.no),
881 labelcancel: str(rawOptions.labelcancel, labels.cancel),
883 closable: bool(rawOptions.closable, true),
884 backdropclose: bool(rawOptions.backdropclose, false),
885 escclose: bool(rawOptions.escclose, true),
887 onyes: (typeof rawOptions.onyes === "function") ? rawOptions.onyes : null,
888 onno: (typeof rawOptions.onno === "function") ? rawOptions.onno : null,
889 oncancel: (typeof rawOptions.oncancel === "function") ? rawOptions.oncancel : null
892 if (_dialogs[opts.id]) {
893 dbx.confirm.close(opts.id, { action: "close" });
896 return new Promise((resolve) => {
898 const ui = createDialogElements(opts.root, opts);
909 _dialogs[opts.id] = entry;
911 applyDialogState(entry);
913 emit("confirm:open", {
915 source: opts.source || null,
916 root: opts.root || null,
920 ui.footer.addEventListener("click", function (e) {
922 const btn = e.target.closest("[data-confirm-action]");
925 const action = btn.getAttribute("data-confirm-action");
927 if (action === "yes" && opts.onyes) {
931 dbx.error("[dbx.confirm] onyes failed", err);
935 if (action === "no" && opts.onno) {
939 dbx.error("[dbx.confirm] onno failed", err);
943 if (action === "cancel" && opts.oncancel) {
945 opts.oncancel(entry);
947 dbx.error("[dbx.confirm] oncancel failed", err);
954 source: opts.source || null,
955 root: opts.root || null,
959 if (action === "yes" && opts.source) {
960 continueOriginalAction(entry);
964 ui.btnClose.addEventListener("click", function () {
966 if (!opts.closable) return;
971 source: opts.source || null,
972 root: opts.root || null,
977 ui.overlay.addEventListener("click", function (e) {
979 if (e.target !== ui.overlay) return;
980 if (!opts.backdropclose) return;
985 source: opts.source || null,
986 root: opts.root || null,
991 entry.keyHandler = function (e) {
993 if (e.key !== "Escape") return;
994 if (!opts.escclose) return;
999 source: opts.source || null,
1000 root: opts.root || null,
1005 document.addEventListener("keydown", entry.keyHandler, true);
1007 const oldResolve = entry.resolve;
1009 entry.resolve = function (result) {
1010 document.removeEventListener("keydown", entry.keyHandler, true);
1017 /* =========================================================
1019 * ========================================================= */
1021 dbx.confirm.open = function (options) {
1022 return openDialog(options || {});
1025 dbx.confirm.update = function (id, patch) {
1027 const entry = _dialogs[id];
1028 if (!entry) return false;
1030 const opts = entry.options;
1031 const data = patch || {};
1033 if ("title" in data) opts.title = str(data.title, "");
1034 if ("question" in data) opts.question = str(data.question, "");
1035 if ("hint" in data) opts.hint = str(data.hint, "");
1037 applyDialogState(entry);
1041 dbx.confirm.close = function (id, result) {
1043 const entry = _dialogs[id];
1044 if (!entry) return false;
1046 closeDialog(entry, {
1048 action: (result && result.action) ? result.action : "close",
1049 source: entry.options.source || null,
1050 root: entry.options.root || null,
1057 dbx.confirm.get = function (id) {
1058 return _dialogs[id] || null;
1062 /* =========================================================
1063 * DECLARE SCHEMA (Defaults + data-* Aliase)
1064 * ========================================================= */
1066 if (dbx.declare && typeof dbx.declare.registerSchema === "function") {
1067 const schemaLabels = defaultLabels();
1069 dbx.declare.registerSchema("confirm", {
1072 default: "dbxConfirm"
1075 default: "link,button,form",
1076 aliases: ["data-confirm-bind"]
1080 aliases: ["data-confirm-title", "data-title"]
1084 aliases: ["data-confirm-question", "data-confirm"]
1088 aliases: ["data-confirm-hint"]
1092 aliases: ["data-confirm-buttons"]
1096 aliases: ["data-confirm-titlehtml"]
1100 aliases: ["data-confirm-questionhtml"]
1104 aliases: ["data-confirm-hinthtml"]
1108 aliases: ["data-confirm-labelhtml"]
1111 default: schemaLabels.yes,
1112 aliases: ["data-confirm-labelyes"]
1115 default: schemaLabels.no,
1116 aliases: ["data-confirm-labelno"]
1119 default: schemaLabels.cancel,
1120 aliases: ["data-confirm-labelcancel"]
1124 aliases: ["data-confirm-closable"]
1128 aliases: ["data-confirm-backdropclose"]
1132 aliases: ["data-confirm-escclose"]
1137 dbx.declare.transforms.confirm = function (raw) {
1140 class: normalizeClassFilter(raw.class),
1141 bind: normalizeBind(raw.bind),
1142 title: str(raw.title, ""),
1143 question: str(raw.question, ""),
1144 hint: str(raw.hint, ""),
1145 buttons: normalizeButtons(raw.buttons),
1146 titlehtml: bool(raw.titlehtml, true),
1147 questionhtml: bool(raw.questionhtml, true),
1148 hinthtml: bool(raw.hinthtml, true),
1149 labelhtml: bool(raw.labelhtml, true),
1150 labelyes: str(raw.labelyes, schemaLabels.yes),
1151 labelno: str(raw.labelno, schemaLabels.no),
1152 labelcancel: str(raw.labelcancel, schemaLabels.cancel),
1153 closable: bool(raw.closable, true),
1154 backdropclose: bool(raw.backdropclose, false),
1155 escclose: bool(raw.escclose, true)
1161 /* =========================================================
1163 * ========================================================= */
1165 dbx.feature.register("confirm", {
1175 el.__dbxInitialized = el.__dbxInitialized || {};
1176 if (el.__dbxInitialized["confirm"]) return;
1177 el.__dbxInitialized["confirm"] = true;
1179 el.setAttribute("data-dbx-confirm-root", "1");
1182 dbx.log("[dbx.confirm] init", {
1183 rootId: el.id || "",
1184 configs: el._dbxConfirmConfigs
1187 el.addEventListener("click", function (e) {
1189 const source = e.target.closest("a, button, input[type='button'], input[type='submit'], input[type='image']");
1190 if (!source) return;
1191 if (!el.contains(source)) return;
1193 if (source.__dbxConfirmBypass === true) {
1194 delete source.__dbxConfirmBypass;
1198 const nearestRoot = source.closest("[data-dbx-confirm-root='1']");
1199 if (nearestRoot !== el) return;
1201 const type = elementType(source);
1202 const cfgMatch = findMatchingConfig(el, source, type);
1204 if (!cfgMatch) return;
1208 const options = readOptionsFromElement(source, cfgMatch);
1211 openDialog(options).catch(err => {
1212 dbx.error("[dbx.confirm] open failed", err);
1216 el.addEventListener("submit", function (e) {
1218 const form = e.target.closest("form");
1220 if (!el.contains(form)) return;
1222 if (form.__dbxConfirmBypass === true) {
1223 delete form.__dbxConfirmBypass;
1227 const nearestRoot = form.closest("[data-dbx-confirm-root='1']");
1228 if (nearestRoot !== el) return;
1230 const type = elementType(form);
1231 const cfgMatch = findMatchingConfig(el, form, type);
1233 if (!cfgMatch) return;
1237 const options = readOptionsFromElement(form, cfgMatch);
1240 openDialog(options).catch(err => {
1241 dbx.error("[dbx.confirm] open failed", err);
1250 if (el.__dbxInitialized && el.__dbxInitialized["confirm"]) {
1251 delete el.__dbxInitialized["confirm"];
1254 delete el._dbxConfirmConfigs;
1255 el.removeAttribute("data-dbx-confirm-root");
1257 dbx.log("[dbx.confirm] destroy", {
1264})(window, document);