1(function (window) { 2 'use strict'; 3 if (!window.ManualsPremiumUi) { 4 (function installPremiumUi(document) { 5 var key = 'manuals-premium-ui-v1'; 6 var ttl = 86400000; 7 var pagePremium = false; 8 9 function installSuppressionStyle() { 10 if (document.getElementById('manuals-premium-ad-suppression')) return; 11 var style = document.createElement('style'); 12 style.id = 'manuals-premium-ad-suppression'; 13 style.textContent = 'html.mp-premium header .site-description{font-size:0!important;line-height:0!important;white-space:nowrap}html.mp-premium header .site-description::after{content:"PREMIUM";display:inline-block;font-size:.78rem;line-height:1.2;font-weight:800;letter-spacing:.08em;color:#0073aa}html.mp-premium .mp-ad-slot,html.mp-premium .premium-ad-section,html.mp-premium [id^="ezoic-pub-ad-"],html.mp-premium .ezoic-ad,html.mp-premium [class^="ezoic-ad"],html.mp-premium [class*=" ezoic-ad"],html.mp-premium [data-ezoic-ad],html.mp-premium [data-ez-name],html.mp-premium [id^="div-gpt-ad-"],html.mp-premium [id^="google_ads_iframe"],html.mp-premium .ez-placeholder,html.mp-premium [class*="ez-placeholder"],html.mp-premium .ezmob-footer,html.mp-premium [id^="ezmobfooter"],html.mp-premium .ez-sticky-video,html.mp-premium [id^="ez-video"],html.mp-premium ins.adsbygoogle,html.mp-premium iframe[src*="googlesyndication.com"]{display:none!important;min-height:0!important;height:0!important;margin:0!important;padding:0!important;border:0!important;overflow:hidden!important}'; 14 (document.head || document.documentElement).appendChild(style); 15 } 16 17 function suppressAdRuntime(active) { 18 if (!active || window.__manualsPremiumAdsDisabled) return; 19 window.__manualsPremiumAdsDisabled = true; 20 window.ezstandalone = window.ezstandalone || {}; 21 window.ezstandalone.cmd = window.ezstandalone.cmd || []; 22 window.ezstandalone.cmd.unshift(function () { 23 try { 24 if (typeof window.ezstandalone.setEzoicAnchorAd === 'function') window.ezstandalone.setEzoicAnchorAd(false); 25 if (typeof window.ezstandalone.config === 'function') { 26 window.ezstandalone.config({disableInterstitial:true,disableVideo:true,disableLeftSideRail:true,disableRightSideRail:true,disableSidebarFloating:true}); 27 } 28 } catch (ignore) {} 29 }); 30 try { 31 if (window.ezsubscriptions && typeof window.ezsubscriptions.disableAds === 'function') window.ezsubscriptions.disableAds(); 32 } catch (ignore) {} 33 } 34 35 function serverMembershipActive() { 36 var bodyMember = document.body && document.body.getAttribute('data-member') === '1'; 37 var premium = window.MANUALS_PREMIUM || {}; 38 var account = window.MANUALS_ACCOUNT_PREMIUM || {}; 39 return bodyMember || premium.member === true || account.active === true || account.recordedActive === true; 40 } 41 42 function isActive() { 43 if (pagePremium) return true; 44 try { 45 var value = JSON.parse(window.localStorage.getItem(key) || 'null'); 46 if (value && value.active === true && Number(value.expires) > Date.now()) return true; 47 if (value) window.localStorage.removeItem(key); 48 } catch (ignore) {} 49 return false; 50 } 51 52 function updateLabel(active) { 53 if (!document.body) return; 54 document.querySelectorAll('header *').forEach(function (node) { 55 if (active && node.textContent.trim() === 'User Manuals Simplified.') { 56 node.textContent = 'PREMIUM'; 57 node.setAttribute('data-manuals-premium-label', '1'); 58 } else if (!active && node.getAttribute('data-manuals-premium-label') === '1') { 59 node.textContent = 'User Manuals Simplified.'; 60 node.removeAttribute('data-manuals-premium-label'); 61 } 62 }); 63 } 64 65 function apply(value) { 66 var active = typeof value === 'boolean' ? value : isActive(); 67 if (active) pagePremium = true; 68 else if (pagePremium) active = true; 69 installSuppressionStyle(); 70 document.documentElement.classList.toggle('mp-premium', active); 71 updateLabel(active); 72 suppressAdRuntime(active); 73 return active; 74 } 75 76 function set(active) { 77 try { 78 if (active) { 79 window.localStorage.setItem(key, JSON.stringify({active: true, expires: Date.now() + ttl})); 80 } else { 81 window.localStorage.removeItem(key); 82 } 83 } catch (ignore) {} 84 apply(!!active); 85 try { 86 window.dispatchEvent(new CustomEvent('manualsPremium:change', {detail: {active: !!active}})); 87 } catch (ignore) {} 88 return !!active; 89 } 90 91 window.ManualsPremiumUi = {isActive: isActive, apply: apply, set: set}; 92 if (serverMembershipActive()) set(true); else apply(); 93 if (document.readyState === 'loading') { 94 document.addEventListener('DOMContentLoaded', function () { 95 if (serverMembershipActive()) set(true); else apply(); 96 }, {once: true}); 97 } 98 window.addEventListener('storage', function (event) { if (event.key === key) apply(); }); 99 }(document)); 100 } 101 if (window.ManualsEzoicAccountLink) return; 102 103 var domainId = 309095; 104 var authReady = null; 105 var subscriptionReadiness = new WeakMap(); 106 var authRestoration = new WeakMap(); 107 108 async function ensureSubscriptionsReady(api) { 109 if (!api || typeof api.getProducts !== 'function') return; 110 var ready = subscriptionReadiness.get(api); 111 if (!ready) {
112 // The SDK command queue can flush before magic-link redemption finishes. 113 // getProducts() awaits that bootstrap; getSessionToken() does not. 114 ready = Promise.resolve().then(function () { return api.getProducts(); }); 115 subscriptionReadiness.set(api, ready); 116 ready.catch(function () { subscriptionReadiness.delete(api); }); 117 } 118 await ready; 119 } 120 121 function ensureEzAuth() { 122 if (authReady) return authReady; 123 authReady = new Promise(function (resolve) { 124 var configured = window.ezAuthConfig && Number(window.ezAuthConfig.domainId || 0); 125 if (configured > 0 && configured !== domainId) { 126 resolve(null); 127 return; 128 } 129 window.ezAuthConfig = Object.assign({}, window.ezAuthConfig || {}, {domainId: domainId}); 130 131 function initialized() { 132 return window.ezAuth 133 && typeof window.ezAuth.isInitialized === 'function' 134 && window.ezAuth.isInitialized(); 135 } 136 if (initialized()) { 137 if (typeof window.ezAuth.getDomainId === 'function' 138 && Number(window.ezAuth.getDomainId() || 0) !== domainId) { 139 resolve(null); 140 return; 141 } 142 resolve(window.ezAuth); 143 return; 144 } 145 if (window.ezAuth && typeof window.ezAuth.init === 'function') { 146 try { window.ezAuth.init({domainId: domainId}); } catch (ignore) {} 147 } else if (!document.querySelector('script[src="https://www.ezojs.com/detroitchicago/ferndale.js"]')) { 148 var script = document.createElement('script'); 149 script.src = 'https://www.ezojs.com/detroitchicago/ferndale.js'; 150 script.async = true; 151 document.head.appendChild(script); 152 } 153 154 var deadline = Date.now() + 10000; 155 (function poll() { 156 if (initialized()) { 157 resolve(window.ezAuth); 158 } else if (Date.now() >= deadline) { 159 authReady = null; 160 resolve(null); 161 } else { 162 window.setTimeout(poll, 50); 163 } 164 }()); 165 }); 166 return authReady; 167 } 168 169 async function credentials(api) { 170 await ensureSubscriptionsReady(api); 171 var accountId = 0; 172 var accessToken = ''; 173 try { 174 var auth = await ensureEzAuth(); 175 if (auth && typeof auth.getUser === 'function') { 176 if (typeof auth.restore === 'function' && !auth.getUser()) { 177 var restoring = authRestoration.get(auth); 178 if (!restoring) { 179 // Anonymous restore emits onChange too. Restore once rather than 180 // starting another restore for every reconciliation it triggers. 181 restoring = Promise.resolve().then(function () { return auth.restore(); }); 182 authRestoration.set(auth, restoring); 183 restoring.catch(function () { authRestoration.delete(auth); }); 184 } 185 await restoring; 186 } 187 var user = auth.getUser(); 188 accountId = user && Number.isSafeInteger(Number(user.accountId)) ? Number(user.accountId) : 0; 189 if (accountId > 0 && typeof auth.getAccessToken === 'function') { 190 accessToken = await auth.getAccessToken() || ''; 191 } 192 } 193 } catch (ignore) { 194 accountId = 0; 195 accessToken = ''; 196 } 197 198 // Restoration can complete while the identity calls above are awaiting. 199 // Sample the current reader session after those awaits, for every request. 200 var readerToken = null; 201 try { readerToken = api && api.getSessionToken ? api.getSessionToken() : null; } catch (ignore) {} 202 203 if ((accountId <= 0 || !accessToken) && !readerToken) { 204 return null; 205 } 206 207 var result = {reader_token: readerToken}; 208 if (accountId > 0 && accessToken) { 209 result.ezoic_account_id = accountId; 210 result.ezoic_access_token = accessToken; 211 } 212 return result; 213 } 214 215 async function sync(api, endpoint, payload) { 216 var identity = await credentials(api); 217 if (!identity) return {ok: false, anonymous: true}; 218 var response = await fetch(endpoint || '/account/api/premium/', { 219 method: 'POST', 220 credentials: 'same-origin', 221 cache: 'no-store', 222 headers: {'Content-Type': 'application/json', 'Accept': 'application/json'}, 223 body: JSON.stringify(Object.assign(identity, payload || {})) 224 }); 225 var data = await response.json().catch(function () { return {}; }); 226 if (!response.ok || !data.ok) { 227 var error = new Error(data.error || 'ezoic_link_failed'); 228 error.code = data.error || 'ezoic_link_failed'; 229 error.status = response.status; 230 throw error; 231 } 232 return data; 233 } 234 235 async function login(api) { 236 var identity = await credentials(api); 237 if (identity) { 238 return api.login(); 239 } 240 var staleReader = null; 241 try { staleReader = api && api.getSessionToken ? api.getSessionToken() : null; } catch (ignore) {} 242 if (staleReader && api && typeof api.logout === 'function') { 243 await api.logout(); 244 } 245 return api.login(); 246 } 247 248 function message(error) { 249 if (error && error.code === 'account_link_conflict') { 250 return 'This Ezoic account is already linked to a different Manuals+ account. Your current Manuals+ login was not changed.'; 251 } 252 if (error && error.code === 'ezoic_identity_invalid') { 253 return 'Your email-link sign-in expired. Use Login with Email Link again to reconnect it safely.'; 254 } 255 return 'The Ezoic account link could not be verified. Your existing Manuals+ login was not changed.'; 256 } 257 258 window.ManualsEzoicAccountLink = {ready: ensureEzAuth, credentials: credentials, sync: sync, login: login, message: message}; 259}(window));
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.