1(function() { 2 var prestitialAds = { 3 4 daysBetweenPrestitial: 7, 5 defaultTimeoutSeconds: 15, 6 7 testing: function() { 8 return document.location.search.indexOf('prestitial_test') !== -1; 9 }, 10 11 testingNoAd: function() { 12 return this.testing() === true && document.location.search.indexOf('no_ad') !== -1; 13 }, 14 15 overrideTimeout: function() { 16 return this.testing() === true && document.location.search.match('no_timeout') !== null; 17 }, 18 19 timeoutSeconds: function() { 20 return this.defaultTimeoutSeconds * 1000; 21 }, 22 23 setPrestitialPossibleCookie: function() { 24 // "possible" prestitial tracking for ad inventory reporting. _prestitialViewed is used 25 // for deciding whether or not it's time to show the ad 26 if (document.cookie.indexOf('_prestitialPossible') === -1) { 27 this.setCookie( 28 '_prestitialPossible', 29 Date.now(), 30 this.daysBetweenPrestitial 31 ); 32 } 33 }, 34 35 setCookie: function(cName, value, expireDays) { 36 var exdate = new Date(); 37 exdate.setDate(exdate.getDate() + expireDays); 38 document.cookie = 39 cName + '=' + encodeURIComponent(value) 40 + ((expireDays === null) ? '' : ';expires=' + exdate.toUTCString()) 41 + '; path=/'; 42 }, 43 44 screenMeetsSizeReqs: function() { 45 // Figure out if window is wide/high enough to show a prestitial 46 return (window.innerWidth > 640 && window.innerHeight > 640); 47 }, 48 49 userIsPrestitialEligible: function() { 50 return ( 51 (document.cookie.indexOf('_prestitialViewed') === -1) 52 && (window.location.href.indexOf('?prestitial_skip') === -1) 53 ); 54 }, 55 56 userIsNotOnSearchPage: function() { 57 // Don't show prestitial on search page 58 return !window.location.href.includes('/search/'); 59 }, 60 61 pageIsPrestitialEligible: function() { 62 // override if we are forcing a test 63 if (this.testing() === true) { 64 return true; 65 } 66 // Check 3 conditions: 67 // 1. The screen size meets minimum requirements 68 // 2. User is eligible (via cookies). 69 // 3. User is not on search page 70 return ( 71 this.screenMeetsSizeReqs() === true && 72 this.userIsPrestitialEligible() === true && 73 this.userIsNotOnSearchPage() 74 ); 75 }, 76 77 logClosePrestitialAdBy: function(closedBy) { // eslint-disable-line no-unused-vars 78 // No longer logged per TECH-8759 79 }, 80 81 showOverlay: function() { 82 document.body.style.overflow = 'hidden'; 83 document.querySelector('div#prestitial-init').style.display = 'block'; 84 }, 85 86 initialize: function(daysBetweenPrestitial) { 87 if (typeof daysBetweenPrestitial === 'undefined') { 88 // Not sure if we still need the variable being set at the top of this file via daysBetweenPrestitial = 7 89 this.daysBetweenPrestitial = 7; 90 } else { 91 this.daysBetweenPrestitial = daysBetweenPrestitial; 92 } 93 94 // Alias because "this" is a different namespace in the next chunk. 95 var closeFunc = this.logClosePrestitialAdBy; 96 var hideFunc = this.closePrestitial.bind(this); 97 98 // Cache references to prestitial elements 99 this.prestitialInit = document.querySelector('#prestitial-init'); 100 this.prestitialOuter = document.querySelector('#prestitial-outer'); 101 this.prestitialOOP = document.querySelector('#prestitial-oop'); 102 103 // Get reference to prestitial but DON'T show yet 104 // Wait for OOP check in slotRenderEnded event 105 this.prestitialOuter.addEventListener('click', function() { 106 closeFunc('user'); 107 hideFunc(); 108 }); 109 }, 110 111 showStandardPrestitial: function() { 112 if (this.prestitialOuter) { 113 this.prestitialOuter.style.display = 'block'; 114 } 115 }, 116 117 showOOPPrestitial: function() { 118 if (this.prestitialOOP) { 119 this.prestitialOOP.style.display = 'block'; 120 } 121 }, 122 123 closePrestitial: function() { 124 // close all prestitial things and set the body overflow back to default 125 document.body.style.overflow = ''; 126 if (this.prestitialInit) this.prestitialInit.style.display = 'none'; 127 if (this.prestitialOuter) this.prestitialOuter.style.display = 'none'; 128 if (this.prestitialOOP) this.prestitialOOP.style.display = 'none'; 129 }, 130 131 prestitialAdIsClosed: function() { 132 // Replacement for jQuery :visible psuedo selector 133 var isVisible = function(elem) { 134 return elem && !!(elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length); 135 }; 136 return ( 137 !isVisible(this.prestitialOuter) 138 && !isVisible(this.prestitialInit) 139 && !isVisible(this.prestitialOOP) 140 ); 141 }, 142 143 sentPrestitialEligibleEvent: function() { 144 // wait for obd adn the diagnosticPrintOut object to be available 145 if (typeof window.obd?.diagnosticPrintOut === 'undefined') { 146 setTimeout(this.sentPrestitialEligibleEvent.bind(this), 100); 147 return; 148 } 149 // check if all keys in the diagnosticPrintOut object are true 150 var allKeysTrue = Object.keys(window.obd.diagnosticPrintOut).every(function(key) { 151 return window.obd.diagnosticPrintOut[key] === true; 152 }); 153 // if all keys are true, send the event 154 if (allKeysTrue) { 155 var eventObject = { 156 'event': 'user.prestitial', 157 'user.prestitial': { 158 'name': 'Prestitial Eligible', 159 'eligible': 1, 160 }, 161 }; 162 window.pushGTMEvent(eventObject); 163 } 164 }, 165 166 MIN_VISIBLE_OOP_CONTENT_SIZE: 10, 167 168 hasVisibleOOPContent: function() { 169 // Consider the OOP slot to have real content once an iframe inside it 170 // exceeds this size, rather than checking for a specific vendor's 171 // element (e.g. Airtory's #air_init_holder). Vendor element IDs/classes 172 // are implementation details that can change without notice, and this 173 // doesn't depend on any of them. 174 // 175 // Scoped to iframes specifically (not just any element): ad creatives 176 // are always delivered inside an iframe, while GAM's own wrapper divs 177 // can have non-trivial incidental size (e.g. a container div that's 178 // ~20px tall before the creative renders) that has nothing to do with 179 // whether an ad is actually visible. Checking only iframes avoids 180 // mistaking that scaffolding for real content. 181 // 182 // The size threshold exists because GPT's own 1x1 tracking iframe stays 183 // in the DOM the entire time regardless of the creative's state, so a 184 // plain "is anything non-zero-size" check would never tell a real ad 185 // apart from GPT's own bookkeeping iframe. 186 if (!this.prestitialOOP) return false;
187 188 const iframes = this.prestitialOOP.querySelectorAll('iframe'); 189 for (let i = 0; i < iframes.length; i++) { 190 const el = iframes[i]; 191 if ( 192 el.offsetWidth > this.MIN_VISIBLE_OOP_CONTENT_SIZE && 193 el.offsetHeight > this.MIN_VISIBLE_OOP_CONTENT_SIZE 194 ) { 195 return true; 196 } 197 } 198 return false; 199 }, 200 201 waitForOOPAdContent: function(callback) { 202 const maxAttempts = 50; 203 let attempts = 0; 204 const self = this; 205 206 const checkInterval = setInterval(function() { 207 attempts += 1; 208 209 if (self.hasVisibleOOPContent()) { 210 clearInterval(checkInterval); 211 callback(); 212 } else if (attempts >= maxAttempts) { 213 // Give up after ~2.5 seconds (50 * 50ms) 214 clearInterval(checkInterval); 215 callback(); // Hide overlay anyway 216 } 217 }, 50); // Check every 50ms 218 }, 219 220 watchForOOPAdClose: function(callback) { 221 if (!this.prestitialOOP) return; 222 const self = this; 223 224 if (!self.hasVisibleOOPContent()) { 225 // Nothing ever rendered, so there's no visible-to-hidden transition 226 // left to wait for. Close now instead of leaving the wrapper open 227 // indefinitely. 228 callback(); 229 return; 230 } 231 232 const observer = new MutationObserver(function() { 233 if (!self.hasVisibleOOPContent()) { 234 observer.disconnect(); 235 callback(); 236 } 237 }); 238 239 observer.observe(this.prestitialOOP, { 240 attributes: true, 241 childList: true, 242 subtree: true, 243 }); 244 }, 245 }; 246 247 window.prestitialAds = prestitialAds; 248})();
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.