Quelltext des aktuellen Standes
/* OpenNIT – Ticket-Formular JS (extern eingebunden, nicht inline) */
(function () {
'use strict';
/* ----------------------------------------------------------------
Durchsuchbare Combo-Box
---------------------------------------------------------------- */
function opennitInitCombo(wrapper) {
var search = wrapper.querySelector('.opennit-combo-search');
var dropdown = wrapper.querySelector('.opennit-combo-dropdown');
var hidden = wrapper.querySelector('.opennit-combo-hidden');
if (!search || !dropdown || !hidden) return;
var allOpts = Array.from(dropdown.querySelectorAll('.opennit-combo-option'));
var focused = null, selected = null;
/* Dropdown-Pfeil als sichtbare Affordance (klickbar) – signalisiert,
dass man das Feld auch ohne Suchbegriff aufklappen kann. */
var chevron = document.createElement('span');
chevron.className = 'opennit-combo-chevron';
chevron.setAttribute('aria-hidden', 'true');
chevron.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg>';
wrapper.appendChild(chevron);
/* ARIA: echte Combobox-/Listbox-Semantik für Screenreader & Tastatur */
var comboUid = 'opennit-combo-' + Math.random().toString(36).slice(2, 8);
dropdown.id = comboUid + '-listbox';
dropdown.setAttribute('role', 'listbox');
search.setAttribute('role', 'combobox');
search.setAttribute('aria-haspopup', 'listbox');
search.setAttribute('aria-expanded', 'false');
search.setAttribute('aria-controls', dropdown.id);
search.setAttribute('aria-autocomplete', 'list');
allOpts.forEach(function (opt, i) {
opt.id = dropdown.id + '-opt-' + i;
opt.setAttribute('role', 'option');
opt.setAttribute('aria-selected', 'false');
});
/* Portal: Dropdown an <body> hängen – kein overflow:hidden eines Vorfahren stört.
position:absolute statt fixed, weil body{overflow-x:hidden} (Elementor) in Chrome
dazu führt, dass fixed-Elemente relativ zu body positioniert werden. */
document.body.appendChild(dropdown);
Object.assign(dropdown.style, {
position: 'absolute',
zIndex: '2147483647',
background: '#ffffff',
border: '1px solid #3b82f6',
borderTop: 'none',
borderRadius: '0 0 6px 6px',
maxHeight: '210px',
overflowY: 'auto',
overflowX: 'hidden',
boxShadow: '0 4px 12px rgba(0,0,0,.15)',
boxSizing: 'border-box',
margin: '0',
padding: '0',
transform: 'none',
opacity: '1',
visibility: 'visible',
pointerEvents: 'auto',
display: 'none',
fontFamily: '-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif',
fontSize: '14px',
lineHeight: '1.5',
color: '#1f2937',
right: 'auto',
bottom: 'auto',
});
allOpts.forEach(function (opt) {
Object.assign(opt.style, {
padding: '8px 12px',
cursor: 'pointer',
color: '#1f2937',
background: 'transparent',
borderBottom: '1px solid #f3f4f6',
boxSizing: 'border-box',
fontSize: '14px',
lineHeight: '1.5',
margin: '0',
width: '100%',
textAlign: 'left',
fontWeight: 'normal',
whiteSpace: 'normal',
});
opt.style.setProperty('display', 'block', 'important');
});
function positionDropdown() {
var r = search.getBoundingClientRect();
dropdown.style.left = (r.left + window.pageXOffset) + 'px';
dropdown.style.top = (r.bottom + window.pageYOffset) + 'px';
dropdown.style.width = r.width + 'px';
}
function show() {
positionDropdown();
dropdown.style.display = 'block';
dropdown.style.visibility = 'visible';
dropdown.style.opacity = '1';
wrapper.classList.add('opennit-combo-open');
search.setAttribute('aria-expanded', 'true');
}
function hide() {
dropdown.style.display = 'none';
focused = null;
wrapper.classList.remove('opennit-combo-open');
search.setAttribute('aria-expanded', 'false');
search.removeAttribute('aria-activedescendant');
}
/* Klick auf den Pfeil öffnet/schließt die Liste (toggle) */
chevron.addEventListener('mousedown', function (e) {
e.preventDefault(); // verhindert Blur des Inputs
if (dropdown.style.display === 'block') {
hide();
} else {
search.focus();
filter('');
show();
}
});
window.addEventListener('scroll', function () {
if (dropdown.style.display === 'block') positionDropdown();
}, true);
window.addEventListener('resize', function () {
if (dropdown.style.display === 'block') positionDropdown();
});
/* Tiefe einer Option aus dem "– "-Präfix ableiten (U+2013 + Leerzeichen = 2 JS-Zeichen) */
function getOptDepth(opt) {
var text = opt.textContent;
var d = 0;
while (text.charCodeAt(d * 2) === 0x2013) d++;
return d;
}
function filter(q) {
var lq = q.toLowerCase();
if (!lq) {
allOpts.forEach(function (o) { o.style.setProperty('display', 'block', 'important'); });
return;
}
/* Matching-Optionen + ihre Elternknoten sichtbar machen */
var visible = {};
allOpts.forEach(function (o, i) {
if (o.textContent.toLowerCase().indexOf(lq) === -1) return;
visible[i] = true;
/* Eltern aufsteigend suchen (DFS-Reihenfolge garantiert Eltern vor Kindern) */
var targetDepth = getOptDepth(o) - 1;
for (var j = i - 1; j >= 0 && targetDepth >= 0; j--) {
if (getOptDepth(allOpts[j]) === targetDepth) {
visible[j] = true;
targetDepth--;
}
}
});
allOpts.forEach(function (o, i) {
o.style.setProperty('display', visible[i] ? 'block' : 'none', 'important');
});
}
function setOptState(opt, state) {
if (state === 'hover') {
opt.style.background = '#eff6ff'; opt.style.color = '#1d4ed8';
} else if (state === 'selected') {
opt.style.background = '#dbeafe'; opt.style.color = '#1d4ed8'; opt.style.fontWeight = '600';
} else {
opt.style.background = 'transparent'; opt.style.color = '#1f2937'; opt.style.fontWeight = 'normal';
}
}
function selectOpt(opt) {
allOpts.forEach(function (o) {
o.classList.remove('opennit-combo-selected', 'opennit-combo-hover');
o.setAttribute('aria-selected', 'false');
setOptState(o, 'normal');
});
opt.classList.add('opennit-combo-selected');
opt.setAttribute('aria-selected', 'true');
setOptState(opt, 'selected');
selected = opt;
hidden.value = opt.getAttribute('data-value');
search.value = opt.classList.contains('opennit-combo-option-none') ? '' : opt.textContent.trim();
search.placeholder = opt.classList.contains('opennit-combo-option-none')
? search.getAttribute('data-placeholder')
: opt.textContent.trim();
filter('');
hide();
}
function getVisible() {
return allOpts.filter(function (o) { return o.style.getPropertyValue('display') !== 'none'; });
}
function moveFocus(dir) {
var vis = getVisible();
if (!vis.length) return;
if (focused) {
focused.classList.remove('opennit-combo-hover');
setOptState(focused, focused.classList.contains('opennit-combo-selected') ? 'selected' : 'normal');
}
var idx = vis.indexOf(focused);
if (dir === 1) idx = idx < vis.length - 1 ? idx + 1 : 0;
else idx = idx > 0 ? idx - 1 : vis.length - 1;
focused = vis[idx];
focused.classList.add('opennit-combo-hover');
setOptState(focused, 'hover');
focused.scrollIntoView({ block: 'nearest' });
search.setAttribute('aria-activedescendant', focused.id);
}
search.setAttribute('data-placeholder', search.placeholder);
search.addEventListener('focus', function () { filter(''); show(); });
search.addEventListener('input', function () {
hidden.value = '';
selected = null;
allOpts.forEach(function (o) { o.classList.remove('opennit-combo-selected'); setOptState(o, 'normal'); });
filter(search.value);
show();
});
search.addEventListener('keydown', function (e) {
if (e.key === 'ArrowDown') { e.preventDefault(); moveFocus(1); show(); }
else if (e.key === 'ArrowUp') { e.preventDefault(); moveFocus(-1); show(); }
else if (e.key === 'Enter') {
e.preventDefault();
var target = focused || getVisible()[0];
if (target) selectOpt(target);
} else if (e.key === 'Escape') {
search.value = (selected && !selected.classList.contains('opennit-combo-option-none'))
? selected.textContent.trim() : '';
hide();
}
});
dropdown.addEventListener('mousedown', function (e) { e.preventDefault(); });
allOpts.forEach(function (opt) {
opt.addEventListener('click', function () { selectOpt(opt); });
opt.addEventListener('mouseenter', function () {
if (focused) {
focused.classList.remove('opennit-combo-hover');
setOptState(focused, focused.classList.contains('opennit-combo-selected') ? 'selected' : 'normal');
}
focused = opt;
opt.classList.add('opennit-combo-hover');
setOptState(opt, 'hover');
});
});
search.addEventListener('blur', function () {
setTimeout(function () {
if (!hidden.value && selected && selected.classList.contains('opennit-combo-option-none')) {
search.value = '';
} else if (!hidden.value && !selected) {
search.value = '';
} else if (selected && !selected.classList.contains('opennit-combo-option-none')) {
search.value = selected.textContent.trim();
}
hide();
}, 200);
});
wrapper._comboSelectByValue = function (val) {
var opt = allOpts.filter(function (o) { return o.getAttribute('data-value') === String(val); })[0];
if (opt) selectOpt(opt);
};
}
/* ----------------------------------------------------------------
Initialisierung
---------------------------------------------------------------- */
function initTicketForm() {
var wrap = document.getElementById('opennit-ticket-wrap');
if (!wrap || wrap._opennitInit) return;
wrap._opennitInit = true;
/* Combos initialisieren */
wrap.querySelectorAll('.opennit-combo').forEach(opennitInitCombo);
/* Asset per URL-Parameter vorauswählen */
var preselectId = wrap.getAttribute('data-preselect-asset') || '';
if (preselectId) {
var assetCombo = document.getElementById('opennit-asset-combo');
if (assetCombo && assetCombo._comboSelectByValue) {
assetCombo._comboSelectByValue(preselectId);
}
}
/* ----------------------------------------------------------------
Formular-Logik
---------------------------------------------------------------- */
var form = document.getElementById('opennit-ticket-form');
var successPanel = document.getElementById('opennit-ticket-success-panel');
var successMsg = document.getElementById('opennit-ticket-success-msg');
var newBtn = document.getElementById('opennit-ticket-new-btn');
var errorEl = document.getElementById('opennit-ticket-error');
var submit = document.getElementById('opennit-ticket-submit');
var fileInput = document.getElementById('opennit-attachment');
var preview = document.getElementById('opennit-attachment-preview');
if (!form) return;
var ajaxUrl = wrap.getAttribute('data-ajax-url') || '';
var successText = wrap.getAttribute('data-success-text') || 'Ticket erfolgreich übermittelt.';
var submitLabel = wrap.getAttribute('data-submit-label') || 'Ticket absenden';
if (newBtn) {
newBtn.addEventListener('click', function () {
successPanel.style.display = 'none';
form.style.display = '';
errorEl.style.display = 'none';
});
}
if (fileInput) {
var dropzone = wrap.querySelector('.opennit-dropzone');
var dzText = dropzone ? dropzone.querySelector('.opennit-dropzone-text') : null;
var dzDefault = dzText ? dzText.innerHTML : '';
fileInput.addEventListener('change', function () {
if (!fileInput.files.length) {
preview.style.display = 'none'; preview.textContent = '';
if (dropzone) dropzone.classList.remove('is-filled');
if (dzText) dzText.innerHTML = dzDefault;
return;
}
var f = fileInput.files[0];
if (f.size > 10 * 1024 * 1024) {
errorEl.textContent = 'Die Datei ist zu groß (max. 10 MB).';
errorEl.style.display = 'block';
fileInput.value = '';
preview.style.display = 'none';
if (dropzone) dropzone.classList.remove('is-filled');
if (dzText) dzText.innerHTML = dzDefault;
return;
}
preview.textContent = '📎 ' + f.name + ' (' + (f.size / 1024).toFixed(1) + ' KB)';
preview.style.display = 'block';
if (dropzone) dropzone.classList.add('is-filled');
if (dzText) dzText.innerHTML = '<strong>Datei ausgewählt</strong> – zum Ändern klicken';
});
}
form.addEventListener('submit', function (e) {
e.preventDefault();
successPanel.style.display = 'none';
errorEl.style.display = 'none';
var categoryId = form.querySelector('[name="category_id"]').value;
var locationId = form.querySelector('[name="location_id"]').value;
var subject = form.querySelector('[name="subject"]').value.trim();
var description = form.querySelector('[name="description"]').value.trim();
if (!categoryId || !locationId || !subject || !description) {
errorEl.textContent = 'Bitte alle Pflichtfelder ausfüllen.';
errorEl.style.display = 'block';
return;
}
var file = fileInput && fileInput.files.length ? fileInput.files[0] : null;
function doSubmit(attachmentB64, attachmentName, attachmentMime) {
var data = new FormData();
data.append('action', 'opennit_submit_ticket');
data.append('nonce', form.querySelector('[name="nonce"]').value);
data.append('from_email', form.querySelector('[name="from_email"]').value);
data.append('from_name', form.querySelector('[name="from_name"]').value);
data.append('category_id', categoryId);
data.append('location_id', locationId);
data.append('subject', subject);
data.append('description', description);
var assetEl = form.querySelector('[name="asset_id"]');
if (assetEl && assetEl.value) data.append('asset_id', assetEl.value);
if (attachmentB64) {
data.append('attachment_b64', attachmentB64);
data.append('attachment_name', attachmentName);
data.append('attachment_mime', attachmentMime);
}
fetch(ajaxUrl, { method: 'POST', body: data })
.then(function (r) { return r.json(); })
.then(function (res) {
if (res.success) {
form.style.display = 'none';
errorEl.style.display = 'none';
successMsg.textContent = (res.data && res.data.message) ? res.data.message : successText;
successPanel.style.display = '';
form.reset();
if (preview) { preview.style.display = 'none'; preview.textContent = ''; }
var dz = wrap.querySelector('.opennit-dropzone');
if (dz) {
dz.classList.remove('is-filled');
var dzt = dz.querySelector('.opennit-dropzone-text');
if (dzt) dzt.innerHTML = '<strong>Datei auswählen</strong>';
}
wrap.querySelectorAll('.opennit-combo').forEach(function (combo) {
var s = combo.querySelector('.opennit-combo-search');
var h = combo.querySelector('.opennit-combo-hidden');
if (s) s.value = '';
if (h) h.value = '';
combo.querySelectorAll('.opennit-combo-option').forEach(function (o) {
o.classList.remove('opennit-combo-selected', 'opennit-combo-hover');
});
});
} else {
var msg = (res.data && res.data.message) ? res.data.message : (res.data || 'Unbekannter Fehler.');
errorEl.textContent = msg;
errorEl.style.display = 'block';
}
})
.catch(function () {
errorEl.textContent = 'Verbindungsfehler. Bitte versuchen Sie es erneut.';
errorEl.style.display = 'block';
})
.finally(function () {
submit.disabled = false;
submit.textContent = submitLabel;
});
}
submit.disabled = true;
submit.textContent = 'Wird gesendet…';
if (file) {
var reader = new FileReader();
reader.onload = function (ev) { doSubmit(ev.target.result, file.name, file.type); };
reader.onerror = function () {
errorEl.textContent = 'Fehler beim Lesen der Datei.';
errorEl.style.display = 'block';
submit.disabled = false;
submit.textContent = submitLabel;
};
reader.readAsDataURL(file);
} else {
doSubmit('', '', '');
}
});
}
/* Initialisierung: sofort, bei DOMContentLoaded und nach kurzer Verzögerung
(fängt Elementor-Widgets ab, die nach dem initialen Render geladen werden) */
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initTicketForm);
} else {
initTicketForm();
}
window.addEventListener('load', initTicketForm);
setTimeout(initTicketForm, 800);
})();