https://www.zendrop.com/wp-content/plugins/zendrop-elementor-blocks/assets/js/multistep.js?ver=3.5.5
1(function ($) { 2 $(document).ready(function () { 3 // Get user information from Zendrop account API 4 async function getUserInfo() { 5 try { 6 const response = await fetch("https://account.zendrop.com/api/user", { 7 method: "GET", 8 credentials: "include", 9 redirect: "error", 10 }); 11 12 if (!response.ok) { 13 throw new Error("Network response was not ok"); 14 } 15 return await response.json(); 16 } catch (error) { 17 // Return fallback values for local development 18 return { 19 // id: "test_user_123", 20 // email: "[email protected]", 21 // name: "Test User", 22 }; 23 } 24 } 25 26 // Check if user has completed multistep form 27 function checkMultistepFormStatus(userEmail) { 28 return new Promise((resolve, reject) => { 29 // Check if zendrop_ajax is available 30 if (typeof zendrop_ajax === "undefined") { 31 resolve({ completed: false, declined: false }); // Default to false if AJAX not available 32 return; 33 } 34 35 if (!userEmail) { 36 resolve({ completed: false, declined: false }); 37 return; 38 } 39 40 const ajaxData = { 41 action: "zendrop_check_multistep_status", 42 security: zendrop_ajax.nonce, 43 user_email: userEmail, 44 }; 45 46 $.ajax({ 47 url: '/wp-admin/admin-ajax.php', 48 type: "POST", 49 data: ajaxData, 50 success: function (response) { 51 if (response.success) { 52 resolve(response.data); 53 } else { 54 resolve({ completed: false, declined: false }); // Default to false on error 55 } 56 }, 57 error: function () { 58 resolve({ completed: false, declined: false }); // Default to false on error 59 }, 60 }); 61 }); 62 } 63 64 // Check upsell container and handle form redirection 65 async function checkUpsellContainerAndRedirect() { 66 // Check if upsell-container element exists 67 const upsellContainer = document.getElementById("upsell-container"); 68 69 if (!upsellContainer) { 70 // Element doesn't exist, do nothing 71 return; 72 } 73 74 try { 75 // First request: Get user information 76 const userInfo = await getUserInfo(); 77 78 if (!userInfo || !userInfo.email) { 79 const upsellLoader = document.getElementById("upsell-loader"); 80 81 if (upsellLoader) { 82 upsellLoader.style.display = "none"; 83 } 84 85 if (upsellContainer) { 86 upsellContainer.style.display = "flex"; 87 } 88 89 return; 90 } 91 92 // Second request: Check if user has filled multistep form 93 const formStatus = await checkMultistepFormStatus(userInfo.email); 94 95 if (!formStatus || (!formStatus.completed && !formStatus.declined)) { 96 // User hasn't filled or declined the form, redirect to /onboarding page 97 window.location.href = "/onboarding"; 98 } else { 99 const upsellLoader = document.getElementById("upsell-loader"); 100 // User has completed or declined the form, show the upsell container 101 if (upsellLoader) { 102 upsellLoader.style.display = "none"; 103 } 104 105 if (upsellContainer) { 106 upsellContainer.style.display = "flex"; 107 } 108 } 109 } catch (error) { 110 // Silently handle error 111 } 112 } 113 114 // Run the upsell check when page loads 115 checkUpsellContainerAndRedirect(); 116 117 // Initialize Multi-Step Forms 118 $(".zendrop-multistep-form-container").each(function () { 119 initMultiStepForm($(this)); 120 }); 121 122 function initMultiStepForm($container) { 123 const $form = $container.find(".zendrop-multistep-form"); 124 const $steps = $form.find(".zendrop-form-step"); 125 const $nextBtn = $form.find(".zendrop-next-btn"); 126 const $prevBtn = $form.find(".zendrop-prev-btn"); 127 const $submitBtn = $form.find(".zendrop-submit-btn"); 128 const $progressSteps = $container.find(".zendrop-progress-step"); 129 const $currentStepText = $container.find(".current-step"); 130 const totalSteps = parseInt($container.data("total-steps")); 131 132 let currentStep = 1; 133 let storedAnswerArray = []; // Store answer arrays from all steps: [hubspot_field, value, question] 134 let userEmail = ""; // Store user email for step saving 135 136 // Store original button text 137 $submitBtn.data("original-text", $submitBtn.text()); 138 139 // Function to collect data from ALL steps and convert to answer array format 140 function collectAllStepsData() { 141 const allAnswers = []; 142 143 // Loop through ALL steps, not just current step 144 $steps.each(function (stepIndex) { 145 const $stepDiv = $(this); 146 const stepAnswers = []; 147 148 collectStepData($stepDiv, stepAnswers); 149 allAnswers.push(...stepAnswers); 150 }); 151 152 // Update stored answer array with all collected data 153 storedAnswerArray = allAnswers; 154 return allAnswers; 155 } 156 157 // Function to save current step progress to server 158 async function saveCurrentStepProgress() { 159 const $currentStepDiv = $steps.eq(currentStep - 1); 160 const stepAnswers = []; 161 162 // Collect data from current step 163 collectStepData($currentStepDiv, stepAnswers); 164 165 // Convert answer array to simple key-value format for saving 166 // Only save HubSpot field name and value, nothing else 167 const stepData = {};
168 stepAnswers.forEach((answer) => { 169 const [hubspotField, value] = answer; 170 if (hubspotField) { 171 stepData[hubspotField] = value; 172 } 173 }); 174 175 // Get form ID 176 const formId = $form.data("form-id"); 177 178 // Get user email if not already fetched 179 if (!userEmail) { 180 try { 181 const userInfo = await getUserInfo(); 182 if (userInfo && userInfo.email) { 183 userEmail = userInfo.email; 184 } 185 } catch (error) { 186 return false; 187 } 188 } 189 190 // Check if zendrop_ajax is available 191 if (typeof zendrop_ajax === "undefined") { 192 return false; 193 } 194 195 // Save via AJAX 196 return new Promise((resolve) => { 197 $.ajax({ 198 url: '/wp-admin/admin-ajax.php', 199 type: "POST", 200 data: { 201 action: "zendrop_save_step_progress", 202 security: zendrop_ajax.nonce, 203 form_id: formId, 204 step_number: currentStep, 205 step_data: stepData, 206 user_email: userEmail, 207 }, 208 success: function (response) { 209 if (response.success) { 210 resolve(true); 211 } else { 212 resolve(false); 213 } 214 }, 215 error: function () { 216 resolve(false); 217 }, 218 }); 219 }); 220 } 221 222 // Helper function to collect data from a specific step div 223 function collectStepData($stepDiv, stepAnswers) { 224 // Helper function to check if field should be excluded 225 function shouldExcludeField(fieldName) { 226 if (!fieldName) return true; 227 if (fieldName.includes("widget_settings[")) return true; 228 if (fieldName.includes("form_steps[")) return true; 229 // DON'T exclude _visual fields - they contain user selections! 230 // Only exclude specific Elementor/widget configuration fields, not user answers 231 if (fieldName.startsWith("widget_settings")) return true; 232 if (fieldName.startsWith("form_steps")) return true; 233 if (fieldName.startsWith("elementor_")) return true; 234 return false; 235 } 236 237 // Helper function to get field info (hubspot mapping and question text) 238 function getFieldInfo($input) { 239 const $fieldGroup = $input.closest(".zendrop-field-group"); 240 const $label = $fieldGroup.find(".zendrop-field-label"); 241 const hubspotField = 242 $fieldGroup.data("hubspot-field") || 243 $fieldGroup.attr("data-hubspot-field") || 244 ""; 245 const questionText = 246 $label.length > 0 ? $label.text().replace(/\*$/, "").trim() : ""; 247 248 return { hubspotField, questionText }; 249 } 250 251 // Collect ALL inputs from this step (including selects and hidden fields) 252 const $allInputs = $stepDiv.find("input, textarea, select"); 253 254 $allInputs.each(function () { 255 const $input = $(this); 256 const name = $input.attr("name"); 257 const type = $input.attr("type"); 258 let value = $input.val(); 259 260 if (shouldExcludeField(name)) { 261 return; 262 } 263 264 // Handle different input types 265 if ($input.is("select")) { 266 // Handle select elements (including hidden ones) 267 if (value && value !== "" && value !== "-- Select --") { 268 if (Array.isArray(value)) { 269 value = value.filter( 270 (v) => v && v !== "" && v !== "-- Select --" 271 ); 272 if (value.length > 0) { 273 const { hubspotField, questionText } = getFieldInfo($input); 274 stepAnswers.push([ 275 hubspotField, 276 value.join(", "), 277 questionText, 278 ]); 279 } 280 } else { 281 const { hubspotField, questionText } = getFieldInfo($input); 282 stepAnswers.push([hubspotField, value, questionText]); 283 } 284 } 285 } else if (type === "radio") { 286 // Handle radio buttons (only if checked) 287 if ($input.is(":checked")) { 288 const { hubspotField, questionText } = getFieldInfo($input); 289 stepAnswers.push([hubspotField, value, questionText]); 290 } 291 } else if (type === "checkbox") { 292 // Handle checkboxes (only if checked) 293 if ($input.is(":checked")) { 294 const { hubspotField, questionText } = getFieldInfo($input); 295 296 // If checkbox has empty value but has a label, use the label text as value 297 let checkboxValue = value; 298 if (!checkboxValue || checkboxValue.trim() === "") { 299 const $label = $input.closest(".zendrop-option-label"); 300 if ($label.length > 0) { 301 checkboxValue = $label.text().trim(); 302 } 303 } 304 305 if (checkboxValue && checkboxValue.trim() !== "") { 306 stepAnswers.push([hubspotField, checkboxValue, questionText]); 307 } 308 } 309 } else { 310 // Handle regular text inputs, textareas, etc. 311 if (value && value.trim() !== "") { 312 const { hubspotField, questionText } = getFieldInfo($input); 313 stepAnswers.push([hubspotField, value.trim(), questionText]); 314 } 315 } 316 }); 317 } 318 319 // Handle conditional fields 320 function handleConditionalFields() { 321 $form.find(".conditional-field").each(function () { 322 const $field = $(this); 323 const parentFieldId = $field.data("parent-field"); 324 const triggerValues = $field 325 .data("trigger-value") 326 .toString() 327 .split(",") 328 .map((v) => v.trim()); 329 // Look for parent field - check hidden select first, then visual inputs, then regular inputs 330 let $parentField = $form.find(`#${parentFieldId}_hidden_select`); 331 332 if ($parentField.length === 0) { 333 $parentField = $form.find(`select[name="${parentFieldId}"]`); 334 } 335 336 if ($parentField.length === 0) { 337 $parentField = $form.find(`input[name="${parentFieldId}_visual"]`); 338 } 339 340 if ($parentField.length === 0) { 341 $parentField = $form.find( 342 `input[name="${parentFieldId}"], #${parentFieldId}` 343 ); 344 } 345 346 if ($parentField.length === 0) return; 347 348 function checkVisibility() { 349 let parentValue = ""; 350 351 if ($parentField.is("select")) { 352 // Hidden select field 353 parentValue = $parentField.val(); 354 if (Array.isArray(parentValue)) { 355 parentValue = parentValue.join(","); 356 } 357 } else if ($parentField.is('input[type="radio"]')) { 358 // Visual radio buttons 359 parentValue = $parentField.filter(":checked").val() || ""; 360 } else if ($parentField.is('input[type="checkbox"]')) { 361 // Visual checkboxes 362 const checkedValues = $parentField 363 .filter(":checked") 364 .map(function () { 365 return $(this).val(); 366 }) 367 .get(); 368 parentValue = checkedValues.join(","); 369 } else { 370 // Regular text inputs 371 parentValue = $parentField.val() || ""; 372 } 373 374 const shouldShow = triggerValues.some((triggerValue) => { 375 if (parentValue.includes(",")) { 376 return parentValue.split(",").includes(triggerValue); 377 } 378 return parentValue === triggerValue; 379 }); 380 381 if (shouldShow) { 382 $field.slideDown(300, function () { 383 updateButtonState(); // Update button state when field becomes visible 384 385 // Capture data from newly visible fields that have values 386 $field 387 .find( 388 "input:checked, input[value!=''], textarea[value!=''], select" 389 ) 390 .each(function () { 391 const $input = $(this); 392 const name = $input.attr("name"); 393 let value = ""; 394 395 if ($input.is(":radio") || $input.is(":checkbox")) { 396 if ($input.is(":checked")) { 397 value = $input.val(); 398 } 399 } else if ($input.is("select")) { 400 value = $input.val(); 401 if (Array.isArray(value)) { 402 value = value.filter( 403 (v) => v && v !== "" && v !== "-- Select --" 404 ); 405 } 406 } else { 407 value = $input.val(); 408 } 409 410 // If field has value, add it to stored answers 411 if ( 412 value && 413 (Array.isArray(value) 414 ? value.length > 0 415 : value.toString().trim() !== "") 416 ) { 417 const $fieldGroup = $input.closest( 418 ".zendrop-field-group" 419 ); 420 const $label = $fieldGroup.find(".zendrop-field-label"); 421 const hubspotField = 422 $fieldGroup.data("hubspot-field") || 423 $fieldGroup.attr("data-hubspot-field") || 424 ""; 425 const questionText = 426 $label.length > 0 427 ? $label.text().replace(/\*$/, "").trim() 428 : ""; 429 430 const processedValue = Array.isArray(value) 431 ? value.join(", ") 432 : value.toString().trim(); 433 const newAnswer = [ 434 hubspotField, 435 processedValue, 436 questionText, 437 ]; 438 439 // Check if this answer already exists 440 const exists = storedAnswerArray.some( 441 (stored) => 442 stored[0] === newAnswer[0] && 443 stored[2] === newAnswer[2] 444 ); 445 if (!exists) { 446 storedAnswerArray.push(newAnswer); 447 } 448 } 449 }); 450 }); 451 } else {
452 $field.slideUp(300, function () { 453 updateButtonState(); // Update button state when field becomes hidden 454 }); 455 // Clear field values when hidden 456 $field.find("input, select, textarea").val(""); 457 $field 458 .find('input[type="checkbox"], input[type="radio"]') 459 .prop("checked", false); 460 } 461 } 462 463 // Initial check 464 checkVisibility(); 465 466 // Listen for changes on both visible and hidden fields 467 $parentField.on("change input", checkVisibility); 468 469 // Also listen to hidden select if we found visual inputs 470 if ($parentField.is("input")) { 471 const hiddenSelectId = parentFieldId + "_hidden_select"; 472 const $hiddenSelect = $form.find(`#${hiddenSelectId}`); 473 if ($hiddenSelect.length > 0) { 474 $hiddenSelect.on("change", checkVisibility); 475 } 476 } 477 }); 478 } 479 480 // Initialize conditional fields 481 handleConditionalFields(); 482 483 // Function to update hidden select fields based on visual input changes 484 function updateHiddenSelect($input, $targetSelect, inputType) { 485 const value = $input.val(); 486 const isChecked = $input.is(":checked"); 487 488 if (inputType === "radio") { 489 // For radio buttons, set the select value directly 490 if (isChecked) { 491 $targetSelect.val(value); 492 } 493 } else if (inputType === "checkbox") { 494 // For checkboxes, handle multiple selections 495 let currentValues = $targetSelect.val() || []; 496 if (!Array.isArray(currentValues)) { 497 currentValues = currentValues ? [currentValues] : []; 498 } 499 500 if (isChecked) { 501 // Add value if not already present 502 if (!currentValues.includes(value)) { 503 currentValues.push(value); 504 } 505 } else { 506 // Remove value if present 507 currentValues = currentValues.filter((v) => v !== value); 508 } 509 510 $targetSelect.val(currentValues); 511 } 512 513 // Trigger change event on the hidden select to update any dependent logic 514 $targetSelect.trigger("change"); 515 } 516 517 // Handle custom radio/checkbox styling and hidden field updates 518 function handleCustomOptions() { 519 // Add visual styling for selected options and update hidden fields 520 $form.on( 521 "change", 522 "input[type='radio'], input[type='checkbox']", 523 function () { 524 const $input = $(this); 525 const $label = $input.closest(".zendrop-option-label"); 526 const inputType = $input.attr("type"); 527 const targetSelectId = $input.data("target-select"); 528 529 // Update hidden select field if this is a visual input 530 if (targetSelectId) { 531 const $targetSelect = $form.find(`#${targetSelectId}`); 532 if ($targetSelect.length > 0) { 533 updateHiddenSelect($input, $targetSelect, inputType); 534 } 535 } 536 537 if (inputType === "radio") { 538 // Clear other radio buttons in the same group 539 const inputName = $input.attr("name"); 540 const $radioGroup = $form.find(`input[name="${inputName}"]`); 541 542 $radioGroup 543 .closest(".zendrop-option-label") 544 .removeClass("selected"); 545 546 // If this input has a target select, clear other radio buttons' target selects in the same group 547 if (targetSelectId) { 548 $radioGroup.not($input).each(function () { 549 const otherTargetSelectId = $(this).data("target-select"); 550 if ( 551 otherTargetSelectId && 552 otherTargetSelectId !== targetSelectId 553 ) { 554 const $otherTargetSelect = $form.find( 555 `#${otherTargetSelectId}` 556 ); 557 if ($otherTargetSelect.length > 0) { 558 $otherTargetSelect.val("").trigger("change"); 559 } 560 } 561 }); 562 } 563 564 // Select this one if checked 565 if ($input.is(":checked")) { 566 $label.addClass("selected"); 567 } 568 } else if (inputType === "checkbox") { 569 // Toggle checkbox styling 570 $label.toggleClass("selected", $input.is(":checked")); 571 } 572 } 573 ); 574 575 // Initialize selected states and hidden field values 576 $form 577 .find("input:checked") 578 .closest(".zendrop-option-label") 579 .addClass("selected"); 580 581 // Initialize hidden select values based on pre-checked inputs 582 $form 583 .find("input[type='radio']:checked, input[type='checkbox']:checked") 584 .each(function () { 585 const $input = $(this); 586 const targetSelectId = $input.data("target-select");
587 if (targetSelectId) { 588 const $targetSelect = $form.find(`#${targetSelectId}`); 589 if ($targetSelect.length > 0) { 590 updateHiddenSelect($input, $targetSelect, $input.attr("type")); 591 } 592 } 593 }); 594 } 595 596 // Initialize custom options 597 handleCustomOptions(); 598 599 // Check if current step has required fields and update button state 600 function updateButtonState() { 601 const isValid = validateCurrentStep(); 602 603 if (currentStep === totalSteps) { 604 // Last step - update submit button 605 $submitBtn.prop("disabled", !isValid); 606 } else { 607 // Not last step - update next button 608 $nextBtn.prop("disabled", !isValid); 609 } 610 } 611 612 // Listen for changes in form fields to update button state 613 function initButtonStateTracking() { 614 $form.on("input change", "input, textarea, select", function () { 615 updateButtonState(); 616 }); 617 618 // Also listen for custom option changes 619 $form.on("click", ".zendrop-option-label", function () { 620 setTimeout(updateButtonState, 10); 621 }); 622 } 623 624 // Initialize button state tracking 625 initButtonStateTracking(); 626 627 // Validate current step 628 function validateCurrentStep() { 629 const $currentStepDiv = $steps.eq(currentStep - 1); 630 let isValid = true; 631 632 // Check regular required fields (visible inputs, textareas) 633 const $requiredFields = $currentStepDiv 634 .find("input[required], textarea[required]") 635 .filter(":visible"); 636 637 $requiredFields.each(function () { 638 const $field = $(this); 639 const fieldType = $field.attr("type"); 640 641 // Clear previous error styling 642 $field.removeClass("error"); 643 644 if (fieldType === "radio") { 645 const name = $field.attr("name"); 646 const $radioGroup = $currentStepDiv.find(`input[name="${name}"]`); 647 if (!$radioGroup.is(":checked")) { 648 $radioGroup.addClass("error"); 649 isValid = false; 650 } 651 } else if (fieldType === "checkbox") { 652 const name = $field.attr("name"); 653 const $checkboxGroup = $currentStepDiv.find( 654 `input[name="${name}"]` 655 ); 656 if (!$checkboxGroup.is(":checked")) { 657 $checkboxGroup.addClass("error"); 658 isValid = false; 659 } 660 } else { 661 if (!$field.val().trim()) { 662 $field.addClass("error"); 663 isValid = false; 664 } 665 } 666 }); 667 668 // Check required radio and checkbox groups 669 $currentStepDiv 670 .find(".zendrop-field-options[data-field-type]") 671 .each(function () { 672 const $fieldOptions = $(this); 673 const fieldType = $fieldOptions.data("field-type"); 674 const $requiredInputs = $fieldOptions.find("input[required]"); 675 676 if ($requiredInputs.length > 0) { 677 const $allInputs = $fieldOptions.find("input"); 678 679 if (fieldType === "multiselect") { 680 // For multiselect, check if at least one checkbox is checked 681 if (!$allInputs.is(":checked")) { 682 $allInputs.closest(".zendrop-option-label").addClass("error"); 683 isValid = false; 684 } 685 } else if (fieldType === "select") { 686 // For single select, check if a radio is selected 687 if (!$allInputs.is(":checked")) { 688 $allInputs.closest(".zendrop-option-label").addClass("error"); 689 isValid = false; 690 } 691 } 692 } 693 }); 694 695 return isValid; 696 } 697 698 // Update progress bar 699 function updateProgress() { 700 $currentStepText.text(currentStep); 701 702 // Update progress steps 703 $progressSteps.each(function (index) { 704 const $step = $(this); 705 const stepNumber = index + 1; 706 707 $step.removeClass("active completed"); 708 709 if (stepNumber < currentStep) { 710 $step.addClass("completed"); 711 } else if (stepNumber === currentStep) { 712 $step.addClass("active"); 713 } 714 }); 715 } 716 717 // Show specific step 718 function showStep(stepNumber) { 719 $steps.removeClass("active"); 720 $steps.eq(stepNumber - 1).addClass("active"); 721 722 // Update button visibility (previous button always hidden/
722disabled) 723 $prevBtn.hide(); 724 725 if (stepNumber === totalSteps) { 726 $nextBtn.hide(); 727 $submitBtn.show(); 728 } else { 729 $nextBtn.show(); 730 $submitBtn.hide(); 731 } 732 733 updateProgress(); 734 handleConditionalFields(); // Re-check conditional fields 735 handleCustomOptions(); // Re-initialize custom option styling 736 updateButtonState(); // Update button state for new step 737 } 738 739 // Next button click 740 $nextBtn.on("click", async function (e) { 741 e.preventDefault(); 742 743 if (validateCurrentStep()) { 744 // Collect data from all steps before moving to next step 745 collectAllStepsData(); 746 747 // Save current step progress 748 await saveCurrentStepProgress(); 749 750 if (currentStep < totalSteps) { 751 currentStep++; 752 showStep(currentStep); 753 } 754 } else { 755 // Show validation message 756 showValidationMessage("Please fill in all required fields."); 757 } 758 }); 759 760 // Function to decline form 761 async function declineForm() { 762 // Collect data from all steps first 763 collectAllStepsData(); 764 765 // Get user email if not already fetched 766 if (!userEmail) { 767 try { 768 const userInfo = await getUserInfo(); 769 if (userInfo && userInfo.email) { 770 userEmail = userInfo.email; 771 } 772 } catch (error) { 773 return false; 774 } 775 } 776 777 // Check if zendrop_ajax is available 778 if (typeof zendrop_ajax === "undefined") { 779 return false; 780 } 781 782 // Get form ID 783 const formId = $form.data("form-id"); 784 785 // Convert answer array to simple key-value format for saving 786 const stepData = {}; 787 storedAnswerArray.forEach((answer) => { 788 const [hubspotField, value] = answer; 789 if (hubspotField) { 790 stepData[hubspotField] = value; 791 } 792 }); 793 794 // Save partial data and declined status via AJAX 795 return new Promise((resolve) => { 796 $.ajax({ 797 url: '/wp-admin/admin-ajax.php', 798 type: "POST", 799 data: { 800 action: "zendrop_save_step_progress", 801 security: zendrop_ajax.nonce, 802 form_id: formId, 803 step_number: currentStep, 804 step_data: stepData, 805 user_email: userEmail, 806 }, 807 success: function (response) { 808 // After saving partial data, mark as declined 809 $.ajax({ 810 url: '/wp-admin/admin-ajax.php', 811 type: "POST", 812 data: { 813 action: "zendrop_decline_form", 814 security: zendrop_ajax.nonce, 815 user_email: userEmail, 816 }, 817 success: function (response) { 818 resolve(response.success); 819 }, 820 error: function () { 821 resolve(false); 822 }, 823 }); 824 }, 825 error: function () { 826 $.ajax({ 827 url: '/wp-admin/admin-ajax.php', 828 type: "POST", 829 data: { 830 action: "zendrop_decline_form", 831 security: zendrop_ajax.nonce, 832 user_email: userEmail, 833 }, 834 success: function (response) { 835 resolve(response.success); 836 }, 837 error: function () { 838 resolve(false); 839 }, 840 }); 841 }, 842 }); 843 }); 844 } 845 846 // Close button click (if exists) 847 const $closeBtn = $container.find(".zendrop-form-close-btn"); 848 $closeBtn.on("click", async function (e) { 849 e.preventDefault(); 850 851 // Save declined status before redirecting 852 await declineForm(); 853 854 // Get widget settings for close button URL 855 let widgetSettings = {}; 856 try { 857 const settingsData = $container.data("widget-settings"); 858 if (settingsData) { 859 widgetSettings = 860 typeof settingsData === "string" 861 ? JSON.parse(settingsData) 862 : settingsData; 863 } 864 } catch (e) { 865 // If parsing fails, use history.back() 866 window.history.back(); 867 return; 868 } 869 870 const closeButtonUrl = widgetSettings.close_button_url; 871 872 if (closeButtonUrl && closeButtonUrl.trim() !== "") { 873 // Redirect to custom URL 874 window.location.href = closeButtonUrl; 875 } else { 876 // Redirect back to previous page 877 window.history.back(); 878 } 879 }); 880 881 // Previous button click (disabled) 882 $prevBtn.on("click", function (e) { 883 e.preventDefault(); 884 // Previous button is disabled - no functionality 885 return false;
886 }); 887 888 // Form submission 889 $form.on("submit", async function (e) { 890 e.preventDefault(); // Always prevent default form submission 891 892 if (!validateCurrentStep()) { 893 showValidationMessage("Please fill in all required fields."); 894 return false; 895 } 896 897 // Show loading state 898 $submitBtn.prop("disabled", true).text("Submitting..."); 899 900 try { 901 // Get user information from Zendrop API 902 const userInfo = await getUserInfo(); 903 904 // Collect data from all steps for final submission 905 collectAllStepsData(); 906 907 // Add Zendrop user information to answer array 908 if (userInfo) { 909 // Add user ID 910 const userIdAnswer = [ 911 "zendrop_user_id", 912 userInfo.id, 913 "Zendrop user id", 914 ]; 915 const emailAnswer = ["email", userInfo.email, "Email"]; 916 917 // Check if these don't already exist 918 const userIdExists = storedAnswerArray.some( 919 (stored) => stored[0] === "zendrop_user_id" 920 ); 921 const emailExists = storedAnswerArray.some( 922 (stored) => stored[0] === "email" 923 ); 924 925 if (!userIdExists) { 926 storedAnswerArray.push(userIdAnswer); 927 } 928 if (!emailExists) { 929 storedAnswerArray.push(emailAnswer); 930 } 931 } 932 933 // Deduplicate answers by HubSpot field name (keep the last occurrence) 934 const deduplicatedAnswers = []; 935 const seenFields = new Map(); 936 937 // Process in reverse to keep last occurrence 938 for (let i = storedAnswerArray.length - 1; i >= 0; i--) { 939 const [hubspotField, value, questionText] = storedAnswerArray[i]; 940 941 // Use hubspot field as key, or question text if no hubspot field 942 const key = hubspotField || questionText; 943 944 if (key && !seenFields.has(key)) { 945 seenFields.set(key, true); 946 deduplicatedAnswers.unshift([hubspotField, value, questionText]); 947 } 948 } 949 950 // Submit via AJAX 951 submitFormAjax(deduplicatedAnswers); 952 } catch (error) { 953 // Restore button state 954 $submitBtn 955 .prop("disabled", false) 956 .text($submitBtn.data("original-text") || "Submit"); 957 showValidationMessage( 958 "Error retrieving user information. Please try again." 959 ); 960 } 961 }); 962 963 // AJAX form submission 964 function submitFormAjax(answerArray) { 965 // Check if zendrop_ajax is available 966 if (typeof zendrop_ajax === "undefined") { 967 showValidationMessage( 968 "Form configuration error. Please refresh the page and try again." 969 ); 970 $submitBtn 971 .prop("disabled", false) 972 .text($submitBtn.data("original-text") || "Submit"); 973 return; 974 } 975 976 // Get form ID 977 const formId = $form.data("form-id"); 978 979 // Process the answer array (it's already in the correct format) 980 // Just apply auto-mapping for fields without HubSpot mapping 981 982 // Auto-map common fields if no HubSpot field is specified 983 const processedAnswers = answerArray.map((answer) => { 984 let [hubspotField, value, questionText] = answer; 985 986 // Auto-map common fields if no HubSpot field is specified 987 if (!hubspotField) { 988 if ( 989 questionText.toLowerCase().includes("email") || 990 (typeof value === "string" && value.includes("@")) 991 ) { 992 hubspotField = "email"; 993 } else if ( 994 questionText.includes("zendrop_user_id") || 995 questionText.includes("Zendrop user id") 996 ) { 997 hubspotField = "zendrop_user_id"; 998 } else if ( 999 questionText.toLowerCase().includes("name") && 1000 !questionText.toLowerCase().includes("company") 1001 ) { 1002 hubspotField = "firstname"; 1003 } else if (questionText.toLowerCase().includes("company")) { 1004 hubspotField = "company"; 1005 } else if (questionText.toLowerCase().includes("phone")) { 1006 hubspotField = "phone"; 1007 } 1008 } 1009 1010 return [hubspotField, value, questionText]; 1011 }); 1012 1013 // Create clean submission data with processed answer array 1014 const submissionData = { 1015 form_id: formId, 1016 answers: processedAnswers, 1017 }; 1018 1019 $.ajax({ 1020 url: '/wp-admin/admin-ajax.php', 1021 type: "POST", 1022 data: { 1023 action: "zendrop_submit_questionare", 1024 security: zendrop_ajax.nonce, 1025 submission: submissionData, 1026 }, 1027 success: function (response) { 1028 if (response.success) { 1029 let message = 1030 response.data.message || "Form submitted successfully!"; 1031 1032 // Add HubSpot sync status to message 1033 if (response.data.hubspot) { 1034 if (response.data.hubspot.success) { 1035 message += " Contact synced to HubSpot."; 1036 } 1037 } 1038 1039 // Handle redirect if configured, otherwise show success message 1040 const hasRedirect = checkForRedirect(); 1041 if (hasRedirect) { 1042 handleSuccessRedirect(); 1043 } else { 1044 showSuccessMessage(message); 1045 } 1046 } else { 1047 showValidationMessage( 1048 response.data.message || 1049 "Form submission failed. Please try again." 1050 ); 1051 } 1052 }, 1053 error: function () { 1054 showValidationMessage("Form submission failed. Please try again."); 1055 }, 1056 complete: function () { 1057 // Restore button state 1058 $submitBtn 1059 .prop("disabled", false) 1060 .text($submitBtn.data("original-text") || "Submit"); 1061 }, 1062 }); 1063 } 1064 1065 // Check if redirect is configured 1066 function checkForRedirect() { 1067 let widgetSettings = {}; 1068 try { 1069 const settingsData = $container.data("widget-settings"); 1070 if (settingsData) { 1071 widgetSettings = 1072 typeof settingsData === "string" 1073 ? JSON.parse(settingsData) 1074 : settingsData; 1075 } 1076 } catch (e) { 1077 return false;
1078 } 1079 1080 const redirectUrl = widgetSettings.success_redirect_url; 1081 return redirectUrl && redirectUrl.trim() !== ""; 1082 } 1083 1084 // Handle success redirect 1085 function handleSuccessRedirect() { 1086 // Get widget settings for redirect configuration 1087 let widgetSettings = {}; 1088 try { 1089 const settingsData = $container.data("widget-settings"); 1090 if (settingsData) { 1091 widgetSettings = 1092 typeof settingsData === "string" 1093 ? JSON.parse(settingsData) 1094 : settingsData; 1095 } 1096 } catch (e) { 1097 return; 1098 } 1099 1100 const redirectUrl = widgetSettings.success_redirect_url; 1101 1102 if (redirectUrl && redirectUrl.trim() !== "") { 1103 // Always redirect immediately 1104 window.location.href = redirectUrl; 1105 } 1106 } 1107 1108 // Show validation message 1109 function showValidationMessage(message) { 1110 // Remove existing messages 1111 $container.find(".zendrop-form-message").remove(); 1112 1113 const $message = $( 1114 `<div class="zendrop-form-message error">${message}</div>` 1115 ); 1116 $form.prepend($message); 1117 1118 setTimeout(function () { 1119 $message.fadeOut(function () { 1120 $message.remove(); 1121 }); 1122 }, 3000); 1123 } 1124 1125 // Show success message 1126 function showSuccessMessage(message) { 1127 // Remove existing messages 1128 $container.find(".zendrop-form-message").remove(); 1129 1130 const $message = $( 1131 `<div class="zendrop-form-message success">${message}</div>` 1132 ); 1133 $form.prepend($message); 1134 1135 // Don't reset form automatically - let user navigate away or manually reset 1136 setTimeout(function () { 1137 $message.fadeOut(function () { 1138 $message.remove(); 1139 }); 1140 }, 3000); 1141 } 1142 1143 // Initialize first step 1144 showStep(currentStep); 1145 1146 // Set initial button state 1147 updateButtonState(); 1148 } 1149 }); 1150})(jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.