PageSourceSearch

https://italianhomerestaurant.com/wp-content/plugins/listeo-core/assets/js/submit-listing.steps.js?ver=2.0.26

js italianhomerestaurant.com collected 2026-10-08 05:48:19 UTC 24,980 bytes, 730 lines download raw bytes

1document.addEventListener("DOMContentLoaded", function () {
2
3  function showLoader() {
4    const loader = document.createElement("div");
5    loader.className = "msf-loader-overlay";
6
7    loader.innerHTML = `
8    <svg class="msf-loader-spinner" viewBox="0 0 50 50">
9      <circle cx="25" cy="25" r="20"></circle>
10    </svg>
11  `;
12
13    document.body.appendChild(loader);
14  }
15
16
17
18  const form = document.querySelector(".listing-manager-form");
19  if (!form) return;
20
21  const allSections = Array.from(form.querySelectorAll(".add-listing-section"));
22
23const submitPageDiv = document.querySelector(".submit-page");
24
25if (!submitPageDiv || !submitPageDiv.classList.contains("multi-step-form")) {
26  return; // or throw new Error('Required class not found');
27}
28
29  
30  const submitButton = form.querySelector('button[name="submit_listing"]');
31
32  if (!submitButton || allSections.length <= 1) {
33    return;
34  }
35
36  // --- Start of Your Custom Configuration ---
37  let stepConfiguration = [];
38
39  try {
40    const rawStepsEncoded = document.getElementById(
41      "listeo_form_steps_json"
42    )?.value;
43
44    if (rawStepsEncoded) {
45      const textarea = document.createElement("textarea");
46      textarea.innerHTML = rawStepsEncoded;
47      const rawSteps = textarea.value;
48
49      stepConfiguration = JSON.parse(rawSteps);
50    }
51  } catch (e) {
52    console.error("Failed to parse step configuration", e);
53  }
54
55  // if stepConfiguration is empty or not an array, disable multi-step
56  if (!Array.isArray(stepConfiguration) || stepConfiguration.length === 0) {
57    return;
58  }
59
60  allSections.forEach((section) => {
61    section.removeAttribute("style");
62  });
63  // const stepConfiguration = [
64  //   {
65  //     title: "Listing Essentials",
66  //     selectors: [".basic_info", ".location", ".gallery", ".details"],
67  //   },
68  //   {
69  //     title: "Offerings & Schedule",
70  //     selectors: [
71  //       ".opening_hours",
72  //       ".menu",
73  //       ".booking",
74  //       ".slots",
75  //       ".basic_prices",
76  //       ".availability_calendar",
77  //     ],
78  //   },
79  //   {
80  //     title: "Final Details",
81  //     selectors: [".faq", ".my_listings_section"],
82  //   },
83  // ];
84  // --- End of Your Custom Configuration ---
85
86  let currentStep = 0;
87  let highestStepReached = 0;
88  const steps = [];
89
90  // Reworked Step Creation Logic
91  stepConfiguration.forEach((groupInfo) => {
92    const groupSections = [];
93    groupInfo.selectors.forEach((selector) => {
94      const foundSections = form.querySelectorAll(selector);
95      foundSections.forEach((section) => {
96        // Ensure we only add sections that actually exist in the form
97        if (allSections.includes(section)) {
98          groupSections.push(section);
99        }
100      });
101    });
102    
103    // Also check for custom fields sections using both ID and class selectors
104    groupInfo.selectors.forEach((selector) => {
105      if (selector.startsWith('#custom-fields-') || selector === '.custom-term-features') {
106        // For ID selectors, look for specific custom section
107        if (selector.startsWith('#custom-fields-')) {
108          const customSection = document.querySelector(selector);
109          if (customSection && !groupSections.includes(customSection)) {
110            groupSections.push(customSection);
111          }
112        } 
113        // For class selector, get all custom sections
114        else if (selector === '.custom-term-features') {
115          const customSections = document.querySelectorAll(selector);
116          customSections.forEach((customSection) => {
117            if (!groupSections.includes(customSection)) {
118              groupSections.push(customSection);
119            }
120          });
121        }
122      }
123    });
124    
125    if (groupSections.length > 0) {
126      steps.push(groupSections);
127    }
128  });
129
130  const progressContainer = document.createElement("div");
131  progressContainer.className = "form-progress-container";
132  const navContainer = document.createElement("div");
133  navContainer.className = "form-navigation";
134  const prevButton = document.createElement("button");
135  prevButton.type = "button";
136  prevButton.className = "btn-prev button";
137  prevButton.textContent =  listeo_core.prev;
138  const nextButton = document.createElement("button");
139  nextButton.type = "button";
140  nextButton.className = "btn-next button";
141  nextButton.textContent =  listeo_core.next;
142  form.insertBefore(progressContainer, form.firstChild);
143  const lastSection = allSections[allSections.length - 1];
144  lastSection.parentNode.insertBefore(navContainer, lastSection.nextSibling);
145  navContainer.appendChild(prevButton);
146  navContainer.appendChild(nextButton);
147  navContainer.appendChild(submitButton);
148  if (submitButton.parentElement.nodeName === "P") {
149    submitButton.parentElement.style.display = "block";
150    submitButton.parentElement.style.margin = "0";
151    submitButton.parentElement.classList.add("form-navigation-submit");
152  }
153
154  // Reworked Progress Bar Creation
155  steps.forEach((stepSections, index) => {
156    const stepElement = document.createElement("div");
157    stepElement.className = "form-progress-step";
158    const icon = document.createElement("div");
159    icon.className = "progress-step-icon";
160    const titleElement = document.createElement("div");
161    titleElement.className = "progress-step-title";
162
163    // Use the title from the new configuration
164    titleElement.textContent =
165      stepConfiguration[index].title || `Step ${index + 1}`;
166
167    stepElement.appendChild(icon);
168    stepElement.appendChild(titleElement);
169    progressContainer.appendChild(stepElement);
170  });
171  const progressSteps = progressContainer.querySelectorAll(
172    ".form-progress-step"
173  );
174
175  function smoothScrollToTop() {
176    window.scrollTo({ top: 0, behavior: "smooth" });
177  }
178
179  function isFieldRequired(label) {
180    if (!label) return false;
181    const iTags = label.querySelectorAll("i");
182    for (const iTag of iTags) {
183      if (iTag.textContent.trim() === "*") return true;
184    }
185    return false;
186  }
187
188  function validateStep(stepIndex) {
189    let isStepValid = true;
190    const sectionsToValidate = steps[stepIndex];
191    for (const section of sectionsToValidate) {
192      const standardFields = section.querySelectorAll(
193        'input[required]:not([type="hidden"]), select[required], textarea[required]'
194      );
195      for (const field of standardFields) {
196        // Special handling for Select2 fields
197        if (field.tagName === 'SELECT' && field.classList.contains('select2-hidden-accessible')) {
198          const isValid = field.value && field.value !== '-1' && field.value !== '';
199          const select2Container = field.nextElementSibling;
200
201          if (!isValid) {
202            isStepValid = false;
203            field.classList.add("msf-input-error");
204            if (select2Container && select2Container.classList.contains('select2-container')) {
205              select2Container.classList.add("msf-input-error");
206            }
207          } else {
208            field.classList.remove("msf-input-error");
209            if (select2Container && select2Container.classList.contains('select2-container')) {
210              select2Container.classList.remove("msf-input-error");
211            }
212          }
213        } else {
214          // Standard HTML5 validation for non-Select2 fields
215          if (!field.checkValidity()) {
216            isStepValid = false;
217            field.classList.add("msf-input-error");
218          } else {
219            field.classList.remove("msf-input-error");
220          }
221        }
222      }
223      const drilldownMenus = section.querySelectorAll("div.drilldown-menu");
224      for (const menu of drilldownMenus) {
225        const name = menu.getAttribute("data-name");
226        if (!name) continue;
227        const label = section.querySelector(
228          `label[for="${name}"], .label-${name}`
229        );
230        if (!isFieldRequired(label)) continue;
231        const hasValue =
232          menu.querySelector("input.drilldown-generated") !== null;
233        const menuToggle = menu.querySelector(".menu-toggle");
234        if (menuToggle) {
235          if (!hasValue) {
236            isStepValid = false;
237            menuToggle.classList.add("msf-input-error");
238          } else {
239            menuToggle.classList.remove("msf-input-error");
240          }
241        }
242      }
243      const galleryUploader = section.querySelector("#media-uploader");
244      if (
245        galleryUploader &&
246        galleryUploader.getAttribute("data-required") === "required"
247      ) {
248        const hasImages =
249          galleryUploader.querySelector(".dz-image-preview") !== null;
250        const existingError = galleryUploader.nextElementSibling;
251        if (!hasImages) {
252          isStepValid = false;
253          if (
254            !existingError ||
255            !existingError.classList.contains("msf-gallery-error-message")
256          ) {
257            const errorDiv = document.querySelector(
258              ".msf-gallery-error-message"
259            );
260            if (errorDiv) {
261              errorDiv.style.display = "block";
262            }
263          }
264        } else {
265          if (
266            existingError &&
267            existingError.classList.contains("msf-gallery-error-message")
268          ) {
269            existingError.remove();
270          }
271        }
272      }
273      if (typeof tinymce !== "undefined") {
274        const textareas = section.querySelectorAll("textarea");
275        for (const textarea of textareas) {
276          const editorId = textarea.id;
277          if (!editorId) continue;
278          const editor = tinymce.get(editorId);
279          if (!editor) continue;
280          const label = section.querySelector(`label[for="${editorId}"]`);
281          if (!isFieldRequired(label)) continue;
282          const content = editor.getContent({ format: "text" }).trim();
283          const editorContainer = editor.getContainer();
284          if (content === "") {
285            isStepValid = false;
286            if (editorContainer)
287              editorContainer.classList.add("msf-input-error");
288          } else {
289            if (editorContainer)
290              editorContainer.classList.remove("msf-input-error");
291          }
292        }
293      }
294      const fileInputs = section.querySelectorAll('input[type="file"]');
295      for (const fileInput of fileInputs) {
296        const inputId = fileInput.id;
297        if (!inputId) continue;
298        const label = section.querySelector(`label[for="${inputId}"]`);
299        if (!isFieldRequired(label)) continue;
300        const fieldContainer = fileInput.closest(
301          ".form-field-container-type-file"
302        );
303        const uploadWrapper = fileInput.closest(".uploadButton");
304        if (uploadWrapper && fieldContainer) {
305          const uploadButtonLabel = uploadWrapper.querySelector(
306            ".uploadButton-button"
307          );
308          const hasExistingPreview =
309            fieldContainer.querySelector(".listeo-uploaded-file-preview") !==
310            null;
311          const hasNewFile = fileInput.files.length > 0;
312          const isFieldValid = hasNewFile || hasExistingPreview;
313          if (uploadButtonLabel) {
314            if (!isFieldValid) {
315              isStepValid = false;
316              uploadButtonLabel.classList.add("msf-input-error");
317            } else {
318              uploadButtonLabel.classList.remove("msf-input-error");
319            }
320          }
321        }
322      }
323    }
324    return isStepValid;
325  }
326
327  function updateStepStatus(stepIndex) {
328    const isStepValid = validateStep(stepIndex);
329    const stepElement = progressSteps[stepIndex];
330    if (isStepValid) {
331      stepElement.classList.remove("step-warning");
332    } else {
333      stepElement.classList.add("step-warning");
334    }
335  }
336
337  // Unified visibility controller
338  function shouldSectionBeVisible(section) {
339    // Check if section belongs to current step
340    const isInCurrentStep = steps[currentStep] && steps[currentStep].includes(section);
341    
342    // Check if section is booking-dependent
343    const isBookingDependent = section.classList.contains('availability_calendar') ||
344                              section.classList.contains('slots') ||
345                              section.classList.contains('basic_prices');
346    
347    // Check if booking is enabled
348    const bookingInput = form.querySelector(
349      '.booking input[name*="booking_status"], .booking select[name*="booking_status"]'
350    );
351    let isBookingEnabled = false;
352    
353    if (bookingInput) {
354      if (bookingInput.type === "checkbox" || bookingInput.type === "radio") {
355        isBookingEnabled = bookingInput.checked || bookingInput.value === "enabled";
356      } else if (bookingInput.tagName === "SELECT") {
357        isBookingEnabled = bookingInput.value === "enabled";
358      }
359    }
360
361    // Apply unified visibility logic
362    if (isBookingDependent) {
363      return isInCurrentStep && isBookingEnabled;
364    }
365    return isInCurrentStep;
366  }
367
368  function updateBookingDependencies() {
369    const bookingInput = form.querySelector(
370      '.booking input[name*="booking_status"], .booking select[name*="booking_status"]'
371    );
372    if (!bookingInput) return;
373
374    let isEnabled = false;
375
376    if (bookingInput.type === "checkbox" || bookingInput.type === "radio") {
377      isEnabled = bookingInput.checked || bookingInput.value === "enabled";
378    } else if (bookingInput.tagName === "SELECT") {
379      isEnabled = bookingInput.value === "enabled";
380    }
381
382    // Update the booking switcher visual state
383    const bookingSection = form.querySelector('.booking');
384    if (bookingSection) {
385      if (isEnabled) {
386        bookingSection.classList.add("switcher-on");
387      } else {
388        bookingSection.classList.remove("switcher-on");
389      }
390    }
391
392    // Trigger form UI update to apply unified visibility logic
393    updateFormUI();
394  }
395
396  // Set up initial state and watch for changes
397  updateBookingDependencies();
398  const bookingInput = form.querySelector(
399    '.booking input[name*="booking_status"]'
400  );
401  if (bookingInput) {
402    bookingInput.addEventListener("change", updateBookingDependencies);
403  }
404
405  // Modify the updateFormUI function to use unified visibility logic
406  function updateFormUI() {
407    if (typeof window.listeoCurrentStep !== "undefined") {
408      window.listeoCurrentStep = currentStep;
409    }
410
411    // Hide all sections first
412    allSections.forEach((section) => {
413      section.classList.remove("active");
414      section.style.display = "none";
415    });
416
417    // Hide all custom fields sections
418    const customTermFeatures = document.querySelectorAll(".custom-term-features");
419    customTermFeatures.forEach(function (element) {
420      element.classList.remove("active");
421      element.style.display = "none";
422    });
423
424    // Clean up empty custom sections before showing the current step
425    const emptyCustomSections = document.querySelectorAll(".custom-term-features");
426    emptyCustomSections.forEach(function (element) {
427      const content = element.querySelector('.custom-term-features-content');
428      if (content && content.children.length === 0) {
429        console.log("Removing empty custom section from updateFormUI:", element.id);
430        element.remove();
431      }
432    });
433
434    if (steps[currentStep]) {
435      const activeSections = steps[currentStep];
436      let needsResize = false;
437
438      // Show sections using unified visibility logic
439      activeSections.forEach((section) => {
440        // Check if the section still exists in the DOM (might have been removed as empty)
441        if (!document.contains(section)) {
442          return;
443        }
444
445        // Use unified visibility controller
446        if (shouldSectionBeVisible(section)) {
447          section.classList.add("active");
448          section.style.display = "";
449
450          if (
451            section.querySelector("#submit_map") ||
452            section.querySelector(".fullCalendar, .fc")
453          ) {
454            needsResize = true;
455          }
456        }
457      });
458
459      if (needsResize) {
460        setTimeout(() => {
461          window.dispatchEvent(new Event("resize"));
462        }, 50);
463      }
464    }
465
466    // FIXED: Check custom sections based on current step configuration
467    const customSections = document.querySelectorAll(".custom-term-features");
468    customSections.forEach(function (element) {
469      var sectionId = element.id;
470      var shouldBeVisible = false;
471
472      // Check if this section's selector is in current step configuration
473      if (stepConfiguration[currentStep] && stepConfiguration[currentStep].selectors) {
474        // Check for both ID selector and class selector
475        var idSelector = "#" + sectionId;
476        var classSelector = ".custom-term-features";
477        
478        shouldBeVisible = 
479          stepConfiguration[currentStep].selectors.indexOf(idSelector) !== -1 ||
480          stepConfiguration[currentStep].selectors.indexOf(classSelector) !== -1;
481      }
482
483      // Only show sections that have content
484      const content = element.querySelector('.custom-term-features-content');
485      const hasContent = content && content.children.length > 0;
486
487      if (shouldBeVisible && hasContent) {
488        element.classList.add("active");
489        element.style.display = "";
490        console.log("Showing custom section:", sectionId, "in step:", currentStep);
491      } else {
492        console.log("Hiding custom section:", sectionId, "in step:", currentStep, "hasContent:", hasContent);
493      }
494    });
495
496    progressSteps.forEach((step, index) => {
497      const icon = step.querySelector(".progress-step-icon");
498      step.classList.remove("active", "completed");
499      if (
500        index <= highestStepReached &&
501        !step.classList.contains("step-warning")
502      ) {
503        step.classList.add("completed");
504      }
505      if (index === currentStep) {
506        step.classList.add("active");
507        step.classList.remove("completed");
508      }
509      if (
510        step.classList.contains("completed") &&
511        !step.classList.contains("active")
512      ) {
513        icon.textContent = "";
514      } else {
515        icon.textContent = index + 1;
516      }
517    });
518    prevButton.style.display = currentStep === 0 ? "none" : "inline-block";
519    nextButton.style.display =
520      currentStep === steps.length - 1 ? "none" : "inline-block";
521    submitButton.style.display =
522      currentStep === steps.length - 1 ? "inline-block" : "none";
523}
524
525// Add this function after the step creation logic (around line 120)
526// Add a function to refresh steps when custom sections are added
527window.refreshStepsConfiguration = function() {
528  // Get the updated step configuration from the hidden input
529  const stepsInput = document.getElementById('listeo_form_steps_json');
530  if (!stepsInput) return;
531  
532  try {
533    const textarea = document.createElement("textarea");
534    textarea.innerHTML = stepsInput.value;
535    const updatedStepConfiguration = JSON.parse(textarea.value);
536    
537    // Rebuild the steps array
538    steps.length = 0; // Clear existing steps
539    
540    updatedStepConfiguration.forEach((groupInfo) => {
541      const groupSections = [];
542      groupInfo.selectors.forEach((selector) => {
543        // Use querySelectorAll on the entire document for custom sections
544        const foundSections = document.querySelectorAll(selector);
545        foundSections.forEach((section) => {
546          // Check if it's in allSections or if it's a custom section
547          if (allSections.includes(section) || section.classList.contains('custom-term-features')) {
548            groupSections.push(section);
549          }
550        });
551      });
552      
553      if (groupSections.length > 0) {
554        steps.push(groupSections);
555      }
556    });
557    
558    // Update the step configuration reference
559    stepConfiguration = updatedStepConfiguration;
560    
561    console.log('Steps configuration refreshed:', steps);
562    
563    // DON'T force update the UI here - let it happen naturally
564    // This was causing the section to be removed before content was added
565    // updateFormUI();
566    
567  } catch (e) {
568    console.error('Error refreshing step configuration:', e);
569  }
570};
571
572// Also add debugging to the step navigation to see what's happening
573nextButton.addEventListener("click", () => {
574  updateStepStatus(currentStep);
575  if (currentStep < steps.length - 1) {
576    currentStep++;
577    highestStepReached = Math.max(highestStepReached, currentStep);
578    console.log("Moving to step:", currentStep, "with sections:", steps[currentStep]);
579    updateFormUI();
580    smoothScrollToTop();
581  }
582});
583
584prevButton.addEventListener("click", () => {
585  updateStepStatus(currentStep);
586  if (currentStep > 0) {
587    currentStep--;
588    console.log("Moving to step:", currentStep, "with sections:", steps[currentStep]);
589    updateFormUI();
590    smoothScrollToTop();
591  }
592});
593
594progressSteps.forEach((step, index) => {
595  step.addEventListener("click", () => {
596    if (index === currentStep) return;
597    if (index > currentStep) {
598      for (let i = currentStep; i < index; i++) {
599        updateStepStatus(i);
600      }
601    } else {
602      updateStepStatus(currentStep);
603    }
604    currentStep = index;
605    highestStepReached = Math.max(highestStepReached, currentStep);
606    console.log("Clicking to step:", currentStep, "with sections:", steps[currentStep]);
607    updateFormUI();
608    smoothScrollToTop();
609  });
610});
611  submitButton.addEventListener("click", (e) => {
612    const isPreview =
613      e.target.name === "submit_listing" ||
614      e.target.value.toLowerCase().includes("preview") ||
615      e.target.textContent.toLowerCase().includes("preview");
616
617    // Always validate steps
618    for (let i = 0; i < steps.length; i++) {
619      updateStepStatus(i);
620    }
621
622    let firstInvalidStep = -1;
623    progressSteps.forEach((step, index) => {
624      if (step.classList.contains("step-warning") && firstInvalidStep === -1) {
625        firstInvalidStep = index;
626      }
627    });
628
629    if (firstInvalidStep !== -1) {
630      e.preventDefault(); // prevent even preview from submitting
631      currentStep = firstInvalidStep;
632      highestStepReached = Math.max(highestStepReached, currentStep);
633      updateFormUI();
634      smoothScrollToTop();
635
636      const firstInvalidField = steps[firstInvalidStep][0].querySelector(
637        ".msf-input-error, .msf-gallery-error-message"
638      );
639      if (firstInvalidField) firstInvalidField.focus({ preventScroll: true });
640      // alert(
641      //   "Please correct the errors on the highlighted step before continuing."
642      // );
643      return;
644    }
645
646    // If valid
647    if (isPreview) {
648       showLoader();
649       allSections.forEach((section) => {
650         section.classList.add("active", "msf-preview-hidden");
651       });
652
653       progressContainer.style.display = "none";
654       navContainer.style.display = "none";
655        
656      console.log("Preview button clicked - showing all sections");
657      return;
658    }
659
660    e.preventDefault();
661    console.log("Form is valid. Submitting...");
662    form.submit();
663  });
664
665
666  const previewButton = form.querySelector(
667    'button[name="preview_listing"], input[name="preview_listing"]'
668  );
669  if (previewButton) {
670    previewButton.addEventListener("click", (e) => {
671      // Don't prevent default - let preview work normally
672
673      // Temporarily show all sections for preview
674      allSections.forEach((section) => {
675        section.classList.add("active");
676        section.style.display = "block";
677      });
678
679      // Let the preview submission proceed without validation
680      console.log("Preview button clicked - showing all sections");
681    });
682  }
683
684  // Alternative: If preview button is identified differently, try this selector
685  const previewButtons = form.querySelectorAll(
686    'button[value*="preview"], input[value*="preview"], [name*="preview"]'
687  );
688  previewButtons.forEach((button) => {
689    button.addEventListener("click", (e) => {
690      allSections.forEach((section) => {
691        section.classList.add("active");
692        section.style.display = "block";
693      });
694      console.log("Preview button clicked - showing all sections");
695    });
696  });
697
698  const urlParams = new URLSearchParams(window.location.search);
699  if (urlParams.get("action") === "edit") {
700    console.log("Edit mode detected. All steps will be marked as completed.");
701    highestStepReached = steps.length - 1;
702  }
703  updateBookingDependencies();
704  updateFormUI();
705
706  // Remove error styling when user selects a value in Select2 fields
707  document.addEventListener('change', function(e) {
708    const target = e.target;
709
710    // Check if the changed element is a required select with Select2
711    if (target.tagName === 'SELECT' &&
712        target.hasAttribute('required') &&
713        target.classList.contains('select2-hidden-accessible')) {
714
715      const value = target.value;
716      const container = target.nextElementSibling;
717
718      // If a valid value is selected, remove error styling
719      if (value && value !== '-1' && value !== '') {
720        target.classList.remove('msf-input-error');
721        if (container && container.classList.contains('select2-container')) {
722          container.classList.remove('msf-input-error');
723        }
724      }
725    }
726  });
727
728  // Expose updateFormUI globally for coordination with frontend.js
729  window.listeoUpdateFormUI = updateFormUI;
730});

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.