PageSourceSearch

https://farmlib.org/App_Plugins/UmbracoForms/Assets/themes/default/umbracoforms.js

js farmlib.org collected 2026-10-02 11:07:33 UTC 38,106 bytes, 860 lines download raw bytes

1(function () {
2
3
4    // polyfill for matches and closest
5    if (!Element.prototype.matches) Element.prototype.matches = Element.prototype.msMatchesSelector;
6    if (!Element.prototype.closest) Element.prototype.closest = function (selector) {
7        var el = this;
8        while (el) {
9            if (el.matches(selector)) {
10                return el;
11            }
12            el = el.parentElement;
13        }
14    };
15
16
17
18    //execute init() on document ready
19    document.addEventListener("DOMContentLoaded", listen);
20
21    function listen() {
22
23        // Inline script setting umbracoFormsCollection was removed in 8.11/9.3, and replaced by config elements.
24        // So if we have them, use it, otherwise fall-back to legacy method.
25        var configElements = document.getElementsByClassName("umbraco-forms-form-config");
26        if (configElements.length > 0) {
27            var umbracoFormsCollectionFromConfig = [];
28            for (var i = 0; i < configElements.length; i++) {
29                var configElement = configElements[i];
30                var form = {
31                    formId: configElement.dataset.id,
32                    pageButtonConditions: JSON.parse(configElement.dataset.serializedPageButtonConditions),
33                    fieldSetConditions: JSON.parse(configElement.dataset.serializedFieldsetConditions),
34                    fieldConditions: JSON.parse(configElement.dataset.serializedFieldConditions),
35                    triggerConditionsCheckOn: configElement.dataset.triggerConditionsCheckOn,
36                    disableValidationDependencyCheck: configElement.dataset.disableValidationDependencyCheck === "true",
37                    recordValues: JSON.parse(configElement.dataset.serializedFieldsNotDisplayed),
38                    formElementHtmlIdPrefix: configElement.dataset.formElementHtmlIdPrefix,
39                };
40                umbracoFormsCollectionFromConfig.push(form);
41            }
42            initCollection(umbracoFormsCollectionFromConfig, true);  // Set the 'jsonParsed' flag, indicating that we already have a JSON structure, which we don't have with the legacy, inline script method.
43        } else {
44            if (typeof umbracoFormsCollection === "undefined") {
45                //this will occur if this js file is loaded before the inline scripts, in which case
46                //we'll listen for the inline scripts to execute a custom event.
47                document.addEventListener("umbracoFormLoaded", init);
48            }
49            else {
50                initCollection(umbracoFormsCollection);
51            }
52        }
53
54        // Scrolling to the submitted form was also moved from an inline script in 8.11/9.3.
55        var submittedFormElement = document.getElementById("umbraco-forms-form-submitted");
56        if (submittedFormElement) {
57            var formClientId = submittedFormElement.dataset.formClientId;
58            scrollToSubmittedForm(formClientId);
59        }
60    }
61
62    function initCollection(formsCollection, jsonParsed) {
63        configureUmbracoFormsValidation();
64
65        for (var i = 0; i < formsCollection.length; i++) {
66            init({ form: formsCollection[i] }, jsonParsed);
67        }
68    }
69
70    function init(e, jsonParsed) {
71
72        var formItem = jsonParsed ? e.form : JSON.parse(decodeURI(e.form));
73
74        var forms = document.querySelectorAll('.umbraco-forms-form');
75
76        for (var i = 0; i < forms.length; i++) {
77            var form = forms[i];
78
79            if (!formItem.disableValidationDependencyCheck) {
80                dependencyCheck(form);
81            }
82
83            var page = form.querySelector('.umbraco-forms-page');
84            var conditions = new UmbracoFormsConditions(page,
85                formItem.pageButtonConditions,
86                formItem.fieldSetConditions,
87                formItem.fieldConditions,
88                formItem.triggerConditionsCheckOn && formItem.triggerConditionsCheckOn.length > 0 ? formItem.triggerConditionsCheckOn : "change",
89                formItem.recordValues,
90                formItem.formElementHtmlIdPrefix);
91            conditions.watch();
92
93            applyFormAccessibility(form);
94        }
95    }
96
97    function scrollToSubmittedForm(formClientId) {
98        document.onreadystatechange = function () {
99
100            if (document.readyState == "complete") {
101
102                // We have various cases where we want to scroll to the form rendered on the page.
103                // - When we have a server-side validation error, we should scroll to the first error element (which class 'field-validation-error').
104                // - When the form has been submitted, we should scroll to the displayed message (which will have class 'umbraco-forms-submitmessage' or 'umbraco-forms-submitmessage-html').
105                // - When navigating back and forth on a multi-page form, we should scroll to the start of the form page (which will have an id of #umbraco_form_[form client id]).
106                var validationsMessageElements = document.querySelectorAll(".field-validation-error");
107                var messageOnSubmitElement = document.querySelectorAll(".umbraco-forms-submitmessage");
108                var messageOnSubmitHtmlElement = document.querySelectorAll(".umbraco-forms-submitmessage-html");
109                var submittedFormElement = document.getElementById("umbraco_form_" + formClientId);
110                if (validationsMessageElements.length > 0) {
111                    scrollToFirstFieldWithValidationError(validationsMessageElements);
112                } else if (messageOnSubmitElement.length > 0) {
113                    scrollElementIntoView(messageOnSubmitElement[0].parentElement);
114                } else if (messageOnSubmitHtmlElement.length > 0) {
115                    scrollElementIntoView(messageOnSubmitHtmlElement[0]);
116                } else if (submittedFormElement) {
117                    scrollElementIntoView(submittedFormElement);
118                }
119            }
120        }
121
122        function scrollToFirstFieldWithValidationError(validationsMessageElements) {
123            var scrollY = 0;
124            for (var i = 0; i < validationsMessageElements.length; i++) {
125                var node = getFormFieldElement(validationsMessageElements[i]);
126                var offset = node.getBoundingClientRect().top;
127                if (0 < offset && (offset < scrollY || scrollY === 0)) {
128                    scrollY = offset;
129                }
130            }
131
132            if (scrollY > 0) {
133                window.scrollTo(0, scrollY);
134            }
135        }
136
137        function scrollElementIntoView(element) {
138            element.scrollIntoView({
139                behavior: "auto",
140                block: "start",
141            });
142        }
143
144        function getFormFieldElement(node) {
145            var runner = node;
146            while (runner.tagName !== "BODY") {
147                if (runner.classList.contains("umbraco-forms-field")) {
148                    return runner;
149                }
150
151                runner = runner.parentNode;
152            }
153
154            return node;
155        }
156    }
157
158    /** Configures the jquery validation for Umbraco forms */
159    function configureUmbracoFormsValidation() {
160
161        if (window.aspnetValidation !== undefined) {
162            // Asp-net validation setup:
163
164            var validationService = new aspnetValidation.ValidationService();
165
166            // TODO: equivilant to this:
167            /*
168            $.validator.setDefaults({
169                ignore: ":hidden"
170            });
171            */
172
173            var required = function (value, element) {
174                // Handle single and multiple checkboxes:
175                if (element.type.toLowerCase() === "checkbox" || element.type.toLowerCase() === "radio") {
176                    var allCheckboxesOfThisName = element.form.querySelectorAll("input[name='" + element.name + "']");
177                    for (var i = 0; i < allCheckboxesOfThisName.length; i++) {
178                        if (allCheckboxesOfThisName[i].checked === true) {
179                            return true;
180                        }
181                    }
182                    return false;
183                }
184                return Boolean(value);
185            }
186            validationService.addProvider("requiredcb", required);
187            validationService.addProvider("required", required);// this will go instead of the build-in required.
188
189
190            var umbracoforms_regex = function (value, element, params) {
191                if (!value || !params.pattern) {
192                    return true;
193                }
194
195                var r = new RegExp(params.pattern);
196                return r.test(value);
197            }
198            validationService.addProvider("umbracoforms_regex", umbracoforms_regex);
199
200            var wrapProviderWithIgnorerBehaviour = function (provider) {
201                return function (value, element, params) {
202
203                    // If field is hidden we ignorer the validation.
204                    if (element.offsetParent === null) {
205                        return true;
206                    }
207
208                    return provider(value, element, params);
209                }
210            }
211
212            // we can only incept with default validator if we do it after bootstrapping but before window load event triggers validationservice.
213            window.addEventListener('load', function () {
214
215                // Wrap all providers with ignorer hidden fields logic:
216                for (var key in validationService.providers) {
217                    validationService.providers[key] = wrapProviderWithIgnorerBehaviour(validationService.providers[key]);
218                }
219            });
220
221            // bootstrap validation service.
222            validationService.bootstrap();
223
224            // make validation service avaialble globally for custom client-side code.
225            window.umbracoFormsValidationService = validationService;
226
227            // Without jquery validation, the previous page submit button click isn't sent with it's name,
228            // so can't be used server-side to determine whether to go forward or back.
229            // Hence we'll use an alternate method, setting a hidden field that's also used in the check.
230            var handlePreviousClicked = function () {
231                this.form.elements["PreviousClicked"].value = "clicked";
232            };
233
234            // - previous buttons are indicated by a data attribute of data-umb="prev-forms-form", introduced in 8.12/9.4.
235            var previousButtonElements = document.querySelectorAll("[data-umb='prev-forms-form']");
236
237            // - prior to that classes were used to identify them, so for compatibility with custom themes, we'll check for those too
238            //   if we haven't found any via the data attribute.
239            if (previousButtonElements.length === 0) {
240                previousButtonElements = document.getElementsByClassName("prev cancel");
241            }
242
243            for (var i = 0; i < previousButtonElements.length; i++) {
244                previousButtonElements[i].form.elements["PreviousClicked"].value = "";
245                previousButtonElements[i].addEventListener('click', handlePreviousClicked, false);
246            }
247
248        } else if (typeof jQuery === "function" && $.validator && $.validator.unobtrusive) {
249            //Jquery validation setup
250
251            $.validator.setDefaults({
252                ignore: ":hidden"
253            });
254
255            $.validator.unobtrusive.adapters.addBool("requiredcb", "required");
256
257
258            $.validator.addMethod("umbracoforms_regex", function (value, element) {
259
260                var regex = $(element).attr("data-regex");
261                var val = $(element).val();
262                if (val.length === 0) {
263                    return true;
264                }
265                return val.match(regex);
266            });
267
268            $.validator.unobtrusive.adapters.addBool("regex", "umbracoforms_regex");
269
270            var submitInputs = document.querySelectorAll(".umbraco-forms-form input[type=submit]:not(.cancel)");
271            for (let i = 0; i < submitInputs.length; i++) {
272                var input = submitInputs[i];
273                input.addEventListener("click", function (evt) {
274                    evt.preventDefault();
275                    var frm = $(this).closest("form");
276                    resetValidationMessages(frm[0]);
277                    frm.validate();
278                    if (frm.valid()) {
279                        frm.submit();
280                        this.setAttribute("disabled", "disabled");
281
282                    }
283                }.bind(input));
284            }
285        }
286
287    }
288
289    /**
290     * method to determine if Umbraco Forms can run and has the required dependencies loaded
291     * @param {Form Element} formEl the element of the form
292     */
293    function dependencyCheck(formEl) {
294        // Only perform check if the global 'Umbraco.Sys' is null/undefined.
295        // If present means we are in backoffice & that this is being rendered as a macro preview and we do not need to perform this check here.
296        // Similarly we need a check for if running in a rich text editor.
297        var isBackOffice = function () {
298            return typeof Umbraco !== "undefined" && typeof Umbraco.Sys !== "undefined";
299        };
300        var isBackOfficeRte = function () {
301            return document.body.id === "tinymce";
302        };
303
304        if (isBackOffice() || isBackOfficeRte()) {
305            return;
306        }
307
308        var errorElement = document.createElement("div");
309        errorElement.className = "umbraco-forms missing-library";
310        errorElement.style.color = "#fff";
311        errorElement.style.backgroundColor = "#9d261d";
312        errorElement.style.padding = "15px";
313        errorElement.style.margin = "10px 0";
314        var errorMessage = "";
315
316        //Ensure umbracoForm is not null
317        if (formEl) {
318
319            //Check to see if the message for the form has been inserted already
320            var checkForExistinhgErr = formEl.getElementsByClassName('umbraco-forms missing-library');
321            if (checkForExistinhgErr.length > 0) {
322                return;
323            }
324
325            var hasValidationFramework = false;
326
327            if (window.jQuery && $ && $.validator !== undefined) {
328                hasValidationFramework = true;
329            } else if (window.aspnetValidation !== undefined) {
330                hasValidationFramework = true;
331            }
332
333            if (hasValidationFramework === false) {
334                errorMessage = errorMessage + "Umbraco Forms requires a validation framework to run, please read documentation for posible options.";
335            }
336
337            if (errorMessage !== "") {
338                errorElement.innerHTML = errorMessage + '<br/> <a href="https://docs.umbraco.com/umbraco-forms/developer/prepping-frontend" target="_blank" style="text-decoration:underline; color:#fff;">See Umbraco Forms Documentation</a>';
339
340                formEl.insertBefore(errorElement, formEl.childNodes[0]);
341            }
342        }
343    }
344
345    /**
346     * Applies form accessibility improvements.
347     * @param {Element} formEl the element of the form.
348     */
349    function applyFormAccessibility(formEl) {
350        setFocusToFirstElementOnValidationError(formEl);
351    }
352
353    /**
354     * Monitors for validation errors and when found sets the focus to the first field with an error.
355     * @param {Element} formEl the element of the form.
356     */
357    function setFocusToFirstElementOnValidationError(formEl) {
358        if ("MutationObserver" in window === false) {
359            return;
360        }
361
362        // To implement this, we are relying on on monitoring for validation message elements, which only fires when there are changes.
363        // So if you have two errors, and fix the first one, it wouldn't then highlight the second one on re-submitting the form.
364        // Unless we reset the validation messages on submit, so they get changed back on errors.
365        if (window.aspnetValidation !== undefined) {
366            var form = formEl.getElementsByTagName('form')[0];
367            var handleResetValidationMessages = function () {
368                resetValidationMessages(form);
369            };
370            form.addEventListener('submit', handleResetValidationMessages, false);
371        } else {
372            // For jquery.validate, we need to hook this in as part of the submit handler coded in configureUmbracoFormsValidation();
373        }
374
375        // Watch for changes to the validation error messages in the DOM tree using a MutationObserver.
376        // See: https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
377        var observer = new MutationObserver(function (mutationRecords) {
378            for (var i = 0; i < mutationRecords.length; i++) {
379                var mutationRecord = mutationRecords[i];
380                if (mutationRecord.target.className === 'field-validation-error') {
381                    setFocusOnFormField(mutationRecord.target);
382                    break;
383                }
384            }
385        });
386
387        observer.observe(formEl, {
388            attributes: true,
389            attributeFilter: ['class'],
390            childList: false,
391            characterData: false,
392            subtree: true
393        });
394    }
395
396    /**
397     * Resets the validation messages for a form.
398     * @param {Element} formEl the element of the form.
399     */
400    function resetValidationMessages(formEl) {
401        var validationErrorMessageElements = formEl.getElementsByClassName('field-validation-error');
402        for (var i = 0; i < validationErrorMessageElements.length; i++) {
403            validationErrorMessageElements[i].className = 'field-validation-valid';
404        }
405    }
406
407    /**
408     * Sets the focus to the form field input element associated with the provided validation message element.
409     * @param {Element} validationErrorEl the element of the validation error.
410     */
411    function setFocusOnFormField(validationErrorEl) {
412        var formFieldElement = validationErrorEl.previousElementSibling;
413        while (formFieldElement) {
414            if (formFieldElement.tagName.toLowerCase() === 'input' ||
415                formFieldElement.tagName.toLowerCase() === 'textarea' ||
416                formFieldElement.tagName.toLowerCase() === 'select') {
417                formFieldElement.focus();
418                break;
419            }
420
421            if (formFieldElement.classList.contains("radiobuttonlist") ||
422                formFieldElement.classList.contains("checkboxlist")) {
423                for (var i = 0; i < formFieldElement.children.length; i++) {
424                    var formFieldChildElement = formFieldElement.children[i];
425                    if (formFieldChildElement.tagName.toLowerCase() === 'input') {
426                        formFieldChildElement.focus();
427                        break;
428                    }
429                }
430
431                break;
432            }
433
434            formFieldElement = formFieldElement.previousElementSibling;
435        }
436    }
437
438    /**
439     * Class to handle Umbraco Forms conditional statements
440     * @param {any} form a reference to the form
441     * @param {any} pageButtonConditions a reference to the page button conditions
442     * @param {any} fieldsetConditions a reference to the fieldset conditions
443     * @param {any} fieldConditions a reference to the field conditions
444     * @param {any} triggerConditionsCheckOn the client-side event on which condition checks should be triggered
445     * @param {any} values the form values
446     * @param {any} formElementHtmlIdPrefix the HTML ID prefix used when rendering the form
447     * @return {Object} reference to the created class
448     */
449    function UmbracoFormsConditions(form, pageButtonConditions, fieldsetConditions, fieldConditions, triggerConditionsCheckOn, values, formElementHtmlIdPrefix) {
450
451        //our conditions "class" - must always be newed to work as it uses a form instance to operate on
452        //load all the information from the dom and serverside info and then the class will take care of the rest
453
454        var self = {};
455        self.form = form;
456        self.pageButtonConditions = pageButtonConditions;
457        self.fieldsetConditions = fieldsetConditions;
458        self.fieldConditions = fieldConditions;
459        self.triggerConditionsCheckOn = triggerConditionsCheckOn;
460        self.values = values;
461        self.formElementHtmlIdPrefix = formElementHtmlIdPrefix;
462        self.dataTypes = {};
463
464        //Iterates through all the form elements found on the page to update the registered value
465        function populateFieldValues(page, formValues, dataTypes) {
466
467            var selectFields = page.querySelectorAll("select");
468            for (let i = 0; i < selectFields.length; i++) {
469                const field = selectFields[i];
470                formValues[field.getAttribute("id")] = field.value;
471                dataTypes[field.getAttribute("id")] = "select";
472            }
473
474            var textareaFields = page.querySelectorAll("textarea");
475            for (let i = 0; i < textareaFields.length; i++) {
476                const field = textareaFields[i];
477                formValues[field.getAttribute("id")] = field.value;
478                dataTypes[field.getAttribute("id")] = "textarea";
479            }
480
481            var getKeyForRadioOrCheckBoxListElement = function (field) {
482                const idParts = field.getAttribute("id").split("_");
483                return idParts.slice(0, idParts.length - 1).join("_");
484            }
485
486            // clear out all saved checkbox values to we can safely append
487            var checkboxFields = page.querySelectorAll("input[type=checkbox]");
488            for (let i = 0; i < checkboxFields.length; i++) {
489                const field = checkboxFields[i];
490
491                // Need to handle both a single checkbox, and a checkbox list. We can distinguish by comparing the field's
492                // id and name attributes. If they are the same, accounting for a custom prefix, then we have a single checkbox.
493                if (field.getAttribute("id") !== self.formElementHtmlIdPrefix + field.getAttribute("name")) {
494                    // Handle checkbox list.
495                    // For a checkbox in a list we need to remove the suffix from the Id (which will be '_0', '_1' etc.).
496                    const checkboxKey = getKeyForRadioOrCheckBoxListElement(field);
497                    formValues[checkboxKey] = null;
498                    dataTypes[checkboxKey] = "checkbox";
499                } else {
500                    // Handle single checkbox.
501                    formValues[field.getAttribute("id")] = null;
502                    dataTypes[field.getAttribute("id")] = "checkbox";
503                }
504            }
505
506            var inputFields = page.querySelectorAll("input");
507            for (let i = 0; i < inputFields.length; i++) {
508                const field = inputFields[i];
509
510                if (field.getAttribute('type') === "text" ||
511                    field.getAttribute('type') === "number" ||
512                    field.getAttribute('type') === "email" ||
513                    field.getAttribute('type') === "url" ||
514                    field.getAttribute('type') === "tel" ||
515                    field.getAttribute('type') === "time" ||
516                    field.getAttribute('type') === "date" ||
517                    field.getAttribute('type') === "datetime-local" ||
518                    field.getAttribute("type") === "hidden") {
519                    formValues[field.getAttribute("id")] = field.value;
520                    dataTypes[field.getAttribute("id")] = "text";
521                }
522
523                if (field.getAttribute('type') === "radio") {
524                    if (field.matches(':checked')) {
525                        // For a radio button in a list we need to remove the suffix from the Id (which will be '_0', '_1' etc.).
526                        const radioKey = getKeyForRadioOrCheckBoxListElement(field);
527                        formValues[radioKey] = field.value;
528                        dataTypes[radioKey] = "radio";
529                    }
530                }
531
532                if (field.getAttribute("type") === "checkbox") {
533                    // Again, need to handle both a single checkbox, and a checkbox list.
534                    if (field.getAttribute("id") !== self.formElementHtmlIdPrefix + field.getAttribute("name")) {
535                        // Handle checkbox list.
536                        // For a checkbox in a list we need to remove the suffix from the Id (which will be '_0', '_1' etc.).
537                        const checkboxKey = getKeyForRadioOrCheckBoxListElement(field);
538                        if (field.matches(":checked")) {
539                            if (formValues[checkboxKey] === null) {
540                                formValues[checkboxKey] = field.value;
541                            }
542                            else {
543                                formValues[checkboxKey] += ";;" + field.value;
544                            }
545                        }
546                    }
547                    else {
548                        // Handle single checkbox.
549                        formValues[field.getAttribute("id")] = (field.matches(":checked") ? "true" : "false");
550                    }
551                }
552            }
553        }
554
555        /* Public api */
556
557        self.operators = {
558            Is: function (value, expected, dataType) {
559                if ((value || "") === expected) {
560                    return true;
561                }
562                if (value == null) {
563                    return (expected == value);
564                }
565
566                if (dataType === "checkbox") {
567                    if (expected.toUpperCase() === "TRUE" || expected.toUpperCase() === "ON") {
568                        expected = "true"
569                    } else if (expected.toUpperCase() === "FALSE" || expected.toUpperCase() === "OFF") {
570                        expected = "false"
571                    }
572                }
573
574                var values = value.split(';;');
575                var matchingExpected = values.filter(
576                    function (o) {
577                        return o === expected;
578                    });
579                return matchingExpected.length > 0;
580            },
581            IsNot: function (value, unexpected, dataType) {
582                if (value == null) {
583                    return (unexpected != value);
584                }
585                var values = value.split(';;');
586                var matchingUnexpected = values.filter(
587                    function (o) {
588                        return o === unexpected;
589                    });
590
591                if (dataType === "checkbox") {
592                    if (unexpected.toUpperCase() === "TRUE" || unexpected.toUpperCase() === "ON") {
593                        unexpected = "true"
594                    } else if (unexpected.toUpperCase() === "FALSE" || unexpected.toUpperCase() === "OFF") {
595                        unexpected = "false"
596                    }
597                }
598                return (value || "") !== unexpected && matchingUnexpected.length === 0;
599            },
600            GreaterThen: function (value, limit) {
601                if (isNaN(value) || isNaN(limit)) {
602                    return value > limit;
603                }
604
605                return parseInt(value) > parseInt(limit);
606            },
607            LessThen: function (value, limit) {
608                if (isNaN(value) || isNaN(limit)) {
609                    return value < limit;
610                }
611
612                return parseInt(value) < parseInt(limit);
613            },
614            StartsWith: function (value, criteria) {
615                return value && value.startsWith(criteria);
616            },
617            EndsWith: function (value, criteria) {
618                return value && value.endsWith(criteria);
619            },
620            Contains: function (value, criteria) {
621                return value && value.indexOf(criteria) > -1;
622            },
623            ContainsIgnoreCase: function (value, criteria) {
624                return value && criteria && value.toUpperCase().indexOf(criteria.toUpperCase()) >= 0;
625            },
626            StartsWithIgnoreCase: function (value, criteria) {
627                return value && criteria && value.toUpperCase().startsWith(criteria.toUpperCase());
628            },
629            EndsWithIgnoreCase: function (value, criteria) {
630                return value && criteria && value.toUpperCase().endsWith(criteria.toUpperCase());
631            },
632            NotContains: function (value, criteria) {
633                return !value || (criteria && value.indexOf(criteria) < 0);
634            },
635            NotStartsWith: function (value, criteria) {
636                return !value || (criteria && !value.startsWith(criteria));
637            },
638            NotEndsWith: function (value, criteria) {
639                return !value || (criteria && !value.endsWith(criteria));
640            },
641            NotContainsIgnoreCase: function (value, criteria) {
642                return !value || (criteria && value.toUpperCase().indexOf(criteria.toUpperCase()) < 0);
643            },
644            NotStartsWithIgnoreCase: function (value, criteria) {
645                return !value || (criteria && !value.toUpperCase().startsWith(criteria.toUpperCase()));
646            },
647            NotEndsWithIgnoreCase: function (value, criteria) {
648                return !value || (criteria && !value.toUpperCase().endsWith(criteria.toUpperCase()));
649            },
650        };
651
652        self.watch = function () {
653            // This is a special case for pikaday
654            // The only way around to pickup the value, for now, is to 
655            // subscribe to blur events 
656            var datepickerfields = self.form.querySelectorAll('.datepickerfield');
657            for (let i = 0; i < datepickerfields.length; i++) {
658                const field = datepickerfields[i];
659                field.addEventListener('blur', function () {
660                    if (this.value === "") {
661                        // Here comes the hack
662                        // Force the hidden datepicker field the datepicker field
663                        var id = this.getAttribute("id");
664                        var hiddenDatePickerField = id.substr(0, id.length - 2);
665                        self.values[hiddenDatePickerField] = "";
666                        document.getElementById(hiddenDatePickerField).value = "";// sadly we cant use querySelector with current mark-up (would need to prefix IDs)
667                    }
668
669                    populateFieldValues(self.form, self.values, self.dataTypes);
670                    //process the conditions
671                    self.run();
672                }.bind(field));
673            }
674            //subscribe to change or input event as per configuration
675            var changeablefields = self.form.querySelectorAll("input, textarea, select");
676            for (let i = 0; i < changeablefields.length; i++) {
677                const field = changeablefields[i];
678                field.addEventListener(self.triggerConditionsCheckOn, function () {
679                    populateFieldValues(self.form, self.values, self.dataTypes);
680                    //process the conditions
681                    self.run();
682                }.bind(field));
683            }
684
685            //register all values from the current fields on the page
686            populateFieldValues(self.form, self.values, self.dataTypes);
687
688            //the initial run-through of all the conditions
689            self.run();
690        };
691
692        self.run = function () {
693            var pageId,
694                htmlFieldId,
695                fsId,
696                fieldId,
697
698                /*
699                fsConditions = params.fsConditions || {},
700                fieldConditions = params.fieldConditions || {},
701                values = params.values || {},*/
702
703                cachedResults = {};
704
705            function evaluateRuleInstance(rule) {
706                var value = self.values[self.formElementHtmlIdPrefix + rule.field],
707                    dataType = self.dataTypes[rule.field],
708                    func = self.operators[rule.operator],
709                    result = value !== null && func(value, rule.value, dataType);
710                return result;
711            }
712
713            function evaluateRule(rule) {
714                var dependencyIsVisible = true;
715
716                if (self.fieldConditions[rule.field]) {
717                    dependencyIsVisible = isVisible(rule.field, self.fieldConditions[rule.field]);
718                }
719
720                if (dependencyIsVisible) {
721                    return evaluateRuleInstance(rule);
722                }
723                else {
724                    return false;
725                }
726            }
727
728            function evaluateCondition(id, condition) {
729                // This was once pretty. Now it needs refactoring again. :)
730
731                var any = condition.logicType === "Any",
732                    all = condition.logicType === "All",
733                    fieldsetVisibilities = {},
734                    hasHiddenFieldset = false,
735                    success = true,
736                    rule,
737                    i;
738
739                // If we don't have any rules defined, we must return false (as neither 'any' nor 'all' of the conditions
740                // can be considered passing).
741                if (condition.rules.length === 0) {
742                    return false;
743                }
744
745                for (i = 0; i < condition.rules.length; i++) {
746                    rule = condition.rules[i];
747
748                    if (id === rule.field || id === rule.fieldsetId) {
749                        throw new Error("Field or fieldset " + id + " has a condition on itself.");
750                    }
751
752                    if (fieldsetVisibilities[rule.fieldsetId] !== undefined) {
753                        continue;
754                    }
755
756                    if (self.fieldsetConditions[rule.fieldsetId]) {
757
758                        fieldsetVisibilities[rule.fieldsetId] =
759                            isVisible(rule.fieldsetId, self.fieldsetConditions[rule.fieldsetId]);
760
761                        if (!fieldsetVisibilities[rule.fieldsetId]) {
762                            hasHiddenFieldset = true;
763                        }
764                    }
765                    else {
766                        fieldsetVisibilities[rule.fieldsetId] = true;
767                    }
768                }
769
770                if (all && hasHiddenFieldset) {
771                    return false;
772                }
773
774                for (i = 0; i < condition.rules.length; i++) {
775                    rule = condition.rules[i];
776
777                    if (fieldsetVisibilities[rule.fieldsetId]) {
778                        success = evaluateRule(condition.rules[i]);
779                    }
780                    else {
781                        success = false;
782                    }
783
784                    if (any && success) {
785                        break;
786                    }
787                    if (all && !success) {
788                        break;
789                    }
790                }
791                return success;
792            }
793
794            function evaluateConditionVisibility(id, condition) {
795                var show = condition.actionType === "Show",
796                    cachedResult = cachedResults[id];
797
798                var success;
799                if (cachedResult === undefined) {
800                    cachedResults[id] = show; // set default value to avoid circular issues
801                    success = (cachedResults[id] = evaluateCondition(id, condition));
802                } else {
803                    success = cachedResult;
804                }
805
806                var visible = !(success ^ show);
807                return visible;
808            }
809
810            function isVisible(id, condition) {
811                if (condition) {
812                    return evaluateConditionVisibility(id, condition);
813                }
814                return true;
815            }
816
817            function handleCondition(element, id, condition) {
818                var shouldShow = isVisible(id, condition);
819                if (element) {
820                    if (shouldShow) {
821                        element.classList.remove("umbraco-forms-hidden");
822                    }
823                    else {
824                        element.classList.add("umbraco-forms-hidden");
825                    }
826                }
827            }
828
829            for (pageId in self.pageButtonConditions) {
830                if (Object.prototype.hasOwnProperty.call(self.pageButtonConditions, pageId)) {
831                    var pageElem = document.getElementById(pageId);
832                    if (pageElem) {
833                        handleCondition(pageElem.querySelector("input[name='__next'], button[name='__next'], input[data-form-navigate='next'], button[data-form-navigate='next']"), pageId, self.pageButtonConditions[pageId], "Page");
834                    }
835                }
836            }
837
838            for (fsId in self.fieldsetConditions) {
839                if (Object.prototype.hasOwnProperty.call(self.fieldsetConditions, fsId)) {
840                    htmlFieldId = self.formElementHtmlIdPrefix + fsId;
841                    handleCondition(document.getElementById(htmlFieldId), fsId, self.fieldsetConditions[fsId], "Fieldset");// sadly we cant use querySelector with current mark-up (would need to prefix IDs)
842                }
843            }
844
845            for (fieldId in self.fieldConditions) {
846                if (Object.prototype.hasOwnProperty.call(self.fieldConditions, fieldId)) {
847                    htmlFieldId = self.formElementHtmlIdPrefix + fieldId;
848                    if (document.getElementById(htmlFieldId)) {
849                        handleCondition(document.getElementById(htmlFieldId).closest(".umbraco-forms-field"),// sadly we cant use querySelector with current mark-up (would need to prefix IDs)
850                            fieldId,
851                            self.fieldConditions[fieldId],
852                            "Field");
853                    }
854                }
855            }
856        };
857
858        return self;
859    }
860})();

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.