'use strict'; (function() { // TODO: Refactor steps, display-updating of things, and query aborts into a clearly-defined state machine with transition actions and well-defined states let stepContainer = null; function showNextStep(currentStep) { // Get all steps const steps = Array.from(stepContainer.querySelectorAll('.step')); // Determine this step and next step const nextStep = steps[steps.indexOf(currentStep) + 1]; // Update hidden state of next step nextStep.classList.remove('hidden'); updateProgressIndicator(nextStep.id); return nextStep; } // Register the initializer for the diffusers product group productKindWizards['diffusers'] = function initDiffusersWizard(container) { // // General // // Set step container for use in nextStep function stepContainer = container; // Set up help buttons container.querySelectorAll('button.help-button').forEach(btn => { const helpText = btn.closest('.question').querySelector('.help'); btn.addEventListener('click', () => helpText.classList.toggle('active')); }); // // Next buttons // // Set up next step buttons (and special actions for those buttons) container.querySelectorAll('.next-button').forEach(b => b.addEventListener('click', () => { // Update hidden state of buttons and next step b.classList.add('hidden'); const nextStep = showNextStep(b.closest('.step')); // Scroll the next step into view nextStep.scrollIntoView({ behavior: 'smooth' }); })); // Special case for the button going into the product group step container.querySelector('button[data-action="show-matching-groups"]').addEventListener('click', updateProductGroupUI); // // Category selector // // Set up product category selector and category updating, and locked category handling document.querySelectorAll('#category-selector input').forEach(e => e.addEventListener('change', updateCategorySelectorUI)); // We always run the initial update, even of the input is locked (the actual state of the selectors should // match the preselected category so this is allowed) updateCategorySelectorUI(); // If we have a locked selector, we schedule a progress indicator update for after the initialisation if(document.querySelector('#category-selector:is(.locked)')) { window.setTimeout(() => document.querySelector('.diffusers.step button.next-button').click()); } // Wire up category selector unlock action document.querySelectorAll('button[data-action="unlock-category-selector"]').forEach(btn => { btn.addEventListener('click', () => { document.querySelector('#category-selector').classList.remove('locked'); }); }); // // Situation & Criteria inputs // // Set up temperature UI document.querySelector("#temperature-difference").addEventListener("input", updateTemperatureUI); document.querySelector("#temperature-room").addEventListener("input", updateTemperatureUI); updateTemperatureUI(); // If we change the room name, we update to order card directly document.querySelector('#room-name').addEventListener('change', () => { // LATER: If this also includes changing the room information or selection criteria, call `updateProductGroupUI` instead! if(isProductStepVisible()) { const card = container.querySelector('.order-card'); getOrderCardCallbacks(card).setRoomName(document.querySelector('#room-name').value); } }); // Set up results updating // TODO: Debounce this (especially for the #temperature-* inputs) document.querySelector('#total-air-volume').addEventListener('change', updateProductGroupUI); document.querySelector('#air-direction').addEventListener('change', updateProductGroupUI); document.querySelector('#max-air-speed').addEventListener('change', updateProductGroupUI); document.querySelector('#temperature-room').addEventListener('change', updateProductGroupUI); document.querySelector('#temperature-room').addEventListener('input', updateProductGroupUI); document.querySelector('#temperature-difference').addEventListener('change', updateProductGroupUI); document.querySelector('#temperature-difference').addEventListener('input', updateProductGroupUI); document.querySelector('#number-of-vents').addEventListener('change', updateProductGroupUI); // COMMENTED OUT - may become relevant again later // document.querySelector('#room-height').addEventListener('change', updateProductGroupUI); // document.querySelector('#room-width').addEventListener('change', updateProductGroupUI); // document.querySelector('#room-length').addEventListener('change', updateProductGroupUI); // END COMMENTED OUT document.querySelector('#remove-limits').addEventListener('change', updateProductGroupUI); document.querySelector('#cleanroom-use-filter').addEventListener('change', updateProductGroupUI); document.querySelector('#cleanroom-filter-airflow').addEventListener('change', updateProductGroupUI); document.querySelector('#cleanroom-filter-pressure-drop').addEventListener('change', updateProductGroupUI); document.querySelector('#cleanroom-filter-height').addEventListener('change', updateProductGroupUI); document.querySelector('#cleanroom-filter-width').addEventListener('change', updateProductGroupUI); document.querySelector('#channel-diameter').addEventListener('change', updateProductGroupUI); // // Product groups // // Set up results list expander button document.querySelector('button[data-action="expand-product-groups"]').addEventListener('click', () => { expandProductGroupsUI(); }); document.querySelectorAll('.results-table [data-sort-field]').forEach(f => f.addEventListener('click', () => { const header = f.closest('.results-header'); // Determine current sort order for this field const currentOrder = f.classList.contains('ascending') ? 'ascending' : f.classList.contains('descending') ? 'descending' : 'neutral'; const orderChangeTable = { 'neutral': 'ascending', 'ascending': 'descending', 'descending': 'neutral', }; const newOrder = orderChangeTable[currentOrder]; // Clear out old sorting header.querySelectorAll('[data-sort-field]').forEach(e => e.classList.remove('ascending', 'descending')); if(newOrder != 'neutral') { f.classList.add(newOrder); } reorderProductGroups(); })); // Trigger to modify questions based on the selected product document.querySelectorAll('#category-selector input[name="select_for_rooster"]').forEach(selected => selected.addEventListener('change', modify_questions_by_product)); document.querySelectorAll('#category-selector input[name="select_for_rooster.air-filtration"]').forEach(selected => selected.addEventListener('change', modify_questions_by_product)); modify_questions_by_product(); // trigger to modify filter questions if it is selected document.getElementById("cleanroom-use-filter").addEventListener('change', modify_questions_by_filter_status); modify_questions_by_filter_status(); } function modify_questions_by_filter_status() { // Get the main selected_product const selected_product = document.querySelector("input[name='select_for_rooster']:checked")?.value; const selected_type = document.querySelector("input[name='select_for_rooster.air-filtration']:checked")?.value; const selected_filter = document.querySelector("#cleanroom-use-filter")?.value; const is_custom_filter = selected_filter == "Custom" && selected_product == "rooster.air-filtration" && selected_type == "rooster.air-filtration.cleanroom"; const filters = { "H13": {'flow': 600, 'drop': 100, 'height': 610, 'width': 610}, "H14": {'flow': 600, 'drop': 125, 'height': 610, 'width': 610}, "E10": {'flow': 600, 'drop': 55, 'height': 610, 'width': 610}, "E11": {'flow': 600, 'drop': 60, 'height': 610, 'width': 610}, "E12": {'flow': 600, 'drop': 80, 'height': 610, 'width': 610} }; document.querySelectorAll(".extra-filter-question").forEach(n => { n.classList.toggle('hidden', !is_custom_filter); n.querySelector(':scope input').disabled = !is_custom_filter; }); if(selected_product == "rooster.air-filtration" && selected_type == "rooster.air-filtration.cleanroom") { if (!is_custom_filter && selected_filter != "Without filter") { const filter = filters[selected_filter]; document.querySelector("#cleanroom-filter-airflow").value = filter['flow']; document.querySelector("#cleanroom-filter-pressure-drop").value = filter['drop']; document.querySelector("#cleanroom-filter-height").value = filter['height']; document.querySelector("#cleanroom-filter-width").value = filter['width']; } // update since values may have changed updateProductGroupUI(); } } function modify_questions_by_product() { // Get the main selected_product const selected_product = document.querySelector('input[name="select_for_rooster"]:checked')?.value; const selected_type = document.querySelector('input[name="select_for_rooster.air-filtration"]:checked')?.value; function hideIf(selector, predicate) { document.querySelectorAll(selector).forEach(n => { n.classList.toggle('hidden', predicate); }); } function showIf(selector, predicate) { hideIf(selector, !predicate); } // Customize air direction question and worp text for buitenluchtroosters document.querySelectorAll('#air-direction-question *').forEach(n => { if (n.dataset.labelBuitenrooster) n.textContent = selected_product == 'rooster.buitenlucht' ? n.dataset.labelBuitenrooster : n.dataset.labelDefault; }); document.querySelectorAll('#worp-title').forEach(n => { n.textContent = selected_product == 'rooster.buitenlucht' ? n.dataset.textBuitenrooster : n.dataset.textDefault; }); showIf('.cleanroom-question', selected_product == 'rooster.air-filtration' && selected_type == 'rooster.air-filtration.cleanroom'); showIf('#channel-diameter-question', selected_product == 'rooster.wand-en-kanaal'); // COMMENTED OUT - may become relevant again later // // hide Hoogte / Breedte / Diepte // document.querySelector('#room-height').closest('.question').classList.toggle('hidden', selected_product == 'rooster.buitenlucht'); // END COMMENTED OUT hideIf('#max-air-speed-question', selected_product == 'rooster.buitenlucht' || (selected_product == 'rooster.air-filtration' && (selected_type == 'rooster.air-filtration.fluff-separator' || selected_type == 'rooster.air-filtration.duct-wall-filter-units')) ); hideIf('#temperature-section', selected_product == 'rooster.buitenlucht'); hideIf('#air-direction-question', selected_product == 'rooster.air-filtration' && selected_type == 'rooster.air-filtration.fluff-separator'); if (selected_type == 'rooster.air-filtration.fluff-separator') { // Force air direction to retour when switching to fluff separators document.querySelector('#air-direction-question select').value = 'retour'; updateProductGroupUI(); } modify_questions_by_filter_status(); } function updateCategorySelectorUI() { // Grab the selector and reset it const selector = document.querySelector('#category-selector'); selector.querySelectorAll('.question').forEach(q => q.classList.add('hidden')); /* We start at the root category, and walk through all selector parts from top to bottom, refining the active category as we go. This way, the final state will be that only parts for the active selector are shown, and `activeSelector` will contain the most specific selected category at the end of the process. */ // Start at the root category, as defined in the selector HTML let activeCategory = selector.dataset.root; // Inspect each selector part from top to bottom for(const question of selector.querySelectorAll('.question')) { // If the part's category matches our current category, we need to show it and potentially update the // active category. Otherwise we can leave it hidden. if(question.dataset.category == activeCategory) { // Show the selector part question.classList.remove('hidden'); // See if the user has selected an item from this selector var selectedCategory = question.querySelector(':checked'); if(selectedCategory == null) { question.querySelector('input[type=radio]').checked = true; selectedCategory = question.querySelector(':checked'); } if(selectedCategory.value == ':no-preference') { // The user selected 'no preference', so we are done with updating, and all other parts remain // hidden. break; } else { // The user selected a category, so we make it the active one. activeCategory = selectedCategory.value; } } } // Update if we actually switched category const currentCategory = document.querySelector('#product-category') if(currentCategory.value != activeCategory) { currentCategory.value = activeCategory; updateProductGroupUI(); } } function updateTemperatureUI() { const roomTemperatureInput = document.querySelector('#temperature-room'); const differenceTemperatureInput = document.querySelector('#temperature-difference'); const roomTemperature = Number(roomTemperatureInput.value); const temperatureDifference = Number(differenceTemperatureInput.value); const supplyTemperature = roomTemperature + temperatureDifference; document.querySelectorAll(".temperature-room").forEach(e => e.textContent = roomTemperature); document.querySelectorAll('.temperature-difference').forEach(e => e.textContent = temperatureDifference); document.querySelectorAll(".temperature-supply").forEach(e => e.textContent = supplyTemperature); } /* ** Order submission */ function submitOrder() { const card = document.querySelector('#order-card-container .order-card'); const orders = [getOrderCardCallbacks(card).getOrderData()]; const payload = { orders }; if(document.querySelector('#project-status').value == 'new') { payload.project = { title: document.querySelector('#project-title').value, location: document.querySelector('#project-location').value, } } // We use a hidden form so the data can be submitted and the page redirected in the same request const orderForm = document.querySelector('#order-form'); orderForm.elements['data'].value = JSON.stringify(payload); orderForm.submit(); } /* ** Product Grouping List */ let shouldScrollToProductGroups = true; function isProductGroupStepVisible() { return !document.querySelector('#product-group-step').classList.contains('hidden'); } function updateProductGroupUI() { if(isProductGroupStepVisible()) { hideProductStep(); queryProductGroups(); } } function expandProductGroupsUI() { if(isProductGroupStepVisible()) { const table = document.querySelector('.results-table'); table.classList.remove('collapsed'); table.querySelectorAll('.no-collapse').forEach(e => e.classList.remove('no-collapse')); } } function hideProductGroupStep() { // If we are hiding this piece of the UI, we are also aborting any running queries (since we won't be // displaying them anymore) abortProductGroupQuery(); document.querySelector('#product-group-step').classList.add('hidden'); } let productGroupQueryAborter = null; function abortProductGroupQuery() { if(productGroupQueryAborter) { productGroupQueryAborter.abort(); } } function queryProductGroups() { // Abort any current in-flight request abortProductGroupQuery(); abortProductStepFetch(); // Create new aborter productGroupQueryAborter = new AbortController(); const diffuserQuery = { 'category': document.querySelector('#product-category').value, 'air-direction': document.querySelector('#air-direction').value, 'situation': { 'total-air-volume': document.querySelector('#total-air-volume').valueAsNumber, 'number-of-vents': document.querySelector('#number-of-vents').valueAsNumber, 'max-air-speed': Number(document.querySelector('#max-air-speed').value), 'temperature-room': document.querySelector('#temperature-room').valueAsNumber, 'temperature-supply': document.querySelector('#temperature-room').valueAsNumber + document.querySelector('#temperature-difference').valueAsNumber, 'channel-diameter': document.querySelector('#channel-diameter').valueAsNumber, 'room': { 'height': 0, // COMMENTED OUT: document.querySelector('#room-height').valueAsNumber, 'width': 0, // COMMENTED OUT: document.querySelector('#room-width').valueAsNumber, 'length': 0 // COMMENTED OUT: document.querySelector('#room-length').valueAsNumber, }, 'filter': { 'with_filter': document.querySelector("#cleanroom-use-filter").value != "Without filter", 'airflow': document.querySelector("#cleanroom-filter-airflow").valueAsNumber, 'pressure_drop': document.querySelector("#cleanroom-filter-pressure-drop").valueAsNumber, 'height': document.querySelector("#cleanroom-filter-height").valueAsNumber, 'width': document.querySelector("#cleanroom-filter-width").valueAsNumber, } }, 'ignore-limits': document.querySelector("#remove-limits").checked, }; // Invoke API // TODO: Improved error handling, loading indicator in UI after certain time const mountpoint = $('meta[name="mountpoint"]').attr('content'); fetch(mountpoint + '/diffusers/product-groups?lang=' + document.body.dataset.language, { signal: productGroupQueryAborter.signal, method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(diffuserQuery), }).then(async (response) => { if(response.ok) { expandProductGroupsUI(); const container = document.querySelector('#product-group-container'); container.innerHTML = await response.text(); initProductGroups(container); if(shouldScrollToProductGroups) { shouldScrollToProductGroups = false container.scrollIntoView({ behavior: 'smooth' }); } } else { const errorJson = await response.json(); console.warn('api call result (error):', errorJson); } }).catch(error => { if(error instanceof DOMException) { // Request was aborted in-flight due to user activity return; } else { console.warn('api call error:', error); } }); } function initProductGroups(container) { // Remove any existing attention drawing markers document.querySelectorAll('.question').forEach(e => e.classList.remove('draw-attention')); // Get the table itself const table = container.closest('.results-table'); // If we have no results table, no further initialization can be done const empty = container.querySelector('.empty'); table.classList.toggle('show-empty', empty); if(empty) { // We have an empty result, so do some highlighting magic container.querySelectorAll('label').forEach(e => e.control.closest('.question').classList.add('draw-attention')); return; } // set correct worp unit // Get the main selected_product const selected_product = document.querySelector("input[name='select_for_rooster']:checked").value; // change unit for worp sort section if(selected_product == "rooster.buitenlucht") { const worpUnit = document.querySelector("#worp-title").dataset.unitBuitenrooster; document.querySelectorAll(".worp-unit").forEach(unit => unit.textContent = worpUnit); } else { const worpUnit = document.querySelector("#worp-title").dataset.unitDefault; document.querySelectorAll(".worp-unit").forEach(unit => unit.textContent = worpUnit); } function selectOption(event) { const option = event.target.closest('.option'); const result = event.target.closest('.result'); // Store current selection and unselect if needed const currentSelection = table.querySelector('.selected'); if(currentSelection) { currentSelection.classList.remove('selected'); } // Update visual state of new selection option.classList.add('selected'); result.classList.add('no-collapse'); // Update result table state to collapsed table.classList.add('collapsed'); if(option != currentSelection) { // Show the next step and show the actual product step showNextStep(table.closest('.step')); showProductStep(parseInt(option.dataset.entryId)); } } container.querySelectorAll('.result .option').forEach(r => r.addEventListener('click', e => selectOption(e))); reorderProductGroups(); } function reorderProductGroups() { // Do no work if step is not visible if(!isProductGroupStepVisible()) { return; } const table = document.querySelector('#product-group-step .results-table'); const container = document.querySelector('#product-group-container'); // If there are no groups, we can shortcut reordering if(container.children.length == 0) { return; } // Determine ordering criteria that are applied to each group and to the options inside each group const groupComparisons = []; const optionComparisons = []; table.querySelectorAll('[data-sort-field].ascending, [data-sort-field].descending').forEach(f => { const direction = f.classList.contains('ascending') ? 'ascending' : 'descending'; const field = f.dataset.sortField; // Helpers const [selectValue, numberComparison] = { 'ascending': [(xs) => Math.min(...xs), (a, b) => a - b], 'descending': [(xs) => Math.max(...xs), (a, b) => b - a] }[direction]; const valueFromOption = opt => JSON.parse(opt.dataset.sortValues)[field] ?? 0; // Groups get their value by looking at selecting the appropriate value from their options groupComparisons.push((a, b) => { const aValue = selectValue(Array.from(a.querySelectorAll('.option')).map(valueFromOption)); const bValue = selectValue(Array.from(b.querySelectorAll('.option')).map(valueFromOption)); return numberComparison(aValue, bValue); }); // Options directly compare their values optionComparisons.push((a, b) => numberComparison(valueFromOption(a), valueFromOption(b))); }); // Add natural ordering as tie-breaker (and also to restore natural order if no other ordering criteria // are set), note that the natural order is always in ascending fashion. const naturalOrder = (a, b) => { return parseInt(a.dataset.naturalOrder) - parseInt(b.dataset.naturalOrder); }; groupComparisons.push(naturalOrder); optionComparisons.push(naturalOrder); // Helper to attempt each comparison function in order, only looking at later comparisons if all // comparisons before turn up equal function comparisonsAsFunction(comparisons) { return (a, b) => { for(const c of comparisons) { const r = c(a, b); if(r != 0) { return r; } } return 0; }; } // Reorder result groups and options inside groups Array.from(container.querySelectorAll('.result')) .sort(comparisonsAsFunction(groupComparisons)) .forEach(group => { // Reorder options inside group const options = group.querySelector('.options'); Array.from(options.querySelectorAll('.option')) .sort(comparisonsAsFunction(optionComparisons)) .forEach(option => options.append(option)); // Append the result group to the container container.append(group); }); } /* ** Product specification step */ function isProductStepVisible() { return !document.querySelector('#product-step').classList.contains('hidden'); } function hideProductStep() { abortProductStepFetch(); document.querySelector('#product-step').classList.add('hidden'); } let productStepFetchAborter = null; function abortProductStepFetch() { if(productStepFetchAborter) { productStepFetchAborter.abort(); } } function showProductStep(entryId) { // Abort any current in-flight request abortProductGroupQuery(); abortProductStepFetch(); // Create new aborter productStepFetchAborter = new AbortController(); const lookupIdPayload = { 'entry-id': entryId, 'room-name': document.querySelector('#room-name').value, 'situation':{ 'total-air-volume': document.querySelector('#total-air-volume').valueAsNumber, 'number-of-vents': document.querySelector('#number-of-vents').valueAsNumber, 'max-air-speed': Number(document.querySelector('#max-air-speed').value), 'temperature-room': document.querySelector('#temperature-room').valueAsNumber, 'temperature-supply': document.querySelector('#temperature-room').valueAsNumber + document.querySelector('#temperature-difference').valueAsNumber, 'channel-diameter': document.querySelector('#channel-diameter').valueAsNumber, 'room': { 'height': 0, // COMMENTED OUT: document.querySelector('#room-height').valueAsNumber, 'width': 0, // COMMENTED OUT: document.querySelector('#room-width').valueAsNumber, 'length': 0 // COMMENTED OUT: document.querySelector('#room-length').valueAsNumber, }, 'filter': { 'with_filter': document.querySelector("#cleanroom-use-filter").value != "Without filter", 'airflow': document.querySelector("#cleanroom-filter-airflow").valueAsNumber, 'pressure_drop': document.querySelector("#cleanroom-filter-pressure-drop").valueAsNumber, 'height': document.querySelector("#cleanroom-filter-height").valueAsNumber, 'width': document.querySelector("#cleanroom-filter-width").valueAsNumber, } } }; // invoke API // TODO: Improved error handling, loading indicator in UI after certain time const mountpoint = $('meta[name="mountpoint"]').attr('content'); fetch(mountpoint + '/diffusers/step?lang=' + document.body.dataset.language, { signal: productStepFetchAborter.signal, method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(lookupIdPayload), }).then(async (response) => { if(response.ok) { const container = document.querySelector('#order-card-container'); container.innerHTML = await response.text(); initProductStep(container); } else { const errorJson = await response.json(); console.warn('api call result (error):', errorJson); } }).catch(error => { if(error instanceof DOMException) { // Request was aborted in-flight due to user activity return; } else { console.warn('api call error:', error); } }); } function initProductStep(container) { // Initialize product card const card = container.querySelector('.order-card'); if(card) { initializeOrderCard(card); // Set up the order button container.querySelectorAll('button[data-action="add-to-project"]').forEach(btn => { btn.addEventListener('click', () => { submitOrder(); }); }); container.querySelectorAll('button[data-action="open-in-tab"]').forEach(btn => { btn.addEventListener('click', () => { openOrderCardTab(card); }); }); // Scroll the next step into view card.scrollIntoView({ behavior: 'smooth' }); } } /* ** Diffuser product card initializers */ function makeUnitPriceFetcher(card, updateCallback) { let unitPrice = Number(card.dataset.unitPrice); // XXX: Initially empty let price_components = [] if(card.dataset.priceComponents) { price_components = JSON.parse(card.dataset.priceComponents); } function getUnitPrice() { return unitPrice; } function getPriceComponents() { return price_components; } let aborter = null; function fetchUnitPrice() { // TODO: Improved error handling, loading indicator in UI after certain time if(aborter) { aborter.abort(); } aborter = new AbortController(); const mountpoint = $('meta[name="mountpoint"]').attr('content'); fetch(mountpoint + '/api/price?lang=' + document.body.dataset.language, { signal: aborter.signal, method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(getOrderCardCallbacks(card).getOrderData()), }).then(async (response) => { if(response.ok) { const data = await response.json(); unitPrice = data['unit-price']; price_components = data['components']; updateCallback(); } else { const errorJson = await response.json(); console.warn('api call result (error):', errorJson); } }).catch(error => { if(error instanceof DOMException) { // Request was aborted in-flight due to user activity return; } else { console.warn('api call error:', error); } }); } return [getUnitPrice, fetchUnitPrice, getPriceComponents]; } function makeProductInfoFetcher(card, updateCallback) { let aborter = null; function fetchProductInfo() { // TODO: Improved error handling, loading indicator in UI after certain time if(aborter) { aborter.abort(); } aborter = new AbortController(); const mountpoint = $('meta[name="mountpoint"]').attr('content'); fetch(mountpoint + '/diffusers/product-info?lang=' + document.body.dataset.language, { signal: aborter.signal, method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(getOrderCardCallbacks(card).getOrderData()), }).then(async (response) => { if(response.ok) { const data = await response.json(); updateCallback(data); } else { const errorJson = await response.json(); console.warn('api call result (error):', errorJson); } }).catch(error => { if(error instanceof DOMException) { // Request was aborted in-flight due to user activity return; } else { console.warn('api call error:', error); } }); } return fetchProductInfo; } function commonOrderCardInit(card, callbacks) { // Set up help buttons card.querySelectorAll('button.help-button').forEach(btn => { const helpText = btn.closest('.question').querySelector('.help'); btn.addEventListener('click', () => helpText.classList.toggle('active')); }); const orderQuantity = card.querySelector('[data-question="order-quantity"]'); orderQuantity.addEventListener('change', updateAmountAndPrice); orderQuantity.addEventListener('input', updateAmountAndPrice); orderQuantity.addEventListener('change', fireChoicesChanged); function getQuantity() { return orderQuantity.valueAsNumber; } function getId() { return card.dataset.id; } const roomName = card.querySelector('[data-question="room-name"]'); function getRoomName() { return roomName.value; } function setRoomName(newName) { roomName.value = newName; } const [getUnitPrice, fetchUnitPrice] = makeUnitPriceFetcher(card, updateAmountAndPrice); function updateAmountAndPrice() { card.querySelectorAll('.quantity').forEach(e => e.textContent = getQuantity()); const unitPrice = getUnitPrice(); if(unitPrice >= 0) { card.querySelectorAll('.unit-price').forEach(e => e.textContent = formatMoney(getUnitPrice())); card.querySelectorAll('.total-price').forEach(e => e.textContent = formatMoney(getUnitPrice() * getQuantity())); } else { card.querySelectorAll('.unit-price').forEach(e => e.textContent = '—'); card.querySelectorAll('.total-price').forEach(e => e.textContent = '—'); } card.dispatchEvent(new CustomEvent('priceUpdate', {bubbles: true})); } updateAmountAndPrice(); const fetchProductInfo = makeProductInfoFetcher(card, function updatePerformance(reply) { card.querySelectorAll('.worp-display').forEach(e => e.innerHTML = reply.worp_html); card.querySelectorAll('.drukverlies-display').forEach(e => e.innerHTML = reply.drukverlies_html); card.querySelectorAll('.geluidsdrukniveau-display').forEach(e => e.innerHTML = reply.geluidsdrukniveau_html); card.querySelectorAll('.product-id').forEach(e => e.textContent = reply.order_code_text); card.dispatchEvent(new CustomEvent('performanceUpdate', {bubbles: true})); }); function fireChoicesChanged(event) { card.dispatchEvent(new CustomEvent('choicesChanged', {bubbles: true, detail: event})); } const [choices, choiceElements] = getChoices(card, function() { return card.dataset.direction; }); // Hook up input events to UI update functions for(const el of Object.values(choiceElements)) { el.addEventListener('change', fireChoicesChanged); el.addEventListener('change', fetchUnitPrice); el.addEventListener('change', fetchProductInfo); } // Set up callbacks callbacks.getUnitPrice = getUnitPrice; callbacks.getQuantity = getQuantity; callbacks.getRoomName = getRoomName; callbacks.setRoomName = setRoomName; callbacks.getId = getId; callbacks.getOrderData = function() { return { 'id': getId(), 'quantity': getQuantity(), 'room-name': getRoomName(), 'product': { 'type': card.dataset.productType, 'choices': choices, 'situation': JSON.parse(card.dataset.situation), } } }; fetchProductInfo(); // Hide/show questions and options in .product-options if (typeof orderCardUpdateQuestions === 'function') { card.addEventListener('choicesChanged', function(e) { orderCardUpdateQuestions(card, e); }); orderCardUpdateQuestions(card, { target: card }); } return [choices, choiceElements]; } function updateOptions(node, validOptions) { node.querySelectorAll(':scope option').forEach(n => { n.hidden = !validOptions.includes(n.value); }); // if an invalid option is selected, swap to the first valid option if (!validOptions.includes(node.value) && validOptions.length > 0) { node.value = validOptions[0]; } // hide the entire option if there's nothing to choose node.closest('.question').classList.toggle('hidden', validOptions.length == 0); } function commonWorpdiagramInit(card) { // Update diagram function updateDiagram() { const maxAirSpeed = JSON.parse(card.dataset.situation)['max-air-speed']; card.querySelectorAll('.diagram-max-air-speed').forEach(e => e.textContent = ' ' + maxAirSpeed + ' m/s'); if(card.querySelector('.worp-display .no-value')) { card.querySelectorAll('.diagram-worp-text').forEach(e => e.textContent = e.closest('svg').dataset.worpTranslation); } else { const worp = card.querySelector('.worp-display').textContent; card.querySelectorAll('.diagram-worp-text').forEach(e => e.textContent = e.closest('svg').dataset.worpTranslation + ' = ' + worp); } } card.addEventListener('performanceUpdated', updateDiagram); updateDiagram(); } orderCardInitializers['waterloo-wtf'] = function(card, callbacks) { // Initialize things common to all order cards const [choices, choiceElements] = commonOrderCardInit(card, callbacks); // Update diagram commonWorpdiagramInit(card); // UI updating functions function updateTexts() { // product title const selectedVentOption = choiceElements.vent_shape.querySelector(':checked'); const titleSummary = choices.vent_shape + '-' + choices.diameter + ' – ' + selectedVentOption.dataset.description; card.querySelectorAll('.title-summary').forEach(e => e.textContent = titleSummary); // product marketing blurb const context = Object.assign({direction: card.dataset.direction}, choices); chooseOneProcessing(card.querySelector('.product-blurb'), context); } // Initialize card ui card.addEventListener('choicesChanged', updateTexts); card.addEventListener('choicesChanged', updateQuestions); card.dispatchEvent(new CustomEvent('choicesChanged', {bubbles: true, detail: event})); } orderCardInitializers['waterloo-rwk'] = function(card, callbacks) { // Initialize things common to all order cards const [choices, choiceElements] = commonOrderCardInit(card, callbacks); // Update diagram commonWorpdiagramInit(card); // UI updating functions function updateTexts() { // product title var titleSummary = "RWK-" if(card.dataset.direction == "retour") { titleSummary += 'R-'; } titleSummary += choices.diameter; card.querySelectorAll('.title-summary').forEach(e => e.textContent = titleSummary); // product marketing blurb const context = Object.assign({direction: card.dataset.direction}, choices); chooseOneProcessing(card.querySelector('.product-blurb'), context); } // Initialize card ui card.addEventListener('choicesChanged', updateTexts); card.addEventListener('choicesChanged', updateQuestions); card.dispatchEvent(new CustomEvent('choicesChanged', {bubbles: true, detail: event})); } orderCardInitializers['waterloo-rwi'] = function(card, callbacks) { // Initialize things common to all order cards const [choices, choiceElements] = commonOrderCardInit(card, callbacks); // Update diagram commonWorpdiagramInit(card); // UI updating functions function updateTexts() { // product title var titleSummary = "RWI-" if(card.dataset.direction == "retour") { titleSummary += 'R-'; } titleSummary += choices.diameter; card.querySelectorAll('.title-summary').forEach(e => e.textContent = titleSummary); // product marketing blurb const context = Object.assign({direction: card.dataset.direction}, choices); chooseOneProcessing(card.querySelector('.product-blurb'), context); } // Initialize card ui card.addEventListener('choicesChanged', updateTexts); card.addEventListener('choicesChanged', updateQuestions); card.dispatchEvent(new CustomEvent('choicesChanged', {bubbles: true, detail: event})); } orderCardInitializers['waterloo-av'] = function(card, callbacks) { // Initialize things common to all order cards const [choices, choiceElements] = commonOrderCardInit(card, callbacks); // Update diagram commonWorpdiagramInit(card); // UI updating functions function updateTexts() { // product title var titleSummary = ""; titleSummary += choices.type; titleSummary += "-" titleSummary += choices.diameter; card.querySelectorAll('.title-summary').forEach(e => e.textContent = titleSummary); // product marketing blurb const context = Object.assign({direction: card.dataset.direction}, choices); chooseOneProcessing(card.querySelector('.product-blurb'), context); } // Initialize card ui card.addEventListener('choicesChanged', updateTexts); card.addEventListener('choicesChanged', updateQuestions); card.dispatchEvent(new CustomEvent('choicesChanged', {bubbles: true, detail: event})); } orderCardInitializers['waterloo-rwv'] = function(card, callbacks) { // Initialize things common to all order cards const [choices, choiceElements] = commonOrderCardInit(card, callbacks); // Update diagram commonWorpdiagramInit(card); // UI updating functions function updateTexts() { // product title var titleSummary = "RWV-" if(card.dataset.direction == "retour") { titleSummary += 'R-'; } titleSummary += choices.diameter; if(card.dataset.direction == "supply") { titleSummary += " (" + choices.uitblaasrichting + ")"; } card.querySelectorAll('.title-summary').forEach(e => e.textContent = titleSummary); // product marketing blurb const context = Object.assign({direction: card.dataset.direction}, choices); chooseOneProcessing(card.querySelector('.product-blurb'), context); } // Initialize card ui card.addEventListener('choicesChanged', updateTexts); card.addEventListener('choicesChanged', updateQuestions); card.dispatchEvent(new CustomEvent('choicesChanged', {bubbles: true, detail: event})); } orderCardInitializers['waterloo-wtv'] = function(card, callbacks) { // Initialize things common to all order cards const [choices, choiceElements] = commonOrderCardInit(card, callbacks); // Update diagram commonWorpdiagramInit(card); // UI updating functions function updateTexts() { // product title const selectedVentOption = choiceElements.vent_shape.querySelector(':checked'); const titleSummary = choices.vent_shape + '-' + choices.diameter + ' – ' + selectedVentOption.dataset.description; card.querySelectorAll('.title-summary').forEach(e => e.textContent = titleSummary); // product marketing blurb const context = Object.assign({direction: card.dataset.direction}, choices); chooseOneProcessing(card.querySelector('.product-blurb'), context); } // Initialize card ui card.addEventListener('choicesChanged', updateTexts); updateTexts(); } orderCardInitializers['waterloo-wpd'] = function(card, callbacks) { // Initialize things common to all order cards const [choices, choiceElements] = commonOrderCardInit(card, callbacks); // Update diagram commonWorpdiagramInit(card); // UI updating functions function updateTexts() { // product title const selectedVentOption = choiceElements.vent_type.querySelector(':checked'); var ventText = choices.vent_type if(choices.vent_type == "WPD") { ventText += '-' + choices.used_sides } else if(choices.vent_type == "WPDR") { if(choices.frontplaat == "MPV-600") { ventText += "-600" } else if (choices.frontplaat == "MPV-625") { ventText += "-625" } } ventText += '-' + choices.diameter const titleSummary = ventText + ' – ' + selectedVentOption.dataset.description; card.querySelectorAll('.title-summary').forEach(e => e.textContent = titleSummary); // product marketing blurb const context = Object.assign({direction: card.dataset.direction}, choices); chooseOneProcessing(card.querySelector('.product-blurb'), context); } // Initialize card UI card.addEventListener('choicesChanged', updateTexts); card.addEventListener('choicesChanged', updateQuestions); card.dispatchEvent(new CustomEvent('choicesChanged', {bubbles: true, detail: event})); } orderCardInitializers['waterloo-mwvpd'] = function(card, callbacks) { // Initialize things common to all order cards const [choices, choiceElements] = commonOrderCardInit(card, callbacks); // Update diagram commonWorpdiagramInit(card); // UI updating functions function updateTexts() { // product title const selectedVentOption = choiceElements.vent_type.querySelector(':checked'); const ventText = { 'MWVPD-R': 'MWVPD-R', 'MWVPD-RF': 'MWVPD-RF', 'MWVP-R': 'MWVP-R', 'MWVP-RF': 'MWVP-RF', 'MWVPD': 'MWVPD' + '-' + choices.used_sides, 'MWVP': 'MWVP' + '-' + choices.used_sides, }[choices.vent_type] + '-' + choices.diameter; const titleSummary = ventText + ' – ' + selectedVentOption.dataset.description; card.querySelectorAll('.title-summary').forEach(e => e.textContent = titleSummary); // product marketing blurb const context = Object.assign({direction: card.dataset.direction}, choices); chooseOneProcessing(card.querySelector('.product-blurb'), context); } // Initialize card UI card.addEventListener('choicesChanged', updateTexts); card.addEventListener('choicesChanged', updateQuestions); card.dispatchEvent(new CustomEvent('choicesChanged', {bubbles: true, detail: event})); } orderCardInitializers['impklima-od5'] = function(card, callbacks) { // Initialize things common to all order cards const [choices, choiceElements] = commonOrderCardInit(card, callbacks); // Update diagram commonWorpdiagramInit(card); // UI updating functions function updateTexts() { // product title const selectedVentOption = choiceElements.plate_type.querySelector(':checked'); const titleSummary = 'OD-5/' + choices.plate_type + ' – ' + selectedVentOption.dataset.description; card.querySelectorAll('.title-summary').forEach(e => e.textContent = titleSummary); // product marketing blurb const context = Object.assign({direction: card.dataset.direction}, choices); chooseOneProcessing(card.querySelector('.product-blurb'), context); } card.addEventListener('choicesChanged', updateTexts); card.addEventListener('choicesChanged', updateQuestions); card.dispatchEvent(new CustomEvent('choicesChanged', {bubbles: true, detail: event})); } orderCardInitializers['impklima-lfh'] = function(card, callbacks) { // Initialize things common to all order cards const [choices, choiceElements] = commonOrderCardInit(card, callbacks); // Update diagram commonWorpdiagramInit(card); // UI updating functions function updateTexts() { // product title const selectedFilterOption = choiceElements.filter_dimension.querySelector(':checked'); const titleSummary = choices.filter_dimension + ' – ' + card.dataset.diffuserType; card.querySelectorAll('.title-summary').forEach(e => e.textContent = titleSummary); // product marketing blurb const context = Object.assign({direction: card.dataset.direction}, choices); chooseOneProcessing(card.querySelector('.product-blurb'), context); } // Initialize card ui card.addEventListener('choicesChanged', updateTexts); card.addEventListener('choicesChanged', updateQuestions); card.dispatchEvent(new CustomEvent('choicesChanged', {bubbles: true, detail: event})); } orderCardInitializers['impklima-ln1'] = function(card, callbacks) { // Initialize things common to all order cards const [choices, choiceElements] = commonOrderCardInit(card, callbacks); // Update diagram commonWorpdiagramInit(card); // UI updating functions function updateTexts() { // product title const titleSummary = "LN-1 / " + choices.dimension; card.querySelectorAll('.title-summary').forEach(e => e.textContent = titleSummary); // product marketing blurb const context = Object.assign({direction: card.dataset.direction}, choices); chooseOneProcessing(card.querySelector('.product-blurb'), context); } // Initialize card ui card.addEventListener('choicesChanged', updateTexts); card.addEventListener('choicesChanged', updateQuestions); card.dispatchEvent(new CustomEvent('choicesChanged', {bubbles: true, detail: event})); } orderCardInitializers['impklima-ln2'] = function(card, callbacks) { // Initialize things common to all order cards const [choices, choiceElements] = commonOrderCardInit(card, callbacks); // Update diagram commonWorpdiagramInit(card); // UI updating functions function updateTexts() { // product title const titleSummary = "LN-2 / " + choices.dimension; card.querySelectorAll('.title-summary').forEach(e => e.textContent = titleSummary); // product marketing blurb const context = Object.assign({direction: card.dataset.direction}, choices); chooseOneProcessing(card.querySelector('.product-blurb'), context); } // Initialize card ui card.addEventListener('choicesChanged', updateTexts); card.addEventListener('choicesChanged', updateQuestions); card.dispatchEvent(new CustomEvent('choicesChanged', {bubbles: true, detail: event})); } orderCardInitializers['impklima-fr'] = function(card, callbacks) { // Initialize things common to all order cards const [choices, choiceElements] = commonOrderCardInit(card, callbacks); // Update diagram commonWorpdiagramInit(card); // UI updating functions function updateTexts() { // product title const titleSummary = "FR / " + choices.dimension; card.querySelectorAll('.title-summary').forEach(e => e.textContent = titleSummary); // product marketing blurb const context = Object.assign({direction: card.dataset.direction}, choices); chooseOneProcessing(card.querySelector('.product-blurb'), context); } // Initialize card ui card.addEventListener('choicesChanged', updateTexts); card.addEventListener('choicesChanged', updateQuestions); card.dispatchEvent(new CustomEvent('choicesChanged', {bubbles: true, detail: event})); } orderCardInitializers['impklima-afh1'] = function(card, callbacks) { // Initialize things common to all order cards const [choices, choiceElements] = commonOrderCardInit(card, callbacks); // Update diagram commonWorpdiagramInit(card); // UI updating functions function updateTexts() { // product title const titleSummary = "AFH-1"; card.querySelectorAll('.title-summary').forEach(e => e.textContent = titleSummary); // product marketing blurb const context = Object.assign({direction: card.dataset.direction}, choices); chooseOneProcessing(card.querySelector('.product-blurb'), context); } // Initialize card ui card.addEventListener('choicesChanged', updateTexts); card.addEventListener('choicesChanged', updateQuestions); card.dispatchEvent(new CustomEvent('choicesChanged', {bubbles: true, detail: event})); } orderCardInitializers['impklima-akf1'] = function(card, callbacks) { // Initialize things common to all order cards const [choices, choiceElements] = commonOrderCardInit(card, callbacks); // Update diagram commonWorpdiagramInit(card); // UI updating functions function updateTexts() { // product title var titleSummary = "AKF-I"; if(choices.prefilter != 'N/A') { titleSummary += "+KPF"; } card.querySelectorAll('.title-summary').forEach(e => e.textContent = titleSummary); // product marketing blurb const context = Object.assign({direction: card.dataset.direction}, choices); chooseOneProcessing(card.querySelector('.product-blurb'), context); } // Initialize card ui card.addEventListener('choicesChanged', updateTexts); card.addEventListener('choicesChanged', updateQuestions); card.dispatchEvent(new CustomEvent('choicesChanged', {bubbles: true, detail: event})); } orderCardInitializers['impklima-akf2'] = function(card, callbacks) { // Initialize things common to all order cards const [choices, choiceElements] = commonOrderCardInit(card, callbacks); // Update diagram commonWorpdiagramInit(card); // UI updating functions function updateTexts() { // product title var titleSummary = "AKF-II"; if(choices.prefilter != 'N/A') { titleSummary += "+KPF"; } card.querySelectorAll('.title-summary').forEach(e => e.textContent = titleSummary); // product marketing blurb const context = Object.assign({direction: card.dataset.direction}, choices); chooseOneProcessing(card.querySelector('.product-blurb'), context); } // Initialize card ui card.addEventListener('choicesChanged', updateTexts); card.addEventListener('choicesChanged', updateQuestions); card.dispatchEvent(new CustomEvent('choicesChanged', {bubbles: true, detail: event})); } orderCardInitializers['impklima-afv-8'] = function(card, callbacks) { // Initialize things common to all order cards const [choices, choiceElements] = commonOrderCardInit(card, callbacks); // Update diagram commonWorpdiagramInit(card); // UI updating functions function updateTexts() { // product title const selectedFilterOption = choiceElements.filter_dimension.querySelector(':checked'); const titleSummary = "AFV-8 / " + card.dataset.diffuserType; card.querySelectorAll('.title-summary').forEach(e => e.textContent = titleSummary); // product marketing blurb const context = Object.assign({direction: card.dataset.direction}, choices); chooseOneProcessing(card.querySelector('.product-blurb'), context); } // Initialize card ui card.addEventListener('choicesChanged', updateTexts); card.addEventListener('choicesChanged', updateQuestions); card.dispatchEvent(new CustomEvent('choicesChanged', {bubbles: true, detail: event})); } orderCardInitializers['impklima-afv-8b'] = function(card, callbacks) { // Initialize things common to all order cards const [choices, choiceElements] = commonOrderCardInit(card, callbacks); // Update diagram commonWorpdiagramInit(card); // UI updating functions function updateTexts() { // product title const selectedFilterOption = choiceElements.filter_dimension.querySelector(':checked'); const titleSummary = "AFV-8B / " + card.dataset.diffuserType; card.querySelectorAll('.title-summary').forEach(e => e.textContent = titleSummary); // product marketing blurb const context = Object.assign({direction: card.dataset.direction}, choices); chooseOneProcessing(card.querySelector('.product-blurb'), context); } // Initialize card ui card.addEventListener('choicesChanged', updateTexts); card.addEventListener('choicesChanged', updateQuestions); card.dispatchEvent(new CustomEvent('choicesChanged', {bubbles: true, detail: event})); } orderCardInitializers['impklima-afv-8g'] = function(card, callbacks) { // Initialize things common to all order cards const [choices, choiceElements] = commonOrderCardInit(card, callbacks); // Update diagram commonWorpdiagramInit(card); // UI updating functions function updateTexts() { // product title const selectedFilterOption = choiceElements.filter_dimension.querySelector(':checked'); const titleSummary = "AFV-8G / " + card.dataset.diffuserType; card.querySelectorAll('.title-summary').forEach(e => e.textContent = titleSummary); // product marketing blurb const context = Object.assign({direction: card.dataset.direction}, choices); chooseOneProcessing(card.querySelector('.product-blurb'), context); } // Initialize card ui card.addEventListener('choicesChanged', updateTexts); card.addEventListener('choicesChanged', updateQuestions); card.dispatchEvent(new CustomEvent('choicesChanged', {bubbles: true, detail: event})); } orderCardInitializers['waterloo-wid'] = function(card, callbacks) { // Initialize things common to all order cards const [choices, choiceElements] = commonOrderCardInit(card, callbacks); // Update diagram commonWorpdiagramInit(card); // UI updating functions function updateTexts() { // product title const selectedVentOption = choiceElements.vent_type.querySelector(':checked'); const ventText = choices.vent_type + '-' + choices.diameter const titleSummary = ventText + ' – ' + selectedVentOption.dataset.description; card.querySelectorAll('.title-summary').forEach(e => e.textContent = titleSummary); // product marketing blurb const context = Object.assign({direction: card.dataset.direction}, choices); chooseOneProcessing(card.querySelector('.product-blurb'), context); } // Initialize card UI card.addEventListener('choicesChanged', updateTexts); card.addEventListener('choicesChanged', updateQuestions); card.dispatchEvent(new CustomEvent('choicesChanged', {bubbles: true, detail: event})); } orderCardInitializers['waterloo-widfr'] = function(card, callbacks) { // Initialize things common to all order cards const [choices, choiceElements] = commonOrderCardInit(card, callbacks); // Update diagram commonWorpdiagramInit(card); // UI updating functions function updateTexts() { // product title const ventText = 'WID-FR' + (choices.afdekplaat == ':none' ? '' : ('-' + choices.afdekplaat)) + '-' + choices.diameter const titleSummary = ventText; card.querySelectorAll('.title-summary').forEach(e => e.textContent = titleSummary); // product marketing blurb const context = Object.assign({direction: card.dataset.direction}, choices); chooseOneProcessing(card.querySelector('.product-blurb'), context); } // Initialize card UI card.addEventListener('choicesChanged', updateTexts); card.addEventListener('choicesChanged', updateQuestions); card.dispatchEvent(new CustomEvent('choicesChanged', {bubbles: true, detail: event})); } orderCardInitializers['waterloo-wisl'] = function(card, callbacks) { // Initialize things common to all order cards const [choices, choiceElements] = commonOrderCardInit(card, callbacks); // Update diagram commonWorpdiagramInit(card); // UI updating functions function updateTexts() { // product title const ventText = 'WISL-' + choices.diameter const titleSummary = ventText; card.querySelectorAll('.title-summary').forEach(e => e.textContent = titleSummary); // product marketing blurb const context = Object.assign({direction: card.dataset.direction}, choices); chooseOneProcessing(card.querySelector('.product-blurb'), context); } // Initialize card UI card.addEventListener('choicesChanged', updateTexts); card.addEventListener('choicesChanged', updateQuestions); card.dispatchEvent(new CustomEvent('choicesChanged', {bubbles: true, detail: event})); } orderCardInitializers['waterloo-ws'] = function(card, callbacks) { // Initialize things common to all order cards const [choices, choiceElements] = commonOrderCardInit(card, callbacks); // Update diagram commonWorpdiagramInit(card); // UI updating functions function updateTexts() { // product title const selectedVentOption = choiceElements.vent_shape.querySelector(':checked'); const titleSummary = choices.vent_shape + '-' + choices.d1 + ' – ' + selectedVentOption.dataset.description; card.querySelectorAll('.title-summary').forEach(e => e.textContent = titleSummary); // product marketing blurb const context = Object.assign({direction: card.dataset.direction}, choices); chooseOneProcessing(card.querySelector('.product-blurb'), context); } // Initialize card ui card.addEventListener('choicesChanged', updateTexts); card.addEventListener('choicesChanged', updateQuestions); card.dispatchEvent(new CustomEvent('choicesChanged', {bubbles: true, detail: event})); } orderCardInitializers['waterloo-wg'] = function(card, callbacks) { // Initialize things common to all order cards const [choices, choiceElements] = commonOrderCardInit(card, callbacks); // Update diagram commonWorpdiagramInit(card); // UI updating functions function updateTexts() { // product title const selectedVentOption = choiceElements.vent_shape.querySelector(':checked'); var titleSummary; if(choices.diameter == "CUSTOM") { titleSummary = choices.vent_shape + ' - ' + selectedVentOption.dataset.description; } else { titleSummary = choices.vent_shape + '-' + choices.diameter + ' – ' + selectedVentOption.dataset.description; } card.querySelectorAll('.title-summary').forEach(e => e.textContent = titleSummary); // product marketing blurb const context = Object.assign({direction: card.dataset.direction}, choices); chooseOneProcessing(card.querySelector('.product-blurb'), context); } // Initialize card ui card.addEventListener('choicesChanged', updateTexts); card.addEventListener('choicesChanged', updateQuestions); card.dispatchEvent(new CustomEvent('choicesChanged', {bubbles: true, detail: event})); } orderCardInitializers['waterloo-hv'] = function(card, callbacks) { // Initialize things common to all order cards const [choices, choiceElements] = commonOrderCardInit(card, callbacks); // Update diagram commonWorpdiagramInit(card); // UI updating functions function updateTexts() { // product title const selectedVentOption = choiceElements.vent_shape.querySelector(':checked'); const titleSummary = choices.vent_shape + '-' + choices.diameter + ' – ' + selectedVentOption.dataset.description; card.querySelectorAll('.title-summary').forEach(e => e.textContent = titleSummary); // product marketing blurb const context = Object.assign({direction: card.dataset.direction}, choices); chooseOneProcessing(card.querySelector('.product-blurb'), context); } // Initialize card ui card.addEventListener('choicesChanged', updateTexts); card.addEventListener('choicesChanged', updateQuestions); card.dispatchEvent(new CustomEvent('choicesChanged', {bubbles: true, detail: event})); } orderCardInitializers['waterloo-al'] = function(card, callbacks) { // Initialize things common to all order cards const [choices, choiceElements] = commonOrderCardInit(card, callbacks); // Update diagram commonWorpdiagramInit(card); // UI updating functions function updateTexts() { // product title const uitvoering = choiceElements.uitvoering.querySelector(':checked'); var titleSummary = choices.vent_shape + '-' + choices.diameter; if(uitvoering == "2") { titleSummary = "2" + titleSummary; } card.querySelectorAll('.title-summary').forEach(e => e.textContent = titleSummary); // product marketing blurb const context = Object.assign({direction: card.dataset.direction}, choices); chooseOneProcessing(card.querySelector('.product-blurb'), context); } // Initialize card ui card.addEventListener('choicesChanged', updateTexts); card.addEventListener('choicesChanged', updateQuestions); card.dispatchEvent(new CustomEvent('choicesChanged', {bubbles: true, detail: event})); } orderCardInitializers['waterloo-rv'] = function(card, callbacks) { // Initialize things common to all order cards const [choices, choiceElements] = commonOrderCardInit(card, callbacks); // Update diagram commonWorpdiagramInit(card); // UI updating functions function updateTexts() { // product title const selectedVentOption = choiceElements.vent_shape.querySelector(':checked'); const titleSummary = choices.vent_shape + '-' + choices.diameter + ' – ' + selectedVentOption.dataset.description; card.querySelectorAll('.title-summary').forEach(e => e.textContent = titleSummary); // product marketing blurb const context = Object.assign({direction: card.dataset.direction}, choices); chooseOneProcessing(card.querySelector('.product-blurb'), context); } // Initialize card ui card.addEventListener('choicesChanged', updateTexts); card.addEventListener('choicesChanged', updateQuestions); card.dispatchEvent(new CustomEvent('choicesChanged', {bubbles: true, detail: event})); } orderCardInitializers['waterloo-dv'] = function(card, callbacks) { // Initialize things common to all order cards const [choices, choiceElements] = commonOrderCardInit(card, callbacks); // Update diagram commonWorpdiagramInit(card); // UI updating functions function updateTexts() { // product title const selectedVentOption = choiceElements.vent_shape.querySelector(':checked'); const titleSummary = choices.vent_shape + '-' + choices.diameter + ' – ' + selectedVentOption.dataset.description; card.querySelectorAll('.title-summary').forEach(e => e.textContent = titleSummary); // product marketing blurb const context = Object.assign({direction: card.dataset.direction}, choices); chooseOneProcessing(card.querySelector('.product-blurb'), context); } // Initialize card ui card.addEventListener('choicesChanged', updateTexts); card.addEventListener('choicesChanged', updateQuestions); card.dispatchEvent(new CustomEvent('choicesChanged', {bubbles: true, detail: event})); } orderCardInitializers['waterloo-rtc'] = function(card, callbacks) { // Initialize things common to all order cards const [choices, choiceElements] = commonOrderCardInit(card, callbacks); // Update diagram commonWorpdiagramInit(card); // UI updating functions function updateTexts() { // product title const selectedVentOption = choiceElements.vent_shape.querySelector(':checked'); const titleSummary = choices.vent_shape + '-' + choices.diameter + ' – ' + selectedVentOption.dataset.description; card.querySelectorAll('.title-summary').forEach(e => e.textContent = titleSummary); // product marketing blurb const context = Object.assign({direction: card.dataset.direction}, choices); chooseOneProcessing(card.querySelector('.product-blurb'), context); } // Initialize card ui card.addEventListener('choicesChanged', updateTexts); card.addEventListener('choicesChanged', updateQuestions); card.dispatchEvent(new CustomEvent('choicesChanged', {bubbles: true, detail: event})); } orderCardInitializers['waterloo-adk'] = function(card, callbacks) { // Initialize things common to all order cards const [choices, choiceElements] = commonOrderCardInit(card, callbacks); // Update diagram commonWorpdiagramInit(card); // UI updating functions function updateTexts() { // product title const selectedVentOption = choiceElements.vent_shape.querySelector(':checked'); const titleSummary = choices.vent_shape + '-' + choices.diameter + ' – ' + selectedVentOption.dataset.description; card.querySelectorAll('.title-summary').forEach(e => e.textContent = titleSummary); // product marketing blurb const context = Object.assign({direction: card.dataset.direction}, choices); chooseOneProcessing(card.querySelector('.product-blurb'), context); const orderQuantity = card.querySelector('[data-question="order-quantity"]'); function getQuantity() { return orderQuantity.valueAsNumber; } const [getUnitPrice, fetchUnitPrice, getPriceComponents] = makeUnitPriceFetcher(card, updateAsComponents); fetchUnitPrice(); function updateAsComponents() { const components = getPriceComponents(); card.querySelectorAll('.quantity').forEach(e => e.textContent = getQuantity()); // XXX: This order is now hardcoded and that's pretty damned fragile. DO NOT REUSE THIS if(components.length == 2) { let unitPriceMain = components[0][1]; let dakopstandID = components[1][0]; let dakopstandPrice = components[1][1]; if(unitPriceMain >= 0) { card.querySelectorAll('.unit-price-main').forEach(e => e.textContent = formatMoney(unitPriceMain)); } else { card.querySelectorAll('.unit-price-main').forEach(e => e.textContent = '—'); } card.querySelector('.dakopstand-id').textContent = dakopstandID; card.querySelector('.dakopstand-price').textContent = formatMoney(dakopstandPrice); card.querySelectorAll('.adk-dakopstand-sectie').forEach(e => e.classList.remove("hidden")); } else { let unitPriceMain = components[0][1]; if(unitPriceMain >= 0) { card.querySelectorAll('.unit-price-main').forEach(e => e.textContent = formatMoney(unitPriceMain)); } else { card.querySelectorAll('.unit-price-main').forEach(e => e.textContent = '—'); } card.querySelector('.dakopstand-id').textContent = ""; card.querySelector('.dakopstand-price').textContent = "—"; card.querySelectorAll('.adk-dakopstand-sectie').forEach(e => e.classList.add("hidden")); } } updateAsComponents(); } // Initialize card ui card.addEventListener('choicesChanged', updateTexts); card.addEventListener('choicesChanged', updateQuestions); card.dispatchEvent(new CustomEvent('choicesChanged', {bubbles: true, detail: event})); } orderCardInitializers['waterloo-wgd'] = function(card, callbacks) { // Initialize things common to all order cards const [choices, choiceElements] = commonOrderCardInit(card, callbacks); // Update diagram commonWorpdiagramInit(card); // UI updating functions function updateTexts() { // product title const selectedVentOption = choiceElements.vent_shape.querySelector(':checked'); const titleSummary = choices.vent_shape + '-' + choices.diameter + ' – ' + selectedVentOption.dataset.description; card.querySelectorAll('.title-summary').forEach(e => e.textContent = titleSummary); // product marketing blurb const context = Object.assign({direction: card.dataset.direction}, choices); chooseOneProcessing(card.querySelector('.product-blurb'), context); } // Initialize card ui card.addEventListener('choicesChanged', updateTexts); updateTexts(); } orderCardInitializers['waterloo-sd'] = function(card, callbacks) { // Initialize things common to all order cards const [choices, choiceElements] = commonOrderCardInit(card, callbacks); // Update diagram commonWorpdiagramInit(card); // UI updating functions function updateTexts() { // product title const selectedVentOption = choiceElements.vent_shape.querySelector(':checked'); const titleSummary = choices.vent_shape + '-' + choices.diameter + ' – ' + selectedVentOption.dataset.description; card.querySelectorAll('.title-summary').forEach(e => e.textContent = titleSummary); // product marketing blurb const context = Object.assign({direction: card.dataset.direction}, choices); chooseOneProcessing(card.querySelector('.product-blurb'), context); } updateOptions(choiceElements.schoepkleur, ['ZWART']); // Initialize card ui card.addEventListener('choicesChanged', updateTexts); card.addEventListener('choicesChanged', updateQuestions); card.dispatchEvent(new CustomEvent('choicesChanged', {bubbles: true, detail: event})); } orderCardInitializers['waterloo-yg'] = function(card, callbacks) { // Initialize things common to all order cards const [choices, choiceElements] = commonOrderCardInit(card, callbacks); // Update diagram commonWorpdiagramInit(card); // UI updating functions function updateTexts() { // product title const selectedVentOption = choiceElements.vent_shape.querySelector(':checked'); var titleSummary; if(choices.diameter == "CUSTOM") { titleSummary = choices.vent_shape + ' - ' + selectedVentOption.dataset.description; } else { titleSummary = choices.vent_shape + '-' + choices.diameter + ' – ' + selectedVentOption.dataset.description; } card.querySelectorAll('.title-summary').forEach(e => e.textContent = titleSummary); // product marketing blurb const context = Object.assign({direction: card.dataset.direction}, choices); chooseOneProcessing(card.querySelector('.product-blurb'), context); } // Initialize card ui card.addEventListener('choicesChanged', updateTexts); card.addEventListener('choicesChanged', updateQuestions); card.dispatchEvent(new CustomEvent('choicesChanged', {bubbles: true, detail: event})); } orderCardInitializers['waterloo-abrs4'] = function(card, callbacks) { // Initialize things common to all order cards const [choices, choiceElements] = commonOrderCardInit(card, callbacks); // Update diagram commonWorpdiagramInit(card); // UI updating functions function updateTexts() { // product title const selectedVentOption = choiceElements.vent_shape.querySelector(':checked'); var titleSummary; if(choices.diameter == "CUSTOM") { titleSummary = choices.vent_shape + ' - ' + selectedVentOption.dataset.description; } else { titleSummary = choices.vent_shape + '-' + choices.diameter + ' – ' + selectedVentOption.dataset.description; } card.querySelectorAll('.title-summary').forEach(e => e.textContent = titleSummary); // product marketing blurb const context = Object.assign({direction: card.dataset.direction}, choices); chooseOneProcessing(card.querySelector('.product-blurb'), context); } // Initialize card ui card.addEventListener('choicesChanged', updateTexts); card.addEventListener('choicesChanged', updateQuestions); card.dispatchEvent(new CustomEvent('choicesChanged', {bubbles: true, detail: event})); } orderCardInitializers['waterloo-ygt'] = function(card, callbacks) { // Initialize things common to all order cards const [choices, choiceElements] = commonOrderCardInit(card, callbacks); // Update diagram commonWorpdiagramInit(card); // UI updating functions function updateTexts() { // product title const selectedVentOption = choiceElements.vent_shape.querySelector(':checked'); var titleSummary; if(choices.diameter == "CUSTOM") { titleSummary = choices.vent_shape + ' - ' + selectedVentOption.dataset.description; } else { titleSummary = choices.vent_shape + '-' + choices.diameter + ' – ' + selectedVentOption.dataset.description; } card.querySelectorAll('.title-summary').forEach(e => e.textContent = titleSummary); // product marketing blurb const context = Object.assign({direction: card.dataset.direction}, choices); chooseOneProcessing(card.querySelector('.product-blurb'), context); } // Initialize card ui card.addEventListener('choicesChanged', updateTexts); card.addEventListener('choicesChanged', updateQuestions); card.dispatchEvent(new CustomEvent('choicesChanged', {bubbles: true, detail: event})); } orderCardInitializers['waterloo-dsr'] = function(card, callbacks) { // Initialize things common to all order cards const [choices, choiceElements] = commonOrderCardInit(card, callbacks); // Update diagram commonWorpdiagramInit(card); // UI updating functions function updateTexts() { // product title const selectedVentOption = choiceElements.vent_shape.querySelector(':checked'); const titleSummary = choices.vent_shape + '-' + choices.diameter + ' – ' + selectedVentOption.dataset.description; card.querySelectorAll('.title-summary').forEach(e => e.textContent = titleSummary); // product marketing blurb const context = Object.assign({direction: card.dataset.direction}, choices); chooseOneProcessing(card.querySelector('.product-blurb'), context); } // Initialize card ui card.addEventListener('choicesChanged', updateTexts); card.addEventListener('choicesChanged', updateQuestions); card.dispatchEvent(new CustomEvent('choicesChanged', {bubbles: true, detail: event})); } orderCardInitializers['waterloo-cs'] = function(card, callbacks) { // Initialize things common to all order cards const [choices, choiceElements] = commonOrderCardInit(card, callbacks); // Update diagram commonWorpdiagramInit(card); // UI updating functions function updateTexts() { // product title const selectedVentOption = choiceElements.vent_shape.querySelector(':checked'); const titleSummary = choices.vent_shape + '-' + choices.diameter + ' – ' + selectedVentOption.dataset.description; card.querySelectorAll('.title-summary').forEach(e => e.textContent = titleSummary); // product marketing blurb const context = Object.assign({direction: card.dataset.direction}, choices); chooseOneProcessing(card.querySelector('.product-blurb'), context); } function updatePlenumRange() { const profielElem = document.querySelector('input[data-choice="profiel_lengte"]'); const plenumElem = document.querySelector('input[data-choice="diameter"]'); profielElem.min = plenumElem.value; // Call exactly the event listener on the onchange property, not the entire change event profielElem.onchange(profielElem); } updatePlenumRange(); card.addEventListener('choicesChanged', updateTexts); card.addEventListener('choicesChanged', updateQuestions); card.dispatchEvent(new CustomEvent('choicesChanged', {bubbles: true, detail: event})); } })(); // Invoke scoping function