DTS-Timber_FC.png

DTS TIMBER PLUS
DORPEL
CONFIGURATOR

kg

CO2 opslag 🌿

Dividers Example

Basisgegevens

Dividers Example

Basismaten dorpel

Dividers Example

Basismaten neuten


Kalksponningen


Neutbreedtes


Neutbreedtes


Neutbreedtes


Neutbreedtes


Neutbreedtes


Voorsponningen

Dropdown Example

Boorgatposities - X


Voorsponningen

Dropdown Example

Boorgatposities - X


Voorsponningen

Dropdown Example

Boorgatposities - X


Voorsponningen

Dropdown Example

Boorgatposities - X


Voorsponningen

Dropdown Example

Boorgatposities - X

Dividers Example

Dagmaten

Dividers Example

Vakvullingen

Dividers Example

Vakvullingen

Dividers Example

Vakvullingen

Dividers Example

Vakvullingen

Dividers Example

Vakvullingen

Dividers Example

Vakvullingen

Dividers Example

Vakvullingen

Dividers Example

Vakvullingen

Dividers Example

Vakvullingen

Dividers Example

Vakvullingen

Dividers Example

Kadersponning


Wisselsponning


Draaikiepsponning


Draaikiep nokafmeting

Dividers Example

Sluitpot


Toevoegen aan Mijn Bibliotheek

⚠️ Is de benaming niet uniek, dan wordt de oude overschreven

