PageSourceSearch

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

js mercijeannot.com collected 2026-10-04 19:26:49 UTC 22,686 bytes, 680 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        if (!field.checkValidity()) {
197          isStepValid = false;
198          field.classList.add("msf-input-error");
199        } else {
200          field.classList.remove("msf-input-error");
201        }
202      }
203      const drilldownMenus = section.querySelectorAll("div.drilldown-menu");
204      for (const menu of drilldownMenus) {
205        const name = menu.getAttribute("data-name");
206        if (!name) continue;
207        const label = section.querySelector(
208          `label[for="${name}"], .label-${name}`
209        );
210        if (!isFieldRequired(label)) continue;
211        const hasValue =
212          menu.querySelector("input.drilldown-generated") !== null;
213        const menuToggle = menu.querySelector(".menu-toggle");
214        if (menuToggle) {
215          if (!hasValue) {
216            isStepValid = false;
217            menuToggle.classList.add("msf-input-error");
218          } else {
219            menuToggle.classList.remove("msf-input-error");
220          }
221        }
222      }
223      const galleryUploader = section.querySelector("#media-uploader");
224      if (
225        galleryUploader &&
226        galleryUploader.getAttribute("data-required") === "required"
227      ) {
228        const hasImages =
229          galleryUploader.querySelector(".dz-image-preview") !== null;
230        const existingError = galleryUploader.nextElementSibling;
231        if (!hasImages) {
232          isStepValid = false;
233          if (
234            !existingError ||
235            !existingError.classList.contains("msf-gallery-error-message")
236          ) {
237            const errorDiv = document.querySelector(
238              ".msf-gallery-error-message"
239            );
240            if (errorDiv) {
241              errorDiv.style.display = "block";
242            }
243          }
244        } else {
245          if (
246            existingError &&
247            existingError.classList.contains("msf-gallery-error-message")
248          ) {
249            existingError.remove();
250          }
251        }
252      }
253      if (typeof tinymce !== "undefined") {
254        const textareas = section.querySelectorAll("textarea");
255        for (const textarea of textareas) {
256          const editorId = textarea.id;
257          if (!editorId) continue;
258          const editor = tinymce.get(editorId);
259          if (!editor) continue;
260          const label = section.querySelector(`label[for="${editorId}"]`);
261          if (!isFieldRequired(label)) continue;
262          const content = editor.getContent({ format: "text" }).trim();
263          const editorContainer = editor.getContainer();
264          if (content === "") {
265            isStepValid = false;
266            if (editorContainer)
267              editorContainer.classList.add("msf-input-error");
268          } else {
269            if (editorContainer)
270              editorContainer.classList.remove("msf-input-error");
271          }
272        }
273      }
274      const fileInputs = section.querySelectorAll('input[type="file"]');
275      for (const fileInput of fileInputs) {
276        const inputId = fileInput.id;
277        if (!inputId) continue;
278        const label = section.querySelector(`label[for="${inputId}"]`);
279        if (!isFieldRequired(label)) continue;
280        const fieldContainer = fileInput.closest(
281          ".form-field-container-type-file"
282        );
283        const uploadWrapper = fileInput.closest(".uploadButton");
284        if (uploadWrapper && fieldContainer) {
285          const uploadButtonLabel = uploadWrapper.querySelector(
286            ".uploadButton-button"
287          );
288          const hasExistingPreview =
289            fieldContainer.querySelector(".listeo-uploaded-file-preview") !==
290            null;
291          const hasNewFile = fileInput.files.length > 0;
292          const isFieldValid = hasNewFile || hasExistingPreview;
293          if (uploadButtonLabel) {
294            if (!isFieldValid) {
295              isStepValid = false;
296              uploadButtonLabel.classList.add("msf-input-error");
297            } else {
298              uploadButtonLabel.classList.remove("msf-input-error");
299            }
300          }
301        }
302      }
303    }
304    return isStepValid;
305  }
306
307  function updateStepStatus(stepIndex) {
308    const isStepValid = validateStep(stepIndex);
309    const stepElement = progressSteps[stepIndex];
310    if (isStepValid) {
311      stepElement.classList.remove("step-warning");
312    } else {
313      stepElement.classList.add("step-warning");
314    }
315  }
316
317  function updateBookingDependencies() {
318    const bookingInput = form.querySelector(
319      '.booking input[name*="booking_status"], .booking select[name*="booking_status"]'
320    );
321    if (!bookingInput) return;
322
323    let isEnabled = false;
324
325    if (bookingInput.type === "checkbox" || bookingInput.type === "radio") {
326      isEnabled = bookingInput.checked || bookingInput.value === "enabled";
327    } else if (bookingInput.tagName === "SELECT") {
328      isEnabled = bookingInput.value === "enabled";
329    }
330
331    const dependentSelectors = [
332      ".basic_prices",
333      ".slots",
334      ".availability_calendar",
335    ];
336    dependentSelectors.forEach((selector) => {
337      const section = form.querySelector(selector);
338      if (!section) return;
339
340      if (isEnabled) {
341        section.classList.add("manual-visible");
342        section.classList.add("active");
343        section.style.display = "";
344      } else {
345        section.classList.remove("manual-visible");
346        section.classList.remove("active");
347        section.style.display = "none";
348      }
349    });
350  }
351
352  // Set up initial state and watch for changes
353  updateBookingDependencies();
354  const bookingInput = form.querySelector(
355    '.booking input[name*="booking_status"]'
356  );
357  if (bookingInput) {
358    bookingInput.addEventListener("change", updateBookingDependencies);
359  }
360
361  // Modify the updateFormUI function to handle all sections properly
362  function updateFormUI() {
363    if (typeof window.listeoCurrentStep !== "undefined") {
364      window.listeoCurrentStep = currentStep;
365    }
366
367    // Hide all regular sections first
368    allSections.forEach((section) => {
369      if (!section.classList.contains("manual-visible")) {
370        section.classList.remove("active");
371        section.style.display = "none";
372      }
373    });
374
375    // Hide all custom fields sections
376    const customTermFeatures = document.querySelectorAll(".custom-term-features");
377    customTermFeatures.forEach(function (element) {
378      if (!element.classList.contains("manual-visible")) {
379        element.classList.remove("active");
380        element.style.display = "none";
381      }
382    });
383
384    // Clean up empty custom sections before showing the current step
385    const emptyCustomSections = document.querySelectorAll(".custom-term-features");
386    emptyCustomSections.forEach(function (element) {
387      const content = element.querySelector('.custom-term-features-content');
388      if (content && content.children.length === 0) {
389        console.log("Removing empty custom section from updateFormUI:", element.id);
390        element.remove();
391      }
392    });
393
394    if (steps[currentStep]) {
395      const activeSections = steps[currentStep];
396      let needsResize = false;
397
398      const isBookingEnabled = form
399        .querySelector(".booking")
400        ?.classList.contains("switcher-on");
401
402      // Show all sections that belong to the current step
403      activeSections.forEach((section) => {
404        // Check if the section still exists in the DOM (might have been removed as empty)
405        if (!document.contains(section)) {
406          return;
407        }
408
409        const sectionClass = [...section.classList].find((cls) =>
410          [".basic_prices", ".slots", ".availability_calendar"].includes(
411            "." + cls
412          )
413        );
414
415        const isDependentSection = !!sectionClass;
416
417        if (isDependentSection && !isBookingEnabled) {
418          section.classList.remove("active");
419          section.style.display = "none";
420          return;
421        }
422
423        section.classList.add("active");
424        section.style.display = "";
425
426        if (
427          section.querySelector("#submit_map") ||
428          section.querySelector(".fullCalendar, .fc")
429        ) {
430          needsResize = true;
431        }
432      });
433
434      if (needsResize) {
435        setTimeout(() => {
436          window.dispatchEvent(new Event("resize"));
437        }, 50);
438      }
439    }
440
441    // FIXED: Check custom sections based on current step configuration
442    const customSections = document.querySelectorAll(".custom-term-features");
443    customSections.forEach(function (element) {
444      var sectionId = element.id;
445      var shouldBeVisible = false;
446
447      // Check if this section's selector is in current step configuration
448      if (stepConfiguration[currentStep] && stepConfiguration[currentStep].selectors) {
449        // Check for both ID selector and class selector
450        var idSelector = "#" + sectionId;
451        var classSelector = ".custom-term-features";
452        
453        shouldBeVisible = 
454          stepConfiguration[currentStep].selectors.indexOf(idSelector) !== -1 ||
455          stepConfiguration[currentStep].selectors.indexOf(classSelector) !== -1;
456      }
457
458      // Only show sections that have content
459      const content = element.querySelector('.custom-term-features-content');
460      const hasContent = content && content.children.length > 0;
461
462      if (shouldBeVisible && hasContent) {
463        element.classList.add("active");
464        element.style.display = "";
465        console.log("Showing custom section:", sectionId, "in step:", currentStep);
466      } else {
467        console.log("Hiding custom section:", sectionId, "in step:", currentStep, "hasContent:", hasContent);
468      }
469    });
470
471    progressSteps.forEach((step, index) => {
472      const icon = step.querySelector(".progress-step-icon");
473      step.classList.remove("active", "completed");
474      if (
475        index <= highestStepReached &&
476        !step.classList.contains("step-warning")
477      ) {
478        step.classList.add("completed");
479      }
480      if (index === currentStep) {
481        step.classList.add("active");
482        step.classList.remove("completed");
483      }
484      if (
485        step.classList.contains("completed") &&
486        !step.classList.contains("active")
487      ) {
488        icon.textContent = "";
489      } else {
490        icon.textContent = index + 1;
491      }
492    });
493    prevButton.style.display = currentStep === 0 ? "none" : "inline-block";
494    nextButton.style.display =
495      currentStep === steps.length - 1 ? "none" : "inline-block";
496    submitButton.style.display =
497      currentStep === steps.length - 1 ? "inline-block" : "none";
498}
499
500// Add this function after the step creation logic (around line 120)
501// Add a function to refresh steps when custom sections are added
502window.refreshStepsConfiguration = function() {
503  // Get the updated step configuration from the hidden input
504  const stepsInput = document.getElementById('listeo_form_steps_json');
505  if (!stepsInput) return;
506  
507  try {
508    const textarea = document.createElement("textarea");
509    textarea.innerHTML = stepsInput.value;
510    const updatedStepConfiguration = JSON.parse(textarea.value);
511    
512    // Rebuild the steps array
513    steps.length = 0; // Clear existing steps
514    
515    updatedStepConfiguration.forEach((groupInfo) => {
516      const groupSections = [];
517      groupInfo.selectors.forEach((selector) => {
518        // Use querySelectorAll on the entire document for custom sections
519        const foundSections = document.querySelectorAll(selector);
520        foundSections.forEach((section) => {
521          // Check if it's in allSections or if it's a custom section
522          if (allSections.includes(section) || section.classList.contains('custom-term-features')) {
523            groupSections.push(section);
524          }
525        });
526      });
527      
528      if (groupSections.length > 0) {
529        steps.push(groupSections);
530      }
531    });
532    
533    // Update the step configuration reference
534    stepConfiguration = updatedStepConfiguration;
535    
536    console.log('Steps configuration refreshed:', steps);
537    
538    // DON'T force update the UI here - let it happen naturally
539    // This was causing the section to be removed before content was added
540    // updateFormUI();
541    
542  } catch (e) {
543    console.error('Error refreshing step configuration:', e);
544  }
545};
546
547// Also add debugging to the step navigation to see what's happening
548nextButton.addEventListener("click", () => {
549  updateStepStatus(currentStep);
550  if (currentStep < steps.length - 1) {
551    currentStep++;
552    highestStepReached = Math.max(highestStepReached, currentStep);
553    console.log("Moving to step:", currentStep, "with sections:", steps[currentStep]);
554    updateFormUI();
555    smoothScrollToTop();
556  }
557});
558
559prevButton.addEventListener("click", () => {
560  updateStepStatus(currentStep);
561  if (currentStep > 0) {
562    currentStep--;
563    console.log("Moving to step:", currentStep, "with sections:", steps[currentStep]);
564    updateFormUI();
565    smoothScrollToTop();
566  }
567});
568
569progressSteps.forEach((step, index) => {
570  step.addEventListener("click", () => {
571    if (index === currentStep) return;
572    if (index > currentStep) {
573      for (let i = currentStep; i < index; i++) {
574        updateStepStatus(i);
575      }
576    } else {
577      updateStepStatus(currentStep);
578    }
579    currentStep = index;
580    highestStepReached = Math.max(highestStepReached, currentStep);
581    console.log("Clicking to step:", currentStep, "with sections:", steps[currentStep]);
582    updateFormUI();
583    smoothScrollToTop();
584  });
585});
586  submitButton.addEventListener("click", (e) => {
587    const isPreview =
588      e.target.name === "submit_listing" ||
589      e.target.value.toLowerCase().includes("preview") ||
590      e.target.textContent.toLowerCase().includes("preview");
591
592    // Always validate steps
593    for (let i = 0; i < steps.length; i++) {
594      updateStepStatus(i);
595    }
596
597    let firstInvalidStep = -1;
598    progressSteps.forEach((step, index) => {
599      if (step.classList.contains("step-warning") && firstInvalidStep === -1) {
600        firstInvalidStep = index;
601      }
602    });
603
604    if (firstInvalidStep !== -1) {
605      e.preventDefault(); // prevent even preview from submitting
606      currentStep = firstInvalidStep;
607      highestStepReached = Math.max(highestStepReached, currentStep);
608      updateFormUI();
609      smoothScrollToTop();
610
611      const firstInvalidField = steps[firstInvalidStep][0].querySelector(
612        ".msf-input-error, .msf-gallery-error-message"
613      );
614      if (firstInvalidField) firstInvalidField.focus({ preventScroll: true });
615      // alert(
616      //   "Please correct the errors on the highlighted step before continuing."
617      // );
618      return;
619    }
620
621    // If valid
622    if (isPreview) {
623       showLoader();
624       allSections.forEach((section) => {
625         section.classList.add("active", "msf-preview-hidden");
626       });
627
628       progressContainer.style.display = "none";
629       navContainer.style.display = "none";
630        
631      console.log("Preview button clicked - showing all sections");
632      return;
633    }
634
635    e.preventDefault();
636    console.log("Form is valid. Submitting...");
637    form.submit();
638  });
639
640
641  const previewButton = form.querySelector(
642    'button[name="preview_listing"], input[name="preview_listing"]'
643  );
644  if (previewButton) {
645    previewButton.addEventListener("click", (e) => {
646      // Don't prevent default - let preview work normally
647
648      // Temporarily show all sections for preview
649      allSections.forEach((section) => {
650        section.classList.add("active");
651        section.style.display = "block";
652      });
653
654      // Let the preview submission proceed without validation
655      console.log("Preview button clicked - showing all sections");
656    });
657  }
658
659  // Alternative: If preview button is identified differently, try this selector
660  const previewButtons = form.querySelectorAll(
661    'button[value*="preview"], input[value*="preview"], [name*="preview"]'
662  );
663  previewButtons.forEach((button) => {
664    button.addEventListener("click", (e) => {
665      allSections.forEach((section) => {
666        section.classList.add("active");
667        section.style.display = "block";
668      });
669      console.log("Preview button clicked - showing all sections");
670    });
671  });
672
673  const urlParams = new URLSearchParams(window.location.search);
674  if (urlParams.get("action") === "edit") {
675    console.log("Edit mode detected. All steps will be marked as completed.");
676    highestStepReached = steps.length - 1;
677  }
678  updateBookingDependencies();
679  updateFormUI();
680});

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.