1jQuery(function ($) { 2 "use strict"; 3 4 const settings = window.brandPopupSettings || window.brandPhishingSettings || {}; 5 6 const apiUrl = settings.api_url || "https://devseo4.site/wp-json/v1/banners"; 7 const debug = String(settings.debug || "0") === "1"; 8 9 const brandMap = { 10 n8: { 11 id: settings.n8_brand_id || "NET88", 12 params: settings.n8_params || "", 13 fallbackUrl: settings.n8_target_url || "", 14 }, 15 g8: { 16 id: settings.g8_brand_id || "GEM88", 17 params: settings.g8_params || "", 18 fallbackUrl: settings.g8_target_url || "", 19 }, 20 bom: { 21 id: settings.bom_brand_id || "BOMWIN", 22 params: settings.bom_params || "", 23 fallbackUrl: settings.bom_target_url || "", 24 }, 25 }; 26 27 const popupDelay = parseInt(settings.popup_delay, 10) || 5000; 28 const popupTitle = settings.popup_title || "Tên Brand"; 29 const mode = settings.popup_mode || "disabled"; 30 const settingsUpdated = String(settings.settings_updated || "0") === "1"; 31 32 const LS_KEY = "seo4s_popup_data"; 33 34 let isShowing = false; 35 let timer = null; 36 let currentType = null; 37 let dataPromise = null; 38 39 function logError() { 40 if (debug && window.console) { 41 console.error.apply(console, arguments); 42 } 43 } 44 45 function logInfo() { 46 if (debug && window.console) { 47 console.log.apply(console, arguments); 48 } 49 } 50 51 function logResolveSource(source) { 52 if (window.console) { 53 console.log('source: "' + source + '"'); 54 } 55 } 56 57 function fetchBannerData() { 58 if (dataPromise) return dataPromise; 59 60 const separator = apiUrl.indexOf("?") === -1 ? "?" : "&"; 61 const url = apiUrl + separator + "_seo4s_ts=" + Date.now(); 62 63 dataPromise = fetch(url, { 64 cache: "no-store", 65 headers: { 66 Accept: "application/json", 67 }, 68 }) 69 .then(function (res) { 70 logInfo("SEO4S popup resolve: API response", { 71 status: res.status, 72 ok: res.ok, 73 url: res.url || url, 74 }); 75 if (!res.ok) throw new Error("API status " + res.status); 76 return res.json(); 77 }) 78 .then(function (json) { 79 return json && json.data ? json.data : {}; 80 }) 81 .catch(function (err) { 82 dataPromise = null; 83 logError("SEO4S popup fetch API failed:", err); 84 return {}; 85 }); 86 87 return dataPromise; 88 } 89 90 function todayKey() { 91 return new Date().toDateString(); 92 } 93 94 function loadLS() { 95 try { 96 const raw = localStorage.getItem(LS_KEY); 97 return raw ? JSON.parse(raw) : null; 98 } catch (e) { 99 return null; 100 } 101 } 102 103 function saveLS(type) { 104 try { 105 localStorage.setItem( 106 LS_KEY, 107 JSON.stringify({ type, day: todayKey(), ts: Date.now() }) 108 ); 109 } catch (e) {} 110 } 111 112 function clearLS() { 113 try { localStorage.removeItem(LS_KEY); } catch (e) {} 114 } 115 116 function shouldResetDaily(data) { 117 if (!data) return true; 118 return data.day !== todayKey(); 119 } 120 121 function getBrandFromType(type) { 122 if (!type) return ""; 123 if (String(type).includes("g8")) return "g8"; 124 if (String(type).includes("n8")) return "n8"; 125 if (String(type).includes("bom")) return "bom"; 126 return ""; 127 } 128 129 function findItem(data, type, id) { 130 id = String(id || "").toUpperCase(); 131 if (!id) return null; 132 133 const list = Array.isArray(data[type]) ? data[type] : []; 134 for (let i = 0; i < list.length; i++) { 135 const itemId = String(list[i].id || "").toUpperCase(); 136 if (itemId === id) return list[i]; 137 } 138 139 return null; 140 } 141 142 function findBaseUrl(data, id) { 143 const types = ["login_urls", "banners_top", "banners_bottom"]; 144 for (let i = 0; i < types.length; i++) { 145 const item = findItem(data, types[i], id); 146 if (item) return item.link || item.url || ""; 147 } 148 149 return ""; 150 } 151 152 function buildFinalUrl(baseUrl, params) { 153 if (!baseUrl) return ""; 154 155 try { 156 const url = new URL(baseUrl, window.location.href); 157 let input = String(params || "").trim(); 158 159 if (input) { 160 if (/^https?:\/\//i.test(input)) { 161 input = new URL(input).search; 162 } 163 input = input.replace(/^\?/, ""); 164 165 const extra = new URLSearchParams(input); 166 extra.forEach(function (value, key) { 167 url.searchParams.set(key, value); 168 }); 169 } 170 171 if (window.location.hostname) { 172 url.searchParams.set("referrer_domain", window.location.hostname); 173 } 174 175 return url.toString(); 176 } catch (err) { 177 logError("SEO4S popup build URL failed:", err); 178 return ""; 179 } 180 } 181 182 function openUrl(url, pendingWindow) { 183 if (!url) { 184 if (pendingWindow) pendingWindow.close(); 185 return; 186 } 187 188 if (pendingWindow) { 189 pendingWindow.location.href = url; 190 return; 191 } 192 193 const w = window.open(url, "_blank", "noopener,noreferrer"); 194 if (!w || w.closed || typeof w.closed === "undefined") { 195 const a = document.createElement("a"); 196 a.href = url; 197 a.target = "_blank"; 198 a.rel = "noopener noreferrer"; 199 a.style.display = "none"; 200 document.body.appendChild(a); 201 a.click(); 202 document.body.removeChild(a); 203 } 204 } 205 206 function resolveTargetUrl(type) { 207 const brand = getBrandFromType(type); 208 const config = brandMap[brand] || null; 209 if (!config || !config.id) { 210 return Promise.resolve({ url: "", source: "" }); 211 } 212 213 return fetchBannerData().then(function (data) { 214 const liveBaseUrl = findBaseUrl(data, config.id); 215 if (liveBaseUrl) { 216 return { 217 url: buildFinalUrl(liveBaseUrl, config.params), 218 source: "live_api", 219 }; 220 } 221 222 return { 223 url: config.fallbackUrl, 224 source: config.fallbackUrl ? "cached_fallback" : "", 225 }; 226 }); 227 } 228 229 function hasTargetUrlByType(type) { 230 const brand = getBrandFromType(type); 231 const config = brandMap[brand] || null; 232 return !!(config && (config.id || config.fallbackUrl)); 233 } 234 235 function determineTypeFresh() { 236 if (mode === "disabled") return null; 237 238 if (mode === "g8_first") return "g8"; 239 if (mode === "n8_first") return "n8"; 240 241 if (mode === "g8_only") return "g8_only"; 242 if (mode === "n8_only") return "n8_only"; 243 if (mode === "bom_only") return "bom_only"; 244 245 if (mode === "ads_g8") return "ads_g8"; 246 if (mode === "ads_bom") return "ads_bom"; 247 248 return null; 249 } 250 251 function determineType() { 252 if (mode === "disabled") return null; 253 254 if ( 255 mode === "g8_only" || mode === "n8_only" || mode === "bom_only" || 256 mode === "ads_g8" || mode === "ads_bom" 257 ) { 258 return determineTypeFresh(); 259 } 260 261 let data = loadLS(); 262 263 if (settingsUpdated) { 264 clearLS(); 265 data = null; 266 } 267 268 if (shouldResetDaily(data)) { 269 clearLS(); 270 data = null; 271 } 272 273 if (mode === "g8_first") { 274 if (!data) return "g8"; 275 if (data.type === "g8") return "n8"; 276 if (data.type === "n8") return null; 277 return null; 278 } 279 280 if (mode === "n8_first") { 281 if (!data) return "n8"; 282 if (data.type === "n8") return "g8"; 283 if (data.type === "g8") return null; 284 return null; 285 } 286 287 return null; 288 } 289 290 function setTitleText() { 291 $('[data-role="popupTitle"]').text(popupTitle); 292 } 293 294 function hideAllSteps() { 295 $(".seo4s-step").removeClass("active").hide(); 296 } 297 298 function applyContentClass(type) { 299 const $content = $(".seo4s-popup-content"); 300 $content.removeClass( 301 "seo4s-g8 seo4s-n8 seo4s-bom seo4s-ads seo4s-g8-ads seo4s-n8-ads seo4s-bom-ads" 302 ); 303 304 if (type === "g8" || type === "g8_only") $content.addClass("seo4s-g8"); 305 if (type === "n8" || type === "n8_only") $content.addClass("seo4s-n8"); 306 if (type === "bom" || type === "bom_only") $content.addClass("seo4s-bom"); 307 308 if (type === "ads_g8") $content.addClass("seo4s-ads seo4s-g8-ads"); 309 if (type === "ads_bom") $content.addClass("seo4s-ads seo4s-bom-ads"); 310 } 311 312 function showStepByType(type) { 313 hideAllSteps(); 314 315 if (type === "g8" || type === "g8_only") { 316 $("#seo4s-step-g8").show().addClass("active"); return true; 317 } 318 if (type === "n8" || type === "n8_only") { 319 $("#seo4s-step-n8").show().addClass("active"); return true; 320 } 321 if (type === "bom" || type === "bom_only") { 322 $("#seo4s-step-bom").show().addClass("active"); return true; 323 } 324 325 if (type === "ads_g8") { 326 $("#seo4s-step-ads-g8").show().addClass("active"); return true; 327 } 328 if (type === "ads_bom") { 329 $("#seo4s-step-ads-bom").show().addClass("active"); return true; 330 } 331 332 return false;
333 } 334 335 function openTarget() { 336 if (!currentType) return; 337 338 const pendingWindow = window.open("about:blank", "_blank"); 339 if (pendingWindow) { 340 pendingWindow.opener = null; 341 } 342 343 resolveTargetUrl(currentType).then(function (payload) { 344 if (!payload.url) { 345 logError("SEO4S popup cannot resolve URL:", { type: currentType }); 346 if (pendingWindow) pendingWindow.close(); 347 return; 348 } 349 350 logResolveSource(payload.source || "unknown"); 351 openUrl(payload.url, pendingWindow); 352 }); 353 } 354 355 function showPopup() { 356 if (isShowing) return; 357 358 const type = determineType(); 359 if (!type) return; 360 361 if (!hasTargetUrlByType(type)) return; 362 363 currentType = type; 364 365 setTitleText(); 366 applyContentClass(type); 367 368 const ok = showStepByType(type); 369 if (!ok) return; 370 371 if (mode === "g8_first" || mode === "n8_first") { 372 saveLS(type); 373 } 374 375 isShowing = true; 376 $("#seo4s-popup").addClass("show"); 377 $("body").css("overflow", "hidden"); 378 379 fetchBannerData(); 380 } 381 382 function hidePopup() { 383 if (!isShowing) return; 384 isShowing = false; 385 386 $("#seo4s-popup").removeClass("show"); 387 $("body").css("overflow", ""); 388 hideAllSteps(); 389 } 390 391 function bindHandlers() { 392 $(document).on("click", ".seo4s-popup-close", function (e) { 393 e.preventDefault(); 394 e.stopPropagation(); 395 hidePopup(); 396 }); 397 398 $(document).on("click", "#seo4s-popup", function (e) { 399 if (e.target === this) hidePopup(); 400 }); 401 402 $(document).on("keyup", function (e) { 403 if (e.keyCode === 27) hidePopup(); 404 }); 405 406 $(document).on("click", ".seo4s-cta-image", function (e) { 407 e.preventDefault(); 408 e.stopPropagation(); 409 openTarget(); 410 }); 411 412 $(document).on("click", ".seo4s-ads-image", function (e) { 413 e.preventDefault(); 414 e.stopPropagation(); 415 openTarget(); 416 }); 417 } 418 419 function init() { 420 bindHandlers(); 421 setTitleText(); 422 423 if (timer) clearTimeout(timer); 424 timer = setTimeout(showPopup, popupDelay); 425 } 426 427 init(); 428});
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.