17 67x114 47 46.6 4.4 16 12 61 67 69x90 Buitenwerkse kozijnmaat 6 17 90 67 114 32 25 57 17 69x90 12.5 29 55 6 56 Binnen Buiten 67x114 1 2 LET OP !! dichting rondom tpv. binnenblad 17x17 25 32 114 57 17 12.5 29 55 6 67x114 69x90 90x114 69x90 69x90 67 90 57 90 17 32 Buitenwerkse koijnmaat 3 4 detail architect, xx 4 1 2 3 Detaillering draaikiepraam - Houten kozijn: 2x draaikiepraam productdetaillering
//// DELAY CHECKER //// (function () { if (window.__scriptProfilerAuto) return; const AUTO_STOP_MS = 2200; // tijd zonder nieuwe activiteit = meting klaar const MAX_SESSION_MS = 8000; // harde limiet const TOP_N = 15; const state = { activeSession: null, sessions: [], byScript: new Map(), callStack: [], originals: {}, observerMeta: new WeakMap(), timerMeta: new Map(), listenerMap: new WeakMap(), pendingAutoStop: null, pendingHardStop: null, monitorInstalled: false }; function now() { return performance.now(); } function extractScriptNameFromText(text) { if (!text) return "ONBEKEND SCRIPT"; const m = text.match(/\/\/\/\/\s*([^\n\r]+?)\s*\/\/\/\//); return m ? m[1].trim() : "ONBEKEND SCRIPT"; } function currentScriptName() { const cs = document.currentScript; if (!cs) return state.callStack[state.callStack.length - 1] || "ONBEKEND SCRIPT"; return extractScriptNameFromText(cs.textContent || ""); } function ensureBucket(scriptName) { if (!state.byScript.has(scriptName)) { state.byScript.set(scriptName, { script: scriptName, totalMs: 0, events: 0, observers: 0, intervals: 0, timeouts: 0, rafs: 0, querySelectorAll: 0, querySelector: 0, setAttribute: 0, classListOps: 0, styleOps: 0, appendOps: 0, removeOps: 0 }); } return state.byScript.get(scriptName); } function markActivity() { if (!state.activeSession) return; if (state.pendingAutoStop) clearTimeout(state.pendingAutoStop); state.pendingAutoStop = setTimeout(() => { stopSession("idle"); }, AUTO_STOP_MS); } function addTime(scriptName, ms, kind) { if (!state.activeSession) return; const bucket = ensureBucket(scriptName); bucket.totalMs += ms; if (kind === "event") bucket.events += 1; if (kind === "observer") bucket.observers += 1; if (kind === "interval") bucket.intervals += 1; if (kind === "timeout") bucket.timeouts += 1; if (kind === "raf") bucket.rafs += 1; markActivity(); } function withScriptContext(scriptName, kind, fn, args, thisArg) { const start = now(); state.callStack.push(scriptName); try { return fn.apply(thisArg, args || []); } finally { state.callStack.pop(); addTime(scriptName, now() - start, kind); } } function wrapListener(listener, scriptName, eventType) { if (!listener) return listener; if (typeof listener === "function") { const wrapped = function () { return withScriptContext(scriptName, "event", listener, arguments, this); }; state.listenerMap.set(listener, wrapped); return wrapped; } if (typeof listener.handleEvent === "function") { const wrappedObj = { handleEvent: function () { return withScriptContext(scriptName, "event", listener.handleEvent, arguments, listener); } }; state.listenerMap.set(listener, wrappedObj); return wrappedObj; } return listener; } function patchAddEventListener() { const proto = EventTarget.prototype; state.originals.addEventListener = proto.addEventListener; state.originals.removeEventListener = proto.removeEventListener; proto.addEventListener = function (type, listener, options) { const scriptName = currentScriptName(); const wrapped = wrapListener(listener, scriptName, type); return state.originals.addEventListener.call(this, type, wrapped, options); }; proto.removeEventListener = function (type, listener, options) { const wrapped = state.listenerMap.get(listener) || listener; return state.originals.removeEventListener.call(this, type, wrapped, options); }; } function patchTimers() { state.originals.setTimeout = window.setTimeout; state.originals.setInterval = window.setInterval; state.originals.requestAnimationFrame = window.requestAnimationFrame; window.setTimeout = function (fn, delay) { const scriptName = currentScriptName(); const wrapped = typeof fn === "function" ? function () { return withScriptContext(scriptName, "timeout", fn, arguments, this); } : fn; return state.originals.setTimeout.call(window, wrapped, delay); }; window.setInterval = function (fn, delay) { const scriptName = currentScriptName(); const wrapped = typeof fn === "function" ? function () { return withScriptContext(scriptName, "interval", fn, arguments, this); } : fn; return state.originals.setInterval.call(window, wrapped, delay); }; window.requestAnimationFrame = function (fn) { const scriptName = currentScriptName(); const wrapped = function () { return withScriptContext(scriptName, "raf", fn, arguments, this); }; return state.originals.requestAnimationFrame.call(window, wrapped); }; } function patchMutationObserver() { state.originals.MutationObserver = window.MutationObserver; window.MutationObserver = function (callback) { const scriptName = currentScriptName(); const wrapped = function () { return withScriptContext(scriptName, "observer", callback, arguments, this); }; return new state.originals.MutationObserver(wrapped); }; } function patchDomHeavyCalls() { const qsaE = Element.prototype.querySelectorAll; const qsE = Element.prototype.querySelector; const qsaD = Document.prototype.querySelectorAll; const qsD = Document.prototype.querySelector; const setAttr = Element.prototype.setAttribute; const appendChild = Node.prototype.appendChild; const insertBefore = Node.prototype.insertBefore; const removeChild = Node.prototype.removeChild; state.originals.elementQuerySelectorAll = qsaE; state.originals.elementQuerySelector = qsE; state.originals.documentQuerySelectorAll = qsaD; state.originals.documentQuerySelector = qsD; state.originals.setAttribute = setAttr; state.originals.appendChild = appendChild; state.originals.insertBefore = insertBefore; state.originals.removeChild = removeChild; function mark(op) { if (!state.activeSession) return; const scriptName = state.callStack[state.callStack.length - 1]; if (!scriptName) return; const bucket = ensureBucket(scriptName); bucket[op] = (bucket[op] || 0) + 1; markActivity(); } Element.prototype.querySelectorAll = function (sel) { mark("querySelectorAll"); return qsaE.call(this, sel); }; Document.prototype.querySelectorAll = function (sel) { mark("querySelectorAll"); return qsaD.call(this, sel); }; Element.prototype.querySelector = function (sel) { mark("querySelector"); return qsE.call(this, sel); }; Document.prototype.querySelector = function (sel) { mark("querySelector"); return qsD.call(this, sel); }; Element.prototype.setAttribute = function (name, value) { if ( name === "display" || name === "transform" || name === "d" || name === "points" || name === "x" || name === "width" || name === "height" ) { mark("setAttribute"); } return setAttr.call(this, name, value); }; Node.prototype.appendChild = function (node) { mark("appendOps"); return appendChild.call(this, node); }; Node.prototype.insertBefore = function (node, ref) { mark("appendOps"); return insertBefore.call(this, node, ref); }; Node.prototype.removeChild = function (node) { mark("removeOps"); return removeChild.call(this, node); }; const classAdd = DOMTokenList.prototype.add; const classRemove = DOMTokenList.prototype.remove; const classToggle = DOMTokenList.prototype.toggle; state.originals.classAdd = classAdd; state.originals.classRemove = classRemove; state.originals.classToggle = classToggle; DOMTokenList.prototype.add = function () { if (state.activeSession) { const scriptName = state.callStack[state.callStack.length - 1]; if (scriptName) ensureBucket(scriptName).classListOps += 1; markActivity(); } return classAdd.apply(this, arguments); }; DOMTokenList.prototype.remove = function () { if (state.activeSession) { const scriptName = state.callStack[state.callStack.length - 1]; if (scriptName) ensureBucket(scriptName).classListOps += 1; markActivity(); } return classRemove.apply(this, arguments); }; DOMTokenList.prototype.toggle = function () { if (state.activeSession) { const scriptName = state.callStack[state.callStack.length - 1]; if (scriptName) ensureBucket(scriptName).classListOps += 1; markActivity(); } return classToggle.apply(this, arguments); }; const styleSetProperty = CSSStyleDeclaration.prototype.setProperty; state.originals.styleSetProperty = styleSetProperty; CSSStyleDeclaration.prototype.setProperty = function () { if (state.activeSession) { const scriptName = state.callStack[state.callStack.length - 1]; if (scriptName) ensureBucket(scriptName).styleOps += 1; markActivity(); } return styleSetProperty.apply(this, arguments); }; } function startSession(triggerLabel) { if (state.activeSession) stopSession("restart"); state.byScript.clear(); state.activeSession = { label: triggerLabel, startedAt: now() }; if (state.pendingAutoStop) clearTimeout(state.pendingAutoStop); if (state.pendingHardStop) clearTimeout(state.pendingHardStop); state.pendingAutoStop = setTimeout(() => { stopSession("idle"); }, AUTO_STOP_MS); state.pendingHardStop = setTimeout(() => { stopSession("max"); }, MAX_SESSION_MS); console.log(`[AutoProfiler] gestart door: ${triggerLabel}`); } function stopSession(reason) { if (!state.activeSession) return []; if (state.pendingAutoStop) clearTimeout(state.pendingAutoStop); if (state.pendingHardStop) clearTimeout(state.pendingHardStop); const elapsed = now() - state.activeSession.startedAt; const rows = Array.from(state.byScript.values()) .sort((a, b) => b.totalMs - a.totalMs) .map(r => ({ script: r.script, totalMs: Number(r.totalMs.toFixed(1)), events: r.events, observers: r.observers, intervals: r.intervals, timeouts: r.timeouts, rafs: r.rafs, querySelectorAll: r.querySelectorAll, querySelector: r.querySelector, setAttribute: r.setAttribute, classListOps: r.classListOps, styleOps: r.styleOps, appendOps: r.appendOps, removeOps: r.removeOps })); const topRows = rows.slice(0, TOP_N); console.groupCollapsed( `%c[AutoProfiler] ${state.activeSession.label} klaar`, "color:#0a7;font-weight:bold;" ); console.log(`reden: ${reason}`); console.log(`duur: ${elapsed.toFixed(1)} ms`); console.table(topRows); console.log("volledige lijst:", rows); console.groupEnd(); state.sessions.push({ label: state.activeSession.label, reason, elapsedMs: elapsed, rows }); state.activeSession = null; return rows; } function isTargetField(el) { if (!el || !el.name) return false; return ( el.name === "form_fields[dorpeltype]" || el.name === "form_fields[vakken]" ); } function installAutoMonitor() { if (state.monitorInstalled) return; state.monitorInstalled = true; document.addEventListener("change", function (e) { const t = e.target; if (!isTargetField(t)) return; const value = t.getAttribute("data-original-value") || t.value || "(leeg)"; startSession(`${t.name} = ${value}`); }, true); document.addEventListener("input", function (e) { const t = e.target; if (!isTargetField(t)) return; const value = t.getAttribute("data-original-value") || t.value || "(leeg)"; startSession(`${t.name} = ${value}`); }, true); } patchAddEventListener(); patchTimers(); patchMutationObserver(); patchDomHeavyCalls(); installAutoMonitor(); window.__scriptProfilerAuto = { sessions: state.sessions, stopNow() { return stopSession("manual"); }, last() { return state.sessions[state.sessions.length - 1] || null; }, all() { return state.sessions; } }; console.log("[AutoProfiler] actief. Wijzig dorpeltype of vakken; output verschijnt automatisch in de console."); })();
//// VAKVULLING BI #ALLE-VAKKEN //// (() => { const SCALE = 3.84615384615; const SVG_ID = "alle-vakken"; // VVI value -> BI suffix const VVI_TO_SUFFIX = { "dubbelglas": "2glas", "tripleglas": "3glas", "paneel": "paneel", "glaspaneel": "glaspaneel", "deur": "deur", "kaderdeur": "kaderdeur", "dubbelkaderdeur": "dubbelkaderdeur", "draaikiep": "draaikiep", "vul in": null }; const BI_SUFFIXES = ["2glas","3glas","paneel","glaspaneel","deur","kaderdeur","dubbelkaderdeur","draaikiep"]; const esc = (s) => CSS.escape(s); function svgRoot() { return document.querySelector(`svg#${esc(SVG_ID)}`); } function setDisp(el, v) { if (el) el.setAttribute("display", v); } function ensureDefs(svg) { let defs = svg.querySelector("defs"); if (!defs) { defs = document.createElementNS("http://www.w3.org/2000/svg", "defs"); svg.insertBefore(defs, svg.firstChild); } return defs; } function toNumericValue(v) { const s = (v ?? "").toString().trim().replace(",", "."); const n = Number(s); return Number.isFinite(n) ? n : 0; } // Bestaande losse velden op ID (DMI, sponning, etc.) function readNumById(id) { const el = document.getElementById(id); if (!el) return null; return toNumericValue(el.value); } // Nieuw: leest numerieke waarden uit: // 1) input/select met id="form-field-" // 2) radio groep name="form_fields[]" // 3) select name="form_fields[]" function readNumericField(fieldKey) { // input/select met ID const idEl = document.getElementById(`form-field-${fieldKey}`); if (idEl) { return toNumericValue(idEl.value); } // radio groep const radio = document.querySelector(`input[type="radio"][name="form_fields[${fieldKey}]"]:checked`); if (radio) { const raw = radio.getAttribute("data-original-value") || radio.value; return toNumericValue(raw); } // select op name const sel = document.querySelector(`select[name="form_fields[${fieldKey}]"]`); if (sel) { return toNumericValue(sel.value); } return null; } // Prefer prefixA, else prefixB, else 0 function numField(prefixA, prefixB, suffix) { const a = readNumericField(`${prefixA}${suffix}`); if (a != null) return a; const b = readNumericField(`${prefixB}${suffix}`); if (b != null) return b; return 0; } function vakCount() { const checked = document.querySelector('input[type="radio"][name="form_fields[vakken]"]:checked'); if (checked && checked.value) { const m = String(checked.value).match(/([1-5])/); if (m) return Number(m[1]); } return 5; } // VVI select by name form_fields[VVI] function getVVI(n, i) { const sel = document.querySelector(`select[name="form_fields[${n}VVI${i}]"]`); return sel ? String(sel.value || "") : "vul in"; } function hideNeverShow(svg) { // BI shiftboxen nooit tonen svg.querySelectorAll(`[id^="vak-"][id*="-BI-shiftbox-"]`).forEach(el => setDisp(el, "none")); } function hideAllBIVariants(svg, i) { for (const suf of BI_SUFFIXES) { setDisp(svg.querySelector(`#${esc(`vak-${i}-BI-${suf}`)}`), "none"); } } function showGroupAndNestedGs(groupEl) { if (!groupEl) return; // shiftboxen sowieso niet tonen if (String(groupEl.id).includes("-shiftbox-")) { setDisp(groupEl, "none"); return; } setDisp(groupEl, "block"); groupEl.querySelectorAll("g").forEach(g => { if (String(g.id).includes("-shiftbox-")) setDisp(g, "none"); else setDisp(g, "block"); }); } function updateVisibility() { const svg = svgRoot(); if (!svg) return; hideNeverShow(svg); const n = vakCount(); // reset alleen onze BI-groepen for (let i = 1; i <= 5; i++) hideAllBIVariants(svg, i); // show voor i<=n op basis van VVI for (let i = 1; i <= n; i++) { const value = getVVI(n, i); const suffix = VVI_TO_SUFFIX[value]; if (!suffix) continue; const g = svg.querySelector(`#${esc(`vak-${i}-BI-${suffix}`)}`); showGroupAndNestedGs(g); } hideNeverShow(svg); } /****************************************************************** * BI position building (LI + DMI/TI cumulatief) * Alleen DMI geschaald: DMI/SCALE ******************************************************************/ function BI_LI(n) { return numField(`${n}`, "5", "LI"); } function BI_TI(n, i) { return numField(`${n}`, "5", `TI${i}`); } function BI_DMI_scaled(n, i) { return (numField(`${n}`, "5", `DMI${i}`) / SCALE); } function Sponning() { return (readNumById("form-field-BI_sponningbreedte") ?? 0); } function prefixLeft(n, i) { let x = BI_LI(n); if (i >= 2) x += BI_DMI_scaled(n,1) + BI_TI(n,1); if (i >= 3) x += BI_DMI_scaled(n,2) + BI_TI(n,2); if (i >= 4) x += BI_DMI_scaled(n,3) + BI_TI(n,3); if (i >= 5) x += BI_DMI_scaled(n,4) + BI_TI(n,4); return x; } /****************************************************************** * Shiftbox delta-engine ******************************************************************/ function desiredXFromDelta(rect, exprNow) { const curX = Number(rect.getAttribute("x") ?? "0"); if (rect.dataset.baseX == null) rect.dataset.baseX = String(curX); if (rect.dataset.baseExpr == null) rect.dataset.baseExpr = String(exprNow); const baseX = Number(rect.dataset.baseX); const baseExpr = Number(rect.dataset.baseExpr); return baseX + (exprNow - baseExpr); } function boxFromShiftRect(rect, exprNow) { const curX = Number(rect.getAttribute("x") ?? "0"); const curY = Number(rect.getAttribute("y") ?? "0"); const w = Number(rect.getAttribute("width") ?? "0"); const h = Number(rect.getAttribute("height") ?? "0"); const desiredX = desiredXFromDelta(rect, exprNow); return { rect, desiredX, dxInc: desiredX - curX, minX: curX, maxX: curX + w, minY: curY, maxY: curY + h, }; } function buildBoxes(items) { const map = new Map(); // key -> boxes[] for (const it of items) { if (!it.gEl) continue; const r = it.gEl.querySelector("rect"); if (!r) continue; const b = boxFromShiftRect(r, it.exprNow()); if (!map.has(it.key)) map.set(it.key, []); map.get(it.key).push(b); } return map; } function moveBoxes(map) { for (const boxes of map.values()) { for (const b of boxes) b.rect.setAttribute("x", b.desiredX); } } function dxForPoint(x, y, boxes) { let dx = 0; for (const b of boxes) { if (x >= b.minX && x <= b.maxX && y >= b.minY && y <= b.maxY) dx += b.dxInc; } return dx; } /****************************************************************** * Geometry shifters ******************************************************************/ function shiftPointsAttr(pointsStr, boxes) { const nums = pointsStr.trim().replace(/,/g, " ").split(/\s+/).filter(Boolean).map(Number); if (nums.length < 2 || nums.some(n => !Number.isFinite(n))) return pointsStr; const out = []; for (let i = 0; i < nums.length - 1; i += 2) { const x = nums[i], y = nums[i + 1]; out.push(String(x + dxForPoint(x, y, boxes)), String(y)); } return out.join(" "); } function tokenizePath(d) { return d.match(/[a-zA-Z]|-?\d*\.?\d+(?:e[-+]?\d+)?/g) || []; } function shiftPathD(d, boxes) { const t = tokenizePath(d); if (!t.length) return d; let i = 0, cmd = "", x = 0, y = 0, x0 = 0, y0 = 0; const out = []; const isCmd = tok => /^[a-zA-Z]$/.test(tok); const nextNum = () => Number(t[i++]); const push = v => out.push(v); const shiftAbs = (px, py) => [px + dxForPoint(px, py, boxes), py]; while (i < t.length) { if (isCmd(t[i])) cmd = t[i++]; const rel = (cmd === cmd.toLowerCase()); const C = cmd.toUpperCase(); if (C === "M") { push("M"); const px = nextNum(), py = nextNum(); const ax = rel ? x + px : px, ay = rel ? y + py : py; const p = shiftAbs(ax, ay); push(p[0]); push(p[1]); x = ax; y = ay; x0 = ax; y0 = ay; while (i < t.length && !isCmd(t[i])) { push("L"); const lx = nextNum(), ly = nextNum(); const ax2 = rel ? x + lx : lx, ay2 = rel ? y + ly : ly; const p2 = shiftAbs(ax2, ay2); push(p2[0]); push(p2[1]); x = ax2; y = ay2; } continue; } if (C === "L") { push("L"); while (i < t.length && !isCmd(t[i])) { const px = nextNum(), py = nextNum(); const ax = rel ? x + px : px, ay = rel ? y + py : py; const p = shiftAbs(ax, ay); push(p[0]); push(p[1]); x = ax; y = ay; } continue; } if (C === "H") { push("H"); while (i < t.length && !isCmd(t[i])) { const px = nextNum(); const ax = rel ? x + px : px; const p = shiftAbs(ax, y); push(p[0]); x = ax; } continue; } if (C === "V") { push("V"); while (i < t.length && !isCmd(t[i])) { const py = nextNum(); const ay = rel ? y + py : py; push(ay); y = ay; } continue; } if (C === "C") { push("C"); while (i < t.length && !isCmd(t[i])) { const x1 = nextNum(), y1 = nextNum(), x2 = nextNum(), y2 = nextNum(), x3 = nextNum(), y3 = nextNum(); const ax1 = rel ? x + x1 : x1, ay1 = rel ? y + y1 : y1; const ax2 = rel ? x + x2 : x2, ay2 = rel ? y + y2 : y2; const ax3 = rel ? x + x3 : x3, ay3 = rel ? y + y3 : y3; const p1 = shiftAbs(ax1, ay1); const p2 = shiftAbs(ax2, ay2); const p3 = shiftAbs(ax3, ay3); push(p1[0]); push(p1[1]); push(p2[0]); push(p2[1]); push(p3[0]); push(p3[1]); x = ax3; y = ay3; } continue; } if (C === "S") { push("S"); while (i < t.length && !isCmd(t[i])) { const x2 = nextNum(), y2 = nextNum(), x3 = nextNum(), y3 = nextNum(); const ax2 = rel ? x + x2 : x2, ay2 = rel ? y + y2 : y2; const ax3 = rel ? x + x3 : x3, ay3 = rel ? y + y3 : y3; const p2 = shiftAbs(ax2, ay2); const p3 = shiftAbs(ax3, ay3); push(p2[0]); push(p2[1]); push(p3[0]); push(p3[1]); x = ax3; y = ay3; } continue; } if (C === "Q") { push("Q"); while (i < t.length && !isCmd(t[i])) { const x1 = nextNum(), y1 = nextNum(), x2 = nextNum(), y2 = nextNum(); const ax1 = rel ? x + x1 : x1, ay1 = rel ? y + y1 : y1; const ax2 = rel ? x + x2 : x2, ay2 = rel ? y + y2 : y2; const p1 = shiftAbs(ax1, ay1); const p2 = shiftAbs(ax2, ay2); push(p1[0]); push(p1[1]); push(p2[0]); push(p2[1]); x = ax2; y = ay2; } continue; } if (C === "T") { push("T"); while (i < t.length && !isCmd(t[i])) { const x2 = nextNum(), y2 = nextNum(); const ax2 = rel ? x + x2 : x2, ay2 = rel ? y + y2 : y2; const p2 = shiftAbs(ax2, ay2); push(p2[0]); push(p2[1]); x = ax2; y = ay2; } continue; } if (C === "A") { push("A"); while (i < t.length && !isCmd(t[i])) { const rx = nextNum(), ry = nextNum(), rot = nextNum(), laf = nextNum(), sf = nextNum(), x2 = nextNum(), y2 = nextNum(); const ax2 = rel ? x + x2 : x2, ay2 = rel ? y + y2 : y2; const p2 = shiftAbs(ax2, ay2); push(rx); push(ry); push(rot); push(laf); push(sf); push(p2[0]); push(p2[1]); x = ax2; y = ay2; } continue; } if (C === "Z") { push("Z"); x = x0; y = y0; continue; } return d; } return out.join(" "); } function shiftRect(el, boxes) { const x = Number(el.getAttribute("x")); const y = Number(el.getAttribute("y")); const w = Number(el.getAttribute("width")); if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(w)) return; const h = Number(el.getAttribute("height")); const yMid = Number.isFinite(h) ? (y + h / 2) : y; const xL2 = x + dxForPoint(x, yMid, boxes); const xR = x + w; const xR2 = xR + dxForPoint(xR, yMid, boxes); el.setAttribute("x", Math.min(xL2, xR2)); el.setAttribute("width", Math.abs(xR2 - xL2)); } /****************************************************************** * Gradient shifting (zelfde als BU) ******************************************************************/ function parseFillUrl(fill) { const m = String(fill).match(/^url\((["']?)#([^)'" ]+)\1\)$/); return m ? m[2] : null; } function isClearlyUserSpace(gradEl) { const units = (gradEl.getAttribute("gradientUnits") || "").trim(); if (units === "userSpaceOnUse") return true; if (units) return false; for (const a of ["x1","y1","x2","y2","cx","cy","fx","fy","r"]) { const v = gradEl.getAttribute(a); if (v == null) continue; const n = Number(String(v).trim().replace(",", ".")); if (!Number.isFinite(n)) continue; if (n < -0.001 || n > 1.001) return true; } return false; } let gradCloneCounter = 0; function ensureGradientClone(svg, el, baseGradId) { const defs = ensureDefs(svg); if (!el.dataset.gradCloneId) { gradCloneCounter += 1; el.dataset.gradCloneId = `${baseGradId}__clone__${gradCloneCounter}`; } const cloneId = el.dataset.gradCloneId; let clone = defs.querySelector(`#${esc(cloneId)}`); if (!clone) { const orig = svg.querySelector(`#${esc(baseGradId)}`); if (!orig) return null; if (!isClearlyUserSpace(orig)) return null; clone = orig.cloneNode(true); clone.setAttribute("id", cloneId); clone.setAttribute("gradientUnits", "userSpaceOnUse"); clone.setAttribute("data-base-gradient-transform", orig.getAttribute("gradientTransform") || ""); defs.appendChild(clone); } el.setAttribute("fill", `url(#${cloneId})`); return clone; } function dxRepresentative(el, boxes) { const tag = el.tagName.toLowerCase(); if (tag === "rect") { const x = Number(el.getAttribute("x")); const y = Number(el.getAttribute("y")); const h = Number(el.getAttribute("height")); const yMid = Number.isFinite(h) ? (y + h / 2) : y; if (!Number.isFinite(x) || !Number.isFinite(yMid)) return 0; return dxForPoint(x, yMid, boxes); } if (tag === "polygon" || tag === "polyline") { const pts = (el.getAttribute("points") || "").trim().replace(/,/g, " ").split(/\s+/).filter(Boolean).map(Number); if (pts.length < 2 || pts.some(n => !Number.isFinite(n))) return 0; return dxForPoint(pts[0], pts[1], boxes); } if (tag === "path") { const d = el.getAttribute("d") || ""; const nums = d.match(/-?\d*\.?\d+(?:e[-+]?\d+)?/g); if (!nums || nums.length < 2) return 0; const x = Number(nums[0]), y = Number(nums[1]); if (!Number.isFinite(x) || !Number.isFinite(y)) return 0; return dxForPoint(x, y, boxes); } return 0; } function applyGradientShift(svg, el, dx) { const fill = el.getAttribute("fill"); if (!fill) return; const base = parseFillUrl(fill); if (!base) return; const clone = ensureGradientClone(svg, el, base); if (!clone) return; const baseT = (clone.getAttribute("data-base-gradient-transform") || "").trim(); const t = `translate(${dx},0)`; clone.setAttribute("gradientTransform", baseT ? `${baseT} ${t}` : t); } function applyShiftToGroup(svg, g, boxes) { if (!g || !boxes || !boxes.length) return; const gradDx = new Map(); g.querySelectorAll("[fill^='url(']").forEach(el => { const dx = dxRepresentative(el, boxes); if (dx) gradDx.set(el, dx); }); g.querySelectorAll("polygon").forEach(el => { const p = el.getAttribute("points"); if (p) el.setAttribute("points", shiftPointsAttr(p, boxes)); }); g.querySelectorAll("polyline").forEach(el => { const p = el.getAttribute("points"); if (p) el.setAttribute("points", shiftPointsAttr(p, boxes)); }); g.querySelectorAll("rect").forEach(el => shiftRect(el, boxes)); g.querySelectorAll("path").forEach(el => { const d = el.getAttribute("d"); if (d) el.setAttribute("d", shiftPathD(d, boxes)); }); for (const [el, dx] of gradDx.entries()) { applyGradientShift(svg, el, dx); } } function buildAndApplyShifts() { const svg = svgRoot(); if (!svg) return; hideNeverShow(svg); const n = vakCount(); // BI shiftboxen per vak const items = []; for (let i = 1; i <= n; i++) { const gL = svg.querySelector(`#${esc(`vak-${i}-BI-shiftbox-L`)}`); const gR = svg.querySelector(`#${esc(`vak-${i}-BI-shiftbox-R`)}`); items.push({ key: `BI:${i}`, gEl: gL, exprNow: () => prefixLeft(n, i) - Sponning() }); items.push({ key: `BI:${i}`, gEl: gR, exprNow: () => prefixLeft(n, i) + BI_DMI_scaled(n, i) + Sponning() }); } const boxesMap = buildBoxes(items); // Apply geometry shifts alleen op onze BI-variantgroepen for (let i = 1; i <= n; i++) { const boxes = boxesMap.get(`BI:${i}`) || []; if (!boxes.length) continue; for (const suf of BI_SUFFIXES) { const g = svg.querySelector(`#${esc(`vak-${i}-BI-${suf}`)}`); if (g) applyShiftToGroup(svg, g, boxes); } } // Verplaats shiftbox rects (blijven hidden) moveBoxes(boxesMap); hideNeverShow(svg); } function updateAll() { updateVisibility(); buildAndApplyShifts(); } /****************************************************************** * Bind listeners (Elementor friendly) ******************************************************************/ function bind(root = document) { // VVI selects root.querySelectorAll(`select[name^="form_fields["][name*="VVI"]`).forEach(el => { if (el.dataset.biBound === "1") return; el.dataset.biBound = "1"; el.addEventListener("change", updateAll); el.addEventListener("input", updateAll); }); // alle form-field inputs/selects met ID // (includes: DMI, BI_sponningbreedte, etc.) root.querySelectorAll(`input[id^="form-field-"], select[id^="form-field-"]`).forEach(el => { if (el.dataset.biShiftBound === "1") return; el.dataset.biShiftBound = "1"; el.addEventListener("change", updateAll); el.addEventListener("input", updateAll); }); // ✅ nieuwe LI/TI radio's root.querySelectorAll(`input[type="radio"][name^="form_fields["]`).forEach(el => { const name = el.getAttribute("name") || ""; // Alleen LI en TI oppakken; DMI blijft zoals hij is if (!/^form_fields\[(?:[1-5]LI|[1-5]TI[1-5])\]$/.test(name)) return; if (el.dataset.biLiTiRadioBound === "1") return; el.dataset.biLiTiRadioBound = "1"; el.addEventListener("change", updateAll); el.addEventListener("input", updateAll); }); // vakken radios root.querySelectorAll(`input[type="radio"][name="form_fields[vakken]"]`).forEach(el => { if (el.dataset.biVakkenBound === "1") return; el.dataset.biVakkenBound = "1"; el.addEventListener("change", updateAll); el.addEventListener("input", updateAll); }); } function boot() { bind(document); updateAll(); const mo = new MutationObserver(muts => { for (const mu of muts) { mu.addedNodes.forEach(node => { if (!(node instanceof HTMLElement)) return; bind(node); }); } updateAll(); }); mo.observe(document.documentElement, { childList: true, subtree: true }); } if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", boot); } else { boot(); } // Optional: reset baselines (als andere scripts eerst iets aanpassen) window.rebaseAlleVakkenBI = () => { const svg = svgRoot(); if (!svg) return; svg.querySelectorAll(`[id*="-BI-shiftbox-"] rect`).forEach(r => { r.dataset.baseX = String(Number(r.getAttribute("x") ?? "0")); delete r.dataset.baseExpr; }); updateAll(); }; })();
//// VAKVULLING BI #ALLE-VAKKEN //// (() => { const SCALE = 3.84615384615; const SVG_ID = "alle-vakken"; // VVI value -> BI suffix const VVI_TO_SUFFIX = { "dubbelglas": "2glas", "tripleglas": "3glas", "paneel": "paneel", "glaspaneel": "glaspaneel", "deur": "deur", "kaderdeur": "kaderdeur", "dubbelkaderdeur": "dubbelkaderdeur", "draaikiep": "draaikiep", "vul in": null }; const BI_SUFFIXES = ["2glas","3glas","paneel","glaspaneel","deur","kaderdeur","dubbelkaderdeur","draaikiep"]; const esc = (s) => CSS.escape(s); function svgRoot() { return document.querySelector(`svg#${esc(SVG_ID)}`); } function setDisp(el, v) { if (el) el.setAttribute("display", v); } function ensureDefs(svg) { let defs = svg.querySelector("defs"); if (!defs) { defs = document.createElementNS("http://www.w3.org/2000/svg", "defs"); svg.insertBefore(defs, svg.firstChild); } return defs; } function toNumericValue(v) { const s = (v ?? "").toString().trim().replace(",", "."); const n = Number(s); return Number.isFinite(n) ? n : 0; } // Bestaande losse velden op ID (DMI, sponning, etc.) function readNumById(id) { const el = document.getElementById(id); if (!el) return null; return toNumericValue(el.value); } // Nieuw: leest numerieke waarden uit: // 1) input/select met id="form-field-" // 2) radio groep name="form_fields[]" // 3) select name="form_fields[]" function readNumericField(fieldKey) { // input/select met ID const idEl = document.getElementById(`form-field-${fieldKey}`); if (idEl) { return toNumericValue(idEl.value); } // radio groep const radio = document.querySelector(`input[type="radio"][name="form_fields[${fieldKey}]"]:checked`); if (radio) { const raw = radio.getAttribute("data-original-value") || radio.value; return toNumericValue(raw); } // select op name const sel = document.querySelector(`select[name="form_fields[${fieldKey}]"]`); if (sel) { return toNumericValue(sel.value); } return null; } // Prefer prefixA, else prefixB, else 0 function numField(prefixA, prefixB, suffix) { const a = readNumericField(`${prefixA}${suffix}`); if (a != null) return a; const b = readNumericField(`${prefixB}${suffix}`); if (b != null) return b; return 0; } function vakCount() { const checked = document.querySelector('input[type="radio"][name="form_fields[vakken]"]:checked'); if (checked && checked.value) { const m = String(checked.value).match(/([1-5])/); if (m) return Number(m[1]); } return 5; } // VVI select by name form_fields[VVI] function getVVI(n, i) { const sel = document.querySelector(`select[name="form_fields[${n}VVI${i}]"]`); return sel ? String(sel.value || "") : "vul in"; } function hideNeverShow(svg) { // BI shiftboxen nooit tonen svg.querySelectorAll(`[id^="vak-"][id*="-BI-shiftbox-"]`).forEach(el => setDisp(el, "none")); } function hideAllBIVariants(svg, i) { for (const suf of BI_SUFFIXES) { setDisp(svg.querySelector(`#${esc(`vak-${i}-BI-${suf}`)}`), "none"); } } function showGroupAndNestedGs(groupEl) { if (!groupEl) return; // shiftboxen sowieso niet tonen if (String(groupEl.id).includes("-shiftbox-")) { setDisp(groupEl, "none"); return; } setDisp(groupEl, "block"); groupEl.querySelectorAll("g").forEach(g => { if (String(g.id).includes("-shiftbox-")) setDisp(g, "none"); else setDisp(g, "block"); }); } function updateVisibility() { const svg = svgRoot(); if (!svg) return; hideNeverShow(svg); const n = vakCount(); // reset alleen onze BI-groepen for (let i = 1; i <= 5; i++) { hideAllBIVariants(svg, i); } // show voor i<=n op basis van VVI for (let i = 1; i <= n; i++) { const value = getVVI(n, i); const suffix = VVI_TO_SUFFIX[value]; if (!suffix) continue; const g = svg.querySelector(`#${esc(`vak-${i}-BI-${suffix}`)}`); showGroupAndNestedGs(g); } hideNeverShow(svg); } /****************************************************************** * BI position building (LI + DMI/TI cumulatief) * Alleen DMI geschaald: DMI/SCALE ******************************************************************/ function BI_LI(n) { return numField(`${n}`, "5", "LI"); } function BI_TI(n, i) { return numField(`${n}`, "5", `TI${i}`); } function BI_DMI_scaled(n, i) { return (numField(`${n}`, "5", `DMI${i}`) / SCALE); } function Sponning() { return (readNumById("form-field-BI_sponningbreedte") ?? 0); } function prefixLeft(n, i) { let x = BI_LI(n); if (i >= 2) x += BI_DMI_scaled(n,1) + BI_TI(n,1); if (i >= 3) x += BI_DMI_scaled(n,2) + BI_TI(n,2); if (i >= 4) x += BI_DMI_scaled(n,3) + BI_TI(n,3); if (i >= 5) x += BI_DMI_scaled(n,4) + BI_TI(n,4); return x; } /****************************************************************** * Shiftbox delta-engine ******************************************************************/ function desiredXFromDelta(rect, exprNow) { const curX = Number(rect.getAttribute("x") ?? "0"); if (rect.dataset.baseX == null) rect.dataset.baseX = String(curX); if (rect.dataset.baseExpr == null) rect.dataset.baseExpr = String(exprNow); const baseX = Number(rect.dataset.baseX); const baseExpr = Number(rect.dataset.baseExpr); return baseX + (exprNow - baseExpr); } function boxFromShiftRect(rect, exprNow) { const curX = Number(rect.getAttribute("x") ?? "0"); const curY = Number(rect.getAttribute("y") ?? "0"); const w = Number(rect.getAttribute("width") ?? "0"); const h = Number(rect.getAttribute("height") ?? "0"); const desiredX = desiredXFromDelta(rect, exprNow); return { rect, desiredX, dxInc: desiredX - curX, minX: curX, maxX: curX + w, minY: curY, maxY: curY + h, }; } function buildBoxes(items) { const map = new Map(); // key -> boxes[] for (const it of items) { if (!it.gEl) continue; const r = it.gEl.querySelector("rect"); if (!r) continue; const b = boxFromShiftRect(r, it.exprNow()); if (!map.has(it.key)) map.set(it.key, []); map.get(it.key).push(b); } return map; } function moveBoxes(map) { for (const boxes of map.values()) { for (const b of boxes) { b.rect.setAttribute("x", b.desiredX); } } } function dxForPoint(x, y, boxes) { let dx = 0; for (const b of boxes) { if (x >= b.minX && x <= b.maxX && y >= b.minY && y <= b.maxY) dx += b.dxInc; } return dx; } /****************************************************************** * Geometry shifters ******************************************************************/ function shiftPointsAttr(pointsStr, boxes) { const nums = pointsStr.trim().replace(/,/g, " ").split(/\s+/).filter(Boolean).map(Number); if (nums.length < 2 || nums.some(n => !Number.isFinite(n))) return pointsStr; const out = []; for (let i = 0; i < nums.length - 1; i += 2) { const x = nums[i], y = nums[i + 1]; out.push(String(x + dxForPoint(x, y, boxes)), String(y)); } return out.join(" "); } function tokenizePath(d) { return d.match(/[a-zA-Z]|-?\d*\.?\d+(?:e[-+]?\d+)?/g) || []; } function shiftPathD(d, boxes) { const t = tokenizePath(d); if (!t.length) return d; let i = 0, cmd = "", x = 0, y = 0, x0 = 0, y0 = 0; const out = []; const isCmd = tok => /^[a-zA-Z]$/.test(tok); const nextNum = () => Number(t[i++]); const push = v => out.push(v); const shiftAbs = (px, py) => [px + dxForPoint(px, py, boxes), py]; while (i < t.length) { if (isCmd(t[i])) cmd = t[i++]; const rel = (cmd === cmd.toLowerCase()); const C = cmd.toUpperCase(); if (C === "M") { push("M"); const px = nextNum(), py = nextNum(); const ax = rel ? x + px : px, ay = rel ? y + py : py; const p = shiftAbs(ax, ay); push(p[0]); push(p[1]); x = ax; y = ay; x0 = ax; y0 = ay; while (i < t.length && !isCmd(t[i])) { push("L"); const lx = nextNum(), ly = nextNum(); const ax2 = rel ? x + lx : lx, ay2 = rel ? y + ly : ly; const p2 = shiftAbs(ax2, ay2); push(p2[0]); push(p2[1]); x = ax2; y = ay2; } continue; } if (C === "L") { push("L"); while (i < t.length && !isCmd(t[i])) { const px = nextNum(), py = nextNum(); const ax = rel ? x + px : px, ay = rel ? y + py : py; const p = shiftAbs(ax, ay); push(p[0]); push(p[1]); x = ax; y = ay; } continue; } if (C === "H") { push("H"); while (i < t.length && !isCmd(t[i])) { const px = nextNum(); const ax = rel ? x + px : px; const p = shiftAbs(ax, y); push(p[0]); x = ax; } continue; } if (C === "V") { push("V"); while (i < t.length && !isCmd(t[i])) { const py = nextNum(); const ay = rel ? y + py : py; push(ay); y = ay; } continue; } if (C === "C") { push("C"); while (i < t.length && !isCmd(t[i])) { const x1 = nextNum(), y1 = nextNum(), x2 = nextNum(), y2 = nextNum(), x3 = nextNum(), y3 = nextNum(); const ax1 = rel ? x + x1 : x1, ay1 = rel ? y + y1 : y1; const ax2 = rel ? x + x2 : x2, ay2 = rel ? y + y2 : y2; const ax3 = rel ? x + x3 : x3, ay3 = rel ? y + y3 : y3; const p1 = shiftAbs(ax1, ay1); const p2 = shiftAbs(ax2, ay2); const p3 = shiftAbs(ax3, ay3); push(p1[0]); push(p1[1]); push(p2[0]); push(p2[1]); push(p3[0]); push(p3[1]); x = ax3; y = ay3; } continue; } if (C === "S") { push("S"); while (i < t.length && !isCmd(t[i])) { const x2 = nextNum(), y2 = nextNum(), x3 = nextNum(), y3 = nextNum(); const ax2 = rel ? x + x2 : x2, ay2 = rel ? y + y2 : y2; const ax3 = rel ? x + x3 : x3, ay3 = rel ? y + y3 : y3; const p2 = shiftAbs(ax2, ay2); const p3 = shiftAbs(ax3, ay3); push(p2[0]); push(p2[1]); push(p3[0]); push(p3[1]); x = ax3; y = ay3; } continue; } if (C === "Q") { push("Q"); while (i < t.length && !isCmd(t[i])) { const x1 = nextNum(), y1 = nextNum(), x2 = nextNum(), y2 = nextNum(); const ax1 = rel ? x + x1 : x1, ay1 = rel ? y + y1 : y1; const ax2 = rel ? x + x2 : x2, ay2 = rel ? y + y2 : y2; const p1 = shiftAbs(ax1, ay1); const p2 = shiftAbs(ax2, ay2); push(p1[0]); push(p1[1]); push(p2[0]); push(p2[1]); x = ax2; y = ay2; } continue; } if (C === "T") { push("T"); while (i < t.length && !isCmd(t[i])) { const x2 = nextNum(), y2 = nextNum(); const ax2 = rel ? x + x2 : x2, ay2 = rel ? y + y2 : y2; const p2 = shiftAbs(ax2, ay2); push(p2[0]); push(p2[1]); x = ax2; y = ay2; } continue; } if (C === "A") { push("A"); while (i < t.length && !isCmd(t[i])) { const rx = nextNum(), ry = nextNum(), rot = nextNum(), laf = nextNum(), sf = nextNum(), x2 = nextNum(), y2 = nextNum(); const ax2 = rel ? x + x2 : x2, ay2 = rel ? y + y2 : y2; const p2 = shiftAbs(ax2, ay2); push(rx); push(ry); push(rot); push(laf); push(sf); push(p2[0]); push(p2[1]); x = ax2; y = ay2; } continue; } if (C === "Z") { push("Z"); x = x0; y = y0; continue; } return d; } return out.join(" "); } function shiftRect(el, boxes) { const x = Number(el.getAttribute("x")); const y = Number(el.getAttribute("y")); const w = Number(el.getAttribute("width")); if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(w)) return; const h = Number(el.getAttribute("height")); const yMid = Number.isFinite(h) ? (y + h / 2) : y; const xL2 = x + dxForPoint(x, yMid, boxes); const xR = x + w; const xR2 = xR + dxForPoint(xR, yMid, boxes); el.setAttribute("x", Math.min(xL2, xR2)); el.setAttribute("width", Math.abs(xR2 - xL2)); } /****************************************************************** * Gradient shifting (zelfde als BU) ******************************************************************/ function parseFillUrl(fill) { const m = String(fill).match(/^url\((["']?)#([^)'" ]+)\1\)$/); return m ? m[2] : null; } function isClearlyUserSpace(gradEl) { const units = (gradEl.getAttribute("gradientUnits") || "").trim(); if (units === "userSpaceOnUse") return true; if (units) return false; for (const a of ["x1","y1","x2","y2","cx","cy","fx","fy","r"]) { const v = gradEl.getAttribute(a); if (v == null) continue; const n = Number(String(v).trim().replace(",", ".")); if (!Number.isFinite(n)) continue; if (n < -0.001 || n > 1.001) return true; } return false; } let gradCloneCounter = 0; function ensureGradientClone(svg, el, baseGradId) { const defs = ensureDefs(svg); if (!el.dataset.gradCloneId) { gradCloneCounter += 1; el.dataset.gradCloneId = `${baseGradId}__clone__${gradCloneCounter}`; } const cloneId = el.dataset.gradCloneId; let clone = defs.querySelector(`#${esc(cloneId)}`); if (!clone) { const orig = svg.querySelector(`#${esc(baseGradId)}`); if (!orig) return null; if (!isClearlyUserSpace(orig)) return null; clone = orig.cloneNode(true); clone.setAttribute("id", cloneId); clone.setAttribute("gradientUnits", "userSpaceOnUse"); clone.setAttribute("data-base-gradient-transform", orig.getAttribute("gradientTransform") || ""); defs.appendChild(clone); } el.setAttribute("fill", `url(#${cloneId})`); return clone; } function dxRepresentative(el, boxes) { const tag = el.tagName.toLowerCase(); if (tag === "rect") { const x = Number(el.getAttribute("x")); const y = Number(el.getAttribute("y")); const h = Number(el.getAttribute("height")); const yMid = Number.isFinite(h) ? (y + h / 2) : y; if (!Number.isFinite(x) || !Number.isFinite(yMid)) return 0; return dxForPoint(x, yMid, boxes); } if (tag === "polygon" || tag === "polyline") { const pts = (el.getAttribute("points") || "").trim().replace(/,/g, " ").split(/\s+/).filter(Boolean).map(Number); if (pts.length < 2 || pts.some(n => !Number.isFinite(n))) return 0; return dxForPoint(pts[0], pts[1], boxes); } if (tag === "path") { const d = el.getAttribute("d") || ""; const nums = d.match(/-?\d*\.?\d+(?:e[-+]?\d+)?/g); if (!nums || nums.length < 2) return 0; const x = Number(nums[0]), y = Number(nums[1]); if (!Number.isFinite(x) || !Number.isFinite(y)) return 0; return dxForPoint(x, y, boxes); } return 0; } function applyGradientShift(svg, el, dx) { const fill = el.getAttribute("fill"); if (!fill) return; const base = parseFillUrl(fill); if (!base) return; const clone = ensureGradientClone(svg, el, base); if (!clone) return; const baseT = (clone.getAttribute("data-base-gradient-transform") || "").trim(); const t = `translate(${dx},0)`; clone.setAttribute("gradientTransform", baseT ? `${baseT} ${t}` : t); } function applyShiftToGroup(svg, g, boxes) { if (!g || !boxes || !boxes.length) return; const gradDx = new Map(); g.querySelectorAll("[fill^='url(']").forEach(el => { const dx = dxRepresentative(el, boxes); if (dx) gradDx.set(el, dx); }); g.querySelectorAll("polygon").forEach(el => { const p = el.getAttribute("points"); if (p) el.setAttribute("points", shiftPointsAttr(p, boxes)); }); g.querySelectorAll("polyline").forEach(el => { const p = el.getAttribute("points"); if (p) el.setAttribute("points", shiftPointsAttr(p, boxes)); }); g.querySelectorAll("rect").forEach(el => shiftRect(el, boxes)); g.querySelectorAll("path").forEach(el => { const d = el.getAttribute("d"); if (d) el.setAttribute("d", shiftPathD(d, boxes)); }); for (const [el, dx] of gradDx.entries()) { applyGradientShift(svg, el, dx); } } function buildAndApplyShifts() { const svg = svgRoot(); if (!svg) return; hideNeverShow(svg); const n = vakCount(); // BI shiftboxen per vak const items = []; for (let i = 1; i <= n; i++) { const gL = svg.querySelector(`#${esc(`vak-${i}-BI-shiftbox-L`)}`); const gR = svg.querySelector(`#${esc(`vak-${i}-BI-shiftbox-R`)}`); items.push({ key: `BI:${i}`, gEl: gL, exprNow: () => prefixLeft(n, i) - Sponning() }); items.push({ key: `BI:${i}`, gEl: gR, exprNow: () => prefixLeft(n, i) + BI_DMI_scaled(n, i) + Sponning() }); } const boxesMap = buildBoxes(items); // Apply geometry shifts alleen op onze BI-variantgroepen for (let i = 1; i <= n; i++) { const boxes = boxesMap.get(`BI:${i}`) || []; if (!boxes.length) continue; for (const suf of BI_SUFFIXES) { const g = svg.querySelector(`#${esc(`vak-${i}-BI-${suf}`)}`); if (g) applyShiftToGroup(svg, g, boxes); } } // Verplaats shiftbox rects (blijven hidden) moveBoxes(boxesMap); hideNeverShow(svg); } function updateAll() { updateVisibility(); buildAndApplyShifts(); } /****************************************************************** * Bind listeners (Elementor friendly) ******************************************************************/ function bind(root = document) { // VVI selects root.querySelectorAll(`select[name^="form_fields["][name*="VVI"]`).forEach(el => { if (el.dataset.biBound === "1") return; el.dataset.biBound = "1"; el.addEventListener("change", updateAll); el.addEventListener("input", updateAll); }); // alle form-field inputs/selects met ID // (includes: DMI, BI_sponningbreedte, etc.) root.querySelectorAll(`input[id^="form-field-"], select[id^="form-field-"]`).forEach(el => { if (el.dataset.biShiftBound === "1") return; el.dataset.biShiftBound = "1"; el.addEventListener("change", updateAll); el.addEventListener("input", updateAll); }); // ✅ nieuwe LI/TI radio's root.querySelectorAll(`input[type="radio"][name^="form_fields["]`).forEach(el => { const name = el.getAttribute("name") || ""; // Alleen LI en TI oppakken; DMI blijft zoals hij is if (!/^form_fields\[(?:[1-5]LI|[1-5]TI[1-5])\]$/.test(name)) return; if (el.dataset.biLiTiRadioBound === "1") return; el.dataset.biLiTiRadioBound = "1"; el.addEventListener("change", updateAll); el.addEventListener("input", updateAll); }); // vakken radios root.querySelectorAll(`input[type="radio"][name="form_fields[vakken]"]`).forEach(el => { if (el.dataset.biVakkenBound === "1") return; el.dataset.biVakkenBound = "1"; el.addEventListener("change", updateAll); el.addEventListener("input", updateAll); }); } function boot() { bind(document); updateAll(); const mo = new MutationObserver(muts => { for (const mu of muts) { mu.addedNodes.forEach(node => { if (!(node instanceof HTMLElement)) return; bind(node); }); } updateAll(); }); mo.observe(document.documentElement, { childList: true, subtree: true }); } if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", boot); } else { boot(); } // Optional: reset baselines (als andere scripts eerst iets aanpassen) window.rebaseAlleVakkenBI = () => { const svg = svgRoot(); if (!svg) return; svg.querySelectorAll(`[id*="-BI-shiftbox-"] rect`).forEach(r => { r.dataset.baseX = String(Number(r.getAttribute("x") ?? "0")); delete r.dataset.baseExpr; }); updateAll(); }; })();
//// VAKVULLING BI #ALLE-VAKKEN //// (() => { const SCALE = 3.84615384615; const SVG_ID = "alle-vakken"; // VVI value -> BI suffix const VVI_TO_SUFFIX = { "dubbelglas": "2glas", "tripleglas": "3glas", "paneel": "paneel", "glaspaneel": "glaspaneel", "deur": "deur", "kaderdeur": "kaderdeur", "dubbelkaderdeur": "dubbelkaderdeur", "draaikiep": "draaikiep", "vul in": null }; const BI_SUFFIXES = ["2glas","3glas","paneel","glaspaneel","deur","kaderdeur" ,"dubbelkaderdeur","draaikiep"]; const esc = (s) => CSS.escape(s); function svgRoot() { return document.querySelector(`svg#${esc(SVG_ID)}`); } function setDisp(el, v) { if (el) el.setAttribute("display", v); } function ensureDefs(svg) { let defs = svg.querySelector("defs"); if (!defs) { defs = document.createElementNS("http://www.w3.org/2000/svg", "defs"); svg.insertBefore(defs, svg.firstChild); } return defs; } function readNumById(id) { const el = document.getElementById(id); if (!el) return null; const v = (el.value ?? "").toString().trim().replace(",", "."); const n = Number(v); return Number.isFinite(n) ? n : 0; } // Prefer prefixA, else prefixB, else 0 function numField(prefixA, prefixB, suffix) { const a = readNumById(`form-field-${prefixA}${suffix}`); if (a != null) return a; const b = readNumById(`form-field-${prefixB}${suffix}`); if (b != null) return b; return 0; } function vakCount() { const checked = document.querySelector('input[type="radio"][name="form_fields[vakken]"]:checked'); if (checked && checked.value) { const m = String(checked.value).match(/([1-5])/); if (m) return Number(m[1]); } return 5; } // VVI select by name form_fields[VVI] function getVVI(n, i) { const sel = document.querySelector(`select[name="form_fields[${n}VVI${i}]"]`); return sel ? String(sel.value || "") : "vul in"; } function hideNeverShow(svg) { // BI shiftboxen nooit tonen svg.querySelectorAll(`[id^="vak-"][id*="-BI-shiftbox-"]`).forEach(el => setDisp(el, "none")); } function hideAllBIVariants(svg, i) { for (const suf of BI_SUFFIXES) { setDisp(svg.querySelector(`#${esc(`vak-${i}-BI-${suf}`)}`), "none"); } } function showGroupAndNestedGs(groupEl) { if (!groupEl) return; // shiftboxen sowieso niet tonen if (String(groupEl.id).includes("-shiftbox-")) { setDisp(groupEl, "none"); return; } setDisp(groupEl, "block"); groupEl.querySelectorAll("g").forEach(g => { if (String(g.id).includes("-shiftbox-")) setDisp(g, "none"); else setDisp(g, "block"); }); } function updateVisibility() { const svg = svgRoot(); if (!svg) return; hideNeverShow(svg); const n = vakCount(); // reset alleen onze BI-groepen for (let i = 1; i <= 5; i++) hideAllBIVariants(svg, i); // show voor i<=n op basis van VVI for (let i = 1; i <= n; i++) { const value = getVVI(n, i); const suffix = VVI_TO_SUFFIX[value]; if (!suffix) continue; const g = svg.querySelector(`#${esc(`vak-${i}-BI-${suffix}`)}`); showGroupAndNestedGs(g); } hideNeverShow(svg); } /****************************************************************** * BI position building (LI + DMI/TI cumulatief) * Alleen DMI geschaald: DMI/SCALE ******************************************************************/ function BI_LI(n) { return numField(`${n}`, "5", "LI"); } function BI_TI(n, i) { return numField(`${n}`, "5", `TI${i}`); } function BI_DMI_scaled(n, i) { return (numField(`${n}`, "5", `DMI${i}`) / SCALE); } function Sponning() { return (readNumById("form-field-BI_sponningbreedte") ?? 0); } function prefixLeft(n, i) { let x = BI_LI(n); if (i >= 2) x += BI_DMI_scaled(n,1) + BI_TI(n,1); if (i >= 3) x += BI_DMI_scaled(n,2) + BI_TI(n,2); if (i >= 4) x += BI_DMI_scaled(n,3) + BI_TI(n,3); if (i >= 5) x += BI_DMI_scaled(n,4) + BI_TI(n,4); return x; } /****************************************************************** * Shiftbox delta-engine ******************************************************************/ function desiredXFromDelta(rect, exprNow) { const curX = Number(rect.getAttribute("x") ?? "0"); if (rect.dataset.baseX == null) rect.dataset.baseX = String(curX); if (rect.dataset.baseExpr == null) rect.dataset.baseExpr = String(exprNow); const baseX = Number(rect.dataset.baseX); const baseExpr = Number(rect.dataset.baseExpr); return baseX + (exprNow - baseExpr); } function boxFromShiftRect(rect, exprNow) { const curX = Number(rect.getAttribute("x") ?? "0"); const curY = Number(rect.getAttribute("y") ?? "0"); const w = Number(rect.getAttribute("width") ?? "0"); const h = Number(rect.getAttribute("height") ?? "0"); const desiredX = desiredXFromDelta(rect, exprNow); return { rect, desiredX, dxInc: desiredX - curX, minX: curX, maxX: curX + w, minY: curY, maxY: curY + h, }; } function buildBoxes(items) { const map = new Map(); // key -> boxes[] for (const it of items) { if (!it.gEl) continue; const r = it.gEl.querySelector("rect"); if (!r) continue; const b = boxFromShiftRect(r, it.exprNow()); if (!map.has(it.key)) map.set(it.key, []); map.get(it.key).push(b); } return map; } function moveBoxes(map) { for (const boxes of map.values()) { for (const b of boxes) b.rect.setAttribute("x", b.desiredX); } } function dxForPoint(x, y, boxes) { let dx = 0; for (const b of boxes) { if (x >= b.minX && x <= b.maxX && y >= b.minY && y <= b.maxY) dx += b.dxInc; } return dx; } /****************************************************************** * Geometry shifters ******************************************************************/ function shiftPointsAttr(pointsStr, boxes) { const nums = pointsStr.trim().replace(/,/g, " ").split(/\s+/).filter(Boolean).map(Number); if (nums.length < 2 || nums.some(n => !Number.isFinite(n))) return pointsStr; const out = []; for (let i = 0; i < nums.length - 1; i += 2) { const x = nums[i], y = nums[i + 1]; out.push(String(x + dxForPoint(x, y, boxes)), String(y)); } return out.join(" "); } function tokenizePath(d) { return d.match(/[a-zA-Z]|-?\d*\.?\d+(?:e[-+]?\d+)?/g) || []; } function shiftPathD(d, boxes) { const t = tokenizePath(d); if (!t.length) return d; let i = 0, cmd = "", x = 0, y = 0, x0 = 0, y0 = 0; const out = []; const isCmd = tok => /^[a-zA-Z]$/.test(tok); const nextNum = () => Number(t[i++]); const push = v => out.push(v); const shiftAbs = (px, py) => [px + dxForPoint(px, py, boxes), py]; while (i < t.length) { if (isCmd(t[i])) cmd = t[i++]; const rel = (cmd === cmd.toLowerCase()); const C = cmd.toUpperCase(); if (C === "M") { push("M"); const px = nextNum(), py = nextNum(); const ax = rel ? x + px : px, ay = rel ? y + py : py; const p = shiftAbs(ax, ay); push(p[0]); push(p[1]); x = ax; y = ay; x0 = ax; y0 = ay; while (i < t.length && !isCmd(t[i])) { push("L"); const lx = nextNum(), ly = nextNum(); const ax2 = rel ? x + lx : lx, ay2 = rel ? y + ly : ly; const p2 = shiftAbs(ax2, ay2); push(p2[0]); push(p2[1]); x = ax2; y = ay2; } continue; } if (C === "L") { push("L"); while (i < t.length && !isCmd(t[i])) { const px = nextNum(), py = nextNum(); const ax = rel ? x + px : px, ay = rel ? y + py : py; const p = shiftAbs(ax, ay); push(p[0]); push(p[1]); x = ax; y = ay; } continue; } if (C === "H") { push("H"); while (i < t.length && !isCmd(t[i])) { const px = nextNum(); const ax = rel ? x + px : px; const p = shiftAbs(ax, y); push(p[0]); x = ax; } continue; } if (C === "V") { push("V"); while (i < t.length && !isCmd(t[i])) { const py = nextNum(); const ay = rel ? y + py : py; push(ay); y = ay; } continue; } if (C === "C") { push("C"); while (i < t.length && !isCmd(t[i])) { const x1 = nextNum(), y1 = nextNum(), x2 = nextNum(), y2 = nextNum(), x3 = nextNum(), y3 = nextNum(); const ax1 = rel ? x + x1 : x1, ay1 = rel ? y + y1 : y1; const ax2 = rel ? x + x2 : x2, ay2 = rel ? y + y2 : y2; const ax3 = rel ? x + x3 : x3, ay3 = rel ? y + y3 : y3; const p1 = shiftAbs(ax1, ay1); const p2 = shiftAbs(ax2, ay2); const p3 = shiftAbs(ax3, ay3); push(p1[0]); push(p1[1]); push(p2[0]); push(p2[1]); push(p3[0]); push(p3[1]); x = ax3; y = ay3; } continue; } if (C === "S") { push("S"); while (i < t.length && !isCmd(t[i])) { const x2 = nextNum(), y2 = nextNum(), x3 = nextNum(), y3 = nextNum(); const ax2 = rel ? x + x2 : x2, ay2 = rel ? y + y2 : y2; const ax3 = rel ? x + x3 : x3, ay3 = rel ? y + y3 : y3; const p2 = shiftAbs(ax2, ay2); const p3 = shiftAbs(ax3, ay3); push(p2[0]); push(p2[1]); push(p3[0]); push(p3[1]); x = ax3; y = ay3; } continue; } if (C === "Q") { push("Q"); while (i < t.length && !isCmd(t[i])) { const x1 = nextNum(), y1 = nextNum(), x2 = nextNum(), y2 = nextNum(); const ax1 = rel ? x + x1 : x1, ay1 = rel ? y + y1 : y1; const ax2 = rel ? x + x2 : x2, ay2 = rel ? y + y2 : y2; const p1 = shiftAbs(ax1, ay1); const p2 = shiftAbs(ax2, ay2); push(p1[0]); push(p1[1]); push(p2[0]); push(p2[1]); x = ax2; y = ay2; } continue; } if (C === "T") { push("T"); while (i < t.length && !isCmd(t[i])) { const x2 = nextNum(), y2 = nextNum(); const ax2 = rel ? x + x2 : x2, ay2 = rel ? y + y2 : y2; const p2 = shiftAbs(ax2, ay2); push(p2[0]); push(p2[1]); x = ax2; y = ay2; } continue; } if (C === "A") { push("A"); while (i < t.length && !isCmd(t[i])) { const rx = nextNum(), ry = nextNum(), rot = nextNum(), laf = nextNum(), sf = nextNum(), x2 = nextNum(), y2 = nextNum(); const ax2 = rel ? x + x2 : x2, ay2 = rel ? y + y2 : y2; const p2 = shiftAbs(ax2, ay2); push(rx); push(ry); push(rot); push(laf); push(sf); push(p2[0]); push(p2[1]); x = ax2; y = ay2; } continue; } if (C === "Z") { push("Z"); x = x0; y = y0; continue; } return d; } return out.join(" "); } function shiftRect(el, boxes) { const x = Number(el.getAttribute("x")); const y = Number(el.getAttribute("y")); const w = Number(el.getAttribute("width")); if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(w)) return; const h = Number(el.getAttribute("height")); const yMid = Number.isFinite(h) ? (y + h / 2) : y; const xL2 = x + dxForPoint(x, yMid, boxes); const xR = x + w; const xR2 = xR + dxForPoint(xR, yMid, boxes); el.setAttribute("x", Math.min(xL2, xR2)); el.setAttribute("width", Math.abs(xR2 - xL2)); } /****************************************************************** * Gradient shifting (zelfde als BU) ******************************************************************/ function parseFillUrl(fill) { const m = String(fill).match(/^url\((["']?)#([^)'" ]+)\1\)$/); return m ? m[2] : null; } function isClearlyUserSpace(gradEl) { const units = (gradEl.getAttribute("gradientUnits") || "").trim(); if (units === "userSpaceOnUse") return true; if (units) return false; for (const a of ["x1","y1","x2","y2","cx","cy","fx","fy","r"]) { const v = gradEl.getAttribute(a); if (v == null) continue; const n = Number(String(v).trim().replace(",", ".")); if (!Number.isFinite(n)) continue; if (n < -0.001 || n > 1.001) return true; } return false; } let gradCloneCounter = 0; function ensureGradientClone(svg, el, baseGradId) { const defs = ensureDefs(svg); if (!el.dataset.gradCloneId) { gradCloneCounter += 1; el.dataset.gradCloneId = `${baseGradId}__clone__${gradCloneCounter}`; } const cloneId = el.dataset.gradCloneId; let clone = defs.querySelector(`#${esc(cloneId)}`); if (!clone) { const orig = svg.querySelector(`#${esc(baseGradId)}`); if (!orig) return null; if (!isClearlyUserSpace(orig)) return null; clone = orig.cloneNode(true); clone.setAttribute("id", cloneId); clone.setAttribute("gradientUnits", "userSpaceOnUse"); clone.setAttribute("data-base-gradient-transform", orig.getAttribute("gradientTransform") || ""); defs.appendChild(clone); } el.setAttribute("fill", `url(#${cloneId})`); return clone; } function dxRepresentative(el, boxes) { const tag = el.tagName.toLowerCase(); if (tag === "rect") { const x = Number(el.getAttribute("x")); const y = Number(el.getAttribute("y")); const h = Number(el.getAttribute("height")); const yMid = Number.isFinite(h) ? (y + h / 2) : y; if (!Number.isFinite(x) || !Number.isFinite(yMid)) return 0; return dxForPoint(x, yMid, boxes); } if (tag === "polygon" || tag === "polyline") { const pts = (el.getAttribute("points") || "").trim().replace(/,/g, " ").split(/\s+/).filter(Boolean).map(Number); if (pts.length < 2 || pts.some(n => !Number.isFinite(n))) return 0; return dxForPoint(pts[0], pts[1], boxes); } if (tag === "path") { const d = el.getAttribute("d") || ""; const nums = d.match(/-?\d*\.?\d+(?:e[-+]?\d+)?/g); if (!nums || nums.length < 2) return 0; const x = Number(nums[0]), y = Number(nums[1]); if (!Number.isFinite(x) || !Number.isFinite(y)) return 0; return dxForPoint(x, y, boxes); } return 0; } function applyGradientShift(svg, el, dx) { const fill = el.getAttribute("fill"); if (!fill) return; const base = parseFillUrl(fill); if (!base) return; const clone = ensureGradientClone(svg, el, base); if (!clone) return; const baseT = (clone.getAttribute("data-base-gradient-transform") || "").trim(); const t = `translate(${dx},0)`; clone.setAttribute("gradientTransform", baseT ? `${baseT} ${t}` : t); } function applyShiftToGroup(svg, g, boxes) { if (!g || !boxes || !boxes.length) return; const gradDx = new Map(); g.querySelectorAll("[fill^='url(']").forEach(el => { const dx = dxRepresentative(el, boxes); if (dx) gradDx.set(el, dx); }); g.querySelectorAll("polygon").forEach(el => { const p = el.getAttribute("points"); if (p) el.setAttribute("points", shiftPointsAttr(p, boxes)); }); g.querySelectorAll("polyline").forEach(el => { const p = el.getAttribute("points"); if (p) el.setAttribute("points", shiftPointsAttr(p, boxes)); }); g.querySelectorAll("rect").forEach(el => shiftRect(el, boxes)); g.querySelectorAll("path").forEach(el => { const d = el.getAttribute("d"); if (d) el.setAttribute("d", shiftPathD(d, boxes)); }); for (const [el, dx] of gradDx.entries()) applyGradientShift(svg, el, dx); } function buildAndApplyShifts() { const svg = svgRoot(); if (!svg) return; hideNeverShow(svg); const n = vakCount(); // BI shiftboxen per vak const items = []; for (let i = 1; i <= n; i++) { const gL = svg.querySelector(`#${esc(`vak-${i}-BI-shiftbox-L`)}`); const gR = svg.querySelector(`#${esc(`vak-${i}-BI-shiftbox-R`)}`); items.push({ key: `BI:${i}`, gEl: gL, exprNow: () => prefixLeft(n, i) - Sponning() }); items.push({ key: `BI:${i}`, gEl: gR, exprNow: () => prefixLeft(n, i) + BI_DMI_scaled(n, i) + Sponning() }); } const boxesMap = buildBoxes(items); // Apply geometry shifts alleen op onze BI-variantgroepen for (let i = 1; i <= n; i++) { const boxes = boxesMap.get(`BI:${i}`) || []; if (!boxes.length) continue; for (const suf of BI_SUFFIXES) { const g = svg.querySelector(`#${esc(`vak-${i}-BI-${suf}`)}`); if (g) applyShiftToGroup(svg, g, boxes); } } // Verplaats shiftbox rects (blijven hidden) moveBoxes(boxesMap); hideNeverShow(svg); } function updateAll() { updateVisibility(); buildAndApplyShifts(); } /****************************************************************** * Bind listeners (Elementor friendly) ******************************************************************/ function bind(root = document) { // VVI selects root.querySelectorAll(`select[name^="form_fields["][name*="VVI"]`).forEach(el => { if (el.dataset.biBound === "1") return; el.dataset.biBound = "1"; el.addEventListener("change", updateAll); el.addEventListener("input", updateAll); }); // all numeric inputs (LI/RI/TI*/DMI*/sponning etc.) root.querySelectorAll(`input[id^="form-field-"]`).forEach(el => { if (el.dataset.biShiftBound === "1") return; el.dataset.biShiftBound = "1"; el.addEventListener("change", updateAll); el.addEventListener("input", updateAll); }); // vakken radios root.querySelectorAll(`input[type="radio"][name="form_fields[vakken]"]`).forEach(el => { if (el.dataset.biVakkenBound === "1") return; el.dataset.biVakkenBound = "1"; el.addEventListener("change", updateAll); el.addEventListener("input", updateAll); }); } function boot() { bind(document); updateAll(); const mo = new MutationObserver(muts => { for (const mu of muts) { mu.addedNodes.forEach(node => { if (!(node instanceof HTMLElement)) return; bind(node); }); } updateAll(); }); mo.observe(document.documentElement, { childList: true, subtree: true }); } if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", boot); else boot(); // Optional: reset baselines (als andere scripts eerst iets aanpassen) window.rebaseAlleVakkenBI = () => { const svg = svgRoot(); if (!svg) return; svg.querySelectorAll(`[id*="-BI-shiftbox-"] rect`).forEach(r => { r.dataset.baseX = String(Number(r.getAttribute("x") ?? "0")); delete r.dataset.baseExpr; }); updateAll(); }; })();
//// VAKVULLING BU/HS #ALLE-VAKKEN //// (() => { const SCALE = 3.84615384615; const SVG_ID = "alle-vakken"; // BU mapping (VVU -> BU variant) const VVU_TO_BU_SUFFIX = { "dubbelglas": "2glas-BU", "tripleglas": "3glas-BU", "paneel": "paneel", "glaspaneel": "glaspaneel", "deur": "deur", "kaderdeur": "kaderdeur", "dubbelkaderdeur": "dubbelkaderdeur", // ✅ deze 3 blijven in mapping, maar worden geometrisch als BU-BI bucket behandeld "dubbelglas binnen": "2glas-BI", "tripleglas binnen": "3glas-BI", "draaikiep": "draaikiep", "vul in": null, }; // HS mapping (HS-opties zitten óók in VVU) const VVU_TO_HS_MODE = { "linksschuivend": "LS", "rechtsschuivend": "RS", "linksrechtsschuivend": "LR", "ls": "LS", "rs": "RS", "lr": "LR", }; // ✅ BU split: BU-main vs BU-BI const BU_MAIN_SUFFIXES = ["2glas-BU","3glas-BU","paneel","glaspaneel","deur","kaderdeur","dubbelkaderdeur"]; const BU_BI_SUFFIXES = ["2glas-BI","3glas-BI","draaikiep"]; const HS_MODES = ["LS","RS","LR"]; const esc = (s) => CSS.escape(s); function svgRoot() { return document.querySelector(`svg#${esc(SVG_ID)}`); } function setDisp(el, v) { if (el) el.setAttribute("display", v); } function normalizeKey(v) { return String(v || "") .trim() .toLowerCase() .replace(/\s+/g, "") .replace(/_/g, "") .replace(/-/g, ""); } function vakCount() { const checked = document.querySelector('input[type="radio"][name="form_fields[vakken]"]:checked'); if (checked && checked.value) { const m = String(checked.value).match(/([1-5])/); if (m) return Number(m[1]); } return 5; } function toNumericValue(v) { const s = (v ?? "").toString().trim().replace(",", "."); const n = Number(s); return Number.isFinite(n) ? n : 0; } // Bestaande losse velden op ID (DMU, sponning, nokdiktes, etc.) function readInputValueById(id) { const el = document.getElementById(id); if (!el) return null; return toNumericValue(el.value); } // Nieuw: leest numerieke waarden uit: // 1) input/select met id="form-field-" // 2) radio groep name="form_fields[]" // 3) select name="form_fields[]" function readNumericField(fieldKey) { // input/select met ID const idEl = document.getElementById(`form-field-${fieldKey}`); if (idEl) { return toNumericValue(idEl.value); } // radio groep const radio = document.querySelector(`input[type="radio"][name="form_fields[${fieldKey}]"]:checked`); if (radio) { const raw = radio.getAttribute("data-original-value") || radio.value; return toNumericValue(raw); } // select op name const sel = document.querySelector(`select[name="form_fields[${fieldKey}]"]`); if (sel) { return toNumericValue(sel.value); } return null; } // Prefer prefixA, else prefixB, else 0 function numField(prefixA, prefixB, suffix) { const a = readNumericField(`${prefixA}${suffix}`); if (a != null) return a; const b = readNumericField(`${prefixB}${suffix}`); if (b != null) return b; return 0; } // VVU select by name form_fields[VVU] function getVVU(n, i) { const sel = document.querySelector(`select[name="form_fields[${n}VVU${i}]"]`); return sel ? String(sel.value || "") : "vul in"; } // ✅ Dorpeltype radio (pakt liefst data-original-value, anders value) function getDorpelType() { const checked = document.querySelector('input[type="radio"][name="form_fields[dorpeltype]"]:checked'); if (!checked) return null; return (checked.getAttribute("data-original-value") || checked.value || "").toString().trim(); } /****************************************************************** * ✅ NEW: HS verdiept (nok) inputs ******************************************************************/ function getRadioValueByName(name) { const el = document.querySelector(`input[type="radio"][name="${name}"]:checked`); return el ? String(el.value || "").trim().toLowerCase() : ""; } function isVerdieptVJa() { return getRadioValueByName("form_fields[verdiept_v]") === "ja"; } function isVerdieptSJa() { return getRadioValueByName("form_fields[verdiept_s]") === "ja"; } // ✅ optie A: nokdiktes zijn mm -> schalen naar SVG-units function nokV() { return (readInputValueById("form-field-nokdiktevast") ?? 0); } function nokS() { return (readInputValueById("form-field-nokdikteschuif") ?? 0); } /****************************************************************** * NEVER SHOW elements (shiftboxes + BU-LR placeholders) ******************************************************************/ function hideNeverShow(svg) { // BU-LR placeholders for (let i = 1; i <= 5; i++) { setDisp(svg.querySelector(`#${esc(`vak-${i}-BU-LR`)}`), "none"); } // All BU shiftboxes svg.querySelectorAll(`[id*="-BU-shiftbox-"]`).forEach(el => setDisp(el, "none")); // All HS shiftboxes svg.querySelectorAll(`[id*="-HS-shiftbox-"]`).forEach(el => setDisp(el, "none")); } /****************************************************************** * VISIBILITY: only whitelist BU & HS groups ******************************************************************/ function showGroupAndNestedGs(groupEl) { if (!groupEl) return; const deny = (id) => { const s = String(id || ""); return s.includes("-shiftbox-") || s.endsWith("-BU-LR") || s.includes("-BU-LR-"); }; if (!deny(groupEl.id)) setDisp(groupEl, "block"); groupEl.querySelectorAll("g").forEach(g => { if (deny(g.id)) setDisp(g, "none"); else setDisp(g, "block"); }); } function hideAllBU(svg, i) { for (const suf of BU_MAIN_SUFFIXES) setDisp(svg.querySelector(`#${esc(`vak-${i}-BU-${suf}`)}`), "none"); for (const suf of BU_BI_SUFFIXES) setDisp(svg.querySelector(`#${esc(`vak-${i}-BU-${suf}`)}`), "none"); } function hideAllHS(svg, i) { for (const m of HS_MODES) setDisp(svg.querySelector(`#${esc(`vak-${i}-HS-${m}`)}`), "none"); } function updateVisibility() { const svg = svgRoot(); if (!svg) return; hideNeverShow(svg); const n = vakCount(); // reset only whitelisted for (let i = 1; i <= 5; i++) { hideAllBU(svg, i); hideAllHS(svg, i); } for (let i = 1; i <= n; i++) { const raw = getVVU(n, i); const key = normalizeKey(raw); const hsMode = VVU_TO_HS_MODE[key] || null; let buSuffix = Object.prototype.hasOwnProperty.call(VVU_TO_BU_SUFFIX, raw) ? VVU_TO_BU_SUFFIX[raw] : null; // ✅ extra optie: als dorpeltype = KVTPKU, dan kaderdeur -> dubbelkaderdeur const dorpelType = getDorpelType(); if (dorpelType === "KVTPKU" && raw === "kaderdeur") { buSuffix = "dubbelkaderdeur"; } if (hsMode) { const g = svg.querySelector(`#${esc(`vak-${i}-HS-${hsMode}`)}`); showGroupAndNestedGs(g); } else if (buSuffix) { const g = svg.querySelector(`#${esc(`vak-${i}-BU-${buSuffix}`)}`); showGroupAndNestedGs(g); } } hideNeverShow(svg); } /****************************************************************** * SHIFTBOX ENGINE (delta vs baseline) ******************************************************************/ function desiredXFromDelta(rect, exprNow) { const curX = Number(rect.getAttribute("x") ?? "0"); if (rect.dataset.baseX == null) rect.dataset.baseX = String(curX); if (rect.dataset.baseExpr == null) rect.dataset.baseExpr = String(exprNow); const baseX = Number(rect.dataset.baseX); const baseExpr = Number(rect.dataset.baseExpr); return baseX + (exprNow - baseExpr); } function boxFromShiftRect(rect, exprNow) { const curX = Number(rect.getAttribute("x") ?? "0"); const curY = Number(rect.getAttribute("y") ?? "0"); const w = Number(rect.getAttribute("width") ?? "0"); const h = Number(rect.getAttribute("height") ?? "0"); const desiredX = desiredXFromDelta(rect, exprNow); return { rect, desiredX, dxInc: desiredX - curX, minX: curX, maxX: curX + w, minY: curY, maxY: curY + h, }; } function buildBoxes(items) { const map = new Map(); // key -> boxes[] for (const it of items) { if (!it.gEl) continue; const r = it.gEl.querySelector("rect"); if (!r) continue; const b = boxFromShiftRect(r, it.exprNow()); if (!map.has(it.key)) map.set(it.key, []); map.get(it.key).push(b); } return map; } function moveBoxes(map) { for (const boxes of map.values()) { for (const b of boxes) b.rect.setAttribute("x", b.desiredX); } } function dxForPoint(x, y, boxes) { let dx = 0; for (const b of boxes) { if (x >= b.minX && x <= b.maxX && y >= b.minY && y <= b.maxY) dx += b.dxInc; } return dx; } /****************************************************************** * Geometry shifters ******************************************************************/ function shiftPointsAttr(pointsStr, boxes) { const nums = pointsStr.trim().replace(/,/g, " ").split(/\s+/).filter(Boolean).map(Number); if (nums.length < 2 || nums.some(n => !Number.isFinite(n))) return pointsStr; const out = []; for (let i = 0; i < nums.length - 1; i += 2) { const x = nums[i], y = nums[i + 1]; out.push(String(x + dxForPoint(x, y, boxes)), String(y)); } return out.join(" "); } function tokenizePath(d) { return d.match(/[a-zA-Z]|-?\d*\.?\d+(?:e[-+]?\d+)?/g) || []; } function shiftPathD(d, boxes) { const t = tokenizePath(d); if (!t.length) return d; let i = 0, cmd = "", x = 0, y = 0, x0 = 0, y0 = 0; const out = []; const isCmd = tok => /^[a-zA-Z]$/.test(tok); const nextNum = () => Number(t[i++]); const push = v => out.push(v); const shiftAbs = (px, py) => [px + dxForPoint(px, py, boxes), py]; while (i < t.length) { if (isCmd(t[i])) cmd = t[i++]; const rel = (cmd === cmd.toLowerCase()); const C = cmd.toUpperCase(); if (C === "M") { push("M"); const px = nextNum(), py = nextNum(); const ax = rel ? x + px : px, ay = rel ? y + py : py; const p = shiftAbs(ax, ay); push(p[0]); push(p[1]); x = ax; y = ay; x0 = ax; y0 = ay; while (i < t.length && !isCmd(t[i])) { push("L"); const lx = nextNum(), ly = nextNum(); const ax2 = rel ? x + lx : lx, ay2 = rel ? y + ly : ly; const p2 = shiftAbs(ax2, ay2); push(p2[0]); push(p2[1]); x = ax2; y = ay2; } continue; } if (C === "L") { push("L"); while (i < t.length && !isCmd(t[i])) { const px = nextNum(), py = nextNum(); const ax = rel ? x + px : px, ay = rel ? y + py : py; const p = shiftAbs(ax, ay); push(p[0]); push(p[1]); x = ax; y = ay; } continue; } if (C === "H") { push("H"); while (i < t.length && !isCmd(t[i])) { const px = nextNum(); const ax = rel ? x + px : px; const p = shiftAbs(ax, y); push(p[0]); x = ax; } continue; } if (C === "V") { push("V"); while (i < t.length && !isCmd(t[i])) { const py = nextNum(); const ay = rel ? y + py : py; push(ay); y = ay; } continue; } if (C === "C") { push("C"); while (i < t.length && !isCmd(t[i])) { const x1 = nextNum(), y1 = nextNum(), x2 = nextNum(), y2 = nextNum(), x3 = nextNum(), y3 = nextNum(); const ax1 = rel ? x + x1 : x1, ay1 = rel ? y + y1 : y1; const ax2 = rel ? x + x2 : x2, ay2 = rel ? y + y2 : y2; const ax3 = rel ? x + x3 : x3, ay3 = rel ? y + y3 : y3; const p1 = shiftAbs(ax1, ay1); const p2 = shiftAbs(ax2, ay2); const p3 = shiftAbs(ax3, ay3); push(p1[0]); push(p1[1]); push(p2[0]); push(p2[1]); push(p3[0]); push(p3[1]); x = ax3; y = ay3; } continue; } if (C === "S") { push("S"); while (i < t.length && !isCmd(t[i])) { const x2 = nextNum(), y2 = nextNum(), x3 = nextNum(), y3 = nextNum(); const ax2 = rel ? x + x2 : x2, ay2 = rel ? y + y2 : y2; const ax3 = rel ? x + x3 : x3, ay3 = rel ? y + y3 : y3; const p2 = shiftAbs(ax2, ay2); const p3 = shiftAbs(ax3, ay3); push(p2[0]); push(p2[1]); push(p3[0]); push(p3[1]); x = ax3; y = ay3; } continue; } if (C === "Q") { push("Q"); while (i < t.length && !isCmd(t[i])) { const x1 = nextNum(), y1 = nextNum(), x2 = nextNum(), y2 = nextNum(); const ax1 = rel ? x + x1 : x1, ay1 = rel ? y + y1 : y1; const ax2 = rel ? x + x2 : x2, ay2 = rel ? y + y2 : y2; const p1 = shiftAbs(ax1, ay1); const p2 = shiftAbs(ax2, ay2); push(p1[0]); push(p1[1]); push(p2[0]); push(p2[1]); x = ax2; y = ay2; } continue; } if (C === "T") { push("T"); while (i < t.length && !isCmd(t[i])) { const x2 = nextNum(), y2 = nextNum(); const ax2 = rel ? x + x2 : x2, ay2 = rel ? y + y2 : y2; const p2 = shiftAbs(ax2, ay2); push(p2[0]); push(p2[1]); x = ax2; y = ay2; } continue; } if (C === "A") { push("A"); while (i < t.length && !isCmd(t[i])) { const rx = nextNum(), ry = nextNum(), rot = nextNum(), laf = nextNum(), sf = nextNum(), x2 = nextNum(), y2 = nextNum(); const ax2 = rel ? x + x2 : x2, ay2 = rel ? y + y2 : y2; const p2 = shiftAbs(ax2, ay2); push(rx); push(ry); push(rot); push(laf); push(sf); push(p2[0]); push(p2[1]); x = ax2; y = ay2; } continue; } if (C === "Z") { push("Z"); x = x0; y = y0; continue; } return d; } return out.join(" "); } function shiftRect(el, boxes) { const x = Number(el.getAttribute("x")); const y = Number(el.getAttribute("y")); const w = Number(el.getAttribute("width")); if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(w)) return; const h = Number(el.getAttribute("height")); const yMid = Number.isFinite(h) ? (y + h / 2) : y; const xL2 = x + dxForPoint(x, yMid, boxes); const xR = x + w; const xR2 = xR + dxForPoint(xR, yMid, boxes); el.setAttribute("x", Math.min(xL2, xR2)); el.setAttribute("width", Math.abs(xR2 - xL2)); } // ---- gradients ---- function ensureDefs(svg) { let defs = svg.querySelector("defs"); if (!defs) { defs = document.createElementNS("http://www.w3.org/2000/svg", "defs"); svg.insertBefore(defs, svg.firstChild); } return defs; } function parseFillUrl(fill) { const m = String(fill).match(/^url\((["']?)#([^)'" ]+)\1\)$/); return m ? m[2] : null; } function getNumericAttr(el, name) { const v = el.getAttribute(name); if (v == null) return null; const n = Number(String(v).trim().replace(",", ".")); return Number.isFinite(n) ? n : null; } function isClearlyUserSpace(gradEl) { const units = (gradEl.getAttribute("gradientUnits") || "").trim(); if (units === "userSpaceOnUse") return true; if (units) return false; for (const a of ["x1","y1","x2","y2","cx","cy","fx","fy","r"]) { const n = getNumericAttr(gradEl, a); if (n == null) continue; if (n < -0.001 || n > 1.001) return true; } return false; } let gradCloneCounter = 0; function ensureGradientClone(svg, el, baseGradId) { const defs = ensureDefs(svg); if (!el.dataset.gradCloneId) { gradCloneCounter += 1; el.dataset.gradCloneId = `${baseGradId}__clone__${gradCloneCounter}`; } const cloneId = el.dataset.gradCloneId; let clone = defs.querySelector(`#${esc(cloneId)}`); if (!clone) { const orig = svg.querySelector(`#${esc(baseGradId)}`); if (!orig) return null; if (!isClearlyUserSpace(orig)) return null; clone = orig.cloneNode(true); clone.setAttribute("id", cloneId); clone.setAttribute("gradientUnits", "userSpaceOnUse"); clone.setAttribute("data-base-gradient-transform", orig.getAttribute("gradientTransform") || ""); defs.appendChild(clone); } el.setAttribute("fill", `url(#${cloneId})`); return clone; } function dxRepresentative(el, boxes) { const tag = el.tagName.toLowerCase(); if (tag === "rect") { const x = Number(el.getAttribute("x")); const y = Number(el.getAttribute("y")); const h = Number(el.getAttribute("height")); const yMid = Number.isFinite(h) ? (y + h / 2) : y; if (!Number.isFinite(x) || !Number.isFinite(yMid)) return 0; return dxForPoint(x, yMid, boxes); } if (tag === "polygon" || tag === "polyline") { const pts = (el.getAttribute("points") || "").trim().replace(/,/g, " ").split(/\s+/).filter(Boolean).map(Number); if (pts.length < 2 || pts.some(n => !Number.isFinite(n))) return 0; return dxForPoint(pts[0], pts[1], boxes); } if (tag === "path") { const d = el.getAttribute("d") || ""; const nums = d.match(/-?\d*\.?\d+(?:e[-+]?\d+)?/g); if (!nums || nums.length < 2) return 0; const x = Number(nums[0]), y = Number(nums[1]); if (!Number.isFinite(x) || !Number.isFinite(y)) return 0; return dxForPoint(x, y, boxes); } return 0; } function applyGradientShift(svg, el, dx) { const fill = el.getAttribute("fill"); if (!fill) return; const base = parseFillUrl(fill); if (!base) return; const clone = ensureGradientClone(svg, el, base); if (!clone) return; const baseT = (clone.getAttribute("data-base-gradient-transform") || "").trim(); const t = `translate(${dx},0)`; clone.setAttribute("gradientTransform", baseT ? `${baseT} ${t}` : t); } function applyShiftToGroup(svg, g, boxes) { if (!g || !boxes || !boxes.length) return; const gradDx = new Map(); g.querySelectorAll("[fill^='url(']").forEach(el => { const dx = dxRepresentative(el, boxes); if (dx) gradDx.set(el, dx); }); g.querySelectorAll("polygon").forEach(el => { const p = el.getAttribute("points"); if (p) el.setAttribute("points", shiftPointsAttr(p, boxes)); }); g.querySelectorAll("polyline").forEach(el => { const p = el.getAttribute("points"); if (p) el.setAttribute("points", shiftPointsAttr(p, boxes)); }); g.querySelectorAll("rect").forEach(el => shiftRect(el, boxes)); g.querySelectorAll("path").forEach(el => { const d = el.getAttribute("d"); if (d) el.setAttribute("d", shiftPathD(d, boxes)); }); for (const [el, dx] of gradDx.entries()) { applyGradientShift(svg, el, dx); } } /****************************************************************** * SHIFT CALCS ******************************************************************/ // BU uses current n* fields (fallback to 5*) function BU_LU(n) { return numField(`${n}`, "5", "LU"); } function BU_TU(n, i) { return numField(`${n}`, "5", `TU${i}`); } function BU_DMU(n, i) { return (numField(`${n}`, "5", `DMU${i}`) / SCALE); } function buPrefixLeft(n, i) { let x = BU_LU(n); if (i >= 2) x += BU_DMU(n,1) + BU_TU(n,1); if (i >= 3) x += BU_DMU(n,2) + BU_TU(n,2); if (i >= 4) x += BU_DMU(n,3) + BU_TU(n,3); if (i >= 5) x += BU_DMU(n,4) + BU_TU(n,4); return x; } // ✅ dynamic sponning per vak, based on current VVU function sponningBUDefault() { return readInputValueById("form-field-BU_sponningbreedte") ?? 0; } function sponningWissel() { return readInputValueById("form-field-wisselsponningbreedte") ?? 0; } function sponningDraaikiep() { return readInputValueById("form-field-draaikiepsponningbreedte") ?? 0; } function isVVU_BI(raw) { return raw === "dubbelglas binnen" || raw === "tripleglas binnen"; } function isVVU_Draaikiep(raw) { return raw === "draaikiep"; } function sponningForVak(n, i) { const raw = getVVU(n, i); if (isVVU_BI(raw)) return sponningWissel(); if (isVVU_Draaikiep(raw)) return sponningDraaikiep(); return sponningBUDefault(); } // HS uses current n* fields (fallback to 5*) function HS_LU(n) { return numField(`${n}`, "5", "LU"); } function HS_TU(n, i) { return numField(`${n}`, "5", `TU${i}`); } function HS_DMU(n, i) { return (numField(`${n}`, "5", `DMU${i}`) / SCALE); } // scaled for positions function hsPrefixLeft(n, i) { let x = HS_LU(n); if (i >= 2) x += HS_DMU(n,1) + HS_TU(n,1); if (i >= 3) x += HS_DMU(n,2) + HS_TU(n,2); if (i >= 4) x += HS_DMU(n,3) + HS_TU(n,3); if (i >= 5) x += HS_DMU(n,4) + HS_TU(n,4); return x; } function buildAndApplyShifts() { const svg = svgRoot(); if (!svg) return; hideNeverShow(svg); const n = vakCount(); // ---- BU shiftboxes (vak-i-BU-shiftbox-L/R) with dynamic sponning const buItems = []; for (let i = 1; i <= n; i++) { const gL = svg.querySelector(`#${esc(`vak-${i}-BU-shiftbox-L`)}`); const gR = svg.querySelector(`#${esc(`vak-${i}-BU-shiftbox-R`)}`); buItems.push({ key:`BU:${i}`, gEl:gL, exprNow: () => buPrefixLeft(n,i) - sponningForVak(n,i) }); buItems.push({ key:`BU:${i}`, gEl:gR, exprNow: () => buPrefixLeft(n,i) + BU_DMU(n,i) + sponningForVak(n,i) }); } const buBoxes = buildBoxes(buItems); // ---- HS shiftboxes (incl. mm-threshold corrections + verdiept nok-correcties) const hsItems = []; for (let i = 1; i <= n; i++) { const left = () => hsPrefixLeft(n, i); const w = () => HS_DMU(n, i); // scaled for positions const dmuMm = () => numField(`${n}`, "5", `DMU${i}`); // raw mm for thresholds const corr200 = () => (dmuMm() >= 200 ? 31.5 : 0); // for L/R const corr400 = () => (dmuMm() >= 400 ? 47 : 0); // for L1/R1/T1/T3 // Determine HS mode for this vak const rawVVU = getVVU(n, i); const mode = VVU_TO_HS_MODE[normalizeKey(rawVVU)] || null; // ✅ verdiept logic (scaled) const vOn = () => isVerdieptVJa(); const sOn = () => isVerdieptSJa(); const nv = () => nokV(); const ns = () => nokS(); // LS/RS set (L, T, R) // Rules: // - LS + verdiept_v=ja => L - nokV // - RS + verdiept_v=ja => R + nokV // - LS + verdiept_s=ja => R + nokS // - RS + verdiept_s=ja => L - nokS const extraL = () => { let dx = 0; if (mode === "LS" && vOn()) dx += -nv(); if (mode === "RS" && sOn()) dx += -ns(); return dx; }; const extraR = () => { let dx = 0; if (mode === "RS" && vOn()) dx += +nv(); if (mode === "LS" && sOn()) dx += +ns(); return dx; }; hsItems.push({ key:`HS_LTR:${i}`, gEl: svg.querySelector(`#${esc(`vak-${i}-HS-shiftbox-L`)}`), exprNow: () => left() + corr200() + extraL() }); hsItems.push({ key:`HS_LTR:${i}`, gEl: svg.querySelector(`#${esc(`vak-${i}-HS-shiftbox-T`)}`), exprNow: () => left() + (w()/4)*2 }); hsItems.push({ key:`HS_LTR:${i}`, gEl: svg.querySelector(`#${esc(`vak-${i}-HS-shiftbox-R`)}`), exprNow: () => left() + w() - corr200() + extraR() }); // LR set (L1, T1..T3, R1) // Rule: // - LR + verdiept_v=ja => L1 - nokV, R1 + nokV const extraL1 = () => (mode === "LR" && vOn()) ? (-nv()) : 0; const extraR1 = () => (mode === "LR" && vOn()) ? (+nv()) : 0; hsItems.push({ key:`HS_LR:${i}`, gEl: svg.querySelector(`#${esc(`vak-${i}-HS-shiftbox-L1`)}`), exprNow: () => left() + corr400() + extraL1() }); hsItems.push({ key:`HS_LR:${i}`, gEl: svg.querySelector(`#${esc(`vak-${i}-HS-shiftbox-T1`)}`), exprNow: () => left() + (w()/4)*1 + (corr400()/2) }); hsItems.push({ key:`HS_LR:${i}`, gEl: svg.querySelector(`#${esc(`vak-${i}-HS-shiftbox-T2`)}`), exprNow: () => left() + (w()/4)*2 }); hsItems.push({ key:`HS_LR:${i}`, gEl: svg.querySelector(`#${esc(`vak-${i}-HS-shiftbox-T3`)}`), exprNow: () => left() + (w()/4)*3 - (corr400()/2) }); hsItems.push({ key:`HS_LR:${i}`, gEl: svg.querySelector(`#${esc(`vak-${i}-HS-shiftbox-R1`)}`), exprNow: () => left() + w() - corr400() + extraR1() }); } const hsBoxes = buildBoxes(hsItems); // Apply BU geometry to both BU buckets (BU-main + BU-BI) for (let i = 1; i <= n; i++) { const boxes = buBoxes.get(`BU:${i}`) || []; if (!boxes.length) continue; for (const suf of BU_MAIN_SUFFIXES) { const g = svg.querySelector(`#${esc(`vak-${i}-BU-${suf}`)}`); if (g) applyShiftToGroup(svg, g, boxes); } for (const suf of BU_BI_SUFFIXES) { const g = svg.querySelector(`#${esc(`vak-${i}-BU-${suf}`)}`); if (g) applyShiftToGroup(svg, g, boxes); } } // Apply HS geometry for (let i = 1; i <= n; i++) { const bLTR = hsBoxes.get(`HS_LTR:${i}`) || []; const bLR = hsBoxes.get(`HS_LR:${i}`) || []; if (bLTR.length) { const gLS = svg.querySelector(`#${esc(`vak-${i}-HS-LS`)}`); const gRS = svg.querySelector(`#${esc(`vak-${i}-HS-RS`)}`); if (gLS) applyShiftToGroup(svg, gLS, bLTR); if (gRS) applyShiftToGroup(svg, gRS, bLTR); } if (bLR.length) { const gLR = svg.querySelector(`#${esc(`vak-${i}-HS-LR`)}`); if (gLR) applyShiftToGroup(svg, gLR, bLR); } } // Move shiftboxes (remain hidden) moveBoxes(buBoxes); moveBoxes(hsBoxes); hideNeverShow(svg); } function updateAll() { updateVisibility(); buildAndApplyShifts(); } /****************************************************************** * Bind listeners ******************************************************************/ function bind(root = document) { // VVU selects root.querySelectorAll(`select[name^="form_fields["][name*="VVU"]`).forEach(el => { if (el.dataset.avBound === "1") return; el.dataset.avBound = "1"; el.addEventListener("change", updateAll); el.addEventListener("input", updateAll); }); // alle form-field inputs/selects met ID // (includes: DMU, BU_sponningbreedte, wisselsponningbreedte, // draaikiepsponningbreedte, nokdiktes, etc.) root.querySelectorAll(`input[id^="form-field-"], select[id^="form-field-"]`).forEach(el => { if (el.dataset.avShiftBound === "1") return; el.dataset.avShiftBound = "1"; el.addEventListener("change", updateAll); el.addEventListener("input", updateAll); }); // ✅ nieuwe LU/TU radio's root.querySelectorAll(`input[type="radio"][name^="form_fields["]`).forEach(el => { const name = el.getAttribute("name") || ""; // Alleen LU en TU oppakken; DMU blijft zoals hij is if (!/^form_fields\[(?:[1-5]LU|[1-5]TU[1-5])\]$/.test(name)) return; if (el.dataset.avLuTuRadioBound === "1") return; el.dataset.avLuTuRadioBound = "1"; el.addEventListener("change", updateAll); el.addEventListener("input", updateAll); }); // vakken radio root.querySelectorAll(`input[type="radio"][name="form_fields[vakken]"]`).forEach(el => { if (el.dataset.avVakkenBound === "1") return; el.dataset.avVakkenBound = "1"; el.addEventListener("change", updateAll); el.addEventListener("input", updateAll); }); // dorpeltype radio root.querySelectorAll(`input[type="radio"][name="form_fields[dorpeltype]"]`).forEach(el => { if (el.dataset.avDorpeltypeBound === "1") return; el.dataset.avDorpeltypeBound = "1"; el.addEventListener("change", updateAll); el.addEventListener("input", updateAll); }); // ✅ verdiept radios root.querySelectorAll(`input[type="radio"][name="form_fields[verdiept_v]"], input[type="radio"][name="form_fields[verdiept_s]"]`).forEach(el => { if (el.dataset.avVerdieptBound === "1") return; el.dataset.avVerdieptBound = "1"; el.addEventListener("change", updateAll); el.addEventListener("input", updateAll); }); // ✅ Explicit listeners – redundant but harmless ["form-field-wisselsponningbreedte","form-field-draaikiepsponningbreedte"].forEach(id => { const el = document.getElementById(id); if (!el || el.dataset.avExplicitSponningBound === "1") return; el.dataset.avExplicitSponningBound = "1"; el.addEventListener("change", updateAll); el.addEventListener("input", updateAll); }); } function boot() { bind(document); updateAll(); const mo = new MutationObserver(muts => { for (const mu of muts) { mu.addedNodes.forEach(node => { if (!(node instanceof HTMLElement)) return; bind(node); }); } updateAll(); }); mo.observe(document.documentElement, { childList: true, subtree: true }); } if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", boot); } else { boot(); } // Optional: rebase if other scripts set a new "nulstand" window.rebaseAlleVakken = () => { const svg = svgRoot(); if (!svg) return; svg.querySelectorAll(`[id*="-shiftbox-"] rect`).forEach(r => { r.dataset.baseX = String(Number(r.getAttribute("x") ?? "0")); delete r.dataset.baseExpr; }); updateAll(); }; })();
//// PREVENT ENTER KEY SUBMIT //// document.addEventListener("DOMContentLoaded", () => { const submitBtn = document.getElementById("SUBMITBUTTON"); if (!submitBtn) return; const form = submitBtn.closest("form"); if (!form) return; // Debounce zodat we niet 100x per seconde evalueren let rafId = null; const scheduleUpdate = () => { if (rafId) return; rafId = requestAnimationFrame(() => { rafId = null; update(); }); }; function setSubmitVisible(ok) { // écht verbergen/tonen submitBtn.style.display = ok ? "" : "none"; submitBtn.disabled = !ok; submitBtn.setAttribute("aria-disabled", String(!ok)); } function getCheckedValue(name) { const el = form.querySelector(`input[name="${name}"]:checked`); return el ? el.value : null; } function getVakkenCount() { const v = getCheckedValue("form_fields[vakken]"); // "1-vaks" .. "5-vaks" const m = (v || "").match(/^([1-5])\-vaks$/); return m ? parseInt(m[1], 10) : null; } function getDorpelType() { return getCheckedValue("form_fields[dorpeltype]"); // BI / BUVD / HS / KVT } function isVisible(el) { // betrouwbaar voor display:none (Elementor verbergt vaak via display) return !!el && el.offsetParent !== null; } function prefixForType(type) { return type === "BI" ? "VVI" : "VVU"; } function isSubmitAllowed() { const type = getDorpelType(); const n = getVakkenCount(); if (!type || !n) return false; const prefix = prefixForType(type); for (let i = 1; i <= n; i++) { const fieldId = `${n}${prefix}${i}`; // bv. 2VVU1 of 3VVI2 const sel = form.querySelector(`select[name="form_fields[${fieldId}]"]`); if (!sel) return false; if (!isVisible(sel)) return false; if (sel.value === "vul in") return false; } return true; } function update() { setSubmitVisible(isSubmitAllowed()); } // Start verborgen zodat hij niet “flasht” setSubmitVisible(false); update(); // Update bij elke wijziging form.addEventListener("change", scheduleUpdate, true); form.addEventListener("input", scheduleUpdate, true); // Extra safety: als iemand toch klikt via keyboard/JS submitBtn.addEventListener("click", (e) => { if (!isSubmitAllowed()) { e.preventDefault(); scheduleUpdate(); } }); // Optioneel: Elementor kan velden tonen/verbergen zonder input/change // -> lichte polling (veilig, niet zwaar) setInterval(scheduleUpdate, 300); });
//// GLOBAL FLAG //// let shouldSubmit = true; //// PREVENT ENTER KEY SUBMIT //// document.addEventListener("DOMContentLoaded", function () { const form = document.querySelector("form[name='TIMBERPLUS_BESTELFORMULIER']"); const submitButton = document.getElementById("SUBMITBUTTON"); if (form) { // Prevent Enter key from submitting form.addEventListener("keydown", function (event) { if (event.key === "Enter") { event.preventDefault(); } }); } if (submitButton && form) { // MAIN SUBMIT BUTTON CLICK HANDLER submitButton.addEventListener("click", function (event) { shouldSubmit = true; // Reset flag every time ////// VALIDATE DROPDOWNS /////// const dropdowns = form.querySelectorAll("select[name*='VVU'], select[name*='VVI']"); for (let dropdown of dropdowns) { const isHidden = dropdown.closest('.cfef-hidden'); if (!isHidden) { if (dropdown.value === 'vul in') { event.preventDefault(); alert("Niet alle vakvullingen zijn ingevuld."); shouldSubmit = false; return; } } } ////// VALIDATE SLUITPOT /////// const sluitpotRadios = form.querySelectorAll("input[name='form_fields[sluitpot]']"); let sluitpotValue = ''; sluitpotRadios.forEach(radio => { if (radio.checked) { sluitpotValue = radio.value; } }); if (sluitpotValue === 'ja') { const xSluitpot = document.getElementById("form-field-X_sluitpot"); const ySluitpot = document.getElementById("form-field-Y_sluitpot"); if (!xSluitpot.value.trim() || !ySluitpot.value.trim()) { event.preventDefault(); alert("Vul de hartafstand waarden in als 'Ja' is geselecteerd voor sluitpot."); shouldSubmit = false; return; } } ////// CHECK BROWSER FORM VALIDATION /////// if (!form.checkValidity()) { form.reportValidity(); shouldSubmit = false; return; } ////// FADE OUT SCREEN ONLY IF ALL VALIDATIONS PASS /////// if (shouldSubmit) { setTimeout(() => { document.body.style.transition = "opacity 1s ease-in-out"; document.body.style.opacity = "0"; setTimeout(() => { document.body.style.display = "none"; }, 1020); }, 20); } }); } });
//// TOOLTIP //// document.addEventListener("DOMContentLoaded", function () { function checkDropdowns() { // Commenting out sessionStorage limit for dtstimberplus //let popupShown = sessionStorage.getItem("popupShown"); //if (popupShown) return; let dropdowns = document.querySelectorAll("select[name*='VVU']:not(.cfef-hidden), select[name*='VVI']:not(.cfef-hidden)"); let visibleDropdowns = Array.from(dropdowns).filter(select => select.offsetParent !== null); let allFilled = visibleDropdowns.every(select => select.value.toLowerCase() !== 'vul in'); if (allFilled && visibleDropdowns.length > 0) { showPopup(); simulateMouseMovement(); } } function showPopup() { let popup = document.getElementById("tooltip-popup"); if (popup) { setTimeout(() => { popup.style.display = "block"; }, 50); // ⬅️ Delay forces Firefox to properly register the display change document.addEventListener("click", hidePopup, { once: true }); setTimeout(hidePopup, 5000); } } function hidePopup() { let popup = document.getElementById("tooltip-popup"); if (popup) { popup.style.display = "none"; } } function simulateMouseMovement() { let hitboxes = document.querySelectorAll("svg#alle-vakken .hitbox"); if (hitboxes.length === 0) return; let positions = Array.from(hitboxes).map(hitbox => { let rect = hitbox.getBoundingClientRect(); return { x: rect.x, width: rect.width }; }); positions.sort((a, b) => a.x - b.x); // Sort hitboxes by X position let startX = positions[0].x; let endX = positions[positions.length - 1].x + positions[positions.length - 1].width; let svg = document.querySelector("svg#alle-vakken"); if (!svg) return; let cursor = document.createElement("div"); cursor.innerHTML = ``; cursor.style.position = "fixed"; cursor.style.zIndex = "10000"; document.body.appendChild(cursor); let duration = 2500; // 1.5 seconds per movement let frameRate = 60; // Smooth animation let frameTime = 1000 / frameRate; let steps = (duration / frameTime); function easeInOutQuad(t) { return t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2; } function animateCursor(start, end, callback) { let step = 0; let moveCursor = setInterval(() => { let progress = easeInOutQuad(step / steps); let currentX = start + (end - start) * progress + 100; let currentY = svg.getBoundingClientRect().top + 20 + 30; cursor.style.left = `${currentX}px`; cursor.style.top = `${currentY}px`; step++; if (step > steps) { clearInterval(moveCursor); if (callback) callback(); } }, frameTime); } animateCursor(startX, endX, () => { animateCursor(endX, startX, () => { document.body.removeChild(cursor); }); }); } document.addEventListener("change", function (event) { if (event.target.matches("select[name*='VVU'], select[name*='VVI']")) { checkDropdowns(); } }); });

