Вернуться на главную
Вернуться на главную
Вопросы и ответы
/*ANFANG Java Script von PA*/ /* WISY – Variant Price Loader v2 (DOM lazy + idempotent) */ (() => { const HOST_SEL = '.wisy-pricebox'; const DEFAULT_CCY = 'EUR'; const LOCALE = (document.documentElement.lang || 'de-DE'); const getCookie = (name) => { const m = document.cookie.match(new RegExp('(?:^|; )' + name.replace(/([.$?*|{}()\\[\\]\\/\\+^])/g, '\\$1') + '=([^;]*)')); return m ? decodeURIComponent(m[1]) : null; }; const fmtPrice = (v, ccy) => { const n = Number(v); if (!isFinite(n)) return '–'; try { return new Intl.NumberFormat(LOCALE, { style: 'currency', currency: ccy || DEFAULT_CCY }).format(n); } catch { return n.toFixed(2) + ' ' + (ccy || DEFAULT_CCY); } }; const findAccessKey = () => document.querySelector('meta[name="sw-access-key"]')?.content || document.querySelector('[data-store-api-access-key]')?.getAttribute('data-store-api-access-key') || document.body?.dataset?.accessKey || document.body?.dataset?.swAccessKey || null; const ensureContextToken = async (accessKey) => { let t = getCookie('sw-context-token'); if (t) return t; try { const r = await fetch('/store-api/context', { method: 'POST', headers: { 'sw-access-key': accessKey, 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: JSON.stringify({}) }); if (!r.ok) throw 0; const d = await r.json(); return d.token || getCookie('sw-context-token') || null; } catch { return null; } }; const fetchPriceStoreApi = async (id, accessKey, token) => { try { const r = await fetch(`/store-api/product/${encodeURIComponent(id)}`, { method: 'GET', headers: { 'sw-access-key': accessKey, ...(token ? { 'sw-context-token': token } : {}), 'Accept': 'application/json' } }); if (!r.ok) throw 0; const p = await r.json(); const price = p.calculatedPrice?.unitPrice ?? p.calculatedPrice?.totalPrice ?? p.price?.[0]?.gross ?? p.price?.[0]?.net; const currency = p.currency?.isoCode || DEFAULT_CCY; if (price == null) throw 0; return { price, currency }; } catch { return null; } }; const fetchPriceQuickview = async (id) => { try { const r = await fetch(`/widgets/quickview/${encodeURIComponent(id)}`, { headers: { 'X-Requested-With': 'XMLHttpRequest' } }); if (!r.ok) throw 0; const html = await r.text(); const doc = new DOMParser().parseFromString(html, 'text/html'); let content = doc.querySelector('meta[itemprop="price"]')?.getAttribute('content'); if (content) return { price: Number(content), currency: doc.querySelector('meta[itemprop="priceCurrency"]')?.getAttribute('content') || DEFAULT_CCY }; const anyData = doc.querySelector('[data-gtm-product-price]')?.getAttribute('data-gtm-product-price') || doc.querySelector('[data-price]')?.getAttribute('data-price'); if (anyData) return { price: Number(anyData), currency: DEFAULT_CCY }; const node = doc.querySelector('.product-detail-price, .product-price, [itemprop="price"], .price'); if (node) { const txt = node.textContent.replace(/\s+/g, ' ').trim(); const m = txt.match(/([0-9]+(?:[.,][0-9]{1,2})?)/); if (m) return { price: Number(m[1].replace(/\./g, '').replace(',', '.')), currency: DEFAULT_CCY }; } return null; } catch { return null; } }; const fetchOnePrice = async (id) => { const key = findAccessKey(); if (key) { const token = await ensureContextToken(key); const viaStore = await fetchPriceStoreApi(id, key, token); if (viaStore) return viaStore; } const viaWidget = await fetchPriceQuickview(id); if (viaWidget) return viaWidget; return { price: null, currency: DEFAULT_CCY, error: true }; }; // --- Parser: 1) JSON 2) Kinder 3) CSV const parseFromJson = (host) => { const raw = host.getAttribute('data-items-json'); if (!raw) return null; try { const arr = JSON.parse(raw); if (Array.isArray(arr) && arr.length) { return arr.map(x => ({ id: String(x.id || '').trim(), label: String(x.label || x.id || '').trim() })).filter(x => x.id); } } catch { /* noop */ } return null; }; const parseFromChildren = (host) => { const nodes = host.querySelectorAll('.wisy-price-item[data-id]'); if (!nodes.length) return null; return Array.from(nodes).map(n => ({ id: String(n.getAttribute('data-id') || '').trim(), label: String(n.getAttribute('data-label') || n.textContent || '').trim() })).filter(x => x.id); }; const parseFromCsv = (host) => { const raw = host.getAttribute('data-items'); if (!raw) return null; const out = raw.split(';').map(s => s.trim()).filter(Boolean).map(row => { const [id, label] = row.split('|').map(x => (x || '').trim()); return { id, label: label || id }; }).filter(x => x.id); return out.length ? out : null; }; async function renderBox(host) { if (host.dataset.wisyPriceInit === '1') return; // idempotent host.dataset.wisyPriceInit = '1'; const items = parseFromJson(host) || parseFromChildren(host) || parseFromCsv(host) || []; // Grundmarkup sofort sichtbar machen const ul = document.createElement('ul'); ul.className = 'wisy-price-list'; host.innerHTML = ''; host.appendChild(ul); if (!items.length) { const li = document.createElement('li'); li.innerHTML = 'VariantenKeine Einträge'; ul.appendChild(li); return; } items.forEach(({ id, label }) => { const li = document.createElement('li'); li.innerHTML = `${label} `; ul.appendChild(li); }); const results = await Promise.all(items.map(x => fetchOnePrice(x.id))); results.forEach((res, i) => { const id = items[i].id; const out = ul.querySelector(`[data-price-for="${CSS.escape(id)}"]`); if (!out) return; out.classList.remove('is-loading'); if (res && !res.error && res.price != null) { out.textContent = fmtPrice(res.price, res.currency); } else { out.classList.add('is-error'); out.textContent = 'Preis nicht verfügbar'; } }); } function initAll() { document.querySelectorAll(HOST_SEL).forEach(renderBox); } // 1) DOM ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initAll); } else { initAll(); } // 2) Lazy-Load / dynamische Blöcke const mo = new MutationObserver((muts) => { muts.forEach(m => { m.addedNodes.forEach(n => { if (n.nodeType !== 1) return; if (n.matches?.(HOST_SEL)) renderBox(n); n.querySelectorAll?.(HOST_SEL).forEach(renderBox); }); }); }); mo.observe(document.documentElement, { childList: true, subtree: true }); // 3) manueller Trigger (Konsole/Widget) window.wisyInitPrices = initAll; })(); /*ENDE Java Script von PA*/