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.