WEL OF GEEN VOORSPONNING BI

WEL OF GEEN VOORSPONNING BU

BLOCK SUMBIT ON VUL IN

Kozijnmerk check

BOORGAT-MARGE CORRECTIE

SPINNER

SLUITPOTJES

DRAAIKIEP NOKAFMETINGEN

BOORGATEN DRAAIKIEP

//// 5VVU BOORGATEN ON 67 NEUT en DRAAIKIEPSPONNING //// document.addEventListener("DOMContentLoaded", () => { const formFieldsSelector = "[name*='BESTELFORMULIER']"; // Target Elementor form fields with wildcard const svgId = "alle-vakken"; // ID of the SVG element const fieldConditions = [ { field: "form_fields[5VVU1]", value: "draaikiep", dependentField: "form_fields[5LU]", dependentValue: "67", targets: [ { id: "vak-5-SU-L-boorgat-1", adjustment: -11.5, reverseAdjustment: +11.5 }, { id: "vak-5-SU-L-boorgat-1-marge", adjustment: -11.5, reverseAdjustment: +11.5 }, { radioName: "form_fields[Xpos_5_1_1]", adjustment: -11.5, reverseAdjustment: +11.5 } ], active: false }, { field: "form_fields[5VVU1]", value: "draaikiep", dependentField: "form_fields[5TU1]", dependentValue: "67", targets: [ { id: "vak-5-SU-T1-boorgat-1", adjustment: +11.5, reverseAdjustment: -11.5 }, { id: "vak-5-SU-T1-boorgat-1-marge", adjustment: +11.5, reverseAdjustment: -11.5 }, { radioName: "form_fields[Xpos_5_2_1]", adjustment: +11.5, reverseAdjustment: -11.5 } ], active: false }, { field: "form_fields[5VVU2]", value: "draaikiep", dependentField: "form_fields[5TU1]", dependentValue: "67", targets: [ { id: "vak-5-SU-T1-boorgat-1", adjustment: -11.5, reverseAdjustment: +11.5 }, { id: "vak-5-SU-T1-boorgat-1-marge", adjustment: -11.5, reverseAdjustment: +11.5 }, { radioName: "form_fields[Xpos_5_2_1]", adjustment: -11.5, reverseAdjustment: +11.5 } ], active: false }, { field: "form_fields[5VVU2]", value: "draaikiep", dependentField: "form_fields[5TU2]", dependentValue: "67", targets: [ { id: "vak-5-SU-T2-boorgat-1", adjustment: +11.5, reverseAdjustment: -11.5 }, { id: "vak-5-SU-T2-boorgat-1-marge", adjustment: +11.5, reverseAdjustment: -11.5 }, { radioName: "form_fields[Xpos_5_3_1]", adjustment: +11.5, reverseAdjustment: -11.5 } ], active: false }, { field: "form_fields[5VVU3]", value: "draaikiep", dependentField: "form_fields[5TU2]", dependentValue: "67", targets: [ { id: "vak-5-SU-T2-boorgat-1", adjustment: -11.5, reverseAdjustment: +11.5 }, { id: "vak-5-SU-T2-boorgat-1-marge", adjustment: -11.5, reverseAdjustment: +11.5 }, { radioName: "form_fields[Xpos_5_3_1]", adjustment: -11.5, reverseAdjustment: +11.5 } ], active: false }, { field: "form_fields[5VVU3]", value: "draaikiep", dependentField: "form_fields[5TU3]", dependentValue: "67", targets: [ { id: "vak-5-SU-T3-boorgat-1", adjustment: +11.5, reverseAdjustment: -11.5 }, { id: "vak-5-SU-T3-boorgat-1-marge", adjustment: +11.5, reverseAdjustment: -11.5 }, { radioName: "form_fields[Xpos_5_4_1]", adjustment: +11.5, reverseAdjustment: -11.5 } ], active: false }, { field: "form_fields[5VVU4]", value: "draaikiep", dependentField: "form_fields[5TU3]", dependentValue: "67", targets: [ { id: "vak-5-SU-T3-boorgat-1", adjustment: -11.5, reverseAdjustment: +11.5 }, { id: "vak-5-SU-T3-boorgat-1-marge", adjustment: -11.5, reverseAdjustment: +11.5 }, { radioName: "form_fields[Xpos_5_4_1]", adjustment: -11.5, reverseAdjustment: +11.5 } ], active: false }, { field: "form_fields[5VVU4]", value: "draaikiep", dependentField: "form_fields[5TU4]", dependentValue: "67", targets: [ { id: "vak-5-SU-T4-boorgat-1", adjustment: +11.5, reverseAdjustment: -11.5 }, { id: "vak-5-SU-T4-boorgat-1-marge", adjustment: +11.5, reverseAdjustment: -11.5 }, { radioName: "form_fields[Xpos_5_5_1]", adjustment: +11.5, reverseAdjustment: -11.5 } ], active: false }, { field: "form_fields[5VVU5]", value: "draaikiep", dependentField: "form_fields[5TU4]", dependentValue: "67", targets: [ { id: "vak-5-SU-T4-boorgat-1", adjustment: -11.5, reverseAdjustment: +11.5 }, { id: "vak-5-SU-T4-boorgat-1-marge", adjustment: -11.5, reverseAdjustment: +11.5 }, { radioName: "form_fields[Xpos_5_5_1]", adjustment: -11.5, reverseAdjustment: +11.5 } ], active: false }, { field: "form_fields[5VVU5]", value: "draaikiep", dependentField: "form_fields[5RU]", dependentValue: "67", targets: [ { id: "vak-5-SU-R-boorgat-1", adjustment: +11.5, reverseAdjustment: -11.5 }, { id: "vak-5-SU-R-boorgat-1-marge", adjustment: +11.5, reverseAdjustment: -11.5 }, { radioName: "form_fields[Xpos_5_6_1]", adjustment: +11.5, reverseAdjustment: -11.5 } ], active: false } ]; const svg = document.getElementById(svgId); if (!svg) return; // Function to get the selected value of radio buttons function getRadioValue(fieldName) { const radios = document.querySelectorAll(`[name='${fieldName}']`); for (const radio of radios) { if (radio.checked) return radio.value.trim(); } return null; } // Function to adjust cx value function adjustCircleCx(targetId, adjustment) { const gElement = document.getElementById(targetId); if (gElement) { const circle = gElement.querySelector("circle"); if (circle) { let currentCx = parseFloat(circle.getAttribute("cx")) || 0; circle.setAttribute("cx", currentCx + adjustment); } } } // Function to adjust radio button value and label function adjustRadioValue(radioName, adjustment) { const radios = document.querySelectorAll(`[name='${radioName}']`); radios.forEach((radio) => { if (radio.checked) { const currentValue = parseFloat(radio.value) || 0; const newValue = currentValue + adjustment; radio.value = newValue; // Update the corresponding label const label = document.querySelector(`label[for='${radio.id}']`); if (label) label.textContent = newValue; } }); } // Check conditions and perform adjustments function evaluateConditions() { fieldConditions.forEach((condition) => { const field = document.querySelector(`[name='${condition.field}']`); const dependentValue = getRadioValue(condition.dependentField); if (field && dependentValue) { const fieldValue = field.value.trim(); // Ensure BOTH values strictly match before triggering const conditionMet = (fieldValue === condition.value) && (dependentValue === condition.dependentValue); if (conditionMet && !condition.active) { condition.targets.forEach(target => { if (target.id) adjustCircleCx(target.id, target.adjustment); if (target.radioName) adjustRadioValue(target.radioName, target.adjustment); }); condition.active = true; } // Reverse logic: only reverse if previously active and condition no longer met else if (!conditionMet && condition.active) { condition.targets.forEach(target => { if (target.id) adjustCircleCx(target.id, target.reverseAdjustment); if (target.radioName) adjustRadioValue(target.radioName, target.reverseAdjustment); }); condition.active = false; } } else if (condition.active) { // If dependentField is misSUng or empty, reverse the adjustment condition.targets.forEach(target => { if (target.id) adjustCircleCx(target.id, target.reverseAdjustment); if (target.radioName) adjustRadioValue(target.radioName, target.reverseAdjustment); }); condition.active = false; } }); } // Listen to changes dynamically function addFieldListeners() { document.querySelectorAll(formFieldsSelector).forEach((field) => { field.addEventListener("change", evaluateConditions); field.addEventListener("input", evaluateConditions); }); document.querySelectorAll("input[type='radio']").forEach((radio) => { radio.addEventListener("change", evaluateConditions); }); } // Initial evaluation and listeners addFieldListeners(); evaluateConditions(); });
//// 4VVU BOORGATEN ON 67 NEUT en DRAAIKIEPSPONNING //// document.addEventListener("DOMContentLoaded", () => { const formFieldsSelector = "[name*='BESTELFORMULIER']"; // Target Elementor form fields with wildcard const svgId = "alle-vakken"; // ID of the SVG element const fieldConditions = [ { field: "form_fields[4VVU1]", value: "draaikiep", dependentField: "form_fields[4LU]", dependentValue: "67", targets: [ { id: "vak-4-SU-L-boorgat-1", adjustment: -11.5, reverseAdjustment: +11.5 }, { id: "vak-4-SU-L-boorgat-1-marge", adjustment: -11.5, reverseAdjustment: +11.5 }, { radioName: "form_fields[Xpos_4_1_1]", adjustment: -11.5, reverseAdjustment: +11.5 } ], active: false }, { field: "form_fields[4VVU1]", value: "draaikiep", dependentField: "form_fields[4TU1]", dependentValue: "67", targets: [ { id: "vak-4-SU-T1-boorgat-1", adjustment: +11.5, reverseAdjustment: -11.5 }, { id: "vak-4-SU-T1-boorgat-1-marge", adjustment: +11.5, reverseAdjustment: -11.5 }, { radioName: "form_fields[Xpos_4_2_1]", adjustment: +11.5, reverseAdjustment: -11.5 } ], active: false }, { field: "form_fields[4VVU2]", value: "draaikiep", dependentField: "form_fields[4TU1]", dependentValue: "67", targets: [ { id: "vak-4-SU-T1-boorgat-1", adjustment: -11.5, reverseAdjustment: +11.5 }, { id: "vak-4-SU-T1-boorgat-1-marge", adjustment: -11.5, reverseAdjustment: +11.5 }, { radioName: "form_fields[Xpos_4_2_1]", adjustment: -11.5, reverseAdjustment: +11.5 } ], active: false }, { field: "form_fields[4VVU2]", value: "draaikiep", dependentField: "form_fields[4TU2]", dependentValue: "67", targets: [ { id: "vak-4-SU-T2-boorgat-1", adjustment: +11.5, reverseAdjustment: -11.5 }, { id: "vak-4-SU-T2-boorgat-1-marge", adjustment: +11.5, reverseAdjustment: -11.5 }, { radioName: "form_fields[Xpos_4_3_1]", adjustment: +11.5, reverseAdjustment: -11.5 } ], active: false }, { field: "form_fields[4VVU3]", value: "draaikiep", dependentField: "form_fields[4TU2]", dependentValue: "67", targets: [ { id: "vak-4-SU-T2-boorgat-1", adjustment: -11.5, reverseAdjustment: +11.5 }, { id: "vak-4-SU-T2-boorgat-1-marge", adjustment: -11.5, reverseAdjustment: +11.5 }, { radioName: "form_fields[Xpos_4_3_1]", adjustment: -11.5, reverseAdjustment: +11.5 } ], active: false }, { field: "form_fields[4VVU3]", value: "draaikiep", dependentField: "form_fields[4TU3]", dependentValue: "67", targets: [ { id: "vak-4-SU-T3-boorgat-1", adjustment: +11.5, reverseAdjustment: -11.5 }, { id: "vak-4-SU-T3-boorgat-1-marge", adjustment: +11.5, reverseAdjustment: -11.5 }, { radioName: "form_fields[Xpos_4_4_1]", adjustment: +11.5, reverseAdjustment: -11.5 } ], active: false }, { field: "form_fields[4VVU4]", value: "draaikiep", dependentField: "form_fields[4TU3]", dependentValue: "67", targets: [ { id: "vak-4-SU-T3-boorgat-1", adjustment: -11.5, reverseAdjustment: +11.5 }, { id: "vak-4-SU-T3-boorgat-1-marge", adjustment: -11.5, reverseAdjustment: +11.5 }, { radioName: "form_fields[Xpos_4_4_1]", adjustment: -11.5, reverseAdjustment: +11.5 } ], active: false }, { field: "form_fields[4VVU4]", value: "draaikiep", dependentField: "form_fields[4RU]", dependentValue: "67", targets: [ { id: "vak-4-SU-R-boorgat-1", adjustment: +11.5, reverseAdjustment: -11.5 }, { id: "vak-4-SU-R-boorgat-1-marge", adjustment: +11.5, reverseAdjustment: -11.5 }, { radioName: "form_fields[Xpos_4_5_1]", adjustment: +11.5, reverseAdjustment: -11.5 } ], active: false } ]; const svg = document.getElementById(svgId); if (!svg) return; // Function to get the selected value of radio buttons function getRadioValue(fieldName) { const radios = document.querySelectorAll(`[name='${fieldName}']`); for (const radio of radios) { if (radio.checked) return radio.value.trim(); } return null; } // Function to adjust cx value function adjustCircleCx(targetId, adjustment) { const gElement = document.getElementById(targetId); if (gElement) { const circle = gElement.querySelector("circle"); if (circle) { let currentCx = parseFloat(circle.getAttribute("cx")) || 0; circle.setAttribute("cx", currentCx + adjustment); } } } // Function to adjust radio button value and label function adjustRadioValue(radioName, adjustment) { const radios = document.querySelectorAll(`[name='${radioName}']`); radios.forEach((radio) => { if (radio.checked) { const currentValue = parseFloat(radio.value) || 0; const newValue = currentValue + adjustment; radio.value = newValue; // Update the corresponding label const label = document.querySelector(`label[for='${radio.id}']`); if (label) label.textContent = newValue; } }); } // Check conditions and perform adjustments function evaluateConditions() { fieldConditions.forEach((condition) => { const field = document.querySelector(`[name='${condition.field}']`); const dependentValue = getRadioValue(condition.dependentField); if (field && dependentValue) { const fieldValue = field.value.trim(); // Ensure BOTH values strictly match before triggering const conditionMet = (fieldValue === condition.value) && (dependentValue === condition.dependentValue); if (conditionMet && !condition.active) { condition.targets.forEach(target => { if (target.id) adjustCircleCx(target.id, target.adjustment); if (target.radioName) adjustRadioValue(target.radioName, target.adjustment); }); condition.active = true; } // Reverse logic: only reverse if previously active and condition no longer met else if (!conditionMet && condition.active) { condition.targets.forEach(target => { if (target.id) adjustCircleCx(target.id, target.reverseAdjustment); if (target.radioName) adjustRadioValue(target.radioName, target.reverseAdjustment); }); condition.active = false; } } else if (condition.active) { // If dependentField is misSUng or empty, reverse the adjustment condition.targets.forEach(target => { if (target.id) adjustCircleCx(target.id, target.reverseAdjustment); if (target.radioName) adjustRadioValue(target.radioName, target.reverseAdjustment); }); condition.active = false; } }); } // Listen to changes dynamically function addFieldListeners() { document.querySelectorAll(formFieldsSelector).forEach((field) => { field.addEventListener("change", evaluateConditions); field.addEventListener("input", evaluateConditions); }); document.querySelectorAll("input[type='radio']").forEach((radio) => { radio.addEventListener("change", evaluateConditions); }); } // Initial evaluation and listeners addFieldListeners(); evaluateConditions(); });
//// 3VVU BOORGATEN ON 67 NEUT en DRAAIKIEPSPONNING //// document.addEventListener("DOMContentLoaded", () => { const formFieldsSelector = "[name*='BESTELFORMULIER']"; // Target Elementor form fields with wildcard const svgId = "alle-vakken"; // ID of the SVG element const fieldConditions = [ { field: "form_fields[3VVU1]", value: "draaikiep", dependentField: "form_fields[3LU]", dependentValue: "67", targets: [ { id: "vak-3-SU-L-boorgat-1", adjustment: -11.5, reverseAdjustment: +11.5 }, { id: "vak-3-SU-L-boorgat-1-marge", adjustment: -11.5, reverseAdjustment: +11.5 }, { radioName: "form_fields[Xpos_3_1_1]", adjustment: -11.5, reverseAdjustment: +11.5 } ], active: false }, { field: "form_fields[3VVU1]", value: "draaikiep", dependentField: "form_fields[3TU1]", dependentValue: "67", targets: [ { id: "vak-3-SU-T1-boorgat-1", adjustment: +11.5, reverseAdjustment: -11.5 }, { id: "vak-3-SU-T1-boorgat-1-marge", adjustment: +11.5, reverseAdjustment: -11.5 }, { radioName: "form_fields[Xpos_3_2_1]", adjustment: +11.5, reverseAdjustment: -11.5 } ], active: false }, { field: "form_fields[3VVU2]", value: "draaikiep", dependentField: "form_fields[3TU1]", dependentValue: "67", targets: [ { id: "vak-3-SU-T1-boorgat-1", adjustment: -11.5, reverseAdjustment: +11.5 }, { id: "vak-3-SU-T1-boorgat-1-marge", adjustment: -11.5, reverseAdjustment: +11.5 }, { radioName: "form_fields[Xpos_3_2_1]", adjustment: -11.5, reverseAdjustment: +11.5 } ], active: false }, { field: "form_fields[3VVU2]", value: "draaikiep", dependentField: "form_fields[3TU2]", dependentValue: "67", targets: [ { id: "vak-3-SU-T2-boorgat-1", adjustment: +11.5, reverseAdjustment: -11.5 }, { id: "vak-3-SU-T2-boorgat-1-marge", adjustment: +11.5, reverseAdjustment: -11.5 }, { radioName: "form_fields[Xpos_3_3_1]", adjustment: +11.5, reverseAdjustment: -11.5 } ], active: false }, { field: "form_fields[3VVU3]", value: "draaikiep", dependentField: "form_fields[3TU2]", dependentValue: "67", targets: [ { id: "vak-3-SU-T2-boorgat-1", adjustment: -11.5, reverseAdjustment: +11.5 }, { id: "vak-3-SU-T2-boorgat-1-marge", adjustment: -11.5, reverseAdjustment: +11.5 }, { radioName: "form_fields[Xpos_3_3_1]", adjustment: -11.5, reverseAdjustment: +11.5 } ], active: false }, { field: "form_fields[3VVU3]", value: "draaikiep", dependentField: "form_fields[3RU]", dependentValue: "67", targets: [ { id: "vak-3-SU-R-boorgat-1", adjustment: +11.5, reverseAdjustment: -11.5 }, { id: "vak-3-SU-R-boorgat-1-marge", adjustment: +11.5, reverseAdjustment: -11.5 }, { radioName: "form_fields[Xpos_3_4_1]", adjustment: +11.5, reverseAdjustment: -11.5 } ], active: false } ]; const svg = document.getElementById(svgId); if (!svg) return; // Function to get the selected value of radio buttons function getRadioValue(fieldName) { const radios = document.querySelectorAll(`[name='${fieldName}']`); for (const radio of radios) { if (radio.checked) return radio.value.trim(); } return null; } // Function to adjust cx value function adjustCircleCx(targetId, adjustment) { const gElement = document.getElementById(targetId); if (gElement) { const circle = gElement.querySelector("circle"); if (circle) { let currentCx = parseFloat(circle.getAttribute("cx")) || 0; circle.setAttribute("cx", currentCx + adjustment); } } } // Function to adjust radio button value and label function adjustRadioValue(radioName, adjustment) { const radios = document.querySelectorAll(`[name='${radioName}']`); radios.forEach((radio) => { if (radio.checked) { const currentValue = parseFloat(radio.value) || 0; const newValue = currentValue + adjustment; radio.value = newValue; // Update the corresponding label const label = document.querySelector(`label[for='${radio.id}']`); if (label) label.textContent = newValue; } }); } // Check conditions and perform adjustments function evaluateConditions() { fieldConditions.forEach((condition) => { const field = document.querySelector(`[name='${condition.field}']`); const dependentValue = getRadioValue(condition.dependentField); if (field && dependentValue) { const fieldValue = field.value.trim(); // Ensure BOTH values strictly match before triggering const conditionMet = (fieldValue === condition.value) && (dependentValue === condition.dependentValue); if (conditionMet && !condition.active) { condition.targets.forEach(target => { if (target.id) adjustCircleCx(target.id, target.adjustment); if (target.radioName) adjustRadioValue(target.radioName, target.adjustment); }); condition.active = true; } // Reverse logic: only reverse if previously active and condition no longer met else if (!conditionMet && condition.active) { condition.targets.forEach(target => { if (target.id) adjustCircleCx(target.id, target.reverseAdjustment); if (target.radioName) adjustRadioValue(target.radioName, target.reverseAdjustment); }); condition.active = false; } } else if (condition.active) { // If dependentField is misSUng or empty, reverse the adjustment condition.targets.forEach(target => { if (target.id) adjustCircleCx(target.id, target.reverseAdjustment); if (target.radioName) adjustRadioValue(target.radioName, target.reverseAdjustment); }); condition.active = false; } }); } // Listen to changes dynamically function addFieldListeners() { document.querySelectorAll(formFieldsSelector).forEach((field) => { field.addEventListener("change", evaluateConditions); field.addEventListener("input", evaluateConditions); }); document.querySelectorAll("input[type='radio']").forEach((radio) => { radio.addEventListener("change", evaluateConditions); }); } // Initial evaluation and listeners addFieldListeners(); evaluateConditions(); });
//// 2VVU BOORGATEN ON 67 NEUT en DRAAIKIEPSPONNING //// document.addEventListener("DOMContentLoaded", () => { const formFieldsSelector = "[name*='BESTELFORMULIER']"; // Target Elementor form fields with wildcard const svgId = "alle-vakken"; // ID of the SVG element const fieldConditions = [ { field: "form_fields[2VVU1]", value: "draaikiep", dependentField: "form_fields[2LU]", dependentValue: "67", targets: [ { id: "vak-2-SU-L-boorgat-1", adjustment: -11.5, reverseAdjustment: +11.5 }, { id: "vak-2-SU-L-boorgat-1-marge", adjustment: -11.5, reverseAdjustment: +11.5 }, { radioName: "form_fields[Xpos_2_1_1]", adjustment: -11.5, reverseAdjustment: +11.5 } ], active: false }, { field: "form_fields[2VVU1]", value: "draaikiep", dependentField: "form_fields[2TU1]", dependentValue: "67", targets: [ { id: "vak-2-SU-T1-boorgat-1", adjustment: +11.5, reverseAdjustment: -11.5 }, { id: "vak-2-SU-T1-boorgat-1-marge", adjustment: +11.5, reverseAdjustment: -11.5 }, { radioName: "form_fields[Xpos_2_2_1]", adjustment: +11.5, reverseAdjustment: -11.5 } ], active: false }, { field: "form_fields[2VVU2]", value: "draaikiep", dependentField: "form_fields[2TU1]", dependentValue: "67", targets: [ { id: "vak-2-SU-T1-boorgat-1", adjustment: -11.5, reverseAdjustment: +11.5 }, { id: "vak-2-SU-T1-boorgat-1-marge", adjustment: -11.5, reverseAdjustment: +11.5 }, { radioName: "form_fields[Xpos_2_2_1]", adjustment: -11.5, reverseAdjustment: +11.5 } ], active: false }, { field: "form_fields[2VVU2]", value: "draaikiep", dependentField: "form_fields[2RU]", dependentValue: "67", targets: [ { id: "vak-2-SU-R-boorgat-1", adjustment: +11.5, reverseAdjustment: -11.5 }, { id: "vak-2-SU-R-boorgat-1-marge", adjustment: +11.5, reverseAdjustment: -11.5 }, { radioName: "form_fields[Xpos_2_3_1]", adjustment: +11.5, reverseAdjustment: -11.5 } ], active: false } ]; const svg = document.getElementById(svgId); if (!svg) return; // Function to get the selected value of radio buttons function getRadioValue(fieldName) { const radios = document.querySelectorAll(`[name='${fieldName}']`); for (const radio of radios) { if (radio.checked) return radio.value.trim(); } return null; } // Function to adjust cx value function adjustCircleCx(targetId, adjustment) { const gElement = document.getElementById(targetId); if (gElement) { const circle = gElement.querySelector("circle"); if (circle) { let currentCx = parseFloat(circle.getAttribute("cx")) || 0; circle.setAttribute("cx", currentCx + adjustment); } } } // Function to adjust radio button value and label function adjustRadioValue(radioName, adjustment) { const radios = document.querySelectorAll(`[name='${radioName}']`); radios.forEach((radio) => { if (radio.checked) { const currentValue = parseFloat(radio.value) || 0; const newValue = currentValue + adjustment; radio.value = newValue; // Update the corresponding label const label = document.querySelector(`label[for='${radio.id}']`); if (label) label.textContent = newValue; } }); } // Check conditions and perform adjustments function evaluateConditions() { fieldConditions.forEach((condition) => { const field = document.querySelector(`[name='${condition.field}']`); const dependentValue = getRadioValue(condition.dependentField); if (field && dependentValue) { const fieldValue = field.value.trim(); // Ensure BOTH values strictly match before triggering const conditionMet = (fieldValue === condition.value) && (dependentValue === condition.dependentValue); if (conditionMet && !condition.active) { condition.targets.forEach(target => { if (target.id) adjustCircleCx(target.id, target.adjustment); if (target.radioName) adjustRadioValue(target.radioName, target.adjustment); }); condition.active = true; } // Reverse logic: only reverse if previously active and condition no longer met else if (!conditionMet && condition.active) { condition.targets.forEach(target => { if (target.id) adjustCircleCx(target.id, target.reverseAdjustment); if (target.radioName) adjustRadioValue(target.radioName, target.reverseAdjustment); }); condition.active = false; } } else if (condition.active) { // If dependentField is missing or empty, reverse the adjustment condition.targets.forEach(target => { if (target.id) adjustCircleCx(target.id, target.reverseAdjustment); if (target.radioName) adjustRadioValue(target.radioName, target.reverseAdjustment); }); condition.active = false; } }); } // Listen to changes dynamically function addFieldListeners() { document.querySelectorAll(formFieldsSelector).forEach((field) => { field.addEventListener("change", evaluateConditions); field.addEventListener("input", evaluateConditions); }); document.querySelectorAll("input[type='radio']").forEach((radio) => { radio.addEventListener("change", evaluateConditions); }); } // Initial evaluation and listeners addFieldListeners(); evaluateConditions(); });
//// 1VVU BOORGATEN ON 67 NEUT en DRAAIKIEPSPONNING //// document.addEventListener("DOMContentLoaded", () => { const formFieldsSelector = "[name*='BESTELFORMULIER']"; // Target Elementor form fields with wildcard const svgId = "alle-vakken"; // ID of the SVG element const fieldConditions = [ { field: "form_fields[1VVU1]", value: "draaikiep", dependentField: "form_fields[1LU]", dependentValue: "67", targets: [ { id: "vak-1-SU-L-boorgat-1", adjustment: -11.5, reverseAdjustment: +11.5 }, { id: "vak-1-SU-L-boorgat-1-marge", adjustment: -11.5, reverseAdjustment: +11.5 }, { radioName: "form_fields[Xpos_1_1_1]", adjustment: -11.5, reverseAdjustment: +11.5 } ], active: false }, { field: "form_fields[1VVU1]", value: "draaikiep", dependentField: "form_fields[1RU]", dependentValue: "67", targets: [ { id: "vak-1-SU-R-boorgat-1", adjustment: +11.5, reverseAdjustment: -11.5 }, { id: "vak-1-SU-R-boorgat-1-marge", adjustment: +11.5, reverseAdjustment: -11.5 }, { radioName: "form_fields[Xpos_1_2_1]", adjustment: +11.5, reverseAdjustment: -11.5 } ], active: false } ]; const svg = document.getElementById(svgId); if (!svg) return; // Function to get the selected value of radio buttons function getRadioValue(fieldName) { const radios = document.querySelectorAll(`[name='${fieldName}']`); for (const radio of radios) { if (radio.checked) return radio.value.trim(); } return null; } // Function to adjust cx value function adjustCircleCx(targetId, adjustment) { const gElement = document.getElementById(targetId); if (gElement) { const circle = gElement.querySelector("circle"); if (circle) { let currentCx = parseFloat(circle.getAttribute("cx")) || 0; circle.setAttribute("cx", currentCx + adjustment); } } } // Function to adjust radio button value and label function adjustRadioValue(radioName, adjustment) { const radios = document.querySelectorAll(`[name='${radioName}']`); radios.forEach((radio) => { if (radio.checked) { const currentValue = parseFloat(radio.value) || 0; const newValue = currentValue + adjustment; radio.value = newValue; // Update the corresponding label const label = document.querySelector(`label[for='${radio.id}']`); if (label) label.textContent = newValue; } }); } // Check conditions and perform adjustments function evaluateConditions() { fieldConditions.forEach((condition) => { const field = document.querySelector(`[name='${condition.field}']`); const dependentValue = getRadioValue(condition.dependentField); if (field && dependentValue) { const fieldValue = field.value.trim(); // Ensure BOTH values strictly match before triggering const conditionMet = (fieldValue === condition.value) && (dependentValue === condition.dependentValue); if (conditionMet && !condition.active) { condition.targets.forEach(target => { if (target.id) adjustCircleCx(target.id, target.adjustment); if (target.radioName) adjustRadioValue(target.radioName, target.adjustment); }); condition.active = true; } // Reverse logic: only reverse if previously active and condition no longer met else if (!conditionMet && condition.active) { condition.targets.forEach(target => { if (target.id) adjustCircleCx(target.id, target.reverseAdjustment); if (target.radioName) adjustRadioValue(target.radioName, target.reverseAdjustment); }); condition.active = false; } } else if (condition.active) { // If dependentField is missing or empty, reverse the adjustment condition.targets.forEach(target => { if (target.id) adjustCircleCx(target.id, target.reverseAdjustment); if (target.radioName) adjustRadioValue(target.radioName, target.reverseAdjustment); }); condition.active = false; } }); } // Listen to changes dynamically function addFieldListeners() { document.querySelectorAll(formFieldsSelector).forEach((field) => { field.addEventListener("change", evaluateConditions); field.addEventListener("input", evaluateConditions); }); document.querySelectorAll("input[type='radio']").forEach((radio) => { radio.addEventListener("change", evaluateConditions); }); } // Initial evaluation and listeners addFieldListeners(); evaluateConditions(); });

