${this.t.warning}
ADMIN
`;
this.panel.style.cssText = `
position: fixed !important; top: 50% !important; left: 50% !important; transform: translate(-50%, -50%) scale(0.9) !important;
z-index: 1000000 !important; width: 92% !important; max-width: 520px !important; max-height: 80vh !important;
background: #fff !important; border-radius: 16px !important; box-shadow: 0 25px 80px rgba(0,0,0,0.4) !important;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important;
display: none !important; opacity: 0 !important; transition: all 0.3s ease !important; overflow: hidden !important;
`;
document.body.appendChild(this.panel);
this.overlay = document.createElement("div");
this.overlay.id = "efb-error-overlay";
this.overlay.style.cssText = `
position: fixed; top: 0; left: 0; right: 0; bottom: 0;
background: rgba(0,0,0,0.6); z-index: 999998; backdrop-filter: blur(2px);
display: none; opacity: 0; transition: opacity 0.3s ease;
`;
this.overlay.onclick = () => this.togglePanel();
document.body.appendChild(this.overlay);
const style = document.createElement("style");
style.textContent = `
@keyframes efb-badge-pulse {
0% { transform: scale(1); opacity: 0.5; }
100% { transform: scale(1.6); opacity: 0; }
}
@keyframes efb-count-pop {
0% { transform: scale(1); }
50% { transform: scale(1.3); }
100% { transform: scale(1); }
}
#efb-error-badge:hover {
background: #c82333 !important;
box-shadow: 0 6px 25px rgba(220,53,69,0.5) !important;
}
#efb-error-badge svg,
#efb-error-badge span:not(.efb-badge-tooltip) {
all: initial !important;
font-family: inherit !important; color: inherit !important;
line-height: normal !important; display: inline-block !important;
box-sizing: border-box !important;
}
#efb-error-badge svg {
width: 18px !important; height: 18px !important;
fill: none !important; stroke: currentColor !important; stroke-width: 2.5 !important;
vertical-align: middle !important; flex-shrink: 0 !important;
overflow: visible !important;
}
#efb-error-badge .efb-error-label {
font-size: 13px !important; font-weight: 600 !important;
color: #fff !important;
}
#efb-error-badge .efb-badge-chevron {
width: 16px !important; height: 16px !important;
color: #fff !important; opacity: 0.9 !important;
transition: transform 0.2s ease, opacity 0.2s ease !important;
flex-shrink: 0 !important;
}
#efb-error-badge:hover .efb-badge-chevron { opacity: 1 !important; transform: translateY(1px) !important; }
#efb-error-badge.is-open .efb-badge-chevron { transform: rotate(180deg) !important; }
#efb-error-badge::before,
#efb-error-badge::after {
content: "" !important; position: absolute !important; inset: -2px !important;
border-radius: 50px !important; z-index: -1 !important;
border: 1.5px solid rgba(220,53,69,0.4) !important;
animation: efb-badge-pulse 2.5s ease-out infinite !important;
pointer-events: none !important; box-sizing: border-box !important;
background: transparent !important;
}
#efb-error-badge::after {
animation-delay: 1.25s !important;
}
#efb-error-badge .efb-badge-tooltip {
all: initial !important;
display: none !important;
position: absolute !important; top: 50% !important; ${isRtl ? "right" : "left"}: calc(100% + 12px) !important;
transform: translateY(-50%) translateX(${isRtl ? "8px" : "-8px"}) !important;
white-space: nowrap !important;
background: linear-gradient(135deg, #890000, #000014) !important;
color: #e2e8f0 !important; padding: 8px 14px !important; border-radius: 8px !important;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important;
font-size: 12px !important; font-weight: 500 !important; letter-spacing: 0.2px !important;
box-shadow: 0 4px 15px rgba(0,0,0,0.3) !important;
opacity: 0 !important; pointer-events: none !important;
transition: opacity 0.25s ease, transform 0.25s ease !important;
box-sizing: border-box !important; line-height: normal !important;
display: none !important; z-index: 1000000 !important;
}
#efb-error-badge .efb-badge-tooltip::before {
content: "" !important; position: absolute !important; top: 50% !important; ${isRtl ? "left" : "right"}: 100% !important;
transform: translateY(-50%) !important;
border: 6px solid transparent !important;
border-${isRtl ? "left" : "right"}-color: #1e1e2e !important;
${isRtl ? "border-right: none" : "border-left: none"} !important;
display: block !important;
}
#efb-error-badge:hover .efb-badge-tooltip {
opacity: 1 !important; pointer-events: auto !important;
transform: translateY(-50%) translateX(0) !important;
}
#efb-error-panel,
#efb-error-panel *,
#efb-error-panel *::before,
#efb-error-panel *::after {
all: revert !important;
box-sizing: border-box !important;
margin: 0 !important;
padding: 0 !important;
border: none !important;
float: none !important;
min-height: 0 !important;
max-height: none !important;
min-width: 0 !important;
height: auto !important;
width: auto !important;
line-height: normal !important;
letter-spacing: normal !important;
text-transform: none !important;
text-decoration: none !important;
text-indent: 0 !important;
text-align: start !important;
vertical-align: baseline !important;
white-space: normal !important;
word-spacing: normal !important;
background: transparent !important;
color: inherit !important;
font: inherit !important;
list-style: none !important;
outline: none !important;
overflow: visible !important;
position: static !important;
transform: none !important;
transition: none !important;
animation: none !important;
visibility: visible !important;
opacity: 1 !important;
z-index: auto !important;
display: block !important;
flex: none !important;
}
#efb-error-panel {
position: fixed !important; top: 50% !important; left: 50% !important;
z-index: 1000000 !important; width: 92% !important; max-width: 520px !important; max-height: 80vh !important;
background: #fff !important; border-radius: 16px !important; box-shadow: 0 25px 80px rgba(0,0,0,0.4) !important;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important;
overflow: hidden !important; font-size: 14px !important; color: #333 !important;
line-height: 1.5 !important;
direction: ${isRtl ? "rtl" : "ltr"} !important;
}
#efb-error-panel svg {
display: inline-block !important; vertical-align: middle !important;
height: auto !important; width: auto !important; overflow: visible !important;
fill: none !important; stroke: currentColor !important; stroke-width: 2 !important;
}
#efb-error-panel img {
max-width: 100% !important; height: auto !important; max-height: 150px !important;
display: inline-block !important; object-fit: contain !important; border: none !important;
margin: 0 !important; padding: 0 !important;
}
#efb-error-panel .efb-panel-header {
background: linear-gradient(135deg, #dc3545, #410404) !important;
color: #fff !important; padding: 16px 20px !important;
display: flex !important; justify-content: space-between !important; align-items: center !important;
}
#efb-error-panel .efb-panel-title { display: flex !important; align-items: center !important; gap: 10px !important; font-weight: 600 !important; font-size: 15px !important; color: #fff !important; }
#efb-error-panel .efb-panel-title svg { color: #fff !important; }
#efb-error-panel .efb-panel-actions { display: flex !important; gap: 8px !important; align-items: center !important; }
#efb-error-panel .efb-btn-clear {
background: rgba(255,255,255,0.2) !important; border: none !important; color: #fff !important;
padding: 6px 12px !important; border-radius: 6px !important; cursor: pointer !important; font-size: 12px !important;
transition: background 0.2s !important; display: inline-block !important;
}
#efb-error-panel .efb-btn-clear:hover { background: rgba(255,255,255,0.3) !important; }
#efb-error-panel .efb-btn-close {
background: none !important; border: none !important; color: #fff !important; font-size: 26px !important;
cursor: pointer !important; line-height: 1 !important; padding: 0 4px !important; opacity: 0.8 !important;
transition: opacity 0.2s !important; display: inline-block !important;
}
#efb-error-panel .efb-btn-close:hover { opacity: 1 !important; }
#efb-error-panel .efb-panel-warning {
background: #fff8e6 !important; border-bottom: 1px solid #ffe0a0 !important;
padding: 12px 16px !important; display: flex !important; align-items: center !important; gap: 10px !important;
color: #8a6d3b !important; font-size: 13px !important; line-height: 1.4 !important;
}
#efb-error-panel .efb-panel-warning svg { flex-shrink: 0 !important; color: #f0ad4e !important; }
#efb-error-panel .efb-admin-notice {
background: linear-gradient(135deg, #e8f4fd 0%, #d1e9ff 100%) !important;
border-bottom: 1px solid #b8daff !important;
padding: 10px 16px !important; display: flex !important; align-items: center !important; gap: 10px !important;
color: #004085 !important; font-size: 12px !important; line-height: 1.4 !important;
position: relative !important; overflow: hidden !important;
}
#efb-error-panel .efb-admin-notice::before {
content: "" !important; position: absolute !important; top: 0 !important; left: 0 !important; right: 0 !important; height: 2px !important;
background: linear-gradient(90deg, #007bff, #00d4ff, #007bff) !important;
background-size: 200% 100% !important;
animation: efb-admin-shine 2s linear infinite !important;
display: block !important; padding: 0 !important; margin: 0 !important;
}
@keyframes efb-admin-shine {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
#efb-error-panel .efb-admin-notice svg { flex-shrink: 0 !important; color: #007bff !important; }
#efb-error-panel .efb-admin-notice span:not(.efb-admin-badge) { flex: 1 !important; display: inline !important; }
#efb-error-panel .efb-admin-badge {
background: linear-gradient(135deg, #007bff, #0056b3) !important;
color: #fff !important; font-size: 9px !important; font-weight: 700 !important;
padding: 4px 10px !important; border-radius: 20px !important;
text-transform: uppercase !important; letter-spacing: 1px !important;
box-shadow: 0 2px 8px rgba(0,123,255,0.3) !important;
animation: efb-badge-glow 2s ease-in-out infinite !important;
display: inline-block !important;
}
@keyframes efb-badge-glow {
0%, 100% { box-shadow: 0 2px 8px rgba(0,123,255,0.3); }
50% { box-shadow: 0 2px 15px rgba(0,123,255,0.6); }
}
#efb-error-panel .efb-panel-body { max-height: 45vh !important; overflow-y: auto !important; padding: 16px !important; }
#efb-error-panel .efb-panel-footer {
background: #f8f9fa !important; border-top: 1px solid #e9ecef !important;
padding: 10px 16px !important; display: flex !important; align-items: center !important; gap: 8px !important;
color: #6c757d !important; font-size: 11px !important;
}
#efb-error-panel .efb-panel-footer svg { opacity: 0.6 !important; color: #6c757d !important; }
#efb-error-panel .efb-no-errors {
text-align: center !important; color: #28a745 !important; padding: 40px 20px !important;
font-size: 15px !important; font-weight: 500 !important;
}
#efb-error-panel .efb-error-item {
background: #f8f9fa !important; border-radius: 10px !important; padding: 14px !important;
margin-bottom: 12px !important; border-${isRtl ? "right" : "left"}: 4px solid #dc3545 !important;
transition: transform 0.2s, box-shadow 0.2s !important;
}
#efb-error-panel .efb-error-item:hover { transform: translateX(${isRtl ? "-2px" : "2px"}) !important; box-shadow: 0 2px 8px rgba(0,0,0,0.08) !important; }
#efb-error-panel .efb-error-item:last-child { margin-bottom: 0 !important; }
#efb-error-panel .efb-error-item.is-efb { border-${isRtl ? "right" : "left"}-color: #ff4b93 !important; background: #fff5f8 !important; }
#efb-error-panel .efb-error-item.is-theme { border-${isRtl ? "right" : "left"}-color: #28a745 !important; }
#efb-error-panel .efb-error-item.is-core { border-${isRtl ? "right" : "left"}-color: #007bff !important; }
#efb-error-panel .efb-error-item.is-external { border-${isRtl ? "right" : "left"}-color: #6c757d !important; }
#efb-error-panel .efb-error-source {
display: flex !important; align-items: center !important; gap: 8px !important;
font-weight: 600 !important; font-size: 14px !important; margin-bottom: 8px !important;
}
#efb-error-panel .efb-error-source-name { flex: 1 !important; min-width: 0 !important; display: inline-block !important; }
#efb-error-panel .efb-btn-dismiss {
background: #fff !important; border: 1px solid #dee2e6 !important; color: #495057 !important;
padding: 4px 9px !important; border-radius: 6px !important; cursor: pointer !important;
font-size: 11px !important; font-weight: 600 !important; line-height: 1.2 !important;
margin-${isRtl ? "right" : "left"}: auto !important; display: inline-block !important;
}
#efb-error-panel .efb-btn-dismiss:hover {
background: #f1f3f5 !important; color: #212529 !important; border-color: #ced4da !important;
}
#efb-error-panel .efb-error-source .type-badge {
font-size: 9px !important; padding: 3px 8px !important; border-radius: 4px !important;
text-transform: uppercase !important; font-weight: 700 !important; letter-spacing: 0.5px !important;
display: inline-block !important;
}
#efb-error-panel .efb-error-source .type-plugin { background: #fff3cd !important; color: #856404 !important; }
#efb-error-panel .efb-error-source .type-theme { background: #d4edda !important; color: #155724 !important; }
#efb-error-panel .efb-error-source .type-wpCore { background: #cce5ff !important; color: #004085 !important; }
#efb-error-panel .efb-error-source .type-external { background: #e2e3e5 !important; color: #383d41 !important; }
#efb-error-panel .efb-error-source .type-unknown { background: #f5c6cb !important; color: #721c24 !important; }
#efb-error-panel .efb-error-source .type-notice { background: #fff3cd !important; color: #664d03 !important; border: 1px solid #ffecb5 !important; }
#efb-error-panel .efb-error-msg { color: #333 !important; font-size: 13px !important; line-height: 1.5 !important; word-break: break-word !important; margin-bottom: 8px !important; }
#efb-error-panel .efb-error-msg a { color: #0d6efd !important; text-decoration: underline !important; font-weight: 600 !important; }
#efb-error-panel .efb-error-msg strong { font-weight: 700 !important; color: #1f2937 !important; }
#efb-error-panel .efb-error-msg img { max-width: 100% !important; height: auto !important; max-height: 14px !important; display: inline-block !important; object-fit: contain !important; }
#efb-error-panel .efb-error-file {
background: #1e1e2e !important; padding: 8px 12px !important; border-radius: 6px !important;
font-family: "SF Mono", Monaco, Consolas, monospace !important;
font-size: 11px !important; color: #a6e3a1 !important; word-break: break-all !important;
display: flex !important; align-items: flex-start !important; gap: 8px !important;
line-height: 1.5 !important; direction: ltr !important; text-align: left !important;
}
#efb-error-panel .efb-error-file svg { flex-shrink: 0 !important; opacity: 0.7 !important; margin-top: 2px !important; color: #89b4fa !important; }
#efb-error-panel .efb-error-file .efb-full-path { color: #cdd6f4 !important; display: inline !important; }
#efb-error-panel .efb-stack-trace {
margin-top: 10px !important; background: #1e1e2e !important; border-radius: 8px !important;
overflow: hidden !important; border: 1px solid #313244 !important; direction: ltr !important; text-align: left !important;
}
#efb-error-panel .efb-stack-title {
background: #313244 !important; color: #cdd6f4 !important; padding: 8px 12px !important;
font-size: 11px !important; font-weight: 600 !important; display: flex !important; align-items: center !important; gap: 6px !important;
}
#efb-error-panel .efb-stack-title svg { color: #89b4fa !important; }
#efb-error-panel .efb-stack-items { padding: 8px 0 !important; }
#efb-error-panel .efb-stack-item {
display: flex !important; align-items: flex-start !important; gap: 10px !important; padding: 6px 12px !important;
font-family: "SF Mono", Monaco, Consolas, monospace !important; font-size: 11px !important;
transition: background 0.15s !important;
}
#efb-error-panel .efb-stack-item:hover { background: #313244 !important; }
#efb-error-panel .efb-stack-item.is-efb { background: rgba(255,75,147,0.1) !important; }
#efb-error-panel .efb-stack-item.is-efb:hover { background: rgba(255,75,147,0.2) !important; }
#efb-error-panel .efb-stack-num {
color: #6c7086 !important; min-width: 18px !important; text-align: ${isRtl ? "left" : "right"} !important;
font-size: 10px !important; padding-top: 2px !important; display: inline-block !important;
}
#efb-error-panel .efb-stack-content { flex: 1 !important; min-width: 0 !important; }
#efb-error-panel .efb-stack-func { color: #f9e2af !important; display: block !important; margin-bottom: 2px !important; }
#efb-error-panel .efb-stack-loc { display: flex !important; flex-wrap: wrap !important; gap: 6px !important; align-items: center !important; height: auto !important; min-height: 0 !important; max-height: none !important; }
#efb-error-panel .efb-stack-source {
font-size: 9px !important; padding: 2px 6px !important; border-radius: 3px !important;
text-transform: uppercase !important; font-weight: 600 !important; display: inline-block !important;
}
#efb-error-panel .efb-stack-source.plugin { background: #fff3cd !important; color: #856404 !important; }
#efb-error-panel .efb-stack-source.theme { background: #d4edda !important; color: #155724 !important; }
#efb-error-panel .efb-stack-source.wpCore { background: #cce5ff !important; color: #004085 !important; }
#efb-error-panel .efb-stack-source.external { background: #e2e3e5 !important; color: #383d41 !important; }
#efb-error-panel .efb-stack-source.unknown { background: #f5c6cb !important; color: #721c24 !important; }
#efb-error-panel .efb-stack-path { color: #a6adc8 !important; word-break: break-all !important; display: inline !important; }
#efb-error-panel .efb-error-meta { color: #6c757d !important; font-size: 11px !important; margin-top: 8px !important; }
`;
document.head.appendChild(style);
},
togglePanel() {
this.isOpen = !this.isOpen;
if (this.isOpen) {
if (this.badge) this.badge.classList.add("is-open");
this.panel.style.setProperty("display", "block", "important");
this.overlay.style.display = "block";
setTimeout(() => {
this.panel.style.setProperty("opacity", "1", "important");
this.panel.style.setProperty("transform", "translate(-50%, -50%) scale(1)", "important");
this.overlay.style.opacity = "1";
}, 10);
} else {
if (this.badge) this.badge.classList.remove("is-open");
this.panel.style.setProperty("opacity", "0", "important");
this.panel.style.setProperty("transform", "translate(-50%, -50%) scale(0.9)", "important");
this.overlay.style.opacity = "0";
setTimeout(() => {
this.panel.style.setProperty("display", "none", "important");
this.overlay.style.display = "none";
}, 300);
}
},
getInlineTarget(formId) {
const id = formId !== null && formId !== undefined ? String(formId) : "";
if (id !== "") {
return document.getElementById("efb-submit-error-badge-slot-" + id)
|| document.querySelector("#body_efb_" + id + " #efb-final-step")
|| document.querySelector("#body_efb_" + id + " .view-efb");
}
return document.querySelector(".efb-submit-error-badge-slot")
|| document.querySelector(".view-efb #efb-final-step")
|| document.querySelector(".view-efb");
},
showInlineBadge(formId = null) {
if (!this.badge) return;
if (formId !== null && formId !== undefined) {
this.inlineTargetFormId = formId;
}
const target = this.getInlineTarget(this.inlineTargetFormId);
if (target && this.badge.parentNode !== target) {
target.appendChild(this.badge);
} else if (!this.badge.isConnected) {
document.body.appendChild(this.badge);
}
this.showBadge();
},
showBadge() {
if (!this.badge) return;
this.badge.style.setProperty("visibility", "visible", "important");
this.badge.style.setProperty("opacity", "1", "important");
this.badge.style.setProperty("pointer-events", "auto", "important");
},
hideBadge() {
if (!this.badge) return;
this.badge.style.setProperty("opacity", "0", "important");
this.badge.style.setProperty("pointer-events", "none", "important");
this.badge.style.setProperty("visibility", "hidden", "important");
},
clearSubmissionBadge(formId = null) {
this.hideBadge();
if (formId === null || formId === undefined || String(formId) === String(this.inlineTargetFormId)) {
this.inlineTargetFormId = null;
this.visibleErrorKeys.clear();
}
this.closePanelIfOpen();
},
updateCount() {},
addError(errorData) {
errorData = errorData || {};
const { message, source, lineno, stack = [], typeOverride = null, nameOverride = null, formatOptions = {}, showBadge = false, context = "", formId = null } = errorData;
const shouldShowBadge = showBadge === true || context === "submissionAjax";
const errorKey = (message || "") + "|" + (source || "") + "|" + (lineno || "");
if (this.dismissedKeys.has(errorKey)) return;
if (this.errorKeys.has(errorKey)) {
if (shouldShowBadge) {
this.visibleErrorKeys.add(errorKey);
this.showInlineBadge(formId);
}
return;
}
this.errorKeys.add(errorKey);
const parsed = this.parseSource(source);
if (typeOverride) { parsed.type = typeOverride; }
if (nameOverride) { parsed.name = nameOverride; }
const time = new Date().toLocaleTimeString([], {hour: "2-digit", minute: "2-digit"});
const realSource = stack.length > 0 ? stack[0].parsed : parsed;
const realPath = stack.length > 0 ? stack[0].parsed.fullPath + ":" + stack[0].line : (parsed.fullPath + (lineno ? ":" + lineno : ""));
this.errors.push({ key: errorKey, message, source, lineno, parsed, stack, time, showBadge: shouldShowBadge });
if (shouldShowBadge) {
this.visibleErrorKeys.add(errorKey);
this.showInlineBadge(formId);
}
this.updateCount();
if (context === "submissionAjax") return;
const list = document.getElementById("efb-error-list");
if (!list) return;
const noErrors = list.querySelector(".efb-no-errors");
if (noErrors) noErrors.remove();
const typeClass = realSource.isEFB ? "is-efb" :
realSource.type === "theme" ? "is-theme" :
realSource.type === "wpCore" ? "is-core" :
realSource.type === "external" ? "is-external" : "";
let stackHtml = "";
if (stack.length > 0) {
stackHtml = `
Stack Trace
${stack.map((s, i) => `
${i + 1}
${this.escapeHtml(s.func)}
${s.parsed.name}
${s.parsed.fullPath}:${s.line}
`).join("")}
`;
}
const item = document.createElement("div");
item.className = "efb-error-item " + typeClass;
const messageHtml = context === "submissionAjax" ? "" : `
${this.formatMessage(message, formatOptions)}
`;
item.innerHTML = `
${this.t[realSource.type] || realSource.type}
${realSource.name}
${this.t.dismiss}
${messageHtml}
${realPath}
${stackHtml}
${time}
`;
item.dataset.errorKey = errorKey;
const dismissBtn = item.querySelector(".efb-btn-dismiss");
if (dismissBtn) {
dismissBtn.addEventListener("click", () => this.dismissError(errorKey));
}
list.insertBefore(item, list.firstChild);
},
getDismissLabel(label) {
const template = this.t.dismissItem || "Dismiss %s";
return this.escapeHtml(template.replace("%s", label || ""));
},
showEmptyState() {
const list = document.getElementById("efb-error-list");
if (list) {
list.innerHTML = "
✓ " + this.t.noErrors + "
";
}
},
closePanelIfOpen() {
if (this.isOpen) {
this.togglePanel();
}
},
dismissAll() {
this.errors.forEach(error => {
if (error.key) {
this.dismissedKeys.add(error.key);
}
});
this.saveDismissedKeys();
this.errors = [];
this.errorKeys.clear();
this.visibleErrorKeys.clear();
this.updateCount();
const list = document.getElementById("efb-error-list");
list.innerHTML = "
✓ " + this.t.noErrors + "
";
this.hideBadge();
this.closePanelIfOpen();
},
dismissError(errorKey) {
if (!errorKey) return;
this.dismissedKeys.add(errorKey);
this.saveDismissedKeys();
this.errors = this.errors.filter(error => error.key !== errorKey);
this.errorKeys.delete(errorKey);
this.visibleErrorKeys.delete(errorKey);
const list = document.getElementById("efb-error-list");
if (list) {
Array.from(list.querySelectorAll(".efb-error-item")).forEach(item => {
if (item.dataset.errorKey === errorKey) {
item.remove();
}
});
}
this.updateCount();
if (this.errors.length === 0) {
this.showEmptyState();
}
if (this.visibleErrorKeys.size === 0) {
this.hideBadge();
this.closePanelIfOpen();
}
},
clearErrors() {
this.dismissAll();
},
escapeHtml(text) {
const div = document.createElement("div");
div.textContent = text;
return div.innerHTML;
},
escapeRegExp(text) {
const backslash = String.fromCharCode(92);
const specials = backslash + "^$.*+?()[]{}|";
return String(text).split("").map(ch => specials.indexOf(ch) !== -1 ? backslash + ch : ch).join("");
},
formatMessage(message, options = {}) {
let html = this.escapeHtml(message).replace(/\n/g, "
");
const boldTexts = Array.isArray(options.boldTexts) ? options.boldTexts : [];
boldTexts.filter(Boolean).forEach(text => {
const safeText = this.escapeHtml(text);
html = html.replace(new RegExp(this.escapeRegExp(safeText), "g"), "
" + safeText + " ");
});
const links = Array.isArray(options.links) ? options.links : [];
links.filter(Boolean).forEach(link => {
const rawUrl = typeof link === "string" ? link : link.url;
const rawLabel = typeof link === "string" ? link : (link.label || link.url);
if (!rawUrl) return;
const safeUrl = this.escapeHtml(rawUrl);
const safeLabel = this.escapeHtml(rawLabel || rawUrl);
html = html.replace(new RegExp(this.escapeRegExp(safeUrl), "g"), "
" + safeLabel + " ");
});
return html;
},
parseStack(stackString) {
if (!stackString) return [];
const lines = stackString.split("\n");
const stack = [];
for (const line of lines) {
const match = line.match(/at\s+(.+?)\s*\(?(https?:\/\/[^)\s]+):(\d+):(\d+)\)?/);
if (match) {
const [, funcName, url, lineNo, colNo] = match;
const parsed = this.parseSource(url);
stack.push({
func: funcName.trim(),
url: url,
line: lineNo,
col: colNo,
parsed: parsed
});
}
}
return stack;
},
test(message) {
const msg = message || "🧪 This is a TEST error message from EFB_ERROR_PANEL.test()";
const err = new Error(msg);
const stack = this.parseStack(err.stack);
this.addError({
message: msg,
source: window.location.href,
lineno: null,
stack: stack
});
console.info("%c[EFB Debug Panel]%c Test error added: " + msg, "color:#ff4b93;font-weight:bold", "color:inherit");
},
log(message, options = {}) {
if (!message) { console.warn("[EFB Debug Panel] log() requires a message"); return; }
let source = options.source || window.location.href;
let lineno = options.line || options.lineno || null;
let stack = [];
if (Array.isArray(options.stack) && options.stack.length > 0) {
stack = options.stack.map(s => {
const filePath = s.file || s.url || "";
const parsed = this.parseSource(filePath.startsWith("http") ? filePath : window.location.origin + "/" + filePath);
parsed.fullPath = filePath;
return {
func: s.func || s.function || "anonymous",
url: filePath,
line: String(s.line || "?"),
col: String(s.col || s.column || "?"),
parsed: parsed
};
});
} else if (options.captureStack !== false) {
const err = new Error("__efb_log__");
stack = this.parseStack(err.stack);
if (stack.length > 0 && stack[0].func.includes("log")) {
stack.shift();
}
}
const typeOverride = options.type || null;
const nameOverride = options.name || null;
const formatOptions = options.format || {};
const showBadge = options.showBadge === true;
const context = options.context || "";
const formId = options.formId || options.form_id || null;
this.addError({ message, source, lineno, stack, typeOverride, nameOverride, formatOptions, showBadge, context, formId });
console.info("%c[EFB Debug Panel]%c Logged: " + message, "color:#ff4b93;font-weight:bold", "color:inherit");
},
init() {
const self = this;
this.loadDismissedKeys();
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", () => self.createUI());
} else {
self.createUI();
}
// Check if jQuery is loaded and working properly
setTimeout(() => {
if (typeof jQuery === "undefined" || !jQuery || typeof jQuery.ajax !== "function") {
self.addError({
message: self.t.jqueryMessage,
source: "window",
lineno: null,
typeOverride: "jqueryMissing",
nameOverride: self.t.jqueryMissing
});
}
if (typeof $ === "undefined" && typeof jQuery !== "undefined") {
console.warn("[EFB] $ is undefined. If using jQuery in noConflict mode, use jQuery instead of $");
}
}, 500);
window.addEventListener("error", function(event) {
let stack = [];
if (event.error && event.error.stack) {
stack = self.parseStack(event.error.stack);
}
self.addError({
message: event.message,
source: event.filename,
lineno: event.lineno,
stack: stack
});
}, true);
window.addEventListener("unhandledrejection", function(event) {
let source = "";
let stack = [];
let message = event.reason instanceof Error ? event.reason.message : String(event.reason);
if (event.reason && event.reason.stack) {
const match = event.reason.stack.match(/https?:\/\/[^\s]+/);
source = match ? match[0] : "";
stack = self.parseStack(event.reason.stack);
}
self.addError({ message, source, lineno: null, stack });
});
}
};
EFB_ERROR_PANEL.init();
window.EFB_ERROR_PANEL = EFB_ERROR_PANEL;
})();