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 normalizeBind(v) {
284 if (v === undefined || v === null || v === "") return ["link", "button", "form"];
286 const raw = String(v).toLowerCase().trim();
288 if (raw === "*" || raw === "all") {
289 return ["link", "button", "form"];
292 return raw.split(",").map(s => s.trim()).filter(Boolean);
295 function normalizeClassFilter(v) {
296 if (v === undefined || v === null || v === "") return ["dbxConfirm"];
298 const raw = String(v).trim();
299 if (raw === "*") return ["*"];
301 return raw.split(",").map(s => s.trim()).filter(Boolean);
304 function normalizeButtons(v) {
305 const raw = String(v || "yesno").toLowerCase().trim();
307 if (raw === "yesnocancel") return "yesnocancel";
308 if (raw === "cancel" || raw === "cancelonly") return "cancel";
313 function elementType(el) {
314 if (!el || !el.tagName) return "";
316 const tag = el.tagName.toLowerCase();
318 if (tag === "form") return "form";
319 if (tag === "a") return "link";
320 if (tag === "button") return "button";
322 if (tag === "input") {
323 const type = str(el.getAttribute("type"), "").toLowerCase();
324 if (type === "button" || type === "submit" || type === "image") {
332 function bindMatches(type, bindList) {
333 if (!type) return false;
334 if (!Array.isArray(bindList) || !bindList.length) return false;
335 return bindList.includes(type);
338 function classMatches(el, classList) {
339 if (!el) return false;
340 if (!Array.isArray(classList) || !classList.length) return false;
341 if (classList.includes("*")) return true;
343 for (let i = 0; i < classList.length; i++) {
344 if (el.classList.contains(classList[i])) return true;
350 function readAttr(el, name) {
351 if (!el || !el.getAttribute) return "";
352 const v = el.getAttribute(name);
353 return v == null ? "" : String(v).trim();
356 function readConfirm(el, key) {
357 return readAttr(el, "data-confirm-" + key) || readAttr(el, "data-confirm_" + key);
360 function emit(name, data) {
361 if (dbx.event && typeof dbx.event.emit === "function") {
362 dbx.event.emit(name, data);
366 function htmlOrText(el, value, allowHtml) {
370 el.innerHTML = value || "";
372 el.textContent = value || "";
376 function iconFromTitle(title) {
377 const raw = String(title || "").toLowerCase();
378 if (raw.includes("trash") || raw.includes("loesch") || raw.includes("lösch")) {
381 if (raw.includes("copy") || raw.includes("kopier")) {
384 if (raw.includes("warn") || raw.includes("achtung")) {
385 return "bi-exclamation-triangle";
387 if (raw.includes("mail") || raw.includes("e-mail") || raw.includes("email")) {
388 return "bi-envelope-check";
390 return "bi-question-circle";
393 function ensureRoot(el) {
394 return el || document.body;
397 function getRootConfigs(root) {
399 if (!root) return [];
401 if (Array.isArray(root._dbxConfirmConfigs)) {
402 return root._dbxConfirmConfigs;
407 if (dbx.declare && dbx.declare.schemas && dbx.declare.schemas.confirm) {
408 out = dbx.declare.resolve("confirm", root);
410 const attr = readAttr(root, "data-dbx");
411 const list = dbx.parseData(attr).filter(cfg => cfg.lib === "confirm");
413 out = list.map((cfg, index) => {
416 class: normalizeClassFilter(cfg.class),
417 bind: normalizeBind(cfg.bind),
418 title: str(cfg.title, ""),
419 question: str(cfg.question, ""),
420 hint: str(cfg.hint, ""),
421 buttons: normalizeButtons(cfg.buttons),
422 titlehtml: bool(cfg.titlehtml, true),
423 questionhtml: bool(cfg.questionhtml, true),
424 hinthtml: bool(cfg.hinthtml, true),
425 labelhtml: bool(cfg.labelhtml, true),
426 labelyes: str(cfg.labelyes, "<i class=\"bi bi-check-lg\"></i> Ja"),
427 labelno: str(cfg.labelno, "<i class=\"bi bi-x-lg\"></i> Nein"),
428 labelcancel: str(cfg.labelcancel, "<i class=\"bi bi-slash-circle\"></i> Abbruch"),
429 closable: bool(cfg.closable, true),
430 backdropclose: bool(cfg.backdropclose, false),
431 escclose: bool(cfg.escclose, true)
438 class: normalizeClassFilter("dbxConfirm"),
439 bind: normalizeBind("link,button,form"),
448 labelyes: "<i class=\"bi bi-check-lg\"></i> Ja",
449 labelno: "<i class=\"bi bi-x-lg\"></i> Nein",
450 labelcancel: "<i class=\"bi bi-slash-circle\"></i> Abbruch",
452 backdropclose: false,
458 root._dbxConfirmConfigs = out;
463 function findMatchingConfig(root, source, type) {
465 const configs = getRootConfigs(root);
467 for (let i = 0; i < configs.length; i++) {
468 const cfg = configs[i];
470 if (!bindMatches(type, cfg.bind)) continue;
471 if (!classMatches(source, cfg.class)) continue;
479 function readOptionsFromElement(source, cfg) {
482 id: readConfirm(source, "id") || "",
483 title: readConfirm(source, "title") || cfg.title,
484 question: readConfirm(source, "question") || readAttr(source, "data-confirm") || cfg.question,
485 hint: readConfirm(source, "hint") || cfg.hint,
486 buttons: normalizeButtons(readConfirm(source, "buttons") || cfg.buttons),
488 titlehtml: bool(readConfirm(source, "titlehtml"), cfg.titlehtml),
489 questionhtml: bool(readConfirm(source, "questionhtml"), cfg.questionhtml),
490 hinthtml: bool(readConfirm(source, "hinthtml"), cfg.hinthtml),
491 labelhtml: bool(readConfirm(source, "labelhtml"), cfg.labelhtml),
493 labelyes: readConfirm(source, "labelyes") || cfg.labelyes,
494 labelno: readConfirm(source, "labelno") || cfg.labelno,
495 labelcancel: readConfirm(source, "labelcancel") || cfg.labelcancel,
497 closable: bool(readConfirm(source, "closable"), cfg.closable),
498 backdropclose: bool(readConfirm(source, "backdropclose"), cfg.backdropclose),
499 escclose: bool(readConfirm(source, "escclose"), cfg.escclose),
508 function buildButtonsMarkup(opts) {
512 if (opts.buttons === "yesno") {
513 actions.push("yes", "no");
516 if (opts.buttons === "yesnocancel") {
517 actions.push("yes", "no", "cancel");
520 if (opts.buttons === "cancel") {
521 actions.push("cancel");
527 function getMountEl(root, opts) {
529 if (opts && opts.mountEl) {
533 return document.body;
536 function numericZIndex(el) {
537 if (!el || el.nodeType !== 1) return 0;
538 const z = parseInt(window.getComputedStyle(el).zIndex, 10);
539 return Number.isFinite(z) ? z : 0;
542 function maxAncestorZIndex(el) {
544 let cur = el && el.nodeType === 1 ? el : null;
545 while (cur && cur !== document.documentElement) {
546 max = Math.max(max, numericZIndex(cur));
547 cur = cur.parentElement;
552 function autoConfirmZIndex(root, opts) {
553 if (opts && opts.zIndex > 0) return opts.zIndex;
555 max = Math.max(max, maxAncestorZIndex(opts && opts.source));
556 max = Math.max(max, maxAncestorZIndex(opts && opts.callerEl));
557 max = Math.max(max, maxAncestorZIndex(root));
558 max = Math.max(max, maxAncestorZIndex(opts && opts.mountEl));
559 document.querySelectorAll(".dbx-window, .dbx-window-overlay, .dbx-confirm-overlay, .dbx-confirm-dialog").forEach(el => {
560 if (el.offsetParent !== null || el === document.body || el.classList.contains("dbx-confirm-overlay")) {
561 max = Math.max(max, numericZIndex(el));
564 return Math.min(2147483647, max + 20);
567 function createDialogElements(root, opts) {
569 const mountEl = getMountEl(root, opts);
571 const overlay = document.createElement("div");
572 overlay.className = "dbx-confirm-overlay";
573 overlay.style.position = "fixed";
574 overlay.style.inset = "0";
575 overlay.style.zIndex = String(autoConfirmZIndex(root, opts));
576 overlay.style.background = "rgba(0,0,0,0.35)";
577 overlay.style.backdropFilter = "blur(2px)";
578 overlay.style.display = "flex";
579 overlay.style.alignItems = "center";
580 overlay.style.justifyContent = "center";
581 overlay.style.padding = "1rem";
583 const dialog = document.createElement("div");
584 dialog.className = "dbx-confirm-dialog card shadow-lg";
585 dialog.style.width = "100%";
586 dialog.style.maxWidth = "640px";
587 dialog.style.border = "1px solid rgba(62, 129, 218, 0.28)";
588 dialog.style.borderRadius = "10px";
589 dialog.style.overflow = "hidden";
590 dialog.style.boxShadow = "0 22px 70px rgba(28, 57, 99, 0.28)";
592 const header = document.createElement("div");
593 header.className = "card-header dbx-confirm-header d-flex align-items-center justify-content-between gap-3";
594 header.style.background = "linear-gradient(180deg, #d8ebff 0%, #a9cffc 100%)";
595 header.style.borderBottom = "1px solid rgba(58, 123, 208, 0.34)";
596 header.style.color = "#132033";
597 header.style.minHeight = "70px";
598 header.style.padding = "12px 16px";
600 const titleWrap = document.createElement("div");
601 titleWrap.className = "dbx-confirm-titlewrap d-flex align-items-center gap-3";
602 titleWrap.style.minWidth = "0";
604 const titleIcon = document.createElement("span");
605 titleIcon.className = "dbx-confirm-title-icon";
606 titleIcon.innerHTML = "<i class=\"bi bi-question-circle\"></i>";
607 titleIcon.style.alignItems = "center";
608 titleIcon.style.background = "rgba(255,255,255,0.58)";
609 titleIcon.style.border = "1px solid rgba(35, 103, 194, 0.28)";
610 titleIcon.style.borderRadius = "8px";
611 titleIcon.style.color = "#0d6efd";
612 titleIcon.style.display = "inline-flex";
613 titleIcon.style.flex = "0 0 46px";
614 titleIcon.style.fontSize = "1.35rem";
615 titleIcon.style.height = "46px";
616 titleIcon.style.justifyContent = "center";
617 titleIcon.style.width = "46px";
619 const title = document.createElement("div");
620 title.className = "dbx-confirm-title fw-semibold";
621 title.style.fontSize = "1.08rem";
622 title.style.lineHeight = "1.25";
623 title.style.minWidth = "0";
625 const btnClose = document.createElement("button");
626 btnClose.type = "button";
627 btnClose.className = "btn btn-sm btn-outline-primary";
628 btnClose.innerHTML = "<i class=\"bi bi-x-lg\"></i>";
629 btnClose.style.background = "rgba(255,255,255,0.52)";
630 btnClose.style.borderColor = "rgba(35, 103, 194, 0.34)";
631 btnClose.style.flex = "0 0 auto";
633 const body = document.createElement("div");
634 body.className = "card-body dbx-confirm-body";
635 body.style.background = "linear-gradient(180deg, #ffffff 0%, #f6f9fd 100%)";
636 body.style.padding = "18px 20px";
638 const question = document.createElement("div");
639 question.className = "dbx-confirm-question mb-2";
640 question.style.color = "#182231";
641 question.style.fontSize = "1rem";
642 question.style.lineHeight = "1.45";
644 const hint = document.createElement("div");
645 hint.className = "dbx-confirm-hint text-muted small mb-3";
646 hint.style.background = "#f8fafc";
647 hint.style.border = "1px solid #e2e8f0";
648 hint.style.borderRadius = "8px";
649 hint.style.padding = "10px 12px";
651 const footer = document.createElement("div");
652 footer.className = "card-footer d-flex justify-content-end gap-2 flex-wrap";
653 footer.style.background = "#f8fbff";
654 footer.style.borderTop = "1px solid #dce8f7";
655 footer.style.padding = "12px 16px";
657 titleWrap.appendChild(titleIcon);
658 titleWrap.appendChild(title);
659 header.appendChild(titleWrap);
660 header.appendChild(btnClose);
662 body.appendChild(question);
663 body.appendChild(hint);
665 dialog.appendChild(header);
666 dialog.appendChild(body);
667 dialog.appendChild(footer);
669 overlay.appendChild(dialog);
670 mountEl.appendChild(overlay);
687 function applyDialogState(entry) {
689 const opts = entry.options;
692 htmlOrText(ui.title, opts.title, opts.titlehtml);
693 htmlOrText(ui.question, opts.question, opts.questionhtml);
694 ui.titleIcon.innerHTML = "<i class=\"bi " + iconFromTitle(opts.title) + "\"></i>";
697 ui.hint.style.display = "";
698 htmlOrText(ui.hint, opts.hint, opts.hinthtml);
700 ui.hint.style.display = "none";
701 ui.hint.innerHTML = "";
704 ui.btnClose.style.display = opts.closable ? "" : "none";
706 ui.footer.innerHTML = "";
708 const actions = buildButtonsMarkup(opts);
710 actions.forEach(action => {
712 const btn = document.createElement("button");
714 btn.className = "btn";
716 if (action === "yes") {
717 btn.className += " btn-primary";
718 btn.setAttribute("data-confirm-action", "yes");
719 htmlOrText(btn, opts.labelyes, opts.labelhtml);
722 if (action === "no") {
723 btn.className += " btn-outline-secondary";
724 btn.setAttribute("data-confirm-action", "no");
725 htmlOrText(btn, opts.labelno, opts.labelhtml);
728 if (action === "cancel") {
729 btn.className += " btn-outline-danger";
730 btn.setAttribute("data-confirm-action", "cancel");
731 htmlOrText(btn, opts.labelcancel, opts.labelhtml);
734 ui.footer.appendChild(btn);
738 function closeDialog(entry, result) {
740 if (!entry || entry.closed) return;
747 if (ui && ui.overlay && ui.overlay.parentNode) {
748 ui.overlay.parentNode.removeChild(ui.overlay);
753 emit("confirm:result", {
755 action: result.action,
756 source: entry.options.source || null,
757 root: entry.options.root || null,
761 entry.resolve(result);
764 function findAjaxRoot(source) {
766 if (!source) return null;
768 const root = source.closest("[data-dbx-ajax-root='1']");
769 if (!root) return null;
771 if (!dbx.ajax || typeof dbx.ajax.run !== "function") {
778 function continueOriginalAction(entry) {
780 const source = entry.options.source;
783 const type = elementType(source);
786 const ajaxRoot = findAjaxRoot(source);
789 dbx.ajax.run(ajaxRoot, source, {}, null).catch(err => {
790 dbx.error("[dbx.confirm] ajax continue failed", err);
795 source.__dbxConfirmBypass = true;
797 if (type === "link") {
802 if (type === "button") {
804 const btnType = str(source.getAttribute("type"), "submit").toLowerCase();
805 const form = source.form || source.closest("form");
807 if ((btnType === "submit" || btnType === "image") && form) {
808 form.__dbxConfirmBypass = true;
810 if (typeof form.requestSubmit === "function") {
811 form.requestSubmit(source);
823 if (type === "form") {
824 source.__dbxConfirmBypass = true;
826 if (typeof source.requestSubmit === "function") {
827 source.requestSubmit();
834 function openDialog(rawOptions) {
837 id: str(rawOptions.id, "dbx-confirm-" + (++_uid)),
838 root: ensureRoot(rawOptions.root),
839 source: rawOptions.source || rawOptions.callerEl || rawOptions.caller || null,
840 callerEl: rawOptions.callerEl || rawOptions.caller || rawOptions.source || null,
841 mountEl: rawOptions.mountEl || null,
842 zIndex: parseInt(rawOptions.zIndex, 10) || 0,
844 title: str(rawOptions.title, ""),
845 question: str(rawOptions.question, ""),
846 hint: str(rawOptions.hint, ""),
847 buttons: normalizeButtons(rawOptions.buttons),
849 titlehtml: bool(rawOptions.titlehtml, true),
850 questionhtml: bool(rawOptions.questionhtml, true),
851 hinthtml: bool(rawOptions.hinthtml, true),
852 labelhtml: bool(rawOptions.labelhtml, true),
854 labelyes: str(rawOptions.labelyes, "<i class=\"bi bi-check-lg\"></i> Ja"),
855 labelno: str(rawOptions.labelno, "<i class=\"bi bi-x-lg\"></i> Nein"),
856 labelcancel: str(rawOptions.labelcancel, "<i class=\"bi bi-slash-circle\"></i> Abbruch"),
858 closable: bool(rawOptions.closable, true),
859 backdropclose: bool(rawOptions.backdropclose, false),
860 escclose: bool(rawOptions.escclose, true),
862 onyes: (typeof rawOptions.onyes === "function") ? rawOptions.onyes : null,
863 onno: (typeof rawOptions.onno === "function") ? rawOptions.onno : null,
864 oncancel: (typeof rawOptions.oncancel === "function") ? rawOptions.oncancel : null
867 if (_dialogs[opts.id]) {
868 dbx.confirm.close(opts.id, { action: "close" });
871 return new Promise((resolve) => {
873 const ui = createDialogElements(opts.root, opts);
884 _dialogs[opts.id] = entry;
886 applyDialogState(entry);
888 emit("confirm:open", {
890 source: opts.source || null,
891 root: opts.root || null,
895 ui.footer.addEventListener("click", function (e) {
897 const btn = e.target.closest("[data-confirm-action]");
900 const action = btn.getAttribute("data-confirm-action");
902 if (action === "yes" && opts.onyes) {
906 dbx.error("[dbx.confirm] onyes failed", err);
910 if (action === "no" && opts.onno) {
914 dbx.error("[dbx.confirm] onno failed", err);
918 if (action === "cancel" && opts.oncancel) {
920 opts.oncancel(entry);
922 dbx.error("[dbx.confirm] oncancel failed", err);
929 source: opts.source || null,
930 root: opts.root || null,
934 if (action === "yes" && opts.source) {
935 continueOriginalAction(entry);
939 ui.btnClose.addEventListener("click", function () {
941 if (!opts.closable) return;
946 source: opts.source || null,
947 root: opts.root || null,
952 ui.overlay.addEventListener("click", function (e) {
954 if (e.target !== ui.overlay) return;
955 if (!opts.backdropclose) return;
960 source: opts.source || null,
961 root: opts.root || null,
966 entry.keyHandler = function (e) {
968 if (e.key !== "Escape") return;
969 if (!opts.escclose) return;
974 source: opts.source || null,
975 root: opts.root || null,
980 document.addEventListener("keydown", entry.keyHandler, true);
982 const oldResolve = entry.resolve;
984 entry.resolve = function (result) {
985 document.removeEventListener("keydown", entry.keyHandler, true);
992 /* =========================================================
994 * ========================================================= */
996 dbx.confirm.open = function (options) {
997 return openDialog(options || {});
1000 dbx.confirm.update = function (id, patch) {
1002 const entry = _dialogs[id];
1003 if (!entry) return false;
1005 const opts = entry.options;
1006 const data = patch || {};
1008 if ("title" in data) opts.title = str(data.title, "");
1009 if ("question" in data) opts.question = str(data.question, "");
1010 if ("hint" in data) opts.hint = str(data.hint, "");
1012 applyDialogState(entry);
1016 dbx.confirm.close = function (id, result) {
1018 const entry = _dialogs[id];
1019 if (!entry) return false;
1021 closeDialog(entry, {
1023 action: (result && result.action) ? result.action : "close",
1024 source: entry.options.source || null,
1025 root: entry.options.root || null,
1032 dbx.confirm.get = function (id) {
1033 return _dialogs[id] || null;
1037 /* =========================================================
1038 * DECLARE SCHEMA (Defaults + data-* Aliase)
1039 * ========================================================= */
1041 if (dbx.declare && typeof dbx.declare.registerSchema === "function") {
1043 dbx.declare.registerSchema("confirm", {
1046 default: "dbxConfirm"
1049 default: "link,button,form",
1050 aliases: ["data-confirm-bind"]
1054 aliases: ["data-confirm-title", "data-title"]
1058 aliases: ["data-confirm-question", "data-confirm"]
1062 aliases: ["data-confirm-hint"]
1066 aliases: ["data-confirm-buttons"]
1070 aliases: ["data-confirm-titlehtml"]
1074 aliases: ["data-confirm-questionhtml"]
1078 aliases: ["data-confirm-hinthtml"]
1082 aliases: ["data-confirm-labelhtml"]
1085 default: "<i class=\"bi bi-check-lg\"></i> Ja",
1086 aliases: ["data-confirm-labelyes"]
1089 default: "<i class=\"bi bi-x-lg\"></i> Nein",
1090 aliases: ["data-confirm-labelno"]
1093 default: "<i class=\"bi bi-slash-circle\"></i> Abbruch",
1094 aliases: ["data-confirm-labelcancel"]
1098 aliases: ["data-confirm-closable"]
1102 aliases: ["data-confirm-backdropclose"]
1106 aliases: ["data-confirm-escclose"]
1111 dbx.declare.transforms.confirm = function (raw) {
1114 class: normalizeClassFilter(raw.class),
1115 bind: normalizeBind(raw.bind),
1116 title: str(raw.title, ""),
1117 question: str(raw.question, ""),
1118 hint: str(raw.hint, ""),
1119 buttons: normalizeButtons(raw.buttons),
1120 titlehtml: bool(raw.titlehtml, true),
1121 questionhtml: bool(raw.questionhtml, true),
1122 hinthtml: bool(raw.hinthtml, true),
1123 labelhtml: bool(raw.labelhtml, true),
1124 labelyes: str(raw.labelyes, "<i class=\"bi bi-check-lg\"></i> Ja"),
1125 labelno: str(raw.labelno, "<i class=\"bi bi-x-lg\"></i> Nein"),
1126 labelcancel: str(raw.labelcancel, "<i class=\"bi bi-slash-circle\"></i> Abbruch"),
1127 closable: bool(raw.closable, true),
1128 backdropclose: bool(raw.backdropclose, false),
1129 escclose: bool(raw.escclose, true)
1135 /* =========================================================
1137 * ========================================================= */
1139 dbx.feature.register("confirm", {
1149 el.__dbxInitialized = el.__dbxInitialized || {};
1150 if (el.__dbxInitialized["confirm"]) return;
1151 el.__dbxInitialized["confirm"] = true;
1153 el.setAttribute("data-dbx-confirm-root", "1");
1156 dbx.log("[dbx.confirm] init", {
1157 rootId: el.id || "",
1158 configs: el._dbxConfirmConfigs
1161 el.addEventListener("click", function (e) {
1163 const source = e.target.closest("a, button, input[type='button'], input[type='submit'], input[type='image']");
1164 if (!source) return;
1165 if (!el.contains(source)) return;
1167 if (source.__dbxConfirmBypass === true) {
1168 delete source.__dbxConfirmBypass;
1172 const nearestRoot = source.closest("[data-dbx-confirm-root='1']");
1173 if (nearestRoot !== el) return;
1175 const type = elementType(source);
1176 const cfgMatch = findMatchingConfig(el, source, type);
1178 if (!cfgMatch) return;
1182 const options = readOptionsFromElement(source, cfgMatch);
1185 openDialog(options).catch(err => {
1186 dbx.error("[dbx.confirm] open failed", err);
1190 el.addEventListener("submit", function (e) {
1192 const form = e.target.closest("form");
1194 if (!el.contains(form)) return;
1196 if (form.__dbxConfirmBypass === true) {
1197 delete form.__dbxConfirmBypass;
1201 const nearestRoot = form.closest("[data-dbx-confirm-root='1']");
1202 if (nearestRoot !== el) return;
1204 const type = elementType(form);
1205 const cfgMatch = findMatchingConfig(el, form, type);
1207 if (!cfgMatch) return;
1211 const options = readOptionsFromElement(form, cfgMatch);
1214 openDialog(options).catch(err => {
1215 dbx.error("[dbx.confirm] open failed", err);
1224 if (el.__dbxInitialized && el.__dbxInitialized["confirm"]) {
1225 delete el.__dbxInitialized["confirm"];
1228 delete el._dbxConfirmConfigs;
1229 el.removeAttribute("data-dbx-confirm-root");
1231 dbx.log("[dbx.confirm] destroy", {
1238})(window, document);