BU NEUTSPONNINGEN VAR/VAST

//// BU SPONNINGDIEPTE NEUTEN EXCL //// document.addEventListener("DOMContentLoaded", function () { // Function to calculate yValue and update the polygons function updatePolygonPoints() { // Retrieve selected values from the Elementor PRO form fields const sponningDiepteValue = parseFloat(document.querySelector("input[name='form_fields[sponningdiepte_buvd]']:checked")?.value || 0); const dorpelBreedteValue = parseFloat(document.querySelector("input[name='form_fields[dorpelbreedte]']:checked")?.value || 0); const exclusionRules = { "form_fields[1VVU1]": { excludedPositions: [{ id: "vak-1-BU-L", exclude: [5, 7] }, { id: "vak-1-BU-R", exclude: [5, 7] }] }, "form_fields[2VVU1]": { excludedPositions: [{ id: "vak-2-BU-L", exclude: [5, 7] }, { id: "vak-2-BU-T1", exclude: [21, 23] }] }, "form_fields[2VVU2]": { excludedPositions: [{ id: "vak-2-BU-R", exclude: [5, 7] }, { id: "vak-2-BU-T1", exclude: [5, 7] }] }, "form_fields[3VVU1]": { excludedPositions: [{ id: "vak-3-BU-L", exclude: [5, 7] }, { id: "vak-3-BU-T1", exclude: [21, 23] }] }, "form_fields[3VVU2]": { excludedPositions: [{ id: "vak-3-BU-T1", exclude: [5, 7] }, { id: "vak-3-BU-T2", exclude: [21, 23] }] }, "form_fields[3VVU3]": { excludedPositions: [{ id: "vak-3-BU-R", exclude: [5, 7] }, { id: "vak-3-BU-T2", exclude: [5, 7] }] }, "form_fields[4VVU1]": { excludedPositions: [{ id: "vak-4-BU-L", exclude: [5, 7] }, { id: "vak-4-BU-T1", exclude: [21, 23] }] }, "form_fields[4VVU2]": { excludedPositions: [{ id: "vak-4-BU-T1", exclude: [5, 7] }, { id: "vak-4-BU-T2", exclude: [21, 23] }] }, "form_fields[4VVU3]": { excludedPositions: [{ id: "vak-4-BU-T2", exclude: [5, 7] }, { id: "vak-4-BU-T3", exclude: [21, 23] }] }, "form_fields[4VVU4]": { excludedPositions: [{ id: "vak-4-BU-R", exclude: [5, 7] }, { id: "vak-4-BU-T3", exclude: [5, 7] }] }, "form_fields[5VVU1]": { excludedPositions: [{ id: "vak-5-BU-L", exclude: [5, 7] }, { id: "vak-5-BU-T1", exclude: [21, 23] }] }, "form_fields[5VVU2]": { excludedPositions: [{ id: "vak-5-BU-T1", exclude: [5, 7] }, { id: "vak-5-BU-T2", exclude: [21, 23] }] }, "form_fields[5VVU3]": { excludedPositions: [{ id: "vak-5-BU-T2", exclude: [5, 7] }, { id: "vak-5-BU-T3", exclude: [21, 23] }] }, "form_fields[5VVU4]": { excludedPositions: [{ id: "vak-5-BU-T3", exclude: [5, 7] }, { id: "vak-5-BU-T4", exclude: [21, 23] }] }, "form_fields[5VVU5]": { excludedPositions: [{ id: "vak-5-BU-R", exclude: [5, 7] }, { id: "vak-5-BU-T4", exclude: [5, 7] }] } }; // Check exclusions for specific positions function isExcluded(id, position) { for (let fieldName in exclusionRules) { const dropdown = document.querySelector(`select[name='${fieldName}']`); if (dropdown && ["dubbelglas binnen", "tripleglas binnen", "draaikiep"].includes(dropdown.value)) { const { excludedPositions } = exclusionRules[fieldName]; const positionRule = excludedPositions.find(p => p.id === id); if (positionRule && positionRule.exclude.includes(position)) return true; } } return false; } // Calculate yValue const yValue = dorpelBreedteValue - sponningDiepteValue; // List of element IDs for positions 5 and 7 const idsForPositions5and7 = [ "vak-1-BU-L", "vak-1-BU-R", "vak-2-BU-L", "vak-2-BU-T1", "vak-2-BU-R", "vak-3-BU-L", "vak-3-BU-T1", "vak-3-BU-T2", "vak-3-BU-R", "vak-4-BU-L", "vak-4-BU-T1", "vak-4-BU-T2", "vak-4-BU-T3", "vak-4-BU-R", "vak-5-BU-L", "vak-5-BU-T1", "vak-5-BU-T2", "vak-5-BU-T3", "vak-5-BU-T4", "vak-5-BU-R" ]; // List of element IDs for positions 21 and 23 const idsForPositions21and23 = [ "vak-2-BU-T1", "vak-3-BU-T1", "vak-3-BU-T2", "vak-4-BU-T1", "vak-4-BU-T2", "vak-4-BU-T3", "vak-5-BU-T1", "vak-5-BU-T2", "vak-5-BU-T3", "vak-5-BU-T4" ]; // Get the SVG element const svgElement = document.getElementById("alle-vakken"); if (!svgElement) return; // Update points for positions 5 and 7 idsForPositions5and7.forEach(id => { const gElement = svgElement.querySelector(`#${id}`); if (gElement && !isExcluded(id, 5)) { const polygon = gElement.querySelector("polygon"); if (polygon) { const points = polygon.getAttribute("points").trim().split(/\s+/).map(parseFloat); if (points.length > 7) { points[5] = yValue; // Replace position 5 points[7] = yValue; // Replace position 7 polygon.setAttribute("points", points.join(" ")); } } } }); // Update points for positions 21 and 23 idsForPositions21and23.forEach(id => { const gElement = svgElement.querySelector(`#${id}`); if (gElement && !isExcluded(id, 21)) { const polygon = gElement.querySelector("polygon"); if (polygon) { const points = polygon.getAttribute("points").trim().split(/\s+/).map(parseFloat); if (points.length > 23) { points[21] = yValue; // Replace position 21 points[23] = yValue; // Replace position 23 polygon.setAttribute("points", points.join(" ")); } } } }); } // Attach event listeners to the radio buttons const sponningDiepteRadios = document.querySelectorAll("input[name='form_fields[sponningdiepte_buvd]']"); const dorpelBreedteRadios = document.querySelectorAll("input[name='form_fields[dorpelbreedte]']"); const dropdowns = document.querySelectorAll("select[name^='form_fields']"); sponningDiepteRadios.forEach(radio => radio.addEventListener("change", updatePolygonPoints)); dorpelBreedteRadios.forEach(radio => radio.addEventListener("change", updatePolygonPoints)); dropdowns.forEach(dropdown => dropdown.addEventListener("change", updatePolygonPoints)); // Initial update on page load updatePolygonPoints(); });

