PageSourceSearch

https://www.pulitzeramsterdam.com/App_Plugins/UmbracoForms/Assets/Themes/Default/umbracoforms.js

js pulitzeramsterdam.com collected 2026-09-26 03:44:40 UTC 29,056 bytes, 689 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    if (document.readyState === "complete" || (document.readyState !== "loading" && !document.documentElement.doScroll)) {
20        listen();
21    } else {
22        document.addEventListener("DOMContentLoaded", listen);
23    }
24
25    function listen() {
26        if (typeof umbracoFormsCollection === "undefined") {
27            //this will occur if this js file is loaded before the inline scripts, in which case
28            //we'll listen for the inline scripts to execute a custom event.
29            document.addEventListener("umbracoFormLoaded", init);
30        }
31        else {
32            initCollection(umbracoFormsCollection);
33        }
34    }
35
36    function initCollection(formsCollection) {
37        configureUmbracoFormsValidation();
38
39        for (var i = 0; i < formsCollection.length; i++) {
40            init({ form: formsCollection[i] });
41        }
42    }
43
44    function init(e) {
45
46        var formItem = JSON.parse(decodeURI(e.form));
47
48        var forms = document.querySelectorAll('.umbraco-forms-form');
49
50        for(var i = 0; i < forms.length; i++) {
51            var form = forms[i];
52
53            dependencyCheck(form);
54
55            var page = form.querySelector('.umbraco-forms-page');
56            var conditions = new UmbracoFormsConditions(page,
57                formItem.pageButtonConditions,
58                formItem.fieldSetConditions,
59                formItem.fieldConditions,
60                formItem.recordValues);
61            conditions.watch();
62
63            applyFormAccessibility(form);
64        }
65    }
66
67    /** Configures the jquery validation for Umbraco forms */
68    function configureUmbracoFormsValidation() {
69
70        if (window.aspnetValidation !== undefined) {
71            // Asp-net validation setup:
72
73            var validationService = new aspnetValidation.ValidationService();
74
75            // TODO: equivilant to this:
76            /*
77            $.validator.setDefaults({
78                ignore: ":hidden"
79            });
80            */
81           
82            var required = function (value, element) {
83                // Handle single and multiple checkboxes:
84                if(element.type.toLowerCase() === "checkbox" || element.type.toLowerCase() === "radio") {
85                    var allCheckboxesOfThisName = element.form.querySelectorAll("input[name='"+element.name+"']");
86                    for (var i=0; i<allCheckboxesOfThisName.length; i++) {
87                        if (allCheckboxesOfThisName[i].checked === true) {
88                            return true;
89                        }
90                    }
91                    return false;
92                }
93                return Boolean(value);
94            }
95            validationService.addProvider("requiredcb", required);
96            validationService.addProvider("required", required);// this will go instead of the build-in required.
97
98
99            var umbracoforms_regex = function (value, element, params) {
100                if (!value || !params.pattern) {
101                    return true;
102                }
103        
104                var r = new RegExp(params.pattern);
105                return r.test(value);
106            }
107            validationService.addProvider("umbracoforms_regex", umbracoforms_regex);
108
109            var wrapProviderWithIgnorerBehaviour = function (provider) {
110                return function(value, element, params) {
111                    
112                    // If field is hidden we ignorer the validation.
113                    if (element.offsetParent === null) {
114                        return true;
115                    }
116
117                    return provider(value, element, params);
118                }
119            }
120
121            // we can only incept with default validator if we do it after bootstrapping but before window load event triggers validationservice.
122            window.addEventListener('load', function() {
123
124                // Wrap all providers with ignorer hidden fields logic:
125                for (var key in validationService.providers) {
126                    validationService.providers[key] = wrapProviderWithIgnorerBehaviour(validationService.providers[key]);
127                }
128            });
129
130            // bootstrap validation service.
131            validationService.bootstrap();
132
133            // Without jquery validation, the previous page submit button click isn't sent with it's name,
134            // so can't be used server-side to determine whether to go forward or back.
135            // Hence we'll use an alternate method, setting a hidden field that's also used in the check.
136            var handlePreviousClicked = function () {
137                this.form.elements["PreviousClicked"].value = "clicked";
138            };
139            var previousButtonElements = document.getElementsByClassName("prev cancel");
140            for (var i = 0; i < previousButtonElements.length; i++) {
141                previousButtonElements[i].form.elements["PreviousClicked"].value = "";
142                previousButtonElements[i].addEventListener('click', handlePreviousClicked, false);
143            }
144
145        } else if (typeof jQuery === "function" && $.validator) {
146            //Jquery validation setup
147
148            $.validator.setDefaults({
149                ignore: ":hidden"
150            });
151
152            $.validator.unobtrusive.adapters.addBool("requiredcb", "required");
153
154
155            $.validator.addMethod("umbracoforms_regex", function (value, element) {
156
157                var regex = $(element).attr("data-regex");
158                var val = $(element).val();
159                if (val.length === 0) {
160                    return true;
161                }
162                return val.match(regex);
163            });
164
165            $.validator.unobtrusive.adapters.addBool("regex", "umbracoforms_regex");
166
167            var submitInputs = document.querySelectorAll(".umbraco-forms-form input[type=submit]:not(.cancel)");
168            for (let i = 0; i < submitInputs.length; i++) {
169                var input = submitInputs[i];
170                input.addEventListener("click", function (evt) {
171                    evt.preventDefault();
172                    var frm = $(this).closest("form");
173                    resetValidationMessages(frm[0]);
174                    frm.validate();
175                    if (frm.valid()) {
176                        frm.submit();
177                        this.setAttribute("disabled", "disabled");
178    
179                    }
180                }.bind(input));
181            }
182        }
183
184    }
185
186    /**
187     * method to determine if Umbraco Forms can run and has the required dependencies loaded
188     * @param {Form Element} formEl the element of the form
189     */
190    function dependencyCheck(formEl) {
191        // Only perform check if the global 'Umbraco.Sys' is null/undefined.
192        // 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.
193        // Similarly we need a check for if running in a rich text editor.
194        var isBackOffice = function () {
195            return typeof Umbraco !== "undefined" && typeof Umbraco.Sys !== "undefined";
196        };
197        var isBackOfficeRte = function () {
198            return document.body.id === "tinymce";
199        };
200        if (isBackOffice() || isBackOfficeRte()) {
201            return;
202        }
203
204        var errorElement = document.createElement("div");
205        errorElement.className = "umbraco-forms missing-library";
206        errorElement.style.color = "#fff";
207        errorElement.style.backgroundColor = "#9d261d";
208        errorElement.style.padding = "15px";
209        errorElement.style.margin = "10px 0";
210        var errorMessage = "";
211
212        //Ensure umbracoForm is not null
213        if (formEl) {
214
215            //Check to see if the message for the form has been inserted already
216            var checkForExistinhgErr = formEl.getElementsByClassName('umbraco-forms missing-library');
217            if (checkForExistinhgErr.length > 0) {
218                return;
219            }
220
221            var hasValidationFramework = false;
222
223            if (window.jQuery && $ && $.validator !== undefined) {
224                hasValidationFramework = true;
225            } else if (window.aspnetValidation !== undefined) {
226                hasValidationFramework = true;
227            }
228
229            if(hasValidationFramework === false) {
230                errorMessage = errorMessage + "Umbraco Forms requires a validation framework to run, please read documentation for posible options.";
231            }
232
233            if (errorMessage !== "") {
234                errorElement.innerHTML = errorMessage + '<br/> <a href="https://our.umbraco.org/documentation/products/umbracoforms/developer/Prepping-Frontend/" target="_blank" style="text-decoration:underline;
234 color:#fff;">See Umbraco Forms Documentation</a>';
235
236                formEl.insertBefore(errorElement, formEl.childNodes[0]);
237            }
238        }
239    }
240
241    /**
242     * Applies form accessibility improvements.
243     * @param {Element} formEl the element of the form.
244     */
245    function applyFormAccessibility(formEl) {
246        setFocusToFirstElementOnValidationError(formEl);
247    }
248
249    /**
250     * Monitors for validation errors and when found sets the focus to the first field with an error.
251     * @param {Element} formEl the element of the form.
252     */
253    function setFocusToFirstElementOnValidationError(formEl) {
254        if ("MutationObserver" in window === false) {
255            return;
256        }
257
258        // To implement this, we are relying on on monitoring for validation message elements, which only fires when there are changes.
259        // So if you have two errors, and fix the first one, it wouldn't then highlight the second one on re-submitting the form.
260        // Unless we reset the validation messages on submit, so they get changed back on errors.
261        if (window.aspnetValidation !== undefined) {
262            var form = formEl.getElementsByTagName('form')[0];
263            var handleResetValidationMessages = function (event) {
264                resetValidationMessages(form);
265            };
266            form.addEventListener('submit', handleResetValidationMessages, false);
267        } else {
268            // For jquery.validate, we need to hook this in as part of the submit handler coded in configureUmbracoFormsValidation();
269        }
270
271        // Watch for changes to the validation error messages in the DOM tree using a MutationObserver.
272        // See: https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
273        var observer = new MutationObserver(function (mutationRecords) {
274            for (var i = 0; i < mutationRecords.length; i++) {
275                var mutationRecord = mutationRecords[i];
276                if (mutationRecord.target.className === 'field-validation-error') {
277                    setFocusOnFormField(mutationRecord.target);
278                    break;
279                }
280            }
281        });
282
283        observer.observe(formEl, {
284            attributes: true,
285            attributeFilter: ['class'],
286            childList: false,
287            characterData: false,
288            subtree: true
289        });
290    }
291
292    /**
293     * Resets the validation messages for a form.
294     * @param {Element} formEl the element of the form.
295     */
296    function resetValidationMessages(formEl) {
297        var validationErrorMessageElements = formEl.getElementsByClassName('field-validation-error');
298        for (var i = 0; i < validationErrorMessageElements.length; i++) {
299            validationErrorMessageElements[i].className = 'field-validation-valid';
300        }
301    }
302
303    /**
304     * Sets the focus to the form field input element associated with the provided validation message element.
305     * @param {Element} validationErrorEl the element of the validation error.
306     */
307    function setFocusOnFormField(validationErrorEl) {
308        var formFieldElement = validationErrorEl.previousElementSibling;
309        while (formFieldElement) {
310            if (formFieldElement.tagName.toLowerCase() === 'input' ||
311                formFieldElement.tagName.toLowerCase() === 'textarea' ||
312                formFieldElement.tagName.toLowerCase() === 'select') {
313                formFieldElement.focus();
314                break;
315            }
316
317            if (formFieldElement.classList.contains("radiobuttonlist") ||
318                formFieldElement.classList.contains("checkboxlist")) {
319                for (var i = 0; i < formFieldElement.children.length; i++) {
320                    var formFieldChildElement = formFieldElement.children[i];
321                    if (formFieldChildElement.tagName.toLowerCase() === 'input') {
322                        formFieldChildElement.focus();
323                        break;
324                    }
325                }
326
327                break;
328            }
329
330            formFieldElement = formFieldElement.previousElementSibling;
331        }
332     }
333
334    /**
335     * Class to handle Umbraco Forms conditional statements
336     * @param {any} form a reference to the form
337     * @param {any} pageButtonConditions a reference to the page button conditions
338     * @param {any} fieldsetConditions a reference to the fieldset conditions
339     * @param {any} fieldConditions a reference to the field conditions
340     * @param {any} values the form values
341     * @return {Object} reference to the created class
342     */
343    function UmbracoFormsConditions(form, pageButtonConditions, fieldsetConditions, fieldConditions, values) {
344
345        //our conditions "class" - must always be newed to work as it uses a form instance to operate on
346        //load all the information from the dom and serverside info and then the class will take care of the rest
347
348        var self = {};
349        self.form = form;
350        self.pageButtonConditions = pageButtonConditions;
351        self.fieldsetConditions = fieldsetConditions;
352        self.fieldConditions = fieldConditions;
353        self.values = values;
354        self.dataTypes = {};
355
356        //Iterates through all the form elements found on the page to update the registered value
357        function populateFieldValues(page, formValues, dataTypes) {
358
359            var selectFields = page.querySelectorAll("select");
360            for (let i = 0; i < selectFields.length; i++) {
361                const field = selectFields[i];
362                formValues[field.getAttribute("id")] = field.value ? field.querySelector("option[value='" + field.value.replace(/'/g, "\\'") + "']").innerText : null;
363                dataTypes[field.getAttribute("id")] = "select";
364            }
365
366            var textareaFields = page.querySelectorAll("textarea");
367            for (let i=0; i<textareaFields.length; i++) {
368                const field = textareaFields[i];
369                formValues[field.getAttribute("id")] = field.value;
370                dataTypes[field.getAttribute("id")] = "textarea";
371            }
372
373            // clear out all saved checkbox values to we can safely append
374            var checkboxFields = page.querySelectorAll("input[type=checkbox]");
375            for (let i=0; i<checkboxFields.length; i++) {
376                const field = checkboxFields[i];
377                formValues[field.getAttribute("name")] = null;
378                dataTypes[field.getAttribute("id")] = "checkbox";
379            }
380
381            //$page.find("input").each(function () {
382            var inputFields = page.querySelectorAll("input");
383            for (let i=0; i<inputFields.length; i++) {
384                const field = inputFields[i];
385
386                if (field.getAttribute('type') === "text" ||
387                    field.getAttribute('type') === "number" ||
388                    field.getAttribute('type') === "email" ||
389                    field.getAttribute('type') === "url" ||
390                    field.getAttribute('type') === "tel" ||
391                    field.getAttribute('type') === "date" ||
392                    field.getAttribute('type') === "datetime-local" ||
393                    field.getAttribute("type") === "hidden") {
394                    formValues[field.getAttribute("id")] = field.value;
395                    dataTypes[field.getAttribute("id")] = "text";
396                }
397
398                if (field.getAttribute('type') === "radio") {
399                    if (field.matches(':checked')) {
400                        formValues[field.getAttribute("name")] = field.value;
401                        dataTypes[field.getAttribute("id")] = "radio";
402                    }
403                }
404
405                if (field.getAttribute("type") === "checkbox") {
406                    if (field.getAttribute("id") !== field.getAttribute("name")) {
407                        if (field.matches(":checked")) {
408                            if (formValues[field.getAttribute("name")] === null) {
409                                formValues[field.getAttribute("name")] = field.value;
410                            }
411                            else {
412                                formValues[field.getAttribute("name")] += ";;" + field.value;
413                            }
414                        }
415                    }
416                    else {
417                        formValues[field.getAttribute("name")] = (field.matches(":checked") ? "true" : "false");
418                    }
419                }
420            }
421        }
422
423        /* Public api */
424
425        self.operators = {
426            Is: function (value, expected, dataType) {
427                if ((value || "") === expected) {
428                    return true;
429                }
430                if (value == null) {
431                    return (expected == value);
432                }
433
434                if (dataType === "checkbox") {
435                    if (expected.toUpperCase() === "TRUE" || expected.toUpperCase() === "ON") {
436                        expected = "true"
437                    } else if (expected.toUpperCase() === "FALSE" || expected.toUpperCase() === "OFF") {
438                        expected = "false"
439                    }
440                }
441
442                var values = value.split(';;');
443                var matchingExpected = values.filter(
444                    function (o) {
445                        return o === expected;
446                    });
447                return matchingExpected.length > 0;
448            },
449            IsNot: function (value, unexpected, dataType) {
450                if (value == null) {
451                    return (unexpected != value);
452                }
453                var values = value.split(';;');
454                var matchingUnexpected = values.filter(
455                    function (o) {
456                        return o === unexpected;
457                    });
458
459                if (dataType === "checkbox") {
460                    if (unexpected.toUpperCase() === "TRUE" || unexpected.toUpperCase() === "ON") {
461                        unexpected = "true"
462                    } else if (unexpected.toUpperCase() === "FALSE" || unexpected.toUpperCase() === "OFF") {
463                        unexpected = "false"
464                    }
465                }
466                return (value || "") !== unexpected && matchingUnexpected.length === 0;
467            },
468            GreaterThen: function (value, limit) {
469                return parseInt(value) > parseInt(limit);
470            },
471            LessThen: function (value, limit) {
472                return parseInt(value) < parseInt(limit);
473            },
474            StartsWith: function (value, criteria) {
475                return value && value.indexOf(criteria) === 0;
476            },
477            EndsWith: function (value, criteria) {
478                return value && value.indexOf(criteria) === value.length - criteria.length;
479            },
480            Contains: function (value, criteria) {
481                return value && value.indexOf(criteria) > -1;
482            }
483        };
484
485        self.watch = function () {
486            // This is a special case for pikaday
487            // The only way around to pickup the value, for now, is to 
488            // subscribe to blur events 
489            var datepickerfields = self.form.querySelectorAll('.datepickerfield');
490            for(let i = 0; i < datepickerfields.length; i++) {
491                const field = datepickerfields[i];
492                field.addEventListener('blur', function () {
493                    if(this.value===""){
494                        // Here comes the hack
495                        // Force the hidden datepicker field the datepicker field
496                        var id = this.getAttribute("id");
497                        var hiddenDatePickerField = id.substr(0, id.length-2);
498                        self.values[hiddenDatePickerField]="";
499                        document.getElementById(hiddenDatePickerField).value="";// sadly we cant use querySelector with current mark-up (would need to prefix IDs)
500                    }
501    
502                    populateFieldValues(self.form, self.values, self.dataTypes);
503                    //process the conditions
504                    self.run();
505                }.bind(field));
506            }
507            //subscribe to change events
508            var changeablefields = self.form.querySelectorAll("input, textarea, select");
509            for(let i = 0; i < changeablefields.length; i++) {
510                const field = changeablefields[i];
511                field.addEventListener("change", function () {
512                    populateFieldValues(self.form, self.values, self.dataTypes);
513                    //process the conditions
514                    self.run();
515                }.bind(field));
516            }
517
518            //register all values from the current fields on the page
519            populateFieldValues(self.form, self.values, self.dataTypes);
520
521            //the initial run-through of all the conditions
522            self.run();
523        };
524
525        self.run = function () {
526            var fsId,
527                fieldId,
528
529                /*
530                fsConditions = params.fsConditions || {},
531                fieldConditions = params.fieldConditions || {},
532                values = params.values || {},*/
533
534                cachedResults = {};
535
536            function evaluateRuleInstance(rule) {
537                var value = self.values[rule.field],
538                    dataType = self.dataTypes[rule.field],
539                    func = self.operators[rule.operator],
540                    result = value !== null && func(value, rule.value, dataType);
541                return result;
542            }
543
544            function evaluateRule(rule) {
545                var dependencyIsVisible = true;
546
547                if (self.fieldConditions[rule.field]) {
548                    dependencyIsVisible = isVisible(rule.field, self.fieldConditions[rule.field]);
549                }
550
551                if (dependencyIsVisible) {
552                    return evaluateRuleInstance(rule);
553                }
554                else {
555                    return false;
556                }
557            }
558
559            function evaluateCondition(id, condition) {
560                // This was once pretty. Now it needs refactoring again. :)
561
562                var any = condition.logicType === "Any",
563                    all = condition.logicType === "All",
564                    fieldsetVisibilities = {},
565                    hasHiddenFieldset = false,
566                    success = true,
567                    rule,
568                    i;
569
570                // If we don't have any rules defined, we must return false (as neither 'any' nor 'all' of the conditions
571                // can be considered passing).
572                if (condition.rules.length === 0) {
573                    return false;
574                }
575
576                for (i = 0; i < condition.rules.length; i++) {
577                    rule = condition.rules[i];
578
579                    if (id === rule.field || id === rule.fieldsetId) {
580                        throw new Error("Field or fieldset " + id + " has a condition on itself.");
581                    }
582
583                    if (fieldsetVisibilities[rule.fieldsetId] !== undefined) {
584                        continue;
585                    }
586
587                    if (self.fieldsetConditions[rule.fieldsetId]) {
588
589                        fieldsetVisibilities[rule.fieldsetId] =
590                            isVisible(rule.fieldsetId, self.fieldsetConditions[rule.fieldsetId]);
591
592                        if (!fieldsetVisibilities[rule.fieldsetId]) {
593                            hasHiddenFieldset = true;
594                        }
595                    }
596                    else {
597                        fieldsetVisibilities[rule.fieldsetId] = true;
598                    }
599                }
600
601                if (all && hasHiddenFieldset) {
602                    return false;
603                }
604
605                for (i = 0; i < condition.rules.length; i++) {
606                    rule = condition.rules[i];
607
608                    if (fieldsetVisibilities[rule.fieldsetId]) {
609                        success = evaluateRule(condition.rules[i]);
610                    }
611                    else {
612                        success = false;
613                    }
614
615                    if (any && success) {
616                        break;
617                    }
618                    if (all && !success) {
619                        break;
620                    }
621                }
622                return success;
623            }
624
625            function evaluateConditionVisibility(id, condition) {
626                var show = condition.actionType === "Show",
627                    cachedResult = cachedResults[id];
628
629                var success;
630                if (cachedResult === undefined) {
631                    cachedResults[id] = show; // set default value to avoid circular issues
632                    success = (cachedResults[id] = evaluateCondition(id, condition));
633                } else {
634                    success = cachedResult;
635                }
636
637                var visible = !(success ^ show);
638                return visible;
639            }
640
641            function isVisible(id, condition) {
642                if (condition) {
643                    return evaluateConditionVisibility(id, condition);
644                }
645                return true;
646            }
647
648            function handleCondition(element, id, condition) {
649                var shouldShow = isVisible(id, condition);
650                if (element) {
651                    if (shouldShow) {
652                        element.style.display = "";
653                    }
654                    else {
655                        element.style.display = "none";
656                    }
657                }
658            }
659
660            for (pageId in self.pageButtonConditions) {
661                if (Object.prototype.hasOwnProperty.call(self.pageButtonConditions, pageId)) {
662                    var pageElem = document.getElementById(pageId);
663                    if (pageElem) {
664                        handleCondition(pageElem.querySelector("input[name='__next'], button[name='__next']"), fsId, self.pageButtonConditions[pageId], "Page");
665                    }
666                }
667            }
668
669            for (fsId in self.fieldsetConditions) {
670                if (Object.prototype.hasOwnProperty.call(self.fieldsetConditions, fsId)) {
671                    handleCondition(document.getElementById(fsId), fsId, self.fieldsetConditions[fsId], "Fieldset");// sadly we cant use querySelector with current mark-up (would need to prefix IDs)
672                }
673            }
674
675            for (fieldId in self.fieldConditions) {
676                if (Object.prototype.hasOwnProperty.call(self.fieldConditions, fieldId)) {
677                    if (document.getElementById(fieldId)) {
678                        handleCondition(document.getElementById(fieldId).closest(".umbraco-forms-field"),// sadly we cant use querySelector with current mark-up (would need to prefix IDs)
679                            fieldId,
680                            self.fieldConditions[fieldId],
681                            "Field");
682                    }
683                }
684            }
685        };
686
687        return self;
688    }
689})();

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.