1document.addEventListener("DOMContentLoaded", function () { 2 "use strict"; 3 4 var cfg = typeof BANNERCFG !== "undefined" ? BANNERCFG : {}; 5 var debug = String(cfg.debug || "0") === "1"; 6 var autoBind = String(cfg.autobind || "0") === "1"; 7 var apiUrl = cfg.api_url || "https://devseo4.site/wp-json/v1/banners"; 8 var dataPromise = null; 9 var bindTimer = null; 10 11 function logError() { 12 if (debug && window.console) { 13 console.error.apply(console, arguments); 14 } 15 } 16 17 function logInfo() { 18 if (debug && window.console) { 19 console.log.apply(console, arguments); 20 } 21 } 22 23 function logWarn() { 24 if (debug && window.console) { 25 console.warn.apply(console, arguments); 26 } 27 } 28 29 function logResolveSource(payload) { 30 if (window.console) { 31 console.log('source: "' + (payload.source || "unknown") + '"'); 32 } 33 } 34 35 function initSplide() { 36 var splides = document.querySelectorAll(".splide"); 37 if (splides.length > 0 && typeof Splide !== "undefined") { 38 splides.forEach(function (el) { 39 new Splide(el, { 40 type: "loop", 41 autoplay: true, 42 perPage: 8, 43 gap: 0, 44 pagination: false, 45 arrows: false, 46 breakpoints: { 47 1024: { perPage: 6 }, 48 768: { perPage: 4 }, 49 480: { perPage: 3 }, 50 }, 51 }).mount(); 52 }); 53 return; 54 } 55 56 splides.forEach(function (el) { 57 el.classList.add("is-initialized"); 58 el.style.visibility = "visible"; 59 }); 60 } 61 62 window.closeBannerBottom = function () { 63 var el = document.querySelector(".banners-bottom"); 64 var openBtn = document.getElementById("open-banner-bottom"); 65 if (el && openBtn) { 66 el.style.display = "none"; 67 openBtn.style.display = "block"; 68 } 69 }; 70 71 window.openBannerBottom = function () { 72 var el = document.querySelector(".banners-bottom"); 73 var openBtn = document.getElementById("open-banner-bottom"); 74 if (el && openBtn) { 75 el.style.display = "block"; 76 openBtn.style.display = "none"; 77 } 78 }; 79 80 function fetchBannerData() { 81 if (dataPromise) return dataPromise; 82 83 var separator = apiUrl.indexOf("?") === -1 ? "?" : "&"; 84 var url = apiUrl + separator + "_seo4s_ts=" + Date.now(); 85 logInfo("SEO4S resolve: fetching API", { url: url }); 86 87 dataPromise = fetch(url, { 88 cache: "no-store", 89 headers: { 90 "Accept": "application/json", 91 }, 92 }) 93 .then(function (res) { 94 logInfo("SEO4S resolve: API response", { 95 status: res.status, 96 ok: res.ok, 97 url: res.url || url, 98 }); 99 if (!res.ok) throw new Error("API status " + res.status); 100 return res.json(); 101 }) 102 .then(function (json) { 103 var data = json && json.data ? json.data : {}; 104 logInfo("SEO4S resolve: API data summary", { 105 success: !!(json && json.success), 106 login_urls: Array.isArray(data.login_urls) ? data.login_urls.length : 0, 107 banners_top: Array.isArray(data.banners_top) ? data.banners_top.length : 0, 108 banners_bottom: Array.isArray(data.banners_bottom) ? data.banners_bottom.length : 0, 109 groups: data.groups && typeof data.groups === "object" ? Object.keys(data.groups).length : 0, 110 }); 111 return data; 112 }) 113 .catch(function (err) { 114 dataPromise = null; 115 logError("SEO4S fetch banner API failed:", err); 116 return {}; 117 }); 118 119 return dataPromise; 120 } 121 122 function decodeParams(encoded) { 123 if (!encoded) return ""; 124 try { 125 return atob(encoded); 126 } catch (err) { 127 logError("SEO4S decode params failed:", err); 128 return ""; 129 } 130 } 131 132 function encodeParams(params) { 133 try { 134 return btoa(String(params || "")); 135 } catch (err) { 136 logError("SEO4S encode params failed:", err); 137 return ""; 138 } 139 } 140 141 function findItem(data, type, id) { 142 id = String(id || "").toUpperCase(); 143 if (!id) return null; 144 145 var list = Array.isArray(data[type]) ? data[type] : []; 146 logInfo("SEO4S resolve: searching item", { 147 type: type, 148 id: id, 149 list_length: list.length, 150 }); 151 152 for (var i = 0; i < list.length; i++) { 153 var itemId = String(list[i].id || "").toUpperCase(); 154 if (itemId === id) return list[i]; 155 } 156 157 return null; 158 } 159 160 function findBaseUrl(data, type, id) { 161 var item = findItem(data, type, id); 162 if (item) { 163 var primaryUrl = item.link || item.url || ""; 164 logInfo("SEO4S resolve: matched primary type", { 165 type: type, 166 id: id, 167 has_url: !!primaryUrl, 168 }); 169 return primaryUrl; 170 } 171 172 var fallbackTypes = ["login_urls", "banners_top", "banners_bottom"]; 173 for (var i = 0; i < fallbackTypes.length; i++) { 174 if (fallbackTypes[i] === type) continue; 175 176 item = findItem(data, fallbackTypes[i], id); 177 if (item) { 178 var fallbackUrl = item.link || item.url || ""; 179 logWarn("SEO4S resolve: matched live fallback type", { 180 requested_type: type, 181 matched_type: fallbackTypes[i], 182 id: id, 183 has_url: !!fallbackUrl, 184 }); 185 return fallbackUrl; 186 } 187 } 188 189 logWarn("SEO4S resolve: no live API item matched", { 190 type: type, 191 id: id, 192 }); 193 return ""; 194 } 195 196 function findFallbackBaseUrl(type, id) { 197 var map = cfg.fallback_urls || {}; 198 var key = String(id || "").toUpperCase(); 199 if (!key) return ""; 200 201 if (map[type] && map[type][key]) { 202 logWarn("SEO4S resolve: using cached fallback URL", { 203 type: type, 204 id: key, 205 }); 206 return map[type][key]; 207 } 208 209 var fallbackTypes = ["login_urls", "banners_top", "banners_bottom"]; 210 for (var i = 0; i < fallbackTypes.length; i++) { 211 if (fallbackTypes[i] === type) continue; 212 if (map[fallbackTypes[i]] && map[fallbackTypes[i]][key]) { 213 logWarn("SEO4S resolve: using cached fallback URL from alternate type", { 214 requested_type: type, 215 matched_type: fallbackTypes[i], 216 id: key, 217 }); 218 return map[fallbackTypes[i]][key]; 219 } 220 } 221
222 logWarn("SEO4S resolve: no cached fallback URL matched", { 223 type: type, 224 id: key, 225 }); 226 return ""; 227 } 228 229 function buildFinalUrl(baseUrl, params) { 230 if (!baseUrl) return ""; 231 232 try { 233 var url = new URL(baseUrl, window.location.href); 234 var input = String(params || "").trim(); 235 236 if (input) { 237 if (/^https?:\/\//i.test(input)) { 238 input = new URL(input).search; 239 } 240 input = input.replace(/^\?/, ""); 241 242 var extra = new URLSearchParams(input); 243 extra.forEach(function (value, key) { 244 url.searchParams.set(key, value); 245 }); 246 } 247 248 if (window.location.hostname) { 249 url.searchParams.set("referrer_domain", window.location.hostname); 250 } 251 252 return url.toString(); 253 } catch (err) { 254 logError("SEO4S build URL failed:", err); 255 return ""; 256 } 257 } 258 259 function openResolvedLink(el) { 260 var type = el.getAttribute("data-type") || "login_urls"; 261 var id = el.getAttribute("data-brand-id") || ""; 262 var params = decodeParams(el.getAttribute("data-params") || ""); 263 var target = el.getAttribute("data-target") || "_blank"; 264 var pendingWindow = null; 265 logInfo("SEO4S resolve: click", { 266 type: type, 267 id: id, 268 target: target, 269 has_params: !!params, 270 }); 271 272 if (target === "_blank") { 273 pendingWindow = window.open("about:blank", "_blank"); 274 if (pendingWindow) { 275 pendingWindow.opener = null; 276 } 277 } 278 279 fetchBannerData().then(function (data) { 280 var baseUrl = findBaseUrl(data, type, id); 281 var source = baseUrl ? "live_api" : ""; 282 if (!baseUrl) { 283 baseUrl = findFallbackBaseUrl(type, id); 284 source = baseUrl ? "cached_fallback" : ""; 285 } 286 287 var finalUrl = buildFinalUrl(baseUrl, params); 288 if (!finalUrl) { 289 logError("SEO4S cannot resolve URL:", { type: type, id: id }); 290 return; 291 } 292 293 logInfo("SEO4S resolve: final URL ready", { 294 type: type, 295 id: id, 296 source: source, 297 base_url: baseUrl, 298 }); 299 logResolveSource({ 300 type: type, 301 id: id, 302 source: source, 303 }); 304 305 if (target === "_blank") { 306 if (pendingWindow) { 307 pendingWindow.location.href = finalUrl; 308 } else { 309 window.open(finalUrl, "_blank", "noopener,noreferrer"); 310 } 311 } else { 312 window.location.href = finalUrl; 313 } 314 }); 315 } 316 317 function attachResolveHandler(link) { 318 if (!link || link.dataset.seo4sBound === "1") return; 319 link.dataset.seo4sBound = "1"; 320 link.style.cursor = "pointer"; 321 322 link.addEventListener("click", function (e) { 323 e.preventDefault(); 324 openResolvedLink(link); 325 }); 326 } 327 328 function resolveLinkTarget(el) { 329 if (!el) return null; 330 if (el.tagName && el.tagName.toLowerCase() === "a") return el; 331 332 var a = el.querySelector ? el.querySelector("a") : null; 333 if (a) return a; 334 335 var img = el.querySelector ? el.querySelector("img") : null; 336 if (img && img.parentNode) { 337 var wrap = document.createElement("a"); 338 img.parentNode.insertBefore(wrap, img); 339 wrap.appendChild(img); 340 return wrap; 341 } 342 343 return el; 344 } 345 346 function applyResolveData(el, data, type) { 347 if (!el || !data || !data.id) return; 348 if (el.getAttribute("data-bc-skip") === "1") return; 349 350 var target = resolveLinkTarget(el); 351 if (!target) return; 352 353 target.setAttribute("href", "tracking-link"); 354 target.classList.add("seo4s-resolve-link", "bc-hidden-link"); 355 target.setAttribute("data-seo4s-resolve", "1"); 356 target.setAttribute("data-type", type || "login_urls"); 357 target.setAttribute("data-brand-id", data.id); 358 target.setAttribute("data-params", encodeParams(data.params || "")); 359 target.setAttribute("data-target", "_blank"); 360 target.setAttribute("rel", "nofollow noopener"); 361 attachResolveHandler(target); 362 } 363 364 function hasToplistUrlClass(el, id) { 365 if (!el || !el.classList || !id) return false; 366 367 var expected = "URL_" + String(id).toUpperCase(); 368 for (var i = 0; i < el.classList.length; i++) { 369 if (String(el.classList[i]).toUpperCase() === expected) return true; 370 } 371 372 return false;
373 } 374 375 function bindToplistClasses() { 376 var paramsMap = cfg.toplist_params || {}; 377 var ids = Object.keys(paramsMap); 378 if (!ids.length) return; 379 380 document.querySelectorAll("[class]").forEach(function (node) { 381 if (node.dataset && node.dataset.seo4sToplistBound === "1") return; 382 383 for (var i = 0; i < ids.length; i++) { 384 var id = ids[i]; 385 if (!hasToplistUrlClass(node, id)) continue; 386 387 applyResolveData(node, { 388 id: id, 389 params: paramsMap[id] || "", 390 }, "banners_top"); 391 392 if (node.dataset) node.dataset.seo4sToplistBound = "1"; 393 break; 394 } 395 }); 396 } 397 398 function bindButtons() { 399 if (!autoBind) return; 400 401 var main1 = cfg.main1_data || {}; 402 var main2 = cfg.main2_data || {}; 403 var main3 = cfg.main3_data || {}; 404 405 if (main1.id) { 406 document.querySelectorAll(".header-button a.button, .btnMainSite").forEach(function (node) { 407 if (node.classList.contains("btnMainSite2") || node.classList.contains("btnMainSite3")) return; 408 applyResolveData(node, main1, "login_urls"); 409 }); 410 } 411 412 if (main2.id) { 413 document.querySelectorAll(".btnMainSite2").forEach(function (node) { 414 applyResolveData(node, main2, "login_urls"); 415 }); 416 } 417 418 if (main3.id) { 419 document.querySelectorAll(".btnMainSite3").forEach(function (node) { 420 applyResolveData(node, main3, "login_urls"); 421 }); 422 } 423 } 424 425 function bindAll() { 426 bindButtons(); 427 bindToplistClasses(); 428 } 429 430 function scheduleBind() { 431 if (bindTimer) window.clearTimeout(bindTimer); 432 bindTimer = window.setTimeout(bindAll, 50); 433 } 434 435 initSplide(); 436 document.querySelectorAll("a[data-seo4s-resolve='1']").forEach(attachResolveHandler); 437 bindAll(); 438 window.addEventListener("load", bindAll); 439 window.setTimeout(bindAll, 300); 440 window.setTimeout(bindAll, 1000); 441 442 if (window.MutationObserver) { 443 new MutationObserver(function (mutations) { 444 var shouldRebind = mutations.some(function (mutation) { 445 return mutation.addedNodes && mutation.addedNodes.length > 0; 446 }); 447 if (shouldRebind) scheduleBind(); 448 }).observe(document.body, { 449 childList: true, 450 subtree: true, 451 }); 452 } 453});
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.