VOORSPONNING BLOCKERS

BU DRAAIKIEP/WISSEL INITIEEL

VARIATIE DRAAIKIEPSPONNINGEN

VARIATIE WISSELSPONNINGEN

BOORGATEN BIV 21/25

//// 5VVI BOORGATEN ON 67 NEUT en 21/25mm SPONNING //// document.addEventListener("DOMContentLoaded", () => { const formFieldsSelector = "[name*='BESTELFORMULIER']"; // Target Elementor form fields with wildcard const svgId = "alle-vakken"; // ID of the SVG element const fieldConditions = [ { field: "form_fields[5VVI1]", value: "kaderdeur", dependentField: "form_fields[5LI]", dependentValue: "67", targets: [ { id: "vak-5-SI-L-boorgat-1", adjustment: -6, reverseAdjustment: +6 }, { id: "vak-5-SI-L-boorgat-1-marge", adjustment: -6, reverseAdjustment: +6 }, { radioName: "form_fields[Xpos_5_1_1]", adjustment: -6, reverseAdjustment: +6 } ], active: false }, { field: "form_fields[5VVI1]", value: "kaderdeur", dependentField: "form_fields[5TI1]", dependentValue: "67", targets: [ { id: "vak-5-SI-T1-boorgat-1", adjustment: +6, reverseAdjustment: -6 }, { id: "vak-5-SI-T1-boorgat-1-marge", adjustment: +6, reverseAdjustment: -6 }, { radioName: "form_fields[Xpos_5_2_1]", adjustment: +6, reverseAdjustment: -6 } ], active: false }, { field: "form_fields[5VVI2]", value: "kaderdeur", dependentField: "form_fields[5TI1]", dependentValue: "67", targets: [ { id: "vak-5-SI-T1-boorgat-1", adjustment: -6, reverseAdjustment: +6 }, { id: "vak-5-SI-T1-boorgat-1-marge", adjustment: -6, reverseAdjustment: +6 }, { radioName: "form_fields[Xpos_5_2_1]", adjustment: -6, reverseAdjustment: +6 } ], active: false }, { field: "form_fields[5VVI2]", value: "kaderdeur", dependentField: "form_fields[5TI2]", dependentValue: "67", targets: [ { id: "vak-5-SI-T2-boorgat-1", adjustment: +6, reverseAdjustment: -6 }, { id: "vak-5-SI-T2-boorgat-1-marge", adjustment: +6, reverseAdjustment: -6 }, { radioName: "form_fields[Xpos_5_3_1]", adjustment: +6, reverseAdjustment: -6 } ], active: false }, { field: "form_fields[5VVI3]", value: "kaderdeur", dependentField: "form_fields[5TI2]", dependentValue: "67", targets: [ { id: "vak-5-SI-T2-boorgat-1", adjustment: -6, reverseAdjustment: +6 }, { id: "vak-5-SI-T2-boorgat-1-marge", adjustment: -6, reverseAdjustment: +6 }, { radioName: "form_fields[Xpos_5_3_1]", adjustment: -6, reverseAdjustment: +6 } ], active: false }, { field: "form_fields[5VVI3]", value: "kaderdeur", dependentField: "form_fields[5TI3]", dependentValue: "67", targets: [ { id: "vak-5-SI-T3-boorgat-1", adjustment: +6, reverseAdjustment: -6 }, { id: "vak-5-SI-T3-boorgat-1-marge", adjustment: +6, reverseAdjustment: -6 }, { radioName: "form_fields[Xpos_5_4_1]", adjustment: +6, reverseAdjustment: -6 } ], active: false }, { field: "form_fields[5VVI4]", value: "kaderdeur", dependentField: "form_fields[5TI3]", dependentValue: "67", targets: [ { id: "vak-5-SI-T3-boorgat-1", adjustment: -6, reverseAdjustment: +6 }, { id: "vak-5-SI-T3-boorgat-1-marge", adjustment: -6, reverseAdjustment: +6 }, { radioName: "form_fields[Xpos_5_4_1]", adjustment: -6, reverseAdjustment: +6 } ], active: false }, { field: "form_fields[5VVI4]", value: "kaderdeur", dependentField: "form_fields[5TI4]", dependentValue: "67", targets: [ { id: "vak-5-SI-T4-boorgat-1", adjustment: +6, reverseAdjustment: -6 }, { id: "vak-5-SI-T4-boorgat-1-marge", adjustment: +6, reverseAdjustment: -6 }, { radioName: "form_fields[Xpos_5_5_1]", adjustment: +6, reverseAdjustment: -6 } ], active: false }, { field: "form_fields[5VVI5]", value: "kaderdeur", dependentField: "form_fields[5TI4]", dependentValue: "67", targets: [ { id: "vak-5-SI-T4-boorgat-1", adjustment: -6, reverseAdjustment: +6 }, { id: "vak-5-SI-T4-boorgat-1-marge", adjustment: -6, reverseAdjustment: +6 }, { radioName: "form_fields[Xpos_5_5_1]", adjustment: -6, reverseAdjustment: +6 } ], active: false }, { field: "form_fields[5VVI5]", value: "kaderdeur", dependentField: "form_fields[5RI]", dependentValue: "67", targets: [ { id: "vak-5-SI-R-boorgat-1", adjustment: +6, reverseAdjustment: -6 }, { id: "vak-5-SI-R-boorgat-1-marge", adjustment: +6, reverseAdjustment: -6 }, { radioName: "form_fields[Xpos_5_6_1]", adjustment: +6, reverseAdjustment: -6 } ], active: false } ]; const svg = document.getElementById(svgId); if (!svg) return; // Function to get the selected value of radio buttons function getRadioValue(fieldName) { const radios = document.querySelectorAll(`[name='${fieldName}']`); for (const radio of radios) { if (radio.checked) return radio.value.trim(); } return null; } // Function to adjust cx value function adjustCircleCx(targetId, adjustment) { const gElement = document.getElementById(targetId); if (gElement) { const circle = gElement.querySelector("circle"); if (circle) { let currentCx = parseFloat(circle.getAttribute("cx")) || 0; circle.setAttribute("cx", currentCx + adjustment); } } } // Function to adjust radio button value and label function adjustRadioValue(radioName, adjustment) { const radios = document.querySelectorAll(`[name='${radioName}']`); radios.forEach((radio) => { if (radio.checked) { const currentValue = parseFloat(radio.value) || 0; const newValue = currentValue + adjustment; radio.value = newValue; // Update the corresponding label const label = document.querySelector(`label[for='${radio.id}']`); if (label) label.textContent = newValue; } }); } // Check conditions and perform adjustments function evaluateConditions() { fieldConditions.forEach((condition) => { const field = document.querySelector(`[name='${condition.field}']`); const dependentValue = getRadioValue(condition.dependentField); if (field && dependentValue) { const fieldValue = field.value.trim(); // Ensure BOTH values strictly match before triggering const conditionMet = (fieldValue === condition.value) && (dependentValue === condition.dependentValue); if (conditionMet && !condition.active) { condition.targets.forEach(target => { if (target.id) adjustCircleCx(target.id, target.adjustment); if (target.radioName) adjustRadioValue(target.radioName, target.adjustment); }); condition.active = true; } // Reverse logic: only reverse if previously active and condition no longer met else if (!conditionMet && condition.active) { condition.targets.forEach(target => { if (target.id) adjustCircleCx(target.id, target.reverseAdjustment); if (target.radioName) adjustRadioValue(target.radioName, target.reverseAdjustment); }); condition.active = false; } } else if (condition.active) { // If dependentField is missing or empty, reverse the adjustment condition.targets.forEach(target => { if (target.id) adjustCircleCx(target.id, target.reverseAdjustment); if (target.radioName) adjustRadioValue(target.radioName, target.reverseAdjustment); }); condition.active = false; } }); } // Listen to changes dynamically function addFieldListeners() { document.querySelectorAll(formFieldsSelector).forEach((field) => { field.addEventListener("change", evaluateConditions); field.addEventListener("input", evaluateConditions); }); document.querySelectorAll("input[type='radio']").forEach((radio) => { radio.addEventListener("change", evaluateConditions); }); } // Initial evaluation and listeners addFieldListeners(); evaluateConditions(); });
//// 4VVI BOORGATEN ON 67 NEUT en 21/25mm SPONNING //// document.addEventListener("DOMContentLoaded", () => { const formFieldsSelector = "[name*='BESTELFORMULIER']"; // Target Elementor form fields with wildcard const svgId = "alle-vakken"; // ID of the SVG element const fieldConditions = [ { field: "form_fields[4VVI1]", value: "kaderdeur", dependentField: "form_fields[4LI]", dependentValue: "67", targets: [ { id: "vak-4-SI-L-boorgat-1", adjustment: -6, reverseAdjustment: +6 }, { id: "vak-4-SI-L-boorgat-1-marge", adjustment: -6, reverseAdjustment: +6 }, { radioName: "form_fields[Xpos_4_1_1]", adjustment: -6, reverseAdjustment: +6 } ], active: false }, { field: "form_fields[4VVI1]", value: "kaderdeur", dependentField: "form_fields[4TI1]", dependentValue: "67", targets: [ { id: "vak-4-SI-T1-boorgat-1", adjustment: +6, reverseAdjustment: -6 }, { id: "vak-4-SI-T1-boorgat-1-marge", adjustment: +6, reverseAdjustment: -6 }, { radioName: "form_fields[Xpos_4_2_1]", adjustment: +6, reverseAdjustment: -6 } ], active: false }, { field: "form_fields[4VVI2]", value: "kaderdeur", dependentField: "form_fields[4TI1]", dependentValue: "67", targets: [ { id: "vak-4-SI-T1-boorgat-1", adjustment: -6, reverseAdjustment: +6 }, { id: "vak-4-SI-T1-boorgat-1-marge", adjustment: -6, reverseAdjustment: +6 }, { radioName: "form_fields[Xpos_4_2_1]", adjustment: -6, reverseAdjustment: +6 } ], active: false }, { field: "form_fields[4VVI2]", value: "kaderdeur", dependentField: "form_fields[4TI2]", dependentValue: "67", targets: [ { id: "vak-4-SI-T2-boorgat-1", adjustment: +6, reverseAdjustment: -6 }, { id: "vak-4-SI-T2-boorgat-1-marge", adjustment: +6, reverseAdjustment: -6 }, { radioName: "form_fields[Xpos_4_3_1]", adjustment: +6, reverseAdjustment: -6 } ], active: false }, { field: "form_fields[4VVI3]", value: "kaderdeur", dependentField: "form_fields[4TI2]", dependentValue: "67", targets: [ { id: "vak-4-SI-T2-boorgat-1", adjustment: -6, reverseAdjustment: +6 }, { id: "vak-4-SI-T2-boorgat-1-marge", adjustment: -6, reverseAdjustment: +6 }, { radioName: "form_fields[Xpos_4_3_1]", adjustment: -6, reverseAdjustment: +6 } ], active: false }, { field: "form_fields[4VVI3]", value: "kaderdeur", dependentField: "form_fields[4TI3]", dependentValue: "67", targets: [ { id: "vak-4-SI-T3-boorgat-1", adjustment: +6, reverseAdjustment: -6 }, { id: "vak-4-SI-T3-boorgat-1-marge", adjustment: +6, reverseAdjustment: -6 }, { radioName: "form_fields[Xpos_4_4_1]", adjustment: +6, reverseAdjustment: -6 } ], active: false }, { field: "form_fields[4VVI4]", value: "kaderdeur", dependentField: "form_fields[4TI3]", dependentValue: "67", targets: [ { id: "vak-4-SI-T3-boorgat-1", adjustment: -6, reverseAdjustment: +6 }, { id: "vak-4-SI-T3-boorgat-1-marge", adjustment: -6, reverseAdjustment: +6 }, { radioName: "form_fields[Xpos_4_4_1]", adjustment: -6, reverseAdjustment: +6 } ], active: false }, { field: "form_fields[4VVI4]", value: "kaderdeur", dependentField: "form_fields[4RI]", dependentValue: "67", targets: [ { id: "vak-4-SI-R-boorgat-1", adjustment: +6, reverseAdjustment: -6 }, { id: "vak-4-SI-R-boorgat-1-marge", adjustment: +6, reverseAdjustment: -6 }, { radioName: "form_fields[Xpos_4_5_1]", adjustment: +6, reverseAdjustment: -6 } ], active: false } ]; const svg = document.getElementById(svgId); if (!svg) return; // Function to get the selected value of radio buttons function getRadioValue(fieldName) { const radios = document.querySelectorAll(`[name='${fieldName}']`); for (const radio of radios) { if (radio.checked) return radio.value.trim(); } return null; } // Function to adjust cx value function adjustCircleCx(targetId, adjustment) { const gElement = document.getElementById(targetId); if (gElement) { const circle = gElement.querySelector("circle"); if (circle) { let currentCx = parseFloat(circle.getAttribute("cx")) || 0; circle.setAttribute("cx", currentCx + adjustment); } } } // Function to adjust radio button value and label function adjustRadioValue(radioName, adjustment) { const radios = document.querySelectorAll(`[name='${radioName}']`); radios.forEach((radio) => { if (radio.checked) { const currentValue = parseFloat(radio.value) || 0; const newValue = currentValue + adjustment; radio.value = newValue; // Update the corresponding label const label = document.querySelector(`label[for='${radio.id}']`); if (label) label.textContent = newValue; } }); } // Check conditions and perform adjustments function evaluateConditions() { fieldConditions.forEach((condition) => { const field = document.querySelector(`[name='${condition.field}']`); const dependentValue = getRadioValue(condition.dependentField); if (field && dependentValue) { const fieldValue = field.value.trim(); // Ensure BOTH values strictly match before triggering const conditionMet = (fieldValue === condition.value) && (dependentValue === condition.dependentValue); if (conditionMet && !condition.active) { condition.targets.forEach(target => { if (target.id) adjustCircleCx(target.id, target.adjustment); if (target.radioName) adjustRadioValue(target.radioName, target.adjustment); }); condition.active = true; } // Reverse logic: only reverse if previously active and condition no longer met else if (!conditionMet && condition.active) { condition.targets.forEach(target => { if (target.id) adjustCircleCx(target.id, target.reverseAdjustment); if (target.radioName) adjustRadioValue(target.radioName, target.reverseAdjustment); }); condition.active = false; } } else if (condition.active) { // If dependentField is missing or empty, reverse the adjustment condition.targets.forEach(target => { if (target.id) adjustCircleCx(target.id, target.reverseAdjustment); if (target.radioName) adjustRadioValue(target.radioName, target.reverseAdjustment); }); condition.active = false; } }); } // Listen to changes dynamically function addFieldListeners() { document.querySelectorAll(formFieldsSelector).forEach((field) => { field.addEventListener("change", evaluateConditions); field.addEventListener("input", evaluateConditions); }); document.querySelectorAll("input[type='radio']").forEach((radio) => { radio.addEventListener("change", evaluateConditions); }); } // Initial evaluation and listeners addFieldListeners(); evaluateConditions(); });
//// 3VVI BOORGATEN ON 67 NEUT en 21/25mm SPONNING //// document.addEventListener("DOMContentLoaded", () => { const formFieldsSelector = "[name*='BESTELFORMULIER']"; // Target Elementor form fields with wildcard const svgId = "alle-vakken"; // ID of the SVG element const fieldConditions = [ { field: "form_fields[3VVI1]", value: "kaderdeur", dependentField: "form_fields[3LI]", dependentValue: "67", targets: [ { id: "vak-3-SI-L-boorgat-1", adjustment: -6, reverseAdjustment: +6 }, { id: "vak-3-SI-L-boorgat-1-marge", adjustment: -6, reverseAdjustment: +6 }, { radioName: "form_fields[Xpos_3_1_1]", adjustment: -6, reverseAdjustment: +6 } ], active: false }, { field: "form_fields[3VVI1]", value: "kaderdeur", dependentField: "form_fields[3TI1]", dependentValue: "67", targets: [ { id: "vak-3-SI-T1-boorgat-1", adjustment: +6, reverseAdjustment: -6 }, { id: "vak-3-SI-T1-boorgat-1-marge", adjustment: +6, reverseAdjustment: -6 }, { radioName: "form_fields[Xpos_3_2_1]", adjustment: +6, reverseAdjustment: -6 } ], active: false }, { field: "form_fields[3VVI2]", value: "kaderdeur", dependentField: "form_fields[3TI1]", dependentValue: "67", targets: [ { id: "vak-3-SI-T1-boorgat-1", adjustment: -6, reverseAdjustment: +6 }, { id: "vak-3-SI-T1-boorgat-1-marge", adjustment: -6, reverseAdjustment: +6 }, { radioName: "form_fields[Xpos_3_2_1]", adjustment: -6, reverseAdjustment: +6 } ], active: false }, { field: "form_fields[3VVI2]", value: "kaderdeur", dependentField: "form_fields[3TI2]", dependentValue: "67", targets: [ { id: "vak-3-SI-T2-boorgat-1", adjustment: +6, reverseAdjustment: -6 }, { id: "vak-3-SI-T2-boorgat-1-marge", adjustment: +6, reverseAdjustment: -6 }, { radioName: "form_fields[Xpos_3_3_1]", adjustment: +6, reverseAdjustment: -6 } ], active: false }, { field: "form_fields[3VVI3]", value: "kaderdeur", dependentField: "form_fields[3TI2]", dependentValue: "67", targets: [ { id: "vak-3-SI-T2-boorgat-1", adjustment: -6, reverseAdjustment: +6 }, { id: "vak-3-SI-T2-boorgat-1-marge", adjustment: -6, reverseAdjustment: +6 }, { radioName: "form_fields[Xpos_3_3_1]", adjustment: -6, reverseAdjustment: +6 } ], active: false }, { field: "form_fields[3VVI3]", value: "kaderdeur", dependentField: "form_fields[3RI]", dependentValue: "67", targets: [ { id: "vak-3-SI-R-boorgat-1", adjustment: +6, reverseAdjustment: -6 }, { id: "vak-3-SI-R-boorgat-1-marge", adjustment: +6, reverseAdjustment: -6 }, { radioName: "form_fields[Xpos_3_4_1]", adjustment: +6, reverseAdjustment: -6 } ], active: false } ]; const svg = document.getElementById(svgId); if (!svg) return; // Function to get the selected value of radio buttons function getRadioValue(fieldName) { const radios = document.querySelectorAll(`[name='${fieldName}']`); for (const radio of radios) { if (radio.checked) return radio.value.trim(); } return null; } // Function to adjust cx value function adjustCircleCx(targetId, adjustment) { const gElement = document.getElementById(targetId); if (gElement) { const circle = gElement.querySelector("circle"); if (circle) { let currentCx = parseFloat(circle.getAttribute("cx")) || 0; circle.setAttribute("cx", currentCx + adjustment); } } } // Function to adjust radio button value and label function adjustRadioValue(radioName, adjustment) { const radios = document.querySelectorAll(`[name='${radioName}']`); radios.forEach((radio) => { if (radio.checked) { const currentValue = parseFloat(radio.value) || 0; const newValue = currentValue + adjustment; radio.value = newValue; // Update the corresponding label const label = document.querySelector(`label[for='${radio.id}']`); if (label) label.textContent = newValue; } }); } // Check conditions and perform adjustments function evaluateConditions() { fieldConditions.forEach((condition) => { const field = document.querySelector(`[name='${condition.field}']`); const dependentValue = getRadioValue(condition.dependentField); if (field && dependentValue) { const fieldValue = field.value.trim(); // Ensure BOTH values strictly match before triggering const conditionMet = (fieldValue === condition.value) && (dependentValue === condition.dependentValue); if (conditionMet && !condition.active) { condition.targets.forEach(target => { if (target.id) adjustCircleCx(target.id, target.adjustment); if (target.radioName) adjustRadioValue(target.radioName, target.adjustment); }); condition.active = true; } // Reverse logic: only reverse if previously active and condition no longer met else if (!conditionMet && condition.active) { condition.targets.forEach(target => { if (target.id) adjustCircleCx(target.id, target.reverseAdjustment); if (target.radioName) adjustRadioValue(target.radioName, target.reverseAdjustment); }); condition.active = false; } } else if (condition.active) { // If dependentField is missing or empty, reverse the adjustment condition.targets.forEach(target => { if (target.id) adjustCircleCx(target.id, target.reverseAdjustment); if (target.radioName) adjustRadioValue(target.radioName, target.reverseAdjustment); }); condition.active = false; } }); } // Listen to changes dynamically function addFieldListeners() { document.querySelectorAll(formFieldsSelector).forEach((field) => { field.addEventListener("change", evaluateConditions); field.addEventListener("input", evaluateConditions); }); document.querySelectorAll("input[type='radio']").forEach((radio) => { radio.addEventListener("change", evaluateConditions); }); } // Initial evaluation and listeners addFieldListeners(); evaluateConditions(); });
//// 2VVI BOORGATEN ON 67 NEUT en 21/25mm SPONNING //// document.addEventListener("DOMContentLoaded", () => { const formFieldsSelector = "[name*='BESTELFORMULIER']"; // Target Elementor form fields with wildcard const svgId = "alle-vakken"; // ID of the SVG element const fieldConditions = [ { field: "form_fields[2VVI1]", value: "kaderdeur", dependentField: "form_fields[2LI]", dependentValue: "67", targets: [ { id: "vak-2-SI-L-boorgat-1", adjustment: -6, reverseAdjustment: +6 }, { id: "vak-2-SI-L-boorgat-1-marge", adjustment: -6, reverseAdjustment: +6 }, { radioName: "form_fields[Xpos_2_1_1]", adjustment: -6, reverseAdjustment: +6 } ], active: false }, { field: "form_fields[2VVI1]", value: "kaderdeur", dependentField: "form_fields[2TI1]", dependentValue: "67", targets: [ { id: "vak-2-SI-T1-boorgat-1", adjustment: +6, reverseAdjustment: -6 }, { id: "vak-2-SI-T1-boorgat-1-marge", adjustment: +6, reverseAdjustment: -6 }, { radioName: "form_fields[Xpos_2_2_1]", adjustment: +6, reverseAdjustment: -6 } ], active: false }, { field: "form_fields[2VVI2]", value: "kaderdeur", dependentField: "form_fields[2TI1]", dependentValue: "67", targets: [ { id: "vak-2-SI-T1-boorgat-1", adjustment: -6, reverseAdjustment: +6 }, { id: "vak-2-SI-T1-boorgat-1-marge", adjustment: -6, reverseAdjustment: +6 }, { radioName: "form_fields[Xpos_2_2_1]", adjustment: -6, reverseAdjustment: +6 } ], active: false }, { field: "form_fields[2VVI2]", value: "kaderdeur", dependentField: "form_fields[2RI]", dependentValue: "67", targets: [ { id: "vak-2-SI-R-boorgat-1", adjustment: +6, reverseAdjustment: -6 }, { id: "vak-2-SI-R-boorgat-1-marge", adjustment: +6, reverseAdjustment: -6 }, { radioName: "form_fields[Xpos_2_3_1]", adjustment: +6, reverseAdjustment: -6 } ], active: false } ]; const svg = document.getElementById(svgId); if (!svg) return; // Function to get the selected value of radio buttons function getRadioValue(fieldName) { const radios = document.querySelectorAll(`[name='${fieldName}']`); for (const radio of radios) { if (radio.checked) return radio.value.trim(); } return null; } // Function to adjust cx value function adjustCircleCx(targetId, adjustment) { const gElement = document.getElementById(targetId); if (gElement) { const circle = gElement.querySelector("circle"); if (circle) { let currentCx = parseFloat(circle.getAttribute("cx")) || 0; circle.setAttribute("cx", currentCx + adjustment); } } } // Function to adjust radio button value and label function adjustRadioValue(radioName, adjustment) { const radios = document.querySelectorAll(`[name='${radioName}']`); radios.forEach((radio) => { if (radio.checked) { const currentValue = parseFloat(radio.value) || 0; const newValue = currentValue + adjustment; radio.value = newValue; // Update the corresponding label const label = document.querySelector(`label[for='${radio.id}']`); if (label) label.textContent = newValue; } }); } // Check conditions and perform adjustments function evaluateConditions() { fieldConditions.forEach((condition) => { const field = document.querySelector(`[name='${condition.field}']`); const dependentValue = getRadioValue(condition.dependentField); if (field && dependentValue) { const fieldValue = field.value.trim(); // Ensure BOTH values strictly match before triggering const conditionMet = (fieldValue === condition.value) && (dependentValue === condition.dependentValue); if (conditionMet && !condition.active) { condition.targets.forEach(target => { if (target.id) adjustCircleCx(target.id, target.adjustment); if (target.radioName) adjustRadioValue(target.radioName, target.adjustment); }); condition.active = true; } // Reverse logic: only reverse if previously active and condition no longer met else if (!conditionMet && condition.active) { condition.targets.forEach(target => { if (target.id) adjustCircleCx(target.id, target.reverseAdjustment); if (target.radioName) adjustRadioValue(target.radioName, target.reverseAdjustment); }); condition.active = false; } } else if (condition.active) { // If dependentField is missing or empty, reverse the adjustment condition.targets.forEach(target => { if (target.id) adjustCircleCx(target.id, target.reverseAdjustment); if (target.radioName) adjustRadioValue(target.radioName, target.reverseAdjustment); }); condition.active = false; } }); } // Listen to changes dynamically function addFieldListeners() { document.querySelectorAll(formFieldsSelector).forEach((field) => { field.addEventListener("change", evaluateConditions); field.addEventListener("input", evaluateConditions); }); document.querySelectorAll("input[type='radio']").forEach((radio) => { radio.addEventListener("change", evaluateConditions); }); } // Initial evaluation and listeners addFieldListeners(); evaluateConditions(); });
//// 1VVI BOORGATEN ON 67 NEUT en 21/25mm SPONNING //// document.addEventListener("DOMContentLoaded", () => { const formFieldsSelector = "[name*='BESTELFORMULIER']"; // Target Elementor form fields with wildcard const svgId = "alle-vakken"; // ID of the SVG element const fieldConditions = [ { field: "form_fields[1VVI1]", value: "kaderdeur", dependentField: "form_fields[1LI]", dependentValue: "67", targets: [ { id: "vak-1-SI-L-boorgat-1", adjustment: -6, reverseAdjustment: +6 }, { id: "vak-1-SI-L-boorgat-1-marge", adjustment: -6, reverseAdjustment: +6 }, { radioName: "form_fields[Xpos_1_1_1]", adjustment: -6, reverseAdjustment: +6 } ], active: false }, { field: "form_fields[1VVI1]", value: "kaderdeur", dependentField: "form_fields[1RI]", dependentValue: "67", targets: [ { id: "vak-1-SI-R-boorgat-1", adjustment: +6, reverseAdjustment: -6 }, { id: "vak-1-SI-R-boorgat-1-marge", adjustment: +6, reverseAdjustment: -6 }, { radioName: "form_fields[Xpos_1_2_1]", adjustment: +6, reverseAdjustment: -6 } ], active: false } ]; const svg = document.getElementById(svgId); if (!svg) return; // Function to get the selected value of radio buttons function getRadioValue(fieldName) { const radios = document.querySelectorAll(`[name='${fieldName}']`); for (const radio of radios) { if (radio.checked) return radio.value.trim(); } return null; } // Function to adjust cx value function adjustCircleCx(targetId, adjustment) { const gElement = document.getElementById(targetId); if (gElement) { const circle = gElement.querySelector("circle"); if (circle) { let currentCx = parseFloat(circle.getAttribute("cx")) || 0; circle.setAttribute("cx", currentCx + adjustment); } } } // Function to adjust radio button value and label function adjustRadioValue(radioName, adjustment) { const radios = document.querySelectorAll(`[name='${radioName}']`); radios.forEach((radio) => { if (radio.checked) { const currentValue = parseFloat(radio.value) || 0; const newValue = currentValue + adjustment; radio.value = newValue; // Update the corresponding label const label = document.querySelector(`label[for='${radio.id}']`); if (label) label.textContent = newValue; } }); } // Check conditions and perform adjustments function evaluateConditions() { fieldConditions.forEach((condition) => { const field = document.querySelector(`[name='${condition.field}']`); const dependentValue = getRadioValue(condition.dependentField); if (field && dependentValue) { const fieldValue = field.value.trim(); // Ensure BOTH values strictly match before triggering const conditionMet = (fieldValue === condition.value) && (dependentValue === condition.dependentValue); if (conditionMet && !condition.active) { condition.targets.forEach(target => { if (target.id) adjustCircleCx(target.id, target.adjustment); if (target.radioName) adjustRadioValue(target.radioName, target.adjustment); }); condition.active = true; } // Reverse logic: only reverse if previously active and condition no longer met else if (!conditionMet && condition.active) { condition.targets.forEach(target => { if (target.id) adjustCircleCx(target.id, target.reverseAdjustment); if (target.radioName) adjustRadioValue(target.radioName, target.reverseAdjustment); }); condition.active = false; } } else if (condition.active) { // If dependentField is missing or empty, reverse the adjustment condition.targets.forEach(target => { if (target.id) adjustCircleCx(target.id, target.reverseAdjustment); if (target.radioName) adjustRadioValue(target.radioName, target.reverseAdjustment); }); condition.active = false; } }); } // Listen to changes dynamically function addFieldListeners() { document.querySelectorAll(formFieldsSelector).forEach((field) => { field.addEventListener("change", evaluateConditions); field.addEventListener("input", evaluateConditions); }); document.querySelectorAll("input[type='radio']").forEach((radio) => { radio.addEventListener("change", evaluateConditions); }); } // Initial evaluation and listeners addFieldListeners(); evaluateConditions(); });

