DTS-Timber_FC.png

DTS TIMBER PLUS
DORPEL
CONFIGURATOR

kg

CO2 opslag 🌿

Dividers Example

Basisgegevens

Dividers Example

Basismaten dorpel

Dividers Example

Basismaten neuten

Two Columns Example

Kalksponning Links


Kalksponning Rechts

Dividers Example

Neutafmetingen

Dividers Example

Voorsponning

Dropdown Example

Boorgatposities - X

Dividers Example

Voorsponning

Dropdown Example

Boorgatposities - X

Dividers Example

Voorsponning

Dropdown Example

Boorgatposities - X

Dividers Example

Voorsponning

Dropdown Example

Boorgatposities - X

Dividers Example

Voorsponning

Dropdown Example

Boorgatposities - X

Dividers Example

Dagmaten

Dividers Example

Vakvullingen

Dividers Example

Vakvullingen

Dividers Example

Vakvullingen

Dividers Example

Vakvullingen

Dividers Example

Vakvullingen

Dividers Example

Vakvullingen

Dividers Example

Vakvullingen

Dividers Example

Vakvullingen

Dividers Example

Vakvullingen

Dividers Example

Vakvullingen

Dividers Example

Kadersponning

Dividers Example

Wisselsponning

Dividers Example

Draaikiepsponning

Dividers Example

Draaikiep nokafmeting

Dividers Example

Sluitpot

17 67x114 47 46.6 4.4 16 12 61 67 69x90 Buitenwerkse kozijnmaat 6 17 90 67 114 32 25 57 17 69x90 12.5 29 55 6 56 Binnen Buiten 67x114 1 2 LET OP !! dichting rondom tpv. binnenblad 17x17 25 32 114 57 17 12.5 29 55 6 67x114 69x90 90x114 69x90 69x90 67 90 57 90 17 32 Buitenwerkse koijnmaat 3 4 detail architect, xx 4 1 2 3 Detaillering draaikiepraam - Houten kozijn: 2x draaikiepraam productdetaillering
////////// PREVENT ENTER KEY SUBMIT //////// document.addEventListener("DOMContentLoaded", () => { const submitBtn = document.getElementById("SUBMITBUTTON"); if (!submitBtn) return; const form = submitBtn.closest("form"); if (!form) return; // Debounce zodat we niet 100x per seconde evalueren let rafId = null; const scheduleUpdate = () => { if (rafId) return; rafId = requestAnimationFrame(() => { rafId = null; update(); }); }; function setSubmitVisible(ok) { // écht verbergen/tonen submitBtn.style.display = ok ? "" : "none"; submitBtn.disabled = !ok; submitBtn.setAttribute("aria-disabled", String(!ok)); } function getCheckedValue(name) { const el = form.querySelector(`input[name="${name}"]:checked`); return el ? el.value : null; } function getVakkenCount() { const v = getCheckedValue("form_fields[vakken]"); // "1-vaks" .. "5-vaks" const m = (v || "").match(/^([1-5])\-vaks$/); return m ? parseInt(m[1], 10) : null; } function getDorpelType() { return getCheckedValue("form_fields[dorpeltype]"); // BI / BUVD / HS / KVT } function isVisible(el) { // betrouwbaar voor display:none (Elementor verbergt vaak via display) return !!el && el.offsetParent !== null; } function prefixForType(type) { return type === "BI" ? "VVI" : "VVU"; } function isSubmitAllowed() { const type = getDorpelType(); const n = getVakkenCount(); if (!type || !n) return false; const prefix = prefixForType(type); for (let i = 1; i <= n; i++) { const fieldId = `${n}${prefix}${i}`; // bv. 2VVU1 of 3VVI2 const sel = form.querySelector(`select[name="form_fields[${fieldId}]"]`); if (!sel) return false; if (!isVisible(sel)) return false; if (sel.value === "vul in") return false; } return true; } function update() { setSubmitVisible(isSubmitAllowed()); } // Start verborgen zodat hij niet “flasht” setSubmitVisible(false); update(); // Update bij elke wijziging form.addEventListener("change", scheduleUpdate, true); form.addEventListener("input", scheduleUpdate, true); // Extra safety: als iemand toch klikt via keyboard/JS submitBtn.addEventListener("click", (e) => { if (!isSubmitAllowed()) { e.preventDefault(); scheduleUpdate(); } }); // Optioneel: Elementor kan velden tonen/verbergen zonder input/change // -> lichte polling (veilig, niet zwaar) setInterval(scheduleUpdate, 300); });
////////// GLOBAL FLAG //////// let shouldSubmit = true; ////////// PREVENT ENTER KEY SUBMIT //////// document.addEventListener("DOMContentLoaded", function () { const form = document.querySelector("form[name='TIMBERPLUS_BESTELFORMULIER']"); const submitButton = document.getElementById("SUBMITBUTTON"); if (form) { // Prevent Enter key from submitting form.addEventListener("keydown", function (event) { if (event.key === "Enter") { event.preventDefault(); } }); } if (submitButton && form) { // MAIN SUBMIT BUTTON CLICK HANDLER submitButton.addEventListener("click", function (event) { shouldSubmit = true; // Reset flag every time ////// VALIDATE DROPDOWNS /////// const dropdowns = form.querySelectorAll("select[name*='VVU'], select[name*='VVI']"); for (let dropdown of dropdowns) { const isHidden = dropdown.closest('.cfef-hidden'); if (!isHidden) { if (dropdown.value === 'vul in') { event.preventDefault(); alert("Niet alle vakvullingen zijn ingevuld."); shouldSubmit = false; return; } } } ////// VALIDATE SLUITPOT /////// const sluitpotRadios = form.querySelectorAll("input[name='form_fields[sluitpot]']"); let sluitpotValue = ''; sluitpotRadios.forEach(radio => { if (radio.checked) { sluitpotValue = radio.value; } }); if (sluitpotValue === 'ja') { const xSluitpot = document.getElementById("form-field-X_sluitpot"); const ySluitpot = document.getElementById("form-field-Y_sluitpot"); if (!xSluitpot.value.trim() || !ySluitpot.value.trim()) { event.preventDefault(); alert("Vul de hartafstand waarden in als 'Ja' is geselecteerd voor sluitpot."); shouldSubmit = false; return; } } ////// CHECK BROWSER FORM VALIDATION /////// if (!form.checkValidity()) { form.reportValidity(); shouldSubmit = false; return; } ////// FADE OUT SCREEN ONLY IF ALL VALIDATIONS PASS /////// if (shouldSubmit) { setTimeout(() => { document.body.style.transition = "opacity 1s ease-in-out"; document.body.style.opacity = "0"; setTimeout(() => { document.body.style.display = "none"; }, 1020); }, 20); } }); } });

