window.WLC_FORM_CONFIGS=window.WLC_FORM_CONFIGS||{};
window.WLC_FORM_CONFIGS['nexus']={
variant: 'nexus',
title: 'Nexus Request Pricing',
subtitle: 'Get pricing for your Nexus configuration',
productName: 'Dual Sensor Custom Control Panel',
steps: [
{ id: 'specs', label: 'Specs' },
{ id: 'equipment', label: 'Equipment' },
{ id: 'contact', label: 'Contact' }
],
standardFeatures: [
'ETL Listed (UL 508A)',
'Pump Run Timers'
],
step1: {
title: 'System Specifications',
description: 'Tell us about your application requirements',
layout: 'standard',
fields: {
fla: {
label: 'FLA (Full Load Amps)',
type: 'text',
placeholder: '9.4',
helpText: 'Check your pump nameplate for FLA rating'
},
voltage: {
label: 'Voltage Requirement',
type: 'select',
options: [
{ value: '', label: 'Select Voltage...' },
{ value: '120v', label: '120V Single Phase' },
{ value: '208v', label: '208V Single Phase' },
{ value: '230v-3ph', label: '230V Three Phase' },
{ value: '240v', label: '240V Single Phase' },
{ value: '480v', label: '480V Three Phase' }
]
}},
helpCheckbox: {
label: "I'm not sure - help me configure the right system"
},
panelAccessories: {
label: 'Panel Accessories',
fields: {
alarms: {
label: 'Alarms',
type: 'select',
options: [
{ value: 'none', label: 'None' },
{ value: '85db', label: '85db' },
{ value: '120db', label: '120db' }
]
},
beacons: {
label: 'Beacons',
type: 'select',
options: [
{ value: 'solid', label: 'Solid', selected: true },
{ value: 'strobe', label: 'Strobe' }
]
},
hoaSwitches: {
label: 'HOA Switches',
type: 'select',
options: [
{ value: 'no', label: 'No' },
{ value: 'yes', label: 'Yes' }
]
}}
}},
sections: {
sensors: {
enabled: true,
label: 'Sensors',
required: true,
dual: true,
description: 'This panel accepts two independent sensor technologies.',
fields: {
primarySensorType: {
label: 'Primary Sensor',
type: 'select',
options: [
{ value: 'frequency', label: 'WLC Frequency Sensor', selected: true },
{ value: 'float', label: 'Float Switch' }
]
},
primaryWireLength: {
label: 'Wire Length',
type: 'select',
options: [
{ value: '25ft', label: '25 ft' },
{ value: '50ft', label: '50 ft', selected: true },
{ value: '100ft', label: '100 ft' },
{ value: 'custom', label: 'Custom length' }
]
},
secondarySensorType: {
label: 'Secondary Sensor',
type: 'select',
options: [
{ value: 'none', label: 'None' },
{ value: 'ultrasonic', label: 'Ultrasonic' },
{ value: 'pressure', label: 'Pressure Transducer' },
{ value: 'radar-wave', label: 'Radar Wave' },
{ value: '4-20ma', label: '4-20mA Input' }
]
},
secondaryWireLength: {
label: 'Wire Length',
type: 'select',
options: [
{ value: '25ft', label: '25 ft' },
{ value: '50ft', label: '50 ft', selected: true },
{ value: '100ft', label: '100 ft' },
{ value: 'custom', label: 'Custom length' }
]
}},
helpCheckboxLabel: 'Help me choose'
},
tankHeight: {
enabled: true,
label: 'Tank Height',
fieldName: 'tankHeight',
placeholder: '16',
suffix: 'ft',
notSpecifiedCheckbox: true
},
cabinet: {
enabled: true,
label: 'Cabinet / Enclosure',
required: true,
fields: {
nemaGrade: {
label: 'NEMA Grade',
type: 'select',
options: [
{ value: 'nema4x', label: 'NEMA 4X', selected: true },
{ value: 'nema12x', label: 'NEMA 12X' },
{ value: 'nema7', label: 'NEMA 7' }
]
},
enclosureType: {
label: 'Enclosure Type',
type: 'select',
options: [
{ value: 'pvc', label: 'PVC', selected: true },
{ value: 'aluminum', label: 'Aluminum' },
{ value: 'stainless', label: 'Stainless' }
]
}},
helpCheckboxLabel: 'Help me choose'
},
valves: {
enabled: true,
label: 'I need a valve',
optional: true,
fields: {
valveBrand: {
label: 'Brand',
type: 'select',
options: [
{ value: '', label: 'Select brand...' },
{ value: 'jefferson', label: 'Jefferson' },
{ value: 'superior', label: 'Superior' },
{ value: 'belimo', label: 'Belimo' },
{ value: 'asco', label: 'ASCO' },
{ value: 'ocv', label: 'OCV' }
]
},
valveType: {
label: 'Type',
type: 'select',
options: [
{ value: '', label: 'Select type...' },
{ value: 'slow-closing-solenoid', label: 'Slow Closing Solenoid' },
{ value: 'solenoid', label: 'Solenoid' },
{ value: 'motorized-ball', label: 'Motorized Ball Valve' },
{ value: 'spring-return', label: 'Spring Return' },
{ value: 'paragon', label: 'Paragon/Pressure Switch' }
]
},
valveSize: {
label: 'Size',
type: 'select',
options: [
{ value: '', label: 'Select size...' },
{ value: '0.75', label: '3/4"' },
{ value: '1', label: '1"' },
{ value: '1.5', label: '1-1/2"' },
{ value: '2', label: '2"' },
{ value: '2.5', label: '2-1/2"' },
{ value: '3', label: '3"' },
{ value: '4', label: '4"' },
{ value: '6', label: '6"' },
{ value: '8', label: '8"' },
{ value: '10', label: '10"' },
{ value: '12', label: '12"' },
{ value: '14', label: '14"' },
{ value: '16', label: '16"' },
{ value: '24', label: '24"' }
]
},
valveConnection: {
label: 'Connection',
type: 'select',
options: [
{ value: '', label: 'Select connection...' },
{ value: 'npt-threaded', label: 'NPT/Threaded' },
{ value: 'flange', label: 'Flange' },
{ value: 'grooved', label: 'Grooved' }
]
},
valveVoltage: {
label: 'Voltage',
type: 'select',
options: [
{ value: '', label: 'Select voltage...' },
{ value: '24vac', label: '24V AC' },
{ value: '24vdc', label: '24V DC' },
{ value: '120v', label: '120V' }
]
}},
helpCheckboxLabel: 'Help me choose'
},
pumps: {
enabled: true,
label: 'I need a pump',
optional: true,
fields: {
pumpBrand: {
label: 'Brand',
type: 'select',
options: [
{ value: 'no-preference', label: 'No Preference' },
{ value: 'grundfos', label: 'Grundfos' }
]
},
pumpGpm: {
label: 'GPM',
type: 'select',
helpText: 'Gallons Per Minute',
options: [
{ value: '', label: 'Select GPM...' },
{ value: '60', label: '60 GPM' },
{ value: '120', label: '120 GPM' },
{ value: '140', label: '140 GPM' },
{ value: 'unsure', label: 'Not Sure' },
{ value: 'other', label: 'Other' }
]
},
pumpGpmOther: {
label: 'Specify GPM',
type: 'text',
placeholder: 'Enter GPM value'
},
pumpFla: {
label: 'FLA',
type: 'text',
placeholder: '9.4',
helpText: 'Full Load Amps'
}},
helpCheckboxLabel: 'Help me choose'
},
panelAccessories: {
enabled: false
}},
contact: {
notes: false,
callCta: '480-905-1892'
},
submission: {
webhookUrl: null,
fallbackEmail: 'sales@waterlinecontrols.com'
},
leadValue: {
base: 2500,
sensors: 500,
cabinet: 400,
valves: 300,
pump: 800,
accessories: 0
}};
/**
* WLC-5000 QUOTE REQUEST FORM - Self-Rendering Widget
*====================================================* A self-contained multi-step quote request form that slides in from the right.
*
* INSTALLATION:
*   1. Add an empty div with id to your page
*   2. Include this script (and form.css)
*   3. That's it - the widget renders itself
*
* USAGE:
*   <div id="wlc-quote-form"
*        data-cta-color="red"
*        data-product-sku="WLC-5000">
*   </div>
*   <script src="form.js"></script>
*
* CONFIGURATION (via data attributes):
*   data-cta-color:"red" (landing pages) or "blue" (default)
*   data-product-sku:Pre-selected product SKU
*   data-mode:"standalone" (renders full panel) or "embedded" (form only)
*
* INTEGRATION WITH PRODUCT WIZARD:
*   Define window.onWizardQuoteOpen(product, formSlot) to receive callback
*   when user clicks "Request Quote" in the Product Wizard.
*
* SECURITY NOTE:
*   All HTML templates are internal to this module (not user-generated content).
*   Template rendering uses innerHTML for performance with trusted content only.
*
* GTM EVENTS:
*   - quote_form_start:Form opens
*   - quote_form_step_view:Step renders
*   - quote_form_step_complete: Step validated and advanced
*   - quote_form_submit:Successful submission (CONVERSION)
*   - quote_form_abandon:User leaves without completing
*/
(function(){
'use strict';
var CONFIG={
containerId: 'wlc-quote-form',
storageKey: 'wlc_quote_form_data',
storageExpiry: 1 * 60 * 60 * 1000,
gtmEnabled: true,
webhookUrl: null
};
var VARIANT=null;
function getVariantConfig(variantName){
if(window.WLC_FORM_CONFIGS&&window.WLC_FORM_CONFIGS[variantName]){
return window.WLC_FORM_CONFIGS[variantName];
}
console.warn('QuoteForm: No config found for variant "' + variantName + '"');
return null;
}
var FIELDS={
specifications: {
fla: {
label: 'FLA (Full Load Amps)',
type: 'text',
required: false,
placeholder: '9.4',
helpText: 'Check your pump nameplate for FLA rating'
},
voltage: {
label: 'Voltage Requirement',
type: 'select',
required: false,
options: [
{ value: '', label: 'Select Voltage...' },
{ value: '120v', label: '120V Single Phase' },
{ value: '208v', label: '208V Single Phase' },
{ value: '230v-3ph', label: '230V Three Phase' },
{ value: '240v', label: '240V Single Phase' },
{ value: '480v', label: '480V Three Phase' }
]
}},
accessories: {
primarySensorType: {
label: 'Primary Sensor',
type: 'select',
options: [
{ value: 'frequency', label: 'WLC Frequency Sensor', selected: true },
{ value: 'float', label: 'Float Switch' }
]
},
primaryWireLength: {
label: 'Wire Length',
type: 'select',
options: [
{ value: '25ft', label: '25 ft' },
{ value: '50ft', label: '50 ft', selected: true },
{ value: '100ft', label: '100 ft' },
{ value: 'custom', label: 'Custom length' }
]
},
secondarySensorType: {
label: 'Secondary Sensor',
type: 'select',
options: [
{ value: 'none', label: 'None' },
{ value: 'ultrasonic', label: 'Ultrasonic' },
{ value: 'pressure', label: 'Pressure Transducer' },
{ value: 'radar-wave', label: 'Radar Wave' },
{ value: '4-20ma', label: '4-20mA Input' }
]
},
secondaryWireLength: {
label: 'Wire Length',
type: 'select',
options: [
{ value: '25ft', label: '25 ft' },
{ value: '50ft', label: '50 ft', selected: true },
{ value: '100ft', label: '100 ft' },
{ value: 'custom', label: 'Custom length' }
]
},
nemaGrade: {
label: 'NEMA Grade',
type: 'select',
options: [
{ value: 'nema4x', label: 'NEMA 4X', selected: true },
{ value: 'nema12x', label: 'NEMA 12X' },
{ value: 'nema7', label: 'NEMA 7' }
]
},
enclosureType: {
label: 'Enclosure Type',
type: 'select',
options: [
{ value: 'pvc', label: 'PVC', selected: true },
{ value: 'aluminum', label: 'Aluminum' },
{ value: 'stainless', label: 'Stainless' }
]
},
needValves: {
label: 'I need a valve',
type: 'checkbox',
default: false
},
valveBrand: {
label: 'Brand',
type: 'select',
dependsOn: 'needValves',
options: [
{ value: '', label: 'Select brand...' },
{ value: 'jefferson', label: 'Jefferson' },
{ value: 'superior', label: 'Superior' },
{ value: 'belimo', label: 'Belimo' },
{ value: 'asco', label: 'ASCO' },
{ value: 'ocv', label: 'OCV' }
]
},
valveType: {
label: 'Type',
type: 'select',
dependsOn: 'needValves',
options: [
{ value: '', label: 'Select type...' },
{ value: 'slow-closing-solenoid', label: 'Slow Closing Solenoid' },
{ value: 'solenoid', label: 'Solenoid' },
{ value: 'motorized-ball', label: 'Motorized Ball Valve' },
{ value: 'spring-return', label: 'Spring Return' },
{ value: 'paragon', label: 'Paragon/Pressure Switch' }
]
},
valveSize: {
label: 'Size',
type: 'select',
dependsOn: 'needValves',
options: [
{ value: '', label: 'Select size...' },
{ value: '0.75', label: '3/4"' },
{ value: '1', label: '1"' },
{ value: '1.5', label: '1-1/2"' },
{ value: '2', label: '2"' },
{ value: '2.5', label: '2-1/2"' },
{ value: '3', label: '3"' },
{ value: '4', label: '4"' },
{ value: '6', label: '6"' },
{ value: '8', label: '8"' },
{ value: '10', label: '10"' },
{ value: '12', label: '12"' },
{ value: '14', label: '14"' },
{ value: '16', label: '16"' },
{ value: '24', label: '24"' }
]
},
valveConnection: {
label: 'Connection',
type: 'select',
dependsOn: 'needValves',
options: [
{ value: '', label: 'Select connection...' },
{ value: 'npt-threaded', label: 'NPT/Threaded' },
{ value: 'flange', label: 'Flange' },
{ value: 'grooved', label: 'Grooved' }
]
},
valveVoltage: {
label: 'Voltage',
type: 'select',
dependsOn: 'needValves',
options: [
{ value: '', label: 'Select voltage...' },
{ value: '24vac', label: '24V AC' },
{ value: '24vdc', label: '24V DC' },
{ value: '120v', label: '120V' }
]
},
needPump: {
label: 'I need a pump',
type: 'checkbox',
default: false
},
pumpBrand: {
label: 'Brand',
type: 'select',
dependsOn: 'needPump',
options: [
{ value: 'no-preference', label: 'No Preference' },
{ value: 'grundfos', label: 'Grundfos' }
]
},
pumpGpm: {
label: 'GPM',
type: 'select',
dependsOn: 'needPump',
helpText: 'Gallons Per Minute',
options: [
{ value: '', label: 'Select GPM...' },
{ value: '60', label: '60 GPM' },
{ value: '120', label: '120 GPM' },
{ value: '140', label: '140 GPM' },
{ value: 'unsure', label: 'Not Sure' },
{ value: 'other', label: 'Other' }
]
},
pumpGpmOther: {
label: 'Specify GPM',
type: 'text',
dependsOn: 'needPump',
placeholder: 'Enter GPM value'
},
pumpFla: {
label: 'FLA',
type: 'text',
dependsOn: 'needPump',
placeholder: '9.4',
helpText: 'Full Load Amps'
}},
contact: {
name: {
label: 'Full Name',
type: 'text',
required: true,
placeholder: 'John Smith'
},
company: {
label: 'Company',
type: 'text',
required: true,
placeholder: 'ACME Corp'
},
email: {
label: 'Email',
type: 'email',
required: true,
placeholder: 'john@company.com'
},
phone: {
label: 'Phone',
type: 'tel',
required: false,
placeholder: '(555) 123-4567',
helpText: 'Optional - for faster response'
}}
};
var state={
currentStep: 1,
totalSteps: 3,
formData: {},
productContext: null,
isOpen: false,
startTime: null,
stepTimes: {},
container: null,
options: {}};
var TEMPLATES={
main: function(){
return [
'<div class="quote-form-overlay" aria-hidden="true"></div>',
'<div class="quote-form-panel" role="dialog" aria-modal="true" aria-labelledby="quote-form-title">',
'  <div class="quote-form-panel-inner">',
'    <button type="button" class="quote-form-close" aria-label="Close quote form">',
'      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">',
'        <path d="M18 6L6 18M6 6l12 12"/>',
'      </svg>',
'    </button>',
'    <div class="quote-form-header">',
'      <div class="quote-form-product-badge" id="quote-form-product-badge"></div>',
'      <p class="quote-form-product-name" id="quote-form-product-name">' + (VARIANT ? VARIANT.productName:'Water Level Controller') + '</p>',
'      <h2 id="quote-form-title" class="quote-form-title">' + (VARIANT ? VARIANT.title:'Get Your Price') + '</h2>',
'      <p class="quote-form-subtitle">' + (VARIANT ? VARIANT.subtitle:'Get pricing for your specific configuration') + '</p>',
'    </div>',
'    ' + TEMPLATES.progressBar(),
'    <form id="quote-form" class="quote-form" novalidate>',
'      ' + TEMPLATES.hiddenFields(),
'      <div class="quote-form-steps">',
'        ' + TEMPLATES.step1(),
'        ' + TEMPLATES.step2(),
'        ' + TEMPLATES.step3(),
'        ' + TEMPLATES.successPanel(),
'      </div>',
'    </form>',
'  </div>',
'</div>'
].join('\n');
},
embedded: function(){
return [
'<div class="quote-form-embedded">',
'  <div class="quote-form-header">',
'    <h3 class="quote-form-title">Configure Your Quote</h3>',
'  </div>',
'  ' + TEMPLATES.progressBar(),
'  <form id="quote-form" class="quote-form" novalidate>',
'    ' + TEMPLATES.hiddenFields(),
'    <div class="quote-form-steps">',
'      ' + TEMPLATES.step1(),
'      ' + TEMPLATES.step2(),
'      ' + TEMPLATES.step3(),
'      ' + TEMPLATES.successPanel(),
'    </div>',
'  </form>',
'</div>'
].join('\n');
},
progressBar: function(){
var steps=VARIANT ? VARIANT.steps:[
{ id: 'specs', label: 'Specs' },
{ id: 'equipment', label: 'Accessories' },
{ id: 'contact', label: 'Contact' }
];
var stepsHtml=steps.map(function(step, i){
var activeClass=i===0 ? ' active':'';
var html='<div class="progress-step' + activeClass + '" data-step="' + (i + 1) + '">' +
'<span class="progress-step-number">' + (i + 1) + '</span>' +
'<span class="progress-step-label">' + step.label + '</span>' +
'</div>';
if(i < steps.length - 1){
html +='<div class="progress-connector"></div>';
}
return html;
}).join('\n');
return [
'<div class="quote-form-progress" aria-label="Form progress">',
'  <div class="progress-steps">',
'    ' + stepsHtml,
'  </div>',
'  <div class="progress-bar">',
'    <div class="progress-bar-fill" style="width: 33%"></div>',
'  </div>',
'</div>'
].join('\n');
},
hiddenFields: function(){
return [
'<input type="hidden" name="source_page" id="qf-source-page">',
'<input type="hidden" name="utm_source" id="qf-utm-source">',
'<input type="hidden" name="utm_medium" id="qf-utm-medium">',
'<input type="hidden" name="utm_campaign" id="qf-utm-campaign">',
'<input type="hidden" name="utm_content" id="qf-utm-content">',
'<input type="hidden" name="gclid" id="qf-gclid">',
'<input type="hidden" name="product_sku" id="qf-product-sku">',
'<input type="hidden" name="product_name" id="qf-product-name">',
'<input type="hidden" name="form_start_time" id="qf-start-time">',
'<input type="hidden" name="form_variant" id="qf-form-variant">'
].join('\n');
},
step1: function(){
var config=VARIANT ? VARIANT.step1:null;
var content='';
if(config&&config.layout==='split'){
content=TEMPLATES.step1Split(config);
}else{
content=TEMPLATES.step1Standard(config);
}
return [
'<div class="quote-form-step active" data-step="1">',
'  <div class="step-header">',
'    <h3 class="step-title">' + (config ? config.title:'System Specifications') + '</h3>',
'    <p class="step-description">' + (config ? config.description:'Tell us about your application requirements') + '</p>',
'  </div>',
'  <div class="step-content">',
'    ' + content,
'  </div>',
'  <div class="step-actions">',
'    <div></div>',
'    <button type="button" class="btn btn-primary btn-next" data-next="2">',
'      Continue',
'      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">',
'        <path d="M5 12h14M12 5l7 7-7 7"/>',
'      </svg>',
'    </button>',
'  </div>',
'</div>'
].join('\n');
},
step1Standard: function(config){
var fields=config ? config.fields:FIELDS.specifications;
var flaHtml=fields.fla ? '  ' + TEMPLATES.textField('fla', fields.fla):'';
var parts=[
'<div class="spec-fields" id="spec-fields">',
flaHtml,
'  ' + TEMPLATES.selectField('voltage', fields.voltage||FIELDS.specifications.voltage),
'</div>',
'<div class="help-toggle">',
'  <label class="checkbox-field">',
'    <input type="checkbox" name="needsHelp" id="qf-needs-help">',
'    <span class="checkbox-label">' + (config&&config.helpCheckbox ? config.helpCheckbox.label:"I'm not sure - help me configure the right system") + '</span>',
'  </label>',
'</div>'
];
if(config&&config.panelAccessories){
var pa=config.panelAccessories;
var paFieldsHtml='';
Object.keys(pa.fields).forEach(function(key){
var field=pa.fields[key];
if(field.options&&field.options.some(function(o){ return o.selected; })){
paFieldsHtml +=TEMPLATES.selectFieldWithSelected(key, field, false);
}else{
paFieldsHtml +=TEMPLATES.renderField(key, field, false);
}});
parts.push('<div class="step1-panel-accessories">',
'  <div class="section-header">',
'    <span class="section-label">' + pa.label + '</span>',
'  </div>',
'  <div class="section-fields">',
'    ' + paFieldsHtml,
'  </div>',
'</div>'
);
}
return parts.join('\n');
},
step1Split: function(config){
var pump=config.pump;
var panel=config.panel;
return [
'<div class="spec-section" data-section="pump">',
'  <div class="section-header"><span class="section-label">' + pump.label + '</span></div>',
'  <div class="section-fields" id="pump-spec-fields">',
'    <div class="field-row">',
'      ' + TEMPLATES.textField('pumpFla', pump.fields.pumpFla, true),
'      ' + TEMPLATES.selectField('pumpVoltage', pump.fields.pumpVoltage, true),
'    </div>',
'  </div>',
'  <div class="help-toggle">',
'    <label class="checkbox-field">',
'      <input type="checkbox" name="pumpNotSpecified" id="qf-pumpNotSpecified">',
'      <span class="checkbox-label">' + pump.notSpecifiedLabel + '</span>',
'    </label>',
'  </div>',
'</div>',
'<div class="spec-section" data-section="panel">',
'  <div class="section-header"><span class="section-label">' + panel.label + '</span></div>',
'  <div class="section-fields" id="panel-spec-fields">',
'    ' + TEMPLATES.selectField('panelVoltage', panel.fields.panelVoltage),
'  </div>',
'  <div class="help-toggle">',
'    <label class="checkbox-field">',
'      <input type="checkbox" name="panelNotSpecified" id="qf-panelNotSpecified">',
'      <span class="checkbox-label">' + panel.notSpecifiedLabel + '</span>',
'    </label>',
'  </div>',
'</div>'
].join('\n');
},
step2: function(){
var sectionsConfig=VARIANT ? VARIANT.sections:null;
var stepTitle='Equipment &amp; Accessories';
var stepDesc=VARIANT&&(VARIANT.variant==='controller'||VARIANT.variant==='controller-fla')
? 'Select the accessories for your controller'
: (sectionsConfig&&sectionsConfig.sensors&&sectionsConfig.sensors.description)
? sectionsConfig.sensors.description
: 'Configure your equipment and accessories';
var sectionsHtml='';
var sectionOrder=['sensors', 'tankHeight', 'cabinet', 'valves', 'pumps', 'panelAccessories'];
sectionOrder.forEach(function(sectionKey){
var section=sectionsConfig ? sectionsConfig[sectionKey]:null;
if(!section||!section.enabled) return;
if(sectionKey==='tankHeight'){
sectionsHtml +=TEMPLATES.tankHeightSection(section);
}else if(sectionKey==='panelAccessories'){
sectionsHtml +=TEMPLATES.panelAccessoriesSection(section);
}else{
sectionsHtml +=TEMPLATES.accessorySection(sectionKey, section);
}});
return [
'<div class="quote-form-step" data-step="2">',
'  <div class="step-header">',
'    <h3 class="step-title">' + stepTitle + '</h3>',
'    <p class="step-description">' + stepDesc + '</p>',
'  </div>',
'  <div class="step-content">',
'    ' + sectionsHtml,
'  </div>',
'  <div class="step-actions">',
'    <button type="button" class="btn btn-secondary btn-prev" data-prev="1">',
'      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">',
'        <path d="M19 12H5M12 19l-7-7 7-7"/>',
'      </svg>',
'      Back',
'    </button>',
'    <button type="button" class="btn btn-primary btn-next" data-next="3">',
'      Continue',
'      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">',
'        <path d="M5 12h14M12 5l7 7-7 7"/>',
'      </svg>',
'    </button>',
'  </div>',
'</div>'
].join('\n');
},
accessorySection: function(sectionKey, config){
var isOptional=config.optional;
var isRequired=config.required;
var invertToggle=config.invertToggle;
var collapsedClass=isOptional&&!invertToggle ? ' collapsed':'';
var sectionClass='accessory-section' + (isRequired ? ' required-section':'') + collapsedClass;
var headerHtml='';
if(isRequired){
headerHtml=[
'<div class="section-header">',
'  <span class="section-label">' + config.label + '</span>',
'</div>'
].join('\n');
if(config.description){
headerHtml +='<p class="section-description">' + config.description + '</p>';
}}else{
var toggleId='qf-' + sectionKey + 'Toggle';
headerHtml=[
'<label class="checkbox-field section-toggle">',
'  <input type="checkbox" name="' + sectionKey + 'Toggle" id="' + toggleId + '"' + (invertToggle ? ' checked':'') + '>',
'  <span class="checkbox-label section-label">' + config.label + '</span>',
'</label>'
].join('\n');
}
var fieldsHtml='';
var fieldKeys=Object.keys(config.fields);
var fieldPairs=[];
var singleFields=[];
fieldKeys.forEach(function(key){
var field=config.fields[key];
if(field.type==='text'&&field.placeholder&&key.indexOf('Other') > -1){
singleFields.push({ key: key, field: field, isOther: true });
}else{
fieldPairs.push({ key: key, field: field });
}});
for (var i=0; i < fieldPairs.length; i +=2){
if(i + 1 < fieldPairs.length){
fieldsHtml +='<div class="field-row">';
fieldsHtml +=TEMPLATES.renderField(fieldPairs[i].key, fieldPairs[i].field, true);
fieldsHtml +=TEMPLATES.renderField(fieldPairs[i + 1].key, fieldPairs[i + 1].field, true);
fieldsHtml +='</div>';
}else{
fieldsHtml +=TEMPLATES.renderField(fieldPairs[i].key, fieldPairs[i].field, false);
}}
singleFields.forEach(function(sf){
var displayStyle=sf.isOther ? ' style="display:none;"':'';
fieldsHtml +='<div id="' + sf.key + '-container"' + displayStyle + '>';
fieldsHtml +=TEMPLATES.renderField(sf.key, sf.field, false);
fieldsHtml +='</div>';
});
if(sectionKey==='sensors'&&config.dual){
fieldsHtml=TEMPLATES.dualSensorFields(config);
}else if(sectionKey==='sensors'&&!config.dual){
fieldsHtml=TEMPLATES.singleSensorFields(config);
}
var helpHtml='';
if(config.helpCheckboxLabel){
var helpId='qf-' + sectionKey + 'NeedRecommendation';
helpHtml=[
'<div class="section-recommendation">',
'  <label class="checkbox-field">',
'    <input type="checkbox" name="' + sectionKey + 'NeedRecommendation" id="' + helpId + '">',
'    <span class="checkbox-label">' + config.helpCheckboxLabel + '</span>',
'  </label>',
'</div>'
].join('\n');
}
return [
'<div class="' + sectionClass + '" data-section="' + sectionKey + '">',
'  ' + headerHtml,
'  <div class="section-fields" id="' + sectionKey + '-fields">',
'    ' + fieldsHtml,
'  </div>',
'  ' + helpHtml,
'</div>'
].join('\n');
},
dualSensorFields: function(config){
var f=config.fields;
return [
'<p class="section-sublabel">Primary Sensor</p>',
'<div class="field-row">',
'  ' + TEMPLATES.selectFieldWithSelected('primarySensorType', f.primarySensorType, true),
'  ' + TEMPLATES.selectFieldWithSelected('primaryWireLength', f.primaryWireLength, true),
'</div>',
'<p class="section-sublabel">Secondary Sensor</p>',
'<div class="field-row">',
'  ' + TEMPLATES.selectFieldWithSelected('secondarySensorType', f.secondarySensorType, true),
'  ' + TEMPLATES.selectFieldWithSelected('secondaryWireLength', f.secondaryWireLength, true),
'</div>'
].join('\n');
},
singleSensorFields: function(config){
var f=config.fields;
return [
'<div class="field-row">',
'  ' + TEMPLATES.selectFieldWithSelected('primarySensorType', f.primarySensorType, true),
'  ' + TEMPLATES.selectFieldWithSelected('primaryWireLength', f.primaryWireLength, true),
'</div>'
].join('\n');
},
tankHeightSection: function(config){
var fieldName=config.fieldName||'tankHeight';
var notSpecifiedHtml='';
if(config.notSpecifiedCheckbox){
notSpecifiedHtml=[
'<div class="tank-depth-not-specified">',
'  <label class="checkbox-field">',
'    <input type="checkbox" name="' + fieldName + 'NotSpecified" id="qf-' + fieldName + 'NotSpecified">',
'    <span class="checkbox-label">Not Specified</span>',
'  </label>',
'</div>'
].join('\n');
}
return [
'<div class="accessory-section required-section tank-height-section" data-section="tankHeight">',
'  <div class="section-header">',
'    <span class="section-label">' + config.label + '</span>',
'  </div>',
'  <div class="section-fields">',
'    <div class="form-group tank-height-field">',
'      <label class="form-label" for="qf-' + fieldName + '">' + config.label + '</label>',
'      <div class="input-with-suffix">',
'        <input type="number" class="form-input" id="qf-' + fieldName + '" name="' + fieldName + '" placeholder="' + config.placeholder + '" min="0" step="0.1">',
'        <span class="input-suffix">' + config.suffix + '</span>',
'      </div>',
'      ' + notSpecifiedHtml,
'    </div>',
'  </div>',
'</div>'
].join('\n');
},
panelAccessoriesSection: function(config){
var toggleId='qf-panelAccessoriesToggle';
var collapsedClass=config.invertToggle ? '':' collapsed';
var fieldsHtml='';
Object.keys(config.fields).forEach(function(key){
fieldsHtml +=TEMPLATES.renderField(key, config.fields[key], false);
});
var helpHtml='';
if(config.helpCheckboxLabel){
var helpId='qf-panelAccessoriesNeedRecommendation';
helpHtml=[
'<div class="section-recommendation">',
'  <label class="checkbox-field">',
'    <input type="checkbox" name="panelAccessoriesNeedRecommendation" id="' + helpId + '">',
'    <span class="checkbox-label">' + config.helpCheckboxLabel + '</span>',
'  </label>',
'</div>'
].join('\n');
}
return [
'<div class="accessory-section' + collapsedClass + '" data-section="panelAccessories">',
'  <div class="section-header">',
'    <span class="section-label">Panel Accessories</span>',
'  </div>',
'  <label class="checkbox-field section-toggle">',
'    <input type="checkbox" name="panelAccessoriesToggle" id="' + toggleId + '"' + (config.invertToggle ? ' checked':'') + '>',
'    <span class="checkbox-label">' + config.label + '</span>',
'  </label>',
'  <div class="section-fields" id="panelAccessories-fields">',
'    ' + fieldsHtml,
'  </div>',
'  ' + helpHtml,
'</div>'
].join('\n');
},
renderField: function(name, config, halfWidth){
if(config.type==='select'){
if(config.options&&config.options.some(function(o){ return o.selected; })){
return TEMPLATES.selectFieldWithSelected(name, config, halfWidth);
}
return TEMPLATES.selectField(name, config, halfWidth);
}else{
return TEMPLATES.textField(name, config, halfWidth);
}},
step3: function(){
var contactConfig=VARIANT ? VARIANT.contact:{};
var showNotes=contactConfig.notes||false;
var callCta=contactConfig.callCta||'480-905-1892';
var notesHtml=showNotes ? [
'<div class="form-group">',
'  <label class="form-label" for="qf-notes">Notes</label>',
'  <textarea class="form-input form-textarea" id="qf-notes" name="notes" placeholder="Any additional details..." rows="3"></textarea>',
'</div>'
].join('\n'):'';
return [
'<div class="quote-form-step" data-step="3">',
'  <div class="step-header">',
'    <h3 class="step-title">Almost Done!</h3>',
'    <p class="step-description">Where should we send your quote?</p>',
'  </div>',
'  <div class="step-content">',
'    <div class="config-summary" id="config-summary">',
'      <h4 class="summary-title">Your Configuration</h4>',
'      <ul class="summary-list" id="summary-list"></ul>',
'    </div>',
'    <div class="contact-fields">',
'      <div class="field-row">',
'        ' + TEMPLATES.textField('name', { label: 'Full Name', type: 'text', required: true, placeholder: 'John Smith' }, true),
'        ' + TEMPLATES.textField('company', { label: 'Company', type: 'text', required: true, placeholder: 'ACME Corp' }, true),
'      </div>',
'      <div class="field-row">',
'        ' + TEMPLATES.textField('email', { label: 'Email', type: 'email', required: true, placeholder: 'john@company.com' }, true),
'        ' + TEMPLATES.textField('phone', { label: 'Phone', type: 'tel', required: false, placeholder: '(555) 123-4567', helpText: 'Optional - for faster response' }, true),
'      </div>',
'      ' + notesHtml,
'    </div>',
'  </div>',
'  <div class="step-actions">',
'    <button type="button" class="btn btn-secondary btn-prev" data-prev="2">',
'      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">',
'        <path d="M19 12H5M12 19l-7-7 7-7"/>',
'      </svg>',
'      Back',
'    </button>',
'    <button type="submit" class="btn btn-primary btn-submit">',
'      Get My Price',
'      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">',
'        <path d="M22 2L11 13M22 2l-7 20-4-9-9-4 20-7z"/>',
'      </svg>',
'    </button>',
'  </div>',
'  <p class="step-microcopy">Based on your configuration &bull; No obligation</p>',
'</div>'
].join('\n');
},
successPanel: function(){
return [
'<div class="quote-form-step quote-form-success" data-step="success">',
'  <div class="success-content">',
'    <div class="success-icon">',
'      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">',
'        <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/>',
'        <polyline points="22 4 12 14.01 9 11.01"/>',
'      </svg>',
'    </div>',
'    <h3 class="success-title">Quote Request Received!</h3>',
'    <p class="success-message">',
'      Thank you! One of our application specialists will review your configuration and get back to you within one business day.',
'    </p>',
'    <div class="success-contact">',
'      <p>Need immediate assistance?</p>',
'      <a href="tel:+14809051892" class="success-phone">',
'        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">',
'          <path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"/>',
'        </svg>',
'        480-905-1892',
'      </a>',
'    </div>',
'  </div>',
'</div>'
].join('\n');
},
selectField: function(name, config, halfWidth){
var className=halfWidth ? 'form-group half':'form-group';
var requiredAttr=config.required ? ' required':'';
var requiredMark=config.required ? '<span class="required">*</span>':'';
var optionsHtml=config.options.map(function(opt){
return '<option value="' + opt.value + '">' + opt.label + '</option>';
}).join('');
var helpHtml=config.helpText
? '<span class="field-help">' + config.helpText + '</span>'
: '';
return [
'<div class="' + className + '">',
'  <label class="form-label" for="qf-' + name + '">' + config.label + requiredMark + '</label>',
'  <select class="form-select" id="qf-' + name + '" name="' + name + '"' + requiredAttr + '>',
'    ' + optionsHtml,
'  </select>',
'  ' + helpHtml,
'  <span class="field-error"></span>',
'</div>'
].join('\n');
},
selectFieldWithSelected: function(name, config, halfWidth){
var className=halfWidth ? 'form-group half':'form-group';
var requiredAttr=config.required ? ' required':'';
var requiredMark=config.required ? '<span class="required">*</span>':'';
var optionsHtml=config.options.map(function(opt){
var selectedAttr=opt.selected ? ' selected':'';
return '<option value="' + opt.value + '"' + selectedAttr + '>' + opt.label + '</option>';
}).join('');
var helpHtml=config.helpText
? '<span class="field-help">' + config.helpText + '</span>'
: '';
return [
'<div class="' + className + '">',
'  <label class="form-label" for="qf-' + name + '">' + config.label + requiredMark + '</label>',
'  <select class="form-select" id="qf-' + name + '" name="' + name + '"' + requiredAttr + '>',
'    ' + optionsHtml,
'  </select>',
'  ' + helpHtml,
'  <span class="field-error"></span>',
'</div>'
].join('\n');
},
textField: function(name, config, halfWidth){
var className=halfWidth ? 'form-group half':'form-group';
var requiredAttr=config.required ? ' required':'';
var requiredMark=config.required ? '<span class="required">*</span>':'';
var placeholderAttr=config.placeholder ? ' placeholder="' + config.placeholder + '"':'';
var inputType=config.type||'text';
var helpHtml=config.helpText
? '<span class="field-help">' + config.helpText + '</span>'
: '';
return [
'<div class="' + className + '">',
'  <label class="form-label" for="qf-' + name + '">' + config.label + requiredMark + '</label>',
'  <input type="' + inputType + '" class="form-input" id="qf-' + name + '" name="' + name + '"' + placeholderAttr + requiredAttr + '>',
'  ' + helpHtml,
'  <span class="field-error"></span>',
'</div>'
].join('\n');
}};
function init(){
state.container=document.getElementById(CONFIG.containerId);
if(!state.container){
console.warn('QuoteForm: Container #' + CONFIG.containerId + ' not found');
return;
}
var variantName=state.container.dataset.variant||'controller';
VARIANT=getVariantConfig(variantName);
if(!VARIANT){
console.warn('QuoteForm: Falling back to inline defaults');
}
state.options={
ctaColor: state.container.dataset.ctaColor||'red',
productSku: state.container.dataset.productSku||'',
mode: state.container.dataset.mode||'standalone',
variant: variantName
};
if(VARIANT&&VARIANT.submission){
CONFIG.webhookUrl=VARIANT.submission.webhookUrl||null;
}
CONFIG.storageKey='wlc_quote_form_data_' + (VARIANT ? VARIANT.variant:'default');
state.container.classList.add('quote-form-container');
state.container.dataset.ctaColor=state.options.ctaColor;
renderWidget();
cacheDomReferences();
bindEvents();
restoreSavedData();
captureTrackingParams();
trackEvent('quote_form_init', {
cta_color: state.options.ctaColor,
product_sku: state.options.productSku,
mode: state.options.mode
});
}
function renderWidget(){
if(state.options.mode==='embedded'){
state.container.innerHTML=TEMPLATES.embedded();
}else{
state.container.innerHTML=TEMPLATES.main();
var bodyRoot=document.createElement('div');
bodyRoot.className='quote-form-container';
bodyRoot.dataset.ctaColor=state.options.ctaColor;
while (state.container.firstChild){
bodyRoot.appendChild(state.container.firstChild);
}
document.body.appendChild(bodyRoot);
state.container=bodyRoot;
}}
function cacheDomReferences(){
var c=state.container;
state.dom={
overlay: c.querySelector('.quote-form-overlay'),
panel: c.querySelector('.quote-form-panel'),
closeBtn: c.querySelector('.quote-form-close'),
form: c.querySelector('#quote-form'),
steps: c.querySelectorAll('.quote-form-step'),
progressSteps: c.querySelectorAll('.progress-step'),
progressBar: c.querySelector('.progress-bar-fill'),
nextBtns: c.querySelectorAll('.btn-next'),
prevBtns: c.querySelectorAll('.btn-prev'),
submitBtn: c.querySelector('.btn-submit'),
productBadge: c.querySelector('#quote-form-product-badge'),
productName: c.querySelector('#quote-form-product-name'),
summaryList: c.querySelector('#summary-list')
};}
function bindEvents(){
var dom=state.dom;
if(dom.closeBtn){
dom.closeBtn.addEventListener('click', close);
}
if(dom.overlay){
dom.overlay.addEventListener('click', close);
}
dom.nextBtns.forEach(function(btn){
btn.addEventListener('click', function(){
var nextStep=parseInt(btn.dataset.next, 10);
goToStep(nextStep);
});
});
dom.prevBtns.forEach(function(btn){
btn.addEventListener('click', function(){
var prevStep=parseInt(btn.dataset.prev, 10);
goToStep(prevStep);
});
});
if(dom.form){
dom.form.addEventListener('submit', handleSubmit);
}
var recoToggles=state.container.querySelectorAll('[id$="NeedRecommendation"]');
recoToggles.forEach(function(toggle){
toggle.addEventListener('change', function(){
var section=toggle.closest('.accessory-section');
if(!section) return;
var fieldsEl=section.querySelector('.section-fields');
if(!fieldsEl) return;
if(toggle.checked){
fieldsEl.classList.add('fields-disabled');
fieldsEl.querySelectorAll('select, input:not([type="checkbox"])').forEach(function(el){ el.disabled=true; });
}else{
fieldsEl.classList.remove('fields-disabled');
fieldsEl.querySelectorAll('select, input:not([type="checkbox"])').forEach(function(el){ el.disabled=false; });
}});
});
var invertToggles=state.container.querySelectorAll('.section-toggle input');
invertToggles.forEach(function(toggle){
toggle.addEventListener('change', function(){
var section=toggle.closest('.accessory-section');
if(!section) return;
var sectionKey=section.dataset.section;
var sectionConfig=VARIANT&&VARIANT.sections ? VARIANT.sections[sectionKey]:null;
if(sectionConfig&&sectionConfig.invertToggle){
if(toggle.checked){
section.classList.add('collapsed');
}else{
section.classList.remove('collapsed');
}}else{
if(toggle.checked){
section.classList.remove('collapsed');
}else{
section.classList.add('collapsed');
}}
});
});
var needsHelpToggle=state.container.querySelector('#qf-needs-help');
if(needsHelpToggle){
needsHelpToggle.addEventListener('change', handleNeedsHelpToggle);
}
var pumpNotSpecified=state.container.querySelector('#qf-pumpNotSpecified');
var panelNotSpecified=state.container.querySelector('#qf-panelNotSpecified');
if(pumpNotSpecified){
pumpNotSpecified.addEventListener('change', function(){
handleNotSpecifiedToggle('pump-spec-fields', pumpNotSpecified.checked);
checkSkipLogic();
});
}
if(panelNotSpecified){
panelNotSpecified.addEventListener('change', function(){
handleNotSpecifiedToggle('panel-spec-fields', panelNotSpecified.checked);
checkSkipLogic();
});
}
var tankNotSpecifiedCheckboxes=state.container.querySelectorAll('[id$="NotSpecified"][id^="qf-tank"]');
tankNotSpecifiedCheckboxes.forEach(function(cb){
cb.addEventListener('change', function(){
var fieldName=cb.name.replace('NotSpecified', '');
var input=state.container.querySelector('#qf-' + fieldName);
if(!input) return;
if(cb.checked){
input.disabled=true;
input.value='';
input.closest('.form-group').classList.add('field-disabled');
}else{
input.disabled=false;
input.closest('.form-group').classList.remove('field-disabled');
}});
});
var secondarySensorSelect=state.container.querySelector('#qf-secondarySensorType');
if(secondarySensorSelect){
secondarySensorSelect.addEventListener('change', handleSecondarySensorChange);
}
var pumpGpmSelect=state.container.querySelector('#qf-pumpGpm');
if(pumpGpmSelect){
pumpGpmSelect.addEventListener('change', handlePumpGpmChange);
}
state.container.addEventListener('change', function(e){
if(e.target.matches('input, select, textarea')){
saveFormData();
}});
document.addEventListener('keydown', function(e){
if(e.key==='Escape'&&state.isOpen){
close();
}});
window.addEventListener('beforeunload', handleAbandonment);
}
function goToStep(stepNumber){
if(stepNumber > state.currentStep){
if(!validateStep(state.currentStep)){
return;
}
var timeOnStep=Date.now() - (state.stepTimes[state.currentStep]||state.startTime);
trackEvent('quote_form_step_complete', {
form_step: state.currentStep,
form_step_name: getStepName(state.currentStep),
time_on_step: timeOnStep
});
}
if(stepNumber===3){
updateConfigSummary();
}
saveFormData();
state.currentStep=stepNumber;
state.stepTimes[stepNumber]=Date.now();
updateProgressUI();
showStep(stepNumber);
trackEvent('quote_form_step_view', {
form_step: stepNumber,
form_step_name: getStepName(stepNumber)
});
}
function showStep(stepNumber){
state.dom.steps.forEach(function(step){
var stepNum=step.dataset.step;
if(stepNum===String(stepNumber)||stepNum===stepNumber){
step.classList.add('active');
}else{
step.classList.remove('active');
}});
if(window.innerWidth < 768&&state.dom.panel){
state.dom.panel.scrollTop=0;
}}
function updateProgressUI(){
var step=state.currentStep;
var progress=(step / state.totalSteps) * 100;
if(state.dom.progressBar){
state.dom.progressBar.style.width=progress + '%';
}
state.dom.progressSteps.forEach(function(stepEl){
var stepNum=parseInt(stepEl.dataset.step, 10);
stepEl.classList.remove('active', 'completed');
if(stepNum===step){
stepEl.classList.add('active');
}else if(stepNum < step){
stepEl.classList.add('completed');
}});
}
function getStepName(stepNumber){
var names={
1: 'specifications',
2: 'accessories',
3: 'contact'
};
return names[stepNumber]||'unknown';
}
function validateStep(stepNumber){
var stepEl=state.container.querySelector('.quote-form-step[data-step="' + stepNumber + '"]');
if(!stepEl) return true;
var valid=true;
var requiredFields=stepEl.querySelectorAll('[required]');
requiredFields.forEach(function(field){
var group=field.closest('.form-group');
var errorEl=group ? group.querySelector('.field-error'):null;
if(group) group.classList.remove('error');
if(errorEl) errorEl.textContent='';
var section=field.closest('.accessory-section');
if(section&&section.classList.contains('collapsed')){
return;
}
if(!field.value.trim()){
valid=false;
if(group) group.classList.add('error');
if(errorEl) errorEl.textContent='This field is required';
}else if(field.type==='email'&&!isValidEmail(field.value)){
valid=false;
if(group) group.classList.add('error');
if(errorEl) errorEl.textContent='Please enter a valid email';
}});
return valid;
}
function isValidEmail(email){
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
function handleNeedsHelpToggle(e){
var checkbox=e.target;
var specFields=state.container.querySelector('#spec-fields');
if(!specFields) return;
if(checkbox.checked){
specFields.classList.add('fields-disabled');
var inputs=specFields.querySelectorAll('select, input');
inputs.forEach(function(input){
input.disabled=true;
});
}else{
specFields.classList.remove('fields-disabled');
var inputs=specFields.querySelectorAll('select, input');
inputs.forEach(function(input){
input.disabled=false;
});
}}
function handleSecondarySensorChange(e){
var select=e.target;
var wireLengthField=state.container.querySelector('#qf-secondaryWireLength');
var wireLengthGroup=wireLengthField ? wireLengthField.closest('.form-group'):null;
if(!wireLengthField||!wireLengthGroup) return;
if(select.value==='none'){
wireLengthField.disabled=true;
wireLengthGroup.classList.add('field-disabled');
}else{
wireLengthField.disabled=false;
wireLengthGroup.classList.remove('field-disabled');
}}
function handlePumpGpmChange(e){
var select=e.target;
var otherContainer=state.container.querySelector('#pumpGpmOther-container');
if(!otherContainer) return;
if(select.value==='other'){
otherContainer.style.display='block';
}else{
otherContainer.style.display='none';
}}
function handleNotSpecifiedToggle(fieldsId, checked){
var fieldsEl=state.container.querySelector('#' + fieldsId);
if(!fieldsEl) return;
if(checked){
fieldsEl.classList.add('fields-disabled');
fieldsEl.querySelectorAll('select, input').forEach(function(el){ el.disabled=true; });
}else{
fieldsEl.classList.remove('fields-disabled');
fieldsEl.querySelectorAll('select, input').forEach(function(el){ el.disabled=false; });
}}
function checkSkipLogic(){
}
function updateConfigSummary(){
var summaryList=state.dom.summaryList;
if(!summaryList) return;
var standardFeatures=VARIANT&&VARIANT.standardFeatures ? VARIANT.standardFeatures:[];
var items=[];
if(state.productContext){
items.push(state.productContext.model||state.options.productSku);
}else if(state.options.productSku){
items.push(state.options.productSku);
}
if(VARIANT&&VARIANT.step1.layout==='split'){
var pumpNS=state.container.querySelector('#qf-pumpNotSpecified');
if(pumpNS&&pumpNS.checked){
items.push('Pump: Not Specified');
}else{
var pFla=getFieldValue('pumpFla');
var pVolt=getFieldValue('pumpVoltage');
if(pFla) items.push('Pump FLA: ' + pFla + ' Amps');
if(pVolt) items.push('Pump Voltage: ' + getOptionLabelFromConfig('pumpVoltage', pVolt));
}
var panelNS=state.container.querySelector('#qf-panelNotSpecified');
if(panelNS&&panelNS.checked){
items.push('Panel: Not Specified');
}else{
var panelV=getFieldValue('panelVoltage');
if(panelV) items.push('Panel Voltage: ' + getOptionLabelFromConfig('panelVoltage', panelV));
}}else{
var needsHelp=state.container.querySelector('#qf-needs-help');
if(needsHelp&&needsHelp.checked){
items.push('Configuration: Need assistance');
}else{
var fla=getFieldValue('fla');
var voltage=getFieldValue('voltage');
if(fla) items.push(fla + ' Amps FLA');
if(voltage) items.push(getOptionLabelFromConfig('voltage', voltage));
}}
var sectionsConfig=VARIANT ? VARIANT.sections:null;
if(sectionsConfig){
var sectionOrder=['sensors', 'tankHeight', 'cabinet', 'valves', 'pumps', 'panelAccessories'];
sectionOrder.forEach(function(sectionKey){
var section=sectionsConfig[sectionKey];
if(!section||!section.enabled) return;
if(sectionKey==='tankHeight'){
var fieldName=section.fieldName||'tankHeight';
var thVal=getFieldValue(fieldName);
if(thVal) items.push(section.label + ': ' + thVal + ' ' + section.suffix);
return;
}
var recoToggle=state.container.querySelector('#qf-' + sectionKey + 'NeedRecommendation');
if(recoToggle&&recoToggle.checked){
items.push(section.label + ': Need recommendation');
return;
}
if(section.optional){
var sectionToggle=state.container.querySelector('#qf-' + sectionKey + 'Toggle');
if(sectionToggle){
if(section.invertToggle){
if(sectionToggle.checked) return;
}else{
if(!sectionToggle.checked) return;
}}
}
if(section.fields){
Object.keys(section.fields).forEach(function(fieldKey){
var val=getFieldValue(fieldKey);
if(val&&val!==''&&val!=='none'){
var fieldConfig=section.fields[fieldKey];
items.push(fieldConfig.label + ': ' + getOptionLabelFromConfig(fieldKey, val));
}});
}});
}
if(VARIANT&&VARIANT.step1&&VARIANT.step1.panelAccessories){
var pa=VARIANT.step1.panelAccessories;
Object.keys(pa.fields).forEach(function(fieldKey){
var val=getFieldValue(fieldKey);
if(val&&val!==''&&val!=='none'&&val!=='no'){
var fieldConfig=pa.fields[fieldKey];
items.push(fieldConfig.label + ': ' + getOptionLabelFromConfig(fieldKey, val));
}});
}
var notes=getFieldValue('notes');
if(notes) items.push('Notes: ' + notes.substring(0, 50) + (notes.length > 50 ? '...':''));
summaryList.textContent='';
if(standardFeatures.length > 0){
var sfLabel=document.createElement('li');
sfLabel.className='summary-section-label';
sfLabel.textContent='Standard Features';
summaryList.appendChild(sfLabel);
standardFeatures.forEach(function(feature){
var li=document.createElement('li');
li.className='standard-feature';
li.textContent=feature;
summaryList.appendChild(li);
});
var configLabel=document.createElement('li');
configLabel.className='summary-section-label';
configLabel.textContent='Your Configuration';
summaryList.appendChild(configLabel);
}
items.forEach(function(item){
var li=document.createElement('li');
li.textContent=item;
summaryList.appendChild(li);
});
}
function getFieldValue(name){
var field=state.container.querySelector('#qf-' + name);
return field ? field.value:'';
}
function getOptionLabelFromConfig(fieldName, value){
if(VARIANT){
var step1=VARIANT.step1;
if(step1.fields&&step1.fields[fieldName]&&step1.fields[fieldName].options){
var opt=step1.fields[fieldName].options.find(function(o){ return o.value===value; });
if(opt) return opt.label;
}
if(step1.pump&&step1.pump.fields&&step1.pump.fields[fieldName]&&step1.pump.fields[fieldName].options){
var opt=step1.pump.fields[fieldName].options.find(function(o){ return o.value===value; });
if(opt) return opt.label;
}
if(step1.panel&&step1.panel.fields&&step1.panel.fields[fieldName]&&step1.panel.fields[fieldName].options){
var opt=step1.panel.fields[fieldName].options.find(function(o){ return o.value===value; });
if(opt) return opt.label;
}
if(step1.panelAccessories&&step1.panelAccessories.fields&&step1.panelAccessories.fields[fieldName]&&step1.panelAccessories.fields[fieldName].options){
var opt=step1.panelAccessories.fields[fieldName].options.find(function(o){ return o.value===value; });
if(opt) return opt.label;
}
var sections=VARIANT.sections;
if(sections){
var sectionKeys=Object.keys(sections);
for (var i=0; i < sectionKeys.length; i++){
var sec=sections[sectionKeys[i]];
if(sec.fields&&sec.fields[fieldName]&&sec.fields[fieldName].options){
var opt=sec.fields[fieldName].options.find(function(o){ return o.value===value; });
if(opt) return opt.label;
}}
}}
var allFields=Object.assign({}, FIELDS.specifications, FIELDS.accessories, FIELDS.contact);
var field=allFields[fieldName];
if(field&&field.options){
var opt=field.options.find(function(o){ return o.value===value; });
if(opt) return opt.label;
}
return value;
}
function handleSubmit(e){
e.preventDefault();
if(!validateStep(state.currentStep)){
return;
}
var form=e.target;
var formData=new FormData(form);
var leadValue=calculateLeadValue(formData);
trackEvent('quote_form_submit', {
form_name: 'product_quote',
product_sku: state.options.productSku,
lead_value: leadValue,
total_time: Date.now() - state.startTime
});
trackEvent('form_submit', {
form_id: 'wlc-quote-' + state.options.variant,
form_source: 'request_pricing_widget'
});
var submitData={
variant: formData.get('form_variant'),
name: formData.get('name'),
company: formData.get('company'),
email: formData.get('email'),
phone: formData.get('phone'),
notes: formData.get('notes'),
fla: formData.get('fla'),
voltage: formData.get('voltage'),
needsConfigurationHelp: formData.get('needsHelp')==='on',
pumpFla: formData.get('pumpFla'),
pumpVoltage: formData.get('pumpVoltage'),
panelVoltage: formData.get('panelVoltage'),
pumpNotSpecified: formData.get('pumpNotSpecified')==='on',
panelNotSpecified: formData.get('panelNotSpecified')==='on',
primarySensorType: formData.get('primarySensorType'),
primaryWireLength: formData.get('primaryWireLength'),
secondarySensorType: formData.get('secondarySensorType'),
secondaryWireLength: formData.get('secondaryWireLength'),
sensorsNeedRecommendation: formData.get('sensorsNeedRecommendation')==='on',
tankHeight: formData.get('tankHeight'),
tankDepth: formData.get('tankDepth'),
nemaGrade: formData.get('nemaGrade'),
enclosureType: formData.get('enclosureType'),
cabinetNeedRecommendation: formData.get('cabinetNeedRecommendation')==='on',
valvesToggle: formData.get('valvesToggle')==='on',
valvesNeedRecommendation: formData.get('valvesNeedRecommendation')==='on',
valveBrand: formData.get('valveBrand'),
valveType: formData.get('valveType'),
valveSize: formData.get('valveSize'),
valveConnection: formData.get('valveConnection'),
valveVoltage: formData.get('valveVoltage'),
pumpsToggle: formData.get('pumpsToggle')==='on',
pumpsNeedRecommendation: formData.get('pumpsNeedRecommendation')==='on',
pumpBrand: formData.get('pumpBrand'),
pumpGpm: formData.get('pumpGpm'),
pumpGpmOther: formData.get('pumpGpmOther'),
panelAccessoriesToggle: formData.get('panelAccessoriesToggle')==='on',
panelAccessoriesNeedRecommendation: formData.get('panelAccessoriesNeedRecommendation')==='on',
alarms: formData.get('alarms'),
beacons: formData.get('beacons'),
hoaSwitches: formData.get('hoaSwitches'),
source_page: formData.get('source_page'),
utm_source: formData.get('utm_source'),
utm_medium: formData.get('utm_medium'),
utm_campaign: formData.get('utm_campaign'),
utm_content: formData.get('utm_content'),
gclid: formData.get('gclid'),
product_sku: formData.get('product_sku'),
product_name: formData.get('product_name'),
form_start_time: formData.get('form_start_time'),
submit_time: new Date().toISOString(),
lead_value: leadValue
};
if(window.location.hostname==='localhost'||window.location.hostname==='127.0.0.1'){
console.log('[QuoteForm] Submission data:', submitData);
}
if(CONFIG.webhookUrl){
submitToWebhook(submitData);
}
clearSavedData();
showStep('success');
hideProgress();
}
function calculateLeadValue(formData){
var lv=VARIANT ? VARIANT.leadValue:{ base: 2500 };
var value=lv.base||2500;
if(formData.get('sensorsNeedRecommendation')!=='on') value +=(lv.sensors||0);
if(formData.get('cabinetNeedRecommendation')!=='on') value +=(lv.cabinet||0);
if(formData.get('needValves')==='on'||formData.get('valvesToggle')==='on') value +=(lv.valves||0);
if(formData.get('needPump')==='on'||formData.get('pumpsToggle')==='on') value +=(lv.pump||0);
var paToggle=formData.get('panelAccessoriesToggle');
var paConfig=VARIANT&&VARIANT.sections&&VARIANT.sections.panelAccessories;
var paInverted=paConfig&&paConfig.invertToggle;
if(paInverted ? paToggle===null:paToggle!==null) value +=(lv.accessories||0);
return value;
}
function submitToWebhook(data){
fetch(CONFIG.webhookUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
}).catch(function(error){
console.error('[QuoteForm] Webhook error:', error);
});
}
function hideProgress(){
var progressEl=state.container.querySelector('.quote-form-progress');
if(progressEl){
progressEl.style.display='none';
}}
function saveFormData(){
if(!state.dom.form) return;
var formData=new FormData(state.dom.form);
var data={};
formData.forEach(function(value, key){
data[key]=value;
});
data._timestamp=Date.now();
data._step=state.currentStep;
try {
localStorage.setItem(CONFIG.storageKey, JSON.stringify(data));
} catch (e){
console.warn('[QuoteForm] Could not save to localStorage:', e);
}}
function restoreSavedData(){
try {
var saved=localStorage.getItem(CONFIG.storageKey);
if(!saved) return;
var data=JSON.parse(saved);
if(Date.now() - data._timestamp > CONFIG.storageExpiry){
clearSavedData();
return;
}
Object.keys(data).forEach(function(key){
if(key.startsWith('_')) return;
var field=state.container.querySelector('[name="' + key + '"]');
if(!field) return;
if(field.type==='checkbox'){
field.checked=data[key]==='on';
if(field.closest('.section-toggle')){
var section=field.closest('.accessory-section');
if(section){
var sectionKey=section.dataset.section;
var sConf=sectionKey&&VARIANT&&VARIANT.sections ? VARIANT.sections[sectionKey]:null;
var isInverted=sConf&&sConf.invertToggle;
section.classList.toggle('collapsed', isInverted ? field.checked:!field.checked);
}}
}else{
field.value=data[key];
}});
if(data._step&&data._step > 1){
state.currentStep=data._step;
updateProgressUI();
showStep(data._step);
}} catch (e){
console.warn('[QuoteForm] Could not restore from localStorage:', e);
}}
function clearSavedData(){
try {
localStorage.removeItem(CONFIG.storageKey);
} catch (e){
}}
function captureTrackingParams(){
var urlParams=new URLSearchParams(window.location.search);
setHiddenField('source-page', window.location.pathname);
setHiddenField('utm-source', urlParams.get('utm_source')||'');
setHiddenField('utm-medium', urlParams.get('utm_medium')||'');
setHiddenField('utm-campaign', urlParams.get('utm_campaign')||'');
setHiddenField('utm-content', urlParams.get('utm_content')||'');
setHiddenField('gclid', urlParams.get('gclid')||'');
setHiddenField('product-sku', state.options.productSku);
setHiddenField('start-time', new Date().toISOString());
setHiddenField('form-variant', state.options.variant);
}
function setHiddenField(name, value){
var field=document.getElementById('qf-' + name);
if(field){
field.value=value;
}}
function trackEvent(eventName, eventData){
if(!CONFIG.gtmEnabled) return;
var data=Object.assign({
event: eventName,
form_name: 'product_quote',
product_sku: state.options.productSku
}, eventData);
if(typeof window.dataLayer!=='undefined'){
window.dataLayer.push(data);
}
if(window.location.hostname==='localhost'||window.location.hostname==='127.0.0.1'){
console.log('[QuoteForm GTM]', eventName, eventData);
}}
function handleAbandonment(){
if(!state.isOpen||state.currentStep===0) return;
var data={
event: 'quote_form_abandon',
form_name: 'product_quote',
form_step: state.currentStep,
form_step_name: getStepName(state.currentStep),
product_sku: state.options.productSku,
time_on_form: Date.now() - state.startTime
};
if(navigator.sendBeacon&&typeof window.dataLayer!=='undefined'){
console.log('[QuoteForm] Abandonment:', data);
}}
function open(product){
state.isOpen=true;
state.startTime=Date.now();
state.stepTimes[1]=Date.now();
if(product){
state.productContext=product;
state.options.productSku=product.id||product.model||state.options.productSku;
setHiddenField('product-sku', state.options.productSku);
setHiddenField('product-name', product.model||'');
if(state.dom.productBadge){
state.dom.productBadge.textContent=product.model||state.options.productSku;
state.dom.productBadge.style.display='inline-block';
}
if(state.dom.productName&&product.display&&product.display.description){
state.dom.productName.textContent=product.display.description;
}}
state.container.classList.add('quote-form-open');
if(state.dom.panel){
state.dom.panel.setAttribute('aria-hidden', 'false');
}
if(state.dom.overlay){
state.dom.overlay.setAttribute('aria-hidden', 'false');
}
setTimeout(function(){
var firstField=state.container.querySelector('.quote-form-step.active input, .quote-form-step.active select');
if(firstField){
firstField.focus();
}}, 300);
trackEvent('quote_form_start', {
form_step: 1,
product_sku: state.options.productSku
});
document.body.style.overflow='hidden';
}
function close(){
state.isOpen=false;
state.container.classList.remove('quote-form-open');
if(state.dom.panel){
state.dom.panel.setAttribute('aria-hidden', 'true');
}
if(state.dom.overlay){
state.dom.overlay.setAttribute('aria-hidden', 'true');
}
document.body.style.overflow='';
}
function reset(){
state.currentStep=1;
state.formData={};
state.productContext=null;
state.startTime=null;
state.stepTimes={};
if(state.dom.form){
state.dom.form.reset();
}
clearSavedData();
updateProgressUI();
showStep(1);
var progressEl=state.container.querySelector('.quote-form-progress');
if(progressEl){
progressEl.style.display='';
}}
window.onWizardQuoteOpen=function(product, formSlot){
open(product);
};
window.WLCQuoteForm={
init: init,
open: open,
close: close,
reset: reset,
goToStep: goToStep,
trackEvent: trackEvent
};
if(document.readyState==='loading'){
document.addEventListener('DOMContentLoaded', init);
}else{
init();
}})();