1(function() { 2 'use strict'; 3 4 // Global error handler for third-party script errors 5 // This catches syntax errors and other issues from external scripts 6 var originalErrorHandler = window.onerror; 7 window.onerror = function(message, source, line, col) { 8 // Check if error is from third-party scripts we load 9 var thirdPartyDomains = ['influ2.com', 't.influ2.com', 'linkedin.com', 'reddit.com', 'google-analytics.com', 'googletagmanager.com']; 10 var isThirdPartyError = false; 11 12 if (source) { 13 isThirdPartyError = thirdPartyDomains.some(function(domain) { 14 return source.indexOf(domain) !== -1; 15 }); 16 } 17 18 if (isThirdPartyError) { 19 // Log third-party errors for debugging but don't let them break the page 20 // Downgraded to log to avoid Sentry noise (captureConsoleIntegration captures warn/error). 21 // Common causes: ad blockers, Transcend consent enforcement, browser privacy features. 22 // Still captured as breadcrumbs on real errors for debugging context. 23 console.log('Third-party script error (handled):', { 24 message: message, 25 source: source, 26 line: line, 27 col: col, 28 note: message.includes('MIME type') ? 'Likely invalid MIME type - script may not be JavaScript' : undefined 29 }); 30 return true; // Prevent default error handling 31 } 32 33 // For all other errors, use original handler or default behavior 34 if (originalErrorHandler) { 35 return originalErrorHandler.apply(this, arguments); 36 } 37 return false; // Use default error handling 38 }; 39 40 var config = { 41 BNG: "97152766", 42 CR: "e5e40f22-b23d-479e-9ffc-b557acc3093e", 43 DB: "5bd5afc6abd4af8f", 44 FB: "402119569597383", 45 GADS: "AW-821881030", 46 GTAG: "G-CX7P9K6W67", 47 HJ: "646199", 48 INFLU2: "da42b299-331d-4aa3-ac8a-5dfeabfa220e", 49 LI: "4496132", 50 MNTN: "37044", 51 QLF: "hhaW6HiVqGA5oJq1", 52 RD: "a2_g2a9uyehu26u", 53 SEG_prod: "9xLD58UfOuu8iiv5x8vH1nk6wsbEXMev", 54 SEG_beta: "uiC7cIL9obYvtKxfx77yOvGGICsAXdTA", 55 TR: "1f0cf102-c592-4eb4-834f-e07f2fe68ef1" 56 }; 57 58 // Environment detection 59 var env = (function() { 60 var hostname = document.location.hostname; 61 if (hostname === 'localhost' || hostname === '127.0.0.1') { 62 return 'DEV'; 63 } else if (hostname.indexOf('-beta') !== -1) { 64 return 'BETA'; 65 } else { 66 return 'PROD'; 67 } 68 })(); 69 var SEG_env = (env === 'DEV' || env === 'BETA') ? config.SEG_beta : config.SEG_prod; 70 var SEG_cdn = (env === 'DEV' || env === 'BETA') ? 'https://cdn.segment.com' : 'https://evs.cdp.postman.com'; 71 var SEG_src = (env === 'DEV' || env === 'BETA') 72 ? 'https://cdn.segment.com/analytics.js/v1/' + config.SEG_beta + '/analytics.min.js' 73 : 'https://evs.cdp.postman.com/6C88CYC9Neo3CfCCSD2BHa/xae7HN2EeK3mLEpFypgqAS.min.js'; 74 75 // Load prod-only scripts in BETA too (for verification) 76 var loadProdScripts = env === 'PROD' || env === 'BETA'; 77 78 // Helpful page flags 79 var isLandingPage = document.location.href.indexOf('/lp/') !== -1; 80 var isTemplates = !!document.location.href.match('/templates/'); 81 var isThankYou = !!document.location.href.match('thank-you'); 82 var isPricing = !!document.location.href.match('/pricing'); 83 var commonRoomPages = ['/', '/product/', '/solutions/', '/postman-enterprise/', '/pricing/']; 84 85 // Inline pixel scripts (ES5 strings) 86 var bingJS = "(function(w,d,t,r,u){var f,n,i;w[u]=w[u]||[],f=function(){var o={ti:\"" + config.BNG + "\", enableAutoSpaTracking: true};o.q=w[u],w[u]=new UET(o),w[u].push(\"pageLoad\")},n=d.createElement(t),n.src=r,n.async=1,n.onload=n.onreadystatechange=function(){var s=this.readyState;s&&s!==\"loaded\"&&s!==\"complete\"||(f(),n.onload=n.onreadystatechange=null)},i=d.getElementsByTagName(t)[0],i&&i.parentNode?i.parentNode.insertBefore(n,i):(d.head||d.documentElement).appendChild(n)})(window,document,\"script\",\"//bat.bing.com/bat.js\",\"uetq\");"; 87 var demandBaseJS = "(function(d,b,a,s,e){ var t = b.createElement(a),fs = b.getElementsByTagName(a)[0]; t.async=1; t.id=e; t.src=s;fs&&fs.parentNode?fs.parentNode.insertBefore(t, fs):(b.head||b.documentElement).appendChild(t); })(window,document,'script','https://tag.demandbase.com/" + config.DB + ".min.js','demandbase_js_lib');";
88 var GOT = "662445430041-1ph1qkff6b4qqb24rv5b1tuvhu934a7m.apps.googleusercontent.com|805864674475-p5bob5d4p9s80cjvd12ch9lqerpp3t60.apps.googleusercontent.com|922798136421-qq621ephpemoj67dqc3n9nn9j865360g.apps.googleusercontent.com|587115145864-ktkil0eg2h6988ea8q1cod155flhgh31.apps.googleusercontent.com|805864674475-3abs2rivkn7kreou30b8ru8esnti4oih.apps.googleusercontent.com"; 89 var gotJS = "(function(){var env = (!!document.location.host.match('localhost') && 'DEV') || (!!document.location.host.match('beta') && 'BETA') || (!!document.location.host.match('stage') && 'STAGE') || (!!document.location.host.match('preview') && 'PREV') || (!!document.location.host.match('postman.com') && 'PROD');var codes = '" + GOT + "'.split('|');var LaunchPostman = document.getElementById('LaunchPostman'); if(typeof window.pmt==='function'){window.pmt('set', ['google', { client: { DEV: codes[0], BETA: codes[1], STAGE: codes[2], PREV: codes[3], PROD: codes[4] } }]);}var pmtGoogle = typeof window.pmt==='function'?window.pmt('google'):null;var pmtGoogleClient = pmtGoogle && pmtGoogle.client;if (!LaunchPostman) {var SignIn = document.getElementById('SignIn');var href = (LaunchPostman && LaunchPostman.parentNode.href) || (SignIn && SignIn.parentNode.href);SignIn.setAttribute('data-pmGot', pmtGoogleClient && pmtGoogleClient[env]);SignIn.setAttribute('data-topZIndex', 2147483645);SignIn.setAttribute('data-pmGot-href', href);var s=document.createElement('script');s.async=true;s.setAttribute('data-px','<!-- GOT -->');s.src='/_mk-www-next/scripts/pmGot.js?'+new Date().getTime();document.head.appendChild(s);}})();"; 90 // Hotjar snippet is intentionally not defined here. Hotjar is opt-in per page via 91 // the {"type":"Hotjar"} page-data component, which gates injection on Analytics consent. 92 var linkedInJS = "window._linkedin_data_partner_ids = window._linkedin_data_partner_ids || [];window._linkedin_data_partner_ids.push(\"" + config.LI + "\");(function(l) {if (!l){window.lintrk = function(a,b){window.lintrk.q.push([a,b])};window.lintrk.q=[]}var s = document.getElementsByTagName(\"script\")[0];var b = document.createElement(\"script\");b.type = \"text/javascript\";b.async = true;b.src = \"https://snap.licdn.com/li.lms-analytics/insight.min.js\";s&&s.parentNode?s.parentNode.insertBefore(b, s):(document.head||document.documentElement).appendChild(b);})(window.lintrk);"; 93 var mountainJS = "(function(){\"use strict\";var e=null,b=\"4.0.0\",n=\"" + config.MNTN + "\",additional=\"term=value\",t,r,i;try{t=top.document.referer!==\"\"?encodeURIComponent(top.document.referrer.substring(0,2048)):\"\"}catch(o){t=document.referrer!==null?document.referrer.toString().substring(0,2048):\"\"}try{r=window&&window.top&&document.location&&window.top.location===document.location?document.location:window&&window.top&&window.top.location&&\"\"!==window.top.location?window.top.location:document.location}catch(u){r=document.location}try{i=parent.location.href!==\"\"?encodeURIComponent(parent.location.href.toString().substring(0,2048)):\"\"}catch(a){try{i=r!==null?encodeURIComponent(r.toString().substring(0,2048)):\"\"}catch(f){i=\"\"}}var l,c=document.createElement(\"script\"),h=null,p=document.getElementsByTagName(\"script\"),d=Number(p.length)-1,v=document.getElementsByTagName(\"script\")[d];if(typeof l===\"undefined\"){l=Math.floor(Math.random()*1e17)}h=\"dx.mountain.com/spx?\"+\"dxver=\"+b+\"&shaid=\"+n+\"&tdr=\"+t+\"&plh=\"+i+\"&cb=\"+l+additional;c.type=\"text/javascript\";c.src=(\"https:\"===document.location.protocol?\"https://\":\"http://\")+h;v&&v.parentNode?v.parentNode.insertBefore(c,v):(document.head||document.documentElement).appendChild(c)})()"; 94 var redditJS = "!function(w,d){if(!w.rdt){var p=w.rdt=function(){p.sendEvent?p.sendEvent.apply(p,arguments):p.callQueue.push(arguments)};p.callQueue=[];var t=d.createElement(\"script\");t.src=\"https://www.redditstatic.com/ads/pixel.js\",t.async=!0;var s=d.getElementsByTagName(\"script\")[0];s&&s.parentNode?s.parentNode.insertBefore(t,s):(d.head||d.documentElement).appendChild(t)}}(window,document);rdt('init','" + config.RD + "');rdt('track', 'PageVisit');"; 95 var qualifiedJS = "(function(w,q){w['QualifiedObject']=q;w[q]=w[q]||function(){(w[q].q=w[q].q||[]).push(arguments)};})(window,'qualified');"; 96 var segmentJS = "!function(){var i=\"cdp\",cdp=window[i]=window[i]||[];if(!cdp.initialize)if(cdp.invoked)window.console&&console.error&&console.error(\"Segment snippet included twice.\");else{cdp.invoked=!0;cdp.methods=[\"trackSubmit\",\"trackClick\",\"trackLink\",\"trackForm\",\"pageview\",\"identify\",\"reset\",\"group\",\"track\",\"ready\",\"alias\",\"debug\",\"page\",\"screen\",\"once\",\"off\",\"on\",\"addSourceMiddleware\",\"addIntegrationMiddleware\",\"setAnonymousId\",\"addDestinationMiddleware\",\"register\"];cdp.factory=function(e){return function(){if(window[i].initialized)return window[i][e].apply(window[i],arguments);var n=Array.prototype.slice.call(arguments);
96if([\"track\",\"screen\",\"alias\",\"group\",\"page\",\"identify\"].indexOf(e)>-1){var c=document.querySelector(\"link[rel='canonical']\");n.push({__t:\"bpc\",c:c&&c.getAttribute(\"href\")||void 0,p:location.pathname,u:location.href,s:location.search,t:document.title,r:document.referrer})}n.unshift(e);cdp.push(n);return cdp}};for(var n=0;n<cdp.methods.length;n++){var key=cdp.methods[n];cdp[key]=cdp.factory(key)}cdp.load=function(key,n){var t=document.createElement(\"script\");t.type=\"text/javascript\";t.async=!0;t.setAttribute(\"data-global-segment-analytics-key\",i);t.src=\"" + SEG_src + "\";var r=document.getElementsByTagName(\"script\")[0];r&&r.parentNode?r.parentNode.insertBefore(t,r):(document.head||document.documentElement).appendChild(t);cdp._loadOptions=n};cdp._writeKey=\"" + SEG_env + "\";cdp._cdn = \"" + SEG_cdn + "\";cdp.SNIPPET_VERSION=\"5.2.0\";}}();"; 97 98 // Main initialization 99 100 function init() { 101 // Initialize Segment stub early so pmt's click handler can safely 102 // call (window.analytics||window.cdp).track() before Segment fully loads. 103 // The stub just queues calls â actual loading happens later in initSegment(). 104 105 if (!window['analytics'] && !window['cdp']) { 106 loadSegmentInlineScript({ textContent: segmentJS }); 107 } 108 109 // Initialize PMT 110 if (typeof window.pmt === 'function') { 111 window.pmt('setScalp', [{ property: 'www-next' }]); 112 window.pmt('trackClicks', ['data-track-entity', 'pm-analytics']); 113 114 // Attribution head node: fire before Transcend/Segment enrichment (~6.2s). 115 // pmt + polaris are inline from layout.jsx, so traceId/userId/referrer are available. 116 // Enriched meta.user (RampMetrics, human, segmentAnonymousId) is set later in setPmtUser(). 117 window.pmt('scalp', [ 118 'pm-analytics', 119 'load', 120 document.location.pathname 121 ]); 122 } 123 124 // Clean up Shopify cookies 125 deleteCookie('_shopify_s', '/', '.postman.com'); 126 deleteCookie('_shopify_y', '/', '.postman.com'); 127 128 // Initialize Transcend first, then load other scripts 129 // Small delay to allow React hydration to complete before Transcend modifies DOM 130 setTimeout(function() { 131 initTranscend(function() { 132 loadEssentialScripts(); 133 loadAnalyticsScripts(); 134 loadTargetingScripts(); 135 136 // Load page-specific scripts 137 loadPageSpecificScripts(); 138 139 // Set up click monitoring 140 setupClickMonitoring(); 141 }); 142 }, 100); 143 } 144 145 // Initialize Transcend consent manager 146 function initTranscend(cb) { 147 var transcendEnv = env === 'PROD' ? 'cm' : 'cm-test'; 148 149 // Add error handling for airgap.js 150 loadScript({ 151 src: 'https://transcend-cdn.com/' + transcendEnv + '/' + config.TR + '/airgap.js', 152 'data-px': '<!-- Transcend -->', 153 'data-cfasync': 'false', 154 'data-local-sync': 'allow-network-observable', 155 'data-more-choices-view': 'CompleteOptionsToggles', 156 'data-sites': 'postman.com api-first-world.com', 157 onerror: function() { 158 // Downgraded to log to avoid Sentry noise. Expected when ad blockers block transcend-cdn.com. 159 console.log('[Transcend] airgap.js failed to load, proceeding without consent management'); 160 loadEssentialScripts(); 161 } 162 }, function() { 163 // Check if airgap is immediately available 164 if (typeof window.airgap !== 'undefined' || typeof window.transcend !== 'undefined') { 165 if (cb) cb(); 166 return; 167 } 168 169 // Quick check with short timeout - don't wait long for critical path 170 var checkCount = 0; 171 var checkAirgap = setInterval(function() { 172 checkCount++; 173 if (typeof window.airgap !== 'undefined' || typeof window.transcend !== 'undefined') { 174 clearInterval(checkAirgap); 175 if (cb) cb(); 176 } else if (checkCount >= 40) { // 2s max wait (40 * 50ms) 177 clearInterval(checkAirgap); 178 // Downgraded to log to avoid Sentry noise. Expected on slow connections or partial blocks. 179 console.log('[Transcend] airgap not ready after 2s, proceeding anyway'); 180 loadEssentialScripts(); 181 } 182 }, 50); 183 }); 184 } 185 186 // Essential - or strictly necessary - scripts 187 function loadEssentialScripts() { 188 initSegment(); 189 190 if (!isLandingPage && env !== 'DEV' && document.getElementById('SignIn')) { 191 loadScript({ textContent: gotJS, 'data-px': '<!-- GoogleOneTap -->' }); 192 } 193 } 194 195 // Analytics scripts 196 function loadAnalyticsScripts() { 197 if (window.__analyticsLoaded) return; 198 window.__analyticsLoaded = true; 199 200 if (loadProdScripts) { 201 // Hotjar is intentionally NOT loaded globally here. It is opt-in per page 202 // via the {"type":"Hotjar"}
202 page-data component (src/app/components/analytics/Hotjar.jsx), 203 // which gates injection on Transcend Analytics consent. Loading it here would 204 // run Hotjar on every page regardless of page intent or consent. 205 206 // Marketo Munchkin - page-level tracking for Wistia and other integrations 207 loadScript({ src: 'https://munchkin.marketo.net/munchkin.js', 'data-px': '<!-- Munchkin -->' }, function() { 208 if (window.Munchkin) { 209 window.Munchkin.init('067-UMD-991'); 210 } 211 }); 212 213 loadScript({ 214 'data-px': '<!-- RampMetrics -->', 215 src: '/_mk-www-next/scripts/rm.js' 216 }, function() { 217 if (document.forms.length) { 218 loadScript({ 219 'data-px': '<!-- RampMetrics: fallback -->', 220 src: '/_mk-www-next/scripts/rm_fallback.js' 221 }, function() { 222 if (typeof window.pmt === 'function') { 223 window.pmt('log', ['[RampMetrics: fallback] ' + !!document.querySelector('[src*="/_mk-www-next/scripts/rm_fallback.js"]')]); 224 } 225 }); 226 } 227 }); 228 } 229 } 230 231 // Targeting scripts (for example: ad pixels, social media pixels) 232 function loadTargetingScripts() { 233 if (window.__targetingLoaded) return; 234 window.__targetingLoaded = true; 235 236 // Load in prod and beta environments 237 if (loadProdScripts) { 238 // Qualified needs: 239 // 1. Polaris UUID ready (otherwise "invalid visitor state" - MARKETING-SITE-WWW-NEXT-APP-KM) 240 // 2. Transcend consent granted for Analytics + Functional purposes 241 // (otherwise "invalid domain" - MARKETING-SITE-WWW-NEXT-APP-M) 242 // Consent-gating also ensures we respect user privacy preferences. 243 var hasQualifiedConsent = function() { 244 try { 245 if (typeof window.airgap === 'undefined' || typeof window.airgap.getConsent !== 'function') { 246 return false; 247 } 248 var consent = window.airgap.getConsent(); 249 var purposes = consent && consent.purposes; 250 if (!purposes) return false; 251 // Qualified requires both Analytics and Functional consent. 252 // Mirrors the pattern in src/app/lib/analytics/enhancedConversion.js: 253 // - true / 'Auto' => granted ('Auto' = default opt-in under the active regime) 254 // - undefined => purpose not applicable to user's region, treat as granted 255 // - anything else => denied 256 // Strict `=== true` left Hidden-banner users at 'Auto' forever, so Qualified 257 // never loaded on www.postman.com once the US banner went Hidden. 258 var hasPurpose = function(value) { 259 if (value === undefined) return true; 260 return value === true || value === 'Auto'; 261 }; 262 return hasPurpose(purposes.Analytics) && hasPurpose(purposes.Functional); 263 } catch (e) { 264 return false; 265 } 266 }; 267 268 var loadQualified = function() { 269 // Only load if consent was actually granted (don't load on timeout) 270 if (!hasQualifiedConsent()) { 271 // Consent not granted for Analytics + Functional -- skip loading 272 return; 273 } 274 if (window.__qualifiedLoaded) return; 275 window.__qualifiedLoaded = true; 276 loadScript({ 'data-px': '<!-- Qualified -->', textContent: qualifiedJS }); 277 loadScript({ 278 'data-px': '<!-- Qualified:cdn -->', 279 src: 'https://js.qualified.com/qualified.js?token=' + config.QLF 280 }); 281 }; 282 283 waitForDependencies(function() { 284 return typeof window.polaris === 'function' && window.polaris('uuid') && hasQualifiedConsent(); 285 }, loadQualified); 286 287 // If user grants consent after the initial polling window (e.g. interacts with 288 // the Transcend banner mid-session), load Qualified at that point. 289 // consent-change fires on window.airgap per Transcend API, not document. 290 if (window.airgap) { 291 window.airgap.ready(function() { 292 window.airgap.addEventListener('consent-change', function() { 293 if (window.__qualifiedLoaded) return; 294 if (typeof window.polaris === 'function' && window.polaris('uuid')) { 295 loadQualified(); 296 } 297 }); 298 }); 299 } 300 301 shouldLoadCommonRoom(); 302 // NOTE: the `if (loadProdScripts)` gate opened above now stays open through the whole 303 // vendor list below. It used to close HERE, which left Bing, Demandbase, Facebook, 304 // gtag, Influ2, LinkedIn, Mountain, Reddit and X firing in EVERY environment â 305 // local dev and every feature-branch preview included. That sent synthetic traffic 306 // and pageview/conversion events from developer machines into production ad and 307 // analytics accounts, and cost every preview page load nine third-party requests. 308 309 loadScript({ textContent: bingJS, 'data-px': '<!-- Bing -->' }); 310 loadScript({ textContent: demandBaseJS, 'data-px': '<!-- DemandBase -->' }); 311 // Facebook - set up stub then load external script 312 if (!window.fbq) { 313 var n = window.fbq = function() { 314 n.callMethod ? n.callMethod.apply(n, arguments) : n.queue.push(arguments); 315 }; 316 if (!window._fbq) window._fbq = n; 317 n.push = n; 318 n.loaded = true; 319 n.version = '2.0'; 320 n.queue = []; 321 } 322 loadScript({ 323 'data-px': '<!-- Facebook -->', 324 src: 'https://connect.facebook.net/en_US/fbevents.js' 325 }, function() { 326 window.fbq('init', config.FB); 327 window.fbq('track', 'PageView'); 328 }); 329 loadScript({ src: 'https://www.googletagmanager.com/gtag/js?i
329d=' + config.GTAG, 'data-px': '<!-- GTag -->' }, function() { 330 window.dataLayer = window.dataLayer || []; 331 function gtag() { 332 window.dataLayer.push(arguments); 333 } 334 gtag('js', new Date()); 335 window.gtag = gtag; 336 window.gtag('config', config.GTAG); 337 // AdWords: suppress the automatic pageview hit. The explicit 338 // gtag('event', 'conversion', { send_to: AW-821881030/... }) in 339 // src/app/lib/analytics/enhancedConversion.js is the single source of 340 // attribution for form submissions; an auto-pageview on every TY load 341 // would double-count legitimate conversions and add false conversions 342 // for refreshes, bookmarks, and direct entries to TY URLs. 343 window.gtag('config', config.GADS, { send_page_view: false }); 344 }); 345 346 loadScript({ 347 'data-px': '<!-- Influ2 -->', 348 dnt: true, 349 src: 'https://www.influ2.com/tracker?clid=' + config.INFLU2 350 }); 351 352 loadScript({ textContent: linkedInJS, 'data-px': '<!-- LinkedIn -->' }); 353 // Define RB stub to silence errors from Mountain's external script 354 // Mountain expects RB.source to be a base64-encoded string 355 if (typeof window.RB === 'undefined') { window.RB = { source: '' }; } 356 loadScript({ textContent: mountainJS, 'data-px': '<!-- Mountain -->' }); 357 loadScript({ textContent: redditJS, 'data-px': '<!-- Reddit -->' }); 358 loadScript({ src: '/_mk-www-next/scripts/x.js', 'data-px': '<!-- Twitter X -->' }); 359 } 360 } 361 362 363 /** Helpers */ 364 365 // Simple script loader 366 function loadScript(opts, cb) { 367 var script = document.createElement('script'); 368 script.async = true; 369 370 // Handle textContent for inline scripts 371 if (opts.textContent) { 372 script.textContent = opts.textContent; 373 374 // Set attributes for inline scripts (excluding textContent) 375 for (var key in opts) { 376 if (key !== 'textContent') { 377 script.setAttribute(key, opts[key]); 378 } 379 } 380 381 document.head.appendChild(script); 382 // Inline scripts execute immediately, so use setTimeout for callback 383 if (cb) setTimeout(cb, 50); 384 } else { 385 // External scripts use onload and onerror 386 script.onload = function() { 387 // Call the init function if specified 388 if (opts.init && typeof window[opts.init] === 'function') { 389 window[opts.init](); 390 } 391 // Call the callback if provided 392 if (cb) cb(); 393 }; 394 395 // Add error handling for failed script loads 396 script.onerror = function(event) { 397 // Downgraded to log to avoid Sentry noise (captureConsoleIntegration captures warn/error). 398 // Common causes: ad blockers, Transcend consent enforcement, browser privacy features 399 // (e.g. Firefox ETP). Scripts are verified to serve correctly from origin -- failures 400 // are client-side blocking, not deployment or MIME type issues. 401 console.log('Failed to load script:', opts.src); 402 // Still call callback to prevent hanging 403 if (cb) cb(); 404 }; 405 406 for (var attr in opts) { 407 if (attr === 'src') { 408 script.src = opts[attr]; 409 } else if (attr !== 'init') { 410 script.setAttribute(attr, opts[attr]); 411 } 412 } 413 document.head.appendChild(script); 414 } 415 } 416 417 // Simple inline script loader 418 function loadSegmentInlineScript(opts, cb) { 419 var script = document.createElement('script'); 420 script.setAttribute('data-px', '<!-- CDP -->'); 421 script.async = true; 422 script.textContent = opts.textContent; 423 424 for (var key in opts) { 425 if (key !== 'textContent') { 426 script.setAttribute(key, opts[key]); 427 } 428 } 429 430 document.head.appendChild(script); 431 432 if (cb) { 433 setTimeout(cb, 100); 434 } 435 436 return script; 437 } 438 439 function deleteCookie(name, path, domain) { 440 path = path || '/'; 441 var cookieString = name + '=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=' + path + ';'; 442 if (domain) { 443 cookieString += 'domain=' + domain + ';'; 444 } 445 document.cookie = cookieString; 446 } 447 448 // Helper: Add OneScreen pixel 449 function addOneScreenPixel(source) { 450 var pixel = document.createElement('img'); 451 pixel.src = 'https://accretivemedia.go2cloud.org/aff_i?offer_id=483&aff_id=1&source=' + source; 452 pixel.width = '0'; 453 pixel.height = '0'; 454 pixel.style.position = 'absolute'; 455 pixel.style.visibility = 'hidden'; 456 pixel.style.border = '0'; 457 document.head.appendChild(pixel); 458 } 459 460 // Helper: Load page-specific script 461 // Safe wrapper for planSelected - only executes if PlansSection is in the DOM 462 function runPlanSelected() { 463 if (window.planSelected && document.getElementById('PlansSection')) { 464 window.planSelected(); 465 } 466 } 467 468 function loadPageScript(src, dataAttribute, cb, delay) { 469 var script = document.createElement('script'); 470 var called = false;
471 472 function onComplete() { 473 if (!called && typeof cb === 'function') { 474 called = true; 475 cb(); 476 } 477 } 478 479 if (dataAttribute) { 480 script.setAttribute('data-page', dataAttribute); 481 } 482 script.src = src; 483 script.async = true; 484 script.onreadystatechange = function() { 485 if (this.readyState === 'complete' || this.readyState === 'loaded') { 486 onComplete(); 487 } 488 }; 489 script.onload = onComplete; 490 if (delay) { 491 setTimeout(function() { 492 document.head.appendChild(script); 493 }, delay); 494 } else { 495 document.head.appendChild(script); 496 } 497 } 498 499 // Check if CommonRoom should load on this page 500 function shouldLoadCommonRoom() { 501 var pathname = document.location.pathname; 502 if (loadProdScripts && commonRoomPages.indexOf(pathname) !== -1) { 503 loadCommonRoom(); 504 } 505 } 506 507 // Load CommonRoom (only on specific pages, after Transcend is ready) 508 function loadCommonRoom() { 509 if (typeof window.signals !== 'undefined') return; // Already loaded 510 511 // Initialize signals object before loading script 512 var methods = ['page', 'identify', 'form']; 513 window.signals = []; 514 window.signals._opts = { apiHost: 'https://api.cr-relay.com' }; 515 for (var mi = 0; mi < methods.length; mi++) { 516 (function(method) { 517 window.signals[method] = function() { 518 window.signals.push([method, arguments]); 519 return window.signals; 520 }; 521 })(methods[mi]); 522 } 523 524 loadScript({ 525 'data-px': '<!-- CommonRoom -->', 526 src: 'https://cdn.cr-relay.com/v1/site/' + config.CR + '/signals.js' 527 }); 528 } 529 530 function initSegment() { 531 waitForDependencies(function() { 532 return typeof window.polaris === 'function' && typeof window.pmt === 'function'; 533 }, initializeSegmentWithDependencies); 534 } 535 536 // Generic poller: runs callback once conditionFn returns truthy, or after 10s timeout. 537 // Used by Segment (wait for polaris+pmt) and Qualified (wait for polaris UUID). 538 function waitForDependencies(conditionFn, callback) { 539 var maxWaitTime = 10000; 540 var checkInterval = 100; 541 var startTime = Date.now(); 542 543 function check() { 544 if (conditionFn()) { 545 callback(); 546 } else if (Date.now() - startTime >= maxWaitTime) { 547 callback(); 548 } else { 549 setTimeout(check, checkInterval); 550 } 551 } 552 553 check(); 554 } 555 556 function initializeSegmentWithDependencies() { 557 // Segment stub may already exist (initialized early in init() to prevent 558 // pmt clickHandler crashes). If so, skip re-running the snippet. 559 var onStubReady = function() { 560 setTimeout(function () { 561 function getRmUid() { 562 var rmCookieObj = 563 window._rmq && 564 window._rmq.cookie && 565 window._rmq.cookie.props && 566 window._rmq.cookie.props.$uid; 567 var rmCookie = (document.cookie.match( 568 '(^|;) ?rm_asLtEBCh7D4pMetaFgSz=([^;]*)(;|$)' 569 ) || [])[2]; 570 return ( 571 rmCookieObj || 572 (rmCookie && 573 rmCookie.split('{%22$uid%22:%22').pop().split('%').shift()) || 574 null 575 ); 576 } 577 578 // segmentObj is shared by getSegmentAnonymousId() and the load/ready block below, 579 // so it must be declared in this (outer) scope â not inside the function, or the 580 // later reference throws ReferenceError: segmentObj is not defined. 581 var segmentObj = window['analytics'] || window['cdp']; 582 583 function getSegmentAnonymousId() { 584 var key = '|anon.'; 585 // Use a boundary-anchored regex so `?canon=foo` cannot match as `anon=`. 586 // [?&] ensures we only match when the parameter name starts after ? or &. 587 var anonSearchMatch = /[?&]anon=([^&]*)/.exec(window.location.search); 588 var anonFromSearch = null; 589 if (anonSearchMatch) { 590 try { anonFromSearch = decodeURIComponent(anonSearchMatch[1]); } 591 catch (e) { anonFromSearch = anonSearchMatch[1]; } 592 } 593 var anon = 594 window.name.indexOf(key) !== -1 && 595 window.name.split(key).pop().split('|').shift() || 596 anonFromSearch; 597 598 var anonymousIdFromSegment = null; 599 600 if (segmentObj && typeof segmentObj.user === 'function') { 601 try { 602 var user = segmentObj.user(); 603 if (user && typeof user.anonymousId === 'function') { 604 anonymousIdFromSegment = user.anonymousId(); 605 } 606 } catch { 607 // Segment not ready yet 608 } 609 } 610 611 return anon || anonymousIdFromSegment || (document.cookie.match( 612 '(^|;) ?ajs_anonymous_id=([^;]*)(;|$)' 613 ) || [])[2]; 614 } 615 616 var segmentAnonymousId = getSegmentAnonymousId(); 617 618 var user = { 619 rampMetrics: getRmUid(), 620 amplitudeDeviceId: window.amplitude && (typeof window.amplitude.getDeviceId === 'function') && window.amplitude.getDeviceId(), 621 bing: (document.cookie.match('(^|;) ?_uetsid=([^;]*)(;|$)') || [])[2], 622 facebook: (document.cookie.match('(^|;) ?_fbp=([^;]*)(;|$)') || [])[2], 623 google: (document.cookie.match('(^|;) ?_ga=([^;]*)(;|$)') || [])[2], 624 human: typeof window.passport === 'function' ? (window.passport('isHuman') && window.passport('isHuman').toString() || 'false') : 'fal
624se', 625 segmentAnonymousId: segmentAnonymousId, 626 userAgent: navigator.userAgent, 627 }; 628 629 if (typeof window.polaris === 'function') { 630 user.client = window.polaris('client'); 631 user.polaris = window.polaris('uuid'); 632 } 633 634 for (var key in user) { 635 if (!user[key]) { 636 delete user[key]; 637 } 638 } 639 640 function setPmtUser() { 641 if (typeof window.pmt === 'function') { 642 window.pmt('set', ['user', user]); 643 // load event fires early in init(); this block only enriches meta.user for Segment 644 645 // delay to allow user traits to register 646 setTimeout(()=>{ 647 const userEvent = btoa( 648 JSON.stringify( 649 { 650 ...window.pmt('event'), 651 action: 'PMTech', 652 entityId: window.location.href, 653 meta: { 654 ...window.pmt('event')['meta'], 655 user: window.pmt('user') 656 } 657 } 658 ) 659 ); 660 window.pmt('metric', ['tech', userEvent]); 661 window.pmt('log', [`[PMT Metrics:] ${userEvent}`]); 662 }, 500); 663 } 664 } 665 666 if (typeof window.pmt === 'function') { 667 setPmtUser(); 668 } else { 669 setTimeout(function() { 670 setPmtUser(); 671 }, 500); 672 } 673 674 if (segmentObj) { 675 if (typeof segmentObj['load'] === 'function') { 676 segmentObj.load(SEG_env); 677 } 678 if (typeof segmentObj['ready'] !== 'function') { 679 return false; 680 } 681 segmentObj.ready(function(){ 682 function processSegmentData() { 683 var userData = {}; 684 if (typeof window.pmt === 'function' && window.pmt('user')) { 685 var pmtUser = window.pmt('user'); 686 for (var prop in pmtUser) { 687 if (Object.prototype.hasOwnProperty.call(pmtUser, prop)) { 688 userData[prop] = pmtUser[prop]; 689 } 690 } 691 } 692 delete userData.segmentAnonymousId; 693 delete userData.userAgent; 694 695 // Add page-specific properties 696 userData.path = location.pathname; 697 userData.referrer = document.referrer || ''; 698 userData.search = location.search || ''; 699 userData.title = document.title; 700 userData.url = location.href; 701 702 if (typeof window.pmt === 'function') { 703 window.pmt('log', [ 704 { 705 '[CDP (Segment)]': userData 706 } 707 ]); 708 // Segment replaces the global (window.cdp) with the real AnalyticsBrowser 709 // instance once it finishes loading, so segmentObj captured above is stale 710 // here â and .user() is not a stubbed method, so it never exists on the stub. 711 // Re-read the live global so .user()/.page() hit the loaded instance. 712 var liveSegment = window['analytics'] || window['cdp']; 713 if (typeof liveSegment['user'] !== 'function') { 714 return false; 715 } 716 // If the session was initiated from a new-tab trace, `anon=<uuid>` in 717 // the URL is the originating tab's Polaris UUID and is the source of 718 // truth for anonymousId â use it directly so the identity chain is 719 // preserved across tabs. Only fall back to the local Polaris UUID when 720 // no `anon=` value is present (direct visit, same-tab navigation, etc.). 721 var anonFromUrl = null; 722 try { 723 anonFromUrl = (new URLSearchParams(window.location.search)).get('anon'); 724 } catch (e) { 725 // URLSearchParams not available (very old browser) â use a 726 // boundary-anchored regex so `?canon=foo` cannot match as `anon=`. 727 var rawAnonMatch = /[?&]anon=([^&]*)/.exec(window.location.search); 728 if (rawAnonMatch) { 729 try { anonFromUrl = decodeURIComponent(rawAnonMatch[1]); } 730 catch (e2) { anonFromUrl = rawAnonMatch[1]; } 731 } 732 } 733 liveSegment.user().anonymousId(anonFromUrl || window.polaris('uuid')); 734 if (typeof liveSegment['page'] !== 'function') { 735 return false;
736 } 737 liveSegment.page(userData); 738 } 739 } 740 741 if (typeof window.pmt === 'function') { 742 processSegmentData(); 743 } else { 744 setTimeout(function() { 745 processSegmentData(); 746 }, 500); 747 } 748 }); 749 } 750 }, 6200); 751 }; 752 753 if (window.cdp && window.cdp.invoked) { 754 // Stub already created early in init(), proceed directly 755 onStubReady(); 756 } else { 757 loadSegmentInlineScript({ textContent: segmentJS }, onStubReady); 758 } 759 } 760 761 // Load page-specific scripts (on initial page load) 762 function loadPageSpecificScripts() { 763 if (isThankYou) { 764 addOneScreenPixel('thank-you'); 765 } else { 766 addOneScreenPixel('lp'); 767 } 768 769 var qs = (document.currentScript && typeof document.currentScript.src === 'string' && document.currentScript.src.split('?').pop()) || new Date().getTime(); 770 771 if (isTemplates) { 772 loadPageScript('/_mk-www-next/scripts/pageTemplates.js?' + qs, '[Templates]'); 773 } 774 775 if (isThankYou) { 776 loadPageScript( 777 '/_mk-www-next/scripts/mntn_convert.js?' + qs, 778 '<!-- Mountain: conversion -->', 779 function() { 780 var pmtUser = typeof window.pmt === 'function' ? (window.pmt('user') || {}) : {}; 781 var mntnUid = pmtUser.rampMetrics || pmtUser.polaris; 782 if (window.mntnConversionPixel && mntnUid) { 783 window.mntnConversionPixel(mntnUid); 784 } 785 }, 786 5000 787 ); 788 } 789 790 if (isPricing) { 791 loadPageScript( 792 '/_mk-www-next/scripts/planSelected.js?' + qs, 793 '<!-- isPricing -->', 794 runPlanSelected, 795 1000 796 ); 797 } 798 799 setTimeout(function() { 800 var NavPricing = document.getElementById('Nav>Pricing'); 801 if (NavPricing) { 802 NavPricing.addEventListener('mouseup', function() { 803 loadPageScript( 804 '/_mk-www-next/scripts/planSelected.js?' + qs, 805 '<!-- isPricing -->', 806 runPlanSelected, 807 1000 808 ); 809 }, true); 810 } 811 }, 1000); 812 } 813 814 // Click monitoring for SPA navigation (force full page reload for certain sections) 815 function setupClickMonitoring() { 816 var clickMonitor = function(e) { 817 if (e && e.target) { 818 var target = e.target; 819 var parentNode = target.parentNode; 820 var parentNodeParentNode = parentNode && parentNode.parentNode; 821 var parentNodeParentNodeParentNode = parentNodeParentNode && parentNodeParentNode.parentNode; 822 var parentNodeParentNodeParentNodeParentNode = parentNodeParentNodeParentNode && parentNodeParentNodeParentNode.parentNode; 823 824 var lnk = target.getAttribute && target.getAttribute('href'); 825 var parentNodeLink = parentNode && parentNode.getAttribute && parentNode.getAttribute('href'); 826 var parentNodeParentNodeLink = parentNodeParentNode && parentNodeParentNode.getAttribute && parentNodeParentNode.getAttribute('href'); 827 var parentNodeParentNodeParentNodeLink = parentNodeParentNodeParentNode && parentNodeParentNodeParentNode.getAttribute && parentNodeParentNodeParentNode.getAttribute('href'); 828 var parentNodeParentNodeParentNodeParentNodeLink = parentNodeParentNodeParentNodeParentNode && parentNodeParentNodeParentNodeParentNode.getAttribute && parentNodeParentNodeParentNodeParentNode.getAttribute('href'); 829 830 lnk = lnk || parentNodeLink || parentNodeParentNodeLink || parentNodeParentNodeParentNodeLink || parentNodeParentNodeParentNodeParentNodeLink; 831 832 if (!lnk) return; 833 834 var lnkIsRelative = !lnk.match('http'); 835 var lnkIsSolutions = !!lnk.match('/solutions/'); 836 var lnkIsEnterprise = !!lnk.match('enterprise'); 837 var lnkIsLandingPage = !!lnk.match('/lp/'); 838 839 // Force full page reload for certain sections 840 if (lnkIsRelative && (lnkIsSolutions || lnkIsEnterprise || lnkIsLandingPage)) { 841 document.location.href = lnk; 842 } 843 } 844 }; 845 846 // Defensive check to prevent errors during deployment 847 if (document.body) { 848 document.body.addEventListener('mousedown', clickMonitor, true); 849 } else { 850 if (document.readyState === 'loading') { 851 document.addEventListener('DOMContentLoaded', function() { 852 if (document.body) { 853 document.body.addEventListener('mousedown', clickMonitor, true); 854 } 855 }); 856 } 857 } 858 } 859 860 // Handle SPA route changes for page-specific scripts 861 function setupRouteChangeListener() {
862 var lastPath = location.pathname; 863 864 function onRouteChange() { 865 var currentPath = location.pathname; 866 if (currentPath === lastPath) return; 867 lastPath = currentPath; 868 869 var qs = new Date().getTime(); 870 871 // Re-check page-specific conditions and load scripts 872 if (currentPath.match('/pricing')) { 873 loadPageScript( 874 '/_mk-www-next/scripts/planSelected.js?' + qs, 875 '<!-- isPricing -->', 876 runPlanSelected, 877 1000 878 ); 879 } 880 881 if (currentPath.match('/templates/')) { 882 loadPageScript('/_mk-www-next/scripts/pageTemplates.js?' + qs, '[Templates]'); 883 } 884 } 885 886 // Listen for popstate (back/forward navigation) 887 window.addEventListener('popstate', onRouteChange); 888 889 // Intercept pushState and replaceState for programmatic navigation 890 var originalPushState = history.pushState; 891 var originalReplaceState = history.replaceState; 892 893 history.pushState = function() { 894 originalPushState.apply(this, arguments); 895 onRouteChange(); 896 }; 897 898 history.replaceState = function() { 899 originalReplaceState.apply(this, arguments); 900 onRouteChange(); 901 }; 902 } 903 904 // Initialize when DOM is ready â skipped entirely on the internal healthcheck 905 // dashboard (/report/dashboard/**). It's an internal, noindex, no-consent-banner 906 // tool with no analytics need, and pmt's trackClicks (set up inside init(), below) 907 // hard-navigates every same-origin link on mousedown/focusin, which raced against 908 // Next's own client-side router and doubled up browser history entries on the 909 // dashboard's hub -> per-repo navigation. loadScript/setupRouteChangeListener 910 // below are left running unconditionally â they're infra (Transcend's script 911 // proxy, pricing/template script reload on route change), not trackers. 912 var isHealthcheckDashboard = /^\/report\/dashboard(\/|$)/.test(document.location.pathname); 913 if (!isHealthcheckDashboard) { 914 if (document.readyState === 'loading') { 915 document.addEventListener('DOMContentLoaded', init); 916 } else { 917 init(); 918 } 919 } 920 921 // Set up route change listener 922 setupRouteChangeListener(); 923 924 // Expose loadScript globally for Transcend's script proxy context 925 window.loadScript = loadScript; 926})();
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.