WEL OF GEEN VOORSPONNING BI

WEL OF GEEN VOORSPONNING BU

BLOCK SUMBIT ON VUL IN

Kozijnmerk check

BOORGAT-MARGE CORRECTIE

SPINNER

SLUITPOTJES

DRAAIKIEP NOKAFMETINGEN

BOORGATEN DRAAIKIEP

BU NEUTSPONNINGEN VAR/VAST

VOORSPONNING BLOCKERS

BU DRAAIKIEP/WISSEL INITIEEL

VARIATIE DRAAIKIEPSPONNINGEN

VARIATIE WISSELSPONNINGEN

BOORGATEN BIV 21/25

21/25MM SPONNING ON KADERDEUR

CHECK DEFAULT RADIO OPTIONS

HOVER / HITBOXES

VAKVULLING VISUAL

SCHALEN

DRAAIRICHTING

SPONNINGMATEN

AANTAL VAKKEN

NEUTSPONNINGEN

BOORGATEN

KALKSPONNING BI

KALKSPONNING BU

NEUTBREEDTE BI

NEUTBREEDTE BU

DAGMATEN BI

DAGMATEN BU

DORPELLIJNTJES BU/BI

(FOUT)MELDING BOORGATEN

DORPELLENGTE

RESET FORM FIELDS

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

EXTRA’S KADER/DRAAIKIEP

COOKIES

Scroll naar boven

Vragen of opmerkingen

over de DTS TIMBER PLUS configurator?