1/* global googletag, DFPSlots, DFPSlotsLoaded, DFPSlotsTargeting, DFPSlotsRefresh */ 2 3const gptadslots = []; 4 5/** 6 * Defines DFP slot. 7 * 8 * @param {string} slot 9 * Ad slot name. 10 * @param {string} id 11 * Ad slot id. 12 * @param {array<number, number>} sizes 13 * Ad slot sizes. 14 * @param {array<string>} pos 15 * Ad slot pos value. 16 * @param {array<string, array<number, number>>} map 17 * Size mapping. 18 */ 19function DFPDefineSlot(slot, id, sizes, pos, map) { 20 const DFPSlot = sizes.length 21 ? googletag.defineSlot(slot, sizes, id) 22 : googletag.defineOutOfPageSlot(slot, id); 23 if (pos) { 24 DFPSlot.setTargeting('pos', [pos]); 25 } 26 27 // https://support.google.com/dfp_premium/answer/3423562?hl=en 28 if (map.length) { 29 let mapping = googletag.sizeMapping(); 30 for (const item of map) { 31 mapping = mapping.addSize(item[0], item[1]); 32 } 33 const builtMapping = mapping.build(); 34DFPSlot.defineSizeMapping(builtMapping); 35 } 36 37 if (DFPSlot && typeof DFPSlot.addService === 'function') { 38 gptadslots.push(DFPSlot.addService(googletag.pubads())); 39 } 40} 41 42/** 43 * Returns the width of the window. 44 * 45 * @see http://www.howtocreate.co.uk/tutorials/javascript/browserwindow 46 * 47 * @return {number} 48 * Window width. 49 */ 50function windowWidth() { 51 // Non-IE. 52 if (typeof window.innerWidth === 'number') { 53 return window.innerWidth; 54 } 55 56 // IE 6+ in 'standards compliant mode' 57 if (document.documentElement && document.documentElement.clientWidth) { 58 return document.documentElement.clientWidth; 59 } 60 61 // IE 4 compatible. 62 if (document.body && document.body.clientWidth) { 63 return document.body.clientWidth; 64 } 65 66 // Sorry, your browser is weird. 67 return 0; 68} 69 70/** 71 * Checks if screen size matches mobile breakpoint. 72 * @return {number|boolean} 73 * Flag indicating that mobile ads should be used. 74 */ 75function onsiteAdsIsMobile() { 76 const width = windowWidth(); 77 return width && width < 768; 78} 79 80/** 81 * Check if slot should be displayed. 82 * 83 * @param {object} dataset 84 * Ad slot element dataset. 85 * @return {boolean|boolean} 86 * Flag indicating that ad element matches breakpoint. 87 */ 88function onsiteAdsIsDisplayed(dataset) { 89 if (typeof dataset.mobile !== 'undefined') { 90 return onsiteAdsIsMobile() === !!dataset.mobile; 91 } 92 93 const slot = dataset.slot; 94 const DFPSlot = DFPSlots[slot] || {}; 95 const isMobileSlot = slot.indexOf('_mobile') !== -1; 96 const map = DFPSlot.map || []; 97 const isOop = (DFPSlot.map_code || '').toLowerCase() === 'oop' || !dataset.pos; 98 return isOop || onsiteAdsIsMobile() === isMobileSlot || map.length > 0; 99} 100 101/** 102 * Gets parent by selector. 103 * 104 * @param {HTMLElement} element 105 * Element to find the parent for. 106 * @param {string} selector 107 * Selector. 108 * @return {null|*} 109 * Parent found or null if none. 110 */ 111function getParentBySelector(element, selector) { 112 for (; element && element !== document; element = element.parentNode) { 113 if (element.matches(selector)) { 114 return element; 115 } 116 } 117 return null; 118} 119 120/** 121 * Autodetect ad placeholders on the page. 122 */ 123(function () { 124 Object.keys(DFPSlots).forEach(function (key) { 125 DFPSlotsLoaded[key] = {}; 126 }); 127 128 for (const element of document.getElementsByClassName('onsite-ads')) { 129 if ( 130 element.dataset && 131 typeof DFPSlotsLoaded[element.dataset.slot] !== 'undefined' 132 ) { 133 if (onsiteAdsIsDisplayed(element.dataset)) { 134 DFPSlotsLoaded[element.dataset.slot][element.dataset.pos] = element.id; 135 continue; 136 } 137 } 138 139 const parent = getParentBySelector(element, '.block-onsite-ads'); 140 if (!parent) { 141 parent = element; 142 } 143 parent.parentNode.removeChild(parent); 144 } 145})(); 146 147/** 148 * GoogleTag: define slots. 149 */ 150googletag.cmd.push(function () { 151 for (const DFPSlot in DFPSlots) { 152 if (DFPSlots.hasOwnProperty(DFPSlot) && typeof DFPSlotsLoaded[DFPSlot] !== 'undefined') { 153 Object.keys(DFPSlotsLoaded[DFPSlot]).forEach(function (pos) { 154 const id = DFPSlotsLoaded[DFPSlot][pos]; 155 const slot = DFPSlots[DFPSlot].slot; 156 const map = DFPSlots[DFPSlot].map || []; 157 const sizes = DFPSlots[DFPSlot].sizes; 158 DFPDefineSlot(slot, id, sizes, pos, map); 159 }); 160 } 161 } 162 163 googletag.pubads().collapseEmptyDivs(); 164/** 165 * Targeting 166 */ 167 168if (typeof DFPSlotsTargeting !== 'undefined') { 169 Object.entries(DFPSlotsTargeting).forEach(([key, value]) => { 170 googletag.pubads().setTargeting(key, value); 171 }); 172} 173 174// Enable lazyloading. 175 googletag.pubads().enableLazyLoad({ 176 fetchMarginPercent: 25, 177 renderMarginPercent: 0, 178 mobileScaling: 2.0 179 }); 180 181 182googletag.enableServices(); 183}); 184 185// Display ads. 186googletag.cmd.push(function () { 187 const nodes = document.getElementsByClassName('onsite-ads'); 188 if (nodes.length) { 189 for (const node of nodes) { 190 if (node.id && onsiteAdsIsDisplayed(node.dataset)) { 191 googletag.display(node.id); 192 } 193 } 194 } 195}); 196 197 198/** 199 * Quick check that ad slot element is fully visible on screen. 200 * 201 * @param {HTMLElement} element 202 * Element to find the parent for. 203 * @returns {boolean} 204 * Flag if visible. 205 */ 206function onsiteAdsIsElementFullyVisible(element) { 207 const rect = element.getBoundingClientRect(); 208 return rect.top > 0 && rect.bottom < windowHeight(); 209} 210 211/** 212 * Quick check that ad slot is visible partially on screen. 213 * 214 * @param gptadslot 215 * DFP ad slot. 216 * @returns {boolean} 217 * Flag if visible. 218 */ 219function onsiteAdsIsSlotFullyVisible(gptadslot) { 220 const id = gptadslot.getSlotElementId(); 221 const element = document.getElementById(id); 222 return element ? onsiteAdsIsElementFullyVisible(element) : false;
223} 224 225/** 226 * Get slot name. 227 * 228 * @param gptadslot 229 * @returns {string|boolean} 230 */ 231function onsiteAdsGetSlotPos(gptadslot) { 232 const id = gptadslot.getSlotElementId(); 233 const element = document.getElementById(id); 234 return element && element.dataset && element.dataset.pos || false; 235} 236 237// Refresh ads. 238if (DFPSlotsRefresh && DFPSlotsRefresh.interval) { 239 let refreshLimit = parseInt(DFPSlotsRefresh.limit || -1); 240 if (refreshLimit) { 241 const refreshVisible = !!DFPSlotsRefresh.visible; 242 const refreshSlots = DFPSlotsRefresh.slots || []; 243 244 window.gptRefreshCounter = 0; 245 googletag.cmd.push(function () { 246 window.gptRefreshInterval = setInterval(function () { 247 console.log('gpt:refresh', 'tick', window.gptRefreshCounter % DFPSlotsRefresh.interval); 248 if (++window.gptRefreshCounter % DFPSlotsRefresh.interval === 0) { 249 if (!refreshVisible) { 250 googletag.pubads().refresh(gptadslots); 251 refreshLimit--; 252 } else { 253 let refreshed = false; 254 gptadslots.forEach(function (gptadslot) { 255 const pos = onsiteAdsGetSlotPos(gptadslot); 256 if (refreshSlots.length && refreshSlots.indexOf(pos) === -1) { 257 return; 258 } 259 if (onsiteAdsIsSlotFullyVisible(gptadslot)) { 260 googletag.pubads().refresh([gptadslot]); 261 console.log('gpt:refresh', 'refreshed', pos); 262 refreshed = true; 263 } else { 264 console.log('gpt:refresh', 'not in view', pos); 265 } 266 }); 267 if (refreshed) { 268 refreshLimit--; 269 } 270 } 271 console.log('gpt:refresh', 'limit', refreshLimit); 272 if (refreshLimit === 0) { 273 clearInterval(window.gptRefreshInterval); 274 } 275 } 276 }, 1000); 277 }); 278 } 279}
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.