21/25MM SPONNING ON KADERDEUR

CHECK DEFAULT RADIO OPTIONS

HOVER / HITBOXES

//// hover over field //// // Function to show the corresponding element on hover function showElementOnHover(event) { const radioInput = document.querySelector('input[name="form_fields[dorpeltype]"]:checked'); if (!radioInput) { return; // Exit if no radio button is selected } const radioValue = radioInput.value; // e.g., "BI" or "BUVD" const selectValue = event.target.value; // Get the hovered value from the dropdown if (!selectValue) { return; // Exit if no valid selection } // Define the mapping of value combinations to corresponding element IDs const combinationMap = { "BI-deur": "BI-var-deur", "BI-kaderdeur": "BI-var-kaderdeur", "BI-dubbelglas": "BI-var-2glas", "BI-tripleglas": "BI-var-3glas", "BI-paneel": "BI-var-paneel", "BI-glaspaneel": "BI-var-glaspaneel", "BUVD-deur": "BUVD-var-deur", "BUVD-kaderdeur": "BUVD-var-kaderdeur", "BUVD-dubbelglas": "BUVD-var-2glas-BU", "BUVD-tripleglas": "BUVD-var-3glas-BU", "BUVD-dubbelglas binnen": "BUVD-var-2glas-BI", "BUVD-tripleglas binnen": "BUVD-var-3glas-BI", "BUVD-paneel": "BUVD-var-paneel", "BUVD-glaspaneel": "BUVD-var-glaspaneel", "BUVD-draaikiep": ["BUVD-var-draaikiep", "BUVD-basis-draaikiep"] }; // Construct the key for the current combination const key = `${radioValue}-${selectValue}`; // Hide all elements Object.values(combinationMap).flat().forEach(id => { const gElement = document.getElementById(id); if (gElement) { gElement.setAttribute("display", "none"); } }); // Show the element(s) for the hovered option if (combinationMap[key]) { const targetIds = Array.isArray(combinationMap[key]) ? combinationMap[key] : [combinationMap[key]]; targetIds.forEach(targetId => { const targetElement = document.getElementById(targetId); if (targetElement) { targetElement.setAttribute("display", "block"); } }); } } // Attach hover functionality to dropdown fields [ "form-field-1VVI1", "form-field-2VVI1", "form-field-2VVI2", "form-field-3VVI1", "form-field-3VVI2", "form-field-3VVI3", "form-field-4VVI1", "form-field-4VVI2", "form-field-4VVI3", "form-field-4VVI4", "form-field-5VVI1", "form-field-5VVI2", "form-field-5VVI3", "form-field-5VVI4", "form-field-5VVI5", "form-field-1VVU1", "form-field-2VVU1", "form-field-2VVU2", "form-field-3VVU1", "form-field-3VVU2", "form-field-3VVU3", "form-field-4VVU1", "form-field-4VVU2", "form-field-4VVU3", "form-field-4VVU4", "form-field-5VVU1", "form-field-5VVU2", "form-field-5VVU3", "form-field-5VVU4", "form-field-5VVU5", ].forEach(id => { const element = document.getElementById(id); if (element) { // Attach mouseover to dropdown fields element.addEventListener("mouseover", showElementOnHover); } });

