1import {initializeApp} from 'https://www.gstatic.com/firebasejs/10.5.0/firebase-app.js'; 2import {getAuth, connectAuthEmulator, onAuthStateChanged, signOut, sendEmailVerification, signInWithCustomToken} from 'https://www.gstatic.com/firebasejs/10.5.0/firebase-auth.js'; 3 4const app = initializeApp({ 5 apiKey: 'AIzaSyAprP1AGzXG5jID7UFJD0lpwTZYRgYnq58', 6 authDomain: 'aznude-fea6a.firebaseapp.com', 7 databaseURL: 'https://aznude-fea6a.firebaseio.com', 8 projectId: 'aznude-fea6a', 9 storageBucket: 'aznude-fea6a.appspot.com', 10 messagingSenderId: '168249351670', 11}); 12 13const auth = getAuth(app); 14 15// Separate Auth instance from src/firebase.js (this one's loaded straight from the 16// gstatic CDN, not bundled via Vite) â needs its own emulator redirect, same reasoning, 17// and the same ?backend=prod / localStorage override (see src/firebase.js for why). 18const backendParam = new URLSearchParams(location.search).get('backend'); 19if (backendParam === 'prod' || backendParam === 'local') { 20 localStorage.setItem('aznude:backendTarget', backendParam); 21} 22const BACKEND_OVERRIDE = localStorage.getItem('aznude:backendTarget'); 23 24const IS_LOCAL = (location.hostname === 'localhost' || location.hostname === '127.0.0.1') 25 && BACKEND_OVERRIDE !== 'prod'; 26if (IS_LOCAL) { 27 connectAuthEmulator(auth, 'http://127.0.0.1:9099', { disableWarnings: true }); 28} 29 30// /** 31// * Cross-Domain Authentication Module 32// * Handles seamless authentication across aznude.com, azmen.com, azfans.com, and aznudefeet.com 33// */ 34// class CrossDomainAuth { 35// constructor(config) { 36// this.backendUrl = `${PRIMARY_BASE}/auth`; 37// this.firebase = config.firebase; 38// this.debug = config.debug || false; 39// } 40 41// async createSession(user) { 42// try { 43// const token = await user.getIdToken(); 44 45// if (this.debug) { 46// console.log('[CrossDomainAuth] Creating session for user:', user.uid); 47// } 48 49// const response = await fetch(`${this.backendUrl}/create-session`, { 50// method: 'POST', 51// credentials: 'include', 52// headers: { 53// 'Content-Type': 'application/json', 54// }, 55// body: JSON.stringify({ token: token }) 56// }); 57 58// if (!response.ok) { 59// throw new Error(`Failed to create session: ${response.statusText}`); 60// } 61 62// const data = await response.json(); 63 64// if (this.debug) { 65// console.log('[CrossDomainAuth] Session created:', data); 66// } 67 68// return data; 69// } catch (error) { 70// console.error('[CrossDomainAuth] Error creating session:', error); 71// throw error; 72// } 73// } 74 75// async checkAndRestoreSession() { 76// try { 77// const currentUser = this.firebase.currentUser; 78// if (currentUser) { 79// if (this.debug) { 80// console.log('[CrossDomainAuth] User already signed in locally'); 81// } 82// return currentUser; 83// } 84 85// if (this.debug) { 86// console.log('[CrossDomainAuth] Checking for cross-domain session...'); 87// } 88 89// const response = await fetch(`${this.backendUrl}/check-session`, { 90// method: 'GET', 91// credentials: 'include', 92// }); 93 94// if (!response.ok) { 95// if (this.debug) { 96// console.log('[CrossDomainAuth] No valid session found'); 97// } 98// return null; 99// } 100 101// const data = await response.json(); 102 103// if (this.debug) { 104// console.log('[CrossDomainAuth] Valid session found, signing in with custom token...'); 105// } 106 107// const userCredential = await signInWithCustomToken(this.firebase, data.custom_token); 108 109// if (this.debug) { 110// console.log('[CrossDomainAuth] Successfully restored session for:', userCredential.user.email); 111// } 112 113// return userCredential.user;
114// } catch (error) { 115// if (this.debug) { 116// console.log('[CrossDomainAuth] Error checking session:', error.message); 117// } 118// return null; 119// } 120// } 121 122// async deleteSession() { 123// try { 124// if (this.debug) { 125// console.log('[CrossDomainAuth] Deleting session...'); 126// } 127 128// const response = await fetch(`${this.backendUrl}/delete-session`, { 129// method: 'POST', 130// credentials: 'include', 131// }); 132 133// if (!response.ok) { 134// throw new Error(`Failed to delete session: ${response.statusText}`); 135// } 136 137// await signOut(this.firebase); 138 139// if (this.debug) { 140// console.log('[CrossDomainAuth] Session deleted and user signed out'); 141// } 142 143// return true; 144// } catch (error) { 145// console.error('[CrossDomainAuth] Error deleting session:', error); 146// throw error; 147// } 148// } 149 150// async signInWithEmailAndPassword(email, password) { 151// try { 152// const { signInWithEmailAndPassword: firebaseSignIn } = await import('https://www.gstatic.com/firebasejs/10.5.0/firebase-auth.js'); 153// const userCredential = await firebaseSignIn(this.firebase, email, password); 154// await this.createSession(userCredential.user); 155// return userCredential.user; 156// } catch (error) { 157// console.error('[CrossDomainAuth] Login error:', error); 158// throw error; 159// } 160// } 161 162// async signOut() { 163// return await this.deleteSession(); 164// } 165// } 166 167// // Initialize cross-domain auth 168// const crossDomainAuth = new CrossDomainAuth({ 169// firebase: auth, 170// debug: false // Set to true for debugging 171// }); 172 173// // Make it globally available 174// window.crossDomainAuth = crossDomainAuth; 175 176/* SENTRY LAZY LOADER SHIM (imports on demand) */ 177let __sentryModule = null; 178let __sentryInitOptions = null; 179let __sentryQueue = []; 180let __sentryLoading = null; 181let __sentryWantTracing = false; 182let __sentryWantReplay = false; 183let __sentryConsoleCfg = null; 184 185function __sentryCloneWithoutIntegrations(opts) { 186 const base = {}; 187 if (!opts) return base; 188 for (const k in opts) { 189 if (Object.prototype.hasOwnProperty.call(opts, k) && k !== 'integrations') { 190 base[k] = opts[k]; 191 } 192 } 193 return base; 194} 195 196function __sentryBuildIntegrations(mod) { 197 const ints = []; 198 try { 199 if (__sentryWantTracing && typeof mod.browserTracingIntegration === 'function') { 200 ints.push(mod.browserTracingIntegration()); 201 } 202 } catch (_e) {} 203 try { 204 if (__sentryWantReplay && typeof mod.replayIntegration === 'function') { 205 ints.push(mod.replayIntegration()); 206 } 207 } catch (_e) {} 208 try { 209 if (__sentryConsoleCfg && typeof mod.consoleLoggingIntegration === 'function') { 210 ints.push(mod.consoleLoggingIntegration(__sentryConsoleCfg)); 211 } 212 } catch (_e) {} 213 return ints; 214} 215 216function __sentryLoad() { 217 if (__sentryModule) return Promise.resolve(__sentryModule); 218 if (__sentryLoading) return __sentryLoading; 219 220 __sentryLoading = import('https://cdn.jsdelivr.net/npm/@sentry/browser@9/+esm') 221 .then(mod => { 222 __sentryModule = mod; 223 try { 224 const base = __sentryCloneWithoutIntegrations(__sentryInitOptions); 225 base.integrations = __sentryBuildIntegrations(mod); 226 mod.init(base); 227 } catch (_e) {} 228 const queued = __sentryQueue.slice(); 229 __sentryQueue.length = 0; 230 for (let i = 0; i < queued.length; i++) { 231 try { queued[i](mod); } catch (_e) {} 232 } 233 return mod; 234 }) 235 .catch(_err => { 236 return null; 237 }); 238 239 return __sentryLoading; 240} 241 242const Sentry = { 243 init: function (options) { 244 __sentryInitOptions = options || {}; 245 }, 246 browserTracingIntegration: function () { 247 __sentryWantTracing = true; 248 return {}; 249 }, 250 replayIntegration: function () { 251 __sentryWantReplay = true; 252 return {}; 253 }, 254 consoleLoggingIntegration: function (cfg) { 255 __sentryConsoleCfg = cfg || {}; 256 return {}; 257 }, 258 addBreadcrumb: function (crumb) { 259 __sentryQueue.push(function (mod) { 260 try { mod.addBreadcrumb(crumb); } catch (_e) {} 261 }); 262 }, 263 captureException: function (err, options) { 264 __sentryQueue.push(function (mod) { 265 try { mod.captureException(err, options); } catch (_e) {} 266 }); 267 __sentryLoad(); 268 }, 269 setUser: function (userObjOrNull) { 270 __sentryQueue.push(function (mod) { 271 try { mod.setUser(userObjOrNull); } catch (_e) {} 272 }); 273 if (userObjOrNull) { 274 __sentryLoad(); 275 } 276 }, 277 setContext: function (key, ctx) { 278 __sentryQueue.push(function (mod) { 279 try { mod.setContext(key, ctx); } catch (_e) {} 280 }); 281 } 282}; 283 284if (typeof window !== 'undefined') { 285 window.Sentry = Sentry; 286} 287 288Sentry.init({ 289 dsn: "https://eb92d8c5be66047bf0250a811fb742cd@o4507899926740992.ingest.us.sentry.io/4509911205871616", 290 integrations: [ 291 Sentry.browserTracingIntegration(), 292 Sentry.replayIntegration() 293 ], 294 tracesSampleRate: 0.1, 295 replaysSessionSampleRate: 0.01, 296 replaysOnErrorSampleRate: 0.1 297}); 298 299if (!window.UniversalModal) { 300 class UniversalModal { 301 constructor(element, options = {}) { 302 this.element = typeof element === 'string' ? document.querySelector(element) : element; 303 this.options = { 304 backdrop: options.backdrop !== undefined ? options.backdrop : true, 305 keyboard: options.keyboard !== undefined ? options.keyboard : true, 306 focus: options.focus !== undefined ? options.focus : true, 307 show: options.show !== undefined ? options.show : false 308 }; 309 310 this.isShown = false;
311 this.backdrop = null; 312 this.scrollbarWidth = 0; 313 314 this._init(); 315 316 if (this.options.show) { 317 this.show(); 318 } 319 } 320 321 _init() { 322 const closeButtons = this.element.querySelectorAll('[data-dismiss="universal-modal"]'); 323 closeButtons.forEach(btn => { 324 btn.addEventListener('click', (e) => { 325 e.preventDefault(); 326 this.hide(); 327 }); 328 }); 329 330 if (this.options.backdrop) { 331 this.element.addEventListener('click', (e) => { 332 if (e.target === this.element) { 333 this.hide(); 334 } 335 }); 336 } 337 338 if (this.options.keyboard) { 339 document.addEventListener('keydown', (e) => { 340 if (e.key === 'Escape' && this.isShown) { 341 this.hide(); 342 } 343 }); 344 } 345 } 346 347 show() { 348 if (this.isShown) return; 349 350 this.isShown = true; 351 352 this.scrollbarWidth = this._getScrollbarWidth(); 353 354 if (this.scrollbarWidth > 0) { 355 document.body.style.paddingRight = this.scrollbarWidth + 'px'; 356 } 357 358 document.body.classList.add('universal-modal-open'); 359 360 if (this.options.backdrop) { 361 this._showBackdrop(); 362 } 363 364 this.element.style.display = 'block'; 365 this.element.setAttribute('aria-hidden', 'false'); 366 367 setTimeout(() => { 368 this.element.classList.add('show'); 369 370 if (this.options.focus) { 371 this.element.focus(); 372 } 373 }, 10); 374 375 this._triggerEvent('show'); 376 } 377 378 hide() { 379 if (!this.isShown) return; 380 381 this.isShown = false; 382 383 this.element.classList.remove('show'); 384 385 setTimeout(() => { 386 this.element.style.display = 'none'; 387 this.element.setAttribute('aria-hidden', 'true'); 388 389 if (this.backdrop) { 390 this._hideBackdrop(); 391 } 392 393 document.body.classList.remove('universal-modal-open'); 394 document.body.style.paddingRight = ''; 395 396 this._triggerEvent('hide'); 397 }, 300); 398 } 399 400 toggle() { 401 this.isShown ? this.hide() : this.show(); 402 } 403 404 _showBackdrop() { 405 this.backdrop = document.createElement('div'); 406 this.backdrop.className = 'universal-modal-backdrop fade'; 407 document.body.appendChild(this.backdrop); 408 409 setTimeout(() => { 410 this.backdrop.classList.add('show'); 411 }, 10); 412 } 413 414 _hideBackdrop() { 415 if (!this.backdrop) return; 416 417 this.backdrop.classList.remove('show'); 418 419 setTimeout(() => { 420 if (this.backdrop && this.backdrop.parentNode) { 421 this.backdrop.parentNode.removeChild(this.backdrop); 422 } 423 this.backdrop = null; 424 }, 300); 425 } 426 427 _getScrollbarWidth() { 428 const documentWidth = document.documentElement.clientWidth; 429 return Math.abs(window.innerWidth - documentWidth); 430 } 431 432 _triggerEvent(eventName) { 433 const event = new CustomEvent('universalModal.' + eventName, { 434 bubbles: true, 435 cancelable: true, 436 detail: { universalModal: this } 437 }); 438 this.element.dispatchEvent(event); 439 } 440 441 static _jQueryInterface(config) { 442 return this.each(function() { 443 let data = this._universalModalInstance; 444 const _config = typeof config === 'object' && config; 445 446 if (!data) { 447 data = new UniversalModal(this, _config); 448 this._universalModalInstance = data; 449 } 450 451 if (typeof config === 'string') { 452 if (typeof data[config] === 'undefined') {
453 throw new TypeError(`No method named "${config}"`); 454 } 455 data[config](); 456 } 457 }); 458 } 459 } 460 461 document.addEventListener('click', function(e) { 462 const trigger = e.target.closest('[data-toggle="universal-modal"]'); 463 if (!trigger) return; 464 465 e.preventDefault(); 466 467 const targetSelector = trigger.getAttribute('data-target') || trigger.getAttribute('href'); 468 if (!targetSelector) return; 469 470 const targetElement = document.querySelector(targetSelector); 471 if (!targetElement) return; 472 473 let universalModal = targetElement._universalModalInstance; 474 if (!universalModal) { 475 universalModal = new UniversalModal(targetElement); 476 targetElement._universalModalInstance = universalModal; 477 } 478 479 universalModal.show(); 480 }); 481 482 window.UniversalModal = UniversalModal; 483} 484 485const COUNTRY_COOKIE = "countryData"; 486 487function getCountryCode() { 488 return new Promise(function(resolve) { 489 var cached = getCookie(COUNTRY_COOKIE); 490 if (cached) { 491 try { 492 var data = JSON.parse(cached); 493 resolve(data.country_code); 494 return; 495 } catch(_) {} 496 } 497 498 fetch('https://www.cloudflare.com/cdn-cgi/trace') 499 .then(function(response) { return response.text(); }) 500 .then(function(text) { 501 var data = {}; 502 text.split('\n').forEach(function(line) { 503 var parts = line.split('='); 504 var key = parts[0]; 505 var value = parts[1]; 506 if (key && value) data[key] = value; 507 }); 508 var countryCode = data.loc; 509 510 fetch('https://' + varValues.cdn + '/js/countries.json') 511 .then(function(resp) { return resp.json(); }) 512 .then(function(countries) { 513 var country = countries.find(function(c) { return c.code === countryCode; }); 514 var countryName = country ? country.name : "Unknown Country"; 515 var countryData = { country_name: countryName, country_code: countryCode }; 516 setCookie(COUNTRY_COOKIE, JSON.stringify(countryData), 1); 517 resolve(countryCode); 518 }) 519 .catch(function() { 520 var countryData = { country_name: "Unknown", country_code: countryCode }; 521 setCookie(COUNTRY_COOKIE, JSON.stringify(countryData), 1); 522 resolve(countryCode); 523 }); 524 }) 525 .catch(function(error) { 526 resolve(null); 527 }); 528 }); 529} 530 531function isAgeVerified() { 532 try { 533 return localStorage.getItem('az_age_verified') === '1' || getCookie('agego_aver') === 'yes'; 534 } catch (e) { 535 return false; 536 } 537} 538 539async function isAgeVerifiedInDB(uid) { 540 if (!uid) return false; 541 try { 542 const {getDatabase, ref, get} = await import('https://www.gstatic.com/firebasejs/10.5.0/firebase-database.js'); 543 const db = getDatabase(app); 544 const snapshot = await get(ref(db, `users/${uid}/agego/verified`)); 545 return !!snapshot.val(); 546 } catch (e) { 547 return false; 548 } 549} 550 551$(function () { 552 $('.input').on('click', () => { 553 $('.alert-warning').hide(); 554 }); 555 556 $('input').on('change', () => { 557 $('#newcelebmessage').hide(); 558 $('#newmoviemessage').hide(); 559 $('#alert-message').hide(); 560 }); 561}); 562 563// ---- viewing history ------------------------------------------------------ 564// 565// Stories also remember their permanent id. history-stories is the list of primary 566// keys the history page reads, and the DMCA rename replaces a story's primary key: 567// an entry saved before a rename resolves to nothing after it. storyid (MySQL 568// stories.storyid) never changes, and the page prints it in the rating widget as 569// data-urid="S-M<storyid>". So "primary key:storyid" pairs are kept beside the list 570// for the history page to resolve by (src/utils/historyStore.js reads them). 571// 572// The list itself is untouched â same key, same format â so every reader of it keeps 573// working, and entries saved before this have no pair and resolve by primary key as 574// they always did. 575const STORY_HISTORY_IDS = 'history-stories-sid'; 576 577// The same rule as new-story.js getStoryId. "S-M" is the rating widget's prefix, 578// not the story's (a movie's widget prints S-M<movieid>), so the widget is matched 579// to this page by its data-title; without a titled match, only a page with a 580// single widget is trusted. The character set is the one firestore.rules and 581// /fetch-for-history accept; anything else, including what a template prints for 582// a missing value, counts as no storyid. 583function pageStoryId() { 584 const title = String($('meta[name="data:title"]').attr('content') || ''); 585 // Every [data-urid], filtered here rather than by an attribute selector: the 586 // Rating-Widget script lowercases data-urid as it initialises, and both page 587 // scripts are ES modules, so they always run after it (AZ-163). A case-sensitive 588 // '[data-urid^="S-M"]' therefore matches nothing in a real browser. The id keeps 589 // whatever case the DOM holds; /fetch-for-history resolves it either way and 590 // answers with MySQL's own spelling. 591 const widgets = $('[data-urid]').toArray().filter(w => /^s-m/i.test(String($(w).attr('data-urid') || ''))); 592 const titled = title ? widgets.filter(w => $(w).attr('data-title') === title) : []; 593 const chosen = titled.length ? titled : (widgets.length === 1 ? widgets : []); 594 const ids = new Set(chosen.map(w => String($(w).attr('data-urid') || '').slice(3))); 595 if (ids.size !== 1) return null; 596 const id = [...ids][0]; 597 return /^[A-Za-z0-9_=]{1,255}$/.test(id) && !/^(none|null|undefined|nan)$/i.test(id) ? id : null; 598} 599 600// One pair per primary key still in the list. A key cleared from history â on the 601// history page, or by new-main.js's "also clear existing history" â loses its pair 602// the next time a story is recorded. 603function recordStoryId(id, idArray) { 604 const storyid = pageStoryId(); 605 if (!storyid) return; 606 const listed = new Set(idArray); 607 const pairs = (localStorage.getItem(STORY_HISTORY_IDS) || '').split(',').filter(p => {
608 const key = p.split(':')[0]; 609 return key && key !== id && listed.has(key); 610 }); 611 pairs.push(`${id}:${storyid}`); 612 // A revisit of a story already paired changes nothing; skip the write, and the 613 // storage event it would send every other open tab. 614 const next = pairs.join(','); 615 if (next !== (localStorage.getItem(STORY_HISTORY_IDS) || '')) localStorage.setItem(STORY_HISTORY_IDS, next); 616} 617 618function addUserHistory() { 619 try { 620 // The pairs follow the list: once history-stories is gone â cleared on the 621 // history page, or by new-main.js's "also clear existing history" â so are they. 622 if (!localStorage.getItem('history-stories')) localStorage.removeItem(STORY_HISTORY_IDS); 623 624 const contentId = $('meta[name="content_id"]').attr("content"); 625 if(!contentId) return; 626 const [type, id] = contentId.split("_"); 627 const historyEnabled = localStorage.getItem("history-enabled"); 628 629 if (!id || historyEnabled === "false") return; 630 631 const existingIds = localStorage.getItem(`history-${type}`); 632 const idArray = existingIds ? existingIds.split(',') : []; 633 634 // Before the early return, so a story first visited before this shipped gets 635 // its pair on the next visit. 636 if (type === 'stories') { 637 try { recordStoryId(id, idArray.concat(id)); } catch (e) { 638 try { Sentry.captureException(e, { tags: { area: 'history', op: 'recordStoryId' } }); } catch (_e) {} 639 } 640 } 641 642 if (idArray.includes(id)) return; 643 644 idArray.push(id); 645 646 localStorage.setItem(`history-${type}`, idArray.join(',')); 647 } catch (e) { 648 try { Sentry.captureException(e, { tags: { area: 'history', op: 'addUserHistory' } }); } catch (_e) {} 649 } 650} 651 652addUserHistory(); 653 654let historyEnabled = localStorage.getItem("history-enabled") || "true"; 655if(historyEnabled == "true") { 656 $('.account-dropdown__history-toggler__input').prop('checked', true); 657} 658 659// Sexuality-based override: only apply on azfans and aznudefeet. 660// On aznude (gender: "f") and azmen (gender: "m"), the site's own gender is authoritative 661// and the user's stored sexuality preference must NOT override it. 662if (varValues.gender === 'fans' || varValues.gender === 'feet') { 663 if(localStorage.getItem('sexuality') === "Gay") { 664 varValues.genderLong = "male"; 665 varValues.liveSexDropdownGenderTier = 'men'; 666 varValues.liveSexDropdownCategories = [ 667 { title: "Guys", gender: "male", tags: "men" }, 668 { title: "Bisexual", gender: "male", tags: "men/bisexuals" }, 669 { title: "Gay", gender: "male", tags: "men/gays" }, 670 { title: "Straight", gender: "male", tags: "men/straight" }, 671 { title: "Twink", gender: "male", tags: "men/twinks" }, 672 { title: "Anal", gender: "male", tags: "men/anal" }, 673 { title: "Mobile", gender: "male", tags: "men/mobile" }, 674 { title: "VR Cams", gender: "male", tags: "men/vr" } 675 ]; 676 varValues.aznudeLiveCamOutUrl = 'men'; 677 } 678 679 if(localStorage.getItem('sexuality') === "Trans") { 680 varValues.genderLong = "trans"; 681 varValues.liveSexDropdownGenderTier = 'trans'; 682 varValues.liveSexDropdownCategories = [ 683 { title: "Trans", gender: "trans", tags: "trans" }, 684 { title: "Teens", gender: "trans", tags: "trans/teens" }, 685 { title: "Big Tits", gender: "trans", tags: "trans/big-tits" }, 686 { title: "Big Ass", gender: "trans", tags: "trans/big-ass" }, 687 { title: "Anal", gender: "trans", tags: "trans/anal" }, 688 { title: "Blowjob", gender: "trans", tags: "trans/blowjob" }, 689 { title: "VR Cams", gender: "trans", tags: "trans/vr" } 690 ]; 691 varValues.aznudeLiveCamOutUrl = 'trans'; 692 } 693} 694 695if (varValues.gender === 'feet') { 696 const feetSexuality = localStorage.getItem('sexuality'); 697 if (feetSexuality === 'Gay') { 698 varValues.liveSexDropdownGenderTier = 'men/foot-fetish'; 699 varValues.liveSexDropdownGenderTags = 'men/foot-fetish'; 700 varValues.liveSexDropdownCategories = [ 701 { title: "Guys Feet", gender: "male", tags: "men/foot-fetish" }, 702 { title: "Guys", gender: "male", tags: "men" }, 703 { title: "Bisexual", gender: "male", tags: "men/bisexuals" }, 704 { title: "Gay", gender: "male", tags: "men/gays" }, 705 { title: "Straight", gender: "male", tags: "men/straight" }, 706 { title: "Twink", gender: "male", tags: "men/twinks" }, 707 { title: "Anal", gender: "male", tags: "men/anal" }, 708 { title: "Mobile", gender: "male", tags: "men/mobile" }, 709 { title: "VR Cams", gender: "male", tags: "men/vr" } 710 ]; 711 } else { 712 varValues.liveSexDropdownGenderTier = 'girls/foot-fetish'; 713 varValues.liveSexDropdownGenderTags = 'girls/foot-fetish'; 714 varValues.liveSexDropdownCategories = [ 715 { title: "Girls Feet", gender: "female", tags: "girls/foot-fetish" }, 716 { title: "Girls", gender: "female", tags: "girls" }, 717 { title: "Couples", gender: "maleFemale", tags: "couples" }, 718 { title: "Best for private", gender: "female", tags: "girls/best" }, 719 { title: "Interactive Toys", gender: "female", tags: "girls/interactive-toys" }, 720 { title: "Mobile", gender: "female", tags: "girls/mobile" }, 721 { title: "VR Cams", gender: "female", tags: "girls/vr" } 722 ]; 723 } 724} 725 726function resolveGenderForWorker() { 727 if (varValues.genderLong === 'feet') { 728 return localStorage.getItem('sexuality') === 'Gay' ? 'male' : 'female'; 729 } 730 return varValues.genderLong; 731} 732 733let __azRestoreAttempted = false;
734 735// How long a "there is no shared session" answer is trusted before asking again. 736// 737// The in-memory guard below only covers one page load, so a logged-out visitor 738// used to fire one check-session per page view - and for them it can only ever 739// return 401. That is the bulk of the 4xx traffic this endpoint serves. 740// 741// A discarded draft of this cached the negative in sessionStorage, which held it 742// for the entire tab session - a login on another domain would never have been 743// picked up until the tab closed. It never shipped, but the shape of that mistake 744// is why this window is bounded and expires on its own. 745// 746// 2 minutes, from measured per-visitor gaps between session checks rather than 747// intuition (1,100 gaps over 6 hours, pre-filter logs). The elimination curve is 748// very steep and then flat: 60s removes 90% of the repeat traffic, 120s removes 749// 94%, and 10 minutes only reaches 97% - median gap between checks is under 30s. 750// So nearly all the saving is banked inside the first minute, while the cost 751// grows linearly with the window: a sibling domain browsed logged-out inside it 752// keeps showing a logged-out navbar until this expires, even though you have 753// since signed in elsewhere. Going 120s -> 10min would buy ~3 more points of 754// saving for 8 more minutes of that. This is the knee, not a round number. 755const AZ_NO_SESSION_TTL_MS = 2 * 60 * 1000; 756const AZ_NO_SESSION_KEY = 'az_no_session_until'; 757 758function azNoSessionCached() { 759 try { 760 const until = parseInt(localStorage.getItem(AZ_NO_SESSION_KEY) || '0', 10); 761 // Upper bound as well as lower: a value further out than one full TTL 762 // cannot have been written by this code against a correct clock, so treat 763 // it as garbage rather than honouring it. Without this, a clock that was 764 // skewed forward when the key was written keeps the cache alive for the 765 // length of the skew after the clock is corrected. 766 const now = Date.now(); 767 return Number.isFinite(until) && now < until && until <= now + AZ_NO_SESSION_TTL_MS; 768 } catch (_e) { 769 return false; // storage blocked (Safari private mode) - just ask 770 } 771} 772 773function azRememberNoSession() { 774 try { 775 localStorage.setItem(AZ_NO_SESSION_KEY, String(Date.now() + AZ_NO_SESSION_TTL_MS)); 776 } catch (_e) {} 777} 778 779function azForgetNoSession() { 780 try { localStorage.removeItem(AZ_NO_SESSION_KEY); } catch (_e) {} 781} 782 783// A 5xx or a dropped request is not an answer, so it must not be written to 784// az_no_session_until - the comment at the check-session call below explains 785// why. But "not an answer" is no reason to ask again on the very next page 786// view either. Those failures are correlated across every visitor at once, so 787// the endpoint gets its normal load plus the immediate retries of everyone it 788// has just failed: on 22 Aug, 4,526 of the 8,672 requests Cloud Run refused in 789// two minutes were check-session. 790// 791// Deliberately a separate key from az_no_session_until. That one means "there 792// is no shared session"; this one means "we could not ask, wait a moment". 793// Collapsing them would suppress a legitimate restore for a full two minutes on 794// the strength of one failed request, which is the mistake that comment guards 795// against. 796// 797// Short, because the only job is to break up the synchronised wave - long 798// enough that clicking through pages during an outage does not fire one call 799// per page, short enough not to delay recovery. The jitter matters more than 800// the number: without it every visitor's retry lands in the same second, which 801// is the same herd wearing a different hat. 802const AZ_RETRY_AFTER_KEY = 'az_session_retry_after'; 803const AZ_RETRY_BASE_MS = 5 * 1000; 804const AZ_RETRY_JITTER_MS = 5 * 1000; 805 806function azAskTooSoon() { 807 try { 808 const until = parseInt(localStorage.getItem(AZ_RETRY_AFTER_KEY) || '0', 10); 809 // Upper bound for the same reason as azNoSessionCached: a value further 810 // out than one full window cannot have been written by this code against 811 // a correct clock, so treat it as garbage rather than honouring it. 812 const now = Date.now(); 813 return Number.isFinite(until) && now < until 814 && until <= now + AZ_RETRY_BASE_MS + AZ_RETRY_JITTER_MS; 815 } catch (_e) { 816 return false; // storage blocked (Safari private mode) - just ask 817 } 818} 819 820function azBackOff() { 821 try { 822 const wait = AZ_RETRY_BASE_MS + Math.floor(Math.random() * AZ_RETRY_JITTER_MS);
823 localStorage.setItem(AZ_RETRY_AFTER_KEY, String(Date.now() + wait)); 824 } catch (_e) {} 825} 826 827function azClearBackOff() { 828 try { localStorage.removeItem(AZ_RETRY_AFTER_KEY); } catch (_e) {} 829} 830 831export async function azTryRestoreSession() { 832 // Attempt at most once per page load. 833 if (__azRestoreAttempted) return false; 834 __azRestoreAttempted = true; 835 836 // Skip the round trip when we already know there is no shared session. This 837 // is the whole point of the change: for an anonymous visitor the answer is 838 // always the same, so asking once per page view is one request per page for 839 // information we already have. 840 if (azNoSessionCached()) return false; 841 842 // The endpoint failed us recently. Asking again straight away is what turns 843 // one bad minute into a retry storm. 844 if (azAskTooSoon()) return false; 845 846 try { 847 const res = await fetch(`${PRIMARY_BASE}/auth/check-session`, { 848 method: 'GET', 849 credentials: 'include', 850 }); 851 852 if (!res.ok) { 853 // Only a 401 is an answer. A 5xx - Redis down behind the endpoint, or 854 // a 502/503 from the edge - means we failed to ask, exactly like the 855 // fetch rejection below, and caching it would suppress a legitimate 856 // restore for a full window. Those failures are correlated across 857 // users, so a few seconds of backend trouble would otherwise suppress 858 // restores for everyone who loaded a page during it. 859 if (res.status === 401) { 860 azRememberNoSession(); 861 azClearBackOff(); // a 401 is an answer, so the endpoint is healthy 862 } else { 863 azBackOff(); 864 } 865 return false; 866 } 867 868 azClearBackOff(); // it answered, whatever the answer turns out to be 869 870 const data = await res.json(); 871 if (!data || !data.custom_token) { azRememberNoSession(); return false; } // 200, no session 872 873 await signInWithCustomToken(auth, data.custom_token); 874 azForgetNoSession(); 875 // Cookie was readable here, so this browser supports the shared session. 876 // Arm cross-domain logout for the logged-in sync path. 877 try { 878 localStorage.setItem('az_cookie_backed', '1'); 879 } catch (_e) {} 880 return true; // re-fires onAuthStateChanged with the user 881 } catch (e) { 882 // Deliberately not cached. A network failure means we could not ask, not 883 // that there is no session - remembering it would suppress a legitimate 884 // restore for a full window on the strength of one dropped request. It 885 // does earn a short backoff, which is a different thing: wait a moment, 886 // then ask again. 887 azBackOff(); 888 console.warn('[CrossDomainAuth] restore failed:', e); 889 return false; 890 } 891} 892 893let __azSessionSynced = false; 894 895// For a logged-in user: keep this domain in sync with the shared session. 896// If the cookie is readable, arm cross-domain logout. If the cookie is gone and we 897// previously confirmed it worked here, sign out so logout propagates across domains. 898// The armed gate means cookie-blocked browsers (Safari) never false-sign-out. 899export async function azSyncSession() { 900 if (__azSessionSynced) return false; 901 __azSessionSynced = true; 902 903 const p = window.location.pathname; 904 if (p === '/signin.html' || p === '/signup.html') return false; // active login, do not race it 905 906 // Same backoff as the restore path. Returning false here leaves the visitor 907 // signed in, which is the safe direction to fail. 908 if (azAskTooSoon()) return false; 909 910 let armed = false; 911 try { armed = localStorage.getItem('az_cookie_backed') === '1'; } catch (_e) {} 912 913 try { 914 const res = await fetch(`${PRIMARY_BASE}/auth/check-session`, { 915 method: 'GET', 916 credentials: 'include', 917 }); 918 919 if (res.ok) { 920 azClearBackOff(); 921 try { localStorage.setItem('az_cookie_backed', '1'); } catch (_e) {} 922 return false; // session still alive 923 } 924 925 // Only a 401 is an answer, exactly as in azTryRestoreSession. Anything 926 // else - a 5xx from the backend, a 502/503 from the edge - means we 927 // failed to ask. This branch did not exist before, so any non-ok reply 928 // was read as "the shared session is gone" and signed the visitor out: 929 // a few seconds of backend trouble logged out everyone whose cookie had 930 // ever worked on this domain, and the resulting onAuthStateChanged(null) 931 // sent each of them straight back to check-session a second time. 932 if (res.status !== 401) { 933 azBackOff(); 934 return false;
935 } 936 937 azClearBackOff(); // a 401 is an answer, so the endpoint is healthy 938 939 if (!armed) return false; // never confirmed the cookie works here; do not risk a false logout 940 941 // Shared session is gone (logout on another domain). Propagate it here. 942 try { localStorage.removeItem('az_cookie_backed'); } catch (_e) {} 943 await signOut(auth); 944 return true; // re-fires onAuthStateChanged with null 945 } catch (e) { 946 azBackOff(); 947 console.warn('[CrossDomainAuth] session sync failed:', e); 948 return false; 949 } 950} 951 952$(function () { 953 onAuthStateChanged(auth, async (user) => { 954 if (user) { 955 // Somebody is signed in here, so any remembered "no shared session" 956 // is stale. Clearing it on every signed-in render covers every route 957 // in on this origin - local sign-in, restore, a token refresh - 958 // without each of them needing to know about the cache. It does not 959 // reach the sibling domains; their own copies expire on their own. 960 azForgetNoSession(); 961 962 if (await azSyncSession()) return; // shared session gone -> signed out here too 963 964 if (!user.emailVerified) { 965 $('body').prepend(` 966 <div class="email-verification-bar"> 967 Please verify your email (${user.email}). Check your inbox or spam folder, or <a href="#" id="resend-link">click here</a> to resend. You can also go to <a href="/profile.html#settings">Settings</a> to change your email. 968 <div id="email-verification-response" style="display: none;"></div> 969 </div> 970 `); 971 } 972 973 $('#resend-link').on('click', async function(e) { 974 e.preventDefault(); 975 const responseSpan = $('#email-verification-response'); 976 977 try { 978 await sendEmailVerification(auth.currentUser); 979 responseSpan.text('Verification email has been resent.').removeClass('error').addClass('success').show(); 980 } catch (error) { 981 try { Sentry.captureException(error, { tags: { area: 'auth', op: 'sendEmailVerification' } }); } catch (_e) {} 982 if (error.code === "auth/too-many-requests") { 983 responseSpan.text('Too many requests. Please try again later.').removeClass('success').addClass('error').show(); 984 } else { 985 responseSpan.text('Error sending verification email: ' + error.message).removeClass('success').addClass('error').show(); 986 } 987 } finally { 988 setTimeout(() => { 989 responseSpan.hide(); 990 }, 30000); 991 } 992 993 responseSpan.on('click', function(){ 994 responseSpan.hide(); 995 }) 996 }); 997 998 try { 999 const token = await user.getIdTokenResult(true); 1000 Sentry.setUser({ 1001 id: user.uid || undefined, 1002 email: user.email || undefined 1003 }); 1004 Sentry.setContext('firebaseClaims', token?.claims || {}); 1005 1006 const hasLegacyCookie = document.cookie.split('; ').some(cookie => cookie.startsWith('az_legacy_user=')); 1007 1008 if (!hasLegacyCookie) { 1009 const creationTime = new Date(user.metadata.creationTime); 1010 const cutoffDate = new Date('2026-01-01T00:00:00Z'); 1011 1012 const cookieValue = creationTime < cutoffDate ? '1' : '0'; 1013 document.cookie = `az_legacy_user=${cookieValue}; path=/; max-age=86400; SameSite=Strict; Secure`; 1014 1015 getCountryCode().then(function(countryCode) { 1016 var isGatedCountry = countryCode === 'GB' || countryCode === 'AU'; 1017 if (isGatedCountry) return window.location.reload(); 1018 }); 1019 } 1020 } catch (e) { 1021 try { Sentry.captureException(e, { tags: { area: 'auth', op: 'getIdTokenResult' } }); } catch (_e) {} 1022 } 1023 1024 $navbar.removeClass("is-not-logged-in"); 1025 $navbar.addClass("is-logged-in"); 1026 1027 $('.logoutbtn').on('click', async (e) => { 1028 e.preventDefault(); 1029 localStorage.removeItem('azn_id'); 1030 localStorage.removeItem('az_cookie_backed'); 1031 document.cookie = `az_legacy_user=; path=/; max-age=0`; 1032 await signOut(auth); 1033 try { 1034 await fetch(`${PRIMARY_BASE}
1034/auth/delete-session`, { method: 'POST', credentials: 'include', keepalive: true }); 1035 } catch (_e) {} 1036 const AUTH_GATED_REACT_PAGES = new Set(['profile','favorites','upload','view-uploads','edit-upload','edit-report','manage-content','manageuser','two-factor-authentication']); 1037 const pageType = document.getElementById('react-root')?.getAttribute('data-page'); 1038 window.location.href = (pageType && AUTH_GATED_REACT_PAGES.has(pageType)) ? '/' : window.location.href; 1039 }); 1040 1041 if ($('.ribbon-desktop-live-1').length) { 1042 $('.ribbon-desktop-live-1').attr('href', `/aznudelive.html${constructAZNLiveNavbarLink($('.ribbon-desktop-live-1').attr('href'))}`); 1043 $('.ribbon-desktop-live-1').removeAttr('target'); 1044 $('.ribbon-desktop-live-1').removeAttr('rel'); 1045 } 1046 1047 if ($('.ribbon-desktop-live-2').length) { 1048 $('.ribbon-desktop-live-2').attr('href', `/aznudelive.html${constructAZNLiveNavbarLink($('.ribbon-desktop-live-2').attr('href'))}`); 1049 $('.ribbon-desktop-live-2').removeAttr('target'); 1050 $('.ribbon-desktop-live-2').removeAttr('rel'); 1051 } 1052 1053 if ($('.ribbon-mobile-live').length) { 1054 $('.ribbon-mobile-live').attr('href', `/aznudelive.html${constructAZNLiveNavbarLink($('.ribbon-mobile-live').attr('href'))}`); 1055 $('.ribbon-mobile-live').removeAttr('target'); 1056 $('.ribbon-mobile-live').removeAttr('rel'); 1057 } 1058 } else { 1059 Sentry.setUser(null); 1060 1061 // Cross-domain restore: Firebase has authoritatively reported no local 1062 // session, so this is the right point to try the centralized cookie. 1063 const restored = await azTryRestoreSession(); 1064 if (restored) return; // re-fire handles the logged-in render 1065 } 1066 1067 const baseUrl = `/cam-out.html?userId=e5832eb0a24a6150f7d3967ae12ea6e95a0e3ce83557ea66397b6ed5ba72751e&targetDomain=aznudelive.com/${varValues.aznudeLiveCamOutUrl}&p=verify50af`; 1068 const ulElement = document.querySelector('.live-dropdown__ul'); 1069 1070 if(ulElement){ 1071 varValues.liveSexDropdownCategories.forEach((category, index) => { 1072 const liElement = document.createElement('li'); 1073 liElement.className = 'live-dropdown__li'; 1074 if (index === 0) liElement.classList.add('active'); 1075 1076 const aElement = document.createElement('a'); 1077 aElement.href = '#'; 1078 aElement.setAttribute('data-gender', category.gender); 1079 aElement.setAttribute('data-tag', category.tags); 1080 aElement.textContent = category.title; 1081 1082 liElement.appendChild(aElement); 1083 ulElement.appendChild(liElement); 1084 1085 aElement.addEventListener('click', function(event) { 1086 event.preventDefault(); 1087 event.stopPropagation(); 1088 1089 document.querySelectorAll('.live-dropdown__li').forEach(li => li.classList.remove('active')); 1090 liElement.classList.add('active'); 1091 1092 const gridList = document.querySelector('.live-dropdown__promotions-grid'); 1093 gridList.innerHTML = ''; 1094 1095 const gender = aElement.getAttribute('data-gender'); 1096 const tag = aElement.getAttribute('data-tag'); 1097 setupLiveDropdown(gender, tag); 1098 }); 1099 }); 1100 } 1101 1102 const gridList = document.querySelector('.live-dropdown__promotions-grid'); 1103 let hlsInstances = {}; 1104 1105 const generateSingleItem = (model, gender) => { 1106 try { 1107 let icon; 1108 1109 switch (model.gender) { 1110 case 'male': 1111 icon = ` 1112 <svg xmlns="http://www.w3.org/2000/svg" fill="#5AC1F2" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" image-rendering="optimizeQuality" fill-rule="evenodd" clip-rule="evenodd" viewBox="0 0 512 511.01"><path fill-rule="nonzero" d="m456.72 96.62-115.49 115.5c22.46 31.03 35.72 69.17 35.72 110.41 0 52.04-21.1 99.17-55.2 133.27-34.11 34.1-81.23 55.21-133.28 55.21-52.03 0-99.17-21.11-133.27-55.21C21.1 421.7 0 374.57 0 322.53c0-52.04 21.1-99.17 55.2-133.27 34.1-34.1 81.23-55.21 133.27-55.21 42.91 0 82.47 14.35 114.16 38.5L419.89 55.28h-62.84V0H512v158.91h-55.28V96.62zM282.66 228.35c-24.1-24.1-57.41-39.02-94.19-39.02s-70.08 14.92-94.18 39.02c-24.1 24.1-39.01 57.4-39.01 94.18 0 36.78 14.91 70.09 39.01 94.19 24.1 24.1 57.4 39.01 94.18 39.01 36.78 0 70.09-14.91 94.19-39.01 24.1-24.1 39.01-57.41 39.01-94.19s-14.91-70.08-39.01-94.18z"/>
1112</svg>`; 1113 break; 1114 1115 case 'female': 1116 icon = ` 1117 <svg xmlns="http://www.w3.org/2000/svg" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" image-rendering="optimizeQuality" fill-rule="evenodd" clip-rule="evenodd" viewBox="0 0 361 511.42"><path fill="#F576AB" fill-rule="nonzero" d="M203.64 359.53v44.17h78.58v52.94h-78.58v54.78H150.7v-54.78H72.13V403.7h78.57v-45.15c-37.91-6.3-71.82-24.41-97.83-50.42C20.21 275.47 0 230.35 0 180.5c0-49.84 20.21-94.97 52.87-127.63S130.65 0 180.5 0c49.84 0 94.97 20.21 127.63 52.87S361 130.66 361 180.5c0 49.84-20.21 94.97-52.87 127.63-27.52 27.52-63.9 46.2-104.49 51.4zM270.7 90.3c-23.08-23.08-54.98-37.36-90.2-37.36-35.23 0-67.12 14.28-90.2 37.36s-37.36 54.98-37.36 90.2c0 35.23 14.28 67.12 37.36 90.2s54.97 37.36 90.2 37.36c35.22 0 67.12-14.28 90.2-37.36s37.36-54.97 37.36-90.2c0-35.22-14.28-67.12-37.36-90.2z"/></svg>`; 1118 break; 1119 1120 case 'males': 1121 icon = ` 1122 <svg xmlns="http://www.w3.org/2000/svg" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" image-rendering="optimizeQuality" fill-rule="evenodd" clip-rule="evenodd" viewBox="0 0 512 490.08"><path fill="#5AC1F2" fill-rule="nonzero" d="M347.89 73.6l-87.97 87.97a143.238 143.238 0 0120.99 42.2c26.75 2.84 51.3 13.03 71.6 28.51l89.33-89.33h-47.87v-42.11H512v121.05h-42.11v-47.45l-87.97 87.97c17.11 23.64 27.21 52.7 27.21 84.11 0 39.64-16.08 75.54-42.05 101.51-25.98 25.98-61.87 42.05-101.52 42.05-39.63 0-75.54-16.07-101.51-42.05-16.42-16.42-28.89-36.81-35.83-59.6-33.51-3.57-63.59-18.66-86.17-41.24C16.07 321.22 0 285.32 0 245.68c0-39.65 16.07-75.54 42.05-101.52 25.97-25.97 61.87-42.05 101.51-42.05 32.69 0 62.82 10.93 86.96 29.32l89.32-89.32h-47.87V0H390v121.05h-42.11V73.6zm-60.77 173.76c-.44 38.97-16.43 74.23-42.04 99.83-19.54 19.54-44.7 33.48-72.84 39.19 5.12 11.99 12.5 22.8 21.58 31.88 18.36 18.36 43.73 29.72 71.74 29.72 28.02 0 53.39-11.36 71.75-29.72 18.35-18.35 29.71-43.72 29.71-71.74 0-28.02-11.36-53.39-29.71-71.74-13.51-13.51-30.83-23.23-50.19-27.42zm-165.11 97.48c.45-38.98 16.43-74.23 42.04-99.84 19.54-19.54 44.7-33.48 72.84-39.18-5.13-11.99-12.5-22.8-21.58-31.89-18.36-18.35-43.73-29.71-71.75-29.71-28.02 0-53.38 11.36-71.74 29.71-18.36 18.36-29.71 43.73-29.71 71.75 0 28.02 11.35 53.39 29.71 71.74 13.51 13.51 30.82 23.23 50.19 27.42zm123-97.69c-19.77 4.06-37.45 13.88-51.19 27.63-18.03 18.03-29.31 42.84-29.7 70.27 19.77-4.07 37.45-13.89 51.19-27.63 18.04-18.04 29.31-42.84 29.7-70.27z"/></svg>`; 1123 break; 1124 1125 case 'females': 1126 icon = ` 1127 <svg xmlns="http://www.w3.org/2000/svg" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" image-rendering="optimizeQuality" fill-rule="evenodd" clip-rule="evenodd" viewBox="0 0 512 442.01"><path fill="#F576AB" fill-rule="nonzero" d="M176.01 310.73v38.18h67.9v45.75h-67.9v47.35h-45.76v-47.35H62.34v-45.75h67.91v-39.02c-32.77-5.45-62.08-21.1-84.56-43.58C17.47 238.09 0 199.08 0 156.01c0-43.08 17.47-82.09 45.69-110.32C73.92 17.47 112.92 0 156 0c38.05 0 72.92 13.63 100 36.27C283.07 13.63 317.95 0 356 0c43.08 0 82.08 17.47 110.31 45.69C494.53 73.92 512 112.93 512 156.01c0 43.07-17.47 82.08-45.69 110.3-23.79 23.79-55.23 39.93-90.31 44.42v38.18h67.91v45.75H376v47.35h-45.75v-47.35h-67.91v-45.75h67.91v-39.02c-27.96-4.65-53.42-16.73-74.25-34.15-22.25 18.6-49.77 31.13-79.99 34.99zM286.47 70.44c16.14 24.57 25.54 53.97 25.54 85.57 0 31.58-9.4 60.99-25.54 85.55 18.96 15.44 43.16 24.69 69.53 24.69 30.44 0 58.01-12.34 77.96-32.29 19.94-19.94 32.29-47.51 32.29-77.95 0-30.45-12.35-58.02-32.29-77.96C414.01 58.1 386.44 45.76 356 45.76c-26.37 0-50.57 9.25-69.53 24.68zm-60.94 171.12C209.39 217 200 187.59 200 156.01c0-31.6 9.39-61 25.53-85.57-18.96-15.43-43.17-24.68-69.53-24.68-30.44 0-58.01 12.34-77.95 32.29-19.95 19.94-32.29 47.51-32.29 77.96 0 30.44 12.34 58.01 32.29 77.95 19.94 19.95 47.51 32.29 77.95 32.29 26.36 0 50.56-9.25 69.53-24.69zM256 109.52c-6.57 14.13-10.25 29.88-10.25 46.49 0 16.6 3.68 32.35 10.25 46.48 6.58-14.13 10.25-29.88 10.25-46.48 0-16.61-3.67-32.36-10.25-46.49z"/></svg>`; 1128 break; 1129 1130 case 'maleFemale': 1131 icon = ` 1132 <svg xmlns="http://www.w3.org/2000/svg" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" image-rendering="optimizeQuality" fill-rule="evenodd" clip-rule="evenodd" viewBox="0 0 512 502.03"><g fill-rule="nonzero"><path fill="#5AC1F2" d="M285.39 93.9c30.15 0 57.95 10.09 80.21 27.06l82.86-82.86h-30.7c-10.52 0-19.05-8.53-19.05-19.05C398.71 8.53 407.24 0 417.76 0h75.19C503.47 0 512 8.53 512 19.05v77.53c0 10.52-8.53 19.05-19.05 19.05-10.52 0-19.05-8.53-19.05-19.05V67.56l-81.2 81.2c15.77 21.79 25.07 48.57 25.07 77.53 0 36.55-14.82 69.64-38.77 93.6-23.96 23.95-57.06 38.77-93.61 38.77-22.38 0-43.46-5.56-61.95-15.36 5.14-3.95 10-8.21 14.55-12.76 5.19-5.19 10-10.77 14.38-16.7 10.27 3.87 21.4 5.99 33.02 5.99 25.84 0 49.23-10.47 66.16-27.4 16.92-16.92 27.4-40.31 27.4-66.14 0-25.84-10.48-49.23-27.4-66.16-16.93-16.93-40.32
1132-27.4-66.16-27.4-25.83 0-49.22 10.47-66.15 27.4-16.93 16.93-27.4 40.32-27.4 66.16 0 13.23 2.75 25.81 7.7 37.22-3.39 5.89-7.58 11.32-12.42 16.16-4.94 4.93-10.48 9.19-16.5 12.62-11.2-19.43-17.6-41.97-17.6-66 0-36.56 14.82-69.65 38.77-93.61 23.95-23.96 57.05-38.78 93.6-38.78z"/><path fill="#F576AB" d="M132.68 92.55c22.48 0 43.65 5.6 62.2 15.46-5.23 4-10.17 8.33-14.8 12.96-5.14 5.13-9.9 10.66-14.25 16.53-10.31-3.9-21.48-6.03-33.15-6.03-25.9 0-49.34 10.5-66.3 27.46-16.97 16.97-27.46 40.41-27.46 66.3 0 25.9 10.49 49.34 27.46 66.31C83.34 308.5 106.79 319 132.68 319s49.34-10.5 66.3-27.47c16.97-16.96 27.46-40.4 27.46-66.3 0-13.32-2.77-25.99-7.79-37.47a77.953 77.953 0 0112.3-15.98 77.07 77.07 0 0116.75-12.72c11.23 19.48 17.66 42.08 17.66 66.17 0 36.64-14.86 69.81-38.87 93.82-19.77 19.77-45.75 33.33-74.76 37.5v33.15h55.18c10.52 0 19.05 8.53 19.05 19.05 0 10.52-8.53 19.05-19.05 19.05h-55.18v55.18c0 10.52-8.53 19.05-19.05 19.05-10.52 0-19.05-8.53-19.05-19.05V427.8H58.45c-10.52 0-19.05-8.53-19.05-19.05 0-10.52 8.53-19.05 19.05-19.05h55.18v-33.15c-29.01-4.17-55-17.73-74.77-37.5C14.85 295.04 0 261.87 0 225.23c0-36.63 14.85-69.8 38.86-93.81 24.01-24.01 57.18-38.87 93.82-38.87z"/></g></svg>`; 1133 break; 1134 case 'tranny': 1135 icon = ` 1136 <svg width="16" height="16" viewBox="0 0 12 12" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient x1="67.535" y1="422.773" x2="412.713" y2="79.813" id="a" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#f6b"/><stop offset="1" stop-color="#0c30ad"/></linearGradient><linearGradient xlink:href="#a" id="b" gradientUnits="userSpaceOnUse" x1="6" y1="9.375" x2="10.75" y2="5.75"/></defs><path style="fill:none;stroke:url(#b);stroke-width:.99975;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1" d="M4.125 9.375h3.75M3.519.999H1.75v1.77m.669.666 1.768-1.767M6 11V7.75M4.233 3.483l-.62-.621-.622-.62-.62-.622L1.75 1m6.43 0h2.07v2.069M8.5 5.249c0-.689-.28-1.314-.733-1.766A2.5 2.5 0 0 0 6 2.75c-.69 0-1.314.28-1.767.733A2.5 2.5 0 0 0 3.5 5.25c0 1.379 1.121 2.5 2.5 2.5s2.5-1.121 2.5-2.5zm-.733-1.766L10.25.999"/></svg> 1137 `; 1138 break; 1139 1140 default: 1141 break; 1142 } 1143 1144 const sourceId = `sourceId=aznude-interactive-bar-model-${gender === 'male' ? "men" : "women"}`; 1145 const fallbackUrl = `https://aznudelive.com/${model.username}?${sourceId}&userId=e5832eb0a24a6150f7d3967ae12ea6e95a0e3ce83557ea66397b6ed5ba72751e&targetDomain=aznudelive.com/${varValues.aznudeLiveCamOutUrl}&p=verify50af`; 1146 const href = user ? `${baseUrl}&${sourceId}&site=/${model.username}&fallbackUrl=${encodeURIComponent(fallbackUrl)}` : fallbackUrl; 1147 1148 return `<li class="live-dropdown__promotions-grid-item"> 1149 <a href="${href}" data-video-src="${model.stream?.url}" target="_blank" class="promotions-grid-item__wrapper"> 1150 <div class="promotions-grid-item__image" style="background-image: url('${model.popularSnapshotUrl}')"></div> 1151 <h4 class="promotions-grid-item__name"><span>${model.username}</span>${icon}</h4> 1152 </a> 1153 </li>` 1154 } catch (e) { 1155 try { Sentry.captureException(e, { tags: { area: 'liveDropdown', op: 'generateSingleItem' } }); } catch (_e) {} 1156 return ''; 1157 } 1158 }; 1159 1160 async function setupLiveDropdown(gender, tag, isLoggedIn) { 1161 try { 1162 const workerGender = gender === 'feet' ? (localStorage.getItem('sexuality') === 'Gay' ? 'male' : 'female') : gender; 1163 const res = await fetch(`https://broad-term-5811.aznude.workers.dev/?gender=${workerGender}&tag=${tag}&limit=10`); 1164 const data = await res.json(); 1165 1166 gridList.innerHTML = ''; 1167 1168 data.models.forEach(model => gridList.innerHTML += generateSingleItem(model, gender)); 1169 1170 const sourceId = `sourceId=aznude-interactive-bar-${varValues.genderLong === 'male' ? "men" : "women"}`; 1171 const fallbackUrl = `https://aznudelive.com/${tag}?${sourceId}userId=e5832eb0a24a6150f7d3967ae12ea6e95a0e3ce83557ea66397b6ed5ba72751e&targetDomain=aznudelive.com/${varValues.aznudeLiveCamOutUrl}&p=verify50af`; 1172 const href = user ? `${baseUrl}&${sourceId}&site=${tag}&fallbackUrl=${encodeURIComponent(fallbackUrl)}` : fallbackUrl; 1173
1174 document.querySelector('.ribbon-desktop-live-1').setAttribute('href', href) 1175 document.querySelector('.live-dropdown__promotions-header__title').setAttribute('href', href); 1176 document.querySelector('.live-dropdown__promotions-header__title').innerHTML = `Chat with <span>Live</span> ${gender === "male" ? "men" : gender === "trans" ? "trans girls" : "girls"} in ${data.country} now!`; 1177 document.querySelector('.live-dropdown__promotions-header__button').setAttribute('href', href); 1178 document.querySelector('.live-dropdown__promotions-header__button').innerHTML = gender === 'male' ? "View Guys >" : gender === "trans" ? "View Trans Girls >" : "View Girls >"; 1179 1180 if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) { 1181 document.querySelectorAll('.promotions-grid-item__wrapper').forEach(wrapper => { 1182 wrapper.addEventListener('touchend', function () { 1183 const video = this.querySelector('video'); 1184 if (video) { 1185 video.style.transition = 'opacity 0.5s'; 1186 video.style.opacity = 0; 1187 video.addEventListener('transitionend', () => video.remove()); 1188 } 1189 }); 1190 wrapper.addEventListener('touchstart', function () { 1191 const vid_src = this.getAttribute('data-video-src'); 1192 const vid = generateVideo(vid_src); 1193 const imageDiv = this.querySelector('.promotions-grid-item__image'); 1194 imageDiv.appendChild(vid); 1195 vid.style.transition = 'opacity 0.5s'; 1196 vid.style.opacity = 0; 1197 setTimeout(() => vid.style.opacity = 1, 0); 1198 }); 1199 }); 1200 } else { 1201 document.querySelectorAll('.promotions-grid-item__wrapper').forEach(wrapper => { 1202 wrapper.addEventListener('mouseenter', function hoverIn() { 1203 const vid_src = this.getAttribute('data-video-src'); 1204 if (this.getAttribute('data-video-initialized') !== 'true') { 1205 if (this.hls) { 1206 this.hls.destroy(); 1207 } 1208 this.setAttribute('data-video-initialized', 'true'); 1209 const vid = generateVideo(vid_src); 1210 this.querySelector('.promotions-grid-item__image').appendChild(vid); 1211 vid.style.transition = 'opacity 0.5s'; 1212 vid.style.opacity = 0; 1213 setTimeout(() => vid.style.opacity = 1, 0); 1214 } 1215 }); 1216 1217 wrapper.addEventListener('mouseleave', function hoverOut() { 1218 const video = this.querySelector('video'); 1219 if (video) { 1220 video.style.transition = 'opacity 0.5s'; 1221 video.style.opacity = 0; 1222 video.addEventListener('transitionend', () => { 1223 this.setAttribute('data-video-initialized', 'false'); 1224 const hlsInstance = hlsInstances[video.id]; 1225 if (hlsInstance) { 1226 hlsInstance.destroy(); 1227 delete hlsInstances[video.id]; 1228 } 1229 video.remove(); 1230 }); 1231 } 1232 }); 1233 }); 1234 } 1235 } catch (e) { 1236 try { Sentry.captureException(e, { tags: { area: 'liveDropdown', op: 'setupLiveDropdown' } }); } catch (_e) {} 1237 } 1238 } 1239 1240 function generateVideo(src) { 1241 try { 1242 const videoId = 'video_' + Date.now(); 1243 const video = document.createElement('video'); 1244 video.id = videoId; 1245 video.autoplay = true; 1246 video.muted = true; 1247 video.loop = true; 1248 video.style.position = 'absolute'; 1249 video.style.top = '0px'; 1250 video.style.left = '0px'; 1251 video.style.border = `solid 2px ${varValues.primaryColor}`; 1252 video.style.objectFit = 'cover'; 1253 video.style.width = '100%'; 1254 video.style.height = '100%'; 1255 1256 const source = document.createElement('source'); 1257 source.src = src; 1258 video.appendChild(source); 1259 1260 const nativeVideo = video; 1261 if (Hls.isSupported()) { 1262 const hls = new Hls({ 1263 fragLoadingTimeOut: 2000, 1264 fragLoadingMaxRetry: 2, 1265 fragLoadingRetryDelay: 0 1266 }); 1267 hls.loadSource(src); 1268 hls.attachMedia(nativeVideo); 1269 hlsInstances[videoId] = hls; 1270 } else { 1271 video.src = src; 1272 } 1273 return video; 1274 } catch (e) { 1275 try { Sentry.captureException(e, { tags: { area: 'video', op: 'generateVideo' } }); } catch (_e) {} 1276 const fallback = document.createElement('video'); 1277 try { fallback.muted = true; } catch (_e) {} 1278 return fallback; 1279 } 1280 } 1281 1282 if(ulElement){
1283 document.querySelector('.ribbon-li-desktop-live').addEventListener('mouseenter', function () { 1284 if (!this.getAttribute('data-initialized')) { 1285 setupLiveDropdown(varValues.genderLong, varValues.liveSexDropdownGenderTier, !!user); 1286 this.setAttribute('data-initialized', 'true'); 1287 const script = document.createElement('script'); 1288 script.src = 'https://cdn.jsdelivr.net/npm/hls.js@latest'; 1289 document.head.appendChild(script); 1290 } 1291 }); 1292 } 1293 1294 async function initializeStripChatBanners(models) { 1295 try { 1296 const aznlive_banner = document.getElementById('aznlive-banner'); 1297 const banner_items = document.getElementsByClassName('aznlive-banner__item'); 1298 1299 if(aznlive_banner) aznlive_banner.style.display = 'block'; 1300 1301 if (varValues.gender === 'fans') { 1302 const headerTitle = aznlive_banner.querySelector('.aznlive-banner__header h3'); 1303 if (headerTitle) { 1304 if (varValues.genderLong === 'male') { 1305 headerTitle.innerHTML = 'Chat with <span>live</span> guys'; 1306 } else if (varValues.genderLong === 'trans') { 1307 headerTitle.innerHTML = 'Chat with <span>live</span> trans girls'; 1308 } else { 1309 headerTitle.innerHTML = 'Chat with <span>live</span> girls'; 1310 } 1311 } 1312 1313 const footerLink = aznlive_banner.querySelector('.aznlive-banner__footer a'); 1314 const footerButtonRight = aznlive_banner.querySelector('.aznlive-banner__button-right p'); 1315 if (footerLink && footerButtonRight) { 1316 const genderPath = varValues.genderLong === 'male' ? 'men' : varValues.genderLong === 'trans' ? 'trans' : 'girls'; 1317 const sourceId = 'azfans-video'; 1318 1319 footerLink.href = `https://aznudelive.com/${genderPath}?targetDomain=aznudelive.com&p=verify50af&sourceId=${sourceId}&userId=e5832eb0a24a6150f7d3967ae12ea6e95a0e3ce83557ea66397b6ed5ba72751e`; 1320 1321 if (varValues.genderLong === 'male') { 1322 footerButtonRight.textContent = 'Cam Guys'; 1323 } else if (varValues.genderLong === 'trans') { 1324 footerButtonRight.textContent = 'Cam Trans Girls'; 1325 footerButtonRight.style.fontSize = '14px'; 1326 } else { 1327 footerButtonRight.textContent = 'Cam Girls'; 1328 } 1329 } 1330 } 1331 1332 for (let i = 0; i < 6; i++) { 1333 const { username, snapshotUrl, stream } = models[i]; 1334 1335 if(banner_items.length){ 1336 banner_items[i].classList.add('aznlive-banner__item-is-live'); 1337 1338 const sourceIdBanner = `sourceId=aznude-video`; 1339 const fallbackUrlBanner = `https://aznudelive.com/${username}?${sourceIdBanner}&userId=e5832eb0a24a6150f7d3967ae12ea6e95a0e3ce83557ea66397b6ed5ba72751e&targetDomain=aznudelive.com/${varValues.aznudeLiveCamOutUrl}&p=verify50af` 1340 const hrefBanner = user ? `${baseUrl}&${sourceIdBanner}&site=/${username}&fallbackUrl=${encodeURIComponent(fallbackUrlBanner)}` : fallbackUrlBanner; 1341 1342 banner_items[i].setAttribute('data-video-src', stream.url); 1343 banner_items[i].setAttribute('href', hrefBanner); 1344 banner_items[i].style.backgroundImage = `url('${snapshotUrl}')`; 1345 banner_items[i].innerHTML = `<p>${username}</p>`; 1346 } 1347 } 1348 1349 document.querySelectorAll('.aznlive-banner__item').forEach(wrapper => { 1350 wrapper.addEventListener('mouseenter', async function () { 1351 try { 1352 await loadHlsScript(); 1353 const vid_src = this.getAttribute('data-video-src'); 1354 if (this.getAttribute('data-video-initialized') !== 'true') { 1355 if (this.hls) { 1356 this.hls.destroy(); 1357 } 1358 this.setAttribute('data-video-initialized', 'true'); 1359 const vid = generateVideo(vid_src); 1360 this.appendChild(vid); 1361 vid.style.transition = 'opacity 0.5s'; 1362 vid.style.opacity = 0; 1363 setTimeout(() => vid.style.opacity = 1, 0); 1364 } 1365 } catch (err) { 1366 try { Sentry.captureException(err, { tags: { area: 'banners', op: 'rightHoverInit' } }); } catch (_e) {} 1367 console.error(err); 1368 } 1369 }); 1370 1371 wrapper.addEventListener('mouseleave', function () { 1372 const video = this.querySelector('video'); 1373 if (video) { 1374 video.style.transition = 'opacity 0.5s'; 1375 video.style.opacity = 0; 1376 video.addEventListener('transitionend', () => { 1377 this.setAttribute('data-video-initialized', 'false'); 1378 const hlsInstance = hlsInstances[video.id]; 1379 if (hlsInstance) { 1380 hlsInstance.destroy(); 1381 delete hlsInstances[video.id]; 1382 } 1383 video.remove(); 1384 }); 1385 } 1386 }); 1387 }); 1388 1389 async function loadHlsScript() { 1390 if (!document.documentElement.getAttribute('hls-initialized')) { 1391 document.documentElement.setAttribute('hls-initialized', 'true'); 1392 const script = document.createElement('script'); 1393 script.src = 'https://cdn.jsdelivr.net/npm/hls.js@latest'; 1394 return new Promise((resolve, reject) => { 1395 script.onload = () => resolve(); 1396 script.onerror = () => reject(new Error('Failed to load HLS script')); 1397 document.head.appendChild(script); 1398 }); 1399 } 1400 } 1401 1402 if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) { 1403 document.querySelectorAll('.aznlive-horizontal__item').forEach(wrapper => { 1404 1405 wrapper.addEventListener('touchend', function () { 1406 const video = this.querySelector('video'); 1407 if (video) { 1408 video.style.transition = 'opacity 0.5s'; 1409 video.style.opacity = 0; 1410 video.addEventListener('transitionend', () => video.remove()); 1411 } 1412 }); 1413 1414 wrapper.addEventListener('touchstart', async function () { 1415 try { 1416 await loadHlsScript(); 1417 const vid_src = this.getAttribute('data-video-src'); 1418 const vid = generateVideo(vid_src); 1419 this.appendChild(vid); 1420 vid.style.transition = 'opacity 0.5s'; 1421 vid.style.opacity = 0; 1422 setTimeout(() => vid.style.opacity = 1, 0); 1423 } catch (err) { 1424 try { Sentry.captureException(err, { tags: { area: 'banners', op: 'mobileTouchInit' } }); } catch (_e) {} 1425 console.error(err); 1426 } 1427 }); 1428 }); 1429 } else { 1430 document.querySelectorAll('.aznlive-horizontal__item').forEach(wrapper => { 1431 wrapper.addEventListener('mouseenter', async function () { 1432 try { 1433 await loadHlsScript(); 1434 const vid_src = this.getAttribute('data-video-src'); 1435 if (this.getAttribute('data-video-initialized') !== 'true') { 1436 if (this.hls) { 1437 this.hls.destroy(); 1438 } 1439 this.setAttribute('data-video-initialized', 'true'); 1440 const vid = generateVideo(vid_src); 1441 this.appendChild(vid); 1442 vid.style.transition = 'opacity 0.5s'; 1443 vid.style.opacity = 0; 1444 setTimeout(() => vid.style.opacity = 1, 0); 1445 } 1446 } catch (err) { 1447 try { Sentry.captureException(err, { tags: { area: 'banners', op: 'desktopHoverInit' } }); } catch (_e) {} 1448 console.error(err); 1449 } 1450 }); 1451 1452 wrapper.addEventListener('mouseleave', function () { 1453 const video = this.querySelector('video'); 1454 if (video) { 1455 video.style.transition = 'opacity 0.5s'; 1456 video.style.opacity = 0; 1457 video.addEventListener('transitionend', () => { 1458 this.setAttribute('data-video-initialized', 'false'); 1459 const hlsInstance = hlsInstances[video.id]; 1460 if (hlsInstance) { 1461 hlsInstance.destroy(); 1462 delete hlsInstances[video.id]; 1463 } 1464 video.remove(); 1465 }); 1466 } 1467 }); 1468 }); 1469 } 1470 } catch (error) { 1471 try { Sentry.captureException(error, { tags: { area: 'banners', op: 'rightOfPlayer' } }); } catch (_e) {} 1472 if (document.querySelector('.video-right')) document.querySelector('.video-right').style.display = "none"; 1473 if (document.querySelector('.video-left')) document.querySelector('.video-left').style.width = "100%"; 1474 console.error(error); 1475 } 1476 } 1477 1478 if (varValues.gender === "fans") { 1479 (async () => { 1480 try {
1481 const stripChatResponse = await fetch(`https://broad-term-5811.aznude.workers.dev/?gender=${resolveGenderForWorker()}&tag=${varValues.liveSexDropdownGenderTags}`); 1482 const stripChatData = await stripChatResponse.json(); 1483 const { models } = stripChatData; 1484 await initializeStripChatBanners(models); 1485 } catch (error) { 1486 try { Sentry.captureException(error, { tags: { area: 'banners', op: 'fansPageLoad' } }); } catch (_e) {} 1487 console.error(error); 1488 } 1489 })(); 1490 } 1491 1492 const AD_CONFIG = { 1493 [varValues.ad_zones.native4x1_grid]: [ 1494 { selector: '#resized1' }, 1495 { selector: '#singlePageExo .visible-on-desktop' } 1496 ], 1497 [varValues.ad_zones.banner300x100]: [ 1498 { selector: '#resize1', width: 300 }, 1499 { selector: '#singlePageExo .visible-on-mobile', width: 300 } 1500 ], 1501 [varValues.ad_zones.native2x1_grid]: [ 1502 { selector: '#resize1' }, 1503 { selector: '#singlePageExo .visible-on-mobile' } 1504 ], 1505 [varValues.ad_zones.native1x1_mobile]: [ 1506 { selector: '#resize1' }, 1507 { selector: '#singlePageExo .visible-on-mobile' } 1508 ], 1509 [varValues.ad_zones.native1x1_video]: [ 1510 { selector: 'div.media-list-item.exo-item', multi: true, builder: (item, layout) => buildNativeVideo(item, layout) } 1511 ], 1512 [varValues.ad_zones.native4x4_footer]: [ 1513 { selector: '#aznude_native' } 1514 ], 1515 [varValues.ad_zones.banner300x100_alt]: [ 1516 { selector: '.native-footer-mobile', width: 300 } 1517 ], 1518 [varValues.ad_zones.banner300x250_mobile_footer]: [ 1519 { selector: '.native-footer-mobile', width: 300 } 1520 ], 1521 [varValues.ad_zones.native1x1_mobile_footer]: [ 1522 { selector: '.native-footer-mobile' } 1523 ], 1524 [varValues.ad_zones.native2x1_mobile_footer]: [ 1525 { selector: '.native-footer-mobile' } 1526 ], 1527 [varValues.ad_zones.native2x2_mobile_footer]: [ 1528 { selector: '.native-footer-mobile' } 1529 ], 1530 [varValues.ad_zones.banner728x90]: [ 1531 { selector: '#singlePageExo .visible-on-desktop', width: 728 } 1532 ], 1533 [varValues.ad_zones.native5x1_grid]: [ 1534 { selector: '#single-video-bottom-ad .visible-on-desktop' } 1535 ], 1536 [varValues.ad_zones.banner728x90_alt]: [ 1537 { selector: '#single-video-bottom-ad .visible-on-desktop', width: 728 } 1538 ], 1539 [varValues.ad_zones.native1x1_grid]: [ 1540 { selector: '#single-video-bottom-ad .visible-on-mobile' } 1541 ], 1542 [varValues.ad_zones.banner300x100_alt2]: [ 1543 { selector: '#single-video-bottom-ad .visible-on-mobile', width: 300 } 1544 ], 1545 [varValues.ad_zones.native2x1_grid_alt]: [ 1546 { selector: '#single-video-bottom-ad .visible-on-mobile' } 1547 ] 1548 }; 1549 1550 delete AD_CONFIG['undefined']; 1551 1552 const NB_CLS = (() => { 1553 const r = `x${Math.random().toString(36).slice(2, 8)}`; 1554 return { 1555 'exo-native-widget-item-image-wrapper': `${r}iw`, 1556 'exo-native-widget-item-image-ratio': `${r}ir`, 1557 'exo-native-widget-item-content-box': `${r}cb`, 1558 'exo-native-widget-outer-container': `${r}oc`, 1559 'exo-native-widget-item-container': `${r}ic`, 1560 'exo-native-widget-item-content': `${r}ct`, 1561 'exo-native-widget-item-image': `${r}im`, 1562 'exo-native-widget-item-title': `${r}ti`, 1563 'exo-native-widget-item-brand': `${r}br`, 1564 'exo-native-widget-item-text': `${r}tx`, 1565 'exo-native-widget-header': `${r}hd`, 1566 'exo-native-widget-item': `${r}it`,
1567 'exo-native-widget': `${r}wg` 1568 }; 1569 })(); 1570 1571 const NB_CLS_KEYS = Object.keys(NB_CLS).sort((a, b) => b.length - a.length); 1572 1573 let nbMountSeq = 0; 1574 1575 const NB_LAYOUT_OVERRIDES = { 1576 [varValues.ad_zones.native4x4_footer]: { image_height: '150px', mobile_image_width: 243, mobile_image_height: 150 }, 1577 1578 [varValues.ad_zones.native1x1_grid]: { 1579 text_width_pct: 'calc(75% - 20px)', 1580 extra_css: '.exo-native-widget-item-content { height: 34px !important; }' 1581 } 1582 }; 1583 1584 function nbEsc(v) { 1585 return String(v == null ? '' : v) 1586 .replace(/&/g, '&') 1587 .replace(/</g, '<') 1588 .replace(/>/g, '>') 1589 .replace(/"/g, '"') 1590 .replace(/'/g, '''); 1591 } 1592 1593 function nbUrl(v) { 1594 const s = String(v == null ? '' : v).trim(); 1595 return /^https?:\/\//i.test(s) ? s : ''; 1596 } 1597 1598 function nbCssUrl(v) { 1599 return nbUrl(v).replace(/["'()\\]/g, encodeURIComponent); 1600 } 1601 1602 function nbNum(v, fallback) { 1603 const n = parseFloat(v); 1604 return Number.isFinite(n) ? n : fallback; 1605 } 1606 1607 function nbFlow(position, boxSize, imgW, pctOverride) { 1608 const p = String(position == null ? 'bottom' : position).toLowerCase(); 1609 const row = (p === 'left' || p === 'right'); 1610 const boxPx = nbNum(boxSize, 0); 1611 const auto = (boxPx > 0 && imgW > 0) ? (boxPx / (boxPx + imgW)) * 100 : 50; 1612 const override = nbNum(pctOverride, 0); 1613 const w = (typeof pctOverride === 'string' && pctOverride.indexOf('calc') === 0) 1614 ? pctOverride 1615 : `${(override > 0 && override < 100) ? override : auto}%`; 1616 1617 return { 1618 row: row, 1619 dir: p === 'right' ? 'row' 1620 : p === 'left' ? 'row-reverse' 1621 : p === 'top' ? 'column-reverse' 1622 : 'column', 1623 img: row 1624 ? 'flex: 1 1 auto; min-width: 0; width: auto; height: auto;' 1625 : 'flex: initial; width: 100%; height: 100%;', 1626 box: row 1627 ? `flex: 0 0 ${w}; width: ${w}; max-width: ${w}; height: auto; max-height: none;` 1628 : `flex: initial; width: 100%; max-width: 100%; height: ${boxSize}; max-height: ${boxSize};` 1629 }; 1630 } 1631 1632 function nbMapClasses(s) { 1633 let out = String(s == null ? '' : s); 1634 for (let i = 0; i < NB_CLS_KEYS.length; i++) { 1635 out = out.split(NB_CLS_KEYS[i]).join(NB_CLS[NB_CLS_KEYS[i]]); 1636 } 1637 return out; 1638 } 1639 1640 function nbScopeCss(css, scope) { 1641 if (!css) return ''; 1642 const src = String(css).replace(/\/\*[\s\S]*?\*\//g, ''); 1643 let out = ''; 1644 let i = 0; 1645 1646 while (i < src.length) { 1647 const open = src.indexOf('{', i); 1648 if (open === -1) break; 1649 1650 const prelude = src.slice(i, open).trim(); 1651 1652 if (prelude.charAt(0) === '@') { 1653 let depth = 0; 1654 let j = open; 1655 for (; j < src.length; j++) { 1656 if (src[j] === '{') { 1657 depth++; 1658 } else if (src[j] === '}') { 1659 depth--; 1660 if (depth === 0) break; 1661 } 1662 } 1663 out += `${prelude}{${nbScopeCss(src.slice(open + 1, j), scope)}}`; 1664 i = j + 1; 1665 } else { 1666 let close = src.indexOf('}', open); 1667 if (close === -1) close = src.length; 1668 1669 const sel = prelude 1670 .split(',') 1671 .map(s => s.trim()) 1672 .filter(Boolean) 1673 .map(s => `${scope} ${s}`) 1674 .join(','); 1675 1676 if (sel) out += `${sel}{${src.slice(open + 1, close)}}`; 1677 i = close + 1; 1678 } 1679 } 1680 1681 return out; 1682 } 1683 1684 function nbWidgetCss(L, scope) { 1685 const C = NB_CLS; 1686 1687 const perRow = parseInt(L.itemsPerRow, 10) || 1; 1688 const mobCols = parseInt(L.mobile_cols, 10) || perRow; 1689 const mobRows = parseInt(L.mobile_rows, 10) || 1; 1690 const bp = parseInt(L.mobile_breakpoint, 10) || 450; 1691 1692 const gapH = nbNum(L.spacing_h, 0); 1693 const gapV = nbNum(L.spacing_v, 0); 1694 1695 const imgW = nbNum(L.image_width, 0) || 1; 1696 const imgH = nbNum(L.image_height, 0); 1697 const ratio = (imgH / imgW) * 100; 1698 1699 const font = L.font_family || 'Arial, Helvetica, Verdana, sans-serif'; 1700 const baseSize = L.header_font_size || '12px'; 1701 const baseColor = L.header_font_color || '#999999'; 1702 const boxSize = L.text_box_size || '90px'; 1703 const padTop = L.item_padding || '0px'; 1704 const border = nbNum(L.image_border_size, 0); 1705 const cap = Math.max(1, mobRows * mobCols); 1706 1707 const flow = nbFlow(L.text_position, boxSize, imgW, L.text_width_pct); 1708 1709 const mImgW = nbNum(L.mobile_image_width, 0) || imgW; 1710 const mImgH = nbNum(L.mobile_image_height, 0) || imgH; 1711 const mRatio = (mImgH / mImgW) * 100; 1712 const mBoxSize = `${nbNum(L.mobile_text_box_size, 0) || nbNum(boxSize, 90)}px`; 1713 const mFlow = nbFlow(L.mobile_text_position == null ? L.text_position : L.mobile_text_position, mBoxSize, mImgW, L.mobile_text_width_pct == null ? L.text_width_pct : L.mobile_text_width_pct); 1714 const mText = L.mobile_text_enabled == null ? L.text_enabled : L.mobile_text_enabled; 1715 1716 const wg = `.${C['exo-native-widget']}`; 1717 const hd = `.${C['exo-native-widget-header']}`; 1718 const oc = `.${C['exo-native-widget-outer-container']}`; 1719 const ic = `.${C['exo-native-widget-item-container']}`; 1720 const it = `.${C['exo-native-widget-item']}`; 1721 const iw = `.${C['exo-native-widget-item-image-wrapper']}`; 1722 const im = `.${C['exo-native-widget-item-image']}`; 1723 const ir = `.${C['exo-native-widget-item-image-ratio']}`; 1724 const ct = `.${C['exo-native-widget-item-content']}`; 1725 const cb = `.${C['exo-native-widget-item-content-box']}`; 1726 const ti = `.${C['exo-native-widget-item-title']}`; 1727 const tx = `.${C['exo-native-widget-item-text']}`; 1728 const br = `.${C['exo-native-widget-item-brand']}`; 1729 1730 return ` 1731 ${scope}, ${scope} * { box-sizing: border-box; } 1732 ${scope} { 1733 background-color: ${L.widget_background_color || 'transparent'}; 1734 overflow: hidden; 1735 max-width: 100%; 1736 width: ${L.widget_width || '100%'}; 1737 min-width: 103px; 1738 display: inline-flex; 1739 flex-direction: ${parseInt(L.header_is_on_top, 10) === 0 ? 'column-reverse' : 'column'}; 1740 text-align: initial; 1741 } 1742 ${scope} ${hd} { 1743 width: initial; 1744 float: initial; 1745 white-space: nowrap; 1746 font-family: ${font}; 1747 font-size: ${baseSize}; 1748 color: ${baseColor}; 1749 display: ${parseInt(L.header_enabled, 10) === 1 ? 'block' : 'none'}; 1750 padding-bottom: 15px; 1751 line-height: 15px; 1752 } 1753 ${scope} ${hd} a { 1754 display: flex; 1755 justify-content: ${L.header_text_align === 'left' ? 'flex-start' : (L.header_text_align === 'center' ? 'center' : 'flex-end')}; 1756 color: inherit; 1757 text-decoration: none; 1758 } 1759 ${scope} ${oc} { 1760 display: flex; 1761 flex-wrap: wrap; 1762 height: 100%; 1763 align-content: space-between; 1764 margin-bottom: -${gapV}px; 1765 margin-right: -${gapH}px; 1766 width: inherit; 1767 max-width: 100%; 1768 } 1769 ${scope} ${oc} ${ic} { 1770 overflow: hidden; 1771 flex-basis: calc(100% / ${perRow}); 1772 position: relative; 1773 padding-right: ${gapH}px; 1774 padding-bottom: ${gapV}px; 1775 } 1776 ${scope} ${oc} ${ic} a${it} { 1777 text-decoration: none; 1778 font-family: ${font}; 1779 font-size: ${baseSize}; 1780 color: ${baseColor}; 1781 overflow: hidden; 1782 display: flex; 1783 position: relative; 1784 flex-direction: ${flow.dir}; 1785 } 1786 ${scope} ${oc} ${ic} a${it} ${iw} { 1787 position: relative; 1788 float: left; 1789 ${flow.img} 1790 z-index: 2; 1791 height: 100%; 1792 overflow: hidden;${border > 0 ? ` 1793 border: ${border}px solid ${L.image_border_color || '#000000'};` : ''} 1794 } 1795 ${scope} ${oc} ${ic} a${it} ${ir} { 1796 width: 100%; 1797 padding-top: ${ratio}%; 1798 z-index: 1; 1799 } 1800 ${scope} ${oc} ${ic} a${it} ${im} { 1801 background-position: center; 1802 background-repeat: no-repeat; 1803 background-size: cover; 1804 position: absolute; 1805 top: 0; 1806 left: 0; 1807 width: 100%; 1808 height: 100%; 1809 transition: transform .3s, background-size .3s; 1810 } 1811 ${scope} ${oc} ${ic} a${it} ${im}:hover { 1812 transform: scale(1.05); 1813 } 1814 ${scope} ${oc} ${ic} a${it} ${ct} { 1815 ${flow.box} 1816 overflow: hidden; 1817 right: 0; 1818 top: 0; 1819 padding-left: ${flow.dir === 'row' ? padTop : (L.text_margin_left || '0px')}; 1820 padding-right: ${flow.dir === 'row-reverse' ? padTop : (L.text_margin_right || '0px')}; 1821 padding-top: ${flow.row ? (L.text_margin_top || '0px') : padTop}; 1822 padding-bottom: 0; 1823 display: ${parseInt(L.text_enabled, 10) === 0 ? 'none' : 'block'}; 1824 text-align: ${L.text_align || 'left'}; 1825 position: relative; 1826 } 1827 ${scope} ${oc} ${ic} a${it} ${ct} ${cb} { 1828 position: absolute; 1829 width: 100%; 1830 } 1831 ${scope} ${oc} ${ic} a${it} ${ct} ${ti} { 1832 font-family: ${font}; 1833 font-size: ${L.title_font_size || '12px'}; 1834 color: ${L.title_font_color || '#ffffff'}; 1835 font-weight: ${L.title_font_weight || 'normal'}; 1836 text-decoration: ${L.title_decoration || 'none'}; 1837 display: ${parseInt(L.title_enabled, 10) === 1 ? 'block' : 'none'}; 1838 transition: color 0.16s; 1839 } 1840 ${scope} ${oc} ${ic} a${it} ${ct} ${tx} { 1841 font-family: ${font}; 1842 font-size: ${L.description_font_size || '11px'}; 1843 color: ${L.description_font_color || '#555555'}; 1844 font-weight: ${L.description_font_weight || 'normal'}; 1845 text-decoration: ${L.description_decoration || 'none'}; 1846 display: ${parseInt(L.description_enabled, 10) === 1 ? 'block' : 'none'}; 1847 transition: color 0.16s; 1848 } 1849 ${scope} ${oc} ${ic} a${it} ${ct} ${br} { 1850 font-family: ${font}; 1851 font-size: ${L.brand_font_size || '11px'}; 1852 color: ${L.brand_font_color || '#555555'}; 1853 font-weight: ${L.brand_font_weight || 'normal'}; 1854 text-decoration: ${L.brand_decoration || 'none'}; 1855 display: ${parseInt(L.brand_enabled, 10) === 1 ? 'block' : 'none'}; 1856 } 1857 ${scope} ${oc} ${ic} a${it} ${ct} ${ti}:hover { 1858 color: ${L.title_hover_color || L.title_font_color || '#ffffff'}; 1859 font-weight: ${L.title_hover_font_weight || L.title_font_weight || 'normal'}; 1860 text-decoration: ${L.title_hover_decoration || 'none'}; 1861 } 1862 ${scope} ${oc} ${ic} a${it} ${ct} ${tx}:hover { 1863 color: ${L.description_hover_color || L.description_font_color || '#555555'}; 1864 font-weight: ${L.description_hover_font_weight || L.description_font_weight || 'normal'}; 1865 text-decoration: ${L.description_hover_decoration || 'none'}; 1866 } 1867 ${scope} ${ti}, ${scope} ${tx} { 1868 white-space: nowrap; 1869 overflow: hidden; 1870 text-overflow: ellipsis; 1871 } 1872 @media all and (max-width: ${bp}px) { 1873 ${scope} ${oc} ${ic}:nth-child(n + ${cap + 1}) { display: none; } 1874 ${scope} ${oc} ${ic} a${it} { flex-direction: ${mFlow.dir}; } 1875 ${scope} ${oc} ${ic} a${it} ${iw} { ${mFlow.img} } 1876 ${scope} ${oc} ${ic} a${it} ${ir} { padding-top: ${mRatio}%; } 1877 ${scope} ${oc} ${ic} a${it} ${ct} { ${mFlow.box} padding-left: ${mFlow.dir === 'row' ? padTop : (L.text_margin_left || '0px')}; padding-right: ${mFlow.dir === 'row-reverse' ? padTop : (L.text_margin_right || '0px')}; padding-top: ${mFlow.row ? (L.text_margin_top || '0px') : padTop}; display: ${parseInt(mText, 10) === 0 ? 'none' : 'block'}; } 1878 ${scope} ${oc} ${ic} { flex-basis: calc(100% / ${mobCols}); } 1879 ${scope} { width: 100% !important; height: auto !important; } 1880 ${scope} ${ic} { clear: both; max-width: 100% !important; margin-left: 0 !important; } 1881 ${scope} ${it} ${im} { width: 100% !important; max-width: 100% !important; margin: auto !important; } 1882 ${scope} ${ic} ${it} ${ct} { max-width: 100% !important; } 1883 ${scope} ${ic} ${it} { margin: 0 auto !important; } 1884 }`; 1885 } 1886 1887 function buildNbNativeWidget(zoneData, idzone) { 1888 const C = NB_CLS; 1889 const base = (zoneData && zoneData.data && zoneData.data.layout) || {}; 1890 const L = Object.assign({}, base, NB_LAYOUT_OVERRIDES[idzone] || {}); 1891 const items = (zoneData && zoneData.data && zoneData.data.data) || []; 1892 1893 if (!items.length) return ''; 1894 1895 const cap = (parseInt(L.itemsPerRow, 10) || 1) * (parseInt(L.itemsPerCol, 10) || 1); 1896 const shown = items.slice(0, cap || items.length); 1897 1898 const mountId = `az-cw-${++nbMountSeq}`; 1899 const scope = `#${mountId}`; 1900 1901 const newTab = parseInt(L.open_in_new_window, 10) === 1; 1902 const target = newTab ? ' target="_blank"' : ''; 1903 1904 const itemsHTML = shown.map(item => { 1905 const href = nbUrl(item.url); 1906 if (!href) return ''; 1907 const img = nbCssUrl(item.optimum_image || item.image); 1908 const pos = nbEsc(item.image_position || '50% 50%'); 1909 1910 return `<div class="${C['exo-native-widget-item-container']}"> 1911 <a class="${C['exo-native-widget-item']}" href="${nbEsc(href)}" rel="nofollow noopener"${target}> 1912 <div class="${C['exo-native-widget-item-image-wrapper']}"> 1913 <div class="${C['exo-native-widget-item-image']}" style="background-image: url("${img}"); background-position: ${pos};"></div> 1914 <div class="${C['exo-native-widget-item-image-ratio']}"></div> 1915 </div> 1916 <div class="${C['exo-native-widget-item-content']}"> 1917 <div class="${C['exo-native-widget-item-title']}">${nbEsc(item.title)}</div> 1918 <div class="${C['exo-native-widget-item-text']}">${nbEsc(item.description)}</div> 1919 <div class="${C['exo-native-widget-item-bran
1919d']}">${nbEsc(item.brand)}</div> 1920 </div> 1921 </a> 1922 </div>`; 1923 }).join(''); 1924 1925 if (!itemsHTML) return ''; 1926 1927 let css = nbWidgetCss(L, scope); 1928 if (parseInt(L.customcss_enabled, 10) === 1 && L.customcss) { 1929 css += `\n${nbScopeCss(nbMapClasses(L.customcss), scope)}`; 1930 } 1931 if (L.extra_css) { 1932 css += `\n${nbScopeCss(nbMapClasses(L.extra_css), scope)}`; 1933 } 1934 1935 return `<div style="text-align:center;max-width:100%"><style>${css}</style><div id="${mountId}" class="${C['exo-native-widget']}"> 1936 <div class="${C['exo-native-widget-header']}"></div> 1937 <div class="${C['exo-native-widget-outer-container']}">${itemsHTML}</div> 1938 </div></div>`; 1939 } 1940 1941 function buildSimpleBanner(data, width, layout) { 1942 const href = nbUrl(data.url); 1943 const img = nbUrl(data.image); 1944 if (!href || !img) return ''; 1945 return `<a href="${nbEsc(href)}" rel="nofollow noopener" target="_blank"><img src="${nbEsc(img)}" alt="" style="width: 100%; max-width: ${parseInt(width, 10) || 300}px; height: auto" /></a>`; 1946 } 1947 1948 function buildNativeVideo(item, layout) { 1949 const href = nbUrl(item.url); 1950 if (!href) return ''; 1951 const img = nbCssUrl(item.optimum_image || item.image); 1952 return ` 1953 <div class="videos-list__line-ad"> 1954 <span class="video-ad-label">AD</span> 1955 <a href="${nbEsc(href)}" rel="nofollow noopener" target="_blank" class="exo-native-az-box-wrapper"> 1956 <div class="exo-native-az-box-img-wrapper"> 1957 <div class="exo-native-az-box-img" style="background-position: ${nbEsc(item.image_position || '50% 50%')}; background-image: url("${img}");"></div> 1958 </div> 1959 <div class="exo-native-widget-item-content"> 1960 <div class="exo-native-widget-item-title">${nbEsc(item.title)}</div> 1961 <div class="exo-native-widget-item-text">${nbEsc(item.description)}</div> 1962 </div> 1963 </a> 1964 </div> 1965 `; 1966 } 1967 1968 function cleanupFailedAds() { 1969 $(document).ready(function() { 1970 $( 1971 '#resize1, #resized1, .az-footer, #aznude_native,' + 1972 '.clearfix.visible-lg, #exo-native-ad-box-clearfix1, #exo-native-az-box-clearfix1,' + 1973 '#exo-native-az-container, .native-footer-mobile,' + 1974 '#singlePageExo .visible-on-desktop, #singlePageExo .visible-on-mobile,' + 1975 '#single-video-bottom-ad,' + 1976 '.single-video-sidebar-container' 1977 ).each(function () { 1978 this.style.setProperty('display', 'none', 'important'); 1979 }); 1980 1981 $('.single-video-content-wrapper').css('max-width', '100%') 1982 1983 $('div.media-list-item.exo-item').remove(); 1984 1985 $('ins[data-zoneid], ins.eas6a97888e, ins.eas6a97888e2, ins.adsbyexoclick').each(function() { 1986 $(this).hide(); 1987 }); 1988 1989 }); 1990 } 1991 1992 async function fetchNeverblockWithTimeout(zoneId, timeout = 2000) { 1993 const controller = new AbortController(); 1994 const timeoutId = setTimeout(() => controller.abort(), timeout); 1995 1996 if (typeof nbDomain !== 'undefined' && nbDomain) { 1997 try { 1998 const response = await fetch( 1999 `${nbDomain}/?zones=[${zoneId}]`, 2000 { signal: controller.signal } 2001 ); 2002 clearTimeout(timeoutId); 2003 2004 if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); 2005 2006 const data = await response.json(); 2007 if (!data || !data.zones || data.zones.length === 0) throw new Error('Empty or invalid ad response from rotated domain'); 2008 2009 const zone = data.zones[0]; 2010 if (!zone || !zone.idzone || !zone.data) throw new Error('Invalid zone data structure from rotated domain'); 2011 2012 return data; 2013 } catch (error) { 2014 clearTimeout(timeoutId); 2015 try { 2016 Sentry.captureException(new Error(`Rotated domain failed (${nbDomain}): ${error.message}`), { 2017 tags: { area: 'ads', op: 'rotatedDomainFallback' } 2018 }); 2019 } catch (_e) {} 2020 } 2021 } 2022 2023 const fallbackController = new AbortController(); 2024 const fallbackTimeoutId = setTimeout(() => fallbackController.abort(), timeout); 2025 2026 try { 2027 const response = await fetch( 2028 `https://az-neverblock.aznude.workers.dev/?zones=[${zoneId}]`, 2029 { signal: fallbackController.signal } 2030 ); 2031 clearTimeout(fallbackTimeoutId); 2032 2033 if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); 2034 2035 const data = await response.json(); 2036 2037 if (!data || !data.zones || data.zones.length === 0) throw new Error('Empty or invalid ad response'); 2038 2039 const zone = data.zones[0]; 2040 if (!zone || !zone.idzone || !zone.data) throw new Error('Invalid zone data structure'); 2041 2042 return data; 2043 } catch (error) { 2044 clearTimeout(fallbackTimeoutId); 2045 if (error.name === 'AbortError') { 2046 throw new Error('Neverblock request timed out'); 2047 } 2048 throw error; 2049 } 2050 } 2051 2052 function injectAd(zoneId, zoneData) { 2053 const configs = AD_CONFIG[zoneId]; 2054 if (!configs || !zoneData) return; 2055 2056 const isNative = zoneData.type === 'native_ad'; 2057 const layout = isNative ? zoneData.data.layout : {}; 2058 const data = isNative ? zoneData.data.data : zoneData.data; 2059
2060 configs.forEach(config => { 2061 const elements = config.multi 2062 ? Array.from(document.querySelectorAll(config.selector)) 2063 : [document.querySelector(config.selector)].filter(Boolean); 2064 2065 if (!elements.length) return; 2066 2067 elements.forEach((el, idx) => { 2068 let html; 2069 2070 if (config.builder) { 2071 // Rotate through the returned items so repeated slots don't 2072 // all render the identical creative. 2073 html = config.multi && isNative 2074 ? config.builder(data[idx % data.length], layout) 2075 : config.builder(data, layout); 2076 } else if (isNative) { 2077 // Fresh mount id per element so the scoped CSS never collides. 2078 html = buildNbNativeWidget(zoneData, zoneId); 2079 } else { 2080 html = buildSimpleBanner(data, config.width || 300, layout); 2081 } 2082 2083 if (!html) return; 2084 2085 if (config.multi && zoneData.type === 'banner') { 2086 el.parentNode.innerHTML = html; 2087 } else { 2088 el.innerHTML = html; 2089 } 2090 }); 2091 }); 2092 } 2093 2094 (async () => { 2095 try { 2096 if(!(await userHasAdBlockerOn())) return; 2097 } catch(e) { 2098 console.error('Ad blocker check failed:', e); 2099 return; 2100 } 2101 2102 const countryCode = await getCountryCode(); 2103 if(countryCode === "GB" || countryCode === "AU") { 2104 let verified = isAgeVerified(); 2105 2106 if (!verified && auth.currentUser) verified = await isAgeVerifiedInDB(auth.currentUser.uid); 2107 if (!verified) return cleanupFailedAds(); 2108 } 2109 2110 let adsFailed = false; 2111 let stripChatData = null; 2112 2113 try { 2114 if (varValues.gender !== "fans") {
2115 stripChatData = await fetch( 2116 `https://broad-term-5811.aznude.workers.dev/?gender=${resolveGenderForWorker()}&tag=${varValues.liveSexDropdownGenderTags}` 2117 ).then(r => r.json()); 2118 } 2119 } catch (e) { 2120 try { Sentry.captureException(e, { tags: { area: 'ads', op: 'stripChat' } }); } catch (_e) {} 2121 } 2122 2123 if($('#age-modal-ad')){ 2124 $('#age-modal-ad').hide(); 2125 } 2126 2127 const useStripChatFooter = stripChatData && Math.random() >= 0.5; 2128 2129 if (document.querySelector('#resized1') || document.querySelector('#singlePageExo .visible-on-desktop')) { 2130 try { 2131 const isSinglePage = !!document.querySelector('#singlePageExo'); 2132 let desktopZoneId; 2133 2134 if (isSinglePage) { 2135 const desktopRandom = Math.random(); 2136 desktopZoneId = desktopRandom < 0.5 2137 ? varValues.ad_zones.banner728x90 2138 : varValues.ad_zones.native4x1_grid; 2139 } else { 2140 desktopZoneId = varValues.ad_zones.native4x1_grid; 2141 } 2142 2143 const data = await fetchNeverblockWithTimeout(desktopZoneId); 2144 data.zones.forEach(zone => { 2145 if (zone && zone.idzone) { 2146 injectAd(zone.idzone, zone); 2147 } 2148 }); 2149 } catch (error) { 2150 adsFailed = true; 2151 } 2152 } 2153 2154 if (document.querySelector('#resize1') || document.querySelector('#singlePageExo .visible-on-mobile')) { 2155 try { 2156 const mobileHeaderRandom = Math.random(); 2157 let mobileHeaderZoneId; 2158 if (mobileHeaderRandom < 0.3333) { 2159 mobileHeaderZoneId = varValues.ad_zones.banner300x100; 2160 } else if (mobileHeaderRandom < 0.6666) { 2161 mobileHeaderZoneId = varValues.ad_zones.native2x1_grid; 2162 } else { 2163 mobileHeaderZoneId = varValues.ad_zones.native1x1_mobile; 2164 } 2165 2166 const data = await fetchNeverblockWithTimeout(mobileHeaderZoneId); 2167 data.zones.forEach(zone => { 2168 if (zone && zone.idzone) { 2169 injectAd(zone.idzone, zone); 2170 } 2171 }); 2172 } catch (error) { 2173 adsFailed = true; 2174 } 2175 } 2176 2177 if (document.querySelectorAll('div.media-list-item.exo-item').length > 0) { 2178 try { 2179 const data = await fetchNeverblockWithTimeout(varValues.ad_zones.native1x1_video); 2180 data.zones.forEach(zone => { 2181 if (zone && zone.idzone) { 2182 injectAd(zone.idzone, zone); 2183 } 2184 }); 2185 } catch (error) { 2186 adsFailed = true; 2187 } 2188 } 2189 2190 if (document.querySelector('#aznude_native') && !useStripChatFooter) { 2191 try { 2192 const data = await fetchNeverblockWithTimeout(varValues.ad_zones.native4x4_footer); 2193 data.zones.forEach(zone => { 2194 if (zone && zone.idzone) { 2195 injectAd(zone.idzone, zone); 2196 } 2197 }); 2198 } catch (error) { 2199 adsFailed = true; 2200 } 2201 } 2202 2203 if (document.querySelector('.native-footer-mobile')) { 2204 try { 2205 const mobileFooterRandom = Math.random(); 2206 let mobileFooterZoneId; 2207 if (mobileFooterRandom < 0.2) { 2208 mobileFooterZoneId = varValues.ad_zones.banner300x100_alt; 2209 } else if (mobileFooterRandom < 0.4) { 2210 mobileFooterZoneId = varValues.ad_zones.banner300x250_mobile_footer; 2211 }
2211 else if (mobileFooterRandom < 0.6) { 2212 mobileFooterZoneId = varValues.ad_zones.native2x2_mobile_footer; 2213 } else if (mobileFooterRandom < 0.8) { 2214 mobileFooterZoneId = varValues.ad_zones.native2x1_mobile_footer; 2215 } else { 2216 mobileFooterZoneId = varValues.ad_zones.native1x1_mobile_footer; 2217 } 2218 2219 const data = await fetchNeverblockWithTimeout(mobileFooterZoneId); 2220 data.zones.forEach(zone => { 2221 if (zone && zone.idzone) { 2222 injectAd(zone.idzone, zone); 2223 } 2224 }); 2225 } catch (error) { 2226 adsFailed = true; 2227 } 2228 } 2229 2230 if (document.querySelector('#single-video-bottom-ad .visible-on-desktop')) { 2231 try { 2232 const videoBottomDesktopRandom = Math.random(); 2233 const videoBottomDesktopZoneId = videoBottomDesktopRandom < 0.5 2234 ? varValues.ad_zones.native5x1_grid 2235 : varValues.ad_zones.banner728x90_alt; 2236 2237 const data = await fetchNeverblockWithTimeout(videoBottomDesktopZoneId); 2238 data.zones.forEach(zone => { 2239 if (zone && zone.idzone) { 2240 injectAd(zone.idzone, zone); 2241 } 2242 }); 2243 } catch (error) { 2244 adsFailed = true; 2245 } 2246 } 2247 2248 if (document.querySelector('#single-video-bottom-ad .visible-on-mobile')) { 2249 try { 2250 const videoBottomMobileRandom = Math.random(); 2251 let videoBottomMobileZoneId; 2252 if (videoBottomMobileRandom < 0.3333) { 2253 videoBottomMobileZoneId = varValues.ad_zones.native1x1_grid; 2254 } else if (videoBottomMobileRandom < 0.6666) { 2255 videoBottomMobileZoneId = varValues.ad_zones.banner300x100_alt2; 2256 } else { 2257 videoBottomMobileZoneId = varValues.ad_zones.native2x1_grid_alt; 2258 } 2259 2260 const data = await fetchNeverblockWithTimeout(videoBottomMobileZoneId); 2261 data.zones.forEach(zone => { 2262 if (zone && zone.idzone) { 2263 injectAd(zone.idzone, zone); 2264 } 2265 }); 2266 } catch (error) { 2267 adsFailed = true; 2268 } 2269 } 2270 2271 if (stripChatData) { 2272 if (useStripChatFooter) { 2273 try { 2274 $('.az-footer').hide(); 2275 $('.single-video-4x4-ad-container').hide(); 2276 const aznlive_mobile_banner = document.getElementById('aznlive-horizontal__banner'); 2277 const banner_mobile_items = document.getElementsByClassName('aznlive-horizontal__item'); 2278 2279 if(aznlive_mobile_banner) aznlive_mobile_banner.style.display = 'flex'; 2280 2281 for (let i = 0; i < 6; i++) { 2282 const { username, snapshotUrl, stream } = stripChatData.models[i]; 2283 2284 if(banner_mobile_items.length){ 2285 const sourceIdMobile = `sourceId=aznude-footer`; 2286 const fallbackUrlMobile = `https://aznudelive.com/${username}?${sourceIdMobile}&userId=e5832eb0a24a6150f7d3967ae12ea6e95a0e3ce83557ea66397b6ed5ba72751e&targetDomain=aznudelive.com/${varValues.aznudeLiveCamOutUrl}&p=verify50af` 2287 const hrefMobile = user ? `${baseUrl}&${sourceIdMobile}&site=/${username}&fallbackUrl=${encodeURIComponent(fallbackUrlMobile)}` : fallbackUrlMobile; 2288 2289 banner_mobile_items[i].classList.add('aznlive-horizontal__item-is-live'); 2290 banner_mobile_items[i].setAttribute('data-video-src', stream.url); 2291 banner_mobile_items[i].setAttribute('href', hrefMobile); 2292 banner_mobile_items[i].style.backgroundImage = `url('${snapshotUrl}')`; 2293 banner_mobile_items[i].innerHTML = ''; 2294 } 2295 } 2296 } catch (error) { 2297 try { Sentry.captureException(error, { tags: { area: 'banners', op: 'mobileFooterBanner' } }); } catch (_e) {} 2298 document.getElementById('aznlive-horizontal__banner').style.display = "none"; 2299 if(document.querySelector('.single-video-4x4-ad-container')) document.querySelector('.single-video-4x4-ad-container').style.display = "none"; 2300 } 2301 } 2302 2303 try { 2304 await initializeStripChatBanners(stripChatData.models); 2305 } catch (error) { 2306 try { Sentry.captureException(error, { tags: { area: 'ads', op: 'stripChatBanners' } }); } catch (_e) {} 2307 } 2308 } 2309 2310 if (adsFailed) { 2311 cleanupFailedAds(); 2312 } 2313 })(); 2314 }); 2315});
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.