1// Last Updated: 2026.04.16 2// Util functions part 3var groupBy = function (xs, key) { 4 return xs.reduce(function (rv, x) { 5 (rv[x[key].trim()] = rv[x[key].trim()] || []).push(x); 6 return rv; 7 }, {}); 8}; 9function extractTimestampFromString(inputString) { 10 var regex = /\/Date\((\d+)\)\//; 11 var match = regex.exec(inputString); 12 if (match && match[1]) return match[1]; 13 return null; 14} 15function extractValueFromQueryString(inputString) { 16 var regex = /v=([^&]+)/; 17 var match = regex.exec(inputString); 18 if (match && match[1]) return match[1]; 19 else return 0; 20} 21function getTimestampServerCache() { 22 var scriptElements = document.getElementsByTagName('script'); 23 for (var i = 0; i < scriptElements.length; i++) { 24 var scriptSrc = scriptElements[i].getAttribute('src'); 25 if (scriptSrc && scriptSrc.indexOf('header.menu.vue') > -1) 26 return extractValueFromQueryString(scriptSrc); 27 } 28 return 0; 29} 30function getQueryParam(paramName) { 31 var url_string; 32 if (isHeaderPage()) { 33 try { 34 url_string = window.parent.location.href; 35 } catch { 36 url_string = window.location.href; 37 } 38 } else { 39 url_string = window.location.href; 40 } 41 var url = new URL(url_string); 42 return url.searchParams.get(paramName); 43} 44let addPrefix = (variableName) => 45 isNaN(variableName.substring(0, 1)) ? '' : '_'; 46 47function changeHeaderHeight(height) { 48 try { 49 window.parent.document.getElementsByClassName('divHeader')[0].style = 'height:' + height + 'px'; 50 } 51 catch (err) { 52 console.log(err) 53 } 54} 55var isMobileDevice = function () { 56 var check = false; 57 (function (a) { if (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0, 4))) check = true; })(navigator.userAgent || navigator.vendor || window.opera); 58 return check; 59} 60let ligaSbSportSubmenuNameTW = ''; 61function isLigaSbName(gameName, ligaSbSportSubmenuName) { 62 return gameName === ligaSbSportSubmenuName 63} 64 65const latinWords = ['sportsbooks', 'sportsbook', 'sportbooks', 'sportbook', 'sports', 'sport', 'bola']; 66const unicodeWords = [ 67 '\u4F53\u80B2\u7C7B', 68 '\u0E01\u0E35\u0E2C\u0E32', 69 'Th\u1EC3 thao', 70 '\uC2A4\uD3EC\uCE20' 71]; 72 73const latinWordPattern = latinWords.join('|'); 74const unicodeWordPattern = unicodeWords.join('|'); 75 76const latinRegex = new RegExp(`\\b(${latinWordPattern})\\b`, 'iu'); 77const unicodeRegex = new RegExp(`(${unicodeWordPattern})`, 'iu'); 78const latinReplaceRegex = new RegExp(`\\b(${latinWordPattern}
78)(?:\\s*[123])?\\b`, 'giu'); 79const unicodeReplaceRegex = new RegExp(`(${unicodeWordPattern})(?:\\s*[123])?`, 'giu'); 80 81function replaceSPORTLabelHtml(html, sportBookType) { 82 const sportNumber = `${sportBookType || ''}`.trim(); 83 const latinLabel = sportNumber ? `SPORT ${sportNumber}` : 'SPORT'; 84 85 return (html || '') 86 .replace(latinReplaceRegex, latinLabel) 87 .replace(unicodeReplaceRegex, function (_, unicodeLabel) { 88 return sportNumber ? `${unicodeLabel} ${sportNumber}` : unicodeLabel; 89 }) 90 .replace(/\s*\u25BC\s*/g, ''); 91} 92 93function getSPORTByText() { 94 var sportItems = $(mountEl).find('a').filter(function () { 95 var text = $(this).text(); 96 return latinRegex.test(text) || unicodeRegex.test(text); 97 }).map(function () { 98 var parentLi = $(this).closest('li'); 99 return parentLi.length ? parentLi : $(this); 100 }).get(); 101 102 if (sportItems.length > 0) { 103 sportItems.forEach(function (item) { 104 console.log($(item)[0].outerHTML); 105 }); 106 } else { 107 console.log("No <a> tags found with relevant SPORT text."); 108 } 109 110 return sportItems; 111} 112 113function getSPORTByHref() { 114 return $(mountEl).find('a').filter(function () { 115 return $(this).attr('href') && $(this).attr('href').toLowerCase().includes('_view/rmodds1.aspx'); 116 }).map(function () { 117 var parentLi = $(this).closest('li'); 118 return parentLi.length ? parentLi : $(this); 119 }).get(); 120} 121 122function getNonSPORTLinks() { 123 const excludeFiles = ['body.aspx', 'lobbygames.aspx']; 124 125 return $(document).find('a').filter(function () { 126 const href = $(this).attr('href'); 127 if (!href) return false; 128 const hrefLower = href.toLowerCase(); 129 return !excludeFiles.some(ex => hrefLower.indexOf(ex) !== -1); 130 }).map(function () { 131 const parentLi = $(this).closest('li'); 132 return parentLi.length ? parentLi : $(this); 133 }).get(); 134} 135 136function getSportBookTypeList() { 137 return (localStorage.getItem('sportBookTypeList') || '0').trim(); 138} 139function parseSportList() { 140 return getSportBookTypeList().split(',').map(s => s.trim()).filter(Boolean); 141} 142function isSportBookAll() { 143 return parseSportList().includes('0'); 144} 145function canAccessSport(num) { 146 return isSportBookAll() || parseSportList().includes(String(num)); 147} 148function isSportOnly(num) { 149 const list = parseSportList(); 150 return !list.includes('0') && list.includes(String(num)) && list.length === 1; 151} 152function getSingleSportNumber() { 153 const list = parseSportList(); 154 if (!list.includes('0') && list.length === 1) return +list[0]; 155 return 0; 156} 157// Track which sport the user is currently using (updated on sport menu click) 158function setSelectedSport(num) { 159 sessionStorage.setItem('sportBookSelected', String(num)); 160} 161 162// Returns '&lpnl=lsb' for Sport2, '&lpnl=omsb' for Sport3, '' for Sport1 163// Reads sportBookSelected (session), falls back to sportBookDef (default config) 164function getLpnlParam() { 165 const selected = +(sessionStorage.getItem('sportBookSelected') || localStorage.getItem('sportBookDef')) || 2; 166 if (selected === 3) return '&lpnl=omsb'; 167 if (selected === 2) return '&lpnl=lsb'; 168 return ''; 169} 170 171function filterMenuDataBySportBookType(menuData, sportBookTypeList) { 172 if (!Array.isArray(menuData)) return menuData; 173 const list = (sportBookTypeList || '0').split(',').map(s => s.trim()); 174 if (list.includes('0')) return menuData; 175 const canSport1 = list.includes('1'); 176 const canSport2 = list.includes('2'); 177 const canSport3 = list.includes('3'); 178 return menuData.filter(record => { 179 const gameType = (record.GameType || '').trim().toUpperCase(); 180 const isLigaSB = record.IsLigaSB === true; 181 if (gameType === 'AFBSB') return isLigaSB ? canSport2 : canSport1; 182 if (gameType === 'LIGASB') return canSport2; 183 if (gameType === 'OMSB') return canSport3; 184 return true; 185 }); 186} 187 188function filterMenuMapBySportBookType(menuMap, sportBookTypeList) { 189 if (!menuMap || typeof menuMap !== 'object') return menuMap; 190 const list = (sportBookTypeList || '0').split(',').map(s => s.trim()); 191 if (list.includes('0')) return menuMap; 192 return Object.keys(menuMap).reduce((result, key) => { 193 const filteredRecords = filterMenuDataBySportBookType(menuMap[key], sportBookTypeList); 194 if (Array.isArray(filteredRecords) && filteredRecords.length > 0) 195 result[key] = filteredRecords; 196 return result; 197 }, {}); 198} 199 200function syncMenuMap(target, source) { 201 // Remove keys from target not in source 202 Object.keys(target).forEach(key => { 203 if (!(key in source)) 204 delete target[key]; 205 });
206 // If source has keys not yet in target, those would be appended at end (wrong position). 207 // Rebuild target in source key order only when new keys are detected. 208 if (Object.keys(source).some(key => !(key in target))) { 209 Object.keys(target).forEach(key => delete target[key]); 210 } 211 Object.assign(target, source); 212} 213 214function isNewGameLigaSB() { 215 return JSON.parse(localStorage.getItem('menuMap_user') || '{}')?.['LIGASB']?.[0]?.IsNewGame ?? false; 216} 217 218// âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 219// â HEADER MENU SCROLL (SWIPER) â 220// â âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ⣠221// â § 1 Configuration â constants, themes, CTId groups, extra CSS â 222// â § 2 CSS injection â ensureHeaderMenuScrollStyle() â 223// â § 3 Helpers â CTId, theme, host, viewport, DOM utils â 224// â § 4 Core â markup, init, schedule â 225// âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 226 227// ââ § 1 Configuration âââââââââââââââââââââââââââââââââââââââââââââââââââââââ 228 229const HEADER_MENU_SCROLL_STYLE_ID = 'header-menu-scroll-style'; 230const HEADER_MENU_SCROLL_HOST_CLASS = 'header-menu-scroll-host'; 231const HEADER_MENU_SCROLL_ACTIVE_CLASS = 'header-menu-scroll-active'; 232 233// CTIds that will NOT activate scroll menu on header page. 234const HEADER_MENU_SCROLL_DISABLED_CTIDS = [ 235 47, 53, 60, 75, 88, 89,115, 236 159, 168, 172, 173, 174, 185, 237 193, 197, 201, 203, 208, 238]; 239 240// CTIds that will NOT activate scroll menu on default page. 241const HEADER_MENU_SCROLL_DEFAULT_PAGE_DISABLED_CTIDS = [ 242 47, 60, 75, 80, 88, 89, 109, 243 159, 168, 172, 173, 174, 181, 182, 183, 244 185, 193, 198, 200, 203, 208, 217, 218, 221, 245 222 246]; 247 248// Named button themes â defines prev/next button appearance per theme. 249// Mirrors GetThemeColor() in Util.LeftPanel.cs. 250const HEADER_MENU_SCROLL_THEMES = { 251 'blue': { 252 color: '#fff', 253 background: 'rgba(10, 50, 120, .65)', 254 border: '1px solid rgba(255, 255, 255, .18)', 255 }, 256 'blue_hana': { 257 color: '#fff', 258 background: 'rgba(15, 60, 140, .65)', 259 border: '1px solid rgba(255, 255, 255, .18)', 260 }, 261 'gray': { 262 color: '#fff', 263 background: 'rgba(60, 65, 75, .65)', 264 border: '1px solid rgba(255, 255, 255, .18)', 265 }, 266 'green': { 267 color: '#fff', 268 background: 'rgba(15, 75, 35, .65)', 269 border: '1px solid rgba(255, 255, 255, .18)', 270 }, 271 'orange': { 272 color: '#fff', 273 background: 'rgba(150, 65, 5, .7)', 274 border: '1px solid rgba(255, 255, 255, .18)', 275 }, 276 'purple': { 277 color: '#fff', 278 background: 'rgba(70, 20, 110, .65)', 279 border: '1px solid rgba(255, 255, 255, .18)', 280 }, 281 'red': { 282 color: '#fff', 283 background: 'rgba(130, 20, 20, .65)', 284 border: '1px solid rgba(255, 255, 255, .18)', 285 }, 286 'yellow': { 287 color: '#fff', 288 background: 'rgba(120, 90, 5, .7)', 289 border: '1px solid rgba(255, 220, 50, .35)', 290 }, 291 'other': { 292 color: '#fff', 293 background: 'rgba(3, 35, 52, .46)', 294 border: '1px solid rgba(255, 255, 255, .14)', 295 }, 296}; 297 298// Theme groups â mirrors switch cases in GetThemeColor() (Util.LeftPanel.cs). 299// To add/remove a CTId: edit the array for the relevant theme below. 300const HEADER_MENU_SCROLL_THEME_GROUPS = { 301 'blue': [ 302 13, 50, 51, 52, 54, 55, 57, 65, 67, 68, 72, 73, 74, 77, 81, 82, 83, 303 87, 90, 93, 94, 95, 96, 98, 99, 100, 102, 104, 108, 111, 112, 113,
304 116, 118, 120, 122, 125, 127, 130, 131, 132, 133, 135, 139, 140, 143, 305 144, 146, 148, 149, 158, 159, 167, 171, 177, 178, 179, 180, 183, 185, 306 190, 196, 197, 199, 200, 203, 208, 209, 211, 212, 214, 217, 218, 219, 307 220, 225, 226, 229, 230, 233, 238, 239, 242, 246, 249, 250, 252, 258, 308 259, 260, 261, 265, 271, 272, 273, 278, 286, 292, 297, 299, 302, 303, 309 305, 307, 311, 312, 317, 318, 322, 324, 325, 328, 329, 310 ], 311 'blue_hana': [ 312 47, 49, 60, 287, 301, 313 ], 314 'gray': [ 315 76, 84, 91, 101, 114, 168, 195, 210, 234, 316 ], 317 'green': [ 318 63, 69, 78, 109, 115, 117, 124, 126, 129, 134, 137, 138, 141, 147, 319 161, 163, 165, 169, 170, 174, 186, 187, 216, 223, 241, 243, 245, 247, 320 251, 253, 254, 255, 263, 264, 266, 267, 274, 276, 277, 279, 280, 285, 321 288, 290, 293, 296, 300, 304, 306, 316, 320, 331, 322 ], 323 'orange': [ 324 59, 237, 256, 275, 327, 325 ], 326 'purple': [ 327 75, 107, 145, 151, 181, 202, 207, 270, 281, 283, 289, 295, 315, 328 ], 329 'red': [ 330 48, 85, 86, 92, 105, 128, 142, 150, 153, 154, 156, 157, 166, 176, 331 182, 192, 193, 221, 235, 236, 240, 244, 268, 284, 298, 308, 313, 319, 332 321, 323, 326, 333 ], 334 'yellow': [ 335 53, 56, 58, 62, 64, 66, 70, 80, 88, 89, 103, 106, 110, 119, 121, 123, 336 136, 152, 155, 162, 164, 172, 173, 175, 184, 188, 189, 191, 194, 198, 201, 337 204, 205, 206, 213, 215, 222, 224, 227, 228, 231, 232, 248, 257, 262, 338 269, 282, 291, 294, 309, 310, 314, 330, 339 ], 340}; 341 342// Build CTId â theme name map from groups above (do not edit manually). 343const HEADER_MENU_SCROLL_CTID_THEME_MAP = Object.entries(HEADER_MENU_SCROLL_THEME_GROUPS) 344 .reduce(function (map, entry) { 345 entry[1].forEach(function (id) { map[id] = entry[0]; }); 346 return map; 347 }, {}); 348 349// Fallback theme for CTIds not listed in any group (mirrors 'other' in GetThemeColor()). 350const HEADER_MENU_SCROLL_DEFAULT_THEME = 'other'; 351 352// Extra scoped CSS applied per theme group or specific CTIds. 353// Each entry: 354// themes â (optional) array of theme names this applies to 355// CTIds â (optional) array of specific CTId numbers 356// css â CSS string; use {HOST} as placeholder for the scoped host selector 357// e.g. '{HOST} #main-menu > li { display: flex !important; }' 358// Matching: a CTId matches if it appears in CTIds OR its resolved theme appears in themes (OR logic). 359const HEADER_MENU_SCROLL_EXTRA_CSS = [ 360 // Example â flex + vertically-centred li for specific CTIds: 361 // { 362 // CTIds: [13, 55], 363 // css: `{HOST} #main-menu > li { display: flex !important; align-items: center !important; }`, 364 // }, 365 // Example â apply to an entire theme group: 366 // { 367 // themes: ['blue', 'green'], 368 // css: `{HOST} #main-menu > li { display: flex !important; align-items: center !important; }`, 369 // }, 370 { 371 CTIds: [184], 372 css: `{HOST} #main-menu > li { display: flex !important; align-items: center !important; } 373 #main-menu{max-width:unset !important}`, 374 } 375]; 376 377// ââ § 2 CSS injection âââââââââââââââââââââââââââââââââââââââââââââââââââââââ 378 379let headerMenuScrollInitTimer = 0; 380 381function ensureHeaderMenuScrollStyle() { 382 if (document.getElementById(HEADER_MENU_SCROLL_STYLE_ID)) 383 return; 384 385 const style = document.createElement('style'); 386 style.id = HEADER_MENU_SCROLL_STYLE_ID; 387 style.textContent = ` 388 .${HEADER_MENU_SCROLL_HOST_CLASS} { 389 position: relative; 390 overflow: visible; 391 box-sizing: border-box; 392 } 393 .${HEADER_MENU_SCROLL_HOST_CLASS} .header-menu-scroll__viewport { 394 position: relative; 395 width: 100%; 396 overflow-x: auto; 397 overflow-y: hidden; 398 box-sizing: border-box; 399 scrollbar-width: none; 400 -ms-overflow-style: none; 401 touch-action: pan-x; 402 } 403 .${HEADER_MENU_SCROLL_HOST_CLASS} .header-menu-scroll__viewport::-webkit-scrollbar { 404 display: none; 405 } 406 .${HEADER_MENU_SCROLL_HOST_CLASS} .header-menu-scroll__list { 407 display: inline-flex !important; 408 flex-wrap: nowrap !important; 409 justify-content: space-between !important; 410 align-items: stretch !important; 411 width: max-content !important; 412 min-width: 100% !important; 413 margin: 0 !important; 414 padding: 0 !important; 415 white-space: nowrap !important; 416 } 417 .${HEADER_MENU_SCROLL_HOST_CLASS} .header-menu-scroll__list > * { 418 float: none !important; 419 flex: 0 0 auto !important; 420 } 421 .${HEADER_MENU_SCROLL_HOST_CLASS} .header-menu-scroll__list > * > a, 422 .${HEADER_MENU_SCROLL_HOST_CLASS} .header-menu-scroll__list > * a { 423 white-space: nowrap !important; 424 } 425 .${HEADER_MENU_SCROLL_HOST_CLASS} .header-menu-scroll__button { 426 position: absolute; 427 top: 50%; 428 transform: translateY(-50%); 429 width: 26px;
430 height: 26px; 431 border-radius: 999px; 432 padding: 0; 433 display: none; 434 align-items: center; 435 justify-content: center; 436 background: transparent; 437 color: var(--hmsc-btn-color, #fff); 438 border: var(--hmsc-btn-border, 1px solid rgba(255, 255, 255, .14)); 439 font-size: 20px; 440 line-height: 1; 441 cursor: pointer; 442 z-index: 2147483647; 443 box-shadow: 0 2px 8px rgba(0, 0, 0, .18); 444 backdrop-filter: blur(2px); 445 -webkit-backdrop-filter: blur(2px); 446 } 447 .${HEADER_MENU_SCROLL_HOST_CLASS}.${HEADER_MENU_SCROLL_ACTIVE_CLASS} .header-menu-scroll__button { 448 display: inline-flex; 449 } 450 .${HEADER_MENU_SCROLL_HOST_CLASS} .header-menu-scroll__button:disabled { 451 opacity: .35; 452 cursor: default; 453 pointer-events: none; 454 } 455 .${HEADER_MENU_SCROLL_HOST_CLASS} .header-menu-scroll__button--prev { 456 left: 6px; 457 } 458 .${HEADER_MENU_SCROLL_HOST_CLASS} .header-menu-scroll__button--next { 459 right: 6px; 460 } 461 `; 462 463 document.head.appendChild(style); 464} 465 466// ââ § 3 Helpers âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 467 468function getScrollMenuCTId() { 469 const raw = localStorage.getItem('CTId') || sessionStorage.getItem('CTId') || ''; 470 const id = +raw; 471 return isNaN(id) ? 0 : id; 472} 473 474// Header page: enabled by default, opt-out via DISABLED_CTIDS. 475// CTId null/0 â enable (localStorage not yet written â don't block). 476function shouldHeaderPageUseScroll() { 477 const CTId = getScrollMenuCTId(); 478 return !HEADER_MENU_SCROLL_DISABLED_CTIDS.includes(CTId); 479} 480 481// Default page: enabled by default, opt-out via DEFAULT_PAGE_DISABLED_CTIDS. 482// CTId null/0 â enable. 483function shouldDefaultPageUseScroll() { 484 const CTId = getScrollMenuCTId(); 485 return !HEADER_MENU_SCROLL_DEFAULT_PAGE_DISABLED_CTIDS.includes(CTId); 486} 487 488function shouldUseHeaderMenuScroll(menuEl) { 489 const isMountEl = typeof mountEl !== 'undefined' && menuEl === mountEl; 490 if (!(menuEl && (menuEl.id === 'main-menu' || isMountEl) && menuEl.parentElement)) 491 return false; 492 return isHeaderPage() ? shouldHeaderPageUseScroll() : shouldDefaultPageUseScroll(); 493} 494 495function getHeaderMenuScrollButtonTheme(CTId) { 496 const themeName = HEADER_MENU_SCROLL_CTID_THEME_MAP[CTId] || HEADER_MENU_SCROLL_DEFAULT_THEME; 497 return HEADER_MENU_SCROLL_THEMES[themeName] || HEADER_MENU_SCROLL_THEMES[HEADER_MENU_SCROLL_DEFAULT_THEME] || {}; 498} 499 500// Applies the per-CTId button theme by setting CSS custom properties on the host element. 501// Also stamps data-hmsc-theme / data-hmsc-ctid for use by extra CSS overrides. 502function applyHeaderMenuScrollTheme(hostEl, CTId) { 503 if (!hostEl) return; 504 const theme = getHeaderMenuScrollButtonTheme(CTId); 505 if (theme.color) hostEl.style.setProperty('--hmsc-btn-color', theme.color); 506 if (theme.background) hostEl.style.setProperty('--hmsc-btn-bg', theme.background); 507 if (theme.border) hostEl.style.setProperty('--hmsc-btn-border', theme.border); 508 hostEl.setAttribute('data-hmsc-theme', HEADER_MENU_SCROLL_CTID_THEME_MAP[CTId] || HEADER_MENU_SCROLL_DEFAULT_THEME); 509 hostEl.setAttribute('data-hmsc-ctid', String(CTId)); 510} 511 512// Injects a <style> block with extra scoped CSS rules that match the current CTId/theme. 513// Rules from HEADER_MENU_SCROLL_EXTRA_CSS are scoped via the host element's data-hmsc-ctid attribute. 514function applyHeaderMenuScrollExtraCSS(hostEl, CTId) { 515 if (!hostEl || !HEADER_MENU_SCROLL_EXTRA_CSS.length) return; 516 const resolvedTheme = HEADER_MENU_SCROLL_CTID_THEME_MAP[CTId] || HEADER_MENU_SCROLL_DEFAULT_THEME; 517 const hostSel = '.' + HEADER_MENU_SCROLL_HOST_CLASS + '[data-hmsc-ctid="' + CTId + '"]'; 518 const cssBlocks = HEADER_MENU_SCROLL_EXTRA_CSS 519 .filter(function (entry) { 520 const inCTIds = Array.isArray(entry.CTIds) && entry.CTIds.includes(CTId); 521 const inThemes = Array.isArray(entry.themes) && entry.themes.includes(resolvedTheme); 522 return inCTIds || inThemes; 523 }) 524 .map(function (entry) { return (entry.css || '').replace(/\{HOST\}/g, hostSel); }) 525 .join('\n'); 526 if (!cssBlocks) return; 527 const styleId = 'header-menu-scroll-extra-style'; 528 let styleEl = document.getElementById(styleId); 529 if (!styleEl) { 530 styleEl = document.createElement('style'); 531 styleEl.id = styleId; 532 document.head.appendChild(styleEl); 533 } 534 styleEl.textContent += '\n' + cssBlocks; 535} 536 537function getHeaderMenuScrollHost(menuEl) { 538 if (!menuEl) 539 return null; 540 541 let hostEl = null;
542 let current = menuEl.parentElement; 543 while (current) { 544 if (current.classList && current.classList.contains(HEADER_MENU_SCROLL_HOST_CLASS)) 545 hostEl = current; 546 current = current.parentElement; 547 } 548 549 if (hostEl) 550 return hostEl; 551 552 return menuEl.closest('#main-menu-container') || menuEl.parentElement; 553} 554 555function getHeaderMenuViewportWidth(hostEl) { 556 try { 557 const parentDivHeader = window.parent?.document?.getElementsByClassName('divHeader')?.[0]; 558 if (parentDivHeader) { 559 const width = Math.round(parentDivHeader.getBoundingClientRect().width || parentDivHeader.clientWidth || 0); 560 if (width > 0) 561 return width; 562 } 563 } catch (error) { 564 // Same-origin access is best-effort only. 565 } 566 567 if (!hostEl) 568 return 0; 569 570 return Math.round(hostEl.getBoundingClientRect().width || hostEl.clientWidth || 0); 571} 572 573function pruneDuplicateHeaderMenuScrollNodes(hostEl) { 574 const uniqueByRole = function (selector) { 575 const nodes = Array.from(hostEl.querySelectorAll(selector)); 576 if (nodes.length <= 1) 577 return nodes[0] || null; 578 579 nodes.slice(1).forEach(function (node) { 580 if (node && node.parentNode) 581 node.parentNode.removeChild(node); 582 }); 583 return nodes[0]; 584 }; 585 586 const viewportEl = uniqueByRole('.header-menu-scroll__viewport'); 587 const prevButton = uniqueByRole('.header-menu-scroll__button--prev'); 588 const nextButton = uniqueByRole('.header-menu-scroll__button--next'); 589 590 return { 591 viewportEl, 592 prevButton, 593 nextButton 594 }; 595} 596 597function normalizeHeaderMenuScrollHostClasses(hostEl) { 598 Array.from(hostEl.querySelectorAll('.' + HEADER_MENU_SCROLL_HOST_CLASS)).forEach(function (node) { 599 if (node !== hostEl) 600 node.classList.remove(HEADER_MENU_SCROLL_HOST_CLASS); 601 }); 602} 603 604// ââ § 4 Core ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 605 606function ensureHeaderMenuScrollMarkup() { 607 const menuEl = document.getElementById('main-menu') || 608 (typeof mountEl !== 'undefined' && mountEl instanceof Element ? mountEl : null); 609 if (!shouldUseHeaderMenuScroll(menuEl)) 610 return null; 611 612 const hostEl = getHeaderMenuScrollHost(menuEl); 613 if (!hostEl) 614 return null; 615 616 ensureHeaderMenuScrollStyle(); 617 618 hostEl.classList.add(HEADER_MENU_SCROLL_HOST_CLASS); 619 normalizeHeaderMenuScrollHostClasses(hostEl); 620 const _hmscCTId = getScrollMenuCTId(); 621 applyHeaderMenuScrollTheme(hostEl, _hmscCTId); 622 applyHeaderMenuScrollExtraCSS(hostEl, _hmscCTId); 623 624 const pruned = pruneDuplicateHeaderMenuScrollNodes(hostEl); 625 626 let viewportEl = pruned.viewportEl; 627 if (!viewportEl) { 628 viewportEl = document.createElement('div'); 629 viewportEl.className = 'header-menu-scroll__viewport'; 630 hostEl.insertBefore(viewportEl, menuEl); 631 } 632 633 if (menuEl.parentElement !== viewportEl) 634 viewportEl.appendChild(menuEl); 635 menuEl.classList.add('header-menu-scroll__list'); 636 637 let prevButton = pruned.prevButton; 638 if (!prevButton) { 639 prevButton = document.createElement('button'); 640 prevButton.type = 'button'; 641 prevButton.className = 'header-menu-scroll__button header-menu-scroll__button--prev'; 642 prevButton.setAttribute('aria-label', 'Scroll menu left'); 643 prevButton.innerHTML = '‹'; 644 hostEl.appendChild(prevButton); 645 } 646 647 let nextButton = pruned.nextButton; 648 if (!nextButton) { 649 nextButton = document.createElement('button'); 650 nextButton.type = 'button'; 651 nextButton.className = 'header-menu-scroll__button header-menu-scroll__button--next'; 652 nextButton.setAttribute('aria-label', 'Scroll menu right'); 653 nextButton.innerHTML = '›'; 654 hostEl.appendChild(nextButton); 655 } 656 657 return { 658 menuEl, 659 hostEl, 660 viewportEl, 661 prevButton, 662 nextButton 663 }; 664} 665 666function initHeaderMenuScroll() { 667 const scrollParts = ensureHeaderMenuScrollMarkup(); 668 if (!scrollParts) 669 return null; 670 671 const { menuEl, hostEl, viewportEl, prevButton, nextButton } = scrollParts; 672 673 if (menuEl.__headerMenuScroll && 674 menuEl.__headerMenuScroll.hostEl === hostEl && 675 menuEl.__headerMenuScroll.viewportEl === viewportEl) {
676 menuEl.__headerMenuScroll.update(); 677 return menuEl.__headerMenuScroll; 678 } 679 680 if (menuEl.__headerMenuScroll && typeof menuEl.__headerMenuScroll.destroy === 'function') 681 menuEl.__headerMenuScroll.destroy(); 682 683 const update = () => { 684 const viewportWidth = getHeaderMenuViewportWidth(hostEl); 685 if (viewportWidth > 0) { 686 hostEl.style.width = `${viewportWidth}px`; 687 hostEl.style.maxWidth = `${viewportWidth}px`; 688 } 689 const contentWidth = Math.max( 690 Math.round(viewportEl.scrollWidth || 0), 691 Math.round(menuEl.getBoundingClientRect().width || 0) 692 ); 693 const isOverflow = viewportWidth > 0 && contentWidth > viewportWidth + 4; 694 const visibleWidth = Math.max(Math.round(viewportEl.clientWidth || viewportWidth || 0), 0); 695 const maxScrollLeft = Math.max(contentWidth - visibleWidth, 0); 696 const scrollLeft = Math.max(viewportEl.scrollLeft || 0, 0); 697 698 hostEl.classList.toggle(HEADER_MENU_SCROLL_ACTIVE_CLASS, isOverflow); 699 700 if (!isOverflow) { 701 if (scrollLeft > 0) 702 viewportEl.scrollLeft = 0; 703 prevButton.disabled = true; 704 nextButton.disabled = true; 705 return; 706 } 707 708 const safeScrollLeft = Math.min(scrollLeft, maxScrollLeft); 709 if (Math.abs(safeScrollLeft - scrollLeft) > 1) 710 viewportEl.scrollLeft = safeScrollLeft; 711 712 prevButton.disabled = safeScrollLeft <= 1; 713 nextButton.disabled = safeScrollLeft >= maxScrollLeft - 1; 714 }; 715 716 const scrollByStep = (direction) => { 717 const viewportWidth = getHeaderMenuViewportWidth(hostEl); 718 const step = Math.max(160, Math.round(viewportWidth * 0.62)); 719 viewportEl.scrollBy({ 720 left: direction * step, 721 behavior: 'smooth' 722 }); 723 }; 724 725 const onScroll = () => update(); 726 const onPrevClick = function (event) { 727 event.preventDefault(); 728 scrollByStep(-1); 729 }; 730 const onNextClick = function (event) { 731 event.preventDefault(); 732 scrollByStep(1); 733 }; 734 735 let resizeObserver = null; 736 let mutationObserver = null; 737 738 prevButton.addEventListener('click', onPrevClick); 739 nextButton.addEventListener('click', onNextClick); 740 viewportEl.addEventListener('scroll', onScroll, { passive: true }); 741 window.addEventListener('resize', update); 742 743 if (window.ResizeObserver) { 744 resizeObserver = new ResizeObserver(update); 745 resizeObserver.observe(hostEl); 746 resizeObserver.observe(viewportEl); 747 resizeObserver.observe(menuEl); 748 } 749 750 if (window.MutationObserver) { 751 mutationObserver = new MutationObserver(update); 752 mutationObserver.observe(menuEl, { childList: true, subtree: true, characterData: true }); 753 } 754 755 // Sub-menu fix: viewport has overflow-y:hidden which clips absolutely-positioned submenus. 756 // On hover, switch sub-menu to position:fixed so it breaks out of the scroll container. 757 const subMenuFixListeners = []; 758 function fixSubMenuPosition(li) { 759 const submenu = li.querySelector('.sub-menu'); 760 if (!submenu) return; 761 const rect = li.getBoundingClientRect(); 762 submenu.style.setProperty('position', 'fixed', 'important'); 763 //submenu.style.setProperty('top', rect.bottom + 'px', 'important'); 764 submenu.style.setProperty('left', '0', 'important'); 765 submenu.style.setProperty('right', '0', 'important'); 766 submenu.style.setProperty('width', '100%', 'important'); 767 submenu.style.setProperty('z-index', '99999', 'important'); 768 } 769 function clearSubMenuPosition(li) { 770 const submenu = li.querySelector('.sub-menu'); 771 if (!submenu) return; 772 ['position', 'top', 'left', 'right', 'width', 'z-index'].forEach(function (p) { 773 submenu.style.removeProperty(p); 774 }); 775 } 776 Array.from(menuEl.querySelectorAll(':scope > li, :scope > td, :scope >
776 a')).forEach(function (li) { 777 const onEnter = function () { fixSubMenuPosition(li); }; 778 const onLeave = function () { clearSubMenuPosition(li); }; 779 li.addEventListener('mouseenter', onEnter); 780 li.addEventListener('mouseleave', onLeave); 781 subMenuFixListeners.push({ li, onEnter, onLeave }); 782 }); 783 784 // When the page scrolls vertically, clear position:fixed on all sub-menus so they 785 // don't stay frozen at the wrong viewport position while the header scrolls away. 786 const onWindowScroll = function () { 787 subMenuFixListeners.forEach(function (item) { 788 // DEBUG â uncomment to diagnose sub-menu freeze on vertical scroll: 789 // const submenu = item.li.querySelector('.sub-menu'); 790 // const isHovered = item.li.matches && item.li.matches(':hover'); 791 // const hasFixed = submenu && submenu.style.position === 'fixed'; 792 // console.log('[onWindowScroll]', { 793 // li: item.li.textContent.trim().slice(0, 30), 794 // isHovered, 795 // hasFixed, 796 // submenuDisplay: submenu ? window.getComputedStyle(submenu).display : 'no-submenu', 797 // submenuVisibility: submenu ? window.getComputedStyle(submenu).visibility : 'no-submenu', 798 // }); 799 clearSubMenuPosition(item.li); 800 }); 801 }; 802 window.addEventListener('scroll', onWindowScroll, { passive: true }); 803 804 menuEl.__headerMenuScroll = { 805 hostEl, 806 viewportEl, 807 update, 808 destroy() { 809 prevButton.removeEventListener('click', onPrevClick); 810 nextButton.removeEventListener('click', onNextClick); 811 viewportEl.removeEventListener('scroll', onScroll); 812 window.removeEventListener('resize', update); 813 window.removeEventListener('scroll', onWindowScroll); 814 if (resizeObserver) 815 resizeObserver.disconnect(); 816 if (mutationObserver) 817 mutationObserver.disconnect(); 818 subMenuFixListeners.forEach(function (item) { 819 item.li.removeEventListener('mouseenter', item.onEnter); 820 item.li.removeEventListener('mouseleave', item.onLeave); 821 clearSubMenuPosition(item.li); 822 }); 823 subMenuFixListeners.length = 0; 824 } 825 }; 826 827 update(); 828 return menuEl.__headerMenuScroll; 829} 830 831function scheduleHeaderMenuScrollInit() { 832 if (headerMenuScrollInitTimer) 833 clearTimeout(headerMenuScrollInitTimer); 834 835 headerMenuScrollInitTimer = setTimeout(function () { 836 headerMenuScrollInitTimer = 0; 837 try { 838 initHeaderMenuScroll(); 839 } catch (error) { 840 console.log(error); 841 } 842 }, 0); 843} 844 845// ââââââââââââââââââââââââââââ END: HEADER MENU SCROLL ââââââââââââââââââââââââ 846 847// use for TW account only and sportBookType = [1,2,3] 848function renameSPORTToSPORT12(sportItems, sportBookType) { 849 try { 850 sportItems.forEach(item => { 851 const $item = $(item); 852 const anchors = $item.is('a') ? $item : $item.find('a'); 853 anchors.each(function () { 854 const $a = $(this); 855 switch (sportBookType) { 856 case 1: 857 $a.html(replaceSPORTLabelHtml($a.html(), 1)); 858 break; 859 case 2: 860 $a.html(replaceSPORTLabelHtml($a.html(), 2)); 861 const href = ($a.attr('href') || '').toLowerCase(); 862 let iconNew = getTopRightIconNEW(localStorage.getItem('CTId')); 863 let top = isNaN(iconNew.top) ? iconNew.top : `${iconNew.top}px` 864 const newGameTag = `<img title="new" src="_View/img/games/new.png?v=" class="blinking-image" style="filter:none; padding:0; z-index:999999; width: 25px!important; height: 13px; position: absolute; top: ${top}; right: ${iconNew.right}px;">`; 865 $a.css('position', 'relative'); 866 $a.append(newGameTag); 867 if ($a.attr('href') === '#' || href.indexOf('_view/rmodds1.aspx') > -1) { 868 $a.attr({ href: 'LobbyGames.aspx?type=AFBSB', target: 'fraBody' }); 869 } 870 break; 871 case 3: 872 $a.html(replaceSPORTLabelHtml($a.html(), 3)); 873 break; 874 } 875 }); 876 }); 877 } 878 catch (error) { 879 console.log(error) 880 } 881} 882 883function appendSPORT2(sportItems, CTId, currentTimestamp) { 884 addAllSportMenus(CTId, currentTimestamp, sportItems); 885} 886 887// Single-pass function that handles all sport combinations (1,2,3) based on sportBookTypeList 888function addAllSportMenus(CTId, currentTimestamp, sportItemsOverride) { 889 try { 890 const canS1 = canAccessSport(1); 891 const canS2 = canAccessSport(2); 892 const canS3 = canAccessSport(3); 893 if (!canS1 && !canS2 && !canS3) return; 894 895 const staticImageHost = localStorage.getItem('cdnImageHost') || 'https://img
895share.iuf-cfl.cloud'; 896 const getImageType = (id) => id === 184 ? 'svg' : [137, 257].includes(id) ? 'webp' : 'png'; 897 const imageType = getImageType(+CTId); 898 899 let iconNew = getTopRightIconNEW(CTId); 900 let top = isNaN(iconNew.top) ? iconNew.top : `${iconNew.top}px`; 901 const newGameTag = `<img title="new" src="_View/img/games/new.png?v=" class="blinking-image" style="filter:none; padding:0; z-index:999999; width: 25px!important; height: 13px; position: absolute; top: ${top}; right: ${iconNew.right}px;">`; 902 903 const sportItems = sportItemsOverride || (() => { 904 let items = getSPORTByHref(); 905 if (items.length === 0) items = getSPORTByText(); 906 return items; 907 })(); 908 if (sportItems.length === 0) return; 909 910 sportItems.forEach(item => { 911 const $item = $(item); 912 const anchors = $item.is('a') ? $item : $item.find('a'); 913 914 // Track insertion point so clones always land immediately after each other 915 let $lastInserted = $item; 916 917 // Determine what sport number to label the original item 918 const originalSportNum = canS1 ? 1 : (canS2 ? 2 : 3); 919 920 // Rename original and fix its href 921 anchors.each(function () { 922 const $a = $(this); 923 $a.html(replaceSPORTLabelHtml($a.html(), originalSportNum)); 924 const href = ($a.attr('href') || '').toLowerCase(); 925 if ($a.attr('href') === '#' || href.indexOf('_view/rmodds1.aspx') > -1) { 926 if (canS1) $a.attr({ href: 'Body.aspx', target: 'fraBody' }); 927 else if (canS2) $a.attr({ href: 'LobbyGames.aspx?type=AFBSB', target: 'fraBody' }); 928 else $a.attr({ href: 'LobbyGames.aspx?type=OMSB', target: 'fraBody' }); 929 } 930 }); 931 932 // Update icon when original is repurposed as sport2 (canS1=false, canS2=true) 933 if (!canS1 && canS2) { 934 if (CTId === 126) { 935 anchors.each(function () { 936 const $a = $(this); 937 const s = $a.attr('style') || ''; 938 $a.attr('style', s.replace('Images/theme/v1/img/sport.png', `${staticImageHost}/headergames/126/MenuIcon_LIGASB.png?v=${currentTimestamp}) left center no-repeat !important`)); 939 }); 940 } 941 const imgOrig = $item.find('img'); 942 if (imgOrig.length) imgOrig.attr('src', `${staticImageHost}/headergames/${CTId}/MenuIcon_LigaSB.${imageType}?v=${currentTimestamp}`); 943 } 944 945 // Clone for SPORT 2 â inserted immediately after original 946 if (canS2 && canS1) { 947 const clone2 = $item.clone(); 948 const anchor2 = clone2.is('a') ? clone2 : clone2.find('a'); 949 anchor2.attr({ href: 'LobbyGames.aspx?type=AFBSB', target: 'fraBody' }); 950 anchor2.html(replaceSPORTLabelHtml(anchor2.html(), 2)); 951 if (CTId === 126) { 952 const s = anchor2.attr('style') || ''; 953 anchor2.attr('style', s.replace('Images/theme/v1/img/sport.png', `${staticImageHost}/headergames/126/MenuIcon_LIGASB.png?v=${currentTimestamp}) left center no-repeat !important`)); 954 } 955 // Set menu icon src BEFORE appending newGameTag, else find('img') picks up the blinking img too 956 const img2 = clone2.find('img'); 957 if (img2.length) img2.attr('src', `${staticImageHost}/headergames/${CTId}/MenuIcon_LigaSB.${imageType}?v=${currentTimestamp}`); 958 if (isNewGameLigaSB) { 959 const s = anchor2.attr('style') || ''; 960 anchor2.attr('style', s + (s && !s.trim().endsWith(';') ? ';' : '') + ' position: relative!important;'); 961 anchor2.append(newGameTag); 962 } 963 $lastInserted.after(clone2); 964 $lastInserted = clone2; 965 console.log("Added SPORT 2 (LigaSB) menu item"); 966 } 967 968 // Clone for SPORT 3 â inserted immediately after last inserted item 969 if (canS3) { 970 const clone3 = $item.clone(); 971 const anchor3 = clone3.is('a') ? clone3 : clone3.find('a'); 972 anchor3.attr({ href: 'LobbyGames.aspx?type=OMSB', target: 'fraBody' }); 973 anchor3.html(replaceSPORTLabelHtml(anchor3.html(), 3)); 974 // Set menu icon src BEFORE appending newGameTag, else find('img') picks up the blinking img too 975 const img3 = clone3.find('img'); 976 if (img3.length) img3.attr('src', `${staticImageHost}/headergames/${CTId}/MenuIcon_OMSB.${imageType}?v=${currentTimestamp}`); 977 const s3 = anchor3.attr('style') || ''; 978 anchor3.attr('style', s3 + (s3 && !s3.trim().endsWith(';') ? ';' : '') + ' position: relative!important;'); 979 anchor3.append(newGameTag); 980 $lastInserted.after(clone3); 981 $lastInserted = clone3; 982 console.log("Added SPORT 3 (OMEGA) menu item"); 983 } 984 985 // Remove original only when it was not repurposed (sport3-only: original is replaced by clone3) 986 if (!canS1 && !canS2) { 987 $item.remove(); 988 console.log("Removed original SPORT item (sport1, sport2 not in list)"); 989 } 990 }); 991 992 addSPORTEvent(); 993 } 994 catch (error) { 995 console.log(error); 996 } 997} 998 999function addSPORTEvent() { 1000 $('a').on('click touchstart', function (event) { 1001 try { 1002 /*var parent = window.parent.document; 1003 var href = $(this).attr('href'); 1004 var target = $(this).attr('target'); 1005 var divPanel = $(parent).find('.divPanel')[0]; 1006 if (href !== '#' && target !== '_blank') { 1007 if (href.toLowerCase().indexOf('lobbygames.aspx') > -1) { 1008 divPanel.style.cssText = "display:none"; 1009 let width = isMobileDevice() ? 'width: 100% !important;' : 'width: 1024px !important;'; 1010 console.log(width); 1011 $(parent).find('.divMain')[0].style.cssText = width; 1012 } else { 1013 divPanel.style.cssText = "display:inline"; 1014 $(parent).find('.divMain').css('width', 'calc(100% - 205px)'); 1015 1016 //localStorage.setItem('ligaSbHost', ''); 1017 //$('#accTpLst', window.parent.frames["fraHeader"].document).attr('onchange', 'top.location.href = this.form.accTpLst.options[this.form.accTpLst.selectedIndex].value; return false;'); 1018 //let fraPanel = window.parent.document.getElementsByName("fraPanel")[0]; 1019 //if (fraPanel && !fraPanel.src.includes("Panel.aspx")) { 1020 // fraPanel.src = "_Bet/Panel.aspx"; 1021 //} 1022 } 1023 }*/ 1024 var href = $(this).attr('href'); 1025 var target = $(this).attr('target'); 1026 var nonSportLinks = getNonSPORTLinks(); 1027 //event.preventDefault(); 1028 if (href.toLowerCase().indexOf('lobbygames.aspx') > -1 && !isSportOnly(1)) 1029 replaceTargetFraBody(nonSportLinks, 'fraBody'); 1030 else if (href.toLowerCase().indexOf('body.aspx') > -1 && !isSportOnly(1)) 1031 replaceTargetFraBody(nonSportLinks, 'fraMain'); 1032 if (href !== undefined && href !== '#' && target !== '_blank' && target !== '_top' && target !== '_parent') { 1033 if (href.toLowerCase().indexOf('lobbygames.aspx') > -1 || href.toLowerCase().indexOf('body.aspx') > -1) { 1034 // Track selected sport on direct sport navigation 1035 if (href.toLowerCase().indexOf('type=omsb') > -1) setSelectedSport(3); 1036 else if (href.toLowerCase().indexOf('type=afbsb') > -1) setSelectedSport(2); 1037 else if (href.toLowerCase().indexOf('body.aspx') > -1) setSelectedSport(1); 1038 // open normally 1039 } 1040 else { 1041 event.preventDefault(); 1042 if (target == 'fraMain') 1043 window.open(href, target); 1044 else if (target == 'fraBody') { 1045 if (isFraMainLoaded()) { 1046 if (href.toLowerCase().indexOf('body.aspx') === -1) 1047 window.open(href, 'fraMain'); 1048 else 1049 window.open('_View/RMOdds1.aspx', 'fraMain'); 1050 } 1051 else { 1052 if (href.toLowerCase().indexOf('body.aspx') > -1) 1053 href = '_View/RMOdds1.aspx'; 1054 else if (href.toLowerCase().indexOf('//') > -1) 1055 href = encodeURIComponent(href) 1056 1057 window.open('Body.aspx?href=' + href + getLpnlParam(), target); 1058 } 1059 } 1060 } 1061 } 1062 } catch (e) { 1063 console.log(e); 1064 } 1065 });
1066} 1067 1068function isFraMainLoaded() { 1069 try { 1070 // Access the sibling iframe by its ID or index 1071 const siblingFrame = parent.frames['fraBody'].document.getElementsByName("fraMain"); 1072 1073 // Attempt to access its document 1074 // This will throw a SecurityError if it's cross-origin 1075 return !!siblingFrame[0].contentDocument; 1076 } catch (e) { 1077 // A SecurityError means it's cross-origin 1078 return false; 1079 } 1080 1081 //try { 1082 // return parent.frames['fraBody'].frames['fraMain'] !== undefined 1083 //} 1084 //catch (e) { 1085 // return false; 1086 //} 1087} 1088// only set target fraBody for load ligasb as default 1089function replaceTargetFraBody(anchorNonSPORTList, targetValue = 'fraBody') { 1090 if (!anchorNonSPORTList) 1091 anchorNonSPORTList = getNonSPORTLinks() 1092 anchorNonSPORTList.forEach(function (el) { 1093 const $el = $(el); 1094 const $anchor = $el.is('a') ? $el : $el.find('a').first(); 1095 if ($anchor.length && $anchor.is('[target]') && $anchor.attr('target') !== '_blank' && $anchor.attr('target') !== '_top' && $anchor.attr('target') !== '_parent') { 1096 //console.log($anchor.html() + '-' + $anchor.attr('target')); 1097 $anchor.attr('target', targetValue); 1098 } 1099 }); 1100} 1101 1102function addSPORT2Menu(CTId, currentTimestamp) { 1103 addAllSportMenus(CTId, currentTimestamp); 1104} 1105 1106function applyAddMHClones() { 1107 const addMH = +(getQueryParam('AddMH') || 0); 1108 if (!(addMH > 0)) return; 1109 const menuEl = (typeof mountEl !== 'undefined' && mountEl instanceof Element ? mountEl : null) || 1110 document.getElementById('main-menu'); 1111 if (!menuEl) return; 1112 const items = Array.from(menuEl.children).filter(function (child) { 1113 const tagName = child.tagName.toLowerCase(); 1114 return (tagName === 'li' || tagName === 'a') && !(child.classList && child.classList.contains('icon')); 1115 }); 1116 if (!items.length) return; 1117 const lastItem = items[items.length - 1]; 1118 for (let i = 0; i < addMH; i++) { 1119 menuEl.appendChild(lastItem.cloneNode(true)); 1120 } 1121} 1122 1123function removeTANGKAS() { 1124 var tags = $(mountEl).find('a').filter(function () { 1125 return $(this).attr('href') && $(this).attr('href').toLowerCase().includes('tangkas'); 1126 }); 1127 1128 if (tags.length > 0) { 1129 tags.each(function () { 1130 if ($(this).closest('li').length > 0) { 1131 $(this).closest('li').remove(); 1132 console.log("Tangkas menu item removed from <li>"); 1133 } else { 1134 $(this).remove(); 1135 console.log("Tangkas menu item removed from <a>"); 1136 } 1137 }); 1138 } else { 1139 console.log("No <a> tags found with the text tangkas"); 1140 } 1141} 1142 1143function replaceSLOTUrl() { 1144 var tags = $(mountEl).find('a').filter(function () { 1145 return $(this).attr('href') && $(this).attr('href').toLowerCase().includes('gt=slot'); 1146 }); 1147 1148 if (tags.length > 0) { 1149 tags.each(function () { 1150 $(this).attr('href', 'SlotTW.aspx') 1151 }); 1152 } 1153 else 1154 console.log("No <a> tags found with the text slot"); 1155} 1156 1157function getTopRightIconNEW(CTId, isGuest) { 1158 let isSWAccount = localStorage.getItem('isSWAccount') === 'true'; 1159 let top = 'auto', right = 0 1160 switch (+CTId) { 1161 case 47: top = isHeaderPage() ? -5 : 'auto'; break; 1162 case 57: 1163 case 63: 1164 case 67: 1165 case 69: 1166 case 75: 1167 case 82: 1168 case 83: 1169 case 87: 1170 case 164: 1171 case 167: 1172 case 168: 1173 case 172: 1174 case 179: 1175 case 225: 1176 case 272: 1177 case 317: 1178 //top = -9; break; 1179 top = 0; break; 1180 1181 case 82: 1182 case 86: 1183 case 90: 1184 case 95: 1185 case 96: 1186 case 99: 1187 top = 0; break; 1188 1189 case 101: 1190 top = isHeaderPage() ? -9 : 'auto'; break; 1191 1192 case 103: 1193 case 104: 1194 case 115: 1195 case 124: 1196 case 125: 1197 top = 0; break; 1198 1199 case 137: 1200 top = 0; right = -14; break; 1201 1202 case 109: 1203 case 142: 1204 case 155: 1205 top = isHeaderPage() ? 0 : 'auto'; break; 1206 1207 case 149: 1208 case 150: 1209 case 152: 1210 case 151: 1211 case 153: 1212 case 181: 1213 case 182: 1214 case 183: 1215 case 184: 1216 case 193: 1217 case 204: 1218 case 213: 1219 case 217: 1220 case 221: 1221 case 243: 1222 case 244: 1223 case 256: 1224 case 263: 1225 case 264: 1226 case 268: 1227 case 273: 1228 case 289: 1229 case 296: 1230 case 303: 1231 case 322: 1232 top = 0; break; 1233 1234 case 235: 1235 top = isHeaderPage() ? (isGuest ? 4 : (isSWAccount ? -40 : 0)) : 11; 1236 right = isHeaderPage() ? 21 : 23; 1237 break; 1238 case 277: top = isHeaderPage() ? -9 : 0; break; 1239 1240 case 279: 1241 if (!isHeaderPage()) { 1242 right = 29; 1243 } 1244 else { 1245 if (isGuest) { right = 35; top = 0; } 1246 else { right = 9; top = -11; } 1247 } 1248 break; 1249 case 89: top = 'auto'; right = 1200; break; 1250 case 298: right = 29; top = isGuest ? 0 : -12; break; 1251 case 299: right = -17; break; 1252 case 301: top = 0; right = -20; break; 1253 case 306: top = 0; right = -5; break; 1254 } 1255 let newIcon = { top, right } 1256 return newIcon 1257} 1258 1259function setLanguage(langParam, callback) { 1260 let url = 'Handlers/LanguageHandler.ashx?lang=' + langParam; 1261 fetch(url, { 1262 method: 'GET', 1263 headers: { 1264 'Accept': 'text/plain' 1265 } 1266 }) 1267 .then(response => { 1268 if (!response.ok) throw new Error("HTTP error " + response.status); 1269 return response.text(); 1270 }) 1271 .then(data => { 1272 callback(data); 1273 }) 1274 .catch(err => { 1275 alert("Fetch error:", err); 1276 }); 1277} 1278
1279let fraHeaderDocument = null; 1280try { 1281 if (window.parent && window.parent.frames["fraHeader"]) 1282 fraHeaderDocument = window.parent.frames["fraHeader"].document; 1283} 1284catch (e) { 1285 fraHeaderDocument = document; 1286} 1287// Vue App and Components part 1288const { createApp, ref, onMounted, computed, onBeforeMount, provide, inject } = Vue; 1289const SpanArrow = { 1290 template: '<span class="arrow-down-submenu">â¼</span>', 1291}; 1292const DivArrow = { 1293 template: '<div class="arrow-down"></div>', 1294}; 1295const TextArrow = { 1296 template: 'â¼', 1297}; 1298const DownArrow = { 1299 props: { 1300 type: String, 1301 hasSubGame: Boolean, 1302 gameType: String, 1303 isGuest: Boolean, 1304 hideOnMobileResponsive: { 1305 type: Boolean, 1306 default: false 1307 } 1308 }, 1309 components: { 1310 SpanArrow, 1311 DivArrow, 1312 TextArrow, 1313 }, 1314 computed: { 1315 getArrowComponent() { 1316 switch (this.type) { 1317 case 'span': 1318 return SpanArrow; 1319 case 'div': 1320 return DivArrow; 1321 case 'text': 1322 return TextArrow; 1323 } 1324 }, 1325 isVisible() { 1326 if (isMobileDevice() && this.hideOnMobileResponsive) 1327 return false; 1328 return this.isGuest ? (this.hasSubGame ? true : false) : (this.gameType.toLowerCase() === 'afbsb' ? (this.hasSubGame ? true : false) : false); 1329 } 1330 }, 1331 template: ` 1332 <component :is="getArrowComponent" v-if="isVisible"></component> 1333 `, 1334}; 1335const ImageItem = { 1336 props: { 1337 className: { 1338 type: String, 1339 default: '', 1340 }, 1341 cacheVersion: { 1342 type: Number, 1343 default: 0, 1344 }, 1345 imageData: { 1346 type: Object, 1347 required: true, 1348 }, 1349 isSubmenu: { 1350 type: Boolean, 1351 default: false, 1352 }, 1353 width: { 1354 type: String, 1355 default: 'auto', 1356 }, 1357 height: { 1358 type: String, 1359 default: 'auto', 1360 }, 1361 }, 1362 data() { 1363 return { 1364 currentTimestamp: null, 1365 errorHandled: false, 1366 showErrorImage: false, 1367 }; 1368 }, 1369 created() { 1370 this.currentTimestamp = this.getCurrentTimeStamp; 1371 }, 1372 setup() { 1373 const ligaSbSportSubmenuName = inject('ligaSbSportSubmenuName'); 1374 return { ligaSbSportSubmenuName }; 1375 }, 1376 methods: { 1377 handleError() { 1378 if (!this.errorHandled) { 1379 let newTimestamp = this.getRefreshTimestampMS; 1380 //let newTimestamp = this.getRefreshTimestampH; 1381 const refreshTimestamp = +(localStorage.getItem('HMNCacheV2') || 0); 1382 if (refreshTimestamp >= this.currentTimestamp) 1383 //if (refreshTimestamp.toString().indexOf("17") === -1 && refreshTimestamp >= this.currentTimestamp) 1384 newTimestamp = refreshTimestamp; 1385 1386 localStorage.setItem('HMNCacheV2', newTimestamp); 1387 localStorage.removeItem('HMNCacheV'); 1388 this.errorHandled = true; 1389 this.showErrorImage = true; 1390 1391 const delays = [1, 2, 3, 5, 8, 13, 21, 34, 55]; 1392 const delay = delays[Math.floor(Math.random() * delays.length)]; 1393 1394 setTimeout(() => { 1395 this.currentTimestamp = newTimestamp; 1396 this.showErrorImage = false; 1397 }, delay * 1000); 1398 } 1399 }, 1400 }, 1401 computed: { 1402 src() { 1403 if (this.showErrorImage) 1404 return "Images/spinner.gif"; 1405 let staticImageHost = localStorage.getItem('cdnImageHost') || 'https://imgshare.iuf-cfl.cloud'; 1406 let gameName = (+this.imageData.CTId === 13 && isLigaSbName(this.imageData.GameName, this.ligaSbSportSubmenuName)) ? 'LIGA 365' : this.imageData.GameName; 1407 1408 const basePath = this.isSubmenu 1409 ? `/headergames/SubMenuIcon_${this.imageData.HGameId}_${gameName}.png` 1410 : `/headergames/${this.imageData.CTId}/MenuIcon_${this.imageData.GameType}.${this.imageData.ImageType}`; 1411 return `${staticImageHost}${basePath}?v=${this.currentTimestamp}`; 1412 }, 1413 style() { 1414 if (this.showErrorImage) 1415 return "width:32px!important; height:32px!important;"; 1416 return `width:${this.width}; height:${this.height};`; 1417 }, 1418 getCurrentTimeStamp() { 1419 const dbTimestamp = +extractTimestampFromString(this.imageData.HeaderLastUpdatedTime); 1420 const serverTimestamp = +this.cacheVersion; 1421 let latestTimestamp = Math.max(dbTimestamp, serverTimestamp); 1422 1423 const refreshTimestamp = +(localStorage.getItem('HMNCacheV2') || 0); 1424 latestTimestamp = Math.max(latestTimestamp, refreshTimestamp); 1425 return latestTimestamp; 1426 }, 1427 getRefreshTimestampH() { 1428 const date = new Date(); 1429 const year = date.getFullYear();
1430 const month = (date.getMonth() + 1).toString().padStart(2, '0'); 1431 const day = date.getDate().toString().padStart(2, '0'); 1432 const hours = date.getHours().toString().padStart(2, '0'); 1433 //const minuteGroup = Math.floor(date.getMinutes() / 30).toString(); 1434 return +`${year}${month}${day}${hours}`; 1435 }, 1436 getRefreshTimestampMS() { 1437 return this.getCurrentTimeStamp + 1; 1438 } 1439 }, 1440 template: ` 1441 <img 1442 :style="style" 1443 :class="className" 1444 :src="src" 1445 :alt="imageData.GameMenuDisplayName" 1446 @error="handleError" 1447 /> 1448 `, 1449}; 1450 1451const MenuItem = { 1452 props: { 1453 name: String, 1454 className: String, 1455 onclickHandler: Function, 1456 isGuest: Boolean, 1457 hrefs: Object 1458 }, 1459 setup() { 1460 const isLigaSbOnly = inject('isLigaSbOnly'); 1461 const hasTangkas = inject('hasTangkas'); 1462 const cacheVersion = inject('cacheVersion'); 1463 const CTId = inject('CTId'); 1464 const isGuest = inject('isGuest'); 1465 function onclickFunc(event) { 1466 try { 1467 var parent = window.parent.document; 1468 var href = this.href; 1469 var target = this.target 1470 // Track selected sport when user clicks a sport menu item 1471 const hrefLower = (href || '').toLowerCase(); 1472 if (hrefLower.indexOf('type=omsb') > -1) setSelectedSport(3); 1473 else if (hrefLower.indexOf('type=afbsb') > -1) setSelectedSport(2); 1474 else if (hrefLower.indexOf('body.aspx') > -1) setSelectedSport(1); 1475 let CTId = +localStorage.getItem('CTId') 1476 // CTId list of WLs use Default page as Main page 1477 let DMLayoutCTIds = [60] 1478 //console.log(href) 1479 if (href !== undefined && href !== '#' && target !== '_blank') { 1480 if (href.toLowerCase().indexOf('lobbygames.aspx?type=afbsb') > -1 || href.toLowerCase().indexOf('lobbygames.aspx?type=omsb') > -1 || href.toLowerCase().indexOf('body.aspx') > -1) { 1481 //window.open(href, target); 1482 } else { 1483 if (this.isLigaSbOnly) { 1484 // only SPORT 2 1485 } 1486 else { 1487 event.preventDefault(); 1488 if (target == 'fraMain') 1489 window.open(href, target); 1490 else if (target == 'fraBody') { 1491 if (isFraMainLoaded()) { 1492 if (href.toLowerCase().indexOf('body.aspx') === -1) { 1493 window.open(href, 'fraMain'); 1494 //window.parent.frames['fraBody'].postMessage({ ACT: 'LoadRightGame', value: null }, '*'); 1495 } 1496 else 1497 window.open('_View/RMOdds1.aspx', 'fraMain'); 1498 } 1499 else { 1500 if (href.toLowerCase().indexOf('body.aspx') > -1) 1501 href = '_View/RMOdds1.aspx'; 1502 else if (href.toLowerCase().indexOf('//') > -1) 1503 href = encodeURIComponent(href) 1504 window.open('Body.aspx?href=' + href + getLpnlParam(), target); 1505 } 1506 } 1507 } 1508 } 1509 } 1510 1511 if (this.isGuest && !isHeaderPage()) { 1512 // Guest on login page (all in Defaults/ subdirectory). 1513 // Set sport in session via MainGuestHandler, then navigate to app root (clean URL). 1514 // App root is computed by stripping '/Defaults/filename' from current URL â 1515 // works for any virtual path (localhost, subdir, etc.). 1516 event.preventDefault(); 1517 let sportPage = 's1'; 1518 switch (this.name.toLowerCase()) { 1519 case 'ligasb': 1520 sportPage = 's2'; break; 1521 case 'omsb': 1522 sportPage = 's3'; break; 1523 default: 1524 sportPage = 's1'; break 1525 } 1526 $.ajax({
1527 url: 'Handlers/MainGuestHandler.ashx?p=m&s=' + sportPage, 1528 method: 'GET', 1529 success: function (response) { 1530 console.log("Server response:", response); 1531 //location.reload(); 1532 //fixed for firefox load default page after transfer to Main page (live site) 1533 location.href = location.href.replace(/#$/, ''); 1534 }, 1535 error: function (xhr) { 1536 //console.error("AJAX error:", xhr); 1537 } 1538 }); 1539 } 1540 // only for BLPLNGI, BLMAS8, IDWS 1541 if ([60, 55, 81].includes(CTId) && this.isGuest && isHeaderPage() && this.name.toLowerCase() === 'afbsb') { 1542 event.preventDefault(); 1543 if ([60].includes(CTId)) // only for BLPLNGI 1544 window.parent.document.getElementsByClassName('home')[0].style.width = '1024px' 1545 if (isFraMainLoaded()) 1546 window.open('_View/RMOdds1.aspx', 'fraMain'); 1547 else 1548 window.open('Body.aspx?href=_View/RMOdds1.aspx', 'fraBody'); 1549 } 1550 else if ([60].includes(CTId) && this.isGuest && isHeaderPage() && (this.name.toLowerCase() === 'ligasb' || this.name.toLowerCase() === 'omsb')) 1551 window.parent.document.getElementsByClassName('home')[0].style.width = '1024px' 1552 } 1553 catch (e) { 1554 console.log(e) 1555 } 1556 } 1557 return { isLigaSbOnly, hasTangkas, cacheVersion, CTId, isGuest, onclickFunc }; 1558 }, 1559 computed: { 1560 formattedClassName() { 1561 let prefix = addPrefix(this.name); 1562 return `${this.className ? this.className : '' 1563 } ${prefix}${this.name.toLowerCase()}-game`; 1564 }, 1565 formattedName() { 1566 return this.name.toLowerCase(); 1567 }, 1568 refQueryStr() { 1569 return getQueryParam('ref') ? '?ref=' + getQueryParam('ref') : ''; 1570 }, 1571 href() { 1572 if (this.isGuest) { 1573 if (this.name.toLowerCase() === 'afbsb') { 1574 if (isHeaderPage()) 1575 return this.hasSubGameSport ? '#' : (this.isLigaSbOnly ? 'LobbyGames.aspx?type=AFBSB' : 'Body.aspx'); 1576 return this.hasSubGameSport ? '#' : this.refQueryStr 1577 } 1578 else if (this.name.toLowerCase() === 'ligasb') { 1579 return isHeaderPage() ? 'LobbyGames.aspx?type=AFBSB' : this.refQueryStr 1580 } 1581 else if (this.name.toLowerCase() === 'omsb') { 1582 return isHeaderPage() ? 'LobbyGames.aspx?type=OMSB' : this.refQueryStr 1583 } 1584 return '#'; 1585 } 1586 else 1587 switch (this.name.toLowerCase()) { 1588 case 'afbsb': 1589 if (isSportOnly(2) && !this.hasSubGameSport) 1590 return 'LobbyGames.aspx?type=AFBSB'; 1591 return this.hasSubGameSport ? '#' : (this.isLigaSbOnly ? 'LobbyGames.aspx?type=AFBSB' : 'Body.aspx'); 1592 case 'ligasb': 1593 return 'LobbyGames.aspx?type=AFBSB'; 1594 case 'omsb': 1595 return 'LobbyGames.aspx?type=OMSB'; 1596 case 'poker': 1597 // tangkas will be moved to param of slot url 1598 case 'tangkas': 1599 return this.hrefs[this.name.toLowerCase()]; 1600 case 'slot': 1601 return 'LobbyGames.aspx?type=' + this.name + (this.hasTangkas ? '&urltks=' + encodeURIComponent(this.hrefs['tangkas']) : ''); 1602 default: 1603 return 'LobbyGames.aspx?type=' + this.name; 1604 } 1605 }, 1606 target() { 1607 return this.href === '#' ? '' : (isHeaderPage() ? 'fraBody' : ''); 1608 }, 1609 hasSubGameSport() { 1610 return JSON.parse(localStorage.getItem('menuMap_user') || localStorage.getItem('menuMap') || '{}')?.['AFBSB']?.[0]?.HasSubGame ?? false; 1611 },
1612 isNewGame() { 1613 return JSON.parse(localStorage.getItem('menuMap_user') || localStorage.getItem('menuMap') || '{}')?.[this.name.toUpperCase()]?.[0]?.IsNewGame ?? false; 1614 }, 1615 styleIconNEW() { 1616 let iconNew = getTopRightIconNEW(this.CTId, this.isGuest); 1617 let top = isNaN(iconNew.top) ? iconNew.top : `${iconNew.top}px` 1618 let strStyle = `filter:none; padding:0; z-index:999999; width: 25px!important; height: 13px;position: absolute; top: ${top}; right: ${iconNew.right}px;` 1619 return strStyle; 1620 }, 1621 styleMenuItem() { 1622 return this.isNewGame ? 'position: relative!important' : '' 1623 } 1624 }, 1625 template: `<a @click="onclickFunc($event)" :href="href" v-bind:target="target || null" :class="formattedClassName" :name="formattedName" :style="styleMenuItem" vue-click="true"> 1626 <img title="new" v-if="isNewGame" 1627 :src="'_View/img/games/new.png?v=' + cacheVersion" 1628 class="blinking-image" 1629 :style="styleIconNEW"> 1630 <slot></slot> 1631 </a>` 1632}; 1633const SubMenuContainer = { 1634 props: ['name', 'className'], 1635 computed: { 1636 id() { 1637 return 'sub-menu-' + this.name.toLowerCase(); 1638 }, 1639 formattedClass() { 1640 return `sub-menu ${this.className || ''}`; 1641 }, 1642 }, 1643 template: ` 1644 <div :id="id" :class="formattedClass"> 1645 <div class="main_width"> 1646 <ul class="gameNav"> 1647 <slot></slot> 1648 </ul> 1649 </div> 1650 </div> 1651 `, 1652}; 1653const SubMenuItem = { 1654 props: ['name', 'type', 'isGuest'], 1655 setup() { 1656 const ligaSbSportSubmenuName = inject('ligaSbSportSubmenuName'); 1657 return { ligaSbSportSubmenuName }; 1658 }, 1659 computed: { 1660 href() { 1661 if (this.isGuest) { 1662 if (this.type.toLowerCase() === 'afbsb') { 1663 if (isHeaderPage()) { 1664 //switch (this.name.toLowerCase()) { 1665 // case 'liga sb': 1666 // case 'liga 365': 1667 // return 'LobbyGames.aspx?type=' + this.type; 1668 // //case 'sport': return '_View/RMOdds1.aspx'; 1669 // default: return '_View/RMOdds1.aspx'; 1670 //} 1671 if (isLigaSbName(this.name, this.ligaSbSportSubmenuName)) 1672 return 'LobbyGames.aspx?type=' + this.type; 1673 return '_View/RMOdds1.aspx'; 1674 } 1675 else { // default page 1676 let hrefMain = getQueryParam('ref') ? 'Main.aspx?ref=' + getQueryParam('ref') : 'Main.aspx' 1677 //switch (this.name.toLowerCase()) { 1678 // case 'liga sb': 1679 // case 'liga 365': 1680 // return hrefMain + (hrefMain.indexOf('?') > -1 ? '&' : '?') + 'launch=ligasb'; 1681 // //case 'sport': return '_View/RMOdds1.aspx'; 1682 // default: return hrefMain; 1683 //} 1684 return hrefMain; 1685 } 1686 } 1687 return '#'; 1688 } 1689 else { 1690 //switch (this.name.toLowerCase()) { 1691 // case 'liga sb': 1692 // case 'liga 365': 1693 // return 'LobbyGames.aspx?type=' + this.type; 1694 // case 'sport': 1695 // return '_View/RMOdds1.aspx'; 1696 // default: 1697 // return '#'; 1698 //} 1699 if (isLigaSbName(this.name, this.ligaSbSportSubmenuName)) 1700 return 'LobbyGames.aspx?type=' + this.type; 1701 else if (this.name.toLowerCase().indexOf('sport') > -1) 1702 return '_View/RMOdds1.aspx'; 1703 return '#'; 1704 } 1705 }, 1706 target() { 1707 return this.href === '#' ? '' : (isHeaderPage() ? 'fraMain' : '_parent'); 1708 }, 1709 }, 1710 template: ` 1711 <li> 1712 <a :type="type" style="background:none!important; position:relative!important" :href="href" v-bind:target="target || null"> 1713 <slot></slot> 1714 {{name}} 1715 </a> 1716 </li> 1717 `, 1718};
1719 1720const SubMenu = { 1721 props: { 1722 className: String, 1723 submenuData: Object, 1724 isGuest: Boolean, 1725 cacheVersion: Number, 1726 hideOnMobileResponsive: { 1727 type: Boolean, 1728 default: false 1729 } 1730 }, 1731 computed: { 1732 isSubMenu() { 1733 return true; 1734 }, 1735 isVisible() { 1736 if (isMobileDevice() && this.hideOnMobileResponsive) 1737 return false; 1738 return this.isGuest ? (this.submenuData[0].HasSubGame ? true : false) : (this.gameType === 'afbsb' ? (this.submenuData[0].HasSubGame ? true : false) : false); 1739 }, 1740 gameType() { 1741 return this.submenuData[0].GameType.toLowerCase(); 1742 } 1743 }, 1744 template: ` 1745 <submenu-container v-if="isVisible" :class-name="className" :name="submenuData[0].GameType"> 1746 <submenu-item v-for="(record, index) in submenuData" 1747 :type="record.GameType" 1748 :is-guest="isGuest" 1749 :name="record.GameName"> 1750 <image-item :image-data="record" :key="index" :is-submenu="isSubMenu" :cache-version="cacheVersion" /> 1751 <img title="hot" v-if="record.IsHotGame" 1752 :src="'css/submenu/hot-icon.gif?v=' + cacheVersion" 1753 style="width: 36px!important;height: 36px;position: absolute;top: -12px;left: 12px;"> 1754 <img class="gacor" title="hot" v-if="record.IsGacorGame" 1755 :src="'_View/img/games/gacor.ico?v=' + cacheVersion" 1756 style="width: 36px!important;height: 36px;position: absolute;top: -12px;left: 12px;"> 1757 <img title="new" v-if="record.IsNewGame" 1758 :src="'_View/img/games/new.png?v=' + cacheVersion" 1759 class="blinking-image" 1760 style="width: 32px!important;height: 15px;position: absolute;top: 0px;left: 10px;"> 1761 </submenu-item> 1762 </submenu-container> 1763 `, 1764}; 1765 1766function createMenu() { 1767 const refText = 'Referral'; 1768 return createApp({ 1769 methods: { 1770 getGameMenuDisplayNameByLang(menuData, lang) { 1771 let name = menuData.GameMenuDisplayName; 1772 if (!lang) 1773 lang = getQueryParam('lang') 1774 switch (lang) { 1775 case 'ZH-CN': name = this.getValidName(menuData, 'GameMenuDisplayNameC'); break; 1776 case 'TH-TH': name = this.getValidName(menuData, 'GameMenuDisplayNameT'); break; 1777 case 'EN-IE': name = this.getValidName(menuData, 'GameMenuDisplayNameV'); break; 1778 case 'EN-TT': name = this.getValidName(menuData, 'GameMenuDisplayNameK'); break; 1779 case 'EN-AU': name = this.getValidName(menuData, 'GameMenuDisplayNameI'); break; 1780 } 1781 return name; 1782 }, 1783 getValidName(menuData, key) { 1784 if (menuData[key] && menuData[key] !== '') 1785 return menuData[key] 1786 return menuData.GameMenuDisplayName; 1787 }, 1788 i(menuData) { 1789 return this.getGameMenuDisplayNameByLang(menuData); 1790 }, 1791 fetching_menu(menuData) { 1792 return this.getGameMenuDisplayNameByLang(menuData); 1793 }, 1794 }, 1795 setup() { 1796 let data = ref({}); 1797 let dataFetched = ref(false); 1798 let appId = ref(''); 1799 let cacheVersion = ref(0); 1800 let isGuest = ref(true); 1801 let hideSubmenuMobileResponsive = ref(false); 1802 let hiddenReferralMenu = ref(false) 1803 let menuUrls = ref({}); 1804 let isLigaSbOnly = ref(false); 1805 let ligaSbSportSubmenuName = ref(''); 1806 let hasTangkas = ref(false); 1807 let CTId = ref(0); 1808 provide('isLigaSbOnly', isLigaSbOnly); 1809 provide('ligaSbSportSubmenuName', ligaSbSportSubmenuName); 1810 provide('hasTangkas', hasTangkas); 1811 provide('cacheVersion', cacheVersion); 1812 provide('CTId', CTId); 1813 provide('isGuest', isGuest); 1814 1815 const fetchMenuData = (callback) => { 1816 const queryString = window.location.search.substring(1); 1817 const apiUrl = 'public/GameGen.ashx?cmd=GetHeaderMenuGames&' + queryString; 1818 $.ajax({
1819 url: apiUrl, 1820 method: 'GET', 1821 contentType: 'application/json', 1822 dataType: 'json', 1823 success: function (response) { 1824 const sportBookType = +(response.sportBookType || 0); 1825 const sportBookTypeList = response.sportBookTypeList || '0'; 1826 const _typeListParsed = sportBookTypeList.split(',').map(s => s.trim()).filter(Boolean); 1827 const sportBookDef = (sportBookTypeList !== '0' && _typeListParsed.length > 0) 1828 ? (+_typeListParsed[0] || 2) 1829 : +(response.sportBookDef || 2); 1830 let menuData = response.menus 1831 if (response && response.success) { 1832 menuData = filterMenuDataBySportBookType(menuData, sportBookTypeList); 1833 if (!isGuest.value) { 1834 if (response.hasReferral && !hiddenReferralMenu.value) { 1835 const lastRecord = menuData.at(-1) 1836 menuData.push({ 1837 ...lastRecord, 1838 Id: lastRecord.Id + 1, 1839 GameType: refText, 1840 GameMenuDisplayName: refText, 1841 GameMenuDisplayNameC: refText, 1842 GameMenuDisplayNameT: refText, 1843 GameMenuDisplayNameV: refText, 1844 GameMenuDisplayNameK: refText, 1845 GameMenuDisplayNameC: refText, 1846 GameMenuDisplayNameI: refText, 1847 }) 1848 } 1849 } 1850 let menuMap = groupBy(menuData, 'GameType'); 1851 localStorage.setItem('sportBookType', sportBookType); 1852 localStorage.setItem('sportBookTypeList', sportBookTypeList); 1853 localStorage.setItem(isGuest.value ? 'menuMap' : 'menuMap_user', JSON.stringify(menuMap)); 1854 if (isGuest.value || !isHeaderPage()) { 1855 delete menuMap['ALLGAMES']; 1856 delete menuMap[refText]; 1857 } 1858 console.log(menuMap) 1859 1860 syncMenuMap(data.value, menuMap); 1861 scheduleHeaderMenuScrollInit(); 1862 dataFetched.value = true; 1863 isLigaSbOnly.value = response.isLigaSbOnly 1864 ligaSbSportSubmenuName.value = menuMap['AFBSB']?.find(game => game.IsLigaSB)?.GameName ?? ""; 1865 ligaSbSportSubmenuNameTW = ligaSbSportSubmenuName.value; 1866 hasTangkas.value = menuMap['SLOT']?.some(game => game.GameName.toUpperCase() === 'TANGKAS') ?? false; 1867 CTId.value = +response.CTId 1868 console.log('hasTangkas.value: %s', hasTangkas.value); 1869 if (typeof callback === 'function') { 1870 callback({ 1871 menuMap, 1872 hasReferral: response.hasReferral, 1873 isSWAccount: response.isSWAccount, 1874 CTId: +response.CTId, 1875 hasGameSubLigaSB: ligaSbSportSubmenuName.value !== '', 1876 isLigaSbOnly: response.isLigaSbOnly, 1877 //ligaSbSportSubmenuName: ligaSbSportSubmenuName.value, 1878 cacheVersion: cacheVersion.value, 1879 hasGamTypeLigaSB: menuMap['LIGASB'] !== undefined, 1880 hasGameNameTangkas: hasTangkas.value, 1881 sportBookDef: sportBookDef, 1882 sportBookType: sportBookType, 1883 sportBookTypeList: sportBookTypeList, 1884 }); 1885 } 1886 } else { 1887 //alert(JSON.stringify(response)); 1888 console.error(JSON.stringify(response)) 1889 } 1890 }, 1891 error: function (error) { 1892 console.error('Error fetchMenuData():', error); 1893 }, 1894 }); 1895 }; 1896 const fetchMenuUrls = (callback) => { 1897 const queryString = window.location.search.substring(1); 1898 const apiUrl = 'public/GameGen.ashx?cmd=GetHeaderMenuUrls&' + queryString; 1899 $.ajax({
1900 url: apiUrl, 1901 method: 'GET', 1902 contentType: 'application/json', 1903 dataType: 'json', 1904 success: function (response) { 1905 if (response && response.success) { 1906 Object.assign(menuUrls.value, response.urls); 1907 if (typeof callback === 'function') { 1908 callback(response); 1909 } 1910 } else { 1911 console.log(response.text) 1912 } 1913 }, 1914 error: function (error) { 1915 console.error('Error fetchMenuUrls() :', error); 1916 }, 1917 }); 1918 }; 1919 const genStyleSubMenu = (menuMap, mainMenuId) => { 1920 let tagParentSubMenu = document 1921 .getElementById(mainMenuId) 1922 .getAttribute('tag-parent-sub-menu'); 1923 let htmlStyles = ``; 1924 for (const gameType in menuMap) { 1925 htmlStyles += `${tagParentSubMenu}.${addPrefix( 1926 gameType 1927 )}${gameType.toLowerCase()}-game:hover > #sub-menu-${gameType.toLowerCase()} {display: inline-block}\r\n`; 1928 } 1929 //console.log(htmlStyles); 1930 const styleElement = document.createElement('style'); 1931 styleElement.textContent = htmlStyles; 1932 document.body.appendChild(styleElement); 1933 }; 1934 function handleMouseOver(e) { 1935 var className = $(e).attr('name'); 1936 var height = $('#sub-menu-' + className).outerHeight(); 1937 if (className === '365slot') $('#sub-menu-365slot').css('padding-bottom', '40px'); 1938 changeHeaderHeight(HEADER_HEIGHT + height); 1939 } 1940 1941 const runScriptSubMenu = (menuMap) => { 1942 var listNameSubMenu = Object.keys(menuMap) 1943 listNameSubMenu = listNameSubMenu.filter(name => (menuMap[name] && menuMap[name].length >= 1 && menuMap[name][0].HasSubGame)) 1944 listNameSubMenu = listNameSubMenu.map(name => `.${addPrefix(name)}${name.toLowerCase().replaceAll(" ", "-")}-game`); 1945 1946 $(`${listNameSubMenu.toString().trim()}`).mouseover(function () { 1947 handleMouseOver($(this)) 1948 }); 1949 $(`${listNameSubMenu.toString().trim()}`).mouseout(function () { 1950 // Fix for desktop site: when mousing over the SPORT submenu again after login, it will show. 1951 // Need to add this statement because the submenu is automatically hidden for both desktop and mobile sites. 1952 // By default, desktop uses CSS to hide/show the submenu. Therefore, hiding the submenu automatically needs to be handled by JavaScript code ($('#sub-menu-afbsb').hide() was called at runScriptUseSport3rdParty()), which will add a style attribute inline "style=display:none". 1953 // Consequently, the submenu will not show when moused over again, as inline CSS takes priority over <style> tags and imported CSS files. Then it("style=display:none".) will be removed to reset everything as the start. 1954 // This handling is deprecated due to the relocation of SPORT 1 and SPORT 2 to the main menu. 1955 $('#sub-menu-afbsb').removeAttr('style'); 1956 1957 changeHeaderHeight(HEADER_HEIGHT) 1958 }); 1959 }; 1960 const runScriptSubMenuDefaultPage = (menuMap) => { 1961 var listNameSubMenu = Object.keys(menuMap) 1962 listNameSubMenu = listNameSubMenu.filter(name => (menuMap[name] && menuMap[name].length >= 1 && menuMap[name][0].HasSubGame)) 1963 listNameSubMenu = listNameSubMenu.map(name => `.${addPrefix(name)}${name.toLowerCase().replaceAll(" ", "-")}-game`); 1964 1965 $(`${listNameSubMenu.toString().trim()}`).mouseover(function () { 1966 var className = $(this).attr('name');
1967 if (className === '365slot') $('#sub-menu-365slot').css('padding-bottom', '40px'); 1968 //console.log('%s: %s', className, height) 1969 }); 1970 }; 1971 1972 // This function is deprecated 1973 // It also addresses the issue where a user clicks on SPORT 2 before the AJAX call completes, 1974 // causing the "click" or "touchstart" event on the menu's anchor tag to misfire. 1975 // Use Vue's directive event handler (@click) with this function instead. 1976 /*const appendSWLigaSBScript = (isLigaSbOnly) => { 1977 $('a').on('click touchstart', function () { 1978 try { 1979 var parent = window.parent.document; 1980 var href = $(this).attr('href') 1981 var target = $(this).attr('target') 1982 //console.log(href) 1983 if (href !== undefined && href !== '#' && target !== '_blank') { 1984 if (href.toLowerCase().indexOf('lobbygames.aspx?type=afbsb') > -1) { 1985 $(parent).find('.divPanel').hide('fast'); 1986 let width = isMobileDevice() ? 'width: 100% !important;' : 'width: 1024px !important;'; 1987 //console.log(width); 1988 $(parent).find('.divMain')[0].style.cssText = width; 1989 } else { 1990 if (isLigaSbOnly) { 1991 let width = isMobileDevice() ? 'width: 100% !important;' : 'width: 1024px !important;'; 1992 //console.log(width); 1993 $(parent).find('.divMain')[0].style.cssText = width; 1994 } 1995 else { 1996 $(parent).find('.divPanel').show('fast'); 1997 $(parent).find('.divMain').css('width', 'calc(100% - 205px)'); 1998 } 1999 // reset 2000 // seem this one is deprecated 2001 localStorage.setItem('ligaSbHost', '') 2002 $('#accTpLst', window.parent.frames["fraHeader"].document).attr('onchange','top.location.href = this.form.accTpLst.options[this.form.accTpLst.selectedIndex].value; return false;') 2003 } 2004 2005 // This handling is deprecated due to the relocation of SPORT 1 and SPORT 2 to the main menu. 2006 // hide submenu of sport-game when click to other game on main menu 2007 if (isHeaderPage()) { 2008 setTimeout(function () { 2009 $('#sub-menu-afbsb').hide(); 2010 changeHeaderHeight(HEADER_HEIGHT) 2011 }, 500) 2012 $('#sub-menu-afbsb').on('touchstart', function (event) { 2013 event.stopPropagation(); 2014 }); 2015 } 2016 2017 var fraMainIframe = parent.querySelector('iframe[name="fraMain"]'); 2018 if (fraMainIframe.onload === null) 2019 fraMainIframe.onload = function () { 2020 if (href.indexOf('LobbyGames.aspx?type=AFBSB') === -1) { 2021 //$(parent).find('.divPanel').show('fast'); 2022 //$(parent).find('.divMain').css('width', 'calc(100% - 205px)'); 2023 } 2024 //fraMainIframe.onload = null; 2025 } 2026 2027 if (isMobileDevice()) 2028 fraMainIframe.src = href; 2029 } 2030 } 2031 catch (e) { 2032 console.log(e) 2033 } 2034 }); 2035 if (isHeaderPage()) { 2036 if (!isLigaSbOnly) { 2037 $('.afbsb-game').on('touchstart', function (event) { 2038 event.preventDefault(); 2039 var subMenu = $(this).find('.sub-menu');
2040 subMenu.toggle(); 2041 if (subMenu.is(':visible')) 2042 handleMouseOver($(this)) 2043 else 2044 changeHeaderHeight(HEADER_HEIGHT) 2045 }); 2046 2047 $('#sub-menu-afbsb').on('touchstart', function (event) { 2048 event.stopPropagation(); 2049 }); 2050 } 2051 } 2052 } */ 2053 2054 // will be deprecated when moving SPORT 2 to main menu is done 2055 const appendTWLigaSBScript = (CTId, cacheVersion) => { 2056 let arrowType = 'text'; 2057 switch (CTId) { 2058 //case 54: 2059 case 64: 2060 case 114: 2061 case 264: 2062 case 265: 2063 arrowType = 'span'; break; 2064 case 52: 2065 //case 57: 2066 case 69: 2067 case 87: 2068 //case 109: 2069 case 123: 2070 case 256: 2071 case 263: 2072 case 279: // SW WL 2073 arrowType = 'div'; break; 2074 case 99: 2075 case 100: 2076 arrowType = 'none'; break; 2077 } 2078 let aTagIsParentMenu = false 2079 switch (CTId) { 2080 case 85: 2081 case 124: 2082 case 135: 2083 case 208: 2084 case 218: 2085 case 227: 2086 case 244: 2087 case 245: 2088 case 260: 2089 aTagIsParentMenu = true; break; 2090 } 2091 var scriptTagArrow = $('<script></script>').attr('type', 'text/javascript').html(`var arrowOption = '${arrowType}'; var aTagIsParentMenu = ${aTagIsParentMenu}`); 2092 $(mountEl).append(scriptTagArrow); 2093 var scriptTagSubmenuGen = $('<script></script>').attr({ 2094 type: 'text/javascript', 2095 src: 'js/header.submenu.gen.js?v=' + cacheVersion 2096 }); 2097 $(mountEl).append(scriptTagSubmenuGen); 2098 } 2099 2100 const isExistReferralAuto = () => { 2101 try { 2102 const listItems = $(mountEl).find('li'); 2103 for (let i = 0; i < listItems.length; i++) { 2104 let aTag = $(listItems[i]).find('a'); 2105 if (aTag && aTag.attr('href') && aTag.attr('href').toLowerCase() === 'lobbygames.aspx?type=referral') { 2106 return true; 2107 } 2108 } 2109 return false; 2110 } catch (error) { 2111 console.log(error); 2112 return false; 2113 } 2114 } 2115 2116 const removeDuplicatedReferralMenu = () => { 2117 $(mountEl).find('li').each(function () { 2118 let aTag = $(this).find('a') 2119 if (aTag && aTag.attr('href') && aTag.attr('href').toLowerCase() === '_view/referral.aspx') { 2120 $(this).remove(); 2121 } 2122 }); 2123 } 2124 // move whitelist WL from Util.Header.Menu.cs function IsActivatedSubMenuSport 2125 const manualAppendLigaSBScriptCTIds = []; 2126 2127 onMounted(() => { 2128 // Save fraBodyFraMainHref to sessionStorage on page unload (F5/navigate away) 2129 // Only if fraMain is same-domain accessible; cross-domain throws and is silently ignored 2130 window.addEventListener('beforeunload', function () { 2131 try { 2132 var fraMain = window.parent.frames['fraBody'].frames['fraMain']; 2133 var href = fraMain.location.href; 2134 if (href && href !== 'about:blank') 2135 sessionStorage.setItem('fraBodyFraMainHref', href); 2136 } catch (e) { /* cross-domain: ignore */ } 2137 }); 2138 2139 try { 2140 // --- Helpers --- 2141 const $qs = (sel, root = document) => root.querySelector(sel); 2142 const $qsa = (sel, root = document) => Array.from(root.querySelectorAll(sel)); 2143 2144 const replaceImgMenuLoading = () => { 2145 $qsa('img.menu-s12-loading').forEach(img => img.remove()); 2146 }; 2147 2148 const goToHome = async (event) => { 2149 event.preventDefault(); 2150 const link = event.currentTarget; 2151 const href = link.getAttribute('href') || ''; 2152 const queryString = href.includes('?') ? href.split('?')[1] : ''; 2153 try { 2154 const resp = await fetch('Handlers/MainGuestHandler.ashx?p=h', { method: 'GET' }); 2155 // bạn vẫn console như cũ 2156 console.log('Server response ok:', resp.ok); 2157 2158 const origin = window.parent.location.origin.replace(/\/+$/, ''); 2159 const pathParts = window.parent.location.pathname.split('/').filter(p => p); 2160 const firstPath = pathParts.length > 0 ? pathParts[0] : ''; 2161 const search = queryString ? '?' + queryString : ''; 2162 window.parent.location.href = `${origin}/${firstPath}${search}`; 2163 } catch (xhr) { 2164 console.error('AJAX error (fetch):', xhr); 2165 } 2166 }; 2167 2168 // --- Local cache / init --- 2169 //const mountEl = document.body; // or root DOM mount of component 2170 const menuMapKey = isGuest.value ? 'menuMap' : 'menuMap_user'; 2171 if (localStorage.getItem(menuMapKey)) { 2172 let menuMap = JSON.parse(localStorage.getItem(menuMapKey)); 2173 menuMap = filterMenuMapBySportBookType(menuMap, getSportBookTypeList()); 2174 if (isGuest.value || !isHeaderPage()) { 2175 delete menuMap['ALLGAMES']; 2176 delete menuMap[refText]; 2177 } 2178 syncMenuMap(data.value, menuMap); 2179 scheduleHeaderMenuScrollInit(); 2180 } 2181 2182 const isSWAccount = localStorage.getItem('isSWAccount') === 'true'; 2183 const CTId = localStorage.getItem('CTId'); 2184 //const cacheVersion = localStorage.getItem('cacheVersion') || Date.now(); 2185 const sportBookDef = +(localStorage.getItem('sportBookDef') || 2); 2186 const ligaSbHost = localStorage.getItem('latestSPORTHost'); 2187 2188 // --- Logged-in branch --- 2189 if (!isGuest.value) { 2190 if (CTId && !isSWAccount) { 2191 2192 //if (!isGuest.value && !isSWAccount && getSportBookType() === 0)
2193 // addSPORT2Menu(CTId, cacheVersion); 2194 2195 removeTANGKAS(); 2196 2197 if (isSWAccount) 2198 replaceImgMenuLoading(); 2199 if (!isSportOnly(1)) { 2200 if (sportBookDef !== 1) { 2201 setTimeout(() => replaceTargetFraBody(), 111); 2202 } 2203 else { 2204 try { 2205 hrefFraBody = parent.frames['fraBody'].location.href; 2206 } 2207 catch (e) { 2208 setTimeout(() => replaceTargetFraBody(), 111); 2209 } 2210 } 2211 } 2212 } 2213 else if (isSWAccount) { 2214 if (!isSportOnly(1)) 2215 setTimeout(() => replaceTargetFraBody(), 111); 2216 2217 // a:not([vue-click]) -> click handler 2218 $qsa('a:not([vue-click])').forEach(a => { 2219 a.addEventListener('click', (event) => { 2220 try { 2221 const href = a.getAttribute('href'); 2222 const target = a.getAttribute('target'); 2223 const hasOnclick = a.getAttribute('onclick') != null; 2224 2225 if (!hasOnclick && target !== '_blank' && target !== '_top' && target !== '_parent') { 2226 event.preventDefault(); 2227 } 2228 2229 if (target === 'fraMain') { 2230 window.open(href, target); 2231 } else if (target === 'fraBody') { 2232 if (isFraMainLoaded()) { 2233 window.open(href, 'fraMain'); 2234 } else { 2235 window.open('Body.aspx?href=' + href + getLpnlParam(), target); 2236 } 2237 } 2238 } catch (e) { 2239 console.log(e); 2240 } 2241 }); 2242 }); 2243 } 2244 } 2245 2246 // --- Guest header home link handling --- 2247 if (isGuest.value && isHeaderPage()) { 2248 const firstLink = $qs('a', mountEl); 2249 const href = firstLink?.getAttribute('href') || ''; 2250 const text = (firstLink?.textContent || '').trim().toLowerCase(); 2251 const isLocalHomePath = /^\/[a-z0-9_]+$/i.test(href); 2252 const isLikelyHomeLink = href === '/' || isLocalHomePath || text === 'home' || text === ''; 2253 2254 if (firstLink && isLikelyHomeLink) { 2255 firstLink.addEventListener('click', goToHome); 2256 } 2257 2258 const anchorHome = $qsa('a[target="_parent"]').filter(a => { 2259 const aHref = a.href; 2260 const baseUrl = window.location.origin + window.location.pathname; 2261 const currentBase = window.location.origin + '/' + window.location.pathname.split('/')[1] + '/'; 2262 return aHref === window.location.origin + '/' || aHref === currentBase; 2263 }); 2264 anchorHome.forEach(a => a.addEventListener('click', goToHome)); 2265 } 2266 if (!isGuest.value && isHeaderPage()) { 2267 var btnSignout = document.querySelector('a[href*="signout.aspx"]'); 2268 if (!btnSignout) return; 2269 btnSignout.addEventListener('click', function (e) { 2270 e.preventDefault(); 2271 try { 2272 var f = window.parent && window.parent.frames && window.parent.frames['fraBody']; 2273 if (f) f.postMessage({ ACT: 'LogoutAll', value: null }, '*'); 2274 } catch (err) { } 2275 //setTimeout(function () { 2276 window.top.location.href = btnSignout.href; 2277 //}, 50); 2278 }); 2279 } 2280 // --- Fetch menu & run scripts --- 2281 fetchMenuData(({ menuMap, isSWAccount: sw, hasReferral, CTId: ct, hasGameSubLigaSB, isLigaSbOnly, cacheVersion: cv, hasGamTypeLigaSB, hasGameNameTangkas }) => { 2282 genStyleSubMenu(menuMap, appId.value); 2283 2284 if (isHeaderPage()) { 2285 if (isGuest.value) 2286 runScriptSubMenu(menuMap); 2287 if (!isGuest.value && !sw) 2288 addAllSportMenus(ct, cv); 2289 2290 setTimeout(() => { 2291 if (!isGuest.value && hasReferral && isExistReferralAuto()) { 2292 removeDuplicatedReferralMenu(); 2293 } 2294 2295 if (!isGuest.value && !sw && !manualAppendLigaSBScriptCTIds.includes(ct)) { 2296 // Submenu 2297 if (hasGameSubLigaSB) { 2298 appendTWLigaSBScript(ct, cv); 2299 } 2300 // Main menu (specific CTs that serve LIGASB from game data) 2301 else if (hasGamTypeLigaSB) { 2302 if (!localStorage.getItem('CTId')) 2303 //|| localStorage.getItem('isSWAccount') === 'true') 2304 { 2305 //addAllSportMenus(ct, cv); 2306 2307 if (!isSportOnly(1)) 2308 replaceTargetFraBody(); 2309 } 2310 removeTANGKAS(); 2311 if (hasGameNameTangkas) 2312 replaceSLOTUrl(); 2313 } 2314 } 2315 // SW WLs / guest-like 2316 else if (isGuest.value || (sw && !localStorage.getItem('isSWAccount'))) { 2317 if (!isSportOnly(1)) 2318 replaceTargetFraBody(); 2319 $qsa('a:not([vue-click])').forEach(a => { 2320 const handler = (event) => { 2321 try { 2322 const href = a.getAttribute('href'); 2323 const target = a.getAttribute('target'); 2324 const hasOnclick = a.getAttribute('onclick') != null; 2325 2326 if (!hasOnclick && target !== '_blank' && target !== '_top' && target !== '_parent') { 2327 event.preventDefault(); 2328 } 2329 if (target === 'fraMain') { 2330 window.open(href, target); 2331 } else if (target === 'fraBody') { 2332 if (isFraMainLoaded()) { 2333 window.open(href, 'fraMain'); 2334 } else { 2335 window.open('Body.aspx?href=' + href + getLpnlParam(), target); 2336 } 2337 } 2338 } catch (e) { 2339 console.log(e); 2340 } 2341 }; 2342 a.addEventListener('click', handler); 2343 a.addEventListener('touchstart', handler, { passive: false }); 2344 }); 2345 } 2346 2347 localStorage.setItem('CTId', ct); 2348 localStorage.setItem('cacheVersion', cv); 2349 localStorage.setItem('isSWAccount', sw); 2350 localStorage.setItem('sportBookDef', sportBookDef); 2351 // Init selected sport from default if not already set this session 2352 if (!sessionStorage.getItem('sportBookSelected')) 2353 setSelectedSport(sportBookDef); 2354 applyAddMHClones(); 2355 scheduleHeaderMenuScrollInit(); 2356 }, 555); 2357 } else { 2358 runScriptSubMenuDefaultPage(menuMap); 2359 applyAddMHClones(); 2360 scheduleHeaderMenuScrollInit(); 2361 } 2362 }); 2363 2364 // --- After login always run at header page --- 2365 if (!isGuest.value) { 2366 // document ready as DOMContentLoaded (with Vue, onMounted was safe) 2367 fetchMenuUrls(); 2368 replaceImgMenuLoading(); 2369 getMailInfo(); 2370 2371 // accTpLst trong iframe header 2372 const fraHeader = parent.frames['fraHeader']; 2373 const fraHeaderDocument = fraHeader?.document || null; 2374 2375 if (fraHeaderDocument) { 2376 const accSel = fraHeaderDocument.querySelector('#accTpLst'); 2377 const ch = 'BroadcastChannel' in window ? new BroadcastChannel('accTypeChannel') : null; 2378 // Unique ID for each page to prevent message echo loops 2379 const PAGE_ID = (crypto?.randomUUID?.() || Math.random().toString(36).slice(2)); 2380 ch.onmessage = (event) => { 2381 const data = event.data || {}; 2382 if (data?.ACT === 'switch_acctype_fraheader') { 2383 if (data.source === PAGE_ID) return; // ignore self messages 2384 const isLocal = location.host.includes('localhost'); 2385 let hostValue = isLocal ? location.pathname.replace(/\/Header\.aspx$/i, '') : ''; 2386 try { 2387 accSel.value = `${hostValue}/?accType=${data.value}`; 2388 accSel.dispatchEvent(new Event('change', { bubbles: true })); 2389 } 2390 catch (e) { console.log(e) } 2391 } 2392 console.log('[BC] Home received:', data); 2393 }; 2394 if (accSel) { 2395 accSel.removeAttribute('onchange'); 2396 accSel.addEventListener('change', (e) => { 2397 const raw = accSel.value || ''; 2398 const accType = raw.slice(-2); 2399 let hrefFraBody = ''; 2400 try { 2401 // check use is using SPORT 1 or SPORT 2,3 2402 hrefFraBody = parent.frames['fraBody'].location.href; 2403 const lpnl = new URLSearchParams(hrefFraBody).get("lpnl"); 2404 let gameType = ''; 2405 // if SPORT 1 is using. . Case fraBody href is SPORT 1 2406 try { 2407 // check fraMain iframe is accessible (same domain) (sometimes POKER from 3rd party URL) 2408 const hrefMain = parent.frames['fraBody'].frames['fraMain'].location.href; 2409 try { 2410 // handler for SPORT 1 same domain fraMain iframe 2411 const fraPanel = parent.frames['fraBody'].frames['fraPanel']; 2412 const hrefPanel = fraPanel.location.href; 2413 parent.frames['fraBody'].frames['fraMain'].location.href = `_View/RMOdds1.aspx?accType=${accType}` 2414 } 2415 catch (err) { 2416 // handle for SPORT 2/3 same domain fraMain iframe (fraMain page as Dep, Wash...) 2417 switch (lpnl) { 2418 case "lsb": gameType = "AFBSB"; break; 2419 case "omsb": gameType = "OMSB"; break; 2420 } 2421 document.querySelector(`a[href="LobbyGames.aspx?type=${gameType}"]`)?.click(); 2422 console.log('Can not access DOM fraPanel:', err); 2423 } 2424 const programmatic = e.isTrusted === false; 2425 if (!programmatic && ch) { 2426 // User-triggered â broadcast change to other pages 2427 let data = {
2428 ACT: 'switch_acctype_framain', 2429 value: accType, 2430 source: PAGE_ID, 2431 ts: Date.now() 2432 } 2433 ch.postMessage(data); 2434 console.log('[BC] Home sent:', data); 2435 } 2436 } 2437 catch (e) { 2438 // fraMain iframe is not accessible (diff domain) 2439 try { 2440 // handler for SPORT 1 different domain fraMain iframe 2441 const fraPanel = parent.frames['fraBody'].frames['fraPanel']; 2442 const hrefPanel = fraPanel.location.href; 2443 parent.frames['fraBody'].frames['fraMain'].location.href = `_View/RMOdds1.aspx?accType=${accType}` 2444 } 2445 catch (err) { 2446 // handler for SPORT 2/3 different domain fraMain iframe as FISH POKER, TOGEL ... 3rd party URL 2447 switch (lpnl) { 2448 case "lsb": gameType = "AFBSB"; break; 2449 case "omsb": gameType = "OMSB"; break; 2450 } 2451 document.querySelector(`a[href="LobbyGames.aspx?type=${gameType}"]`)?.click(); 2452 console.log('Can not access DOM fraPanel:', err); 2453 } 2454 console.log('Can not access DOM fraMain:', e); 2455 } 2456 } catch (e) { 2457 // handle for SPORT 2 same domain 2458 if (hrefFraBody === '') { 2459 let latestSPORTHost = localStorage.getItem('latestSPORTHost'); 2460 console.log('changeOddStyle via postMessage to:', latestSPORTHost); 2461 const sentObj = { ACT: 'ChangeOddsType', value: accType }; 2462 // parent.frames['fraBody'].postMessage(JSON.stringify(sentObj), 'https://' + latestSPORTHost); 2463 // change to * for working well on multi tab with multi sport page 2464 parent.frames['fraBody'].postMessage(JSON.stringify(sentObj), '*'); 2465 } 2466 else { 2467 // handle for SPORT 1 different domain 2468 // todo : writedown test case for this scenario, don't rmb how to trigger this case 2469 if (hrefFraBody !== '') 2470 parent.frames['fraBody'].frames['fraMain'].location.href = `_View/RMOdds1.aspx?accType=${accType}` 2471 else // handle for SPORT 2 different domain 2472 document.querySelector('a[href="LobbyGames.aspx?type=AFBSB"]')?.click(); 2473 } 2474 } 2475 }); 2476 } 2477 } 2478 } 2479 2480 // --- Language change (header page) --- 2481 if (isHeaderPage()) { 2482 const fraHeader = parent.frames['fraHeader']; 2483 const fraHeaderDocument = fraHeader?.document || null; 2484 2485 if (fraHeaderDocument) { 2486 const langEls = $qsa('#langAfter, #lstLang, #lstLang2', fraHeaderDocument);
2487 langEls.forEach(el => el.removeAttribute('onchange')); 2488 langEls.forEach(el => { 2489 el.addEventListener('change', () => { 2490 let lang = (fraHeaderDocument.querySelector('#langAfter')?.value 2491 || fraHeaderDocument.querySelector('#lstLang')?.value 2492 || fraHeaderDocument.querySelector('#lstLang2')?.value || '').slice(-5); 2493 2494 let hrefFraBody = ''; 2495 try { 2496 hrefFraBody = parent.frames['fraBody'].location.href; 2497 const lpnl = new URLSearchParams(hrefFraBody).get("lpnl"); 2498 let gameType = ''; 2499 // Handle for SPORT 1 is using. Case fraBody href is SPORT 1 2500 try { 2501 // check fraMain iframe is accessible (same domain) (sometimes POKER from 3rd party URL) 2502 const hrefMain = parent.frames['fraBody'].frames['fraMain'].location.href; 2503 // if hrefMain is accessible, continue from here 2504 const fraPanel = parent.frames['fraBody'].frames['fraPanel']; 2505 try { 2506 // handle for SPORT 1 left panel and fraMain is 3rd party URL as POKER, TOGEL ... 2507 const url = fraPanel.location.href; 2508 window.parent.postMessage({ ACT: 'switch_lang', LANG: lang }, '*'); 2509 2510 } catch (e) { 2511 2512 // handle for SPORT 2,3 left panel and fraMain is 3rd party URL as POKER, TOGEL ... 2513 console.log('Can not access DOM fraPanel:', e); 2514 switch (lpnl) { 2515 case "lsb": gameType = "AFBSB"; break; 2516 case "omsb": gameType = "OMSB"; break; 2517 } 2518 fraPanel.location.href = `LobbyGames.aspx?type=${gameType}&hodds=1&lang=` + lang; 2519 window.parent.postMessage({ ACT: 'switch_lang_framain', LANG: lang }, '*'); 2520 } 2521 } 2522 catch (e) { 2523 // Handle for SPORT 2,3 is using. Case fraBody href is SPORT 2, 3 2524 // fraMain iframe is not accessible (diff domain) 2525 try { 2526 // handle for SPORT 1 different domain fraMain iframe 2527 const fraPanel = parent.frames['fraBody'].frames['fraPanel']; 2528 const hrefPanel = fraPanel.location.href; // this is statement to test to break to catch or not 2529 window.parent.postMessage({ ACT: 'switch_lang', LANG: lang }, '*'); 2530 } 2531 catch (err) { 2532 // handle for SPORT 2,3 different domain fraMain iframe 2533 console.log('Can not access DOM fraPanel:', err); 2534 let gameType = ''; 2535 switch (lpnl) { 2536 case "lsb": gameType = "AFBSB"; break; 2537 case "omsb": gameType = "OMSB"; break; 2538 } 2539 parent.frames['fraBody'].frames['fraPanel'].location.href = 'LobbyGames.aspx?type=' + gameType + '&hodds=1&lang=' + lang; 2540 window.parent.postMessage({ ACT: 'switch_lang_framain', LANG: lang }, '*'); 2541 } 2542 console.log('Can not access DOM fraMain:', e); 2543 } 2544 } catch (e) { 2545 // if SPORT 2 is using. Case fraBody href is SPORT 2, 3 2546 try { 2547 //console.log('ChangeLanguage via postMessage to:', ligaSbHost); 2548 //console.log('hrefFraBody:', hrefFraBody); 2549 //console.log('ligaSbHost:', ligaSbHost); 2550 2551 if (hrefFraBody === '') { 2552 let latestSPORTHost = localStorage.getItem('latestSPORTHost'); 2553 console.log('changelang via postMessage to:', latestSPORTHost); 2554 const sentObj = { ACT: 'ChangeLanguage', value: lang }; 2555 //window.parent.frames['fraBody'].postMessage(JSON.stringify(sentObj), 'https://' + latestSPORTHost); 2556 // change to * for working well on multi tab with multi sport page 2557 window.parent.frames['fraBody'].postMessage(JSON.stringify(sentObj), '*'); 2558 } else { 2559 // todo : writedown test case for this scenario, don't rmb how to trigger this case 2560 parent.frames['fraBody'].frames['fraPanel'].location.href = 'LobbyGames.aspx?type=AFBSB&hodds=1&lang=' + lang; 2561 window.parent.postMessage({ ACT: 'switch_lang_framain', LANG: lang }, '*'); 2562 } 2563 //window.parent.frames['fraHeader'].location.href = `Header.aspx?lang=${lang}`; 2564 2565 } catch (err) { 2566 console.log(err); 2567 } 2568 } 2569 window.location.href = 'Header.aspx?lang=' + lang 2570 2571 }); 2572 }); 2573 } 2574 } 2575 // --- guest common handling --- 2576 //$(atob('I2hlbHAtbGluaw==')).on('click', function (e) { 2577 // $.ajax({
2578 // type: "POST", 2579 // url: atob('SGFuZGxlcnMvTExDSGFuZGxlci5hc2h4'), 2580 // }); 2581 //}); 2582 } catch (error) { 2583 console.log(error); 2584 } 2585 }); 2586 2587 onBeforeMount(() => { 2588 appId.value = mountEl.dataset.id; 2589 isGuest.value = mountEl.dataset.isGuest === "true"; 2590 2591 if (mountEl.dataset.hideSubmenuMobileResponsive) 2592 hideSubmenuMobileResponsive.value = mountEl.dataset.hideSubmenuMobileResponsive === "true" 2593 if (mountEl.dataset.hiddenReferralMenu) 2594 hiddenReferralMenu.value = mountEl.dataset.hiddenReferralMenu === "true" 2595 2596 cacheVersion.value = +getTimestampServerCache(); 2597 //console.log(mountEl.dataset.id); 2598 //console.log(mountEl.dataset.isGuest); 2599 //console.log(mountEl.dataset.hideSubmenuMobileResponsive); 2600 //console.log(hideSubmenuMobileResponsive.value); 2601 }); 2602 const computedProps = {}; 2603 return { 2604 menus: data.value, 2605 menuUrls: menuUrls.value, 2606 dataFetched, 2607 ...computedProps, 2608 cacheVersion, 2609 isGuest, 2610 hideSubmenuMobileResponsive, 2611 hiddenReferralMenu, 2612 }; 2613 }, 2614 }); 2615} 2616var menu = createMenu(); 2617menu.component('image-item', ImageItem); 2618menu.component('down-arrow', DownArrow); 2619menu.component('menu-item', MenuItem); 2620menu.component('submenu-container', SubMenuContainer); 2621menu.component('submenu-item', SubMenuItem); 2622menu.component('submenu', SubMenu);
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.