VAKVULLING VISUAL

SCHALEN

//// SCHALEN //// document.addEventListener("DOMContentLoaded", () => { const form = document.querySelector('form[name="TIMBERPLUS_BESTELFORMULIER"]'); const svgElement = document.getElementById("alle-vakken"); function scaleSvgViewBox() { let maxCoordinate = 0; // Find the highest polygon position in terms of x and y values svgElement.querySelectorAll("polygon").forEach((polygon) => { const points = polygon.getAttribute("points").trim().split(/\s+/).map(parseFloat); points.forEach((coord) => { if (coord > maxCoordinate) maxCoordinate = coord; }); }); // Set the viewBox size using the highest coordinate svgElement.setAttribute("viewBox", `0 0 ${maxCoordinate} 114`); } // Add a 20ms delay before executing scaleSvgViewBox function delayedScaleSvgViewBox() { setTimeout(scaleSvgViewBox, 20); } // Listen for changes in the form and call delayedScaleSvgViewBox form.addEventListener("input", delayedScaleSvgViewBox); // Initial scaling based on the current SVG scaleSvgViewBox(); });

DRAAIRICHTING

SPONNINGMATEN

AANTAL VAKKEN

//// AANTAL VAKKEN //// document.addEventListener("DOMContentLoaded", function () { const dorpeltypeRadios = document.querySelectorAll("input[name='form_fields[dorpeltype]']"); const vakkenRadios = document.querySelectorAll("input[name='form_fields[vakken]']"); const svgContainer = document.getElementById("alle-vakken"); const dorpelBK = document.getElementById("dorpel-BK"); // Function to update visibility based on selected values function updateVisibility() { // Get selected values for dorpeltype and vakken const selectedDorpeltype = document.querySelector("input[name='form_fields[dorpeltype]']:checked")?.value; const selectedVakken = document.querySelector("input[name='form_fields[vakken]']:checked")?.value; if (!selectedDorpeltype || !selectedVakken) return; // Exit if no values are selected // Hide all elements in the SVG by setting display="none" svgContainer.querySelectorAll("g").forEach(g => { g.setAttribute("display", "none"); }); // Determine the visibility criteria based on selected values let idPattern1, idPattern2; if (selectedDorpeltype === "BI") { idPattern1 = `vak-${selectedVakken[0]}-BI`; idPattern2 = `vak-${selectedVakken[0]}-SI`; } else if (selectedDorpeltype === "BUVD") { idPattern1 = `vak-${selectedVakken[0]}-BU`; idPattern2 = `vak-${selectedVakken[0]}-SU`; } // Show matching elements by setting display="block" if (idPattern1 && idPattern2) { svgContainer.querySelectorAll(`g[id*='${idPattern1}'], g[id*='${idPattern2}']`).forEach(g => { g.setAttribute("display", "block"); }); } // Ensure dorpel-BK is always visible if (dorpelBK) { dorpelBK.setAttribute("display", "block"); } } // Attach event listeners to radio buttons dorpeltypeRadios.forEach(radio => { radio.addEventListener("change", updateVisibility); }); vakkenRadios.forEach(radio => { radio.addEventListener("change", updateVisibility); }); // Initial visibility setup updateVisibility(); });

NEUTSPONNINGEN

//// BU SPONNINGDIEPTE NEUTEN //// document.addEventListener("DOMContentLoaded", function () { // Function to calculate yValue and update the polygons function updatePolygonPoints() { // Retrieve selected values from the Elementor PRO form fields const sponningDiepteValue = parseFloat(document.querySelector("input[name='form_fields[sponningdiepte_buvd]']:checked")?.value || 0); const dorpelBreedteValue = parseFloat(document.querySelector("input[name='form_fields[dorpelbreedte]']:checked")?.value || 0); const exclusionRules = { "form_fields[1VVU1]": { excludedIds: ["vak-1-BU-L", "vak-1-BU-R"], positions: [] }, "form_fields[2VVU1]": { excludedIds: ["vak-2-BU-L"], positions: [{ id: "vak-2-BU-T1", exclude: [21, 23] }] }, "form_fields[2VVU2]": { excludedIds: ["vak-2-BU-R"], positions: [{ id: "vak-2-BU-T1", exclude: [5, 7] }] }, "form_fields[3VVU1]": { excludedIds: ["vak-3-BU-L"], positions: [{ id: "vak-3-BU-T1", exclude: [21, 23] }] }, "form_fields[3VVU2]": { excludedIds: [], positions: [{ id: "vak-3-BU-T1", exclude: [5, 7] }, { id: "vak-3-BU-T2", exclude: [21, 23] }] }, "form_fields[3VVU3]": { excludedIds: ["vak-3-BU-R"], positions: [{ id: "vak-3-BU-T2", exclude: [5, 7] }] }, "form_fields[4VVU1]": { excludedIds: ["vak-4-BU-L"], positions: [{ id: "vak-4-BU-T1", exclude: [21, 23] }] }, "form_fields[4VVU2]": { excludedIds: [], positions: [{ id: "vak-4-BU-T1", exclude: [5, 7] }, { id: "vak-4-BU-T2", exclude: [21, 23] }] }, "form_fields[4VVU3]": { excludedIds: ["vak-4-BU-T3"], positions: [{ id: "vak-4-BU-T2", exclude: [5, 7] }] }, "form_fields[4VVU4]": { excludedIds: ["vak-4-BU-R"], positions: [{ id: "vak-4-BU-T3", exclude: [5, 7] }] }, "form_fields[5VVU1]": { excludedIds: ["vak-5-BU-L"], positions: [{ id: "vak-5-BU-T1", exclude: [21, 23] }] }, "form_fields[5VVU2]": { excludedIds: [], positions: [{ id: "vak-5-BU-T1", exclude: [5, 7] }, { id: "vak-5-BU-T2", exclude: [21, 23] }] }, "form_fields[5VVU3]": { excludedIds: [], positions: [{ id: "vak-5-BU-T2", exclude: [5, 7] }, { id: "vak-5-BU-T3", exclude: [21, 23] }] }, "form_fields[5VVU4]": { excludedIds: [], positions: [{ id: "vak-5-BU-T3", exclude: [5, 7] }, { id: "vak-5-BU-T4", exclude: [21, 23] }] }, "form_fields[5VVU5]": { excludedIds: ["vak-5-BU-R"], positions: [{ id: "vak-5-BU-T4", exclude: [5, 7] }] } }; // Check dropdown exclusions function isExcluded(id, position) { for (let fieldName in exclusionRules) { const dropdown = document.querySelector(`select[name='${fieldName}']`); if (dropdown && ["dubbelglas binnen", "tripleglas binnen", "draaikiep"].includes(dropdown.value)) { const { excludedIds, positions } = exclusionRules[fieldName]; if (excludedIds.includes(id)) return true; const positionRule = positions.find(p => p.id === id); if (positionRule && positionRule.exclude.includes(position)) return true; } } return false; } // Calculate yValue const yValue = dorpelBreedteValue - sponningDiepteValue; // List of element IDs for positions 5 and 7 const idsForPositions5and7 = [ "vak-1-BU-L", "vak-1-BU-R", "vak-2-BU-L", "vak-2-BU-T1", "vak-2-BU-R", "vak-3-BU-L", "vak-3-BU-T1", "vak-3-BU-T2", "vak-3-BU-R", "vak-4-BU-L", "vak-4-BU-T1", "vak-4-BU-T2", "vak-4-BU-T3", "vak-4-BU-R", "vak-5-BU-L", "vak-5-BU-T1", "vak-5-BU-T2", "vak-5-BU-T3", "vak-5-BU-T4", "vak-5-BU-R" ]; // List of element IDs for positions 21 and 23 const idsForPositions21and23 = [ "vak-2-BU-T1", "vak-3-BU-T1", "vak-3-BU-T2", "vak-4-BU-T1", "vak-4-BU-T2", "vak-4-BU-T3", "vak-5-BU-T1", "vak-5-BU-T2", "vak-5-BU-T3", "vak-5-BU-T4" ]; // Get the SVG element const svgElement = document.getElementById("alle-vakken"); if (!svgElement) return; // Update points for positions 5 and 7 idsForPositions5and7.forEach(id => { const gElement = svgElement.querySelector(`#${id}`); if (gElement && !isExcluded(id, 5)) { const polygon = gElement.querySelector("polygon"); if (polygon) { const points = polygon.getAttribute("points").trim().split(/\s+/).map(parseFloat); if (points.length > 7) { points[5] = yValue; // Replace position 5 points[7] = yValue; // Replace position 7 polygon.setAttribute("points", points.join(" ")); } } } }); // Update points for positions 21 and 23 idsForPositions21and23.forEach(id => { const gElement = svgElement.querySelector(`#${id}`); if (gElement && !isExcluded(id, 21)) { const polygon = gElement.querySelector("polygon"); if (polygon) { const points = polygon.getAttribute("points").trim().split(/\s+/).map(parseFloat); if (points.length > 23) { points[21] = yValue; // Replace position 21 points[23] = yValue; // Replace position 23 polygon.setAttribute("points", points.join(" ")); } } } }); } // Attach event listeners to the radio buttons const sponningDiepteRadios = document.querySelectorAll("input[name='form_fields[sponningdiepte_buvd]']"); const dorpelBreedteRadios = document.querySelectorAll("input[name='form_fields[dorpelbreedte]']"); const dropdowns = document.querySelectorAll("select[name^='form_fields']"); sponningDiepteRadios.forEach(radio => radio.addEventListener("change", updatePolygonPoints)); dorpelBreedteRadios.forEach(radio => radio.addEventListener("change", updatePolygonPoints)); dropdowns.forEach(dropdown => dropdown.addEventListener("change", updatePolygonPoints)); // Initial update on page load updatePolygonPoints(); });

