//// 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();
};
})();
//// 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 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();
});
//// 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();
});
//// 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);
}
});
//// 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();
});
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();
});
//// 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);
});