1/** 2 * Case Study & Blog Form Enhancement 3 * 4 * Adds "Schedule 1:1 call" checkbox functionality: 5 * - Shows/hides phone number field 6 * - Dynamically updates CleverTapDemoForm to MQL or Warm Lead 7 * - Handles phone number input with intlTelInput 8 * - Uses UTM parameters for MQL attribution when checkbox is checked 9 * 10 * MQL Attribution Logic (per markops requirements): 11 * - When checkbox is checked: Form converts from Warm Lead to MQL 12 * - ct_lead_source is set to "MQL" 13 * - MQL attribution fields use UTM parameters (utm_source, utm_medium, utm_campaign) 14 * - If UTM parameters are blank, uses "direct" for source and medium 15 * - Campaign field is empty if UTM campaign is blank 16 * 17 * Applies to: 18 * - All case study forms (detected by URL pattern: /case-study/) 19 * - Blog form with ID: ctForm_3e9d2979-39cb-471a-96f7-419f30729742 20 * - CT University form with ID: ctForm_2d990019-01bb-4c99-8613-f78c3423c452 21 */ 22 23(function() { 24 'use strict'; 25 26 var schedule11TrackingConfig = window.ctSchedule11TrackingConfig && typeof window.ctSchedule11TrackingConfig === 'object' 27 ? Object.assign({}, window.ctSchedule11TrackingConfig) 28 : {}; 29 var schedule11TokenReady = false; 30 var pendingSchedule11Events = []; 31 32 function flushPendingSchedule11Events() { 33 var queued = pendingSchedule11Events.slice(); 34 pendingSchedule11Events = []; 35 for (var i = 0; i < queued.length; i += 1) { 36 trackSchedule11Experiment(queued[i].eventName, queued[i].payload, true); 37 } 38 } 39 40 function refreshSchedule11IngestToken() { 41 var endpoint = schedule11TrackingConfig.ingestConfigEndpoint || '/wp-json/ct/v1/pmm-ingest-config'; 42 if (!window.fetch) { 43 schedule11TokenReady = true; 44 flushPendingSchedule11Events(); 45 return; 46 } 47 fetch(endpoint + '?_ts=' + Date.now(), { 48 method: 'GET', 49 credentials: 'same-origin', 50 cache: 'no-store', 51 headers: { 'Cache-Control': 'no-cache' } 52 }) 53 .then(function (res) { return res.ok ? res.json() : null; }) 54 .then(function (config) { 55 if (config && config.ingestToken) { 56 schedule11TrackingConfig.ingestToken = String(config.ingestToken); 57 if (config.ingestEndpoint) { 58 schedule11TrackingConfig.ingestEndpoint = String(config.ingestEndpoint); 59 } 60 window.ctSchedule11TrackingConfig = schedule11TrackingConfig; 61 } 62 }) 63 .catch(function () {}) 64 .then(function () { 65 schedule11TokenReady = true; 66 flushPendingSchedule11Events(); 67 }); 68 } 69 refreshSchedule11IngestToken(); 70 setTimeout(function () { 71 if (!schedule11TokenReady) { 72 schedule11TokenReady = true; 73 flushPendingSchedule11Events(); 74 } 75 }, 2500); 76 77 function getResourceTypeFromPath(pathname) { 78 var path = (pathname || window.location.pathname || '').toLowerCase(); 79 if (path.indexOf('/ebooks/') > -1 || path.indexOf('/ebook/') > -1) return 'ebook'; 80 if (path.indexOf('/webinars/') > -1 || path.indexOf('/webinar/') > -1) return 'webinar'; 81 if (path.indexOf('/whitepaper') > -1) return 'whitepaper'; 82 if (path.indexOf('/guides/') > -1 || path.indexOf('/guide/') > -1) return 'guide'; 83 if (path.indexOf('/events/') > -1 || path.indexOf('/event/') > -1) return 'event'; 84 if (path.indexOf('/benchmark-report') > -1) return 'benchmark_report'; 85 if (path.indexOf('/case-study/') > -1 || path.indexOf('/case-studies/') > -1) return 'case_study'; 86 if (path.indexOf('/blog/') > -1) return 'blog'; 87 return 'other'; 88 } 89 90 function trackSchedule11Experiment(eventName, payload, forceSend) { 91 if (!eventName || !schedule11TrackingConfig.ingestEndpoint || !schedule11TrackingConfig.ingestToken) { 92 return; 93 } 94 if (!forceSend && !schedule11TokenReady) { 95 pendingSchedule11Events.push({ eventName: eventName, payload: payload || {} }); 96 return; 97 } 98 var trackingSessionId = ''; 99 try { 100 trackingSessionId = sessionStorage.getItem('ct_tracking_session_id') || localStorage.getItem('ct_tracking_session_id') || ''; 101 } catch (e) {} 102 var body = Object.assign({ 103 event: eventName, 104 page_key: getResourceTypeFromPath(), 105 page_path: window.location.pathname, 106 resource_type: getResourceTypeFromPath(), 107 ct_tracking_session_id: trackingSessionId, 108 ingest_token: schedule11TrackingConfig.ingestToken 109 }, payload || {}); 110 111 try { 112 var json = JSON.stringify(body); 113 if (navigator.sendBeacon) { 114 navigator.sendBeacon(schedule11TrackingConfig.ingestEndpoint, new Blob([json], { type: 'application/json' })); 115 return; 116 } 117 if (window.fetch) { 118 fetch(schedule11TrackingConfig.ingestEndpoint, { 119 method: 'POST', 120 headers: { 'Content-Type': 'application/json' }, 121 body: json, 122 credentials: 'same-origin', 123 keepalive: true 124 }); 125 } 126 } catch (e) {} 127 } 128 129 // Function to get UTM parameter from URL or cookies 130 function getUtmParameter(name) { 131 // First check URL parameters
132 var urlParams = new URLSearchParams(window.location.search); 133 var value = urlParams.get(name); 134 if (value) { 135 return decodeURIComponent(value); 136 } 137 138 // Check cookies (stored by ct-hubspot.js) 139 var cookieName = '_mql' + name.substring(3); // utm_source -> _mqlas, utm_medium -> _mqlam, utm_campaign -> _mqlac 140 if (name === 'utm_source') cookieName = '_mqlas'; 141 if (name === 'utm_medium') cookieName = '_mqlam'; 142 if (name === 'utm_campaign') cookieName = '_mqlac'; 143 144 function getCookie(name) { 145 var value = '; ' + document.cookie; 146 var parts = value.split('; ' + name + '='); 147 if (parts.length === 2) return parts.pop().split(';').shift(); 148 return null; 149 } 150 151 value = getCookie(cookieName); 152 return value || null; 153 } 154 155 // Function to get attribution values from UTM parameters 156 function getAttributionFromUtm() { 157 var utmSource = getUtmParameter('utm_source'); 158 var utmMedium = getUtmParameter('utm_medium'); 159 var utmCampaign = getUtmParameter('utm_campaign'); 160 161 // If UTM parameters are blank, use "direct" for source and medium 162 // Per markops requirements: "If it is blank, it will be direct, no value has to be passed" 163 var source = utmSource && utmSource.trim() ? utmSource.trim() : 'direct'; 164 var medium = utmMedium && utmMedium.trim() ? utmMedium.trim() : 'direct'; 165 var campaign = utmCampaign && utmCampaign.trim() ? utmCampaign.trim() : ''; 166 167 return { 168 source: source, 169 medium: medium, 170 campaign: campaign 171 }; 172 } 173 174 // Ensure intlTelInput CSS is loaded 175 function ensureIntlTelInputCSS() { 176 var existingLink = document.querySelector('link[href*="intlTelInput.css"]'); 177 if (existingLink) return; 178 179 var link = document.createElement('link'); 180 link.rel = 'stylesheet'; 181 link.href = 'https://clevertap.com/wp-content/themes/clevertap2023/css/intlTelInput.css?v=20251006'; 182 document.head.appendChild(link); 183 } 184 185 // Inject phone field styles 186 function injectPhoneStyles() { 187 var styleId = 'ct-casestudy-phone-styles'; 188 if (document.getElementById(styleId)) return; 189 190 var style = document.createElement('style'); 191 style.id = styleId; 192 style.textContent = 193 '/* Phone input wrapper */' + 194 '.iti.iti--allow-dropdown.iti--separate-dial-code { width: 100%; }' + 195 '.iti__selected-dial-code { color: #000; }' + 196 '.iti-mobile .iti--container { z-index: 9999999; }' + 197 '/* Phone field container - applies to all forms */' + 198 '[id^="phone_field_container_"] .ctrl-block.phoneg { width: 100%; }' + 199 '[id^="phone_field_container_"] .ctrl-block.phoneg > div { width: 100%; }' + 200 '[id^="phone_"]:not([id*="_hidden"]):not([id*="_e164"]):not([id*="_dial_code"]):not([id*="_country"]) { width: 100%; }' + 201 '/* Hide input labels for case study forms */' + 202 'label.input-label { display: none; }' + 203 '/* Subscription section checkbox alignment */' + 204 '.subscription-section .ctrl-group[id*="schedule_1on1_call"], ' + 205 '.subscription-section .ctrl-group:has([name="schedule_1on1_call"][data-form-id="3e9d2979-39cb-471a-96f7-419f30729742"]) { ' + 206 ' display: flex !important; ' + 207 ' align-items: flex-start !important; ' + 208 ' width: 100% !important; ' + 209 ' margin-left: 0 !important; ' + 210 ' justify-content: flex-start !important; ' + 211 ' flex-wrap: nowrap !important; ' + 212 '}' + 213 '.subscription-section label[for*="schedule_1on1_call"] { ' + 214 ' display: flex !important; ' + 215 ' align-items: center !important; ' + 216 ' gap: 10px !important; ' + 217 ' margin: 0 !important; ' + 218 ' width: auto !important; ' + 219 ' flex-shrink: 0 !important; ' + 220 '}' + 221 '.subscription-section [name="schedule_1on1_call"][data-form-id="3e9d2979-39cb-471a-96f7-419f30729742"] { ' + 222 ' margin: 0 !important; ' + 223 ' flex-shrink: 0 !important; ' + 224 ' min-width: initial !important; ' + 225 ' flex: initial !important; ' + 226 '}' + 227 '/* Subscription section text and phone input styling */' + 228 'section.subscription-section span { ' + 229 ' color: #fff !important; ' + 230 '}' + 231 'section.subscription-section .iti__flag { ' + 232 ' display: none; ' + 233 '}' + 234 '/* Ensure flag shows in popup/modal */' + 235 '#subscribe .iti__flag, ' + 236 '.blog-subscribe-form .iti__flag, ' + 237 '.comp.modal .iti__flag { ' + 238 ' display: block !important; ' + 239 '}' + 240 'section.subscription-section span.iti__country-name { ' + 241 ' color: #000 !important; ' + 242 '}'; 243 document.head.appendChild(style); 244 } 245 246 // Check if current page is a case study page (store result to avoid repeated checks) 247 var _isCaseStudyPage = null; 248 function isCaseStudyPage() { 249 if (_isCaseStudyPage === null) { 250 var pathname = window.location.pathname.toLowerCase(); 251 // Check for case study URL pattern 252 var hasCaseStudyUrl = pathname.indexOf('/case-study/') > -1 || pathname.indexOf('/case-studies/') > -1; 253 254 // Check for case study template class 255 var hasCaseStudyTemplate = document.body.classList.contains('page-template-p-casestudies') || 256 document.querySelector('.page-template-p-casestudies') !== null; 257 258 // Check for case study form with "Loved it" heading 259 var hasCaseStudyForm = false;
260 var forms = document.querySelectorAll('form[id^="ctForm_"]'); 261 for (var i = 0; i < forms.length; i++) { 262 var form = forms[i]; 263 var heading = form.querySelector('h2'); 264 if (heading && (heading.textContent.indexOf('Loved it') > -1 || heading.textContent.indexOf('Get this in your inbox') > -1)) { 265 hasCaseStudyForm = true; 266 break; 267 } 268 } 269 270 _isCaseStudyPage = hasCaseStudyUrl || hasCaseStudyTemplate || hasCaseStudyForm; 271 } 272 return _isCaseStudyPage; 273 } 274 275 // Check if form is a blog form 276 function isBlogForm(formId) { 277 return formId === '3e9d2979-39cb-471a-96f7-419f30729742'; 278 } 279 280 // Check if form is the CT University form 281 function isCtUniversityForm(formId) { 282 return formId === '2d990019-01bb-4c99-8613-f78c3423c452'; 283 } 284 285 /** Same HubSpot form id can exist twice (e.g. blog inline + popup); never use document.getElementById for scoped ids. */ 286 function elByIdIn(root, id) { 287 if (!root || id == null || id === '') return null; 288 var esc = String(id).replace(/\\/g, '\\\\').replace(/"/g, '\\"'); 289 return root.querySelector('[id="' + esc + '"]'); 290 } 291 292 function formHasSchedule11Enhancement(frm, hubspotFormId) { 293 return !!(frm && frm.querySelector('[name="schedule_1on1_call"][data-form-id="' + hubspotFormId + '"]')); 294 } 295 296 // Get HubSpot form UUID from form element (HTML id may be ctForm_<uuid> or ctForm_blog_subscribe_*) 297 function getFormId(form) { 298 if (!form) return null; 299 var hid = form.getAttribute('data-hubspot-form-id'); 300 if (hid) return hid; 301 if (!form.id) return null; 302 var match = form.id.match(/^ctForm_(.+)$/); 303 return match ? match[1] : null; 304 } 305 306 // Get ct_lead_source based on page type 307 function getDefaultLeadSource(formId, isCaseStudy) { 308 if (isCaseStudy) { 309 // For case studies, try to get from page title or use default 310 var pageTitle = document.querySelector('h1'); 311 return pageTitle ? pageTitle.textContent.trim() : 'Case Study'; 312 } else if (isBlogForm(formId)) { 313 return 'Mkt Asset - blogsub'; 314 } else if (isCtUniversityForm(formId)) { 315 return 'CT University'; 316 } 317 return 'Form Submission'; 318 } 319 320 // Wait for DOM and form to be ready 321 function initFormEnhancement() { 322 // Ensure CSS is loaded 323 ensureIntlTelInputCSS(); 324 injectPhoneStyles(); 325 326 var isCaseStudy = isCaseStudyPage(); 327 var formsToEnhance = []; 328 329 // ALWAYS check for blog form with ID ctForm_3e9d2979-39cb-471a-96f7-419f30729742 (regardless of page type) 330 var blogFormId = '3e9d2979-39cb-471a-96f7-419f30729742'; 331 // Find all forms with this ID (can appear in subscribe popup or subscription section) 332 var allBlogForms = document.querySelectorAll('form[data-hubspot-form-id="' + blogFormId + '"]'); 333 allBlogForms.forEach(function(form) { 334 if (!formHasSchedule11Enhancement(form, blogFormId)) { 335 var isInModal = form.closest('.comp.modal, [data-modal], #subscribe') !== null; 336 var isInSubscriptionSection = form.closest('.subscription-section') !== null; 337 formsToEnhance.push({ 338 form: form, 339 formId: blogFormId,
340 isCaseStudy: false, 341 isInModal: isInModal, 342 isInSubscriptionSection: isInSubscriptionSection 343 }); 344 } 345 }); 346 347 // CT University form (supports both data-hubspot-form-id and static ctForm_<id>) 348 var ctUniversityFormId = '2d990019-01bb-4c99-8613-f78c3423c452'; 349 var allCtUniversityForms = document.querySelectorAll( 350 'form[data-hubspot-form-id="' + ctUniversityFormId + '"], form#ctForm_' + ctUniversityFormId 351 ); 352 allCtUniversityForms.forEach(function(form) { 353 if (!formHasSchedule11Enhancement(form, ctUniversityFormId)) { 354 formsToEnhance.push({ 355 form: form, 356 formId: ctUniversityFormId, 357 isCaseStudy: false 358 }); 359 } 360 }); 361 362 // Watch for subscribe modal opening (for blog form) 363 // Check for both #subscribe and .blog-subscribe-form 364 var subscribeModal = document.getElementById('subscribe') || document.querySelector('.blog-subscribe-form'); 365 if (subscribeModal) { 366 var subscribeObserver = new MutationObserver(function(mutations) { 367 mutations.forEach(function(mutation) { 368 if (mutation.type === 'attributes' && mutation.attributeName === 'class') { 369 var modal = mutation.target; 370 if (modal.classList.contains('active') || modal.classList.contains('show') || modal.classList.contains('visible')) { 371 setTimeout(function() { 372 var subscribeForm = modal.querySelector('form[data-hubspot-form-id="' + blogFormId + '"]'); 373 if (subscribeForm && !formHasSchedule11Enhancement(subscribeForm, blogFormId)) { 374 enhanceForm(subscribeForm, blogFormId, false); 375 } 376 }, 200); 377 } 378 } 379 }); 380 }); 381 subscribeObserver.observe(subscribeModal, { attributes: true, attributeFilter: ['class'] }); 382 383 // Check immediately if modal is already open 384 if (subscribeModal.classList.contains('active') || subscribeModal.classList.contains('show') || subscribeModal.classList.contains('visible')) { 385 setTimeout(function() { 386 var subscribeForm = subscribeModal.querySelector('form[data-hubspot-form-id="' + blogFormId + '"]'); 387 if (subscribeForm && !formHasSchedule11Enhancement(subscribeForm, blogFormId)) { 388 enhanceForm(subscribeForm, blogFormId, false); 389 } 390 }, 200); 391 } 392 } 393 394 // Also watch for .blog-subscribe-form specifically (in case it's different from #subscribe) 395 var blogSubscribeForms = document.querySelectorAll('.blog-subscribe-form'); 396 blogSubscribeForms.forEach(function(modal) { 397 if (modal.id !== 'subscribe') { 398 var blogFormObserver = new MutationObserver(function(mutations) { 399 mutations.forEach(function(mutation) { 400 if (mutation.type === 'attributes' && mutation.attributeName === 'class') { 401 var modalEl = mutation.target; 402 if (modalEl.classList.contains('active') || modalEl.classList.contains('show') || modalEl.classList.contains('visible')) { 403 setTimeout(function() { 404 var subscribeForm = modalEl.querySelector('form[data-hubspot-form-id="' + blogFormId + '"]'); 405 if (subscribeForm && !formHasSchedule11Enhancement(subscribeForm, blogFormId)) { 406 enhanceForm(subscribeForm, blogFormId, false); 407 } 408 }, 200); 409 } 410 } 411 }); 412 }); 413 blogFormObserver.observe(modal, { attributes: true, attributeFilter: ['class'] }); 414 415 // Check immediately if modal is already open 416 if (modal.classList.contains('active') || modal.classList.contains('show') || modal.classList.contains('visible')) { 417 setTimeout(function() { 418 var subscribeForm = modal.querySelector('form[data-hubspot-form-id="' + blogFormId + '"]'); 419 if (subscribeForm && !formHasSchedule11Enhancement(subscribeForm, blogFormId)) { 420 enhanceForm(subscribeForm, blogFormId, false); 421 } 422 }, 200); 423 } 424 } 425 }); 426 427 // Find forms to enhance 428 if (isCaseStudy) { 429 // Find all case study forms (any form with ctForm_ prefix on case study pages) 430 // Include forms in modals (even if hidden initially) 431 var allForms = document.querySelectorAll('form[id^="ctForm_"]'); 432 allForms.forEach(function(form) { 433 var formId = getFormId(form); 434 if (formId) { 435 // Check if form is in a modal 436 var isInModal = form.closest('.comp.modal') || form.closest('[data-modal]'); 437 formsToEnhance.push({ 438 form: form, 439 formId: formId, 440 isCaseStudy: true, 441 isInModal: !!isInModal 442 }); 443 } 444 }); 445 446 // Also check for forms that might be added dynamically when modal opens 447 // Listen for modal open events 448 document.addEventListener('click', function(e) { 449 var openModalBtn = e.target.closest('.open-modal, [data-modal], button[data-modal], .open-blog-popup'); 450 if (openModalBtn) { 451 var modalName = openModalBtn.getAttribute('data-modal') || openModalBtn.getAttribute('name'); 452 if (modalName === 'contact' || !modalName) { 453 // Wait a bit for modal to open and form to be visible 454 setTimeout(function() { 455 var modalForms = document.querySelectorAll('.comp.modal[data-modal="contact"] form[id^="ctForm_"], .comp.modal.contact form[id^="ctForm_"], [data-modal="contact"] form[id^="ctForm_"]');
456 modalForms.forEach(function(form) { 457 var formId = getFormId(form); 458 if (formId && !formHasSchedule11Enhancement(form, formId)) { 459 enhanceForm(form, formId, true); 460 } 461 }); 462 }, 400); 463 } else if (modalName === 'subscribe') { 464 // Handle subscribe modal opening 465 setTimeout(function() { 466 var subscribeModal = document.getElementById('subscribe'); 467 if (subscribeModal) { 468 var subscribeForm = subscribeModal.querySelector('form[data-hubspot-form-id="3e9d2979-39cb-471a-96f7-419f30729742"]'); 469 if (subscribeForm && !formHasSchedule11Enhancement(subscribeForm, '3e9d2979-39cb-471a-96f7-419f30729742')) { 470 enhanceForm(subscribeForm, '3e9d2979-39cb-471a-96f7-419f30729742', false); 471 } 472 } 473 }, 400); 474 } 475 } 476 }, true); 477 478 // Use MutationObserver to watch for modal class changes (more reliable) 479 var modalObserver = new MutationObserver(function(mutations) { 480 mutations.forEach(function(mutation) { 481 if (mutation.type === 'attributes' && mutation.attributeName === 'class') { 482 var modal = mutation.target; 483 if (modal.classList.contains('active') || modal.classList.contains('show') || modal.classList.contains('visible')) { 484 setTimeout(function() { 485 var modalForms = modal.querySelectorAll('form[id^="ctForm_"]'); 486 modalForms.forEach(function(form) { 487 var formId = getFormId(form); 488 if (formId && !formHasSchedule11Enhancement(form, formId)) { 489 enhanceForm(form, formId, true); 490 } 491 }); 492 }, 200); 493 } 494 } 495 }); 496 }); 497 498 // Observe all modals on the page (including subscribe modal) 499 var allModals = document.querySelectorAll('.comp.modal, [data-modal], .modal, #subscribe'); 500 allModals.forEach(function(modal) { 501 modalObserver.observe(modal, { attributes: true, attributeFilter: ['class'] }); 502 }); 503 504 // Also watch for subscribe modal specifically 505 var subscribeModal = document.getElementById('subscribe'); 506 if (subscribeModal) { 507 modalObserver.observe(subscribeModal, { attributes: true, attributeFilter: ['class'] }); 508 // Check immediately if already open 509 if (subscribeModal.classList.contains('active') || subscribeModal.classList.contains('show') || subscribeModal.classList.contains('visible')) { 510 setTimeout(function() { 511 var subscribeForm = subscribeModal.querySelector('form[data-hubspot-form-id="3e9d2979-39cb-471a-96f7-419f30729742"]'); 512 if (subscribeForm && !formHasSchedule11Enhancement(subscribeForm, '3e9d2979-39cb-471a-96f7-419f30729742')) { 513 enhanceForm(subscribeForm, '3e9d2979-39cb-471a-96f7-419f30729742', false); 514 } 515 }, 200); 516 } 517 } 518 519 // Also watch for new modals added to DOM 520 var bodyObserver = new MutationObserver(function(mutations) { 521 mutations.forEach(function(mutation) { 522 mutation.addedNodes.forEach(function(node) { 523 if (node.nodeType === 1) { // Element node 524 // Check if it's a modal or contains a modal 525 var modals = node.matches && node.matches('.comp.modal, [data-modal]') 526 ? [node] 527 : node.querySelectorAll ? node.querySelectorAll('.comp.modal, [data-modal]') : []; 528 modals.forEach(function(modal) { 529 modalObserver.observe(modal, { attributes: true, attributeFilter: ['class'] }); 530 // Check for forms immediately 531 var modalForms = modal.querySelectorAll('form[id^="ctForm_"]'); 532 modalForms.forEach(function(form) { 533 var formId = getFormId(form); 534 if (formId && !formHasSchedule11Enhancement(form, formId)) { 535 enhanceForm(form, formId, true); 536 } 537 }); 538 }); 539 } 540 }); 541 }); 542 }); 543 bodyObserver.observe(document.body, { childList: true, subtree: true }); 544 } 545 546 // Watch for subscription section becoming active 547 var subscriptionSection = document.querySelector('.subscription-section'); 548 if (subscriptionSection) { 549 var subscriptionObserver = new MutationObserver(function(mutations) { 550 mutations.forEach(function(mutation) { 551 if (mutation.type === 'attributes' && mutation.attributeName === 'class') { 552 var section = mutation.target; 553 if (section.classList.contains('activeSection')) { 554 setTimeout(function() { 555 var subscriptionForm = section.querySelector('form[data-hubspot-form-id="3e9d2979-39cb-471a-96f7-419f30729742"]'); 556 if (subscriptionForm && !formHasSchedule11Enhancement(subscriptionForm, '3e9d2979-39cb-471a-96f7-419f30729742')) { 557 enhanceForm(subscriptionForm, '3e9d2979-39cb-471a-96f7-419f30729742', false); 558 } 559 }, 200); 560 } 561 } 562 }); 563 }); 564 subscriptionObserver.observe(subscriptionSection, { attributes: true, attributeFilter: ['class'] }); 565 566 // Check immediately if already active 567 if (subscriptionSection.classList.contains('activeSection')) { 568 setTimeout(function() { 569 var subscriptionForm = subscriptionSection.querySelector('form[data-hubspot-form-id="3e9d2979-39cb-471a-96f7-419f30729742"]'); 570 if (subscriptionForm && !formHasSchedule11Enhancement(subscriptionForm, '3e9d2979-39cb-471a-96f7-419f30729742')) { 571 enhanceForm(subscriptionForm, '3e9d2979-39cb-471a-96f7-419f30729742', false); 572 } 573 }, 200); 574 } 575 } 576 577 578 // If no forms found, retry 579 if (formsToEnhance.length === 0) { 580 setTimeout(initFormEnhancement, 200); 581 return; 582 } 583 584 // Enhance each form 585 formsToEnhance.forEach(function(formData) { 586 enhanceForm(formData.form, formData.formId, formData.isCaseStudy); 587 }); 588 } 589 590 // Enhance a single form 591 function enhanceForm(form, formId, isCaseStudy) { 592 if (!form) return; 593 594 // Check if already enhanced 595 if (formHasSchedule11Enhancement(form, formId)) { 596 return; 597 } 598 599 // Check if form is in a modal 600 var modal = form.closest('.comp.modal, [data-modal]'); 601 var isModalHidden = false;
602 603 if (modal) { 604 // Check if modal is hidden 605 var modalStyle = window.getComputedStyle(modal); 606 isModalHidden = ( 607 modal.classList.contains('active') === false && 608 (modal.style.display === 'none' || modalStyle.display === 'none' || modalStyle.visibility === 'hidden') 609 ); 610 611 // If modal might be hidden, set up observer 612 if (isModalHidden) { 613 var observer = new MutationObserver(function(mutations) { 614 var currentStyle = window.getComputedStyle(modal); 615 if (modal.classList.contains('active') || 616 currentStyle.display !== 'none' || 617 currentStyle.visibility !== 'hidden') { 618 // Modal is now visible, enhance the form 619 setTimeout(function() { 620 enhanceFormInternal(form, formId, isCaseStudy); 621 }, 200); 622 observer.disconnect(); 623 } 624 }); 625 observer.observe(modal, { 626 attributes: true, 627 attributeFilter: ['class', 'style'], 628 childList: false, 629 subtree: false 630 }); 631 632 // Also check immediately in case modal is already open 633 setTimeout(function() { 634 var currentStyle = window.getComputedStyle(modal); 635 if (modal.classList.contains('active') || 636 currentStyle.display !== 'none' || 637 currentStyle.visibility !== 'hidden') { 638 enhanceFormInternal(form, formId, isCaseStudy); 639 observer.disconnect(); 640 } 641 }, 100); 642 } 643 } 644 645 // Enhance immediately (works for visible forms and will be called again when modal opens) 646 enhanceFormInternal(form, formId, isCaseStudy); 647 } 648 649 // Internal function to actually enhance the form 650 function enhanceFormInternal(form, formId, isCaseStudy) { 651 if (!form) { 652 console.warn('enhanceFormInternal: form is null for formId:', formId); 653 return; 654 } 655 656 // Double-check if already enhanced 657 if (formHasSchedule11Enhancement(form, formId)) { 658 return; 659 } 660 661 // Debug: uncomment to see form enhancement 662 // console.log('Enhancing form:', formId, 'on case study page:', isCaseStudy); 663 664 // CT University asks for name + work email + optional schedule call (no company field). 665 // Remove company input block before validation can consider it mandatory. 666 if (isCtUniversityForm(formId)) { 667 var companyField = form.querySelector('#company'); 668 if (companyField) { 669 var companyBlock = companyField.closest('.ctrl-block'); 670 if (companyBlock && companyBlock.parentNode) { 671 companyBlock.parentNode.removeChild(companyBlock); 672 } else { 673 companyField.remove(); 674 } 675 } 676 } 677 678 // Find the email field group to insert checkbox after it 679 // Try multiple selectors to find email field 680 var emailInput = form.querySelector('#sdemail') || 681 form.querySelector('input[type="email"]') || 682 form.querySelector('input[name="email"]') || 683 form.querySelector('input[placeholder*="email" i]') || 684 form.querySelector('input[placeholder*="Email" i]') || 685 form.querySelector('input[id*="email" i]'); 686 687 if (!emailInput) { 688 console.warn('Email field not found in form:', formId, 'Form HTML:', form.outerHTML.substring(0, 200)); 689 // Try to find any input field as fallback 690 var firstInput = form.querySelector('input[type="text"]'); 691 if (firstInput) { 692 emailInput = firstInput; 693 console.log('Using first text input as fallback:', firstInput.id || firstInput.name); 694 } else { 695 console.error('No input fields found in form:', formId); 696 return; 697 } 698 } 699 700 // Debug: uncomment to see email field detection 701 // console.log('Found email input:', emailInput.id || emailInput.name || emailInput.placeholder); 702 703 // Find the parent ctrl-group containing the email field 704 var emailGroup = emailInput.closest('.ctrl-group'); 705 if (!emailGroup) { 706 // If no ctrl-group, create one or use the email input's parent 707 emailGroup = emailInput.parentElement; 708 // If parent is not a ctrl-group, wrap in one 709 if (!emailGroup.classList.contains('ctrl-group')) { 710 var wrapper = document.createElement('div'); 711 wrapper.className = 'ctrl-group'; 712 emailGroup.parentNode.insertBefore(wrapper, emailGroup); 713 wrapper.appendChild(emailGroup); 714 emailGroup = wrapper; 715 } 716 } 717 718 // Create checkbox container 719 var checkboxGroup = document.createElement('div'); 720 checkboxGroup.className = 'ctrl-group'; 721 // Check if form is in subscription section and apply specific styling 722 var isInSubscriptionSection = form.closest('.subscription-section') !== null; 723 if (isInSubscriptionSection) { 724 checkboxGroup.style.cssText = 'margin-bottom: 15px; width: 100%; display: flex; align-items: flex-start; justify-content: flex-start; margin-left: 0;'; 725 } else { 726 checkboxGroup.style.cssText = 'margin-bottom: 15px;'; 727 } 728 729 var checkboxBlock = document.createElement('div'); 730 checkboxBlock.className = 'ctrl-block'; 731 732 var checkboxLabel = document.createElement('label'); 733 // Apply specific styling for subscription section 734 if (isInSubscriptionSection) { 735 checkboxLabel.style.cssText = 'display: flex !important; align-items: center !important; gap: 10px !important; cursor: pointer; font-weight: 500; color: #000d26; margin: 0 !important; width: auto !important; flex-shrink: 0 !important;'; 736 } else { 737 checkboxLabel.style.cssText = 'display: flex; align-items: center; gap: 10px; cursor: pointer; font-weight: 500; color: #000d26;'; 738 } 739 740 var checkbox = document.createElement('input'); 741 checkbox.type = 'checkbox'; 742 // Unique id per DOM form: blog inline + popup share the same HubSpot formId (duplicate ids broke label + getElementById targets).
743 var formInstancePart = (form.id || 'noid').replace(/^ctForm_/, '').replace(/[^a-zA-Z0-9_-]/g, '_') || 'noid'; 744 checkbox.id = 'schedule_1on1_call_' + formId.replace(/[^a-zA-Z0-9-]/g, '_') + '_' + formInstancePart; 745 checkbox.name = 'schedule_1on1_call'; 746 checkbox.value = 'Yes'; 747 checkbox.setAttribute('data-form-id', formId); 748 // Apply specific styling for subscription section 749 if (isInSubscriptionSection) { 750 checkbox.style.cssText = 'width: auto !important; margin: 0 !important; cursor: pointer; accent-color: #1076fb; flex-shrink: 0 !important;'; 751 } else { 752 checkbox.style.cssText = 'width: auto; margin: 0; cursor: pointer; accent-color: #1076fb;'; 753 } 754 755 var checkboxText = document.createElement('span'); 756 checkboxText.textContent = isCtUniversityForm(formId) ? 'Schedule a 1:1 demo call' : 'Schedule 1:1 call'; 757 758 checkboxLabel.appendChild(checkbox); 759 checkboxLabel.appendChild(checkboxText); 760 checkboxBlock.appendChild(checkboxLabel); 761 checkboxGroup.appendChild(checkboxBlock); 762 763 trackSchedule11Experiment('schedule_1on1_impression', { 764 form_id: formId, 765 component_name: 'schedule_1on1_checkbox' 766 }); 767 768 // Insert checkbox after email group 769 if (emailGroup && emailGroup.parentNode) { 770 emailGroup.parentNode.insertBefore(checkboxGroup, emailGroup.nextSibling); 771 // Checkbox inserted successfully 772 } else { 773 // Fallback: insert after email input's parent 774 if (emailInput && emailInput.parentNode) { 775 emailInput.parentNode.insertBefore(checkboxGroup, emailInput.nextSibling); 776 } else { 777 // Last resort: append to form 778 form.appendChild(checkboxGroup); 779 } 780 } 781 782 // Create phone field container (initially hidden) - matching exact structure from demo form 783 var phoneGroup = document.createElement('div'); 784 phoneGroup.className = 'ctrl-group'; 785 phoneGroup.id = 'phone_field_container_' + formId; 786 phoneGroup.style.cssText = 'display: none; margin-bottom: 0;'; 787 phoneGroup.setAttribute('data-form-id', formId); 788 789 // Hidden country fields (demo form). Blog footer already has #country + #countrylist â duplicating IDs breaks DOM + queries. 790 var hiddenCountryBlock = document.createElement('div'); 791 hiddenCountryBlock.className = 'ctrl-block'; 792 if (form.querySelector('#country')) { 793 hiddenCountryBlock.style.cssText = 'display: none; height: 0; margin: 0; padding: 0; overflow: hidden; visibility: hidden;'; 794 hiddenCountryBlock.innerHTML = ''; 795 } else { 796 hiddenCountryBlock.style.cssText = 'visibility: hidden; height: 0; margin: 0; padding: 0; overflow: hidden;'; 797 hiddenCountryBlock.innerHTML = '<label class="input-label" for="country">Country</label>' + 798 '<input type="hidden" list="countrylist" tabindex="-1" autocomplete="off" autocapitalize="none" autocorrect="off" spellcheck="false" id="country" placeholder="Country" name="countrylist" class="ctrl" onpaste="return false;" />' + 799 '<input type="hidden" class="country_hidden" tabindex="-1" value="" autocomplete="off" />' + 800 '<datalist type="hidden" id="countrylist" tabindex="-1"></datalist>'; 801 } 802 803 // Phone input block (exact match from demo form) - use unique IDs per form 804 var phoneBlock = document.createElement('div'); 805 phoneBlock.className = 'ctrl-block phoneg'; 806 phoneBlock.innerHTML = '<div>' + 807 '<input id="sdphone" class="ctrl" name="phone" type="tel" inputmode="tel" placeholder="Enter phone number" autocomplete="tel" data-form-id="' + formId + '" />' + 808 '<input id="sdphone_' + formId + '" tabindex="-1" type="tel" inputmode="tel" placeholder="Enter phone number" autocomplete="tel" style="visibility: hidden; height: 0; margin: 0; padding: 0; overflow: hidden;"/>' + 809 '<input type="hidden" id="phone_e164_' + formId + '" name="phone_e164" />' + 810 '<input type="hidden" id="phone_dial_code_' + formId + '" name="phone_dial_code" />' + 811 '<input type="hidden" id="phone_country_iso2_' + formId + '" name="phone_country_iso2" />' + 812 '<input type="hidden" id="phone_country_name_' + formId + '" name="phone_country_name" />' + 813 '<input type="hidden" id="phone_number_country" name="phone_number_country" data-form-id="' + formId + '" />' + 814 '</div>'; 815 816 phoneGroup.appendChild(hiddenCountryBlock); 817 phoneGroup.appendChild(phoneBlock); 818 819 // Insert phone group after checkbox 820 checkboxGroup.parentNode.insertBefore(phoneGroup, checkboxGroup.nextSibling); 821 822 // For subscription section, move the submit button after phone field 823 if (isInSubscriptionSection) { 824 // Use setTimeout to ensure phone group is in DOM first 825 setTimeout(function() { 826 var submitButton = form.querySelector('#schedule_demo, button[name="submit"], button[type="submit"]'); 827 if (submitButton && submitButton.closest('.subscription-section')) { 828 // Check if button is already after phone field 829 var phoneGroupAfter = phoneGroup.nextSibling; 830 var isAlreadyAfter = false;
831 while (phoneGroupAfter) { 832 if (phoneGroupAfter.contains && phoneGroupAfter.contains(submitButton)) { 833 isAlreadyAfter = true; 834 break; 835 } 836 phoneGroupAfter = phoneGroupAfter.nextSibling; 837 } 838 839 if (!isAlreadyAfter && submitButton && submitButton.parentNode) { 840 // Find the button's current parent 841 var buttonParent = submitButton.parentElement; 842 var buttonGroup = buttonParent.closest('.ctrl-group'); 843 844 // If button is in the same flex container as inputs, extract it 845 if (buttonGroup && buttonGroup.style.display === 'flex') { 846 // Remove button from flex container 847 submitButton.parentNode.removeChild(submitButton); 848 849 // Create a new ctrl-group for the button 850 var newButtonGroup = document.createElement('div'); 851 newButtonGroup.className = 'ctrl-group'; 852 newButtonGroup.style.cssText = 'width: 100%; margin-top: 15px;'; 853 newButtonGroup.appendChild(submitButton); 854 855 // Insert new button group after phone group 856 if (phoneGroup.parentNode) { 857 phoneGroup.parentNode.insertBefore(newButtonGroup, phoneGroup.nextSibling); 858 } else { 859 form.appendChild(newButtonGroup); 860 } 861 } else if (buttonGroup && buttonGroup !== checkboxGroup && buttonGroup !== phoneGroup) { 862 // Move the entire button group after phone group 863 if (phoneGroup.parentNode) { 864 phoneGroup.parentNode.insertBefore(buttonGroup, phoneGroup.nextSibling); 865 } 866 } else if (submitButton && submitButton.parentNode) { 867 // Create a new ctrl-group for the button 868 var newButtonGroup = document.createElement('div'); 869 newButtonGroup.className = 'ctrl-group'; 870 newButtonGroup.style.cssText = 'width: 100%; margin-top: 15px;'; 871 872 // Move button to new group 873 submitButton.parentNode.removeChild(submitButton); 874 newButtonGroup.appendChild(submitButton); 875 876 // Insert new button group after phone group 877 if (phoneGroup.parentNode) { 878 phoneGroup.parentNode.insertBefore(newButtonGroup, phoneGroup.nextSibling); 879 } else { 880 form.appendChild(newButtonGroup); 881 } 882 } 883 } 884 } 885 }, 100); 886 } 887 888 // Create hidden inputs for MQL attribution fields and country (for HubSpot POST) 889 var hiddenFieldsContainer = document.createElement('div'); 890 hiddenFieldsContainer.style.cssText = 'display: none;'; 891 892 // Country field (hidden) - unique per form 893 var countryHiddenInput = document.createElement('input'); 894 countryHiddenInput.type = 'hidden'; 895 countryHiddenInput.name = 'country'; 896 countryHiddenInput.id = 'country_hidden_mql_' + formId; 897 countryHiddenInput.value = ''; 898 899 // MQL Attribution fields (hidden) - unique per form 900 var mqlSourceInput = document.createElement('input'); 901 mqlSourceInput.type = 'hidden'; 902 mqlSourceInput.name = 'mql_attribution_source'; 903 mqlSourceInput.id = 'mql_attribution_source_hidden_' + formId; 904 mqlSourceInput.value = ''; 905 906 var mqlMediumInput = document.createElement('input'); 907 mqlMediumInput.type = 'hidden'; 908 mqlMediumInput.name = 'mql_attribution_medium'; 909 mqlMediumInput.id = 'mql_attribution_medium_hidden_' + formId; 910 mqlMediumInput.value = ''; 911 912 var mqlCampaignInput = document.createElement('input'); 913 mqlCampaignInput.type = 'hidden'; 914 mqlCampaignInput.name = 'mql_attribution_campaign'; 915 mqlCampaignInput.id = 'mql_attribution_campaign_hidden_' + formId; 916 mqlCampaignInput.value = ''; 917 918 hiddenFieldsContainer.appendChild(countryHiddenInput); 919 hiddenFieldsContainer.appendChild(mqlSourceInput); 920 hiddenFieldsContainer.appendChild(mqlMediumInput); 921 hiddenFieldsContainer.appendChild(mqlCampaignInput); 922 923 // Insert hidden fields into form 924 form.appendChild(hiddenFieldsContainer); 925 926 // Initialize phone input handler - using exact same technique as demo form 927 var itiInstance = null; 928 var phoneInput = null; 929 var currentFormId = formId; // Store form ID for this form instance 930 var currentIsCaseStudy = isCaseStudy; // Store page type 931 932 // Function to initialize intlTelInput (exact match from demo form) 933 function initPhoneInput() { 934 // Find phone field - use the form we're enhancing, not just any form 935 // ct-hubspot.js expects #sdphone, but we need to find it in the current form 936 var formToSearch = form; // Use the form we're currently enhancing 937 phoneInput = formToSearch ? formToSearch.querySelector('#sdphone[data-form-id="' + currentFormId + '"]') : null; 938 if (!phoneInput) { 939 // Fallback: try to find #sdphone in current form 940 phoneInput = formToSearch ? formToSearch.querySelector('#sdphone') : null; 941 } 942 if (!phoneInput) { 943 // Last fallback: try to find by data-form-id 944 phoneInput = formToSearch ? formToSearch.querySelector('[data-form-id="' + currentFormId + '"]') : null; 945 } 946 if (!phoneInput) return; 947 948 // If already initialized, don't reinitialize 949 if (itiInstance) { 950 // Check if the instance is still valid 951 try { 952 itiInstance.getNumber(); 953 return; // Instance is valid, no need to reinitialize 954 } catch(e) { 955 // Instance is broken, destroy and recreate 956 try { 957 itiInstance.destroy(); 958 } catch(e2) {} 959 itiInstance = null; 960 } 961 } 962 963 // Wait for intlTelInput library to be available 964 if (!window.intlTelInput) { 965 // Retry after a short delay 966 setTimeout(initPhoneInput, 200); 967 return; 968 } 969 970 // Make sure phone input is visible before initializing 971 if (phoneInput.offsetParent === null) { 972 // Element is hidden, wait a bit 973 setTimeout(initPhoneInput, 100); 974 return; 975 } 976 977 try { 978 // After uncheck+destroy, a bare .iti wrapper can remain with
978no valid instance; re-init on that 979 // node breaks the widget. Reuse only when getNumber() works, else unwrap then init fresh. 980 if (phoneInput.closest('.iti')) { 981 var existingIti = phoneInput.intlTelInput || (window.intlTelInput && window.intlTelInput.getInstance && window.intlTelInput.getInstance(phoneInput)); 982 var existingOk = false; 983 if (existingIti) { 984 try { 985 existingIti.getNumber(); 986 existingOk = true; 987 } catch (ex) { 988 try { existingIti.destroy(); } catch (ex2) {} 989 } 990 } 991 if (existingOk) { 992 itiInstance = existingIti; 993 setupPhoneHandlers(); 994 return; 995 } 996 var staleWrap = phoneInput.closest('.iti'); 997 if (staleWrap && staleWrap.parentNode) { 998 staleWrap.parentNode.insertBefore(phoneInput, staleWrap); 999 staleWrap.parentNode.removeChild(staleWrap); 1000 } 1001 } 1002 1003 itiInstance = window.intlTelInput(phoneInput, { 1004 initialCountry: 'auto', 1005 separateDialCode: true, 1006 utilsScript: 'https://clevertap.com/wp-content/themes/clevertap2023/js/utils.js?v=20251006', 1007 geoIpLookup: function (cb) { 1008 fetch('https://ipinfo.io/json', { cache: 'no-store' }) 1009 .then((r) => r.json()) 1010 .then((d) => cb((d && d.country) || 'IN')) 1011 .catch(() => cb('IN')); 1012 } 1013 }); 1014 1015 setupPhoneHandlers(); 1016 } catch(e) { 1017 console.error('Error initializing intlTelInput:', e); 1018 itiInstance = null; 1019 } 1020 } 1021 1022 // Setup phone field handlers (exact match from demo form) 1023 function setupPhoneHandlers() { 1024 if (!phoneInput || !itiInstance) { 1025 console.warn('setupPhoneHandlers: phoneInput or itiInstance not available'); 1026 return; 1027 } 1028 1029 // Verify itiInstance is still valid 1030 try { 1031 itiInstance.getNumber(); 1032 } catch(e) { 1033 console.error('itiInstance is invalid:', e); 1034 itiInstance = null; 1035 return; 1036 } 1037 1038 // ---- grab all fields we may need to touch ---- 1039 // Use the form we're currently enhancing, not just any form 1040 var formToSearch = phoneInput.form || form; // Prefer phoneInput's form, fallback to current form 1041 if (!formToSearch) { 1042 formToSearch = document.querySelector('form[data-hubspot-form-id="' + currentFormId + '"]') || 1043 document.getElementById('ctForm_' + currentFormId); 1044 } 1045 // Find phone_number_country field - ct-hubspot.js looks for #phone_number_country 1046 var hsISD = formToSearch ? formToSearch.querySelector('#phone_number_country') : null; 1047 if (!hsISD) { 1048 // Fallback: try to find by name attribute 1049 hsISD = form ? form.querySelector('input[name="phone_number_country"]') : document.querySelector('input[name="phone_number_country"]'); 1050 } 1051 var countryInput = formToSearch ? formToSearch.querySelector('#country') : null; 1052 var countryHidden = formToSearch ? formToSearch.querySelector('.country_hidden') : null; 1053 // Find phone field - ct-hubspot.js expects #sdphone 1054 var visiblePhone = formToSearch ? formToSearch.querySelector('#sdphone[data-form-id="' + currentFormId + '"]') : null; 1055 if (!visiblePhone) { 1056 // Fallback: try to find #sdphone in current form 1057 visiblePhone = formToSearch ? formToSearch.querySelector('#sdphone') : null; 1058 } 1059 if (!visiblePhone) { 1060 // Last fallback: try to find by data-form-id 1061 visiblePhone = formToSearch ? formToSearch.querySelector('[data-form-id="' + currentFormId + '"]') : null; 1062 } 1063 var mirrorPhone = elByIdIn(formToSearch, 'sdphone_' + currentFormId); 1064 1065 function writeAll() { 1066 if (!itiInstance || !phoneInput) { 1067 console.warn('writeAll() skipped - itiInstance or phoneInput not available'); 1068 return; 1069 } 1070 1071 var c, dial, name, e164; 1072 try { 1073 c = itiInstance.getSelectedCountryData() || {}; 1074 dial = c.dialCode ? '+' + c.dialCode : ''; 1075 name = (c.name || '').replace(/\s*\(.*\)\s*$/, ''); // strip local-language suffix 1076 e164 = itiInstance.getNumber(); // +<code><number> 1077 1078 var rawVis = (phoneInput.value || '').trim(); 1079 var natDigits = rawVis.replace(/\D/g, ''); 1080 var dcDigits = (c.dialCode || '').replace(/\D/g, ''); 1081 1082 if (!e164 && rawVis.indexOf('+') === 0) { 1083 e164 = rawVis.replace(/\s/g, ''); 1084 } 1085 if (!e164 && natDigits && dcDigits) { 1086 e164 = '+' + dcDigits + natDigits; 1087 } 1088 if (!e164 && rawVis) { 1089 e164 = rawVis; 1090 } 1091 1092 e164 = (e164 && String(e164).trim()) ? String(e164).trim() : ''; 1093 } catch(e) { 1094 console.error('Error in writeAll:', e); 1095 return; 1096 } 1097
1098 // 1) HubSpot property: Phone Number Country -> "+ISD|Country" 1099 if (hsISD) { 1100 var combined = dial && name ? dial + '|' + name : dial || name || ''; 1101 hsISD.value = combined; 1102 hsISD.setAttribute('value', combined); 1103 } else { 1104 console.warn('writeAll() - hsISD (phone_number_country) field not found'); 1105 } 1106 1107 // 2) Optional: keep readable country elsewhere 1108 if (countryInput) { 1109 countryInput.value = name; 1110 countryInput.setAttribute('value', name); 1111 } 1112 if (countryHidden) countryHidden.value = name; 1113 1114 // Update hidden country field for MQL submission (with form ID) 1115 var countryHiddenMQL = elByIdIn(form, 'country_hidden_mql_' + currentFormId); 1116 if (countryHiddenMQL) { 1117 countryHiddenMQL.value = name; 1118 countryHiddenMQL.setAttribute('value', name); 1119 } 1120 1121 // 3) With separateDialCode, assigning phoneInput.value = "+91..." corrupts ITI; the next 1122 // getNumber() is empty and later writeAll() blanks the field. Use setNumber() instead. 1123 // Never write '' onto the visible field here â that mimics the same failure mode. 1124 if (e164) { 1125 try { 1126 if (typeof itiInstance.setNumber === 'function') { 1127 itiInstance.setNumber(e164); 1128 } else { 1129 phoneInput.value = e164; 1130 } 1131 } catch (eSet) { 1132 try { 1133 phoneInput.value = e164; 1134 } catch (e2) {} 1135 } 1136 phoneInput.setAttribute('value', phoneInput.value || e164); 1137 } 1138 1139 form.querySelectorAll('input[name="phone"]').forEach(function (el) { 1140 if (el === phoneInput) return; 1141 if (el.id === 'sdphone_' + currentFormId) return; 1142 if (e164) { 1143 el.value = e164; 1144 el.setAttribute('value', e164); 1145 } 1146 }); 1147 1148 // 4) Fill optional breakout fields if present 1149 var dEl = elByIdIn(form, 'phone_dial_code_' + currentFormId); 1150 var isoEl = elByIdIn(form, 'phone_country_iso2_' + currentFormId); 1151 var nmEl = elByIdIn(form, 'phone_country_name_' + currentFormId); 1152 var eEl = elByIdIn(form, 'phone_e164_' + currentFormId); 1153 if (dEl) { 1154 dEl.value = dial; 1155 dEl.setAttribute('value', dial); 1156 } 1157 if (isoEl) { 1158 isoEl.value = (c.iso2 || '').toUpperCase(); 1159 isoEl.setAttribute('value', isoEl.value); 1160 } 1161 if (nmEl) { 1162 nmEl.value = name; 1163 nmEl.setAttribute('value', name); 1164 } 1165 if (eEl && e164) { 1166 eEl.value = e164; 1167 eEl.setAttribute('value', e164); 1168 } 1169 } 1170 1171 // Bind submit/blur/mirror only once per form â re-init after Schedule 1:1 uncheck would stack 1172 // duplicate handlers and destabilize intl-tel-input; only writeAll() needs to run again. 1173 if (form.getAttribute('data-ct-phone-hooks') === '1') { 1174 try { 1175 writeAll(); 1176 } catch (eRe) {} 1177 return; 1178 } 1179 form.setAttribute('data-ct-phone-hooks', '1'); 1180 1181 if (visiblePhone && mirrorPhone) { 1182 mirrorPhone.removeAttribute('name'); 1183 var syncMirror = function () { 1184 mirrorPhone.value = visiblePhone.value; 1185 }; 1186 ['input', 'change', 'blur'].forEach(function (ev) { 1187 visiblePhone.addEventListener(ev, syncMirror); 1188 }); 1189 } 1190 1191 // initial populate + react to country change / blur 1192 try { 1193 writeAll(); 1194 } catch(e) { 1195 console.error('Error in initial writeAll:', e); 1196 } 1197 1198 phoneInput.addEventListener('countrychange', function() { 1199 try { 1200 writeAll(); 1201 // Update country hidden field for MQL when country changes 1202 var countryHiddenMQL = elByIdIn(form, 'country_hidden_mql_' + currentFormId); 1203 if (countryHiddenMQL && checkbox && checkbox.checked && itiInstance) { 1204 var c = itiInstance.getSelectedCountryData() || {}; 1205 var name = (c.name || '').replace(/\s*\(.*\)\s*$/, ''); 1206 if (name) { 1207 countryHiddenMQL.value = name; 1208 countryHiddenMQL.setAttribute('value', name); 1209 } 1210 } 1211 } catch(e) { 1212 console.error('Error in countrychange handler:', e); 1213 } 1214 }); 1215 phoneInput.addEventListener('blur', function() { 1216 try { 1217 writeAll(); 1218 // Update country hidden field for MQL on blur 1219 var countryHiddenMQL = elByIdIn(form, 'country_hidden_mql_' + currentFormId); 1220 if (countryHiddenMQL && checkbox && checkbox.checked && itiInstance) { 1221 var c = itiInstance.getSelectedCountryData() || {}; 1222 var name = (c.name || '').replace(/\s*\(.*\)\s*$/, ''); 1223 if (name) { 1224 countryHiddenMQL.value = name; 1225 countryHiddenMQL.setAttribute('value', name); 1226 } 1227 } 1228 } catch(e) { 1229 console.error('Error in blur handler:', e); 1230 } 1231 }); 1232 1233 function getResolvedPhoneValue() { 1234 var formToSearch = form; 1235 var currentPhone = formToSearch ? formToSearch.querySelector('#sdphone[data-form-id="' + currentFormId + '"]') : null; 1236 if (!currentPhone) { 1237 currentPhone = formToSearch ? formToSearch.querySelector('#sdphone') : null; 1238 } 1239 if (!currentPhone) { 1240 return ''; 1241 } 1242 1243 var currentValue = (currentPhone.value || '').trim(); 1244 if (currentValue) { 1245 return currentValue.replace(/\s/g, ''); 1246 } 1247 1248 var phoneE164 = elByIdIn(formToSearch, 'phone_e164_' + currentFormId); 1249 if (phoneE164 && phoneE164.value && phoneE164.value.trim()) { 1250 return phoneE164.value.trim(); 1251 } 1252 return ''; 1253 } 1254 1255 function shouldSetMqlLead() { 1256 return !!(checkbox && checkbox.checked && getResolvedPhoneValue()); 1257 } 1258 1259 // before any submission/CTA click, rewrite again (covers AJAX flows) 1260 function beforeSubmit() { 1261 var isChecked = !!(checkbox && checkbox.checked); 1262 var phoneVal = ''; 1263 if (itiInstance && phoneInput) { 1264 try { 1265 phoneVal = itiInstance.getNumber() || phoneInput.value || ''; 1266 } catch (ePhone) { 1267 phoneVal = phoneInput.value || ''; 1268 } 1269 } else if (phoneInput) { 1270 phoneVal = phoneInput.value || ''; 1271 } 1272 var hasPhone = !!(phoneVal && String(phoneVal).trim()); 1273 var keepPhoneForSubmit = isChecked || hasPhone; 1274 var leadType = (isChecked && hasPhone) ? 'mql' : 'warm'; 1275 trackSchedule11Experiment('schedule_1on1_form_submit', { 1276 form_id: currentFormId, 1277 schedule_1on1_checked: isChecked, 1278 has_phone: hasPhone, 1279 lead_type: leadType, 1280 component_name: 'schedule_1on1_checkbox' 1281 }); 1282 var keepPhoneForSubmit = isChecked || hasPhone; 1283 // If checkbox is unchecked, ensure phone fields are empty and not required for submission 1284 // This must run FIRST before any validation 1285 if (!keepPhoneForSubmit) { 1286 // Use the current form, not just any form 1287 var formToSearch = form; // Use the form we're currently enhancing 1288 var phoneEl = formToSearch ? formToSearch.querySelector('#sdphone[data-form-id="' + currentFormId + '"]') : null; 1289 if (!phoneEl) { 1290 phoneEl = formToSearch ? formToSearch.querySelector('#sdphone') : null; 1291 } 1292 var sdphoneEl = elByIdIn(formToSearch, 'sdphone_' + currentFormId); 1293 1294 if (phoneEl) { 1295 // Clear phone value 1296 phoneEl.value = ''; 1297 // Remove name attribute so it's not submitted and not val
1297idated 1298 phoneEl.removeAttribute('name'); 1299 // Mark as optional 1300 phoneEl.setAttribute('data-optional', 'true'); 1301 // Remove required attribute if present 1302 phoneEl.removeAttribute('required'); 1303 phoneEl.removeAttribute('aria-required'); 1304 // Remove validation error classes and styles 1305 phoneEl.classList.remove('error', 'invalid', 'required', 'errorMsg'); 1306 phoneEl.style.border = ''; 1307 // DO NOT hide with display:none here - the phoneGroup container is already hidden by togglePhoneField 1308 // Just ensure it's not visible to validation 1309 } 1310 if (sdphoneEl) { 1311 sdphoneEl.value = ''; 1312 sdphoneEl.removeAttribute('name'); 1313 sdphoneEl.setAttribute('data-optional', 'true'); 1314 } 1315 1316 // Also clear phone_number_country field 1317 var phoneNumberCountry = formToSearch ? formToSearch.querySelector('#phone_number_country') : null; 1318 if (phoneNumberCountry) { 1319 phoneNumberCountry.value = ''; 1320 phoneNumberCountry.removeAttribute('name'); 1321 } 1322 1323 // Hide any phone validation error messages 1324 var phoneErrorMsg = formToSearch ? formToSearch.querySelector('.ctFrmErrorMsg[data-id*="phone"], .error-msg-phone, [data-error="phone"]') : null; 1325 if (phoneErrorMsg) { 1326 phoneErrorMsg.style.display = 'none'; 1327 } 1328 1329 // Use jQuery to remove error styling if jQuery is available 1330 if (typeof $ !== 'undefined' && phoneEl) { 1331 $(phoneEl).css('border', '').removeClass('errorMsg'); 1332 } 1333 } 1334 1335 // Only call writeAll() if phone input is initialized (checkbox is checked) 1336 if (itiInstance && phoneInput && checkbox && checkbox.checked) { 1337 try { 1338 // Ensure phone field has name attribute before writing 1339 if (phoneInput && !phoneInput.getAttribute('name')) { 1340 phoneInput.setAttribute('name', 'phone'); 1341 } 1342 // Restore display if it was hidden 1343 if (phoneInput.getAttribute('data-original-display')) { 1344 phoneInput.style.display = phoneInput.getAttribute('data-original-display'); 1345 phoneInput.removeAttribute('data-original-display'); 1346 } 1347 writeAll(); 1348 } catch(e) { 1349 console.error('Error in beforeSubmit writeAll:', e); 1350 } 1351 } 1352 1353 // Ensure country and MQL fields are set before submission if phone is provided. 1354 if (shouldSetMqlLead()) { 1355 // Call writeAll() again to ensure phone and country are set 1356 try { 1357 writeAll(); 1358 } catch(e) { 1359 console.error('Error in beforeSubmit writeAll (second call):', e); 1360 } 1361 // Ensure CleverTapDemoForm exists and has correct formId 1362 if (typeof window.CleverTapDemoForm === 'undefined') { 1363 window.CleverTapDemoForm = { 1364 formId: currentFormId, 1365 block_free_email: false, 1366 form_prop: {} 1367 }; 1368 } 1369 1370 // Update formId to match current form 1371 window.CleverTapDemoForm.formId = currentFormId; 1372 1373 // Ensure form_prop exists 1374 if (!window.CleverTapDemoForm.form_prop) { 1375 window.CleverTapDemoForm.form_prop = {}; 1376 } 1377 1378 // Set MQL properties - MUST be set before submission 1379 // STRICTLY set ct_lead_source to 'MQL' for all forms when checkbox is checked 1380 window.CleverTapDemoForm.form_prop.lifecyclestage = 'marketingqualifiedlead'; 1381 window.CleverTapDemoForm.form_prop.ct_lead_source = 'MQL'; 1382 1383 // Force set ct_lead_source to MQL (override any existing value) 1384 Object.defineProperty(window.CleverTapDemoForm.form_prop, 'ct_lead_source', { 1385 value: 'MQL', 1386 writable: true, 1387 configurable: true, 1388 enumerable: true 1389 }); 1390 1391 var countryHiddenMQL = elByIdIn(form, 'country_hidden_mql_' + currentFormId); 1392 if (countryHiddenMQL && itiInstance) { 1393 var c = itiInstance.getSelectedCountryData() || {}; 1394 var name = (c.name || '').replace(/\s*\(.*\)\s*$/, ''); 1395 if (name) { 1396 countryHiddenMQL.value = name; 1397 countryHiddenMQL.setAttribute('value', name); 1398 } 1399 } 1400 1401 // Get UTM attribution values 1402 var attribution = getAttributionFromUtm(); 1403 1404 // Ensure MQL attribution fields are set with UTM values
1405 var mqlSourceInput = elByIdIn(form, 'mql_attribution_source_hidden_' + currentFormId); 1406 var mqlMediumInput = elByIdIn(form, 'mql_attribution_medium_hidden_' + currentFormId); 1407 var mqlCampaignInput = elByIdIn(form, 'mql_attribution_campaign_hidden_' + currentFormId); 1408 1409 if (mqlSourceInput) { 1410 mqlSourceInput.value = attribution.source; 1411 mqlSourceInput.setAttribute('value', attribution.source); 1412 } 1413 if (mqlMediumInput) { 1414 mqlMediumInput.value = attribution.medium; 1415 mqlMediumInput.setAttribute('value', attribution.medium); 1416 } 1417 if (mqlCampaignInput) { 1418 mqlCampaignInput.value = attribution.campaign; 1419 mqlCampaignInput.setAttribute('value', attribution.campaign); 1420 } 1421 1422 // Update form_prop with UTM values - STRICTLY ensure ct_lead_source is MQL 1423 window.CleverTapDemoForm.form_prop.ct_lead_source = 'MQL'; 1424 // Force set to ensure it's not overwritten 1425 Object.defineProperty(window.CleverTapDemoForm.form_prop, 'ct_lead_source', { 1426 value: 'MQL', 1427 writable: true, 1428 configurable: true, 1429 enumerable: true 1430 }); 1431 window.CleverTapDemoForm.form_prop.mql_attribution_source = attribution.source; 1432 window.CleverTapDemoForm.form_prop.mql_attribution_medium = attribution.medium; 1433 window.CleverTapDemoForm.form_prop.mql_attribution_campaign = attribution.campaign; 1434 1435 // For case studies, ensure created_campaign_id is set 1436 if (currentIsCaseStudy && !window.CleverTapDemoForm.form_prop.created_campaign_id) { 1437 window.CleverTapDemoForm.form_prop.created_campaign_id = '701OX00000kHmz3YAC'; 1438 } 1439 1440 console.log('MQL Form Config Before Submit:', { 1441 formId: window.CleverTapDemoForm.formId, 1442 ct_lead_source: window.CleverTapDemoForm.form_prop.ct_lead_source, 1443 lifecyclestage: window.CleverTapDemoForm.form_prop.lifecyclestage, 1444 mql_attribution: { 1445 source: window.CleverTapDemoForm.form_prop.mql_attribution_source, 1446 medium: window.CleverTapDemoForm.form_prop.mql_attribution_medium, 1447 campaign: window.CleverTapDemoForm.form_prop.mql_attribution_campaign 1448 } 1449 }); 1450 } else { 1451 // If checkbox is unchecked, ensure MQL properties are removed 1452 if (window.CleverTapDemoForm && window.CleverTapDemoForm.form_prop) { 1453 delete window.CleverTapDemoForm.form_prop.lifecyclestage; 1454 delete window.CleverTapDemoForm.form_prop.mql_attribution_source; 1455 delete window.CleverTapDemoForm.form_prop.mql_attribution_medium; 1456 delete window.CleverTapDemoForm.form_prop.mql_attribution_campaign; 1457 // Restore default ct_lead_source 1458 window.CleverTapDemoForm.form_prop.ct_lead_source = getDefaultLeadSource(currentFormId, currentIsCaseStudy); 1459 } 1460 } 1461 } 1462 form && form.addEventListener('submit', beforeSubmit, true); 1463 // Use form-specific button selector to avoid conflicts with other forms 1464 var formButtons = form.querySelectorAll('button[type="submit"], button[name="submit"], #schedule_demo, .c-btn[type="button"]'); 1465 formButtons.forEach(function(btn) { 1466 btn.addEventListener('click', function(e) { 1467 // Check if this button belongs to our form 1468 if (btn.closest('form') === form) { 1469 try { 1470 beforeSubmit(); 1471 } catch (err) { 1472 console.error('Schedule 1:1 beforeSubmit failed:', err); 1473 } 1474 } 1475 }, true); 1476 }); 1477 1478 } 1479 1480 // Function to update CleverTapDemoForm config and hidden fields 1481 function updateFormConfig() { 1482 function shouldSetMqlLeadForCurrentForm() { 1483 if (!(checkbox && checkbox.checked)) { 1484 return false; 1485 } 1486 var formToSearch = form; 1487 var phoneInputEl = formToSearch ? formToSearch.querySelector('#sdphone[data-form-id="' + currentFormId + '"]') : null; 1488 if (!phoneInputEl) { 1489 phoneInputEl = formToSearch ? formToSearch.querySelector('#sdphone') : null; 1490 } 1491 var phoneValue = phoneInputEl && phoneInputEl.value ? String(phoneInputEl.value).trim() : ''; 1492 if (!phoneValue) { 1493 var phoneE164El = elByIdIn(formToSearch, 'phone_e164_' + currentFormId); 1494 phoneValue = phoneE164El && phoneE164El.value ? String(phoneE164El.value).trim() : ''; 1495 } 1496 return !!phoneValue; 1497 } 1498 1499 // Ensure CleverTapDemoForm exists and has correct formId 1500 // Note: If CleverTapDemoForm is already defined (e.g., in page template), use it 1501 // Otherwise, create a new one 1502 if (typeof window.CleverTapDemoForm === 'undefined') { 1503 window.CleverTapDemoForm = { 1504 formId: currentFormId, 1505 block_free_email: false, 1506 form_prop: {} 1507 }; 1508 } 1509 1510 // Update formId to match current form (important for case study pages with dynamic forms) 1511 window.CleverTapDemoForm.formId = currentFormId; 1512 1513 // Ensure form_prop exists (preserve existing values from page template) 1514 if (!window.CleverTapDemoForm.form_prop) { 1515 window.CleverTapDemoForm.form_prop = {}; 1516 } 1517
1518 // Set default ct_lead_source if not already set (preserve if already set in template) 1519 if (!window.CleverTapDemoForm.form_prop.ct_lead_source) { 1520 window.CleverTapDemoForm.form_prop.ct_lead_source = getDefaultLeadSource(currentFormId, currentIsCaseStudy); 1521 } 1522 1523 // Store original lead source for blog forms to restore when checkbox is unchecked 1524 if (isBlogForm(currentFormId) && !window.CleverTapDemoForm._originalLeadSource) { 1525 window.CleverTapDemoForm._originalLeadSource = window.CleverTapDemoForm.form_prop.ct_lead_source || 'Mkt Asset - blogsub'; 1526 } 1527 1528 // For case studies, set created_campaign_id if not set 1529 if (currentIsCaseStudy && !window.CleverTapDemoForm.form_prop.created_campaign_id) { 1530 window.CleverTapDemoForm.form_prop.created_campaign_id = '701OX00000kHmz3YAC'; 1531 } 1532 1533 // For blog forms, ensure block_free_email is set if not already 1534 if (isBlogForm(currentFormId) && typeof window.CleverTapDemoForm.block_free_email === 'undefined') { 1535 window.CleverTapDemoForm.block_free_email = true; 1536 } 1537 1538 // Get hidden input fields (scoped to this form â same HubSpot id can exist twice on the page) 1539 var countryHiddenMQL = elByIdIn(form, 'country_hidden_mql_' + currentFormId); 1540 var mqlSourceInput = elByIdIn(form, 'mql_attribution_source_hidden_' + currentFormId); 1541 var mqlMediumInput = elByIdIn(form, 'mql_attribution_medium_hidden_' + currentFormId); 1542 var mqlCampaignInput = elByIdIn(form, 'mql_attribution_campaign_hidden_' + currentFormId); 1543 1544 if (shouldSetMqlLeadForCurrentForm()) { 1545 // MQL Configuration - merge with existing props 1546 window.CleverTapDemoForm.form_prop.lifecyclestage = 'marketingqualifiedlead'; 1547 1548 // STRICTLY set ct_lead_source to 'MQL' for all forms (case study and blog) 1549 window.CleverTapDemoForm.form_prop.ct_lead_source = 'MQL'; 1550 // Force set to ensure it's not overwritten 1551 Object.defineProperty(window.CleverTapDemoForm.form_prop, 'ct_lead_source', { 1552 value: 'MQL', 1553 writable: true, 1554 configurable: true, 1555 enumerable: true 1556 }); 1557 1558 console.log('Checkbox checked - STRICTLY Setting ct_lead_source to MQL:', window.CleverTapDemoForm.form_prop); 1559 1560 // Get attribution from UTM parameters 1561 var attribution = getAttributionFromUtm(); 1562 1563 // Use UTM values for MQL attribution (or "direct" if blank) 1564 // Instead of warm lead attribution, use MQL attribution fields 1565 window.CleverTapDemoForm.form_prop.mql_attribution_source = attribution.source; 1566 window.CleverTapDemoForm.form_prop.mql_attribution_medium = attribution.medium; 1567 window.CleverTapDemoForm.form_prop.mql_attribution_campaign = attribution.campaign; 1568 1569 // Update hidden input fields for POST submission 1570 if (mqlSourceInput) { 1571 mqlSourceInput.value = attribution.source; 1572 mqlSourceInput.setAttribute('value', attribution.source); 1573 } 1574 if (mqlMediumInput) { 1575 mqlMediumInput.value = attribution.medium; 1576 mqlMediumInput.setAttribute('value', attribution.medium); 1577 } 1578 if (mqlCampaignInput) { 1579 mqlCampaignInput.value = attribution.campaign; 1580 mqlCampaignInput.setAttribute('value', attribution.campaign); 1581 } 1582 1583 // Update country if phone field is already initialized 1584 if (itiInstance && countryHiddenMQL) { 1585 var c = itiInstance.getSelectedCountryData() || {}; 1586 var name = (c.name || '').replace(/\s*\(.*\)\s*$/, ''); 1587 if (name) { 1588 countryHiddenMQL.value = name; 1589 countryHiddenMQL.setAttribute('value', name); 1590 } 1591 } 1592 } else { 1593 // Warm Lead Configuration - remove MQL properties but keep others 1594 delete window.CleverTapDemoForm.form_prop.lifecyclestage; 1595 delete window.CleverTapDemoForm.form_prop.mql_attribution_source; 1596 delete window.CleverTapDemoForm.form_prop.mql_attribution_medium; 1597 delete window.CleverTapDemoForm.form_prop.mql_attribution_campaign; 1598 1599 // Restore original ct_lead_source for warm lead 1600 // For blog forms, preserve original "Mkt Asset - blogsub" if it was set, otherwise use default 1601 if (isBlogForm(currentFormId)) { 1602 // Check if original lead source was preserved or use default 1603 var originalLeadSource = window.CleverTapDemoForm._originalLeadSource || 'Mkt Asset - blogsub'; 1604 window.CleverTapDemoForm.form_prop.ct_lead_source = originalLeadSource; 1605 } else { 1606 window.CleverTapDemoForm.form_prop.ct_lead_source = getDefaultLeadSource(currentFormId, currentIsCaseStudy); 1607 } 1608 1609 console.log('Checkbox unchecked - Setting ct_lead_source to warm lead:', window.CleverTapDemoForm.form_prop.ct_lead_source); 1610 1611 // Clear hidden input fields
1612 if (mqlSourceInput) { 1613 mqlSourceInput.value = ''; 1614 mqlSourceInput.removeAttribute('value'); 1615 } 1616 if (mqlMediumInput) { 1617 mqlMediumInput.value = ''; 1618 mqlMediumInput.removeAttribute('value'); 1619 } 1620 if (mqlCampaignInput) { 1621 mqlCampaignInput.value = ''; 1622 mqlCampaignInput.removeAttribute('value'); 1623 } 1624 if (countryHiddenMQL) { 1625 countryHiddenMQL.value = ''; 1626 countryHiddenMQL.removeAttribute('value'); 1627 } 1628 } 1629 } 1630 1631 // Toggle phone field visibility 1632 function togglePhoneField() { 1633 if (checkbox.checked) { 1634 phoneGroup.style.display = 'block'; 1635 // Restore phone field names for submission 1636 // Use the current form, not just any form 1637 var formToSearch = form; // Use the form we're currently enhancing 1638 var phoneEl = formToSearch ? formToSearch.querySelector('#sdphone[data-form-id="' + currentFormId + '"]') : null; 1639 if (!phoneEl) { 1640 phoneEl = formToSearch ? formToSearch.querySelector('#sdphone') : null; 1641 } 1642 var sdphoneEl = elByIdIn(formToSearch, 'sdphone_' + currentFormId); 1643 if (phoneEl) { 1644 phoneEl.setAttribute('name', 'phone'); 1645 phoneEl.removeAttribute('data-optional'); 1646 } 1647 if (sdphoneEl) { 1648 // sdphone is a mirror field, keep it without name to avoid duplicate 1649 sdphoneEl.removeAttribute('data-optional'); 1650 } 1651 1652 // For subscription section, ensure button is after phone field 1653 if (isBlogForm(currentFormId) && form && form.closest('.subscription-section')) { 1654 setTimeout(function() { 1655 var submitButton = form.querySelector('#schedule_demo, button[name="submit"], button[type="submit"]'); 1656 var phoneGroupEl = phoneGroup; 1657 if (submitButton && phoneGroupEl && phoneGroupEl.style.display !== 'none') { 1658 // Check if button is already after phone field 1659 var isAfterPhone = false; 1660 var nextSibling = phoneGroupEl.nextSibling; 1661 while (nextSibling) { 1662 if (nextSibling.contains && nextSibling.contains(submitButton)) { 1663 isAfterPhone = true; 1664 break; 1665 } 1666 nextSibling = nextSibling.nextSibling; 1667 } 1668 1669 if (!isAfterPhone && submitButton.parentNode) { 1670 var buttonParent = submitButton.parentElement; 1671 var buttonGroup = buttonParent.closest('.ctrl-group'); 1672 1673 // Extract button from its current location 1674 if (buttonGroup && buttonGroup.style.display === 'flex') { 1675 submitButton.parentNode.removeChild(submitButton); 1676 1677 // Create new button group if needed 1678 var newButtonGroup = document.createElement('div'); 1679 newButtonGroup.className = 'ctrl-group'; 1680 newButtonGroup.style.cssText = 'width: 100%; margin-top: 15px;'; 1681 newButtonGroup.appendChild(submitButton); 1682 1683 // Insert after phone group 1684 if (phoneGroupEl.parentNode) { 1685 phoneGroupEl.parentNode.insertBefore(newButtonGroup, phoneGroupEl.nextSibling); 1686 } 1687 } else if (buttonGroup && buttonGroup !== phoneGroupEl) { 1688 // Move entire button group 1689 if (phoneGroupEl.parentNode) { 1690 phoneGroupEl.parentNode.insertBefore(buttonGroup, phoneGroupEl.nextSibling); 1691 } 1692 } 1693 } 1694 } 1695 }, 150); 1696 } 1697 // Initialize phone input when shown - wait a bit for DOM to update 1698 setTimeout(function() { 1699 // Double-check phone field is visible before initializing 1700 // Use the current form, not just any form 1701 var formToSearch = form; // Use the form we're currently enhancing 1702 var phoneEl = formToSearch ? formToSearch.querySelector('#sdphone[data-form-id="' + currentFormId + '"]') : null; 1703 if (!phoneEl) { 1704 phoneEl = formToSearch ? formToSearch.querySelector('#sdphone') : null; 1705 } 1706 if (phoneEl && phoneEl.offsetParent !== null) { 1707 initPhoneInput(); 1708 // Re-run tab order enforcement after phone field appears 1709 setTimeout(enforceTabOrder, 100); 1710 } else { 1711 // Retry if not visible yet 1712 setTimeout(function() { 1713 initPhoneInput(); 1714 setTimeout(enforceTabOrder, 100); 1715 }, 100); 1716 } 1717 }, 150); 1718 } else { 1719 phoneGroup.style.display = 'none'; 1720 // Clear phone values when hidden and make field optional 1721 var formToSearch = form; // Use the form we're currently enhancing 1722 phoneInput = formToSearch ? formToSearch.querySelector('#sdphone[data-form-id="' + currentFormId + '"]') : null; 1723 if (!phoneInput) { 1724 phoneInput = formToSearch ? formToSearch.querySelector('#sdphone') : null; 1725 } 1726 var sdphoneEl = elByIdIn(formToSearch, 'sdphone_' + currentFormId); 1727 1728 // Properly destroy intlTelInput instance before clearing 1729 if (itiInstance) { 1730 try { 1731 itiInstance.destroy(); 1732 } catch(e) { 1733 // Ignore errors during destruction 1734 } 1735 itiInstance = null; 1736 } 1737 1738 if (phoneInput) { 1739 phoneInput.value = ''; 1740 // Remove name attribute so field is not submitted 1741 phoneInput.removeAttribute('name'); 1742 // Mark as optional 1743 phoneInput.setAttribute('data-optional', 'true'); 1744 // Remove required attributes 1745 phoneInput.removeAttribute('required'); 1746 phoneInput.removeAttribute('aria-required'); 1747 // Remove validation error classes 1748 phoneInput.classList.remove('error', 'invalid', 'required', 'errorMsg'); 1749 // Clear error styling 1750 phoneInput.style.border = ''; 1751 // Remove intlTelInput wrapper classes if they exist 1752 var itiWrapper = phoneInput.closest('.iti'); 1753 if (itiWrapper) { 1754 // Move phone input out of iti wrapper if needed 1755 var parent = itiWrapper.parentNode; 1756 if (parent) { 1757 parent.insertBefore(phoneInput, itiWrapper); 1758 itiWrapper.remove(); 1759 } 1760 } 1761 // Use jQuery to remove error styling if available 1762 if (typeof $ !== 'undefined') { 1763 $(phoneInput).css('border', '').removeClass('errorMsg'); 1764 } 1765 } 1766 if (sdphoneEl) { 1767 sdphoneEl.value = ''; 1768 sdphoneEl.removeAttribute('name'); 1769 sdphoneEl.setAttribute('data-optional', 'true'); 1770 } 1771 1772 // Hide any phone validation error messages 1773 var phoneErrorMsg = formToSearch ? formToSearch.querySelector('.ctFrmErrorMsg[data-id*="phone"], .error-msg-phone, [data-error="phone"]') : null; 1774 if (phoneErrorMsg) { 1775 phoneErrorMsg.style.display = 'none'; 1776 }
1777 ['phone_e164', 'phone_dial_code', 'phone_country_iso2', 'phone_country_name', 'phone_number_country'].forEach(function(id) { 1778 var el = elByIdIn(form, id + '_' + currentFormId); 1779 if (el) { 1780 el.value = ''; 1781 } 1782 }); 1783 } 1784 } 1785 1786 // Override phone validation to skip when field is hidden 1787 function overridePhoneValidation() { 1788 // Intercept form submission to handle phone validation when checkbox is unchecked 1789 // Try multiple button selectors 1790 var scheduleDemoBtn = form.querySelector('#schedule_demo') || 1791 form.querySelector('button[type="submit"]') || 1792 form.querySelector('button[name="submit"]') || 1793 form.querySelector('.c-btn[type="button"]'); 1794 1795 if (scheduleDemoBtn) { 1796 // Add click handler that runs before validation (capture phase) 1797 var clickHandler = function(e) { 1798 // Before validation runs, check if phone field should be validated 1799 if (!checkbox.checked) { 1800 // Phone field is hidden, make it optional and remove validation requirements 1801 var formToSearch = form; // Use the form we're currently enhancing 1802 var phoneEl = formToSearch ? formToSearch.querySelector('#sdphone[data-form-id="' + currentFormId + '"]') : null; 1803 if (!phoneEl) { 1804 phoneEl = formToSearch ? formToSearch.querySelector('#sdphone') : null; 1805 } 1806 var sdphoneEl = elByIdIn(formToSearch, 'sdphone_' + currentFormId); 1807 1808 // Make phone field optional by removing name and required attributes 1809 if (phoneEl) { 1810 phoneEl.value = ''; 1811 phoneEl.removeAttribute('data-temp-value'); 1812 phoneEl.removeAttribute('name'); 1813 phoneEl.setAttribute('data-optional', 'true'); 1814 phoneEl.removeAttribute('required'); 1815 phoneEl.removeAttribute('aria-required'); 1816 phoneEl.classList.remove('error', 'invalid', 'required', 'errorMsg'); 1817 phoneEl.style.border = ''; 1818 if (typeof $ !== 'undefined') { 1819 $(phoneEl).css('border', '').removeClass('errorMsg'); 1820 } 1821 } 1822 if (sdphoneEl) { 1823 sdphoneEl.removeAttribute('name'); 1824 sdphoneEl.setAttribute('data-optional', 'true'); 1825 sdphoneEl.value = ''; 1826 } 1827 1828 // Hide any phone validation error messages 1829 var phoneErrorMsg = formToSearch ? formToSearch.querySelector('.ctFrmErrorMsg[data-id*="phone"], .error-msg-phone, [data-error="phone"]') : null; 1830 if (phoneErrorMsg) { 1831 phoneErrorMsg.style.display = 'none'; 1832 } 1833 } else { 1834 // Checkbox is checked, ensure phone field is required and visible 1835 var formToSearch = form; 1836 var phoneEl = formToSearch ? formToSearch.querySelector('#sdphone[data-form-id="' + currentFormId + '"]') : null; 1837 if (!phoneEl) { 1838 phoneEl = formToSearch ? formToSearch.querySelector('#sdphone') : null; 1839 } 1840 if (phoneEl) { 1841 phoneEl.setAttribute('name', 'phone'); 1842 phoneEl.removeAttribute('data-optional'); 1843 // Restore visibility 1844 phoneEl.style.visibility = ''; 1845 phoneEl.style.position = ''; 1846 phoneEl.style.width = ''; 1847 phoneEl.style.height = ''; 1848 phoneEl.style.opacity = ''; 1849 // Clear temporary value if set 1850 if (phoneEl.getAttribute('data-temp-value') === 'true' && phoneEl.value === 'OPTIONAL_FIELD') { 1851 phoneEl.value = ''; 1852 phoneEl.removeAttribute('data-temp-value'); 1853 } 1854 } 1855 } 1856 }; 1857 // Add our handler with capture phase to run before other handlers 1858 scheduleDemoBtn.addEventListener('click', clickHandler, true); 1859 } 1860 } 1861 1862 // Tab order enforcement (from demo form) 1863 function enforceTabOrder() { 1864 if (!form) return; 1865 1866 var fullName = form.querySelector('#sdfirstname'); 1867 var workEmail = form.querySelector('#sdemail'); 1868 var phoneInputEl = form.querySelector('#sdphone'); 1869 if (!phoneInputEl) { 1870 phoneInputEl = elByIdIn(form, 'sdphone_' + currentFormId); 1871 } 1872 var submitBtn = form.querySelector('#schedule_demo') || 1873 form.querySelector('button[type="submit"]') || 1874 form.querySelector('button[name="submit"]'); 1875 1876 var selectedFlag = null; 1877 if (phoneInputEl) { 1878 var itiWrap = phoneInputEl.closest('.iti'); 1879 if (itiWrap) selectedFlag = itiWrap.querySelector('.iti__selected-flag'); 1880 } 1881 1882 if (fullName) fullName.tabIndex = 1; 1883 if (workEmail) workEmail.tabIndex = 2; 1884 if (checkbox) checkbox.tabIndex = 3; 1885 if (selectedFlag) selectedFlag.tabIndex = 4; 1886 if (phoneInputEl) phoneInputEl.tabIndex = 5; 1887 if (submitBtn) submitBtn.tabIndex = 6; 1888 1889 var allowed = new Set( 1890 [fullName, workEmail, checkbox, selectedFlag, phoneInputEl, submitBtn].filter(
1890Boolean) 1891 ); 1892 form.querySelectorAll('a[href], button, input, select, textarea').forEach(function (el) { 1893 if (allowed.has(el)) return; 1894 el.tabIndex = -1; 1895 el.setAttribute('aria-hidden', 'true'); 1896 }); 1897 1898 [ 1899 '#consent_checkbox', 1900 'button[name="close-modal"]', 1901 '#sdphone_' + currentFormId, 1902 '#country', 1903 '.country_hidden', 1904 '#phone_e164_' + currentFormId, 1905 '#phone_dial_code_' + currentFormId, 1906 '#phone_country_iso2_' + currentFormId, 1907 '#phone_country_name_' + currentFormId, 1908 '#phone_number_country_' + currentFormId 1909 ].forEach(function (sel) { 1910 var el = form.querySelector(sel); 1911 if (el) { 1912 el.tabIndex = -1; 1913 el.setAttribute('aria-hidden', 'true'); 1914 } 1915 }); 1916 1917 if (selectedFlag) { 1918 var flagContainer = selectedFlag.parentNode; 1919 var list = flagContainer && flagContainer.querySelector('.iti__country-list'); 1920 if (list) { 1921 list.querySelectorAll('[tabindex], li, a, input, button').forEach(function (el) { 1922 el.tabIndex = -1; 1923 el.setAttribute('aria-hidden', 'true'); 1924 }); 1925 } 1926 } 1927 } 1928 1929 // Schedule tab order enforcement 1930 function scheduleEnforce() { 1931 enforceTabOrder(); 1932 setTimeout(enforceTabOrder, 0); 1933 setTimeout(enforceTabOrder, 50); 1934 setTimeout(enforceTabOrder, 200); 1935 } 1936 1937 // Add event listeners 1938 checkbox.addEventListener('change', function() { 1939 try { 1940 trackSchedule11Experiment('schedule_1on1_toggle', { 1941 form_id: currentFormId, 1942 schedule_1on1_checked: checkbox.checked, 1943 component_name: 'schedule_1on1_checkbox' 1944 }); 1945 togglePhoneField(); 1946 updateFormConfig(); 1947 } catch (err) { 1948 console.error('Schedule 1:1 toggle handler failed:', err); 1949 } 1950 }); 1951 1952 // Initialize 1953 try { 1954 updateFormConfig(); 1955 } catch (err) { 1956 console.error('Schedule 1:1 init updateFormConfig failed:', err); 1957 } 1958 try { 1959 togglePhoneField(); // Set initial state 1960 } catch (err) { 1961 console.error('Schedule 1:1 init togglePhoneField failed:', err); 1962 } 1963 try { 1964 overridePhoneValidation(); // Override validation for hidden phone field 1965 } catch (err) { 1966 console.error('Schedule 1:1 init overridePhoneValidation failed:', err); 1967 } 1968 1969 // Set up tab order enforcement 1970 if (document.readyState === 'loading') { 1971 document.addEventListener('DOMContentLoaded', scheduleEnforce); 1972 } else { 1973 scheduleEnforce(); 1974 } 1975 1976 // Watch for form changes and re-enforce tab order 1977 if (form) { 1978 new MutationObserver(scheduleEnforce).observe(form, { childList: true, subtree: true }); 1979 } 1980 document.addEventListener('countrychange', scheduleEnforce, true); 1981 } 1982 1983 // Ensure intlTelInput library is loaded 1984 function ensureIntlTelInputLibrary() { 1985 if (window.intlTelInput) return; 1986 1987 // Check if script is already in DOM 1988 var existingScript = document.querySelector('script[src*="intlTelInput.min.js"]'); 1989 if (existingScript) { 1990 // Script exists, wait for it to load 1991 var checkInterval = setInterval(function() { 1992 if (window.intlTelInput) { 1993 clearInterval(checkInterval); 1994 } 1995 }, 100); 1996 // Stop checking after 5 seconds 1997 setTimeout(function() { clearInterval(checkInterval); }, 5000); 1998 return; 1999 } 2000 2001 // Load the library 2002 var script = document.createElement('script'); 2003 script.src = 'https://clevertap.com/wp-content/themes/clevertap2023/js/intlTelInput.min.js?v=20251006'; 2004 script.onload = function() { 2005 // Also load utils if needed 2006 if (!document.querySelector('script[src*="utils.js"]')) { 2007 var utilsScript = document.createElement('script'); 2008 utilsScript.src = 'https://clevertap.com/wp-content/themes/clevertap2023/js/utils.js?v=20251006'; 2009 document.body.appendChild(utilsScript); 2010 } 2011 }; 2012 document.body.appendChild(script); 2013 } 2014 2015 // Initialize when DOM is ready 2016 if (document.readyState === 'loading') { 2017 document.addEventListener('DOMContentLoaded', function() { 2018 ensureIntlTelInputLibrary(); 2019 initFormEnhancement(); 2020 }); 2021 } else { 2022 ensureIntlTelInputLibrary(); 2023 initFormEnhancement(); 2024 } 2025 2026 // Also try after delays in case form loads dynamically 2027 setTimeout(function() { 2028 ensureIntlTelInputLibrary(); 2029 initFormEnhancement(); 2030 }, 500); 2031 setTimeout(function() { 2032 ensureIntlTelInputLibrary(); 2033 initFormEnhancement(); 2034 }, 1500); 2035 setTimeout(function() { 2036 ensureIntlTelInputLibrary(); 2037 initFormEnhancement(); 2038 }, 3000); 2039 2040 // For case study pages, also check when window loads completely 2041 window.addEventListener('load', function() { 2042 if (isCaseStudyPage()) { 2043 setTimeout(function() { 2044 ensureIntlTelInputLibrary(); 2045 initFormEnhancement(); 2046 }, 500); 2047 } 2048 }); 2049})();
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.