PageSourceSearch

https://www.winkworth.co.uk/Kentico/Scripts/forms/updatableFormHelper.js

js winkworth.co.uk collected 2026-10-02 05:00:46 UTC 9,007 bytes, 241 lines download raw bytes

1window.kentico = window.kentico || {};
2
3window.kentico.updatableFormHelper = (function () {
4
5    // Duration for which user must not type anything in order for the form to be submitted.
6    var KEY_UP_DEBOUNCE_DURATION = 800;
7
8    /**
9     * Registers event listeners and updates the form upon changes of the form data.
10     * @param {object} config Configuration object.
11     * @param {string} config.formId ID of the form element.
12     * @param {string} config.targetAttributeName Data attribute of element that is used to be replaced by HTML received from the server.
13     * @param {string} config.unobservedAttributeName Data attribute which marks an input as not being observed for changes.
14     */
15    function registerEventListeners(config) {
16        if (!config || !config.formId || !config.targetAttributeName || !config.unobservedAttributeName) {
17            throw new Error("Invalid configuration passed.");
18        }
19
20        var writeableTypes = ["email", "number", "password", "search", "tel", "text", "time"];
21
22        var observedForm = document.getElementById(config.formId);
23        if (!(observedForm && observedForm.getAttribute(config.targetAttributeName))) {
24            return;
25        }
26
27        for (i = 0; i < observedForm.length; i++) {
28            var observedFormElement = observedForm.elements[i];
29            var handleElement = !observedFormElement.hasAttribute(config.unobservedAttributeName) &&
30                observedFormElement.type !== "submit";
31
32            if (handleElement) {
33                var isWriteableElement = (observedFormElement.tagName === "INPUT" && writeableTypes.indexOf(observedFormElement.type) !== -1) || observedFormElement.tagName === "TEXTAREA";
34
35                if (isWriteableElement) {
36                    observedFormElement.previousValue = observedFormElement.value;
37
38                    observedFormElement.addEventListener("keyup", debounce(function (e) {
39                        setTimeout(function () {
40                            if (!observedForm.updating && e.target.previousValue !== e.target.value) {
41                                observedForm.keyupUpdate = true;
42                                updateForm(observedForm, e.target);
43                            }
44                        }, 0);
45                    }, KEY_UP_DEBOUNCE_DURATION));
46
47                    observedFormElement.addEventListener("blur", function (e) {
48                        setTimeout(function () {
49                            if (!observedForm.updating && e.target.previousValue !== e.target.value) {
50                                updateForm(observedForm, e.relatedTarget);
51                            }
52                        }, 0);
53                    });
54                }
55
56                observedFormElement.addEventListener("change", function (e) {
57                    setTimeout(function () {
58                        if (!observedForm.updating) {
59                            updateForm(observedForm);
60                        }
61                    }, 0);
62                });
63            }
64        }
65    }
66
67    /**
68     * Updates the form markup.
69     * @param {HTMLElement} form Element of the form to update.
70     * @param {Element} nextFocusElement Element which shout get focus after update.
71     */
72    function updateForm(form, nextFocusElement) {
73        if (!form) {
74            return;
75        }
76
77        // If form is not updatable then do nothing 
78        var elementIdSelector = form.getAttribute("data-ktc-ajax-update");
79        if (!elementIdSelector) {
80            return;
81        }
82
83        form.querySelectorAll("input[type='submit']").forEach((item) => {
84            item.setAttribute("onclick", "return false;");
85        });
86
87        form.updating = true;
88
89        var formData = new FormData(form);
90        formData.append("kentico_update_form", "true");
91
92        var focus = nextFocusElement || document.activeElement;
93
94        var onResponse = function (event) {
95            if (!event.target.response.data) {
96                var selectionStart = selectionEnd = null;
97                if (focus && (focus.type === "text" || focus.type === "search" || focus.type === "password" || focus.type === "tel" || focus.type === "url")) {
98                    selectionStart = focus.selectionStart;
99                    selectionEnd = focus.selectionEnd;
100                }
101
102                var currentScrollPosition = window.scrollY;
103                var element = document.getElementById(elementIdSelector);
104
105                if (useJQuery()) {
106                    $(element).replaceWith(event.target.responseText);
107                } else {
108                    renderMarkup(event.target.responseText, element);
109                }
110                window.scrollTo(0, currentScrollPosition);
111
112                if (focus.id) {
113                    var newInput = document.getElementById(focus.id);
114                    if (newInput) {
115                        newInput.focus();
116                        setCaretPosition(newInput, selectionStart, selectionEnd);
117                    }
118                }
119            }
120        };
121
122        createRequest(form, formData, onResponse);
123    }
124
125    function submitForm(event) {
126        event.preventDefault();
127        var form = event.target;
128        var formData = new FormData(form);
129
130        var onResponse = function(event) {
131            var contentType = event.target.getResponseHeader("Content-Type");
132
133            if (contentType.indexOf("application/json") === -1) {
134                var currentScrollPosition = window.scrollY;
135                var replaceTargetId = form.getAttribute("data-ktc-ajax-update");
136
137                var element = document.getElementById(replaceTargetId);
138
139                if (useJQuery()) {
140                    $(element).replaceWith(event.target.response);
141                } else {
142                    renderMarkup(event.target.response, element)
143                }
144
145                window.scrollTo(0, currentScrollPosition);
146            } else {
147                var json = JSON.parse(event.target.response);
148
149                location.href = json.redirectTo;
150            }
151        };
152
153        createRequest(form, formData, onResponse);
154    }
155
156    const renderMarkup = (markup, targetElement) => {
157        var elementFromMarkup = createElementFromMarkup(markup);
158
159        targetElement.parentNode.replaceChild(elementFromMarkup, targetElement);
160        const scripts = elementFromMarkup.querySelectorAll("script");
161        Array.prototype.forEach.call(scripts, (scriptElement) => {
162            const parent = scriptElement.parentNode;
163            const temp = document.createElement("script");
164            [...scriptElement.attributes].forEach((attr) => {
165                temp.setAttribute(attr.name, attr.value);
166            });
167
168            temp.innerHTML = scriptElement.innerHTML;
169            parent.replaceChild(temp, scriptElement);
170            scriptElement.remove();
171        });
172    };
173
174    function createElementFromMarkup(markup) {
175        var element = document.createElement("div");
176
177        element.innerHTML = markup;
178
179        return element.firstElementChild;
180    }
181
182    function createRequest(form, formData, onResponse) {
183        var xhr = new XMLHttpRequest();
184
185        xhr.addEventListener("load", onResponse);
186
187        xhr.open("POST", form.action);
188        xhr.send(formData);
189    }
190
191    /**
192     * Sets the caret position.
193     * @param {HTMLInputElement} input Input element in which the caret position should be set.
194     * @param {number} selectionStart Selection start position.
195     * @param {number} selectionEnd Selection end position.
196     */
197    function setCaretPosition(input, selectionStart, selectionEnd) {
198        if (selectionStart === null && selectionEnd === null) {
199            return;
200        }
201
202        if (input.setSelectionRange) {
203            input.setSelectionRange(selectionStart, selectionEnd);
204        }
205    }
206
207    function debounce(func, wait, immediate) {
208        var timeout;
209
210        return function () {
211            var context = this,
212                args = arguments;
213
214            var later = function () {
215                timeout = null;
216
217                if (!immediate) {
218                    func.apply(context, args);
219                }
220            };
221
222            var callNow = immediate && !timeout;
223            clearTimeout(timeout);
224            timeout = setTimeout(later, wait || 200);
225
226            if (callNow) {
227                func.apply(context, args);
228            }
229        };
230    }
231
232    function useJQuery() {
233        return window.kentico.hasOwnProperty("builder") ? window.kentico.builder.useJQuery : false;
234    }
235
236    return {
237        registerEventListeners: registerEventListeners,
238        updateForm: updateForm,
239        submitForm: submitForm
240    };
241}());

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.