PageSourceSearch

https://hrone.cloud/wp-content/themes/hrone-child/assets/js/blog-form-submit.js?ver=551290860

js hrone.cloud collected 2026-09-24 10:24:44 UTC 13,618 bytes, 309 lines download raw bytes

1/**
2 * Blog Sidebar Form Submission Handler
3 * Submits blog lead form directly to HubSpot API (no AJAX/backend required)
4 *
5 * IMPORTANT: footer.php contains a global jQuery handler:
6 *   $(".submit").click(function(){ return false; });
7 * which prevents bubbling + native submit. To work reliably, we bind in CAPTURE phase.
8 */
9
10(function() {
11    'use strict';
12
13    // Debug marker (harmless): lets us confirm the file is actually loading on the page.
14    window.__hroneBlogFormSubmitLoaded = true;
15
16    const HUBSPOT_PORTAL_ID = '7277696';
17    const HUBSPOT_FORM_GUID = '6c325e52-7b50-4941-8ac4-156bda06c288';
18    const HUBSPOT_API_URL = 'https://api.hsforms.com/submissions/v3/integration/submit/' + HUBSPOT_PORTAL_ID + '/' + HUBSPOT_FORM_GUID;
19
20    function sanitizePlainText(value, maxLen = 250) {
21        if (value === null || value === undefined) return '';
22        let str = String(value);
23        str = str.replace(/<[^>]*>/g, '');
24        str = str.replace(/[\u0000-\u001F\u007F]/g, '');
25        str = str.replace(/\s+/g, ' ').trim();
26        if (maxLen && str.length > maxLen) str = str.slice(0, maxLen);
27        return str;
28    }
29
30    function sanitizeEmail(value) {
31        const email = sanitizePlainText(value, 320).toLowerCase();
32        return email.replace(/\s+/g, '');
33    }
34
35    function sanitizePhoneIN(value) {
36        const digits = String(value ?? '').replace(/\D/g, '');
37        return digits.length <= 10 ? digits : digits.slice(-10);
38    }
39
40    function sanitizeUrl(value) {
41        const raw = sanitizePlainText(value, 2000);
42        if (!raw) return '';
43        try {
44            const u = new URL(raw, window.location.origin);
45            if (u.protocol !== 'http:' && u.protocol !== 'https:') return '';
46            return u.href;
47        } catch (e) {
48            return '';
49        }
50    }
51
52    function getCookie(name) {
53        const value = `; ${document.cookie}`;
54        const parts = value.split(`; ${name}=`);
55        if (parts.length === 2) return parts.pop().split(';').shift();
56        return '';
57    }
58
59    function getUtmParamsFromUrl() {
60        try {
61            const url = new URL(window.location.href);
62            return {
63                utm_source: sanitizePlainText(url.searchParams.get('utm_source') || '', 120),
64                utm_medium: sanitizePlainText(url.searchParams.get('utm_medium') || '', 
64120),
65                utm_campaign: sanitizePlainText(url.searchParams.get('utm_campaign') || '', 120),
66                utm_content: sanitizePlainText(url.searchParams.get('utm_content') || '', 120),
67                utm_term: sanitizePlainText(url.searchParams.get('utm_term') || '', 120),
68            };
69        } catch (e) {
70            return {
71                utm_source: '',
72                utm_medium: '',
73                utm_campaign: '',
74                utm_content: '',
75                utm_term: '',
76            };
77        }
78    }
79
80    function isWorkEmailValid(email) {
81        if (typeof window.validateEmail === 'function') {
82            return window.validateEmail(email);
83        }
84        return /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(email);
85    }
86
87    function validateCityAgainstAutocomplete(cityValue) {
88        const city = (cityValue || '').trim();
89        if (!city) return false;
90        // NOTE: availableKeywords is declared with `let` in footer.php, so it is NOT guaranteed to be on window.
91        // Best-effort: if a global variable exists (either as window property or lexical global), use it.
92        try {
93            const list =
94                (typeof availableKeywords !== 'undefined' && Array.isArray(availableKeywords)) ? availableKeywords :
95                (typeof window.availableKeywords !== 'undefined' && Array.isArray(window.availableKeywords)) ? window.availableKeywords :
96                null;
97            if (list) {
98                const exists = list.some(({ city: c }) => c === city);
99                return !!exists;
100            }
101        } catch (e) {}
102        // If autocomplete list isn't available, just require non-empty city
103        return true;
104    }
105
106    function setText(formEl, selector, text) {
107        const el = formEl ? formEl.querySelector(selector) : null;
108        if (el) el.textContent = text || '';
109    }
110
111    function setError(el, hasError) {
112        if (!el) return;
113        if (hasError) el.classList.add('error');
114        else el.classList.remove('error');
115    }
116
117    function toggleLoader(show) {
118        const loaderEl = document.getElementById('loaderpopup');
119        if (loaderEl) loaderEl.style.display = show ? 'block' : 'none';
120    }
121
122    function submitBlogForm(formEl, btnEl) {
123        if (!formEl) return;
124
125        const nameEl = formEl.querySelector('input[name="name"]');
126        const phoneEl = formEl.querySelector('input[name="phone"]');
127        const emailEl = formEl.querySelector('input[name="email"]');
128        const employeesEl = formEl.querySelector('select[name="number_of_employee_v1"]');
129        const cityEl = formEl.querySelector('input[name="city"]#input-box-city, input[name="city"]');
130        const designationEl = formEl.querySelector('input[name="designation"]');
131
132        const permalinkEl = formEl.querySelector('input[name="permalink"]');
133        const titleEl = formEl.querySelector('input[name="title"]');
134
135        // Sanitize values
136        const nameVal = nameEl ? sanitizePlainText(nameEl.value, 120) : '';
137        const phoneVal = phoneEl ? sanitizePhoneIN(phoneEl.value) : '';
138        const emailVal = emailEl ? sanitizeEmail(emailEl.value) : '';
139        const employeesVal = employeesEl ? sanitizePlainText(employeesEl.value || '', 50) : '';
140        const cityVal = cityEl ? sanitizePlainText(cityEl.value || '', 120) : '';
141        const designationVal = designationEl ? sanitizePlainText(designationEl.value || '', 120) : '';
142
143        const permalinkVal = permalinkEl ? sanitizeUrl(permalinkEl.value) : sanitizeUrl(window.location.href);
144        const titleVal = titleEl ? sanitizePlainText(titleEl.value, 200) : sanitizePlainText(document.title, 200);
145
146        // UTM values: prefer hidden inputs (if present), else pull from URL
147        const utmFromUrl = getUtmParamsFromUrl();
148        const utm_source = sanitizePlainText((formEl.querySelector('input[name="utm_source"]') && formEl.querySelector('input[name="utm_source"]').value) || utmFromUrl.utm_source, 120);
149        const utm_medium = sanitizePlainText((formEl.querySelector('input[name="utm_medium"]') && formEl.querySelector('input[name="utm_medium"]').value) || utmFromUrl.utm_medium, 120);
150        const utm_campaign = sanitizePlainText((formEl.querySelector('input[name="utm_campaign"]') && formEl.querySelector('input[name="utm_campaign"]').value) || utmFromUrl.utm_campaign, 120);
151        const utm_content = sanitizePlainText((formEl.querySelector('input[name="utm_content"]') && formEl.querySelector('input[name="utm_content"]').value) || utmFromUrl.utm_content, 120);
152        const utm_term = sanitizePlainText((formEl.querySelector('input[name="utm_term"]') && formEl.querySelector('input[name="utm_term"]').value) || utmFromUrl.utm_term, 120);
153
154        // Validation
155        let isValid = true;
156
157        if (nameEl) {
158            setError(nameEl, !nameVal);
159            if (!nameVal) isValid = false;
160        }
161
162        if (phoneEl) {
163            const phoneRegex = /^[6-9]\d{9}$/;
164            const ok = phoneRegex.test(phoneVal);
165            setError(phoneEl, !ok);
166            setText(formEl, '.phone_error', ok ? '' : 'Insert a valid number ');
167            if (!ok) isValid = false;
168        }
169
170        if (emailEl) {
171            const ok = !!emailVal && isWorkEmailValid(emailVal);
172            setError(emailEl, !ok);
173            setText(formEl, '.email_error', ok ? '' : 'Please insert valid work email. ');
174            if (!ok) isValid = false;
175        }
176
177        if (employeesEl) {
178            const ok = !!employeesVal;
179            setError(employeesEl, !ok);
180            setText(formEl, '.employee_error', ok ? '' : 'Please select the number of employees.');
181            if (!ok) isValid = false;
182        }
183
184        if (cityEl) {
185            const ok = validateCityAgainstAutocomplete(cityVal);
186            setError(cityEl, !ok);
187            setText(formEl, '.city-error', ok ? '' : 'City not found in the list. Please select a valid city.');
188            if (!ok) isValid = false;
189        }
190
191        if (designationEl) {
192            setError(designationEl, !designationVal);
193            if (!designationVal) isValid = false;
194        }
195
196        if (!isValid) return;
197
198        // Disable while submitting
199        if (btnEl) btnEl.disabled = true;
200        toggleLoader(true);
201
202        // Parse first/last name
203        let firstname = '';
204        let lastname = '';
205        if (nameVal) {
206            const parts = nameVal.trim().split(' ');
207            firstname = parts[0] || '';
208            lastname = parts.slice(1).join(' ') || '';
209        } 
210
211        const hubspotCookie = getCookie('hubspotutk') || '';
212
213        const hubspotData = {
214            submittedAt: new Date().toISOString(),
215            fields: [
216                { objectTypeId: '0-1', name: 'firstname', value: firstname },
217                { objectTypeId: '0-1', name: 'lastname', value: lastname },
218                { objectTypeId: '0-1', name: 'email', value: emailVal || '' },
219                // Blog form historically used "mobilephone" in PHP handler; keep the same mapping.
220                { objectTypeId: '0-1', name: 'mobilephone', value: '+91' + (phoneVal || '') },
221                { objectTypeId: '0-1', name: 'city', value: cityVal || '' },
222                { objectTypeId: '0-1', name: 'designation', value: designationVal || '' },
223                { objectTypeId: '0-1', name: 'number_of_employee_v1', value: employeesVal || '' },
224                { objectTypeId: '0-1', name: 'utm_source', value: utm_source || '' },
225                { objectTypeId: '0-1', name: 'utm_medium', value: utm_medium || '' },
226                { objectTypeId: '0-1', name: 'utm_campaign', value: utm_campaign || '' },
227                { objectTypeId: '0-1', name: 'utm_content', value: utm_content || '' },
228                { objectTypeId: '0-1', name: 'utm_term', value: utm_term || '' },
229            ],
230            context: {
231                hutk: hubspotCookie,
232                pageUri: permalinkVal || window.location.href,
233                pageName: titleVal || document.title,
234            },
235            legalConsentOptions: {
236                consent: {
237                    consentToProcess: true,
238                    text: 'I agree to allow HROne to store and process my personal data.',
239                },
240            },
241        };
242
243        fetch(HUBSPOT_API_URL, {
244            method: 'POST',
245            headers: { 'Content-Type': 'application/json' },
246            body: JSON.stringify(hubspotData),
247        })
248            .then(async (r) => {
249                const data = await r.json().catch(() => ({}));
250                if (!r.ok) {
251                    const msg =
252                        (data && data.message) ||
253                        (data && data.errors && data.errors[0] && data.errors[0].message) ||
254                        'Form submission failed. Please try again.';
255                    throw new Error(msg);
256                }
257                return data;
258            })
259            .then(() => {
260                if (typeof formEl.reset === 'function') formEl.reset();
261                setTimeout(function() {
262                    const thankYouUrl = (typeof hroneBlogForm !== 'undefined' && hroneBlogForm.thankYouUrl)
263                        ? hroneBlogForm.thankYouUrl
264                        : (window.location.origin + '/thankyou');
265                    window.location.href = thankYouUrl;
266                }, 900);
267            })
268            .catch((err) => {
269                // Show error message at top of container if available
270                const container = formEl.closest('.multistepfrorm') || formEl.parentElement;
271                if (container) {
272                    const msg = sanitizePlainText(err?.message || '', 250);
273                    container.insertAdjacentHTML(
274                        'afterbegin',
275                        '<div class="hm-banner-msg"><div class="error-msg"><h2>Error!</h2>' + (msg || 'There was an error submitting your form. Please try again.') + '</div></div>'
276                    );
277                }
278            })
279            .finally(() => {
280                if (btnEl) btnEl.disabled = false;
281                toggleLoader(false);
282            });
283    }
284    function isBlogForm(formEl) {
285        if (!formEl || !(formEl instanceof HTMLFormElement)) return false;
286        // There may be duplicate IDs in templates; accept by id OR class.
287        return formEl.id === 'msformBlog' || formEl.classList.contains('msform-blog') || formEl.classList.contains('mstep-blogform');
288    }
289    // Capture-phase click handler so it still fires even if jQuery stops bubbling.
290    document.addEventListener('click', function(e) {
291        const btn = e.target && e.target.closest ? e.target.closest('#submitBlogform') : null;
292        if (!btn) return;
293        const formEl = btn.closest('form');
294        if (!isBlogForm(formEl)) return;
295        e.preventDefault();
296        submitBlogForm(formEl, btn);
297    }, true);
298
299    // Capture-phase submit handler (covers Enter key / programmatic submit)
300    document.addEventListener('submit', function(e) {
301        const formEl = e.target;
302        if (!isBlogForm(formEl)) return;
303        e.preventDefault();
304        const btn = formEl.querySelector('#submitBlogform');
305        submitBlogForm(formEl, btn);
306    }, true);
307})();
308
309

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.