(function () {
// ---------- Hilfen ----------
function $(sel, root){ return (root||document).querySelector(sel); }
function $all(sel, root){ return Array.from((root||document).querySelectorAll(sel)); }
function escapeHtml(str){
return (str==null?'':String(str)).replace(/[&<>"']/g, m => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[m]));
}
// Variation-ID möglichst robust ermitteln
function getVariationId(){
// Standard Ceres: hidden input im Warenkorb-Formular
let el = $('form[name="addToBasket"] input[name="variationId"]');
if (el && el.value) return el.value;
// Häufige Daten-Attribute
el = $('[data-variation-id]'); if (el && el.getAttribute('data-variation-id')) return el.getAttribute('data-variation-id');
el = $('[data-variationid]'); if (el && el.getAttribute('data-variationid')) return el.getAttribute('data-variationid');
// Manchmal hängt es am Body
el = $('body');
if (el){
if (el.dataset && el.dataset.variationId) return el.dataset.variationId;
if (el.getAttribute('data-variation-id')) return el.getAttribute('data-variation-id');
}
return null;
}
async function fetchCharacteristics(variationId){
if(!variationId) return [];
try{
// Öffentlicher Endpunkt der plenty REST – liefert Variation inkl. characteristics
const url = '/rest/items/variations/' + encodeURIComponent(variationId) + '?with=characteristics';
const res = await fetch(url, { credentials: 'same-origin' });
if(!res.ok) return [];
const data = await res.json();
const chars = data && data.variation && Array.isArray(data.variation.characteristics)
? data.variation.characteristics
: [];
return chars;
}catch(e){
return [];
}
}
function renderCharacteristics(chars){
const box = $('#merkmale-box');
if(!box) return;
if(!chars || !chars.length){
box.innerHTML = ''; // Nichts anzeigen, wenn keine Merkmale vorhanden
return;
}
let html = '
Merkmale
';
chars.forEach((ch, i) => {
const label =
ch.name ||
ch.characteristicName ||
ch.backendName ||
('Merkmal ' + (i+1));
// Wert(e) robust ermitteln
let value = '';
if (Array.isArray(ch.values) && ch.values.length){
value = ch.values.join(', ');
} else if (ch.valueText){ value = ch.valueText; }
else if (ch.valueName){ value = ch.valueName; }
else if (ch.value){ value = ch.value; }
html += '- '+ escapeHtml(label) +'
'
+ '- '+ escapeHtml(value || '–') +'
';
});
html += '
';
box.innerHTML = html;
// Minimal-CSS nur einmal anhängen
if(!document.getElementById('c-merkmale-style')){
const s = document.createElement('style');
s.id = 'c-merkmale-style';
s.textContent = '.c-merkmale__title{margin:.5rem 0 .25rem}'
+ '.c-merkmale__list{display:grid;grid-template-columns:1fr 2fr;gap:.25rem .75rem}'
+ '.c-merkmale__label{font-weight:600}'
+ '.c-merkmale__value{word-break:break-word}';
document.head.appendChild(s);
}
}
async function loadAndRender(){
const id = getVariationId();
if(!id) { renderCharacteristics([]); return; }
const chars = await fetchCharacteristics(id);
renderCharacteristics(chars);
}
// ---------- Initial laden ----------
if (document.readyState === 'loading'){
document.addEventListener('DOMContentLoaded', loadAndRender);
} else {
loadAndRender();
}
// ---------- Bei Variantenwechsel neu laden ----------
// 1) plenty/Ceres feuert oft dieses Event beim Variantenswitch:
document.addEventListener('afterVariationChanged', loadAndRender, true);
// 2) Fallback: beobachte Änderungen am hidden variationId-Feld
const varInput = $('form[name="addToBasket"] input[name="variationId"]') || document.body;
if (varInput){
const obs = new MutationObserver(() => loadAndRender());
obs.observe(varInput, { attributes:true, attributeFilter:['value'], subtree:true, childList:true });
}
// 3) Fallback: beobachte Attributauswahl (Dropdowns/Buttons)
$all('[name^="attribute"], .attribute, .variationSelect, .js-variation-select, .attributeSelect').forEach(el=>{
el.addEventListener('change', loadAndRender, true);
el.addEventListener('click', loadAndRender, true);
});
})();