1/** 2 * Freestar Ad Loader â gated by SESSuserinfo cookie, with channel 3 * detection replicating the original PHP is_front_page()/is_single()/ 4 * is_tax()/etc. conditions using WordPress's default body classes. 5 * 6 * IMPORTANT: window.freestar (and enabled_slots) is created 7 * UNCONDITIONALLY and immediately, regardless of subscriber status. 8 * This is required because ad-slot registration scripts scattered 9 * throughout the page (e.g. freestar.config.enabled_slots.push(...)) 10 * run independently of this file and will throw 11 * "freestar is not defined" if the object doesn't exist yet â even 12 * for users where ads end up disabled. 13 */ 14 15(function () { 16 'use strict'; 17 18 /* ---------------------------------------------------- 19 * 0. ALWAYS create the freestar shell first, before any 20 * other logic. This guarantees that any ad-slot script 21 * on the page â no matter where it sits in the HTML, 22 * and regardless of subscriber status â can safely call 23 * freestar.config.enabled_slots.push(...) without error. 24 * ---------------------------------------------------- */ 25 window.freestar = window.freestar || {}; 26 freestar.hitTime = freestar.hitTime || Date.now(); 27 freestar.queue = freestar.queue || []; 28 freestar.config = freestar.config || {}; 29 freestar.config.enabled_slots = freestar.config.enabled_slots || []; 30 freestar.config.disabledProducts = freestar.config.disabledProducts || {}; 31 freestar.config.channel = freestar.config.channel || 'tn_general_company'; 32 freestar.debug = window.location.search.indexOf('fsdebug') !== -1; 33 34 /* ---------------------------------------------------- 35 * 1. Read a cookie by name 36 * ---------------------------------------------------- */ 37 function getCookie(name) { 38 var match = document.cookie.match( 39 new RegExp('(?:^|; )' + name.replace(/([.$?*|{}()[\]\\/+^])/g, '\\$1') + '=([^;]*)') 40 ); 41 return match ? decodeURIComponent(match[1]) : null; 42 } 43 44 /* ---------------------------------------------------- 45 * 2. Check SESSuserinfo cookie for premium_subscriber role 46 * ---------------------------------------------------- */ 47 function isPremiumSubscriber() { 48 var raw = getCookie('SESSuserinfo'); 49 if (!raw) { 50 return false; 51 } 52 53 var userInfo; 54 try { 55 userInfo = JSON.parse(atob(raw)); 56 } catch (e) { 57 return false; 58 } 59 60 return ( 61 userInfo && 62 Array.isArray(userInfo.roles) && 63 userInfo.roles.indexOf('premium_subscriber') !== -1 64 ); 65 } 66 67 /* ---------------------------------------------------- 68 * 2b. Check SESSexpireduser cookie 69 * If this equals '1', the subscriber's access has 70 * expired, so ads should NOT be disabled even though 71 * the premium_subscriber role is still present. 72 * ---------------------------------------------------- */ 73 function isExpiredUser() { 74 return getCookie('SESSexpireduser') === '1'; 75 } 76 77 /* ---------------------------------------------------- 78 * 2c. Final decision: disable ads only for an ACTIVE 79 * premium subscriber (role present AND not expired) 80 * ---------------------------------------------------- */ 81 function shouldDisableAds() { 82 return isPremiumSubscriber() && !isExpiredUser(); 83 } 84 85 /* ---------------------------------------------------- 86 * 3. Determine freestar.config.channel 87 * Mirrors the original PHP if/elseif chain, using the 88 * body classes WordPress adds for the same conditions. 89 * ---------------------------------------------------- */ 90 function determineChannel() { 91 var body = document.body.classList; 92 var path = window.location.pathname; 93 94 if (body.contains('home')) { 95 return 'tn_home'; 96 } 97 98 if (body.contains('single-article') || body.contains('single-podcast')) { 99 return 'tn_article'; 100 } 101 102 if (body.contains('post-type-archive-magazineissue')) { 103 return 'tn_issue_landing'; 104 } 105 106 if (body.contains('tax-admin-taxonomy')) { 107 return 'tn_admin_taxonomy'; 108 } 109 110 if (body.contains('tax-subject')) { 111 return 'tn_subject_landing'; 112 } 113 114 if (body.contains('tax-keyword') || body.contains('tax-special')) { 115 return 'tn_keyword_landing'; 116 } 117 118 if (path.indexOf('take-action/') !== -1) { 119 return 'tn_take_action'; 120 } 121 122 // page-template-templates-primary_landing_page-php (dots/slashes -> dashes) 123 var landingTemplateClass = Array.prototype.find.call(body, function (cls) { 124 return cls.indexOf('page-template-templates-primary_landing_page') === 0; 125 }); 126 if (landingTemplateClass) {
127 var parts = path.split('/').filter(Boolean); 128 var last = parts.length ? parts[parts.length - 1] : ''; 129 return 'tn_' + last; 130 } 131 132 if (body.contains('search-results') || body.contains('search-no-results')) { 133 return 'tn_search'; 134 } 135 136 return 'tn_general_company'; 137 } 138 139 function setChannel() { 140 freestar.config.channel = determineChannel(); 141 } 142 143 if (document.body) { 144 setChannel(); 145 } else { 146 document.addEventListener('DOMContentLoaded', setChannel); 147 } 148 149 /* ---------------------------------------------------- 150 * 4. If an active (non-expired) subscriber: the freestar 151 * shell already exists (step 0), so no ad-slot script 152 * on the page will error out â it'll just push into an 153 * array that never gets used. Stop here WITHOUT loading 154 * preconnects, stylesheet, or pubfig.min.js. 155 * ---------------------------------------------------- */ 156 if (shouldDisableAds()) { 157 return; 158 } 159 160 /* ---------------------------------------------------- 161 * 4b. Reserve fixed-size space for every ad slot to 162 * prevent Cumulative Layout Shift (CLS). 163 * Runs only for non-subscribers, since active 164 * subscribers already returned above at Step 4. 165 * ---------------------------------------------------- */ 166 function parseAdSizes(dataAttr) { 167 return dataAttr 168 .split(/\s+/) 169 .map(function (s) { return s.replace(/_/g, ''); }) 170 .filter(function (s) { return s.indexOf('x') !== -1; }) 171 .map(function (s) { 172 var parts = s.split('x'); 173 return { w: parseInt(parts[0], 10), h: parseInt(parts[1], 10) }; 174 }); 175 } 176 177 function lockAdSlotSize(slot, minWidth, minHeight) { 178 slot.style.minWidth = minWidth + 'px'; 179 slot.style.minHeight = minHeight + 'px'; 180 181 var observer = new MutationObserver(function (mutations) { 182 mutations.forEach(function (m) { 183 if (m.attributeName === 'style') { 184 var currentMinH = parseInt(slot.style.minHeight, 10); 185 if (!currentMinH || currentMinH < minHeight) { 186 slot.style.minHeight = minHeight + 'px'; 187 slot.style.minWidth = minWidth + 'px'; 188 } 189 } 190 }); 191 }); 192 193 observer.observe(slot, { attributes: true, attributeFilter: ['style'] }); 194 } 195 196 function reserveAdSlotSpace() { 197 document.querySelectorAll('[data-freestar-ad]').forEach(function (slot) { 198 var sizes = parseAdSizes(slot.getAttribute('data-freestar-ad')); 199 if (sizes.length === 0) return; 200 201 var maxW = Math.max.apply(null, sizes.map(function (s) { return s.w; })); 202 var maxH = Math.max.apply(null, sizes.map(function (s) { return s.h; })); 203 204 lockAdSlotSize(slot, maxW, maxH); 205 }); 206 } 207 208 if (document.readyState === 'loading') { 209 document.addEventListener('DOMContentLoaded', reserveAdSlotSpace); 210 } else { 211 reserveAdSlotSpace(); 212 } 213 214 /* ---------------------------------------------------- 215 * 5. Preconnects + stylesheet 216 * ---------------------------------------------------- */ 217 var head = document.head; 218 219 [ 220 'https://a.pub.network/', 221 'https://b.pub.network/', 222 'https://c.pub.network/', 223 'https://d.pub.network/', 224 'https://c.amazon-adsystem.com', 225 'https://s.amazon-adsystem.com', 226 'https://btloader.com/', 227 'https://api.btloader.com/', 228 'https://confiant-integrations.global.ssl.fastly.net' 229 ].forEach(function (href) { 230 var link = document.createElement('link'); 231 link.rel = 'preconnect'; 232 link.href = href; 233 link.crossOrigin = 'anonymous'; 234 head.appendChild(link); 235 }); 236 237 var stylesheet = document.createElement('link'); 238 stylesheet.rel = 'stylesheet'; 239 stylesheet.href = 'https://a.pub.network/thenation-com/cls.css'; 240 head.appendChild(stylesheet); 241 242 freestar.queue.push(function () { 243 googletag.pubads().set('page_url', 'https://www.thenation.com'); 244 }); 245 246 /* ---------------------------------------------------- 247 * 6. Disable video if .tn-lightbox exists 248 * ---------------------------------------------------- */ 249 (function () { 250 function disableVideo() { 251 freestar.config.disabledProducts.video = true; 252 } 253 if (document.querySelector('.tn-lightbox')) { 254 disableVideo(); 255 return; 256 } 257 var observer = new MutationObserver(function () { 258 if (document.querySelector('.tn-lightbox')) { 259 disableVideo(); 260 observer.disconnect(); 261 } 262 }); 263 observer.observe(document.documentElement, { childList: true, subtree: true }); 264 })(); 265 266 /* ---------------------------------------------------- 267 * 7. Only load the recovery script + pubfig.min.js if 268 * this page actually has ad slots registered. 269 * 270 * Ad-slot scripts elsewhere on the page (e.g. 271 * freestar.config.enabled_slots.push(...)) are plain 272 * synchronous inline scripts, so by the time 273 * DOMContentLoaded fires, every one of them has 274 * already run. Checking enabled_slots.length at that 275 * point tells us definitively whether this page has 276 * any ads to show â if not, skip both scripts entirely. 277 * ---------------------------------------------------- */ 278 var tnads = new URLSearchParams(window.location.search).get('tnads'); 279 280 function loadAdsIfPresent() { 281 if (tnads !== null && tnads === 'false') { 282 return; 283 } 284 285 if (freestar.config.enabled_slots.length === 0) { 286 return;
286 // no ad slots on this page â nothing to recover or serve 287 } 288 289 function loadPubfig() { 290 var pubfigScript = document.createElement('script'); 291 pubfigScript.async = true; 292 pubfigScript.src = 293 'https://a.pub.network/thenation-com' + 294 (freestar.debug ? '/qa/pubfig.min.js' : '/pubfig.min.js'); 295 document.head.appendChild(pubfigScript); 296 297 freestar.initCallback = function () { 298 if (freestar.config.enabled_slots.length === 0) { 299 freestar.initCallbackCalled = false; 300 } else { 301 freestar.newAdSlots(freestar.config.enabled_slots, freestar.config.channel); 302 } 303 }; 304 } 305 306 var recoveryScript = document.createElement('script'); 307 recoveryScript.src = 308 (window.freestarConfig && window.freestarConfig.recoveryScriptUrl) || 309 'https://www.thenation.com/wp-content/themes/thenation-2023/js/freestar-adblock-recovery.js'; 310 recoveryScript.addEventListener('load', loadPubfig); 311 recoveryScript.addEventListener('error', loadPubfig); // don't block ads if recovery script fails to load 312 if (recoveryScript.src) { 313 document.head.appendChild(recoveryScript); 314 } else { 315 loadPubfig(); // no recovery URL configured â load ads anyway 316 } 317 } 318 319 if (document.readyState === 'loading') { 320 document.addEventListener('DOMContentLoaded', loadAdsIfPresent); 321 } else { 322 // DOM already parsed (e.g. script loaded late) â slots already registered, check now 323 loadAdsIfPresent(); 324 } 325})();
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.