1(() => { 2 class GWD_Banner_Renderer { 3 static normalizeKey(key) { 4 return String(key || "").toLowerCase().replace(/[^a-z0-9]+/g, ""); 5 } 6 7 static matchesKey(key, aliases) { 8 const normalized = this.normalizeKey(key); 9 return aliases.some(alias => normalized.startsWith(this.normalizeKey(alias))); 10 } 11 12 static getValueByKeys(item, aliases) { 13 if (!item || typeof item !== "object") return ""; 14 15 const normalizedAliases = aliases.map(alias => this.normalizeKey(alias)); 16 const key = Object.keys(item).find(name => normalizedAliases.includes(this.normalizeKey(name))); 17 18 return key ? item[key] : ""; 19 } 20 21 static findAdByKeys(data, aliases) { 22 const normalizedAliases = aliases.map(alias => this.normalizeKey(alias)); 23 const key = Object.keys(data).find(name => normalizedAliases.includes(this.normalizeKey(name))); 24 25 return key ? data[key] : null; 26 } 27 28 static findAdsByPrefix(data, prefix) { 29 const normalizedPrefix = this.normalizeKey(prefix); 30 31 return Object.entries(data) 32 .filter(([key, value]) => this.normalizeKey(key).startsWith(normalizedPrefix) && value) 33 .map(([, value]) => value); 34 } 35 36 static appendAd(container, value) { 37 const html = this.getValueByKeys(value, ["html_code", "html code", "html-code", "html"]); 38 39 if (html) { 40 container.insertAdjacentHTML("beforeend", html); 41 return; 42 } 43 44 const zone = value && typeof value === "object" 45 ? this.getValueByKeys(value, ["zone", "zone_id", "zone id", "id", "data-z", "data_z", "data z"]) 46 : value; 47 48 if (!zone) return; 49 50 const ins = document.createElement("ins"); 51 ins.className = "banner_item-theme"; 52 ins.setAttribute("data-z", zone); 53 ins.setAttribute("data-revive-id", "1718ffff6aff14155bf9e84ffb3a29ee"); 54 container.appendChild(ins); 55 } 56 57 static renderVideoBottom(data, container) { 58 // khong can check container o day 59 container.innerHTML = ""; 60 61 const overlay = document.createElement("div"); 62 overlay.className = "gwd-banner-bottom__overlay"; 63 64 const infeed = document.createElement("div"); 65 infeed.className = "gwd-banner-bottom__infeed"; 66 67 Object.entries(data).forEach(([key, value]) => { 68 if (this.matchesKey(key, ["bottom-overlay"])) { 69 this.appendAd(overlay, value); 70 } 71 72 if (this.matchesKey(key, ["bottom-infeed-left","bottom-infeed-right"])) { 73 this.appendAd(infeed, value); 74 } 75 }); 76 77 if (overlay.children.length) { 78 container.appendChild(overlay); 79 } 80 81 if (infeed.children.length) { 82 container.appendChild(infeed); 83 } 84 } 85 86 static renderVideoTop(data, container) { 87 // khong can check container o day 88 container.innerHTML = ""; 89 container.classList.add("gwd-top-strip-marquee"); 90 91 const track = document.createElement("div"); 92 track.className = "gwd-top-strip-marquee__track"; 93 94 Object.entries(data).forEach(([key, value]) => { 95 if (this.matchesKey(key, ["top-strip"])) { 96 this.appendAd(track, value); 97 } 98 }); 99 100 if (!track.children.length) return; 101 102 Array.from(track.children).forEach((child) => { 103 track.appendChild(child.cloneNode(true)); 104 }); 105 106 container.appendChild(track); 107 } 108 109 static renderTopOverlay(data, container) { 110 // khong can check container o day 111 let overlay = container.querySelector(".gwd-banner-top__overlay"); 112 113 if (!overlay) { 114 overlay = document.createElement("div"); 115 overlay.className = "gwd-banner-top__overlay"; 116 container.prepend(overlay); 117 } 118 119 overlay.innerHTML = ""; 120
121 Object.entries(data).forEach(([key, value]) => { 122 if (this.matchesKey(key, ["top-overlay"])) { 123 this.appendAd(overlay, value); 124 } 125 }); 126 } 127 128 static renderGutterAfterHeader(data, referenceNode) { 129 if (!referenceNode) return; 130 131 let random = referenceNode.dataset.gutterRandom; 132 133 if (!random) { 134 random = Math.random().toString(36).slice(2, 8); 135 referenceNode.dataset.gutterRandom = random; 136 } 137 138 const gutterClass = `${random}_gutter_banner gutter-banner`; 139 const leftClass = `${random}_gutter_left gutter-left`; 140 const rightClass = `${random}_gutter_right gutter-right`; 141 142 let left = referenceNode.nextElementSibling?.classList.contains(leftClass) 143 ? referenceNode.nextElementSibling 144 : document.createElement("div"); 145 146 let right = left.nextElementSibling?.classList.contains(rightClass) 147 ? left.nextElementSibling 148 : document.createElement("div"); 149 150 if (!left.parentElement) { 151 left.className = `${gutterClass} ${leftClass}`; 152 referenceNode.after(left); 153 } 154 155 if (!right.parentElement) { 156 right.className = `${gutterClass} ${rightClass}`; 157 left.after(right); 158 } 159 160 left.innerHTML = ""; 161 right.innerHTML = ""; 162 163 Object.entries(data).forEach(([key, value]) => { 164 if (this.matchesKey(key, ["sidebar-left"])) { 165 this.appendAd(left, value); 166 } else if (this.matchesKey(key, ["sidebar-right"])) { 167 this.appendAd(right, value); 168 } 169 }); 170 } 171 172 static renderBetButtons(data) { 173 const buttons = document.querySelectorAll(".gwd_btn_bet"); 174 const ads = this.findAdsByPrefix(data, "betbutton-"); 175 176 if (!buttons.length || !ads.length) return; 177 178 buttons.forEach(button => { 179 button.innerHTML = ""; 180 }); 181 182 ads.forEach((ad, index) => { 183 const button = buttons[index] || buttons[buttons.length - 1]; 184 185 this.appendAd(button, ad); 186 }); 187 } 188 189 static renderOddListLogo(data) { 190 const ads = this.findAdsByPrefix(data, "logoicon-").slice(0, 3); 191 const items = document.querySelectorAll(".table_oddList li"); 192 193 if (!ads.length || !items.length) return; 194 195 ads.forEach((ad, index) => { 196 const item = items[index]; 197 if (!item) return; 198 199 item.innerHTML = ""; 200 201 this.appendAd(item, ad); 202 }); 203 } 204 205 206 static renderChooseOddStream(data) { 207 const targets = document.querySelectorAll(".chose_odd_stream"); 208 const ad = this.findAdsByPrefix(data, "logoicon-")[0]; 209 210 if (!targets.length || !ad) return; 211 212 targets.forEach(target => { 213 target.innerHTML = ""; 214 215 const wrapper = document.createElement("div"); 216 wrapper.className = "gwd_choose_odd_stream_ad"; 217 218 this.appendAd(wrapper, ad); 219 220 wrapper.insertAdjacentHTML( 221 "beforeend", 222 ` 223 <span class="gwd_choose_odd_stream_icon"> 224 <svg xmlns="http://www.w3.org/2000/svg" width="8" height="6" viewBox="0 0 8 6" fill="none"> 225 <path d="M7.525 0.5L8 1.04688L4 5.5L0 1.04688L0.475 0.5L4 4.40625L7.525 0.5Z" fill="#4FA747"></path> 226 </svg> 227 </span> 228 ` 229 ); 230 231 target.appendChild(wrapper); 232 }); 233 } 234 235 static countDivInsSiteLive() { 236 const config = window.GWD_CONFIG_BANNER?.data?.data || {}; 237 238 const logoIcons = Object.entries(config) 239 .filter(([key]) => /^logoicon-\d+$/i.test(key)) 240 .sort((a, b) => { 241 const numA = parseInt(a[0].match(/\d+/)?.[0] || 0, 10); 242 const numB = parseInt(b[0].match(/\d+/)?.[0] || 0, 10); 243 return numA - numB; 244 }) 245 .map(([, value]) => value); 246 247 if (!logoIcons.length) { 248 return; 249 } 250 251 function render() { 252 document.querySelectorAll('.gwd-logoicon-ins').forEach((el, index) => { 253 // Không append nhiá»u lần 254 if (el.querySelector('ins[data-revive-zoneid]')) { 255 return; 256 } 257 258 const zoneId = logoIcons[index % logoIcons.length]; 259 260 el.insertAdjacentHTML( 261 'beforeend', 262 ` 263 <ins 264 data-revive-zoneid="${zoneId}" 265 data-revive-id="1718ffff6aff14155bf9e84ffb3a29ee"> 266 </ins> 267 ` 268 ); 269 }); 270 } 271
272 // Xá» lý các div Äã có sẵn 273 render(); 274 275 // Lắng nghe các div ÄÆ°á»£c thêm sau nà y 276 const observer = new MutationObserver(() => { 277 render(); 278 }); 279 280 observer.observe(document.body, { 281 childList: true, 282 subtree: true 283 }); 284 } 285 286 static appendCloseAdsButton() { 287 const videoTarget = document.querySelector(".gwd-banner-bottom__infeed"); 288 289 if (!videoTarget) { 290 return; 291 } 292 293 // Tránh render nhiá»u lần 294 if (videoTarget.querySelector(".gwd-close-banner-btn")) { 295 return; 296 } 297 298 const button = document.createElement("button"); 299 300 button.className = 301 "gwd-close-banner-btn inter absolute right-0 -top-3.5 z-20 flex min-h-2 cursor-pointer items-center gap-1.5 bg-[#F6FF00] px-1 text-[10px] text-black transition-colors md:-top-4 md:text-xs"; 302 303 button.type = "button"; 304 button.setAttribute("aria-label", "Tắt quảng cáo"); 305 306 button.innerHTML = ` 307 <span class="whitespace-nowrap font-light">Tắt quảng cáo</span> 308 <span class="inline-flex items-center font-light leading-none text-black" aria-hidden="true"> 309 <span>[</span> 310 <svg xmlns="http://www.w3.org/2000/svg" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 311 <line x1="18" y1="6" x2="6" y2="18"></line> 312 <line x1="6" y1="6" x2="18" y2="18"></line> 313 </svg> 314 <span>]</span> 315 </span> 316 `; 317 318 button.addEventListener("click", (event) => { 319 event.preventDefault(); 320 event.stopPropagation(); 321 hideBanner(); 322 }); 323 324 videoTarget.appendChild(button); 325 } 326 327 static init() { 328 const config = window.GWD_CONFIG_BANNER; 329 const options = window.GWD_CONFIG_OPTIONS || {}; 330 const data = config?.data?.data; 331 if (!data) return; 332 const isLiveZone = [ 333 "gwd-zone-live", 334 "gwd-zone-saoke" 335 ].some(className => document.body.classList.contains(className)); 336 337 this.renderBetButtons(data); 338 339 this.renderChooseOddStream(data); 340 341 this.renderOddListLogo(data); 342 343 // bottom banner 344 const videoTarget = document.querySelector(".gwd_banner_video_bottom"); 345 if (videoTarget && isLiveZone) this.renderVideoBottom(data, videoTarget); 346 347 this.appendCloseAdsButton(); 348 349 // top banner 350 const videoTopTarget = document.querySelector(".gwd_banner_top_marquee"); 351 352 if (videoTopTarget && isLiveZone) this.renderVideoTop(data, videoTopTarget); 353 354 const topOverlayTarget = document.querySelector(".gwd_banner_top_video_wrapper"); 355 if (topOverlayTarget && isLiveZone) this.renderTopOverlay(data, topOverlayTarget); 356 357 // gutter banner 358 const gutterReference = document.querySelector( 359 "#header, #masthead, .site-header, header" 360 ) || document.body.firstElementChild; 361 362 if (gutterReference && options.gutter) { 363 this.renderGutterAfterHeader(data, gutterReference); 364 } 365 } 366 } 367 368 document.addEventListener("DOMContentLoaded", () => GWD_Banner_Renderer.init()); 369 document.addEventListener("DOMContentLoaded", () => GWD_Banner_Renderer.countDivInsSiteLive()); 370 371 372 let bannerTimer = null; 373let bannerShowTimer = null; 374let overlayTimer = null; 375let isBannerDown = false; 376 377const HIDE_DURATION = 30000; 378const SHOW_DURATION = 10000; 379const OVERLAY_DELAY = 1000; 380const OVERLAY_DURATION = 10000; 381 382function getBannerEls() { 383 return {
384 videoTarget: document.querySelector(".gwd_banner_video_bottom"), 385 videoTargetDown: document.querySelector(".gwd_controls_banner"), 386 videoControl: document.querySelector(".gwd_controls"), 387 overlay: document.querySelector(".gwd-banner-bottom__overlay"), 388 overlayImg: document.querySelector(".gwd-banner-bottom__overlay img"), 389 }; 390} 391 392function clearBannerTimers() { 393 clearTimeout(bannerTimer); 394 clearTimeout(bannerShowTimer); 395 clearTimeout(overlayTimer); 396} 397 398function updateBannerPosition() { 399 const { videoTarget, videoTargetDown, videoControl } = getBannerEls(); 400 401 if (!videoTarget || !videoTargetDown || !videoControl || !isBannerDown) { 402 return; 403 } 404 405 const height = videoTarget.offsetHeight; 406 407 videoControl.style.paddingBottom = "12px"; 408 409 // Chá» di chuyá»n nguyên cụm theo trục Y 410 videoTargetDown.style.transition = "transform .3s ease"; 411 videoTargetDown.style.transform = `translate3d(0, ${height}px, 0)`; 412} 413 414function resetOverlayImg() { 415 const { overlayImg } = getBannerEls(); 416 417 if (!overlayImg) { 418 return; 419 } 420 421 // Chá» reset theo trục X 422 overlayImg.style.transition = "none"; 423 overlayImg.style.transform = "translate3d(-100%, 0, 0)"; 424} 425 426function startOverlayProgress() { 427 const { overlayImg } = getBannerEls(); 428 429 if (!overlayImg) { 430 return; 431 } 432 433 resetOverlayImg(); 434 435 overlayImg.offsetHeight; 436
437 // Chá» chạy ngang từ trái sang phải 438 overlayImg.style.transition = `transform ${OVERLAY_DURATION}ms linear`; 439 overlayImg.style.transform = "translate3d(0, 0, 0)"; 440} 441 442function hideBanner() { 443 const { videoTarget } = getBannerEls(); 444 445 if (!videoTarget) { 446 return; 447 } 448 449 clearBannerTimers(); 450 451 isBannerDown = true; 452 videoTarget.classList.add("gwd-down"); 453 454 // Bưá»c 1: nguyên cụm Äi từ trên xuá»ng 455 updateBannerPosition(); 456 457 // Bưá»c 2: sau 1s ảnh overlay má»i chạy từ trái sang phải 458 overlayTimer = setTimeout(() => { 459 startOverlayProgress(); 460 }, OVERLAY_DELAY); 461 462 // Sau 30s show lại 463 bannerTimer = setTimeout(() => { 464 showBannerBack(); 465 }, HIDE_DURATION); 466} 467 468function showBannerBack() { 469 const { videoTargetDown, videoControl, overlay, overlayImg } = getBannerEls(); 470 471 if (!videoTargetDown || !videoControl) { 472 return; 473 } 474 475 isBannerDown = false; 476 477 // Không remove gwd-down 478 videoTargetDown.style.transition = "transform .3s ease"; 479 videoTargetDown.style.transform = "translate3d(0, -10px, 0)"; 480 481 videoControl.style.paddingBottom = ""; 482 483 if (overlay) { 484 overlay.style.transition = ""; 485 overlay.style.transform = ""; 486 } 487 488 if (overlayImg) { 489 overlayImg.style.transition = "none"; 490 overlayImg.style.transform = "translate3d(0, 0, 0)"; 491 } 492 493 clearTimeout(bannerTimer); 494 bannerTimer = null; 495 496 // Sau khi show 10s thì ẩn lại 497 bannerShowTimer = setTimeout(() => { 498 hideBanner(); 499 }, SHOW_DURATION); 500} 501 502function recalcBannerPositionOnly() { 503 if (!isBannerDown) { 504 return; 505 } 506 507 requestAnimationFrame(() => { 508 requestAnimationFrame(updateBannerPosition); 509 }); 510} 511 512document.addEventListener("click", (e) => { 513 const target = e.target instanceof Element ? e.target : e.target.parentElement; 514 const balloonClose = target?.closest("[data-gwd-balloon-close]"); 515 516 if (balloonClose) { 517 e.preventDefault(); 518 e.stopPropagation(); 519 520 balloonClose.closest("[data-gwd-balloon]")?.classList.add("gwd-hide"); 521 return; 522 } 523 524 if (target?.closest(".gwd-close-banner-btn")) { 525 e.preventDefault(); 526 hideBanner(); 527 return; 528 } 529 530 if (target?.closest(".gwd_fullscreen-btn")) { 531 recalcBannerPositionOnly(); 532 } 533}); 534 535window.addEventListener("resize", recalcBannerPositionOnly); 536 537})();
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.