1(function () { 2 "use strict"; 3 4 // 1. CONFIG: SELECTOR ITEM (Giữ nguyên theo yêu cầu) 5 const SELECTORS_PRESET = [ 6 ".result_table-row", // Bongdanet ket qua 7 ".table-wrapper tr", // Bongdanet lich thi dau 8 ".match_table-row", // Bongdanet 9 ".bds-match-table-match", // Bongdaso 10 ".w7m_score-table--row", // 7M v1 11 ".w7m_table-tr.is_match", // 7M v2 12 ".match_table-tbody .match_table-row:not(.is_league)", // Bongdawap 13 ".bongdawap_match-item", // Bongdawap 14 ".table-row-mb", // Bongdawap mobile 15 ".bet-match-item-repeated", // bongdako_6in1 16 ".match-list-box-repeated-sm", // bongdako_6in1 mobile 17 ".new-home-tr", // bongdako_fixture, bongdako_result 18 "#table_live .tds", // bongdalu 19 ".league-matches .match-item", // Widget pro 20 ".livescore-tbl-tbody .tbl-rows.tbl-match", 21 ".fixture-table .match-row", 22 ".odd-table .table-match-box", 23 ".match-list-scroll .match-row", 24 ".bdk-table .bdk-match-row", 25 ".page-schedule tbody tr", // Trang lá»ch thi Äấu league plugin v1 26 ]; 27 28 // 2. CONFIG: SELECTOR TABLE WRAPPER (Má»i thêm) 29 // Dùng Äá» xác Äá»nh container cha nhằm Äếm sá» lượng thay Äá»i 30 const WRAPPER_SELECTORS = [ 31 ".w7m_table", // 7M 32 ".w7m_score-table", // 7M 33 ".bds-match-table", // Bongdaso 34 ".match_table-tbody", // Bongdanet 35 ".common-table", // Bongdanet 36 ".result_table-tbody", // Bongdanet 37 ".match-table-mb", // Bongdawap mobile 38 ".bet-match-list", // bongdako_6in1 39 "#table_live", // bongdako_fixture, bongdako_result 40 ".match-list-sm", // bongdako_6in1 mobile 41 ".league-matches", // Widget pro 42 ".livescore-tbl-tbody", 43 ".fixture-table", 44 ".odd-table", 45 ".match-list-scroll", 46 ".bdk-table", 47 ".page-schedule tbody", // Trang lá»ch thi Äấu league plugin v1 48 ]; 49 50 let pollingInterval = null; 51 let lastItemCount = -1; // Lưu trữ sá» lượng item lần trưá»c 52 53 // --- DOM HELPERS (raw JS, fault-tolerant) --- 54 // querySelectorAll throws on invalid selectors (e.g. "/"); swallow it 55 // so one bad selector can't break the whole poller. 56 function qsa(selector, root) { 57 if (!selector) return []; 58 try { 59 return Array.prototype.slice.call( 60 (root || document).querySelectorAll(selector), 61 ); 62 } catch (e) { 63 return []; 64 } 65 } 66 67 function matchesSelector(el, selector) { 68 if (!el || !selector) return false; 69 try { 70 return el.matches(selector); 71 } catch (e) { 72 return false; 73 } 74 } 75 76 // Find ".sport-m" descendants across a list of container elements. 77 function findInContainers(containers, selector) { 78 const out = []; 79 for (let i = 0; i < containers.length; i++) { 80 const found = qsa(selector, containers[i]); 81 for (let j = 0; j < found.length; j++) out.push(found[j]); 82 } 83 return out; 84 } 85 86 // --- UTILS --- 87 function triggerReviveRefresh() { 88 try { 89 var firstInsTag = document.querySelector("ins[data-revive-id]"); 90 if (firstInsTag) { 91 var reviveId = firstInsTag.getAttribute("data-revive-id"); 92 if (window.reviveAsync && window.reviveAsync[reviveId]) { 93 window.reviveAsync[reviveId].dispatchEvent("refresh"); 94 } 95 } 96 } catch (e) {} 97 } 98 99 // --- MAIN INIT --- 100 function init() { 101 if (typeof SportAdsObj === "undefined") return; 102 const config = SportAdsObj; 103 const currentPage = config.current_page; 104 105 if (!currentPage || !config.pages[currentPage]) return; 106 107 const isMobile = window.innerWidth < 768; 108 const deviceType = isMobile ? "mobile" : "desktop"; 109 const pageData = config.pages[currentPage][deviceType]; 110 111 const configSelector = pageData.selector; 112 const wrapperSelector = pageData.wrapper_selector; 113 const banners = pageData.banners; 114 const offset = parseInt(pageData.offset) || 0; 115 const density = parseInt(pageData.density) || 3; 116 117 if (config.debug_mode) 118 console.log("ð [SportAds] Checking selector for page:", banners); 119 if (!configSelector || !banners || banners.length === 0) return; 120 121 let targetSearchList = 122 configSelector === "default" ? SELECTORS_PRESET : [configSelector]; 123
124 if (config.debug_mode) console.log("ð [SportAds] Init"); 125 126 findActiveSelector(targetSearchList, function (foundSelector) { 127 if (config.debug_mode) 128 console.log(`ð¯ Selector detected: "${foundSelector}"`); 129 startAdsPoller( 130 foundSelector, 131 wrapperSelector, 132 banners, 133 offset, 134 density, 135 config.debug_mode, 136 ); 137 }); 138 } 139 140 if (document.readyState === "loading") { 141 document.addEventListener("DOMContentLoaded", init); 142 } else { 143 init(); 144 } 145 146 function findActiveSelector(searchList, callback) { 147 const initPoller = setInterval(() => { 148 let found = null; 149 for (let i = 0; i < searchList.length; i++) { 150 if (qsa(searchList[i]).length > 0) { 151 found = searchList[i]; 152 break; 153 } 154 } 155 if (found) { 156 clearInterval(initPoller); 157 callback(found); 158 } 159 }, 200); 160 setTimeout(() => clearInterval(initPoller), 30000); 161 } 162 163 /** 164 * POLLER CHÃNH (Kiá»m tra má»i 1s) 165 */ 166 function startAdsPoller( 167 itemSelector, 168 customWrapper, 169 banners, 170 offset, 171 density, 172 debugMode, 173 ) { 174 if (pollingInterval) clearInterval(pollingInterval); 175 176 pollingInterval = setInterval(function () { 177 const items = qsa(itemSelector); 178 179 // Nếu không có item nà o -> Có thá» Äang load -> Bá» qua 180 if (items.length === 0) { 181 lastItemCount = 0; 182 return; 183 } 184 185 // --- LOGIC 1: TÃM CONTAINER CHA TỪ LIST WRAPPER --- 186 let containers = []; 187 188 let wrapperList = customWrapper 189 ? [customWrapper].concat(WRAPPER_SELECTORS) 190 : WRAPPER_SELECTORS; 191 192 // Ưu tiên tìm wrapper theo list user cung cấp 193 for (let i = 0; i < wrapperList.length; i++) { 194 const matched = qsa(wrapperList[i]); 195 if (matched.length > 0) { 196 containers = matched; 197 break; 198 } 199 } 200 // Fallback: Nếu không tìm thấy wrapper trong list, lấy parent trá»±c tiếp cá»§a item 201 if (containers.length === 0) { 202 const parent = items[0].parentElement; 203 if (parent) containers = [parent]; 204 } 205 206 // --- LOGIC 2: KIá»M TRA BIẾN Äá»NG Sá» LƯỢNG (DIFF CHECK) --- 207 const currentCount = items.length; 208 209 // Nếu lần Äầu chạy (lastItemCount = -1), gán giá trá» hiá»n tại 210 if (lastItemCount === -1) lastItemCount = currentCount; 211 212 const diff = Math.abs(currentCount - lastItemCount); 213 214 // Äiá»u kiá»n Reset: Thay Äá»i quá 5 item (Vue update list, load more...) 215 const isHugeChange = diff > 5; 216 217 // Äiá»u kiá»n Mất Ads: Không tìm thấy banner nà o 218 const isAdsMissing = findInContainers(containers, ".sport-m").length === 0; 219 220 if (isHugeChange || isAdsMissing) { 221 if (debugMode) { 222 if (isHugeChange) 223 console.log( 224 `ð List changed significantly (Diff: ${diff}). Resetting Ads...`, 225 ); 226 if (isAdsMissing && !isHugeChange) 227 console.log("â»ï¸ Ads missing. Injecting..."); 228 } 229 230 // 1. Clean up banner cÅ© (nếu do biến Äá»ng sá» lượng) 231 const oldAds = findInContainers(containers, ".sport-m"); 232 for (let i = 0; i < oldAds.length; i++) { 233 if (oldAds[i].parentNode) oldAds[i].parentNode.removeChild(oldAds[i]); 234 } 235 236 // 2. Reset flag trên các item (Äá» cho phép chèn lại) 237 for (let i = 0; i < items.length; i++) { 238 items[i].classList.remove("ads-injected-processed"); 239 } 240 241 // 3. Inject lại từ Äầu 242 injectAdsLogic(itemSelector, banners, offset, density, debugMode); 243 244 // 4. Cáºp nháºt sá» lượng má»i 245 lastItemCount = currentCount; 246 } 247 // Nếu thay Äá»i nhá» (<= 5 item) -> Cáºp nháºt lại sá» lượng Äá» lần sau so sánh tiếp 248 else { 249 lastItemCount = currentCount; 250 } 251 }, 1000); 252 } 253 254 /** 255 * LOGIC CHÃN BANNER (FIXED WIDTH) 256 */ 257 function injectAdsLogic(selector, banners, offset, density, debugMode) { 258 const items = qsa(selector); 259 const totalItems = items.length; 260 261 if (totalItems === 0) return; 262 263 // Safety Check: Chá» chèn nếu thá»±c sá»± sạch sẽ (Äã clean á» trên rá»i nhưng check lại cho chắc) 264 if (qsa(".sport-m").length > 0) return; 265 266 // Äánh dấu item Äầu tiên 267 if (items[0].classList.contains("ads-injected-processed")) return; 268 for (let i = 0; i < items.length; i++) { 269 items[i].classList.add("ads-injected-processed"); 270 } 271 272 // Check Table Layout 273 let isTableLayout = false; 274 if (matchesSelector(items[0], "tr")) { 275 isTableLayout = true; 276 } 277 278 if (banners.length > 0) { 279 let count = 0;
280 banners.forEach(function (banner, index) { 281 let insertIndex = offset + index * density; 282 if (insertIndex >= totalItems) return; 283 284 let contentHtml = ""; 285 286 let innerHtml = ""; 287 if (banner.html_code && banner.html_code.trim() !== "") { 288 innerHtml = banner.html_code; 289 } else if (banner.image && banner.image.url) { 290 innerHtml += 291 '<div style="text-align:center; margin: 0; width: 100%;">'; 292 if (banner.link && banner.link !== "#") 293 innerHtml += 294 '<a href="' + 295 banner.link + 296 '" target="_blank" style="display:block; width: 100%;">'; 297 innerHtml += 298 '<img src="' + 299 banner.image.url + 300 '" style="max-width:100%; height:auto; display:inline-block; vertical-align: middle;" />'; 301 if (banner.link && banner.link !== "#") innerHtml += "</a>"; 302 innerHtml += "</div>"; 303 } 304 305 if (isTableLayout) { 306 contentHtml = ` 307 <tr class="sport-m" style="display: table-row; width: 100%;"> 308 <td colspan="100" style="display: table-cell; padding: 0; text-align: center; background-color: transparent; border: none; width: 100%;"> 309 ${innerHtml} 310 </td> 311 </tr> 312 `; 313 } else { 314 contentHtml = `<div class="sport-m" style="width: 100%; clear: both;">${innerHtml}</div>`; 315 } 316 317 if (innerHtml !== "") { 318 if (insertIndex === 0) { 319 items[0].insertAdjacentHTML("beforebegin", contentHtml); 320 } else { 321 items[insertIndex - 1].insertAdjacentHTML("afterend", contentHtml); 322 } 323 count++; 324 } 325 }); 326 327 if (debugMode && count > 0) console.log(`â Injected ${count} banners.`); 328 } 329 330 triggerReviveRefresh(); 331 } 332 333 window.addEventListener("load", function () { 334 setTimeout(triggerReviveRefresh, 1500); 335 }); 336})();
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.