Die amtliche Gewaehrleistungsgrafik steht in hervorgehobenem Kasten mit Ueberschrift vor der Zusammenfassung der Kasse (hoechstens 420 px breit, sofort geladen, Vergroessern oeffnet sie in einem mit Esc und Knopf schliessbaren Dialog). Direkt ueber "Zahlungspflichtig bestellen" steht "Es gelten unsere AGB. Informationen zu Ihrem Widerrufsrecht finden Sie in der Widerrufsbelehrung." mit Links auf beide Texte. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1781 lines
81 KiB
JavaScript
1781 lines
81 KiB
JavaScript
/*! WEBklar Webshop: Einbau-Skript fuer Kundenwebsites.
|
||
* <div data-webklar-shop="SITEKEY" data-api="https://project.webklar.com">Ausweichtext</div>
|
||
* data-ansicht="widerruf" oeffnet gleich die Widerrufsfunktion ("Vertrag widerrufen"); die Seitenadresse kennt ?ansicht=widerruf und ?ansicht=agb.
|
||
* Keine Abhaengigkeiten, keine Cookies, keine Drittanfragen. Einziger Browser-Speicher: der Warenkorb (nur Artikel-IDs und Mengen) in der
|
||
* sessionStorage des Besuchers, solange die Seite im Tab geoeffnet ist (technisch erforderlich, kein Tracking). Alle Texte der Haendler nur ueber
|
||
* textContent. Das Widget rechnet nie: Preise, Summen, Versand, Steuerhinweise und Datumstexte kommen als fertige Texte vom Server. */
|
||
(function () {
|
||
'use strict'
|
||
var SELF = document.currentScript
|
||
var STYLE_ID = 'wks-style'
|
||
var API_PFAD = '/api/public/shop/v1/'
|
||
var CONFIG_ALTER_MAX_MS = 100 * 60 * 1000 // vor dem Ablauf des Formular-Tokens (2 h) neu laden
|
||
var TIMEOUT_MS = 20000
|
||
var FORM_MIN_VORGABE_S = 3 // so lange muss ein Formular-Token mindestens alt sein (wie der Server)
|
||
var FORM_PUFFER_MS = 500 // Sicherheitszuschlag zu FORM_MIN_VORGABE_S (Laufzeit der Anfrage)
|
||
var MAX_MENGE = 99
|
||
var MAX_POSITIONEN = 50
|
||
var STATUS_VERSUCHE = 5 // Abrufe des Bestellstatus nach der Rueckkehr von Stripe, solange die Bestellung "offen" ist
|
||
var STATUS_ABSTAND_MS = 2000
|
||
var PREFIX = 'wks' + Math.random().toString(36).slice(2, 6) + '-'
|
||
var zaehler = 0
|
||
var EMAIL_FORM = /^[^\s@,;<>"]+@[^\s@,;<>"]+\.[^\s@,;<>"]+$/
|
||
var ID_FORM = /^[A-Za-z0-9][A-Za-z0-9._-]{0,35}$/
|
||
var TOKEN_FORM = /^[A-Za-z0-9._~-]{8,300}$/
|
||
var LAENDER = { DE: 'Deutschland', AT: 'Österreich' }
|
||
var rueckkehr = null // Rueckkehr von Stripe (wk_bestellung/wk_t oder wk_abbruch): der erste Container, der startet, verbraucht sie
|
||
var queryAnsicht = ''
|
||
|
||
var CSS = [
|
||
'@layer wks{',
|
||
'.wks-root{--wks-i-akzent:var(--wks-akzent,var(--wks-vorgabe-akzent,#1d4ed8));--wks-i-akzent-text:var(--wks-akzent-text,var(--wks-vorgabe-akzent-text,#fff));',
|
||
'--wks-i-bg:var(--wks-hintergrund,#fff);--wks-i-text:var(--wks-text,#1f2937);--wks-i-rand:var(--wks-rand,#858f9e);--wks-i-radius:var(--wks-radius,10px);',
|
||
'--wks-i-weich:#f3f4f6;--wks-i-fehler:#b91c1c;--wks-i-schrift:var(--wks-schrift,inherit);--wks-i-titel:var(--wks-schrift-titel,var(--wks-schrift,inherit))}',
|
||
'@supports (color:color-mix(in srgb,red,blue)){.wks-root{--wks-i-weich:color-mix(in srgb,var(--wks-i-text) 6%,var(--wks-i-bg))}}',
|
||
'.wks-root{container:wks/inline-size;display:block;position:relative;box-sizing:border-box;padding:20px;border:1px solid #c9ced6;border-radius:var(--wks-i-radius);',
|
||
'background:var(--wks-i-bg);color:var(--wks-i-text);font-family:var(--wks-i-schrift);font-size:16px;line-height:1.5;text-align:left}',
|
||
'.wks-root *,.wks-root *::before,.wks-root *::after{box-sizing:border-box}',
|
||
'.wks-root [hidden]{display:none!important}',
|
||
'.wks-vh{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}',
|
||
'.wks-kopfzeile{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:.5rem 1rem;margin:0 0 .25rem}',
|
||
'.wks-shopname{font-family:var(--wks-i-titel);font-size:1.5rem;line-height:1.2;font-weight:700;margin:0;color:inherit}',
|
||
'.wks-lieferinfo{margin:0 0 .75rem;font-size:.9rem;opacity:.9}',
|
||
'.wks-titel{font-family:var(--wks-i-titel);font-size:1.3rem;line-height:1.25;font-weight:700;margin:.5rem 0 .75rem;color:inherit}',
|
||
'.wks-titel:focus{outline:none}.wks-titel:focus-visible{outline:3px solid var(--wks-i-text);outline-offset:3px}',
|
||
'.wks-untertitel{font-size:1.05rem;font-weight:700;margin:1rem 0 .35rem;color:inherit}',
|
||
'.wks-root p{margin:0 0 .75rem}',
|
||
'.wks-laden{opacity:.8}',
|
||
'.wks-btn{font:inherit;font-weight:600;min-height:44px;padding:.55rem 1.25rem;border-radius:var(--wks-i-radius);border:2px solid var(--wks-i-akzent);background:var(--wks-i-akzent);color:var(--wks-i-akzent-text);cursor:pointer;line-height:1.25}',
|
||
'.wks-btn:hover{filter:brightness(1.08)}',
|
||
'.wks-btn-sek{background:transparent;color:inherit;border-color:var(--wks-i-rand)}',
|
||
'.wks-btn[disabled]{opacity:.6;cursor:default}',
|
||
'.wks-link{font:inherit;color:inherit;background:none;border:0;padding:.2rem 0;min-height:24px;text-decoration:underline;cursor:pointer;text-align:left}',
|
||
'.wks-nav{display:flex;flex-wrap:wrap;gap:.75rem;justify-content:space-between;align-items:center;margin-top:1.25rem}',
|
||
'.wks-nav>:only-child{margin-left:auto}',
|
||
'.wks-root :focus-visible{outline:3px solid var(--wks-i-text);outline-offset:2px}',
|
||
'.wks-chips{display:flex;flex-wrap:wrap;gap:.5rem;margin:0 0 1rem}',
|
||
'.wks-chip{font:inherit;min-height:44px;padding:.4rem 1rem;border-radius:999px;border:1px solid var(--wks-i-rand);background:var(--wks-i-bg);color:inherit;cursor:pointer}',
|
||
'.wks-chip[aria-pressed="true"]{background:var(--wks-i-akzent);color:var(--wks-i-akzent-text);border-color:var(--wks-i-akzent);font-weight:600}',
|
||
'.wks-raster{list-style:none;margin:0 0 1rem;padding:0;display:grid;grid-template-columns:repeat(auto-fill,minmax(11rem,1fr));gap:1rem}',
|
||
'.wks-raster>li{margin:0;padding:0}',
|
||
'.wks-karte{position:relative;display:flex;flex-direction:column;gap:.3rem;height:100%;border:1px solid var(--wks-i-rand);border-radius:var(--wks-i-radius);padding:.6rem;background:var(--wks-i-bg)}',
|
||
'.wks-karte:hover{border-color:var(--wks-i-akzent)}',
|
||
'.wks-karte:focus-within{outline:3px solid var(--wks-i-text);outline-offset:2px}',
|
||
'.wks-karte-bild,.wks-karte-leer{display:block;width:100%;aspect-ratio:1/1;object-fit:cover;border-radius:calc(var(--wks-i-radius) * .7);background:var(--wks-i-weich)}',
|
||
'.wks-karte-name{margin:0;font-size:1rem;line-height:1.3}',
|
||
'.wks-karte-knopf{font:inherit;font-weight:700;text-align:left;background:none;border:0;padding:0;color:inherit;cursor:pointer}',
|
||
'.wks-karte-knopf::after{content:"";position:absolute;inset:0}',
|
||
'.wks-karte-knopf:focus-visible{outline:none}',
|
||
'.wks-karte-text{font-size:.9rem;margin:0}',
|
||
'.wks-preis{font-weight:700;margin:0}',
|
||
'.wks-grundpreis,.wks-klein{font-size:.85rem;opacity:.85;margin:0}',
|
||
'.wks-aus{font-size:.85rem;font-weight:600;color:var(--wks-i-fehler);margin:0}',
|
||
'.wks-hinweiszeile{font-size:.85rem;margin:0 0 .75rem;opacity:.9}',
|
||
'.wks-detail{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:1.25rem;margin:0 0 1rem}',
|
||
'.wks-galerie-gross{display:block;width:100%;aspect-ratio:1/1;object-fit:contain;border:1px solid var(--wks-i-rand);border-radius:var(--wks-i-radius);background:var(--wks-i-weich)}',
|
||
'.wks-daumen{display:flex;flex-wrap:wrap;gap:.5rem;margin-top:.5rem}',
|
||
'.wks-daumen button{width:4rem;height:4rem;min-width:44px;min-height:44px;padding:0;border:2px solid var(--wks-i-rand);border-radius:calc(var(--wks-i-radius) * .7);background:var(--wks-i-bg);cursor:pointer;overflow:hidden}',
|
||
'.wks-daumen button[aria-pressed="true"]{border-color:var(--wks-i-akzent)}',
|
||
'.wks-daumen img{display:block;width:100%;height:100%;object-fit:cover}',
|
||
'.wks-beschreibung p,.wks-text p{white-space:pre-line}',
|
||
'.wks-form{margin:0}',
|
||
'.wks-feld{margin:0 0 1rem;min-width:0}',
|
||
'fieldset.wks-feld{border:1px solid var(--wks-i-rand);border-radius:var(--wks-i-radius);padding:.5rem 1rem .75rem}',
|
||
'.wks-feld legend{font-weight:600;padding:0 .25rem}',
|
||
'.wks-label{display:block;font-weight:600;margin-bottom:.25rem}',
|
||
'.wks-stern{color:var(--wks-i-fehler)}',
|
||
'.wks-hinweis{font-size:.875rem;margin:0 0 .35rem;opacity:.9}',
|
||
'.wks-eingabe{display:block;width:100%;min-height:44px;font:inherit;color:inherit;background:var(--wks-i-bg);border:1px solid var(--wks-i-rand);border-radius:calc(var(--wks-i-radius) * .7);padding:.5rem .7rem}',
|
||
'.wks-eingabe-menge{width:6rem}',
|
||
'textarea.wks-eingabe{min-height:6rem;resize:vertical}',
|
||
'.wks-eingabe[aria-invalid="true"]{border-color:var(--wks-i-fehler);border-width:2px}',
|
||
'.wks-feldfehler,.wks-fehlertext{color:var(--wks-i-fehler);font-weight:600;font-size:.9rem;margin:.3rem 0 0}',
|
||
'.wks-fehlertext{margin:0 0 .75rem}',
|
||
'.wks-meldung{margin:0 0 .75rem;padding:.6rem .8rem;border-left:4px solid var(--wks-i-akzent);background:var(--wks-i-weich);font-weight:600}',
|
||
'.wks-radio{display:flex;gap:.65rem;align-items:flex-start;margin:0 0 .5rem;min-height:44px}',
|
||
'.wks-radio input{flex:none;width:1.4rem;height:1.4rem;margin:.35rem 0 0;accent-color:var(--wks-i-akzent)}',
|
||
'.wks-radio label{cursor:pointer;padding-top:.2rem}',
|
||
'.wks-zeilen{list-style:none;margin:0 0 1rem;padding:0;display:grid;gap:.6rem}',
|
||
'.wks-zeilen>li{margin:0;padding:.7rem .9rem;border:1px solid var(--wks-i-rand);border-radius:var(--wks-i-radius);display:flex;flex-wrap:wrap;gap:.5rem 1rem;align-items:center;justify-content:space-between}',
|
||
'.wks-zeile-name{font-weight:700;margin:0}',
|
||
'.wks-zeile-info{flex:1 1 12rem;min-width:0}',
|
||
'.wks-zeile-menge{display:flex;align-items:center;gap:.5rem}',
|
||
'.wks-zeile-menge label{font-size:.9rem}',
|
||
'.wks-pflicht{margin:1.25rem 0;padding:.9rem 1rem;border:2px solid var(--wks-i-akzent);border-radius:var(--wks-i-radius);background:var(--wks-i-bg)}',
|
||
'.wks-pflicht>:last-child{margin-bottom:0}',
|
||
'.wks-pflicht h4{font-size:1rem;margin:.9rem 0 .25rem}',
|
||
'.wks-pflicht h4:first-child{margin-top:0}',
|
||
'.wks-pos{list-style:none;margin:0 0 .75rem;padding:0;display:grid;gap:.5rem}',
|
||
'.wks-pos>li{display:flex;flex-wrap:wrap;justify-content:space-between;gap:.25rem 1rem;margin:0}',
|
||
'.wks-pos-text{flex:1 1 12rem;min-width:0;overflow-wrap:anywhere}',
|
||
'.wks-summen{margin:0 0 .75rem;display:grid;grid-template-columns:1fr max-content;gap:.25rem 1rem}',
|
||
'.wks-summen dt{margin:0}.wks-summen dd{margin:0;text-align:right}',
|
||
'.wks-summen .wks-gesamt{font-weight:700;font-size:1.1rem;border-top:1px solid var(--wks-i-rand);padding-top:.35rem}',
|
||
'.wks-zusammen{margin:0 0 1rem;display:grid;grid-template-columns:max-content 1fr;gap:.35rem 1rem}',
|
||
'.wks-zusammen dt{font-weight:600}.wks-zusammen dd{margin:0;overflow-wrap:anywhere;white-space:pre-line}',
|
||
'.wks-gewaehr{margin:1.25rem 0;padding:.9rem 1rem;border:2px solid var(--wks-i-akzent);border-radius:var(--wks-i-radius);background:var(--wks-i-weich)}',
|
||
'.wks-gewaehr h4{font-size:1rem;margin:0 0 .6rem}',
|
||
'.wks-gewaehr img{display:block;width:100%;max-width:420px;height:auto;margin:0 0 .6rem;border:1px solid var(--wks-i-rand);border-radius:calc(var(--wks-i-radius) * .7);background:#fff}',
|
||
'.wks-gewaehr>:last-child{margin-bottom:0}',
|
||
'.wks-agb-hinweis{margin:0 0 .75rem}',
|
||
'dialog.wks-dialog.wks-dialog-gross{width:min(64rem,calc(100vw - 1.5rem))}',
|
||
'.wks-dialog-bild{display:block;max-width:100%;height:auto;margin:0 auto 1rem;background:#fff}',
|
||
'.wks-links{display:flex;flex-wrap:wrap;gap:.25rem 1.25rem;margin:0 0 .75rem}',
|
||
'.wks-box{margin:0 0 1rem;padding:.75rem 1rem;border-radius:var(--wks-i-radius);background:var(--wks-i-weich)}',
|
||
'.wks-box h4{font-size:1rem;margin:0 0 .25rem}',
|
||
'.wks-box p{white-space:pre-line}',
|
||
'.wks-fuss{margin-top:1.5rem;padding-top:.75rem;border-top:1px solid #c9ced6;font-size:.875rem}',
|
||
'.wks-fuss-links{display:flex;flex-wrap:wrap;align-items:baseline;gap:.35rem 1.25rem;margin:0 0 .5rem}',
|
||
'.wks-fuss a{display:inline-block;padding:.2rem 0;min-height:24px;color:inherit}',
|
||
'.wks-anbieter{margin:0 0 .35rem;opacity:.85;overflow-wrap:anywhere}',
|
||
'.wks-powered{margin:0;opacity:.75;font-size:.8rem}',
|
||
'.wks-ergebnis-text{font-size:1.05rem}',
|
||
'dialog.wks-dialog{position:fixed;inset:auto;left:50%;top:50%;transform:translate(-50%,-50%);margin:0;padding:0;border:0;overflow:auto;width:min(40rem,calc(100vw - 1.5rem));max-height:calc(100vh - 1.5rem);max-height:calc(100dvh - 1.5rem);border-radius:var(--wks-radius,10px);background:var(--wks-hintergrund,#fff);color:var(--wks-text,#1f2937);box-shadow:0 20px 50px rgba(0,0,0,.35)}',
|
||
'dialog.wks-dialog::backdrop{background:rgba(15,23,42,.55)}',
|
||
'.wks-dialog-innen{padding:20px;font-family:var(--wks-schrift,inherit);font-size:16px;line-height:1.5;text-align:left}',
|
||
'.wks-dialog-innen h3{font-size:1.2rem;margin:0 3rem .75rem 0}',
|
||
'.wks-dialog-text{font-size:.95rem}',
|
||
'.wks-dialog-text p{white-space:pre-line;margin:0 0 .75rem}',
|
||
'.wks-schliessen{position:absolute;top:.5rem;right:.5rem;z-index:1;font:inherit;font-size:1.5rem;line-height:1;min-width:44px;min-height:44px;border-radius:var(--wks-i-radius,10px);border:1px solid #858f9e;background:#fff;color:#1f2937;cursor:pointer}',
|
||
'@container wks (max-width:34rem){',
|
||
'.wks-detail{grid-template-columns:minmax(0,1fr)}',
|
||
'.wks-nav{flex-direction:column-reverse;align-items:stretch}.wks-nav .wks-btn{width:100%}',
|
||
'.wks-zusammen{grid-template-columns:1fr;gap:0}.wks-zusammen dd{margin-bottom:.5rem}',
|
||
'.wks-raster{grid-template-columns:repeat(auto-fill,minmax(9rem,1fr));gap:.6rem}',
|
||
'}',
|
||
'@media (max-width:30rem){.wks-root{padding:16px}}',
|
||
'@media (forced-colors:active){.wks-chip[aria-pressed="true"],.wks-daumen button[aria-pressed="true"]{outline:2px solid Highlight}}',
|
||
'}'
|
||
].join('\n')
|
||
|
||
function injectStyle () {
|
||
if (document.getElementById(STYLE_ID)) return
|
||
var st = document.createElement('style')
|
||
st.id = STYLE_ID
|
||
st.textContent = CSS
|
||
document.head.appendChild(st)
|
||
}
|
||
|
||
function el (tag, cls, text) {
|
||
var e = document.createElement(tag)
|
||
if (cls) e.className = cls
|
||
if (text != null) e.textContent = text
|
||
return e
|
||
}
|
||
|
||
function knopf (cls, text, onClick) {
|
||
var b = el('button', cls, text)
|
||
b.type = 'button'
|
||
if (onClick) b.addEventListener('click', onClick)
|
||
return b
|
||
}
|
||
|
||
function httpsUrl (u) {
|
||
if (typeof u !== 'string') return ''
|
||
try {
|
||
var p = new URL(u)
|
||
return p.protocol === 'https:' ? p.href : ''
|
||
} catch (e) { return '' }
|
||
}
|
||
|
||
function istText (v) { return typeof v === 'string' }
|
||
function nichtLeer (v) { return typeof v === 'string' && v.replace(/\s+/g, '') !== '' }
|
||
function istObjekt (v) { return v !== null && typeof v === 'object' && !Array.isArray(v) }
|
||
function liste (v) { return Array.isArray(v) ? v : [] }
|
||
function knoten (nl) { return Array.prototype.slice.call(nl) }
|
||
|
||
// Pfade des Servers (ohne Host) bekommen die API-Adresse vorangestellt; nur Pfade, die mit genau einem / beginnen
|
||
function absUrl (api, pfad) {
|
||
if (!istText(pfad) || pfad.charAt(0) !== '/' || pfad.charAt(1) === '/') return ''
|
||
return api + pfad
|
||
}
|
||
|
||
function apiBasis (root) {
|
||
var a = String(root.getAttribute('data-api') || '').replace(/\/+$/, '')
|
||
if (a) return a
|
||
var s = SELF && SELF.src ? SELF : document.querySelector('script[src*="/embed/shop.js"]')
|
||
if (s && s.src) {
|
||
try { return new URL(s.src).origin } catch (e) { return '' }
|
||
}
|
||
return ''
|
||
}
|
||
|
||
function queryWert (name) {
|
||
var m = new RegExp('[?&]' + name + '=([^&#]*)').exec(location.search || '')
|
||
if (!m) return ''
|
||
try { return decodeURIComponent(m[1].replace(/\+/g, ' ')) } catch (e) { return '' }
|
||
}
|
||
|
||
// Parameter aus der Adresse der Seite entfernen, ohne neu zu laden (nach der Rueckkehr von Stripe)
|
||
function bereinigeUrl (namen) {
|
||
try {
|
||
var rest = String(location.search || '').replace(/^\?/, '').split('&').filter(function (p) {
|
||
return p !== '' && namen.indexOf(p.split('=')[0]) < 0
|
||
}).join('&')
|
||
history.replaceState(history.state, '', location.pathname + (rest ? '?' + rest : '') + (location.hash || ''))
|
||
} catch (e) { /* Adresse bleibt, wie sie ist */ }
|
||
}
|
||
|
||
// @farben-anfang (reine Funktionen wie in embed/termine.js)
|
||
// Relative Leuchtdichte nach WCAG 2.1 fuer '#rrggbb'.
|
||
function luminanz (farbe) {
|
||
var rgb = [1, 3, 5].map(function (i) {
|
||
var c = parseInt(farbe.slice(i, i + 2), 16) / 255
|
||
return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4)
|
||
})
|
||
return 0.2126 * rgb[0] + 0.7152 * rgb[1] + 0.0722 * rgb[2]
|
||
}
|
||
|
||
// Kontrastverhaeltnis zweier Leuchtdichten (1 bis 21).
|
||
function kontrast (a, b) {
|
||
return (Math.max(a, b) + 0.05) / (Math.min(a, b) + 0.05)
|
||
}
|
||
|
||
// Schrift auf der Akzentfarbe: Weiss oder das dunkle Grau, je nachdem was mehr Kontrast gibt; erreicht keines 4,5 : 1, nimmt sie Schwarz.
|
||
function textFarbeAuf (farbe) {
|
||
var l = luminanz(farbe)
|
||
var weiss = kontrast(l, 1)
|
||
var dunkel = kontrast(l, luminanz('#111827'))
|
||
var beste = Math.max(weiss, dunkel)
|
||
if (beste < 4.5 && kontrast(l, 0) > beste) return '#000000'
|
||
return weiss >= dunkel ? '#ffffff' : '#111827'
|
||
}
|
||
// @farben-ende
|
||
|
||
// Akzentfarbe des Shops (#rrggbb), wenn die Seite --wks-akzent nicht selbst setzt
|
||
function setzeAkzent (box, farbe) {
|
||
if (!/^#[0-9a-fA-F]{6}$/.test(farbe || '')) return
|
||
if (getComputedStyle(box).getPropertyValue('--wks-akzent').trim()) return
|
||
box.style.setProperty('--wks-vorgabe-akzent', farbe)
|
||
box.style.setProperty('--wks-vorgabe-akzent-text', textFarbeAuf(farbe))
|
||
}
|
||
|
||
// ---------------------------------------------------------------- Warenkorb im Browser (nur IDs und Mengen)
|
||
|
||
function korbSchluessel (key) { return 'wks-korb-' + key }
|
||
|
||
function korbLesen (key) {
|
||
var aus = []
|
||
try {
|
||
var roh = window.sessionStorage.getItem(korbSchluessel(key))
|
||
var arr = roh ? JSON.parse(roh) : []
|
||
if (Array.isArray(arr)) {
|
||
arr.slice(0, MAX_POSITIONEN).forEach(function (z) {
|
||
if (!Array.isArray(z) || z.length !== 3 || !istText(z[0]) || !ID_FORM.test(z[0]) || !istText(z[1]) || (z[1] !== '' && !ID_FORM.test(z[1]))) return
|
||
var n = z[2]
|
||
if (typeof n !== 'number' || n % 1 !== 0 || n < 1) return
|
||
aus.push({ id: z[0], v: z[1], n: Math.min(n, MAX_MENGE) })
|
||
})
|
||
}
|
||
} catch (e) { aus = [] }
|
||
return aus
|
||
}
|
||
|
||
function korbSchreiben (key, korb) {
|
||
try {
|
||
if (korb.length === 0) window.sessionStorage.removeItem(korbSchluessel(key))
|
||
else window.sessionStorage.setItem(korbSchluessel(key), JSON.stringify(korb.map(function (z) { return [z.id, z.v, z.n] })))
|
||
} catch (e) { /* ohne Speicher bleibt der Warenkorb im Speicher der Seite */ }
|
||
}
|
||
|
||
// Zeilen eines Textes: Absaetze sind durch Leerzeilen getrennt
|
||
function absaetze (text, cls) {
|
||
var box = el('div', cls)
|
||
String(text).split(/\n{2,}/).forEach(function (a) { if (a.trim()) box.appendChild(el('p', '', a.trim())) })
|
||
return box
|
||
}
|
||
|
||
function adresseText (a) {
|
||
if (!istObjekt(a)) return ''
|
||
var land = istText(a.land) ? (LAENDER[a.land] || a.land) : ''
|
||
return [a.name, a.strasse, [a.plz, a.ort].filter(nichtLeer).join(' '), land].filter(nichtLeer).join(', ')
|
||
}
|
||
|
||
// ---------------------------------------------------------------- Ein Shop
|
||
|
||
function init (root) {
|
||
var key = root.getAttribute('data-webklar-shop')
|
||
if (!key || root.getAttribute('data-wks-init')) return
|
||
root.setAttribute('data-wks-init', '1')
|
||
var api = apiBasis(root)
|
||
var id = PREFIX + (++zaehler) + '-'
|
||
var ausweich = null // Ausweichtext der Seite (Kindknoten des Containers), solange das Widget ihn ersetzt
|
||
var dlg = null
|
||
var dlgAusloeser = null
|
||
var ui = { box: null, live: null, kopf: null, body: null, fuss: null, titel: null, statusBox: null }
|
||
var S = null
|
||
|
||
function neuerZustand () {
|
||
return {
|
||
cfg: S ? S.cfg : null,
|
||
cfgAt: S ? S.cfgAt : 0,
|
||
korb: S ? S.korb : [],
|
||
ansicht: '',
|
||
vorWiderruf: '',
|
||
kategorie: '',
|
||
produktId: '',
|
||
auswahl: { v: '', n: 1 },
|
||
bild: 0,
|
||
lieferart: '',
|
||
land: '',
|
||
pruef: null,
|
||
pruefFehler: null,
|
||
pruefSeq: 0,
|
||
honig: '',
|
||
fehler: {},
|
||
meldung: '',
|
||
sendet: false,
|
||
tot: false,
|
||
fokusGesetzt: false,
|
||
seq: 0,
|
||
statusSeq: 0,
|
||
status: null,
|
||
wd: { name: '', email: '', bestellnummer: '', nachricht: '' },
|
||
wdSchritt: 'form',
|
||
wdEingang: ''
|
||
}
|
||
}
|
||
S = neuerZustand()
|
||
|
||
function fid (name) { return id + String(name).replace(/[^a-z0-9_-]/gi, '-') }
|
||
|
||
// ------------------------------------------------------------ Anfragen
|
||
|
||
function request (method, pfad, body, frisch) {
|
||
var opts = { method: method, credentials: 'omit', headers: { Accept: 'application/json' } }
|
||
if (frisch) opts.cache = 'reload' // Konfiguration erneuern: nicht aus dem Browser-Cache (dort steht das alte Formular-Token)
|
||
if (body !== undefined) {
|
||
opts.headers['Content-Type'] = 'application/json'
|
||
opts.body = JSON.stringify(body)
|
||
}
|
||
var ctrl = typeof AbortController === 'function' ? new AbortController() : null
|
||
var timer = null
|
||
if (ctrl) {
|
||
opts.signal = ctrl.signal
|
||
timer = setTimeout(function () { ctrl.abort() }, TIMEOUT_MS)
|
||
}
|
||
function fertig () { if (timer) clearTimeout(timer) }
|
||
return fetch(api + API_PFAD + encodeURIComponent(key) + pfad, opts).then(function (r) {
|
||
return r.json().then(function (d) { return { status: r.status, ok: r.ok, body: d } }, function () { return { status: r.status, ok: r.ok, body: null } })
|
||
}, function () {
|
||
throw { netz: true }
|
||
}).then(function (r) {
|
||
fertig()
|
||
if (r.ok && r.body !== null && typeof r.body === 'object') return r.body
|
||
var fehler = {}
|
||
var b = r.body
|
||
if (b && typeof b.fehler === 'object' && b.fehler) {
|
||
Object.keys(b.fehler).forEach(function (k) { if (istText(b.fehler[k])) fehler[k] = b.fehler[k] })
|
||
}
|
||
throw { status: r.status, fehler: fehler, code: b && istText(b.code) ? b.code : '', produktId: b && istText(b.produktId) ? b.produktId : '', netz: r.status >= 500 || r.status === 403 || r.status === 404 || r.status === 0 }
|
||
}, function (err) {
|
||
fertig()
|
||
throw err
|
||
})
|
||
}
|
||
|
||
// ------------------------------------------------------------ Konfiguration
|
||
|
||
// Fail closed: fehlt etwas, das der Shop zwingend braucht, gilt er als nicht aktiv (der Ausweichtext der Seite bleibt)
|
||
function konfigGueltig (d) {
|
||
if (!d || d.aktiv !== true || !istObjekt(d.shop) || !nichtLeer(d.shop.name) || !nichtLeer(d.formToken)) return false
|
||
if (d.nurWiderruf === true) return true
|
||
if (typeof d.offen !== 'boolean' || !Array.isArray(d.produkte) || !istObjekt(d.shop.anbieter)) return false
|
||
return d.produkte.every(function (p) {
|
||
return istObjekt(p) && nichtLeer(p.id) && ID_FORM.test(p.id) && istText(p.name) && istText(p.preisText)
|
||
})
|
||
}
|
||
|
||
function lade (neu) {
|
||
return request('GET', '', undefined, neu).then(function (d) {
|
||
if (!konfigGueltig(d)) throw { inaktiv: true }
|
||
S.cfg = d
|
||
S.cfgAt = performance.now()
|
||
return d
|
||
})
|
||
}
|
||
|
||
function configFrisch () { return !!S.cfg && performance.now() - S.cfgAt <= CONFIG_ALTER_MAX_MS }
|
||
|
||
// Alte Konfiguration (das Formular-Token laeuft nach 2 h ab) erneuern. Aufgeloest wird mit true, wenn neu geladen wurde.
|
||
// Ist der Server nicht erreichbar, geht es mit der alten Konfiguration weiter: der naechste Aufruf meldet es.
|
||
function sichereConfig () {
|
||
if (configFrisch()) return Promise.resolve(false)
|
||
return lade(true).then(function () { bereinigeKorb(); return true }, function (err) {
|
||
if (err && err.inaktiv) { zeigeAusweich(); throw err }
|
||
return false
|
||
})
|
||
}
|
||
|
||
// Der Server speichert nichts (und antwortet doch mit Erfolg), wenn das Formular-Token juenger als 3 Sekunden ist: das Widget wartet vor dem
|
||
// Absenden, bis es alt genug ist (gemessen ab Eintreffen der Konfiguration; das Token ist beim Server immer etwas aelter).
|
||
function formWartezeitMs () {
|
||
return Math.max(0, FORM_MIN_VORGABE_S * 1000 + FORM_PUFFER_MS - (performance.now() - S.cfgAt))
|
||
}
|
||
|
||
// true = Token alt genug; false = die Ansicht hat sich inzwischen geaendert: nicht senden.
|
||
function warteAufFormToken (senden) {
|
||
var ms = formWartezeitMs()
|
||
if (ms <= 0) return Promise.resolve(true)
|
||
var zustand = S
|
||
var seq = S.seq
|
||
var beschriftung = senden.textContent
|
||
senden.textContent = 'Einen Moment …'
|
||
ansage('Einen Moment …')
|
||
return new Promise(function (resolve) {
|
||
setTimeout(function () {
|
||
if (S !== zustand || S.seq !== seq || S.tot) { zustand.sendet = false; resolve(false); return }
|
||
senden.textContent = beschriftung
|
||
resolve(true)
|
||
}, ms)
|
||
})
|
||
}
|
||
|
||
// ------------------------------------------------------------ Daten des Shops
|
||
|
||
function shop () { return S.cfg.shop }
|
||
function nurWiderruf () { return S.cfg.nurWiderruf === true }
|
||
function rechtstexte () { return istObjekt(S.cfg.rechtstexte) ? S.cfg.rechtstexte : null }
|
||
function gewaehr () {
|
||
var g = S.cfg.gewaehrleistung
|
||
return istObjekt(g) && nichtLeer(g.text) ? g : null
|
||
}
|
||
// Bestellen ist nur moeglich, wenn der Shop geoeffnet ist und die bestaetigten Rechtstexte samt Gewaehrleistungshinweis vorliegen
|
||
function bestellbar () {
|
||
var r = rechtstexte()
|
||
return !nurWiderruf() && S.cfg.offen === true && !!r && nichtLeer(r.agb) && nichtLeer(r.widerrufsbelehrung) && !!gewaehr()
|
||
}
|
||
function produktMitId (pid) {
|
||
var p = liste(S.cfg.produkte)
|
||
for (var i = 0; i < p.length; i++) if (p[i].id === pid) return p[i]
|
||
return null
|
||
}
|
||
function varianteMitId (p, vid) {
|
||
var w = p && istObjekt(p.varianten) ? liste(p.varianten.werte) : []
|
||
for (var i = 0; i < w.length; i++) if (w[i].id === vid) return w[i]
|
||
return null
|
||
}
|
||
function hatVarianten (p) { return !!p && istObjekt(p.varianten) && liste(p.varianten.werte).length > 0 }
|
||
function anzahlImKorb () { return S.korb.reduce(function (s, z) { return s + z.n }, 0) }
|
||
function speichereKorb () { korbSchreiben(key, S.korb) }
|
||
|
||
// Positionen, die es nicht (mehr) gibt, fliegen aus dem Warenkorb; gibt die Anzahl entfernter Positionen zurueck
|
||
function bereinigeKorb () {
|
||
var vorher = S.korb.length
|
||
S.korb = S.korb.filter(function (z) {
|
||
var p = produktMitId(z.id)
|
||
if (!p) return false
|
||
if (hatVarianten(p)) return !!varianteMitId(p, z.v)
|
||
return z.v === ''
|
||
})
|
||
var weg = vorher - S.korb.length
|
||
if (weg > 0) {
|
||
speichereKorb()
|
||
S.korbHinweis = true
|
||
}
|
||
return weg
|
||
}
|
||
|
||
function lieferInfoText () {
|
||
var s = shop()
|
||
var teile = []
|
||
var laender = liste(s.lieferlaender).map(function (l) { return istObjekt(l) ? l.name : '' }).filter(nichtLeer)
|
||
if (laender.length) teile.push('Lieferung nach: ' + laender.join(', '))
|
||
if (istObjekt(s.abholung) && s.abholung.aktiv === true) teile.push('Abholung möglich')
|
||
if (nichtLeer(s.zahlungsarten)) teile.push('Zahlung: ' + s.zahlungsarten)
|
||
return teile.join(' · ')
|
||
}
|
||
|
||
function anbieterZeile () {
|
||
var a = shop().anbieter
|
||
if (!istObjekt(a)) return ''
|
||
var teile = [adresseText(a)]
|
||
if (nichtLeer(a.email)) teile.push('E-Mail: ' + a.email)
|
||
if (nichtLeer(a.telefon)) teile.push('Telefon: ' + a.telefon)
|
||
return teile.filter(nichtLeer).join(', ')
|
||
}
|
||
|
||
// ------------------------------------------------------------ Geruest
|
||
|
||
function bauGeruest (box) {
|
||
box.classList.add('wks-root')
|
||
var live = el('div', 'wks-vh')
|
||
live.setAttribute('aria-live', 'polite')
|
||
live.setAttribute('aria-atomic', 'true')
|
||
var kopf = el('div', 'wks-kopf')
|
||
var body = el('div', 'wks-body')
|
||
var fuss = el('div', 'wks-fuss')
|
||
box.appendChild(live)
|
||
box.appendChild(kopf)
|
||
box.appendChild(body)
|
||
box.appendChild(fuss)
|
||
ui.box = box
|
||
ui.live = live
|
||
ui.kopf = kopf
|
||
ui.body = body
|
||
ui.fuss = fuss
|
||
}
|
||
|
||
function ansage (text) {
|
||
if (ui.live) ui.live.textContent = text
|
||
}
|
||
|
||
function add (node) { ui.body.appendChild(node); return node }
|
||
|
||
function titel (text) {
|
||
var h = el('h3', 'wks-titel', text)
|
||
h.tabIndex = -1
|
||
h.id = fid('titel')
|
||
ui.titel = h
|
||
return h
|
||
}
|
||
|
||
function meldungAnzeigen () {
|
||
if (S.meldung) {
|
||
add(el('p', 'wks-meldung', S.meldung))
|
||
ansage(S.meldung)
|
||
}
|
||
}
|
||
|
||
function inBestellung (a) { return a === 'raster' || a === 'detail' || a === 'korb' || a === 'kasse' }
|
||
|
||
function zeichneKopf () {
|
||
ui.kopf.textContent = ''
|
||
var zeile = el('div', 'wks-kopfzeile')
|
||
zeile.appendChild(el('h2', 'wks-shopname', shop().name))
|
||
if (bestellbar() && S.ansicht !== 'status') {
|
||
var n = anzahlImKorb()
|
||
var kk = knopf('wks-btn wks-btn-sek', 'Warenkorb (' + n + ')', function () { gehe('korb') })
|
||
kk.setAttribute('aria-label', 'Warenkorb, ' + n + ' Stück')
|
||
zeile.appendChild(kk)
|
||
}
|
||
ui.kopf.appendChild(zeile)
|
||
if (bestellbar() && inBestellung(S.ansicht)) {
|
||
var info = lieferInfoText()
|
||
if (info) ui.kopf.appendChild(el('p', 'wks-lieferinfo', info))
|
||
}
|
||
}
|
||
|
||
function zeichne (fokus) {
|
||
S.seq++
|
||
S.fokusGesetzt = false
|
||
S.statusSeq++
|
||
ui.body.textContent = ''
|
||
ui.titel = null
|
||
zeichneKopf()
|
||
var f = {
|
||
raster: zeichneRaster, detail: zeichneDetail, korb: zeichneKorb, kasse: zeichneKasse,
|
||
status: zeichneStatus, widerruf: zeichneWiderruf, geschlossen: zeichneGeschlossen
|
||
}
|
||
;(f[S.ansicht] || zeichneRaster)()
|
||
zeichneFuss()
|
||
if (fokus) {
|
||
if (ui.titel && !S.fokusGesetzt) ui.titel.focus()
|
||
try {
|
||
if (ui.box.getBoundingClientRect().top < 0) ui.box.scrollIntoView({ block: 'start' })
|
||
} catch (e) { /* Scrollen ist nur eine Hilfe */ }
|
||
}
|
||
}
|
||
|
||
function gehe (ansicht, opt) {
|
||
opt = opt || {}
|
||
if (ansicht === 'widerruf' && S.ansicht !== 'widerruf') S.vorWiderruf = S.ansicht
|
||
S.ansicht = ansicht
|
||
S.fehler = opt.fehler || {}
|
||
S.meldung = opt.meldung || ''
|
||
zeichne(opt.fokus !== false)
|
||
}
|
||
|
||
function startAnsicht () {
|
||
if (nurWiderruf()) return 'widerruf'
|
||
return bestellbar() ? 'raster' : 'geschlossen'
|
||
}
|
||
|
||
// Fuss: immer AGB, Widerrufsbelehrung, Versand & Zahlung, Vertrag widerrufen, Impressum, Datenschutz (soweit bekannt)
|
||
function zeichneFuss () {
|
||
ui.fuss.textContent = ''
|
||
var r = rechtstexte()
|
||
var links = el('p', 'wks-fuss-links')
|
||
if (r && nichtLeer(r.agb)) links.appendChild(knopf('wks-link', 'AGB', function (e) { zeigeAgb(e.currentTarget) }))
|
||
if (r && nichtLeer(r.widerrufsbelehrung)) links.appendChild(knopf('wks-link', 'Widerrufsbelehrung', function (e) { zeigeBelehrung(e.currentTarget) }))
|
||
if (r && nichtLeer(r.versandUndZahlung)) links.appendChild(knopf('wks-link', 'Versand & Zahlung', function (e) { zeigeVersand(e.currentTarget) }))
|
||
if (!nurWiderruf()) links.appendChild(knopf('wks-btn wks-btn-sek', 'Vertrag widerrufen', zeigeWiderruf)) // im Widerrufs-Modus ist das Formular schon die ganze Ansicht
|
||
var imp = r ? httpsUrl(r.impressumUrl) : ''
|
||
if (imp) links.appendChild(externerLink('Impressum', imp))
|
||
var ds = r ? httpsUrl(r.datenschutzUrl) : ''
|
||
if (ds) links.appendChild(externerLink('Datenschutz', ds))
|
||
ui.fuss.appendChild(links)
|
||
var anb = anbieterZeile()
|
||
if (anb) ui.fuss.appendChild(el('p', 'wks-anbieter', 'Anbieter: ' + anb))
|
||
ui.fuss.appendChild(el('p', 'wks-powered', 'Shop-Technik: WEBklar'))
|
||
}
|
||
|
||
function externerLink (text, href) {
|
||
var a = el('a', '', text)
|
||
a.href = href
|
||
a.target = '_blank'
|
||
a.rel = 'noopener noreferrer'
|
||
return a
|
||
}
|
||
|
||
// Shop nicht erreichbar oder nicht aktiv: der Ausweichtext der Seite bleibt bzw. kommt zurueck
|
||
function zeigeAusweich () {
|
||
S.tot = true
|
||
root.classList.remove('wks-root')
|
||
root.textContent = ''
|
||
if (ausweich) root.appendChild(ausweich.cloneNode(true)) // Kopie: ein zweiter Aufruf darf den Text nicht verlieren
|
||
}
|
||
|
||
// Netz weg oder Server kaputt mitten in der Strecke: Meldung und "Erneut versuchen"; nie ein selbststaendiger neuer Abruf
|
||
function ausfall (wiederhole, meldung) {
|
||
S.sendet = false
|
||
ui.body.textContent = ''
|
||
ui.titel = null
|
||
var p = el('p', 'wks-fehlertext', meldung || 'Gerade nicht erreichbar. Bitte versuchen Sie es später noch einmal.')
|
||
p.tabIndex = -1
|
||
add(p)
|
||
add(knopf('wks-btn wks-btn-sek', 'Erneut versuchen', function () { wiederhole() }))
|
||
ansage(p.textContent)
|
||
p.focus()
|
||
}
|
||
|
||
function meldung429 (err) {
|
||
return (err.fehler && err.fehler._) || 'Zu viele Anfragen. Bitte versuchen Sie es später noch einmal.'
|
||
}
|
||
|
||
// ------------------------------------------------------------ Dialog fuer Rechtstexte
|
||
|
||
function dialogOeffnen (ueberschrift, bloecke, ausloeser) {
|
||
if (!dlg) {
|
||
dlg = el('dialog', 'wks-dialog')
|
||
dlg.addEventListener('click', function (e) { if (e.target === dlg) dialogSchliessen() })
|
||
dlg.addEventListener('close', function () { if (dlgAusloeser && dlgAusloeser.focus) dlgAusloeser.focus() })
|
||
// Esc schliesst auch dort, wo showModal fehlt (der Browser erledigt es sonst selbst)
|
||
dlg.addEventListener('keydown', function (e) { if (e.key === 'Escape' || e.key === 'Esc') { e.preventDefault(); dialogSchliessen() } })
|
||
root.appendChild(dlg)
|
||
}
|
||
dlgAusloeser = ausloeser || null
|
||
dlg.className = 'wks-dialog' + (bloecke.some(function (b) { return !!b.bild }) ? ' wks-dialog-gross' : '')
|
||
dlg.textContent = ''
|
||
var innen = el('div', 'wks-dialog-innen')
|
||
var zu = knopf('wks-schliessen', '×', dialogSchliessen)
|
||
zu.setAttribute('aria-label', 'Schließen')
|
||
innen.appendChild(zu)
|
||
var h = el('h3', '', ueberschrift)
|
||
h.tabIndex = -1
|
||
innen.appendChild(h)
|
||
var text = el('div', 'wks-dialog-text')
|
||
text.tabIndex = 0 // der Text scrollt: auch mit der Tastatur erreichbar
|
||
text.setAttribute('role', 'region')
|
||
text.setAttribute('aria-label', ueberschrift)
|
||
bloecke.forEach(function (b) {
|
||
if (b.bild) {
|
||
var gross = el('img', 'wks-dialog-bild')
|
||
gross.src = b.bild
|
||
gross.alt = nichtLeer(b.text) ? b.text : ''
|
||
gross.decoding = 'async'
|
||
text.appendChild(gross)
|
||
return
|
||
}
|
||
if (!nichtLeer(b.text)) return
|
||
if (b.titel) text.appendChild(el('h4', 'wks-untertitel', b.titel))
|
||
text.appendChild(absaetze(b.text, ''))
|
||
})
|
||
innen.appendChild(text)
|
||
var nav = el('div', 'wks-nav')
|
||
nav.appendChild(knopf('wks-btn wks-btn-sek', 'Schließen', dialogSchliessen))
|
||
innen.appendChild(nav)
|
||
dlg.appendChild(innen)
|
||
dlg.setAttribute('aria-label', ueberschrift)
|
||
if (typeof dlg.showModal === 'function') {
|
||
if (!dlg.open) dlg.showModal()
|
||
} else {
|
||
dlg.setAttribute('open', '')
|
||
}
|
||
h.focus()
|
||
}
|
||
|
||
function dialogSchliessen () {
|
||
if (!dlg) return
|
||
if (typeof dlg.close === 'function') dlg.close()
|
||
else dlg.removeAttribute('open')
|
||
if (dlgAusloeser && dlgAusloeser.focus) dlgAusloeser.focus()
|
||
}
|
||
|
||
function zeigeAgb (ausloeser) {
|
||
var r = rechtstexte()
|
||
if (r) dialogOeffnen('Allgemeine Geschäftsbedingungen', [{ text: r.agb }], ausloeser)
|
||
}
|
||
function zeigeBelehrung (ausloeser) {
|
||
var r = rechtstexte()
|
||
if (r) dialogOeffnen('Widerrufsbelehrung', [{ text: r.widerrufsbelehrung }, { titel: 'Muster-Widerrufsformular', text: r.widerrufsformular }], ausloeser)
|
||
}
|
||
function zeigeVersand (ausloeser) {
|
||
var r = rechtstexte()
|
||
var s = shop()
|
||
var bloecke = []
|
||
if (nichtLeer(s.versandText)) bloecke.push({ titel: 'Versandkosten', text: s.versandText })
|
||
if (nichtLeer(s.mindestbestellText)) bloecke.push({ text: s.mindestbestellText })
|
||
if (nichtLeer(s.lieferzeit)) bloecke.push({ titel: 'Lieferzeit', text: s.lieferzeit })
|
||
if (r && nichtLeer(r.versandUndZahlung)) bloecke.push({ text: r.versandUndZahlung })
|
||
dialogOeffnen('Versand und Zahlung', bloecke, ausloeser)
|
||
}
|
||
|
||
// Hinweis auf das gesetzliche Gewaehrleistungsrecht (Art. 246a § 1 Abs. 1 S. 1 Nr. 11 EGBGB): amtliche Grafik in hervorgehobenem
|
||
// Kasten vor der Zusammenfassung der Kasse, sofort geladen, mit dem woertlichen Text als Alternativtext und einer Vergroesserung
|
||
function gewaehrKasten () {
|
||
var g = gewaehr()
|
||
if (!g) return null
|
||
var kasten = el('section', 'wks-gewaehr')
|
||
kasten.setAttribute('aria-label', 'Ihre gesetzlichen Gewährleistungsrechte')
|
||
kasten.appendChild(el('h4', '', 'Ihre gesetzlichen Gewährleistungsrechte'))
|
||
var bildUrl = httpsUrl(g.bild)
|
||
if (bildUrl) {
|
||
var img = el('img')
|
||
img.src = bildUrl
|
||
img.alt = g.text
|
||
img.loading = 'eager'
|
||
img.decoding = 'async'
|
||
kasten.appendChild(img)
|
||
kasten.appendChild(knopf('wks-btn wks-btn-sek', 'Vergrößern', function (e) {
|
||
dialogOeffnen('Ihre gesetzlichen Gewährleistungsrechte', [{ bild: bildUrl, text: g.text }], e.currentTarget)
|
||
}))
|
||
} else {
|
||
kasten.appendChild(absaetze(g.text, 'wks-text'))
|
||
}
|
||
return kasten
|
||
}
|
||
|
||
// ------------------------------------------------------------ Formularhelfer
|
||
|
||
function honigfeld () {
|
||
var wrap = el('div', 'wks-hp')
|
||
wrap.setAttribute('aria-hidden', 'true')
|
||
wrap.style.cssText = 'position:absolute;left:-10000px;top:auto;width:1px;height:1px;overflow:hidden'
|
||
var lab = el('label', '', 'Bitte leer lassen')
|
||
var inp = document.createElement('input')
|
||
inp.type = 'text'
|
||
inp.name = 'kontakt_url'
|
||
inp.value = S.honig
|
||
inp.setAttribute('autocomplete', 'off')
|
||
inp.setAttribute('tabindex', '-1')
|
||
inp.addEventListener('input', function () { S.honig = inp.value })
|
||
lab.appendChild(inp)
|
||
wrap.appendChild(lab)
|
||
return wrap
|
||
}
|
||
|
||
// opt: { name, label, typ (text|email|textarea), wert, pflicht, hinweis, autocomplete, maxlength, onInput }; refs sammelt { name: { input, fehler, beschreibung } }
|
||
function feld (refs, opt) {
|
||
var wrap = el('div', 'wks-feld')
|
||
var fieldId = fid('f-' + opt.name)
|
||
var lab = el('label', 'wks-label', opt.label)
|
||
lab.htmlFor = fieldId
|
||
if (opt.pflicht) {
|
||
var st = el('span', 'wks-stern', ' *')
|
||
st.setAttribute('aria-hidden', 'true')
|
||
lab.appendChild(st)
|
||
} else {
|
||
lab.appendChild(document.createTextNode(' (freiwillig)'))
|
||
}
|
||
wrap.appendChild(lab)
|
||
var beschreibung = []
|
||
if (opt.hinweis) {
|
||
var h = el('p', 'wks-hinweis', opt.hinweis)
|
||
h.id = fieldId + '-h'
|
||
beschreibung.push(h.id)
|
||
wrap.appendChild(h)
|
||
}
|
||
var inp
|
||
if (opt.typ === 'textarea') {
|
||
inp = document.createElement('textarea')
|
||
inp.rows = 4
|
||
} else {
|
||
inp = document.createElement('input')
|
||
inp.type = opt.typ || 'text'
|
||
}
|
||
inp.id = fieldId
|
||
inp.name = opt.name
|
||
inp.className = 'wks-eingabe'
|
||
inp.value = opt.wert || ''
|
||
if (opt.pflicht) inp.required = true
|
||
if (opt.autocomplete) inp.setAttribute('autocomplete', opt.autocomplete)
|
||
if (opt.maxlength) inp.maxLength = opt.maxlength
|
||
if (beschreibung.length) inp.setAttribute('aria-describedby', beschreibung.join(' '))
|
||
var fehler = el('p', 'wks-feldfehler')
|
||
fehler.id = fieldId + '-e'
|
||
fehler.hidden = true
|
||
inp.addEventListener('input', function () { opt.onInput(inp.value) })
|
||
inp.addEventListener('change', function () { opt.onInput(inp.value) })
|
||
wrap.appendChild(inp)
|
||
wrap.appendChild(fehler)
|
||
refs[opt.name] = { input: inp, fehler: fehler, beschreibung: beschreibung }
|
||
return wrap
|
||
}
|
||
|
||
// Fehlertexte an die Felder schreiben; liefert die Meldungen ohne Feld
|
||
function zeigeFeldfehler (refs, fehler) {
|
||
var uebrig = []
|
||
var erster = null
|
||
Object.keys(refs).forEach(function (k) {
|
||
var r = refs[k]
|
||
var text = fehler[k]
|
||
if (text) {
|
||
r.fehler.textContent = text
|
||
r.fehler.hidden = false
|
||
r.input.setAttribute('aria-invalid', 'true')
|
||
r.input.setAttribute('aria-describedby', r.beschreibung.concat([r.fehler.id]).join(' '))
|
||
if (!erster) erster = r.input
|
||
} else {
|
||
r.fehler.textContent = ''
|
||
r.fehler.hidden = true
|
||
r.input.removeAttribute('aria-invalid')
|
||
if (r.beschreibung.length) r.input.setAttribute('aria-describedby', r.beschreibung.join(' '))
|
||
else r.input.removeAttribute('aria-describedby')
|
||
}
|
||
})
|
||
Object.keys(fehler).forEach(function (k) { if (!refs[k]) uebrig.push(fehler[k]) })
|
||
if (erster) {
|
||
S.fokusGesetzt = true
|
||
erster.focus()
|
||
ansage('Bitte prüfen Sie Ihre Eingaben: ' + Object.keys(fehler).map(function (k) { return fehler[k] }).join(' '))
|
||
}
|
||
return uebrig
|
||
}
|
||
|
||
// Meldungen ohne eigenes Feld in den Fehlerkasten: nie verschlucken
|
||
function zeigeRest (box, texte) {
|
||
texte.forEach(function (t) { box.appendChild(el('p', 'wks-fehlertext', t)) })
|
||
if (texte.length) ansage(texte.join(' '))
|
||
}
|
||
|
||
function mengeFeld (wert, onChange, labelText, idName) {
|
||
var wrap = el('div', 'wks-zeile-menge')
|
||
var inp = document.createElement('input')
|
||
inp.type = 'number'
|
||
inp.min = '1'
|
||
inp.max = String(MAX_MENGE)
|
||
inp.step = '1'
|
||
inp.className = 'wks-eingabe wks-eingabe-menge'
|
||
inp.id = fid(idName)
|
||
inp.value = String(wert)
|
||
inp.setAttribute('inputmode', 'numeric')
|
||
var lab = el('label', '', labelText)
|
||
lab.htmlFor = inp.id
|
||
inp.addEventListener('change', function () {
|
||
var n = parseInt(inp.value, 10)
|
||
if (!(n >= 1)) n = 1
|
||
if (n > MAX_MENGE) n = MAX_MENGE
|
||
inp.value = String(n)
|
||
onChange(n)
|
||
})
|
||
wrap.appendChild(lab)
|
||
wrap.appendChild(inp)
|
||
return wrap
|
||
}
|
||
|
||
// ------------------------------------------------------------ Raster
|
||
|
||
function preisAnzeige (p) { return nichtLeer(p.preisAbText) ? p.preisAbText : p.preisText }
|
||
|
||
function bildElement (p, grosse, cls) {
|
||
var b = liste(p.bilder)[0]
|
||
var src = istObjekt(b) ? absUrl(api, b[grosse]) : ''
|
||
if (!src) return null
|
||
var img = el('img', cls)
|
||
img.src = src
|
||
img.alt = p.name
|
||
img.loading = 'lazy'
|
||
img.decoding = 'async'
|
||
return img
|
||
}
|
||
|
||
function zeichneRaster () {
|
||
var kategorien = liste(S.cfg.kategorien).filter(nichtLeer)
|
||
add(titel('Unsere Artikel'))
|
||
meldungAnzeigen()
|
||
if (S.korbHinweis) {
|
||
S.korbHinweis = false
|
||
add(el('p', 'wks-meldung', 'Einige Artikel in Ihrem Warenkorb sind nicht mehr verfügbar und wurden entfernt.'))
|
||
}
|
||
var chips = null
|
||
if (kategorien.length > 1) {
|
||
chips = el('div', 'wks-chips')
|
||
chips.setAttribute('role', 'group')
|
||
chips.setAttribute('aria-label', 'Kategorien')
|
||
add(chips)
|
||
}
|
||
var ul = el('ul', 'wks-raster')
|
||
var leer = el('p', '', 'Zurzeit sind keine Artikel verfügbar.')
|
||
function fuelle () {
|
||
ul.textContent = ''
|
||
var n = 0
|
||
liste(S.cfg.produkte).forEach(function (p) {
|
||
if (S.kategorie && p.kategorie !== S.kategorie) return
|
||
n++
|
||
ul.appendChild(karte(p))
|
||
})
|
||
ul.hidden = n === 0
|
||
leer.hidden = n !== 0
|
||
}
|
||
if (chips) {
|
||
var alle = ['']
|
||
kategorien.forEach(function (k) { alle.push(k) })
|
||
alle.forEach(function (k) {
|
||
var b = knopf('wks-chip', k || 'Alle', function () {
|
||
S.kategorie = k
|
||
knoten(chips.childNodes).forEach(function (c) { c.setAttribute('aria-pressed', String(c === b)) })
|
||
fuelle()
|
||
ansage(k ? 'Kategorie ' + k : 'Alle Artikel')
|
||
})
|
||
b.setAttribute('aria-pressed', String(S.kategorie === k))
|
||
chips.appendChild(b)
|
||
})
|
||
}
|
||
add(ul)
|
||
add(leer)
|
||
fuelle()
|
||
var hinweis = el('p', 'wks-hinweiszeile')
|
||
if (nichtLeer(shop().steuerHinweisKurz)) hinweis.appendChild(document.createTextNode(shop().steuerHinweisKurz + ' '))
|
||
hinweis.appendChild(knopf('wks-link', 'zzgl. Versand', function (e) { zeigeVersand(e.currentTarget) }))
|
||
add(hinweis)
|
||
}
|
||
|
||
function karte (p) {
|
||
var li = el('li')
|
||
var art = el('div', 'wks-karte')
|
||
var bild = bildElement(p, 'klein', 'wks-karte-bild')
|
||
art.appendChild(bild || el('span', 'wks-karte-leer'))
|
||
var h = el('h4', 'wks-karte-name')
|
||
var b = knopf('wks-karte-knopf', p.name, function () { zeigeDetail(p.id) })
|
||
h.appendChild(b)
|
||
art.appendChild(h)
|
||
if (nichtLeer(p.kurz)) art.appendChild(el('p', 'wks-karte-text', p.kurz))
|
||
art.appendChild(el('p', 'wks-preis', preisAnzeige(p)))
|
||
if (nichtLeer(p.grundpreisText)) art.appendChild(el('p', 'wks-grundpreis', p.grundpreisText))
|
||
if (p.ausverkauft === true || p.verfuegbar === false) art.appendChild(el('p', 'wks-aus', 'Ausverkauft'))
|
||
li.appendChild(art)
|
||
return li
|
||
}
|
||
|
||
// ------------------------------------------------------------ Detail
|
||
|
||
function zeigeDetail (pid) {
|
||
S.produktId = pid
|
||
S.auswahl = { v: '', n: 1 }
|
||
S.bild = 0
|
||
gehe('detail')
|
||
}
|
||
|
||
function herstellerZeilen (dl, p) {
|
||
var si = istObjekt(p.sicherheit) ? p.sicherheit : {}
|
||
var h = istObjekt(si.hersteller) ? si.hersteller : null
|
||
function adr (a) { return [a.name, a.anschrift, a.kontakt].filter(nichtLeer).join(', ') }
|
||
function zeile (l, w) { if (nichtLeer(w)) { dl.appendChild(el('dt', '', l)); dl.appendChild(el('dd', '', w)) } }
|
||
if (h && nichtLeer(adr(h))) zeile('Hersteller', adr(h))
|
||
else zeile('Verkäufer und Verantwortlicher', anbieterZeile())
|
||
var eu = istObjekt(si.euVerantwortlicher) ? adr(si.euVerantwortlicher) : ''
|
||
zeile('Verantwortliche Person in der EU', eu)
|
||
zeile('Sicherheitshinweise', si.hinweise)
|
||
}
|
||
|
||
function zeichneDetail () {
|
||
var p = produktMitId(S.produktId)
|
||
if (!p) { gehe('raster'); return }
|
||
var bilder = liste(p.bilder).filter(function (b) { return istObjekt(b) && absUrl(api, b.gross) })
|
||
var mitVar = hatVarianten(p)
|
||
var verkaeuflich = p.ausverkauft !== true && p.verfuegbar !== false
|
||
|
||
add(knopf('wks-btn wks-btn-sek', 'Zurück zur Übersicht', function () { gehe('raster') }))
|
||
add(titel(p.name))
|
||
meldungAnzeigen()
|
||
|
||
var gitter = el('div', 'wks-detail')
|
||
var links = el('div', 'wks-galerie')
|
||
if (bilder.length) {
|
||
var gross = el('img', 'wks-galerie-gross')
|
||
gross.alt = p.name
|
||
gross.decoding = 'async'
|
||
gross.src = absUrl(api, bilder[Math.min(S.bild, bilder.length - 1)].gross)
|
||
links.appendChild(gross)
|
||
if (bilder.length > 1) {
|
||
var daumen = el('div', 'wks-daumen')
|
||
bilder.forEach(function (b, i) {
|
||
var db = el('button')
|
||
db.type = 'button'
|
||
db.setAttribute('aria-label', 'Bild ' + (i + 1) + ' von ' + bilder.length + ' anzeigen')
|
||
db.setAttribute('aria-pressed', String(i === S.bild))
|
||
var di = el('img')
|
||
di.src = absUrl(api, b.klein) || absUrl(api, b.gross)
|
||
di.alt = ''
|
||
di.loading = 'lazy'
|
||
db.appendChild(di)
|
||
db.addEventListener('click', function () {
|
||
S.bild = i
|
||
gross.src = absUrl(api, b.gross)
|
||
knoten(daumen.childNodes).forEach(function (c, j) { c.setAttribute('aria-pressed', String(j === i)) })
|
||
})
|
||
daumen.appendChild(db)
|
||
})
|
||
links.appendChild(daumen)
|
||
}
|
||
}
|
||
gitter.appendChild(links)
|
||
|
||
var rechts = el('div', 'wks-info')
|
||
var preisEl = el('p', 'wks-preis')
|
||
var gpEl = el('p', 'wks-grundpreis')
|
||
var verfEl = el('p', verkaeuflich ? 'wks-klein' : 'wks-aus')
|
||
rechts.appendChild(preisEl)
|
||
rechts.appendChild(gpEl)
|
||
var hinweis = el('p', 'wks-hinweiszeile')
|
||
if (nichtLeer(shop().steuerHinweisKurz)) hinweis.appendChild(document.createTextNode(shop().steuerHinweisKurz + ' '))
|
||
hinweis.appendChild(knopf('wks-link', 'zzgl. Versand', function (e) { zeigeVersand(e.currentTarget) }))
|
||
rechts.appendChild(hinweis)
|
||
verfEl.textContent = verkaeuflich ? 'Verfügbar' : 'Ausverkauft'
|
||
rechts.appendChild(verfEl)
|
||
|
||
var form = el('form', 'wks-form')
|
||
form.noValidate = true
|
||
var varSel = null
|
||
var fehlerVar = el('p', 'wks-feldfehler')
|
||
fehlerVar.hidden = true
|
||
fehlerVar.id = fid('var-e')
|
||
function aktualisierePreis () {
|
||
var w = mitVar ? varianteMitId(p, S.auswahl.v) : null
|
||
preisEl.textContent = w && nichtLeer(w.preisText) ? w.preisText : preisAnzeige(p)
|
||
var gp = w && nichtLeer(w.grundpreisText) ? w.grundpreisText : (w ? '' : p.grundpreisText)
|
||
gpEl.textContent = nichtLeer(gp) ? gp : ''
|
||
gpEl.hidden = !nichtLeer(gp)
|
||
if (w) {
|
||
verfEl.textContent = w.verfuegbar === false ? 'Ausverkauft' : 'Verfügbar'
|
||
verfEl.className = w.verfuegbar === false ? 'wks-aus' : 'wks-klein'
|
||
}
|
||
}
|
||
if (mitVar) {
|
||
var vf = el('div', 'wks-feld')
|
||
var vl = el('label', 'wks-label', p.varianten.name)
|
||
varSel = document.createElement('select')
|
||
varSel.id = fid('variante')
|
||
varSel.className = 'wks-eingabe'
|
||
vl.htmlFor = varSel.id
|
||
var o0 = document.createElement('option')
|
||
o0.value = ''
|
||
o0.textContent = 'Bitte wählen'
|
||
varSel.appendChild(o0)
|
||
liste(p.varianten.werte).forEach(function (w) {
|
||
var o = document.createElement('option')
|
||
o.value = w.id
|
||
o.textContent = w.name + (nichtLeer(w.preisText) ? ' – ' + w.preisText : '') + (w.verfuegbar === false ? ' (ausverkauft)' : '')
|
||
if (w.verfuegbar === false) o.disabled = true
|
||
varSel.appendChild(o)
|
||
})
|
||
varSel.value = S.auswahl.v
|
||
varSel.addEventListener('change', function () {
|
||
S.auswahl.v = varSel.value
|
||
fehlerVar.hidden = true
|
||
varSel.removeAttribute('aria-invalid')
|
||
aktualisierePreis()
|
||
})
|
||
vf.appendChild(vl)
|
||
vf.appendChild(varSel)
|
||
vf.appendChild(fehlerVar)
|
||
form.appendChild(vf)
|
||
}
|
||
aktualisierePreis()
|
||
var bestaetigung = el('p', 'wks-meldung')
|
||
bestaetigung.hidden = true
|
||
if (verkaeuflich) {
|
||
var mf = el('div', 'wks-feld')
|
||
var mw = mengeFeld(S.auswahl.n, function (n) { S.auswahl.n = n }, 'Menge', 'menge')
|
||
mf.appendChild(mw)
|
||
form.appendChild(mf)
|
||
var plus = el('button', 'wks-btn', 'In den Warenkorb')
|
||
plus.type = 'submit'
|
||
form.appendChild(plus)
|
||
form.addEventListener('submit', function (e) {
|
||
e.preventDefault()
|
||
if (mitVar && !S.auswahl.v) {
|
||
fehlerVar.textContent = 'Bitte wählen Sie eine Variante (' + p.varianten.name + ').'
|
||
fehlerVar.hidden = false
|
||
varSel.setAttribute('aria-invalid', 'true')
|
||
varSel.setAttribute('aria-describedby', fehlerVar.id)
|
||
varSel.focus()
|
||
ansage(fehlerVar.textContent)
|
||
return
|
||
}
|
||
var n = parseInt(form.querySelector('input[type="number"]').value, 10)
|
||
if (!(n >= 1)) n = 1
|
||
legeInKorb(p.id, mitVar ? S.auswahl.v : '', n)
|
||
bestaetigung.textContent = p.name + ' wurde in den Warenkorb gelegt.'
|
||
bestaetigung.hidden = false
|
||
ansage(bestaetigung.textContent)
|
||
zeichneKopf()
|
||
})
|
||
}
|
||
rechts.appendChild(form)
|
||
rechts.appendChild(bestaetigung)
|
||
var weiter = el('div', 'wks-nav')
|
||
weiter.appendChild(knopf('wks-btn wks-btn-sek', 'Weiter einkaufen', function () { gehe('raster') }))
|
||
weiter.appendChild(knopf('wks-btn wks-btn-sek', 'Zum Warenkorb', function () { gehe('korb') }))
|
||
rechts.appendChild(weiter)
|
||
gitter.appendChild(rechts)
|
||
add(gitter)
|
||
|
||
if (nichtLeer(p.beschreibung)) {
|
||
add(el('h4', 'wks-untertitel', 'Beschreibung'))
|
||
add(absaetze(p.beschreibung, 'wks-beschreibung'))
|
||
}
|
||
var fakten = el('dl', 'wks-zusammen')
|
||
function fakt (l, w) { if (nichtLeer(w)) { fakten.appendChild(el('dt', '', l)); fakten.appendChild(el('dd', '', w)) } }
|
||
fakt('Lieferzeit', shop().lieferzeit)
|
||
fakt('Lieferart', p.lieferHinweis)
|
||
fakt('Widerrufsrecht', p.widerrufText)
|
||
if (fakten.childNodes.length) add(fakten)
|
||
|
||
if (istObjekt(p.lebensmittel) && nichtLeer(p.lebensmittel.pflichtangaben)) {
|
||
add(el('h4', 'wks-untertitel', 'Angaben nach Lebensmittelrecht'))
|
||
add(absaetze(p.lebensmittel.pflichtangaben, 'wks-text'))
|
||
}
|
||
add(el('h4', 'wks-untertitel', 'Produktsicherheit'))
|
||
var sicher = el('dl', 'wks-zusammen')
|
||
herstellerZeilen(sicher, p)
|
||
add(sicher)
|
||
}
|
||
|
||
function legeInKorb (pid, vid, n) {
|
||
var gefunden = null
|
||
S.korb.forEach(function (z) { if (z.id === pid && z.v === vid) gefunden = z })
|
||
if (gefunden) gefunden.n = Math.min(MAX_MENGE, gefunden.n + n)
|
||
else if (S.korb.length < MAX_POSITIONEN) S.korb.push({ id: pid, v: vid, n: Math.min(MAX_MENGE, n) })
|
||
speichereKorb()
|
||
S.pruef = null
|
||
}
|
||
|
||
// ------------------------------------------------------------ Warenkorb
|
||
|
||
function zeichneKorb () {
|
||
add(titel('Warenkorb'))
|
||
meldungAnzeigen()
|
||
if (S.korbHinweis) {
|
||
S.korbHinweis = false
|
||
add(el('p', 'wks-meldung', 'Einige Artikel in Ihrem Warenkorb sind nicht mehr verfügbar und wurden entfernt.'))
|
||
}
|
||
if (S.korb.length === 0) {
|
||
add(el('p', '', 'Ihr Warenkorb ist leer.'))
|
||
var nav0 = el('div', 'wks-nav')
|
||
nav0.appendChild(knopf('wks-btn', 'Weiter einkaufen', function () { gehe('raster') }))
|
||
add(nav0)
|
||
return
|
||
}
|
||
var ul = el('ul', 'wks-zeilen')
|
||
S.korb.forEach(function (z, i) {
|
||
var p = produktMitId(z.id)
|
||
var w = hatVarianten(p) ? varianteMitId(p, z.v) : null
|
||
var li = el('li')
|
||
var info = el('div', 'wks-zeile-info')
|
||
info.appendChild(el('p', 'wks-zeile-name', p.name))
|
||
if (w) info.appendChild(el('p', 'wks-klein', p.varianten.name + ': ' + w.name))
|
||
var einzel = w && nichtLeer(w.preisText) ? w.preisText : p.preisText
|
||
info.appendChild(el('p', 'wks-klein', 'Einzelpreis: ' + einzel))
|
||
var gp = w ? w.grundpreisText : p.grundpreisText
|
||
if (nichtLeer(gp)) info.appendChild(el('p', 'wks-grundpreis', gp))
|
||
var ausv = w ? w.verfuegbar === false : (p.ausverkauft === true || p.verfuegbar === false)
|
||
if (ausv) info.appendChild(el('p', 'wks-aus', 'Ausverkauft'))
|
||
li.appendChild(info)
|
||
li.appendChild(mengeFeld(z.n, function (n) { z.n = n; speichereKorb(); S.pruef = null; zeichneKopf() }, 'Menge', 'korb-menge-' + i))
|
||
var weg = knopf('wks-btn wks-btn-sek', 'Entfernen', function () {
|
||
S.korb.splice(i, 1)
|
||
speichereKorb()
|
||
S.pruef = null
|
||
S.meldung = p.name + ' wurde aus dem Warenkorb entfernt.'
|
||
zeichne(false)
|
||
if (ui.titel) { S.fokusGesetzt = true; ui.titel.focus() }
|
||
})
|
||
weg.setAttribute('aria-label', p.name + ' aus dem Warenkorb entfernen')
|
||
li.appendChild(weg)
|
||
ul.appendChild(li)
|
||
})
|
||
add(ul)
|
||
add(el('p', 'wks-hinweiszeile', 'Die Preise, Versandkosten und die Gesamtsumme sehen Sie im nächsten Schritt.'))
|
||
var nav = el('div', 'wks-nav')
|
||
nav.appendChild(knopf('wks-btn wks-btn-sek', 'Weiter einkaufen', function () { gehe('raster') }))
|
||
nav.appendChild(knopf('wks-btn', 'Zur Kasse', function () { gehe('kasse') }))
|
||
add(nav)
|
||
}
|
||
|
||
// ------------------------------------------------------------ Kasse
|
||
|
||
function kannVersenden () {
|
||
var s = shop()
|
||
if (liste(s.lieferlaender).length === 0) return false
|
||
return !S.korb.some(function (z) { var p = produktMitId(z.id); return p && p.lieferart === 'abholung' })
|
||
}
|
||
function kannAbholen () { return istObjekt(shop().abholung) && shop().abholung.aktiv === true }
|
||
|
||
function positionenBody () {
|
||
return S.korb.map(function (z) {
|
||
var o = { produktId: z.id, menge: z.n }
|
||
if (z.v) o.varianteId = z.v
|
||
return o
|
||
})
|
||
}
|
||
|
||
function landCode () {
|
||
var l = liste(shop().lieferlaender)
|
||
if (S.land) return S.land
|
||
return l.length && istObjekt(l[0]) ? l[0].code : ''
|
||
}
|
||
|
||
function zeichneKasse () {
|
||
if (S.korb.length === 0) { gehe('korb'); return }
|
||
var s = shop()
|
||
var versand = kannVersenden()
|
||
var abholung = kannAbholen()
|
||
if (!versand && !abholung) {
|
||
add(titel('Bestellung'))
|
||
add(el('p', 'wks-fehlertext', 'Mit diesem Warenkorb ist keine Bestellung möglich. Bitte ändern Sie Ihren Warenkorb.'))
|
||
add(knopf('wks-btn', 'Zum Warenkorb', function () { gehe('korb') }))
|
||
return
|
||
}
|
||
if (S.lieferart !== 'versand' && S.lieferart !== 'abholung') S.lieferart = versand ? 'versand' : 'abholung'
|
||
if (S.lieferart === 'versand' && !versand) S.lieferart = 'abholung'
|
||
if (S.lieferart === 'abholung' && !abholung) S.lieferart = 'versand'
|
||
if (!S.land) S.land = landCode()
|
||
|
||
add(titel('Bestellung prüfen'))
|
||
meldungAnzeigen()
|
||
var form = el('form', 'wks-form')
|
||
form.noValidate = true
|
||
|
||
var fs = el('fieldset', 'wks-feld')
|
||
fs.appendChild(el('legend', '', 'Lieferart'))
|
||
var landWrap = el('div', 'wks-feld')
|
||
var landSel = document.createElement('select')
|
||
landSel.id = fid('land')
|
||
landSel.className = 'wks-eingabe'
|
||
var landLab = el('label', 'wks-label', 'Lieferland')
|
||
landLab.htmlFor = landSel.id
|
||
liste(s.lieferlaender).forEach(function (l) {
|
||
if (!istObjekt(l)) return
|
||
var o = document.createElement('option')
|
||
o.value = l.code
|
||
o.textContent = l.name
|
||
landSel.appendChild(o)
|
||
})
|
||
landSel.value = S.land
|
||
landWrap.appendChild(landLab)
|
||
landWrap.appendChild(landSel)
|
||
function landSichtbar () { landWrap.hidden = S.lieferart !== 'versand' }
|
||
function radio (wert, text) {
|
||
var r = el('div', 'wks-radio')
|
||
var inp = document.createElement('input')
|
||
inp.type = 'radio'
|
||
inp.name = fid('lieferart')
|
||
inp.id = fid('la-' + wert)
|
||
inp.value = wert
|
||
inp.checked = S.lieferart === wert
|
||
inp.addEventListener('change', function () {
|
||
if (!inp.checked) return
|
||
S.lieferart = wert
|
||
landSichtbar()
|
||
pruefe()
|
||
})
|
||
var lab = el('label', '', text)
|
||
lab.htmlFor = inp.id
|
||
r.appendChild(inp)
|
||
r.appendChild(lab)
|
||
return r
|
||
}
|
||
if (versand) fs.appendChild(radio('versand', 'Versand'))
|
||
if (abholung) {
|
||
var abText = 'Abholung'
|
||
if (nichtLeer(s.abholung.adresse)) abText += ' (' + s.abholung.adresse + ')'
|
||
fs.appendChild(radio('abholung', abText))
|
||
if (nichtLeer(s.abholung.hinweis)) fs.appendChild(el('p', 'wks-hinweis', s.abholung.hinweis))
|
||
}
|
||
form.appendChild(fs)
|
||
form.appendChild(landWrap)
|
||
landSichtbar()
|
||
landSel.addEventListener('change', function () { S.land = landSel.value; pruefe() })
|
||
|
||
// Gewaehrleistungshinweis: hervorgehoben vor der Zusammenfassung (wird nicht mit der Zusammenfassung neu aufgebaut)
|
||
var gkasten = gewaehrKasten()
|
||
if (gkasten) form.appendChild(gkasten)
|
||
|
||
// Pflichtinformationen: stehen unmittelbar ueber dem Knopf und kommen zum grossen Teil vom Server (/pruefen)
|
||
var pflicht = el('div', 'wks-pflicht')
|
||
form.appendChild(pflicht)
|
||
|
||
form.appendChild(honigfeld())
|
||
var fehlerBox = el('div', 'wks-fehlerbox')
|
||
form.appendChild(fehlerBox)
|
||
// Einbeziehung der AGB (§ 305 Abs. 2 BGB): ausdruecklicher Hinweis unmittelbar ueber dem Knopf, beide Texte oeffnen sich im Dialog
|
||
var agbHinweis = el('p', 'wks-agb-hinweis')
|
||
agbHinweis.appendChild(document.createTextNode('Es gelten unsere '))
|
||
agbHinweis.appendChild(knopf('wks-link', 'AGB', function (e) { zeigeAgb(e.currentTarget) }))
|
||
agbHinweis.appendChild(document.createTextNode('. Informationen zu Ihrem Widerrufsrecht finden Sie in der '))
|
||
agbHinweis.appendChild(knopf('wks-link', 'Widerrufsbelehrung', function (e) { zeigeBelehrung(e.currentTarget) }))
|
||
agbHinweis.appendChild(document.createTextNode('.'))
|
||
form.appendChild(agbHinweis)
|
||
var senden = el('button', 'wks-btn', 'Zahlungspflichtig bestellen')
|
||
senden.type = 'submit'
|
||
senden.disabled = true
|
||
var nav = el('div', 'wks-nav')
|
||
nav.appendChild(knopf('wks-btn wks-btn-sek', 'Zurück zum Warenkorb', function () { gehe('korb') }))
|
||
nav.appendChild(senden)
|
||
form.appendChild(nav)
|
||
add(form)
|
||
|
||
function fuellePflicht () {
|
||
pflicht.textContent = ''
|
||
var ps = S.pruef
|
||
if (S.pruefFehler) {
|
||
pflicht.appendChild(el('p', 'wks-fehlertext', S.pruefFehler.text))
|
||
if (S.pruefFehler.zumWarenkorb) pflicht.appendChild(knopf('wks-btn wks-btn-sek', 'Warenkorb ändern', function () { gehe('korb') }))
|
||
if (S.pruefFehler.erneut) pflicht.appendChild(knopf('wks-btn wks-btn-sek', 'Erneut versuchen', function () { pruefe() }))
|
||
senden.disabled = true
|
||
return
|
||
}
|
||
if (!ps) {
|
||
pflicht.appendChild(el('p', 'wks-laden', 'Wird berechnet …'))
|
||
senden.disabled = true
|
||
return
|
||
}
|
||
pflicht.appendChild(el('h4', '', 'Ihre Bestellung'))
|
||
var ul = el('ul', 'wks-pos')
|
||
liste(ps.positionen).forEach(function (z) {
|
||
var li = el('li')
|
||
var t = el('span', 'wks-pos-text', z.menge + ' × ' + z.name + (nichtLeer(z.varianteText) ? ' (' + z.varianteText + ')' : '') + ', je ' + z.einzelText)
|
||
li.appendChild(t)
|
||
li.appendChild(el('span', '', z.summeText))
|
||
ul.appendChild(li)
|
||
})
|
||
pflicht.appendChild(ul)
|
||
var t = ps.texte
|
||
var dl = el('dl', 'wks-summen')
|
||
dl.appendChild(el('dt', '', 'Zwischensumme'))
|
||
dl.appendChild(el('dd', '', t.zwischensumme))
|
||
dl.appendChild(el('dt', '', S.lieferart === 'abholung' ? 'Lieferart' : 'Versandkosten'))
|
||
dl.appendChild(el('dd', '', t.versand))
|
||
dl.appendChild(el('dt', 'wks-gesamt', 'Gesamtsumme'))
|
||
dl.appendChild(el('dd', 'wks-gesamt', t.gesamt))
|
||
pflicht.appendChild(dl)
|
||
if (nichtLeer(t.steuerHinweis)) pflicht.appendChild(el('p', 'wks-klein', t.steuerHinweis))
|
||
else if (nichtLeer(s.steuerHinweisKurz)) pflicht.appendChild(el('p', 'wks-klein', s.steuerHinweisKurz))
|
||
if (nichtLeer(s.mindestbestellText)) pflicht.appendChild(el('p', 'wks-klein', s.mindestbestellText))
|
||
|
||
pflicht.appendChild(el('h4', '', 'Lieferung und Zahlung'))
|
||
var lz = el('dl', 'wks-zusammen')
|
||
function zeile (l, w) { if (nichtLeer(w)) { lz.appendChild(el('dt', '', l)); lz.appendChild(el('dd', '', w)) } }
|
||
zeile('Lieferart', S.lieferart === 'abholung' ? 'Abholung' : 'Versand' + (S.land ? ' nach ' + landName(S.land) : ''))
|
||
zeile('Lieferzeit', s.lieferzeit)
|
||
zeile('Zahlungsarten', s.zahlungsarten)
|
||
zeile('Anbieter', anbieterZeile())
|
||
pflicht.appendChild(lz)
|
||
|
||
pflicht.appendChild(el('h4', '', 'Widerrufsrecht'))
|
||
pflicht.appendChild(el('p', '', 'Sie haben ein gesetzliches Widerrufsrecht, soweit unten für einzelne Artikel nichts anderes steht. Einzelheiten finden Sie in der Widerrufsbelehrung.'))
|
||
liste(ps.positionen).forEach(function (z) {
|
||
var txt = nichtLeer(z.widerrufText) ? z.widerrufText : ((produktMitId(z.produktId) || {}).widerrufText)
|
||
if (nichtLeer(txt)) pflicht.appendChild(el('p', '', z.name + ': ' + txt))
|
||
})
|
||
var links = el('p', 'wks-links')
|
||
links.appendChild(knopf('wks-link', 'AGB lesen', function (e) { zeigeAgb(e.currentTarget) }))
|
||
links.appendChild(knopf('wks-link', 'Widerrufsbelehrung lesen', function (e) { zeigeBelehrung(e.currentTarget) }))
|
||
links.appendChild(knopf('wks-link', 'Versand & Zahlung lesen', function (e) { zeigeVersand(e.currentTarget) }))
|
||
pflicht.appendChild(links)
|
||
|
||
pflicht.appendChild(el('p', '', 'Name, Lieferadresse und Zahlung geben Sie im nächsten Schritt bei unserem Zahlungsdienstleister Stripe ein.'))
|
||
senden.disabled = S.sendet
|
||
}
|
||
|
||
function landName (code) {
|
||
var l = liste(shop().lieferlaender)
|
||
for (var i = 0; i < l.length; i++) if (istObjekt(l[i]) && l[i].code === code) return l[i].name
|
||
return code
|
||
}
|
||
|
||
// Zusammenfassung vom Server (nie im Browser gerechnet); bei jeder Aenderung neu, veraltete Antworten werden verworfen
|
||
function pruefe () {
|
||
var seq = ++S.pruefSeq
|
||
var ansichtSeq = S.seq
|
||
S.pruef = null
|
||
S.pruefFehler = null
|
||
fuellePflicht()
|
||
request('POST', '/pruefen', { positionen: positionenBody(), lieferart: S.lieferart, land: landCode() }).then(function (d) {
|
||
if (seq !== S.pruefSeq || ansichtSeq !== S.seq) return
|
||
if (!d || d.ok !== true || !istObjekt(d.texte) || !Array.isArray(d.positionen)) throw { netz: true }
|
||
S.pruef = d
|
||
fuellePflicht()
|
||
ansage('Die Zusammenfassung wurde aktualisiert. Gesamtsumme ' + d.texte.gesamt + '.')
|
||
}).catch(function (err) {
|
||
if (seq !== S.pruefSeq || ansichtSeq !== S.seq) return
|
||
if (err && err.inaktiv) { zeigeAusweich(); return }
|
||
if (err && err.status === 429) S.pruefFehler = { text: meldung429(err), erneut: true }
|
||
else if (err && !err.netz && err.fehler && Object.keys(err.fehler).length) {
|
||
S.pruefFehler = { text: Object.keys(err.fehler).map(function (k) { return err.fehler[k] }).join(' '), zumWarenkorb: true }
|
||
} else S.pruefFehler = { text: 'Gerade nicht erreichbar. Bitte versuchen Sie es später noch einmal.', erneut: true }
|
||
fuellePflicht()
|
||
ansage(S.pruefFehler.text)
|
||
})
|
||
}
|
||
S.pruefSeq++
|
||
pruefe()
|
||
|
||
form.addEventListener('submit', function (e) {
|
||
e.preventDefault()
|
||
if (S.sendet || !S.pruef || S.pruefFehler) return
|
||
fehlerBox.textContent = ''
|
||
bestelle(senden, fehlerBox)
|
||
})
|
||
}
|
||
|
||
// Verbindlich bestellen: der Server rechnet alles neu und legt die Bestellung an; die Antwort fuehrt zur Zahlung bei Stripe
|
||
function bestelle (senden, fehlerBox) {
|
||
S.sendet = true
|
||
senden.disabled = true
|
||
senden.setAttribute('aria-busy', 'true')
|
||
sichereConfig().then(function (neu) {
|
||
if (neu) {
|
||
S.sendet = false
|
||
gehe('kasse', { meldung: 'Der Shop wurde aktualisiert. Bitte prüfen Sie Ihre Bestellung noch einmal.' })
|
||
return null
|
||
}
|
||
return warteAufFormToken(senden).then(function (los) {
|
||
if (!los) return null
|
||
return request('POST', '/bestellung', {
|
||
positionen: positionenBody(),
|
||
lieferart: S.lieferart,
|
||
land: landCode(),
|
||
rueckUrl: location.href,
|
||
formToken: S.cfg.formToken,
|
||
kontakt_url: S.honig
|
||
}).then(function (d) {
|
||
var ziel = d ? httpsUrl(d.url) : ''
|
||
if (!ziel) throw { netz: true }
|
||
senden.textContent = 'Weiterleitung zu Stripe …'
|
||
ansage('Sie werden zur Zahlung bei Stripe weitergeleitet.')
|
||
location.href = ziel // S.sendet bleibt gesetzt: kein zweiter Klick waehrend der Weiterleitung
|
||
})
|
||
})
|
||
}).catch(function (err) {
|
||
S.sendet = false
|
||
senden.removeAttribute('aria-busy')
|
||
senden.disabled = false
|
||
if (err && err.inaktiv) return
|
||
nachSendeFehler(err, senden, fehlerBox)
|
||
})
|
||
}
|
||
|
||
function nachSendeFehler (err, senden, fehlerBox) {
|
||
if (err && err.code === 'neu_laden') {
|
||
// Formular-Token abgelaufen oder ungueltig: Konfiguration neu holen, dem Besucher Bescheid geben
|
||
lade(true).then(function () {
|
||
bereinigeKorb()
|
||
gehe('kasse', { fokus: false, meldung: 'Das Formular wurde erneuert. Bitte versuchen Sie es noch einmal.' })
|
||
}, function (e2) { if (e2 && e2.inaktiv) zeigeAusweich(); else ausfall(function () { zeichne(true) }) })
|
||
return
|
||
}
|
||
if (err && err.status === 429) { err.fehler = { _: meldung429(err) } }
|
||
if (err && !err.netz && err.fehler && Object.keys(err.fehler).length) {
|
||
fehlerBox.textContent = ''
|
||
zeigeRest(fehlerBox, Object.keys(err.fehler).map(function (k) { return err.fehler[k] }))
|
||
return
|
||
}
|
||
ausfall(function () { zeichne(true) })
|
||
}
|
||
|
||
// ------------------------------------------------------------ Rueckkehr von Stripe
|
||
|
||
function zeigeRueckkehr (r) {
|
||
if (r.art === 'abbruch') {
|
||
S.ansicht = bestellbar() ? 'korb' : startAnsicht()
|
||
S.meldung = 'Die Zahlung wurde abgebrochen. Ihre Bestellung wurde nicht abgeschlossen.' + (S.korb.length ? ' Ihr Warenkorb ist noch gefüllt.' : '')
|
||
zeichne(true)
|
||
return
|
||
}
|
||
S.ansicht = 'status'
|
||
S.status = { id: r.id, t: r.t, daten: null, fehler: false, versuch: 0 }
|
||
zeichne(true)
|
||
holeStatus(S.statusSeq)
|
||
}
|
||
|
||
function holeStatus (seq) {
|
||
var st = S.status
|
||
st.versuch++
|
||
request('GET', '/bestellung/' + encodeURIComponent(st.id) + '?t=' + encodeURIComponent(st.t)).then(function (d) {
|
||
if (seq !== S.statusSeq) return
|
||
if (!d || !istText(d.status)) throw { netz: true }
|
||
st.daten = d
|
||
st.fehler = false
|
||
if (d.status !== 'offen' && d.status !== 'storniert') {
|
||
S.korb = []
|
||
speichereKorb()
|
||
zeichneKopf()
|
||
}
|
||
fuelleStatus()
|
||
if (d.status === 'offen' && st.versuch < STATUS_VERSUCHE) {
|
||
setTimeout(function () { if (seq === S.statusSeq) holeStatus(seq) }, STATUS_ABSTAND_MS)
|
||
}
|
||
}).catch(function (err) {
|
||
if (seq !== S.statusSeq) return
|
||
if (err && err.inaktiv) return
|
||
if (!st.daten) st.fehler = true
|
||
fuelleStatus()
|
||
})
|
||
}
|
||
|
||
function zeichneStatus () {
|
||
add(titel('Ihre Bestellung'))
|
||
var box = el('div', 'wks-statusbox')
|
||
box.id = fid('status')
|
||
add(box)
|
||
ui.statusBox = box
|
||
add(knopf('wks-btn wks-btn-sek', 'Zurück zum Shop', function () { gehe(startAnsicht()) }))
|
||
fuelleStatus()
|
||
}
|
||
|
||
function fuelleStatus () {
|
||
var box = ui.statusBox
|
||
var st = S.status
|
||
if (!box || !st || S.ansicht !== 'status') return
|
||
box.textContent = ''
|
||
var d = st.daten
|
||
var text = ''
|
||
if (!d) {
|
||
if (st.fehler) {
|
||
box.appendChild(el('p', 'wks-fehlertext', 'Wir können den Stand Ihrer Bestellung gerade nicht abrufen. Wenn Sie bezahlt haben, erhalten Sie eine Bestätigung per E-Mail.'))
|
||
text = box.textContent
|
||
} else {
|
||
box.appendChild(el('p', 'wks-laden', 'Ihre Bestellung wird geprüft …'))
|
||
text = 'Ihre Bestellung wird geprüft.'
|
||
}
|
||
ansage(text)
|
||
return
|
||
}
|
||
var nr = nichtLeer(d.bestellnummer) ? d.bestellnummer : ''
|
||
if (d.status === 'storniert') {
|
||
box.appendChild(el('p', 'wks-ergebnis-text', 'Ihre Bestellung' + (nr ? ' ' + nr : '') + ' wurde nicht abgeschlossen. Die Zahlung ist nicht erfolgt oder wurde abgebrochen.'))
|
||
box.appendChild(el('p', '', 'Wenn Sie den Artikel noch möchten, legen Sie ihn bitte erneut in den Warenkorb.'))
|
||
} else {
|
||
box.appendChild(el('p', 'wks-ergebnis-text', 'Vielen Dank für Ihre Bestellung' + (nr ? ' ' + nr : '') + '!'))
|
||
if (d.status === 'offen') {
|
||
box.appendChild(el('p', '', 'Die Zahlung ist noch nicht bestätigt. Bei einer SEPA-Lastschrift kann das einige Tage dauern. Sobald sie bestätigt ist, erhalten Sie eine E-Mail.'))
|
||
} else {
|
||
box.appendChild(el('p', '', 'Ihre Zahlung ist eingegangen.' + (nichtLeer(d.email) ? ' Eine Bestätigung senden wir an ' + d.email + '.' : ' Eine Bestätigung senden wir Ihnen per E-Mail.')))
|
||
}
|
||
var dl = el('dl', 'wks-zusammen')
|
||
if (nr) { dl.appendChild(el('dt', '', 'Bestellnummer')); dl.appendChild(el('dd', '', nr)) }
|
||
if (nichtLeer(d.gesamtText)) { dl.appendChild(el('dt', '', 'Gesamtsumme')); dl.appendChild(el('dd', '', d.gesamtText)) }
|
||
if (d.lieferart === 'versand' || d.lieferart === 'abholung') { dl.appendChild(el('dt', '', 'Lieferart')); dl.appendChild(el('dd', '', d.lieferart === 'abholung' ? 'Abholung' : 'Versand')) }
|
||
if (dl.childNodes.length) box.appendChild(dl)
|
||
}
|
||
ansage(box.textContent)
|
||
}
|
||
|
||
// ------------------------------------------------------------ Geschlossen
|
||
|
||
function zeichneGeschlossen () {
|
||
add(titel('Shop geschlossen'))
|
||
add(el('p', 'wks-ergebnis-text', 'Der Shop ist gerade geschlossen.'))
|
||
}
|
||
|
||
// ------------------------------------------------------------ Widerruf (Widerrufsfunktion, § 356a BGB)
|
||
|
||
function zeigeWiderruf () {
|
||
S.wdSchritt = 'form'
|
||
S.wdEingang = ''
|
||
gehe('widerruf')
|
||
}
|
||
|
||
function eingangText (e) {
|
||
if (istText(e)) return e
|
||
if (istObjekt(e)) return [e.datum, nichtLeer(e.uhrzeit) ? e.uhrzeit + ' Uhr' : ''].filter(nichtLeer).join(', ')
|
||
return ''
|
||
}
|
||
|
||
function zeichneWiderruf () {
|
||
add(titel('Vertrag widerrufen'))
|
||
var w = S.wd
|
||
if (S.wdSchritt === 'fertig') {
|
||
var e = eingangText(S.wdEingang)
|
||
add(el('p', 'wks-ergebnis-text', 'Ihr Widerruf ist am ' + (e || 'heute') + ' bei uns eingegangen. Eine Eingangsbestätigung haben wir Ihnen per E-Mail geschickt.'))
|
||
ansage('Ihr Widerruf ist bei uns eingegangen.')
|
||
if (!nurWiderruf()) {
|
||
var nav0 = el('div', 'wks-nav')
|
||
nav0.appendChild(knopf('wks-btn wks-btn-sek', 'Zurück zum Shop', zurueckVomWiderruf))
|
||
add(nav0)
|
||
}
|
||
return
|
||
}
|
||
if (S.wdSchritt === 'pruefen') {
|
||
add(el('p', '', 'Bitte prüfen Sie Ihre Angaben. Mit „Widerruf bestätigen“ erklären Sie den Widerruf Ihres Vertrags.'))
|
||
var dl = el('dl', 'wks-zusammen')
|
||
function zeile (l, v) { if (nichtLeer(v)) { dl.appendChild(el('dt', '', l)); dl.appendChild(el('dd', '', v)) } }
|
||
zeile('Name', w.name.trim())
|
||
zeile('E-Mail-Adresse', w.email.trim())
|
||
zeile('Bestellnummer', w.bestellnummer.trim())
|
||
zeile('Erklärung', w.nachricht.trim())
|
||
add(dl)
|
||
var fb = el('div', 'wks-fehlerbox')
|
||
add(fb)
|
||
var senden = el('button', 'wks-btn', 'Widerruf bestätigen')
|
||
senden.type = 'button'
|
||
var nav1 = el('div', 'wks-nav')
|
||
nav1.appendChild(knopf('wks-btn wks-btn-sek', 'Angaben ändern', function () { S.wdSchritt = 'form'; zeichne(true) }))
|
||
nav1.appendChild(senden)
|
||
add(nav1)
|
||
senden.addEventListener('click', function () { sendeWiderruf(senden, fb) })
|
||
return
|
||
}
|
||
add(el('p', '', 'Hier können Sie Ihren Vertrag widerrufen. Bitte geben Sie Ihren Namen, die E-Mail-Adresse Ihrer Bestellung und möglichst die Bestellnummer an. Sie steht in Ihrer Bestätigungs-E-Mail.'))
|
||
meldungAnzeigen()
|
||
var form = el('form', 'wks-form')
|
||
form.noValidate = true
|
||
var refs = {}
|
||
form.appendChild(feld(refs, { name: 'name', label: 'Ihr Name', typ: 'text', wert: w.name, pflicht: true, autocomplete: 'name', maxlength: 100, onInput: function (v) { w.name = v } }))
|
||
form.appendChild(feld(refs, { name: 'email', label: 'E-Mail-Adresse der Bestellung', typ: 'email', wert: w.email, pflicht: true, autocomplete: 'email', maxlength: 254, onInput: function (v) { w.email = v } }))
|
||
form.appendChild(feld(refs, { name: 'bestellnummer', label: 'Bestellnummer (falls zur Hand)', typ: 'text', wert: w.bestellnummer, pflicht: false, maxlength: 40, hinweis: 'Ohne Bestellnummer: Nennen Sie bitte Bestelldatum und Artikel im Feld „Erklärung“, damit wir Ihren Vertrag zuordnen können.', onInput: function (v) { w.bestellnummer = v } }))
|
||
form.appendChild(feld(refs, { name: 'nachricht', label: 'Erklärung', typ: 'textarea', wert: w.nachricht, pflicht: false, maxlength: 1000, hinweis: 'Zum Beispiel, welche Artikel Sie widerrufen. Ohne Angabe widerrufen Sie die gesamte Bestellung.', onInput: function (v) { w.nachricht = v } }))
|
||
form.appendChild(honigfeld())
|
||
var fehlerBox = el('div', 'wks-fehlerbox')
|
||
form.appendChild(fehlerBox)
|
||
var weiter = el('button', 'wks-btn', 'Weiter')
|
||
weiter.type = 'submit'
|
||
var nav = el('div', 'wks-nav')
|
||
if (!nurWiderruf()) nav.appendChild(knopf('wks-btn wks-btn-sek', 'Zurück zum Shop', zurueckVomWiderruf))
|
||
nav.appendChild(weiter)
|
||
form.appendChild(nav)
|
||
add(form)
|
||
if (Object.keys(S.fehler).length) zeigeRest(fehlerBox, zeigeFeldfehler(refs, S.fehler))
|
||
form.addEventListener('submit', function (e) {
|
||
e.preventDefault()
|
||
var f = {}
|
||
if (!w.name.trim()) f.name = 'Bitte geben Sie Ihren Namen an.'
|
||
if (!EMAIL_FORM.test(w.email.trim())) f.email = 'Bitte geben Sie eine gültige E-Mail-Adresse an.'
|
||
if (!w.bestellnummer.trim() && !w.nachricht.trim()) f.nachricht = 'Bitte nennen Sie Bestelldatum und Artikel, damit wir Ihren Vertrag zuordnen können.'
|
||
fehlerBox.textContent = ''
|
||
zeigeFeldfehler(refs, f)
|
||
if (Object.keys(f).length) return
|
||
S.fehler = {}
|
||
S.wdSchritt = 'pruefen'
|
||
zeichne(true)
|
||
})
|
||
}
|
||
|
||
function zurueckVomWiderruf () {
|
||
S.wdSchritt = 'form'
|
||
var z = S.vorWiderruf
|
||
gehe(z && z !== 'widerruf' && z !== 'status' && z !== 'kasse' ? z : startAnsicht())
|
||
}
|
||
|
||
function sendeWiderruf (senden, fehlerBox) {
|
||
if (S.sendet) return
|
||
var w = S.wd
|
||
S.sendet = true
|
||
senden.disabled = true
|
||
senden.setAttribute('aria-busy', 'true')
|
||
sichereConfig().then(function () {
|
||
return warteAufFormToken(senden)
|
||
}).then(function (los) {
|
||
if (!los) return null
|
||
return request('POST', '/widerruf', {
|
||
name: w.name.trim(), email: w.email.trim(), bestellnummer: w.bestellnummer.trim(), nachricht: w.nachricht.trim(),
|
||
formToken: S.cfg.formToken, kontakt_url: S.honig
|
||
}).then(function (d) {
|
||
S.sendet = false
|
||
if (d && d.aktiv === false) { zeigeAusweich(); return }
|
||
if (!d || d.ok !== true) throw { netz: true }
|
||
S.wdEingang = d.eingang
|
||
S.wdSchritt = 'fertig'
|
||
S.wd = { name: '', email: '', bestellnummer: '', nachricht: '' }
|
||
zeichne(true)
|
||
})
|
||
}).catch(function (err) {
|
||
S.sendet = false
|
||
senden.removeAttribute('aria-busy')
|
||
senden.disabled = false
|
||
if (err && err.inaktiv) return
|
||
if (err && err.code === 'neu_laden') {
|
||
lade(true).then(function () { S.wdSchritt = 'form'; gehe('widerruf', { fokus: false, meldung: 'Das Formular wurde erneuert. Bitte senden Sie es noch einmal ab.' }) }, function () { ausfall(function () { zeichne(true) }) })
|
||
return
|
||
}
|
||
if (err && err.status === 429) err.fehler = { _: meldung429(err) }
|
||
if (err && !err.netz && err.fehler && Object.keys(err.fehler).length) {
|
||
var felder = ['name', 'email', 'bestellnummer', 'nachricht']
|
||
if (Object.keys(err.fehler).some(function (k) { return felder.indexOf(k) >= 0 })) {
|
||
S.wdSchritt = 'form'
|
||
gehe('widerruf', { fehler: err.fehler })
|
||
return
|
||
}
|
||
fehlerBox.textContent = ''
|
||
zeigeRest(fehlerBox, Object.keys(err.fehler).map(function (k) { return err.fehler[k] }))
|
||
return
|
||
}
|
||
ausfall(function () { zeichne(true) })
|
||
})
|
||
}
|
||
|
||
// ------------------------------------------------------------ Start
|
||
|
||
function starte () {
|
||
var cfg = S.cfg
|
||
var at = S.cfgAt
|
||
S = neuerZustand()
|
||
S.cfg = cfg
|
||
S.cfgAt = at
|
||
setzeAkzent(ui.box, shop().akzent)
|
||
if (!nurWiderruf()) {
|
||
S.korb = korbLesen(key)
|
||
bereinigeKorb()
|
||
}
|
||
var r = rueckkehr
|
||
if (r) {
|
||
rueckkehr = null
|
||
bereinigeUrl(['wk_bestellung', 'wk_t', 'wk_abbruch'])
|
||
if (!nurWiderruf()) { zeigeRueckkehr(r); return }
|
||
}
|
||
var wunsch = root.getAttribute('data-ansicht') || queryAnsicht
|
||
queryAnsicht = ''
|
||
if (wunsch === 'widerruf') { gehe('widerruf', { fokus: false }); return }
|
||
gehe(startAnsicht(), { fokus: false })
|
||
if (wunsch === 'agb' && rechtstexte() && nichtLeer(rechtstexte().agb)) zeigeAgb(null)
|
||
}
|
||
|
||
// Zurueck per Browser-Verlauf von Stripe (Seite aus dem Zwischenspeicher): der Knopf "Zahlungspflichtig bestellen" muss wieder gehen
|
||
window.addEventListener('pageshow', function (e) {
|
||
if (e.persisted && S && S.sendet && S.cfg && !S.tot) {
|
||
S.sendet = false
|
||
zeichne(false)
|
||
}
|
||
})
|
||
|
||
// Der Ausweichtext der Seite wird beiseite gelegt, sobald der Shop aktiv ist; scheitert der Aufbau, steht er wieder da
|
||
lade().then(function () {
|
||
injectStyle()
|
||
ausweich = document.createDocumentFragment()
|
||
while (root.firstChild) ausweich.appendChild(root.firstChild)
|
||
try {
|
||
bauGeruest(root)
|
||
starte()
|
||
} catch (e) {
|
||
zeigeAusweich()
|
||
}
|
||
}, function () { /* nicht erreichbar oder nicht aktiv: der Ausweichtext der Seite bleibt unberuehrt stehen */ })
|
||
}
|
||
|
||
function run () {
|
||
var b = queryWert('wk_bestellung')
|
||
var t = queryWert('wk_t')
|
||
if (ID_FORM.test(b) && TOKEN_FORM.test(t)) rueckkehr = { art: 'status', id: b, t: t }
|
||
else if (queryWert('wk_abbruch')) rueckkehr = { art: 'abbruch' }
|
||
var a = queryWert('ansicht')
|
||
if (a === 'widerruf' || a === 'agb') queryAnsicht = a
|
||
var nodes = document.querySelectorAll('[data-webklar-shop]')
|
||
for (var i = 0; i < nodes.length; i++) init(nodes[i])
|
||
}
|
||
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', run)
|
||
else run()
|
||
})()
|