BOORGATEN

KALKSPONNING BI

//// L KALKSPONNINGDIEPTE //// document.addEventListener("DOMContentLoaded", function() { const inputField = document.getElementById("form-field-Ldiepte"); let previousValue = parseFloat(inputField.value) || 10; // Initial value, fallback to 10 inputField.addEventListener("input", function() { const newValue = parseFloat(inputField.value) || 0; // Default to 0 if input is empty or NaN const SpbValue4 = newValue - previousValue; // Calculate the difference previousValue = newValue; // Update previousValue to the current input // Function to adjust specified points function adjustPolygonPoints(points, indices, adjustment) { let pointsArray = points.trim().split(" ").map(Number); indices.forEach(index => { if (index < pointsArray.length) { pointsArray[index] += adjustment; } }); return pointsArray.join(" "); } // Adjust points 19 and 21 of polygons in elements with IDs containing '-L' document.querySelectorAll("g[id*='-L']").forEach(g => { const polygon = g.querySelector("polygon"); if (polygon) { const points = polygon.getAttribute("points"); const updatedPoints = adjustPolygonPoints(points, [18, 20], SpbValue4); // 19th and 21st points (0-based) polygon.setAttribute("points", updatedPoints); } }); }); });
//// R KALKSPONNINGDIEPTE //// document.addEventListener("DOMContentLoaded", function() { const inputField = document.getElementById("form-field-Rdiepte"); let previousValue = parseFloat(inputField.value) || 10; // Initial value, fallback to 10 inputField.addEventListener("input", function() { const newValue = parseFloat(inputField.value) || 0; // Default to 0 if input is empty or NaN const SpbValue4 = previousValue - newValue; // Calculate the difference previousValue = newValue; // Update previousValue to the current input // Function to adjust specified points function adjustPolygonPoints(points, indices, adjustment) { let pointsArray = points.trim().split(" ").map(Number); indices.forEach(index => { if (index < pointsArray.length) { pointsArray[index] += adjustment; } }); return pointsArray.join(" "); } // Adjust points 19 and 21 of polygons in elements with IDs containing '-R' document.querySelectorAll("g[id*='-R']").forEach(g => { const polygon = g.querySelector("polygon"); if (polygon) { const points = polygon.getAttribute("points"); const updatedPoints = adjustPolygonPoints(points, [18, 20], SpbValue4); // 19th and 21st points (0-based) polygon.setAttribute("points", updatedPoints); } }); }); });

KALKSPONNING BU

NEUTBREEDTE BI

NEUTBREEDTE BU

DAGMATEN BI

DAGMATEN BU

DORPELLIJNTJES BU/BI

(FOUT)MELDING BOORGATEN

document.addEventListener("DOMContentLoaded", () => { const form = document.querySelector('form[name="TIMBERPLUS_BESTELFORMULIER"]'); const svgElement = document.getElementById("alle-vakken"); const messagePopup = document.getElementById("message-popup"); // Create an offscreen canvas to render the SVG const canvas = document.createElement("canvas"); const ctx = canvas.getContext("2d"); // Reference color for the circle's red const TARGET_COLOR = { r: 208, g: 46, b: 38 }; // Core color const COLOR_TOLERANCE = 105; // Euclidean distance tolerance // Function to calculate Euclidean distance in RGB space function colorDistance(r1, g1, b1, r2, g2, b2) { return Math.sqrt( Math.pow(r1 - r2, 2) + Math.pow(g1 - g2, 2) + Math.pow(b1 - b2, 2) ); } // Function to check if a color matches the target with tolerance function isCloseToRed(r, g, b, a) { // Normalize alpha to account for anti-aliasing const alphaFactor = a / 255; // Normalize alpha to [0, 1] const normalizedR = r * alphaFactor; const normalizedG = g * alphaFactor; const normalizedB = b * alphaFactor; // Calculate color distance const distance = colorDistance( normalizedR, normalizedG, normalizedB, TARGET_COLOR.r, TARGET_COLOR.g, TARGET_COLOR.b ); // Check if the color is within tolerance return distance <= COLOR_TOLERANCE; } let debounceTimeout; // For debounce mechanism function checkForRedPixels() { const viewBox = svgElement.viewBox.baseVal; const originalWidth = viewBox.width; const originalHeight = viewBox.height; canvas.width = originalWidth; canvas.height = originalHeight; // Serialize SVG and draw it on the canvas const data = new XMLSerializer().serializeToString(svgElement); const svgBlob = new Blob([data], { type: "image/svg+xml;charset=utf-8" }); const url = URL.createObjectURL(svgBlob); const img = new Image(); img.onload = () => { ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.drawImage(img, 0, 0, canvas.width, canvas.height); URL.revokeObjectURL(url); // Get pixel data const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height).data; // Loop through all pixels for (let i = 0; i < imageData.length; i += 4) { const [r, g, b, a] = imageData.slice(i, i + 4); if (isCloseToRed(r, g, b, a)) { messagePopup.style.display = "block"; // Show the popup return; // Exit on first match } } // Hide the popup if no red pixels are found messagePopup.style.display = "none"; }; img.src = url; } function debounceCheck() { clearTimeout(debounceTimeout); debounceTimeout = setTimeout(checkForRedPixels, 200); // Respect 200ms delay } // Trigger detection on form input changes form.addEventListener("input", debounceCheck); // Continuous monitoring using MutationObserver for SVG changes const observer = new MutationObserver(mutations => { mutations.forEach(mutation => { if (mutation.target.nodeName === "G" || mutation.target.nodeName === "CIRCLE") { debounceCheck(); } }); }); observer.observe(svgElement, { attributes: true, // Watch for attribute changes childList: true, // Watch for added/removed child elements subtree: true, // Watch all descendants }); // Initial check in case pre-existing conditions trigger the popup debounceCheck(); });

DORPELLENGTE

//// BLOCK SUBMIT WHEN POPUP //// document.addEventListener("DOMContentLoaded", function () { function checkPopupAndDisable() { const popup1 = document.getElementById("message-6000bi"); const popup2 = document.getElementById("message-6000bu"); const popup3 = document.getElementById("message-popup"); const submitButton = document.getElementById("SUBMITBUTTON"); if ((popup1 && popup1.style.display !== "none") || (popup2 && popup2.style.display !== "none") || (popup3 && popup3.style.display !== "none")) { // Disable the submit button if (submitButton) { submitButton.disabled = true; submitButton.style.opacity = "0.5"; // Optional: Reduce opacity to indicate disabled state submitButton.style.pointerEvents = "none"; // Prevent clicks } // Disable the Enter key document.addEventListener("keydown", disableEnterKey); } else { // Enable the submit button if the pop-up is not visible if (submitButton) { submitButton.disabled = false; submitButton.style.opacity = "1"; // Reset opacity submitButton.style.pointerEvents = "auto"; // Enable clicks } document.removeEventListener("keydown", disableEnterKey); } } function disableEnterKey(event) { if (event.key === "Enter") { event.preventDefault(); } } // Periodically check if the pop-up becomes visible setInterval(checkPopupAndDisable, 500); // Check every 500ms });

RESET FORM FIELDS

//// FORM SUBMIT CONTROLLER (MERGED DTS TIMBER) //// (function () { document.addEventListener("DOMContentLoaded", () => { const form = document.querySelector("form[name='DTS-TIMBER_BESTELFORMULIER']"); if (!form) return; const submitBtn = document.getElementById("SUBMITBUTTON") || form.querySelector("[type='submit']"); if (!submitBtn) return; // Compat (als je elders nog shouldSubmit gebruikt) window.shouldSubmit = true; // ---------------- Fade-out CSS (1x) ---------------- (function injectFadeCss() { if (document.getElementById("page-fade-css")) return; const css = ` html.fade-out, body.fade-out { opacity: 0; transition: opacity 1000ms ease; } `; const style = document.createElement("style"); style.id = "page-fade-css"; style.appendChild(document.createTextNode(css)); document.head.appendChild(style); })(); // ---------------- SVG helpers ---------------- function hideTopLevelGroups(svgId) { const svg = document.getElementById(svgId); if (!svg) return; // Alleen top-level onder de svg.querySelectorAll("g").forEach((g) => { if (g.parentNode === svg) g.setAttribute("display", "none"); }); } function performReset() { hideTopLevelGroups("alle-vakken"); hideTopLevelGroups("dorpel"); } function svgToPngDataUrl(svgEl, scale = 1) { return new Promise((resolve, reject) => { try { const svgData = new XMLSerializer().serializeToString(svgEl); const canvas = document.createElement("canvas"); const ctx = canvas.getContext("2d"); const viewBox = svgEl.getAttribute("viewBox"); let width, height; if (viewBox) { const vb = viewBox.trim().split(/\s+|,/).map(parseFloat); width = vb[2]; height = vb[3]; } else { const bbox = svgEl.getBBox(); width = bbox.width; height = bbox.height; } canvas.width = Math.ceil(width * scale); canvas.height = Math.ceil(height * scale); ctx.setTransform(scale, 0, 0, scale, 0, 0); // Blob URL i.p.v. btoa (unicode-safe) const blob = new Blob([svgData], { type: "image/svg+xml;charset=utf-8" }); const url = URL.createObjectURL(blob); const img = new Image(); img.onload = () => { try { ctx.drawImage(img, 0, 0); URL.revokeObjectURL(url); resolve(canvas.toDataURL("image/png")); } catch (e) { URL.revokeObjectURL(url); reject(e); } }; img.onerror = (e) => { URL.revokeObjectURL(url); reject(e); }; img.src = url; } catch (e) { reject(e); } }); } // ---------------- Rules (vul-in/visibility) ---------------- function setSubmitVisible(ok) { submitBtn.style.display = ok ? "" : "none"; submitBtn.disabled = !ok; submitBtn.setAttribute("aria-disabled", String(!ok)); } function getCheckedValue(name) { const el = form.querySelector(`input[name="${name}"]:checked`); return el ? el.value : null; } function getVakkenCount() { const v = getCheckedValue("form_fields[vakken]"); // "1-vaks" .. "5-vaks" const m = (v || "").match(/^([1-5])\-vaks$/); return m ? parseInt(m[1], 10) : null; } function getDorpelType() { return getCheckedValue("form_fields[dorpeltype]"); // BI / BUVD / HS / KVT (in jouw form vaak BUVD voor HS/KVT) } function isVisible(el) { return !!el && el.offsetParent !== null; } function prefixForType(type) { // Zoals in je hide-script: BI => VVI, anders VVU return type === "BI" ? "VVI" : "VVU"; } function isSubmitAllowed() { const type = getDorpelType(); const n = getVakkenCount(); if (!type || !n) return false; const prefix = prefixForType(type); for (let i = 1; i <= n; i++) { const fieldId = `${n}${prefix}${i}`; // bv. 2VVU1 of 3VVI2 const sel = form.querySelector(`select[name="form_fields[${fieldId}]"]`); if (!sel) return false; if (!isVisible(sel)) return false; if (String(sel.value).toLowerCase() === "vul in") return false; } return true; } // Debounce update let rafId = null; const scheduleUpdate = () => { if (rafId) return; rafId = requestAnimationFrame(() => { rafId = null; setSubmitVisible(isSubmitAllowed()); }); }; // Start verborgen zodat hij niet flasht setSubmitVisible(false); scheduleUpdate(); form.addEventListener("change", scheduleUpdate, true); form.addEventListener("input", scheduleUpdate, true); setInterval(scheduleUpdate, 300); // vang Elementor show/hide zonder change/input // ---------------- Prevent Enter submit ---------------- form.addEventListener("keydown", (event) => { if (event.key === "Enter") event.preventDefault(); }); // ---------------- Extra validations (sluitpot) ---------------- function validateSluitpotOrAlert() { const sluitpotJa = form.querySelector("input[name='form_fields[sluitpot]'][value='ja']:checked"); if (!sluitpotJa) return true; const x = form.querySelector("#form-field-X_sluitpot"); const y = form.querySelector("#form-field-Y_sluitpot"); if (!x?.value?.trim() || !y?.value?.trim()) { alert("Vul de hartafstand waarden in als 'Ja' is geselecteerd voor sluitpot."); return false; } return true; } // ---------------- Reload after add-to-cart success (fade-out 1000ms) ---------------- function reloadPageAfterSuccess() { if (window.__DTS_RELOAD_SCHEDULED__) return; window.__DTS_RELOAD_SCHEDULED__ = true; // Start fade-out document.documentElement.classList.add("fade-out"); document.body.classList.add("fade-out"); // Reload NA fade (1000ms) setTimeout(() => { window.location.reload(); }, 1000); } // Elementor Forms AJAX success hook if (window.jQuery) { window.jQuery(document).on("submit_success", function (event) { const targetForm = event && event.target ? event.target : null; if (targetForm === form) { reloadPageAfterSuccess(); } }); } // Woo fallback (als theme/woocommerce dit event fired) document.body.addEventListener("added_to_cart", function () { reloadPageAfterSuccess(); }); // ---------------- Submit interceptor (PNG + block rules) ---------------- form.addEventListener( "submit", async (e) => { // voorkom loop bij re-submit if (form.dataset.pngDone === "1") return; window.shouldSubmit = true; // 1) Block als 'vul in' nog niet ok is if (!isSubmitAllowed()) { e.preventDefault(); window.shouldSubmit = false; alert("Niet alle vakvullingen zijn ingevuld."); scheduleUpdate(); return; } // 2) Sluitpot validatie if (!validateSluitpotOrAlert()) { e.preventDefault(); window.shouldSubmit = false; return; } // 3) Browser validity if (!form.checkValidity()) { e.preventDefault(); form.reportValidity(); window.shouldSubmit = false; return; } // 4) PNG maken van SVG (alle-vakken) const svgEl = document.getElementById("alle-vakken"); if (!svgEl) return; // fail-open e.preventDefault(); e.stopImmediatePropagation(); try { const pngData = await svgToPngDataUrl(svgEl, 1); // upsert hidden input let input = form.querySelector('input[name="svg_png_data"]'); if (!input) { input = document.createElement("input"); input.type = "hidden"; input.name = "svg_png_data"; form.appendChild(input); } input.value = pngData; // 5) Reset/hide na PNG performReset(); // 6) Submit opnieuw (Elementor/AJAX-vriendelijk) form.dataset.pngDone = "1"; if (window.jQuery) { jQuery(form).trigger("submit"); } else if (typeof form.requestSubmit === "function") { form.requestSubmit(); } else { form.submit(); } } catch (err) { console.error("SVG->PNG failed:", err); // fail-open: alsnog submit form.dataset.pngDone = "1"; if (window.jQuery) jQuery(form).trigger("submit"); else form.submit(); } }, true ); }); })();
//// CREATE PNG -> HIDE SVG AFTER SUBMIT //// document.addEventListener('DOMContentLoaded', () => { const hideSvgGroups = (svgId) => { const svg = document.getElementById(svgId); if (svg) { const groups = svg.querySelectorAll('g'); groups.forEach((group) => { if (group.parentNode === svg) { group.setAttribute('display', 'none'); } }); } }; const performReset = () => { hideSvgGroups('alle-vakken'); hideSvgGroups('dorpel'); }; const form = document.querySelector('form[name="TIMBERPLUS_BESTELFORMULIER"]'); const svgElement = document.getElementById('alle-vakken'); const submitButton = document.getElementById('SUBMITBUTTON'); if (form && svgElement && submitButton) { submitButton.addEventListener('click', (event) => { if (typeof shouldSubmit !== 'undefined' && !shouldSubmit) { event.preventDefault(); return; } if (!form.checkValidity()) { form.reportValidity(); event.preventDefault(); } else { // === STEP 1: Convert SVG to PNG BEFORE hiding === var svgData = new XMLSerializer().serializeToString(svgElement); var canvas = document.createElement("canvas"); var ctx = canvas.getContext("2d"); var img = new Image(); // Extract viewBox values var viewBox = svgElement.getAttribute('viewBox'); if (viewBox) { var viewBoxValues = viewBox.split(' '); var vbX = parseFloat(viewBoxValues[0]); var vbY = parseFloat(viewBoxValues[1]); var vbWidth = parseFloat(viewBoxValues[2]); var vbHeight = parseFloat(viewBoxValues[3]); // Set canvas size based on viewBox × 5 canvas.width = vbWidth * 1; canvas.height = vbHeight * 1; // Draw at 5x scale, adjusting ctx.scale(1, 1); } else { // Fallback: use BBox if no viewBox var bbox = svgElement.getBBox(); canvas.width = bbox.width * 1; canvas.height = bbox.height * 1; ctx.scale(1, 1); } img.onload = function () { ctx.drawImage(img, 0, 0); var pngData = canvas.toDataURL("image/png"); // Add hidden input for PNG data var input = document.createElement("input"); input.type = "hidden"; input.name = "svg_png_data"; input.value = pngData; form.appendChild(input); // === STEP 2: Hide SVG after PNG === performReset(); // === STEP 3: Trigger Elementor form submit === event.preventDefault(); jQuery(form).trigger('submit'); }; img.src = 'data:image/svg+xml;base64,' + btoa(svgData); // Prevent default submit while PNG processing event.preventDefault(); } }); } });

EXTRA’S KADER/DRAAIKIEP

COOKIES

//// COOKIES //// document.addEventListener("DOMContentLoaded", function () { const form = document.querySelector('.elementor-form'); console.log("Form found:", form); function setCookie(name, value) { const days = 365; const date = new Date(); date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); const cookie = `${name}=${value}; expires=${date.toUTCString()}; path=/`; document.cookie = cookie; console.log(`Cookie set: ${cookie}`); } function getCookie(name) { const cookies = document.cookie.split('; '); for (let i = 0; i < cookies.length; i++) { const [key, value] = cookies[i].split('='); if (key === name) return decodeURIComponent(value); } return null; } const biFields = { BI_Vsponningbreedte: 'form_fields[BI_Vsponningbreedte]', BI_Vsponningdiepte: 'form_fields[BI_Vsponningdiepte]', sponningdiepte_bi: 'form_fields[sponningdiepte_bi]', }; const buFields = { BU_Vsponningbreedte: 'form_fields[BU_Vsponningbreedte]', BU_Vsponningdiepte: 'form_fields[BU_Vsponningdiepte]', sponningdiepte_buvd: 'form_fields[sponningdiepte_buvd]', draaikiepsponningbreedte: 'form_fields[draaikiepsponningbreedte]', draaikiepsponningdiepte: 'form_fields[draaikiepsponningdiepte]', draaikiepvoorsponningbreedte: 'form_fields[draaikiepvoorsponningbreedte]', draaikiepvoorsponningdiepte: 'form_fields[draaikiepvoorsponningdiepte]', wisselsponningdiepte: 'form_fields[wisselsponningdiepte]', wisselvoorsponningbreedte: 'form_fields[wisselvoorsponningbreedte]', wisselvoorsponningdiepte: 'form_fields[wisselvoorsponningdiepte]', draaikiepnokdiepte: 'form_fields[draaikiepnokdiepte]', draaikiepnokbreedte: 'form_fields[draaikiepnokbreedte]', // NIEUW KalkspL: 'form_fields[KalkspL]', KalkspR: 'form_fields[KalkspR]', }; const allFields = { ...biFields, ...buFields }; function saveFieldToCookie(key, name) { const inputs = form.querySelectorAll(`[name="${name}"]`); if (!inputs.length) { console.log(`Input(s) not found for ${name}`); return; } const firstInput = inputs[0]; let value = ''; if (firstInput.type === 'radio') { const checked = form.querySelector(`[name="${name}"]:checked`); value = checked ? checked.value : ''; } else { value = firstInput.value; } setCookie(key, encodeURIComponent(value)); console.log(`Saved ${key}: ${value}`); } function saveSpecifiedFieldsToCookies() { console.log("Saving fields to cookies..."); for (const [key, name] of Object.entries(allFields)) { saveFieldToCookie(key, name); } } function fillFieldsFromCookies() { console.log("Filling fields from cookies..."); for (const [key, name] of Object.entries(allFields)) { const value = getCookie(key); if (value === null) { console.log(`No cookie found for ${key}`); continue; } const inputs = form.querySelectorAll(`[name="${name}"]`); if (!inputs.length) { console.log(`Input(s) not found for ${name}`); continue; } const firstInput = inputs[0]; if (firstInput.type === 'radio') { const radio = form.querySelector(`[name="${name}"][value="${CSS.escape(value)}"]`); if (radio) { radio.checked = true; radio.dispatchEvent(new Event('change', { bubbles: true })); } } else { firstInput.value = value; firstInput.setAttribute('value', value); firstInput.dispatchEvent(new Event('input', { bubbles: true })); } console.log(`Filled ${name} with ${value}`); } } function addInputListeners() { for (const [key, name] of Object.entries(allFields)) { const inputs = form.querySelectorAll(`[name="${name}"]`); if (!inputs.length) continue; inputs.forEach((input) => { if (input.type === 'radio') { input.addEventListener('change', () => saveFieldToCookie(key, name)); } else { input.addEventListener('input', () => saveFieldToCookie(key, name)); input.addEventListener('change', () => saveFieldToCookie(key, name)); } }); } } if (form) { addInputListeners(); form.addEventListener('submit', function () { console.log("Form submit event triggered"); if (form.checkValidity()) { saveSpecifiedFieldsToCookies(); console.log("Form is valid. Cookies saved before submission."); } else { console.log("Form is not valid"); } }); fillFieldsFromCookies(); jQuery(document).on('submit_success', function(event) { if (event.target === form) { console.log("Elementor form submitted successfully. Reloading page to show updated values."); saveSpecifiedFieldsToCookies(); location.reload(); } }); } else { console.error("Form not found"); } window.addEventListener('load', fillFieldsFromCookies); });
//// COOKIES //// document.addEventListener("DOMContentLoaded", function () { const form = document.querySelector('.elementor-form'); console.log("Form found:", form); // Function to set a cookie with 365-day expiration function setCookie(name, value) { const days = 365; // Set the cookie expiration to 365 days const date = new Date(); date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); // Convert to milliseconds const cookie = `${name}=${value}; expires=${date.toUTCString()}; path=/`; document.cookie = cookie; console.log(`Cookie set: ${cookie}`); } // Function to get a cookie function getCookie(name) { const cookies = document.cookie.split('; '); for (let i = 0; i < cookies.length; i++) { const [key, value] = cookies[i].split('='); if (key === name) return decodeURIComponent(value); } return null; } // Fields for BI and BU (keep your existing field definitions) const biFields = { BI_Vsponningbreedte: 'form_fields[BI_Vsponningbreedte]', BI_Vsponningdiepte: 'form_fields[BI_Vsponningdiepte]', sponningdiepte_bi: 'form_fields[sponningdiepte_bi]', }; const buFields = { BU_Vsponningbreedte: 'form_fields[BU_Vsponningbreedte]', BU_Vsponningdiepte: 'form_fields[BU_Vsponningdiepte]', sponningdiepte_buvd: 'form_fields[sponningdiepte_buvd]', draaikiepsponningbreedte: 'form_fields[draaikiepsponningbreedte]', draaikiepsponningdiepte: 'form_fields[draaikiepsponningdiepte]', draaikiepvoorsponningbreedte: 'form_fields[draaikiepvoorsponningbreedte]', draaikiepvoorsponningdiepte: 'form_fields[draaikiepvoorsponningdiepte]', wisselsponningdiepte: 'form_fields[wisselsponningdiepte]', wisselvoorsponningbreedte: 'form_fields[wisselvoorsponningbreedte]', wisselvoorsponningdiepte: 'form_fields[wisselvoorsponningdiepte]', draaikiepnokdiepte: 'form_fields[draaikiepnokdiepte]', draaikiepnokbreedte: 'form_fields[draaikiepnokbreedte]', }; // Function to save a single field to cookie function saveFieldToCookie(key, name) { const input = form.querySelector(`[name="${name}"]`); if (input) { const value = input.type === 'radio' ? form.querySelector(`[name="${name}"]:checked`)?.value || '' : input.value; setCookie(key, encodeURIComponent(value), 7); console.log(`Saved ${key}: ${value}`); } else { console.log(`Input not found for ${name}`); } } // Function to save specified fields to cookies function saveSpecifiedFieldsToCookies() { console.log("Saving fields to cookies..."); for (const [key, name] of Object.entries({...biFields, ...buFields})) { saveFieldToCookie(key, name); } } // Function to fill form fields from cookies function fillFieldsFromCookies() { console.log("Filling fields from cookies..."); for (const [key, name] of Object.entries({...biFields, ...buFields})) { const value = getCookie(key); if (value !== null) { const input = form.querySelector(`[name="${name}"]`); if (input) { if (input.type === 'radio') { const radio = form.querySelector(`[name="${name}"][value="${value}"]`); if (radio) radio.checked = true; } else { input.value = value; input.setAttribute('value', value); input.dispatchEvent(new Event('input')); } console.log(`Filled ${name} with ${value}`); } else { console.log(`Input not found for ${name}`); } } else { console.log(`No cookie found for ${key}`); } } } // Function to add input listeners to fields function addInputListeners() { for (const [key, name] of Object.entries({...biFields, ...buFields})) { const input = form.querySelector(`[name="${name}"]`); if (input) { input.addEventListener('input', () => saveFieldToCookie(key, name)); if (input.type === 'radio') { input.addEventListener('change', () => saveFieldToCookie(key, name)); } } } } if (form) { // Add input listeners to save cookies on field input addInputListeners(); // Save cookies just before form submission form.addEventListener('submit', function (e) { console.log("Form submit event triggered"); if (form.checkValidity()) { saveSpecifiedFieldsToCookies(); console.log("Form is valid. Cookies saved before submission."); } else { console.log("Form is not valid"); } }); // Fill fields from cookies on page load fillFieldsFromCookies(); // Elementor form submit success event jQuery(document).on('submit_success', function(event, response) { if (event.target === form) { console.log("Elementor form submitted successfully. Reloading page to show updated values."); saveSpecifiedFieldsToCookies(); // Save cookies again just in case location.reload(); // Force page reload to ensure new cookie values are displayed } }); } else { console.error("Form not found"); } // Additional event listener to fill fields after any page load or reload window.addEventListener('load', fillFieldsFromCookies); });
Scroll naar boven

Vragen of opmerkingen

over de DTS TIMBER PLUS configurator?