PageSourceSearch

https://ocf.ie/wp-content/plugins/breakdance/subplugins/breakdance…/awesome-form@1/js/form.js?bd_ver=2.6.1

js ocf.ie collected 2026-09-24 12:45:08 UTC 25,055 bytes, 855 lines download raw bytes

1/* global grecaptcha */
2(function () {
3  const loadingClass = "is-loading";
4
5  function formatError(error) {
6    if (Array.isArray(error) && error.length) {
7      return error.map((e) => e.message).join("<br />");
8    }
9
10    if (typeof error === "object") return error.message;
11  }
12
13  function interceptResponse(response) {
14    return response.json().then((body) => {
15      if (!response.ok) {
16        throw new Error(formatError(body.data));
17      }
18
19      return body;
20    });
21  }
22
23  function postData(url, data) {
24    const { makeAjaxRequest } = window.BreakdanceFrontend.utils;
25    const payload = {
26      method: "POST",
27      credentials: "same-origin", // Needed in order to store cookies.
28      body: data,
29    };
30
31    return makeAjaxRequest(url, payload).then(interceptResponse);
32  }
33
34  function createMessage(text, type = "success", onDismiss = null) {
35    const node = document.createElement("div");
36    node.innerHTML = text;
37    node.classList.add("breakdance-form-message");
38    node.classList.add("breakdance-form-message--" + type);
39
40    if (typeof onDismiss === "function") {
41      node.classList.add("breakdance-form-message-dismissable");
42
43      const dismissWrapper = document.createElement("div");
44      dismissWrapper.classList.add("breakdance-form-message-dismiss");
45
46      const dismissButton = document.createElement("button");
47      dismissButton.classList.add("breakdance-form-message-dismiss-button");
48      dismissButton.innerHTML = "&times;";
49      dismissButton.addEventListener("click", onDismiss);
50
51      dismissWrapper.appendChild(dismissButton);
52      node.appendChild(dismissWrapper);
53    }
54
55    return node;
56  }
57
58  function createErrorMessage(text) {
59    return createMessage(text, "error");
60  }
61
62  function resetForm(form) {
63    form.reset();
64
65    // Reset file fields
66    const fileFields = form.querySelectorAll(".breakdance-form-field--file");
67
68    fileFields.forEach((field) => {
69      const fileText = field.querySelector(".breakdance-form-file-upload__text");
70
71      if (fileText) {
72        const emptyText = field.querySelector(".breakdance-form-file-upload__input").dataset.i18nEmptyText;
73        fileText.textContent = emptyText;
74      }
75
76      const list = field.querySelector(".breakdance-form-files-list");
77
78      if (list) {
79        list.innerHTML = "";
80        list.classList.remove("is-files-visible");
81      }
82    });
83
84    // Reset conditional fields and steps
85    resetConditionalFields(form);
86    if (form.dataset.steps >= 1) resetSteps(form);
87  }
88
89  function safeEval(code, form, formData) {
90    const formValues = {
91      post_id: formData.get("post_id"),
92      form_id: formData.get("form_id"),
93    };
94
95    for (const [name, value] of formData.entries()) {
96      if (name.startsWith("fields[")) {
97        const parts = name.match(/\[/g).length;
98        const index = name.match(/\[(.*?)\]/)[1];
99
100        if (parts >= 2) {
101          if (!formValues[index]) {
102            formValues[index] = [];
103          }
104          formValues[index].push(value);
105        } else {
106          formValues[index] = value;
107        }
108      }
109    }
110
111    try {
112      eval(code);
113    } catch (e) {
114      console.warn("Could not run Custom JavaScript.", e);
115    }
116  }
117
118  function getRecaptchaToken(apiKey) {
119    const payload = { action: "breakdance_submit" };
120
121    return new Promise((resolve, reject) => {
122      grecaptcha.ready(() => {
123        try {
124          const token = grecaptcha.execute(apiKey, payload);
125          return resolve(token);
126        } catch (error) {
127          return reject(error);
128        }
129      });
130    });
131  }
132
133  function getOptions(form) {
134    const options = JSON.parse(form.getAttribute("data-options"));
135
136    const defaultOptions = {
137      name: "empty",
138      ajaxUrl: null,
139      successMessage: null,
140      errorMessage: null,
141      clearOnSuccess: true,
142      hideOnSuccess: false,
143      redirectUrl: null,
144      customJavaScript: {},
145      popupsOnSuccess: [],
146      popupsOnError: [],
147      recaptcha: {
148        key: null,
149        enabled: false,
150      },
151    };
152
153    if (!options) {
154      return defaultOptions;
155    }
156
157    return Object.assign({}, defaultOptions, options);
158  }
159
160  async function onSubmit(event) {
161    event.preventDefault();
162
163    const form = event.currentTarget;
164    const loading = form.classList.contains(loadingClass);
165    const options = getOptions(form);
166
167    if (loading) return;
168
169    const formData = new FormData(form);
170    formData.append("action", `breakdance_form_${options.slug}`);
171
172    beforeSubmit(form);
173
174    if (options.recaptcha.enabled) {
175      try {
176        const token = await getRecaptchaToken(options.recaptcha.key);
177        formData.append("recaptcha_token", token);
178      } catch (error) {
179        console.error(error);
180      }
181    }
182
183    postData(options.ajaxUrl, formData)
184      .then((response) => onRequestSuccess(response, form, formData))
185      .catch((error) => onRequestError(error, form, formData))
186      .finally(() => afterSubmit(form));
187  }
188
189  function beforeSubmit(form) {
190    form.classList.add(loadingClass);
191
192    const messages = form.parentElement.querySelectorAll(".breakdance-form-message");
193
194    messages.forEach((msg) => {
195      msg.remove();
196    })
197  }
198
199  function afterSubmit(form) {
200    form.classList.remove(loadingClass);
201  }
202
203  function onRequestError(error, form, formData) {
204    console.debug("[BREAKDANCE] Received a form error:", error.message);
205
206    const options = getOptions(form);
207    const message =
208      error.message || options.errorMessage || "An unexpected error occurred.";
209    const errorNode = createErrorMessage(message);
210    form.after(errorNode);
211
212    if (options.popupsOnError.length > 0) {
213      options.popupsOnError.forEach((errorPopup) => {
214        if (errorPopup.popup && errorPopup.action) {
215          BreakdancePopup.runAction(errorPopup.popup, errorPopup.action);
216        }
217      });
218    }
219
220    safeEval(options.customJavaScript?.js_on_error, form, formData);
221  }
222
223  function onRequestSuccess(response, form, formData) {
224    console.debug("[BREAKDANCE] Received form response:", response);
225
226    const options = getOptions(form);
227    const redirectOnSuccess = options.redirect && options.redirectUrl;
228
229    if (options.successMessage && !redirectOnSuccess) {
230      let messageNode = createMessage(options.successMessage);
231
232      if (options.hideOnSuccess) {
233        form.classList.add("breakdance-form--hidden");
234        messageNode = createMessage(options.successMessage, "success", () => {
235          form.classList.remove("breakdance-form--hidden");
236          messageNode.remove();
237        });
238      }
239
240      form.after(messageNode);
241    }
242
243    safeEval(options.customJavaScript?.js_on_success, form, formData);
244
245    if (options.clearOnSuccess) {
246      resetForm(form);
247    }
248
249    if (options.popupsOnSuccess.length > 0) {
250      options.popupsOnSuccess.forEach((successPopup) => {
251        if (successPopup.popup && successPopup.action) {
252          BreakdancePopup.runAction(successPopup.popup, successPopup.action);
253        }
254      });
255    }
256
257    if (redirectOnSuccess) {
258      location.href = options.redirectUrl;
259    }
260  }
261
262  function onInputUpdate(event) {
263    const parent = event.currentTarget.closest(".breakdance-form-field");
264    const activeClass = "breakdance-form-field--filled";
265
266    if (!parent) return;
267
268    if (event.currentTarget.value.length) {
269      parent.classList.add(activeClass);
270    } else {
271      parent.classList.remove(activeClass);
272    }
273  }
274
275  function removeFile(input, fileToRemove, fileIndex) {
276    const dt = new DataTransfer();
277
278    for (let file of input.files) {
279      if (file !== fileToRemove) {
280        dt.items.add(file);
281      }
282    }
283
284    input.files = dt.files;
285    onFileInputChange(input);
286  }
287
288  function maybeShowInlineFileValidation({ input, numOfFiles, maxNumOfFiles, i18nError }) {
289    const parentField = input.closest(".breakdance-form-field");
290    const oldMsg = parentField.querySelector(".breakdance-form-message");
291    // input.setCustomValidity("");
292
293    if (oldMsg) oldMsg.remove();
294
295    if (!maxNumOfFiles) return; // No max value set, unlimited files is allowed.
296    if (numOfFiles <= maxNumOfFiles) return;
297
298    const errorMessage = createErrorMessage(i18nError.replace("%n", `${maxNumOfFiles}`)); // Only %n file(s) allowed.
299    parentField.appendChild(errorMessage);
300    // input.setCustomValidity(i18nError.replace("%n", `${maxNumOfFiles}`));
301  }
302
303  function updateFilesList(input, files, listNode) {
304    const fragment = document.createDocumentFragment();
305
306    files.forEach((file, index) => {
307      const li = document.createElement("li");
308      const size = file.size < 1000000 ?
309        Math.floor(file.size / 1000) + " KB" :
310        Math.floor(file.size / 1000000) + " MB";
311
312      // Name
313      const nameNode = document.createElement("span");
314      nameNode.appendChild(document.createTextNode(file.name));
315      nameNode.classList.add("breakdance-form-files-list-item__name");
316
317      // Size
318      const sizeNode = document.createElement("span");
319      sizeNode.appendChild(document.createTextNode(size));
320      sizeNode.classList.add("breakdance-form-files-list-item__size");
321
322      // Delete Button
323      const deleteBtn = document.createElement("button");
324      deleteBtn.classList.add("breakdance-form-files-list-item__delete");
325      deleteBtn.setAttribute("aria-label", "Delete File");
326      deleteBtn.setAttribute("type", "button");
327
328      deleteBtn.addEventListener("click", () => {
329        removeFile(input, file, index);
330      })
331
332      li.appendChild(nameNode);
333      li.appendChild(sizeNode);
334      li.appendChild(deleteBtn);
335      li.classList.add("breakdance-form-files-list-item");
336
337      fragment.appendChild(li);
338    });
339
340    listNode.innerHTML = "";
341    listNode.appendChild(fragment);
342    listNode.classList.add("is-files-visible");
343  }
344
345  function onFileInputChange(input) {
346    const files = Array.from(input.files);
347    const names = files.map((f) => f.name);
348    const maxNumOfFiles = Number(input.dataset.maxFiles);
349    const i18nFilledText = input.dataset.i18nFilledText;
350    const i18nError = input.dataset.i18nErrorText;
351
352    maybeShowInlineFileValidation({
353      input,
354      numOfFiles: files.length,
355      maxNumOfFiles,
356      i18nError
357    });
358
359    // Inline Upload
360    const textNode = input.parentElement.querySelector(
361      ".breakdance-form-file-upload__text"
362    );
363
364    if (textNode) {
365      if (names.length > 1) {
366        textNode.innerHTML = i18nFilledText.replace("%n", `${names.length}`); // Text: %n files
367      } else {
368        textNode.innerHTML = names[0]; // Show file name if only one selected
369      }
370    }
371
372    // Drag and Drop
373    const listNode = input.closest(".breakdance-form-field--file").querySelector(".breakdance-form-files-list");
374    if (!listNode) return;
375
376    updateFilesList(input, files, listNode);
377  }
378
379  function bindEvents(form) {
380    bindFileEvents(form);
381
382    const inputs = Array.from(form.querySelectorAll("input, select, textarea"));
383
384    inputs.forEach((input) => {
385      input.addEventListener("input", onInputUpdate);
386    });
387
388    form.addEventListener("submit", onSubmit);
389  }
390
391  function bindDropzone(dropzone) {
392    const dragOver = (event) => {
393      event.preventDefault();
394      event.stopPropagation();
395      dropzone.classList.add("is-dragging");
396    };
397
398    const dragLeave = (event) => {
399      event.preventDefault();
400      event.stopPropagation();
401      dropzone.classList.remove("is-dragging");
402    };
403
404    const drop = (event) => {
405      event.preventDefault();
406      event.stopPropagation();
407
408      const fileInput = dropzone.querySelector("input[type='file']");
409
410      if (fileInput) {
411        fileInput.files = event.dataTransfer.files;
412        onFileInputChange(fileInput);
413      }
414    };
415
416    dropzone.addEventListener("dragover", dragOver);
417    dropzone.addEventListener("dragenter", dragOver);
418
419    dropzone.addEventListener("dragleave", dragLeave);
420    dropzone.addEventListener("dragend", dragLeave);
421    dropzone.addEventListener("drop", dragLeave);
422
423    dropzone.addEventListener("drop", drop);
424  }
425
426  function bindFileEvents(form) {
427    const dropZones = Array.from(
428      form.querySelectorAll(".breakdance-form-file-upload")
429    );
430    const inputs = Array.from(
431      form.querySelectorAll(".breakdance-form-file-upload__input")
432    );
433
434    inputs.forEach((input) =>
435      input.addEventListener("change", () => {
436        if (!input.files.length) return;
437        onFileInputChange(input);
438      })
439    );
440
441    dropZones.forEach((dropzone) => bindDropzone(dropzone));
442  }
443
444  function unbindEvents(form) {
445    const inputs = Array.from(form.querySelectorAll("input, select, textarea"));
446
447    inputs.forEach((input) => {
448      input.removeEventListener("input", onInputUpdate);
449    });
450
451    form.removeEventListener("submit", onSubmit);
452
453    form.bdMask?.destroy();
454  }
455
456  function scrollIntoViewIfNeeded(element) {
457    const rect = element.getBoundingClientRect();
458    const notVisible = rect.top < 0;
459
460    if (notVisible) {
461      element.scrollIntoView({ behavior: "smooth", block: "start" });
462    }
463  }
464
465  function initConditionalFields(form, attachEventListeners = false) {
466    const inputs = Array.from(
467      form.querySelectorAll("input, select, textarea, .form-input-html")
468    );
469
470    inputs.forEach((input) => {
471      const { conditionalFieldId, conditionalValue, conditionalOperand } =
472        input.dataset;
473      if (!conditionalFieldId) {
474        return;
475      }
476      const conditionalFields = form.querySelectorAll(
477        `[name="fields[${conditionalFieldId}]"],[name="fields[${conditionalFieldId}][]"]`
478      );
479      const wrapper = input.closest(".breakdance-form-field");
480
481      // Show or hide conditional field on change
482      conditionalFields.forEach((conditionalField) => {
483        const runValidation = () => {
484          showOrHideConditionalField(
485            form,
486            input,
487            wrapper,
488            conditionalFieldId,
489            conditionalValue,
490            conditionalOperand
491          );
492        };
493
494        if (attachEventListeners) {
495          const useChangeEvent = conditionalField.matches(
496            "input[type=radio], input[type=checkbox], input[type=file], select, input[type=number], input[type=date]"
497          );
498
499          if (useChangeEvent) {
500            conditionalField.addEventListener("input", () => runValidation());
501            conditionalField.addEventListener("change", () => runValidation());
502          } else {
503            conditionalField.addEventListener("keyup", () => runValidation());
504          }
505        }
506
507        // also run on init to set the initial state
508        runValidation();
509      });
510    });
511  }
512
513  function showOrHideConditionalField(
514    form,
515    inputElement,
516    fieldElement,
517    conditionalFieldId,
518    conditionalValue,
519    conditionalOperand
520  ) {
521    const fieldValue = getConditionalFieldValue(form, conditionalFieldId);
522
523    if (shouldShowField(fieldValue, conditionalOperand, conditionalValue)) {
524      inputElement.removeAttribute("disabled");
525      fieldElement.classList.remove("breakdance-form-field--condition-hidden");
526      return;
527    }
528
529    inputElement.disabled = true;
530    fieldElement.classList.add("breakdance-form-field--condition-hidden");
531  }
532
533  function getConditionalFieldValue(form, conditionalFieldId) {
534    const selector = `[name="fields[${conditionalFieldId}]"],[name="fields[${conditionalFieldId}][]"], [type='file'][name="fields[${conditionalFieldId}][]"]`;
535    const conditionalFields = Array.from(form.querySelectorAll(selector));
536
537    if (!conditionalFields.length) return null;
538
539
540    const values = conditionalFields.map((field) => {
541      const isCheckboxOrRadio = ["checkbox", "radio"].includes(field.type);
542
543      if (isCheckboxOrRadio && !field.checked) {
544        return null;
545      }
546
547      return field.value;
548    });
549
550    return values?.filter((v) => v !== null).join(",");
551  }
552
553  function resetConditionalFields(form) {
554    initConditionalFields(form, false);
555  }
556
557  function shouldShowField(aValue, operand, bValue) {
558    const aValueIsEmpty = aValue === "" || aValue === null;
559    const bValueIsEmpty = bValue === "" || bValue === null;
560    if (operand === "equals") {
561      return aValue == bValue;
562    } else if (operand === "not equals") {
563      return aValue != bValue;
564    } else if (operand === "is set") {
565      return aValue;
566    } else if (operand === "is not set") {
567      return !aValue;
568    } else if (operand === "is one of") {
569      const bValueArray = bValue.split(",");
570      return bValueArray.some((x) => x.trim() == aValue);
571    } else if (operand === "is none of") {
572      const bValueArray = bValue.split(",");
573      return !bValueArray.some((x) => x.trim() == aValue);
574    } else if (operand === "contains") {
575      if (typeof aValue === "string" && typeof bValue === "string") {
576        return aValue.toLowerCase().includes(bValue.toLowerCase());
577      }
578      return false;
579    } else if (operand === "does not contain") {
580      if (typeof aValue === "string" && typeof bValue === "string") {
581        return !aValue.toLowerCase().includes(bValue.toLowerCase());
582      }
583      return true;
584    } else if (operand === "is greater than") {
585      const aValueInteger = parseInt(aValue);
586      const bValueInteger = parseInt(bValue);
587      if (isNaN(aValueInteger) || isNaN(bValueInteger)) {
588        return false;
589      }
590      return aValueInteger > bValueInteger;
591    } else if (operand === "is less than") {
592      const aValueInteger = parseInt(aValue);
593      const bValueInteger = parseInt(bValue);
594      if (isNaN(aValueInteger) || isNaN(bValueInteger)) {
595        return false;
596      }
597      return aValueInteger < bValueInteger;
598    } else if (operand === "is before date") {
599      if (aValueIsEmpty || bValueIsEmpty) {
600        return false;
601      }
602      const aDateValue = new Date(aValue);
603      const bDateValue = new Date(bValue);
604      const aValueIsValidDate =
605        aDateValue instanceof Date && isFinite(aDateValue);
606      const bValueIsValidDate =
607        bDateValue instanceof Date && isFinite(bDateValue);
608      if (!aValueIsValidDate || !bValueIsValidDate) {
609        return false;
610      }
611      return aDateValue < bDateValue;
612    } else if (operand === "is after date") {
613      if (aValueIsEmpty || bValueIsEmpty) {
614        return false;
615      }
616      const aDateValue = new Date(aValue);
617      const bDateValue = new Date(bValue);
618      const aValueIsValidDate =
619        aDateValue instanceof Date && isFinite(aDateValue);
620      const bValueIsValidDate =
621        bDateValue instanceof Date && isFinite(bDateValue);
622      if (!aValueIsValidDate || !bValueIsValidDate) {
623        return false;
624      }
625      return aDateValue > bDateValue;
626    } else if (operand === "is before time") {
627      if (aValueIsEmpty || bValueIsEmpty) {
628        return false;
629      }
630      const todaysDate = new Date().toDateString();
631      const aDateValue = new Date(`${todaysDate} ${aValue}`);
632      const bDateValue = new Date(`${todaysDate} ${bValue}`);
633      const aValueIsValidDate =
634        aDateValue instanceof Date && isFinite(aDateValue);
635      const bValueIsValidDate =
636        bDateValue instanceof Date && isFinite(bDateValue);
637      if (!aValueIsValidDate || !bValueIsValidDate) {
638        return false;
639      }
640      return aDateValue < bDateValue;
641    } else if (operand === "is after time") {
642      if (aValueIsEmpty || bValueIsEmpty) {
643        return false;
644      }
645      const todaysDate = new Date().toDateString();
646      const aDateValue = new Date(`${todaysDate} ${aValue}`);
647      const bDateValue = new Date(`${todaysDate} ${bValue}`);
648      const aValueIsValidDate =
649        aDateValue instanceof Date && isFinite(aDateValue);
650      const bValueIsValidDate =
651        bDateValue instanceof Date && isFinite(bDateValue);
652      if (!aValueIsValidDate || !bValueIsValidDate) {
653        return false;
654      }
655      return aDateValue > bDateValue;
656    }
657    return true;
658  }
659
660  function destroy(selector) {
661    const form = document.querySelector(selector);
662
663    if (!form) {
664      console.warn("[BREAKDANCE] Could not find form to destroy:", selector);
665      return;
666    }
667
668    unbindEvents(form);
669  }
670
671  function initSteps(form, isBuilder = false) {
672    if (form.dataset.steps == 0) return;
673
674    const nextStepButtons = form.querySelectorAll(
675      ".breakdance-form-button__next-step"
676    );
677
678    nextStepButtons.forEach((button) =>
679      button.addEventListener("click", () => {
680        nextStep(form, isBuilder);
681      })
682    );
683
684    const previousStepButtons = form.querySelectorAll(
685      ".breakdance-form-button__previous-step"
686    );
687
688    previousStepButtons.forEach((button) =>
689      button.addEventListener("click", () => {
690        previousStep(form);
691      })
692    );
693
694    showOrHideSteps(form);
695  }
696
697  function validateStep(form, step) {
698    const inputs = Array.from(
699      form.querySelectorAll(
700        `[data-form-step='${step}'] input,[data-form-step='${step}'] select, [data-form-step='${step}'] textarea`
701      )
702    );
703    return inputs.every((input) => {
704      input.reportValidity();
705      return input.checkValidity();
706    });
707  }
708
709  function getFirstInputInStep(form, step) {
710    const firstFieldInStep = form.querySelector(
711      `.breakdance-form-field[data-form-step="${step}"]:not(.breakdance-form-field--hidden, .breakdance-form-field--html, .breakdance-form-field--condition-hidden)`
712    );
713
714    if (firstFieldInStep) {
715      return firstFieldInStep.querySelector(
716        `input, textarea, select`
717      );
718    }
719
720    return null;
721  }
722
723  function nextStep(form, isBuilder) {
724    const currentStep = parseInt(form.dataset.currentStep);
725    if (!validateStep(form, currentStep) && !isBuilder) {
726      return;
727    }
728    const nextStep = currentStep + 1;
729    setStep(form, nextStep);
730
731    // Set focus on first element in active step
732    const firstInput = getFirstInputInStep(form, nextStep);
733
734    if (firstInput) {
735      firstInput.focus();
736    }
737
738    scrollIntoViewIfNeeded(form);
739  }
740
741  function previousStep(form) {
742    const currentStep = parseInt(form.dataset.currentStep);
743    setStep(form, currentStep - 1);
744    scrollIntoViewIfNeeded(form);
745  }
746
747  function setStep(form, step) {
748    form.dataset.currentStep = step.toString();
749    showOrHideSteps(form);
750  }
751
752  function showOrHideSteps(form) {
753    const currentStep = parseInt(form.dataset.currentStep);
754    const totalSteps = parseInt(form.dataset.steps);
755    const fields = form.querySelectorAll(
756      ".breakdance-form-field:not(.breakdance-form-footer)"
757    );
758
759    fields.forEach((field) => {
760      const formStep = parseInt(field.dataset.formStep);
761      if (formStep === currentStep) {
762        field.classList.remove("hidden-step");
763      } else {
764        field.classList.add("hidden-step");
765      }
766    });
767
768    const submitButton = form.querySelector(".breakdance-form-button__submit");
769    const nextStepButton = form.querySelector(
770      `.breakdance-form-field[data-form-step="${currentStep}"] .breakdance-form-button__next-step`
771    );
772    const previousStepButton = form.querySelector(
773      `.breakdance-form-field[data-form-step="${currentStep}"] .breakdance-form-button__previous-step`
774    );
775    if (currentStep === totalSteps) {
776      submitButton.classList.remove("hidden");
777      nextStepButton.classList.add("hidden");
778    } else {
779      submitButton.classList.add("hidden");
780      nextStepButton.classList.remove("hidden");
781    }
782    if (currentStep > 1) {
783      previousStepButton.classList.remove("hidden");
784    } else {
785      previousStepButton.classList.add("hidden");
786    }
787
788    const steps = form.querySelectorAll(".breakdance-form-stepper__step");
789
790    if (steps) {
791      steps.forEach((step) => {
792        const stepperStep = parseInt(step.dataset.stepperStep);
793        if (stepperStep <= currentStep) {
794          step.classList.add("is-active");
795        } else {
796          step.classList.remove("is-active");
797        }
798      });
799      const currentStepper = form.querySelector(
800        `[data-stepper-step="${currentStep}"]`
801      );
802      if (currentStepper) {
803        currentStepper.classList.add("is-active");
804      }
805    }
806  }
807
808  function resetSteps(form) {
809    setStep(form, 1);
810  }
811
812  function initMask(form, selector) {
813    if (typeof Maska === "undefined") return;
814
815    const tokens = {
816      'z': {
817        pattern: /[a-z|A-Z]/,
818        multiple: true
819      },
820      'Z': {
821        pattern: /[A-Z]/,
822        multiple: true,
823        transform: (chr) => chr.toLocaleUpperCase(),
824      },
825      0: { pattern: /[0-9]/, optional: true },
826      9: { pattern: /[0-9]/, repeated: true },
827    }
828
829    form.bdMask = new Maska.MaskInput(`${selector} [data-maska]`, {
830      tokens
831    });
832  }
833
834  function init(selector) {
835    const form = document.querySelector(selector);
836
837    if (!form) {
838      console.warn("[BREAKDANCE] Could not find form:", selector);
839      return;
840    }
841
842    bindEvents(form);
843    initConditionalFields(form, true);
844    initSteps(form);
845    initMask(form, selector);
846  }
847
848  window.breakdanceForm = {
849    init,
850    destroy,
851    initMask,
852    initConditionalFields,
853    initSteps,
854  };
855})();

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.