Files
justice/www/html/Game/public/js/profile-popup.js
T

497 lines
30 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
(function () {
'use strict';
var script = document.currentScript;
var triggerSelector = (script && script.getAttribute('data-profile-trigger')) || '#btn-myprofile,#btn-profile-set';
var assetBase = (script && script.getAttribute('data-profile-asset-base')) || 'img/03-6-Profile';
var DISPLAY_NAME_KEY = 'roomLobbyDisplayName';
var overlayId = 'room-lobby-profile-overlay';
if (typeof window !== 'undefined') window.__USE_SHARED_PROFILE_POPUP = true;
function resolveAssetBase(raw) {
var base = String(raw || 'img/03-6-Profile').replace(/\/$/, '');
if (/^\/(Game|img)\//i.test(base)) {
return typeof window.appPath === 'function' ? window.appPath(base) : base;
}
try {
return new URL(base + '/', window.location.href).pathname.replace(/\/$/, '');
} catch (e) {
return base;
}
}
var resolvedAssetBase = resolveAssetBase(assetBase);
function asset(name) {
return resolvedAssetBase + '/' + name;
}
function appRel(path) {
if (typeof window.appPath === 'function') return window.appPath(path);
return path;
}
function readName() {
try { return (localStorage.getItem(DISPLAY_NAME_KEY) || localStorage.getItem('playerName') || 'MONE').trim() || 'MONE'; } catch (e) { return 'MONE'; }
}
function readAgentId() {
try { return (localStorage.getItem('agentDisplayId') || '001998').trim() || '001998'; } catch (e) { return '001998'; }
}
function readCoins() {
try { return String(Math.max(0, parseInt(localStorage.getItem('jdCoins') || '0', 10) || 0)); } catch (e) { return '0'; }
}
function readAgentIdRaw() {
try { return (localStorage.getItem('agentDisplayId') || '').trim(); } catch (e) { return ''; }
}
/** key ผู้เล่น (เดียวกับ lobby.js) — สร้างถ้ายังไม่มี เพื่อให้ระบบรหัส sync ใช้ได้ */
function ensurePlayerKey() {
var k = '';
try { k = (localStorage.getItem('jdPlayerKey') || '').trim(); } catch (e) { k = ''; }
if (!k || k.length < 8) {
k = 'p_' + Date.now() + '_' + Math.random().toString(36).slice(2, 14);
try { localStorage.setItem('jdPlayerKey', k); } catch (e2) { /* ignore */ }
}
return k;
}
var refs = {
overlay: null,
backdrop: null,
dialog: null,
closeBtn: null,
avatarEl: null,
nameEl: null,
agentEl: null,
coinsEl: null,
musicBtn: null,
sfxBtn: null,
groupBtns: [],
archivListEl: null,
archivScrollEl: null,
archivScrollBarEl: null,
editBtn: null,
logoutBtn: null
};
function createMarkupIfNeeded() {
if (document.getElementById(overlayId)) return;
var overlay = document.createElement('div');
overlay.id = overlayId;
overlay.className = 'room-lobby-profile-overlay is-hidden';
overlay.setAttribute('aria-hidden', 'true');
overlay.innerHTML = '' +
'<div class="room-lobby-profile-backdrop" id="room-lobby-profile-backdrop" aria-hidden="true"></div>' +
'<div class="room-lobby-profile-dialog" role="dialog" aria-modal="true" aria-label="โปรไฟล์ผู้เล่น">' +
' <button type="button" class="room-lobby-profile-close" id="room-lobby-profile-close" aria-label="ปิดโปรไฟล์">×</button>' +
' <div class="room-lobby-profile-content">' +
' <section class="room-lobby-profile-left-pane">' +
' <div class="room-lobby-profile-inner-frame">' +
' <p class="room-lobby-profile-coins">' +
' <img src="' + asset('profile-pop-coin.png') + '" alt="My Coins" class="room-lobby-profile-coins-label" decoding="async">' +
' <strong id="room-lobby-profile-coins-val">150</strong>' +
' </p>' +
' <div class="room-lobby-profile-char-card">' +
' <img id="room-lobby-profile-overlay-avatar" src="" alt="ตัวละครผู้เล่น" decoding="async">' +
' <div class="room-lobby-profile-overlay-name-row">' +
' <div class="room-lobby-profile-overlay-name" id="room-lobby-profile-overlay-name">PLAYER</div>' +
' <button type="button" class="room-lobby-profile-edit-btn" id="room-lobby-profile-edit-btn" title="แก้ไขชื่อผู้เล่น" aria-label="แก้ไขชื่อผู้เล่น">' +
' <img src="' + asset('btn-edit.png') + '" alt="" decoding="async">' +
' </button>' +
' </div>' +
' <img src="' + asset('profile-char-frame-2.png') + '" alt="" class="room-lobby-profile-name-sep" decoding="async">' +
' <div class="room-lobby-profile-overlay-agent" id="room-lobby-profile-overlay-agent">AGENT ID : 001998</div>' +
' </div>' +
' </div>' +
' <button type="button" class="room-lobby-profile-logout" id="room-lobby-profile-logout" aria-label="ออกจากระบบ">' +
' <img src="' + asset('btn-ex.png') + '" alt="ออกจากระบบ" decoding="async">' +
' </button>' +
' </section>' +
' <section class="room-lobby-profile-right-pane">' +
' <div class="room-lobby-profile-archiv-groups" id="room-lobby-profile-archiv-groups">' +
' <button type="button" class="room-lobby-profile-archiv-group-btn is-active" data-group="1"><img src="' + asset('archiv-group-01-a.png') + '" alt=""></button>' +
' <button type="button" class="room-lobby-profile-archiv-group-btn" data-group="2"><img src="' + asset('archiv-group-02.png') + '" alt=""></button>' +
' <button type="button" class="room-lobby-profile-archiv-group-btn" data-group="3"><img src="' + asset('archiv-group-03.png') + '" alt=""></button>' +
' <button type="button" class="room-lobby-profile-archiv-group-btn" data-group="4"><img src="' + asset('archiv-group-04.png') + '" alt=""></button>' +
' <button type="button" class="room-lobby-profile-archiv-group-btn" data-group="5"><img src="' + asset('archiv-group-05.png') + '" alt=""></button>' +
' </div>' +
' <div class="room-lobby-profile-archiv-frame">' +
' <ul class="room-lobby-profile-achievements-list" id="room-lobby-profile-achievements-list">' +
' <li class="room-lobby-profile-archiv-item"><img class="room-lobby-profile-archiv-icon-bg" src="' + asset('archiv-icon-bg.png') + '" alt=""><div class="room-lobby-profile-archiv-main"><div class="room-lobby-profile-archiv-title-row"><img class="room-lobby-profile-archiv-name" src="' + asset('archiv-name-1.png') + '" alt=""><strong class="room-lobby-profile-archiv-count">1/1</strong></div><div class="room-lobby-profile-archiv-progress-wrap"><img class="room-lobby-profile-archiv-progress-bg" src="' + asset('archiv-progress-bg.png') + '" alt=""><img class="room-lobby-profile-archiv-progress" src="' + asset('archiv-progress.png') + '" alt="" style="--archiv-progress:100%"></div><img class="room-lobby-profile-archiv-line" src="' + asset('archiv-line.png') + '" alt=""></div></li>' +
' <li class="room-lobby-profile-archiv-item"><img class="room-lobby-profile-archiv-icon-bg" src="' + asset('archiv-icon-bg.png') + '" alt=""><div class="room-lobby-profile-archiv-main"><div class="room-lobby-profile-archiv-title-row"><img class="room-lobby-profile-archiv-name" src="' + asset('archiv-name-2.png') + '" alt=""><strong class="room-lobby-profile-archiv-count">2/5</strong></div><div class="room-lobby-profile-archiv-progress-wrap"><img class="room-lobby-profile-archiv-progress-bg" src="' + asset('archiv-progress-bg.png') + '" alt=""><img class="room-lobby-profile-archiv-progress" src="' + asset('archiv-progress.png') + '" alt="" style="--archiv-progress:40%"></div><img class="room-lobby-profile-archiv-line" src="' + asset('archiv-line.png') + '" alt=""></div></li>' +
' <li class="room-lobby-profile-archiv-item"><img class="room-lobby-profile-archiv-icon-bg" src="' + asset('archiv-icon-bg.png') + '" alt=""><div class="room-lobby-profile-archiv-main"><div class="room-lobby-profile-archiv-title-row"><img class="room-lobby-profile-archiv-name" src="' + asset('archiv-name-3.png') + '" alt=""><strong class="room-lobby-profile-archiv-count">3/10</strong></div><div class="room-lobby-profile-archiv-progress-wrap"><img class="room-lobby-profile-archiv-progress-bg" src="' + asset('archiv-progress-bg.png') + '" alt=""><img class="room-lobby-profile-archiv-progress" src="' + asset('archiv-progress.png') + '" alt="" style="--archiv-progress:30%"></div><img class="room-lobby-profile-archiv-line" src="' + asset('archiv-line.png') + '" alt=""></div></li>' +
' <li class="room-lobby-profile-archiv-item"><img class="room-lobby-profile-archiv-icon-bg" src="' + asset('archiv-icon-bg.png') + '" alt=""><div class="room-lobby-profile-archiv-main"><div class="room-lobby-profile-archiv-title-row"><img class="room-lobby-profile-archiv-name" src="' + asset('archiv-name-1.png') + '" alt=""><strong class="room-lobby-profile-archiv-count">4/10</strong></div><div class="room-lobby-profile-archiv-progress-wrap"><img class="room-lobby-profile-archiv-progress-bg" src="' + asset('archiv-progress-bg.png') + '" alt=""><img class="room-lobby-profile-archiv-progress" src="' + asset('archiv-progress.png') + '" alt="" style="--archiv-progress:40%"></div><img class="room-lobby-profile-archiv-line" src="' + asset('archiv-line.png') + '" alt=""></div></li>' +
' <li class="room-lobby-profile-archiv-item"><img class="room-lobby-profile-archiv-icon-bg" src="' + asset('archiv-icon-bg.png') + '" alt=""><div class="room-lobby-profile-archiv-main"><div class="room-lobby-profile-archiv-title-row"><img class="room-lobby-profile-archiv-name" src="' + asset('archiv-name-2.png') + '" alt=""><strong class="room-lobby-profile-archiv-count">5/10</strong></div><div class="room-lobby-profile-archiv-progress-wrap"><img class="room-lobby-profile-archiv-progress-bg" src="' + asset('archiv-progress-bg.png') + '" alt=""><img class="room-lobby-profile-archiv-progress" src="' + asset('archiv-progress.png') + '" alt="" style="--archiv-progress:50%"></div><img class="room-lobby-profile-archiv-line" src="' + asset('archiv-line.png') + '" alt=""></div></li>' +
' </ul>' +
' <div class="room-lobby-profile-archiv-scroll is-hidden"><div class="room-lobby-profile-archiv-scroll-bg"></div><div class="room-lobby-profile-archiv-scroll-bar"></div></div>' +
' </div>' +
' <div class="room-lobby-profile-settings"><img class="room-lobby-profile-settings-frame" src="' + asset('setting-frame.png') + '" alt=""><div class="room-lobby-profile-settings-row">' +
' <div class="room-lobby-profile-setting-item"><img class="room-lobby-profile-setting-label" src="' + asset('setting-txt-1.png') + '" alt="Music"><button type="button" class="room-lobby-profile-toggle" id="room-lobby-profile-music" data-on="true"><img src="' + asset('btn-on.png') + '" alt=""></button></div>' +
' <div class="room-lobby-profile-setting-item"><img class="room-lobby-profile-setting-label" src="' + asset('setting-txt-2.png') + '" alt="SFX"><button type="button" class="room-lobby-profile-toggle" id="room-lobby-profile-sfx" data-on="false"><img src="' + asset('btn-off.png') + '" alt=""></button></div>' +
' </div></div>' +
' </section>' +
' </div>' +
'</div>';
document.body.appendChild(overlay);
}
function bindRefs() {
refs.overlay = document.getElementById(overlayId);
refs.backdrop = document.getElementById('room-lobby-profile-backdrop');
refs.dialog = refs.overlay ? refs.overlay.querySelector('.room-lobby-profile-dialog') : null;
refs.closeBtn = document.getElementById('room-lobby-profile-close');
refs.avatarEl = document.getElementById('room-lobby-profile-overlay-avatar');
refs.nameEl = document.getElementById('room-lobby-profile-overlay-name');
refs.agentEl = document.getElementById('room-lobby-profile-overlay-agent');
refs.coinsEl = document.getElementById('room-lobby-profile-coins-val');
refs.musicBtn = document.getElementById('room-lobby-profile-music');
refs.sfxBtn = document.getElementById('room-lobby-profile-sfx');
refs.groupBtns = Array.prototype.slice.call(document.querySelectorAll('.room-lobby-profile-archiv-group-btn'));
refs.archivListEl = document.getElementById('room-lobby-profile-achievements-list');
refs.archivScrollEl = document.querySelector('.room-lobby-profile-archiv-scroll');
refs.archivScrollBarEl = document.querySelector('.room-lobby-profile-archiv-scroll-bar');
refs.editBtn = document.getElementById('room-lobby-profile-edit-btn');
refs.logoutBtn = document.getElementById('room-lobby-profile-logout');
}
function syncArchivScroll() {
if (!refs.archivListEl || !refs.archivScrollEl || !refs.archivScrollBarEl) return;
var total = refs.archivListEl.scrollHeight || 0;
var visible = refs.archivListEl.clientHeight || 0;
var overflow = total > visible + 1;
refs.archivScrollEl.classList.toggle('is-hidden', !overflow);
if (!overflow) return;
var max = Math.max(1, total - visible);
var ratio = Math.max(0, Math.min(1, refs.archivListEl.scrollTop / max));
var barPct = Math.max(18, Math.min(42, (visible / Math.max(1, total)) * 100));
refs.archivScrollBarEl.style.height = barPct.toFixed(2) + '%';
refs.archivScrollBarEl.style.top = ((100 - barPct) * ratio).toFixed(2) + '%';
}
function syncProfileScale() {
if (!refs.dialog) return;
var rect = refs.dialog.getBoundingClientRect();
if (!rect.width || !rect.height) return;
var scaleW = rect.width / 1701;
var scaleH = rect.height / 951;
var scale = Math.max(0.35, Math.min(1, Math.min(scaleW, scaleH)));
refs.dialog.style.setProperty('--rp-scale', String(scale.toFixed(4)));
}
function setGroup(index) {
refs.groupBtns.forEach(function (btn) {
var id = parseInt(btn.getAttribute('data-group'), 10) || 1;
var active = id === index;
btn.classList.toggle('is-active', active);
var img = btn.querySelector('img');
if (img) img.src = active ? asset('archiv-group-0' + id + '-a.png') : asset('archiv-group-0' + id + '.png');
});
if (window.jdAchievements && refs.archivListEl) {
window.jdAchievements.renderGroup(refs.archivListEl, index);
}
syncArchivScroll();
}
function toggleChip(btn) {
if (!btn) return;
var on = String(btn.getAttribute('data-on') || '').toLowerCase() === 'true';
on = !on;
btn.setAttribute('data-on', on ? 'true' : 'false');
var img = btn.querySelector('img');
if (img) img.src = on ? asset('btn-on.png') : asset('btn-off.png');
}
function setProfileData(data) {
var payload = data || {};
var lobbyAvatar = document.getElementById('lobby-profile-avatar');
var roomAvatar = document.getElementById('room-lobby-profile-avatar');
var src = payload.avatarSrc || (lobbyAvatar && lobbyAvatar.getAttribute('src')) || (roomAvatar && roomAvatar.getAttribute('src')) || '';
if (refs.avatarEl && src) refs.avatarEl.src = src;
if (refs.nameEl) refs.nameEl.textContent = (payload.displayName || readName()).toUpperCase();
if (refs.agentEl) refs.agentEl.textContent = payload.agentIdLabel || ('AGENT ID : ' + readAgentId());
if (refs.coinsEl) refs.coinsEl.textContent = String(payload.coins != null ? payload.coins : readCoins());
}
function openPopup(data) {
if (!refs.overlay) return;
if (window.jdAchievements) window.jdAchievements.load();
setProfileData(data);
refs.overlay.classList.remove('is-hidden');
refs.overlay.setAttribute('aria-hidden', 'false');
syncProfileScale();
syncArchivScroll();
if (refs.closeBtn) refs.closeBtn.focus();
}
function closePopup() {
if (!refs.overlay) return;
refs.overlay.classList.add('is-hidden');
refs.overlay.setAttribute('aria-hidden', 'true');
}
function bindEvents() {
if (refs.backdrop) refs.backdrop.addEventListener('click', closePopup);
if (refs.closeBtn) refs.closeBtn.addEventListener('click', closePopup);
if (refs.musicBtn) refs.musicBtn.addEventListener('click', function () { toggleChip(refs.musicBtn); });
if (refs.sfxBtn) refs.sfxBtn.addEventListener('click', function () { toggleChip(refs.sfxBtn); });
refs.groupBtns.forEach(function (btn) {
btn.addEventListener('click', function () { setGroup(parseInt(btn.getAttribute('data-group'), 10) || 1); });
});
if (refs.archivListEl) refs.archivListEl.addEventListener('scroll', syncArchivScroll, { passive: true });
if (refs.editBtn) refs.editBtn.addEventListener('click', function () {
var runApply = function (draft) {
if (draft == null) return;
var applyFn = typeof window.jdApplyDisplayNameChange === 'function'
? window.jdApplyDisplayNameChange
: null;
if (applyFn) {
var result = applyFn(draft, { socket: window.__jdGameSocket || null });
if (!result.ok) return;
if (refs.nameEl) refs.nameEl.textContent = result.displayName.toUpperCase();
return;
}
try {
localStorage.setItem(DISPLAY_NAME_KEY, draft);
localStorage.setItem('playerName', draft);
} catch (e) { /* ignore */ }
if (refs.nameEl) refs.nameEl.textContent = draft.toUpperCase();
var headerName = document.getElementById('lobby-profile-name');
if (headerName) headerName.textContent = draft.toUpperCase();
};
if (window.jdDisplayName && typeof window.jdDisplayName.openEditor === 'function') {
window.jdDisplayName.openEditor({
initialValue: typeof window.jdReadDisplayName === 'function' ? window.jdReadDisplayName() : readName()
}).then(runApply);
return;
}
if (typeof window.jdPromptAndApplyDisplayName === 'function') {
window.jdPromptAndApplyDisplayName({ socket: window.__jdGameSocket || null }).then(function (result) {
if (result && result.ok && refs.nameEl) refs.nameEl.textContent = result.displayName.toUpperCase();
});
return;
}
var draft = window.prompt('แก้ไขชื่อผู้เล่น', readName() || '');
runApply(draft == null ? null : String(draft).trim() || null);
});
if (refs.logoutBtn) refs.logoutBtn.addEventListener('click', function () {
if (document.getElementById('btn-back')) {
window.location.href = appRel('/Login/');
return;
}
window.location.href = appRel('/Create%20Room/');
});
document.addEventListener('keydown', function (e) {
if (e.key !== 'Escape') return;
var nameEditor = document.getElementById('jd-display-name-editor-overlay');
if (nameEditor && !nameEditor.classList.contains('is-hidden')) return;
if (refs.overlay && !refs.overlay.classList.contains('is-hidden')) closePopup();
});
window.addEventListener('resize', function () {
syncProfileScale();
syncArchivScroll();
});
document.querySelectorAll(triggerSelector).forEach(function (el) {
el.addEventListener('click', function (e) {
e.preventDefault();
openPopup();
});
});
}
/* ===== ระบบรหัสผู้เล่น — sync โปรไฟล์ (สี/COINS/ชื่อ) ข้าม browser/เครื่อง ===== */
var linkRefs = { overlay: null, codeEl: null, input: null, msgEl: null };
function linkApiUrl() {
return appRel('/Admin/api/player-link.php');
}
function buildLinkUi() {
if (document.getElementById('jd-link-overlay')) return;
var st = document.createElement('style');
st.id = 'jd-link-style';
st.textContent = '' +
'.jd-link-trigger{position:absolute;top:14px;left:16px;z-index:6;display:inline-flex;align-items:center;gap:6px;padding:8px 14px;border-radius:999px;border:2px solid rgba(56,232,255,.85);background:rgba(8,24,44,.9);color:#dffaff;font:600 14px/1 Kanit,Segoe UI,sans-serif;cursor:pointer;box-shadow:0 0 14px rgba(56,232,255,.3);}' +
'.jd-link-trigger:hover{background:rgba(20,50,80,.95);}' +
'.jd-link-overlay{position:fixed;inset:0;z-index:10090;display:flex;align-items:center;justify-content:center;padding:16px;}' +
'.jd-link-overlay.is-hidden{display:none!important;}' +
'.jd-link-backdrop{position:absolute;inset:0;background:rgba(0,0,0,.74);}' +
'.jd-link-dialog{position:relative;z-index:1;width:min(440px,calc(100vw - 32px));padding:24px 22px 20px;border-radius:16px;background:linear-gradient(180deg,#0d2848 0%,#061528 100%);border:2px solid rgba(56,232,255,.85);box-shadow:0 0 30px rgba(56,232,255,.35);color:#eaf8ff;font-family:Kanit,Segoe UI,sans-serif;}' +
'.jd-link-close{position:absolute;top:8px;right:12px;background:none;border:none;color:#9fd8ff;font-size:1.7rem;line-height:1;cursor:pointer;}' +
'.jd-link-title{margin:0 0 4px;font-size:1.2rem;font-weight:700;text-align:center;}' +
'.jd-link-sub{margin:0 0 14px;font-size:.82rem;opacity:.75;text-align:center;}' +
'.jd-link-codebox{display:flex;align-items:center;gap:10px;justify-content:center;background:rgba(0,20,40,.85);border:2px solid rgba(56,232,255,.45);border-radius:12px;padding:12px;margin-bottom:6px;}' +
'.jd-link-code{font-size:1.7rem;font-weight:700;letter-spacing:.14em;color:#5ce9ff;}' +
'.jd-link-copy{padding:8px 12px;border-radius:8px;border:2px solid rgba(56,232,255,.55);background:rgba(20,50,80,.7);color:#dffaff;font:600 .85rem Kanit,sans-serif;cursor:pointer;}' +
'.jd-link-hr{height:1px;background:rgba(120,200,255,.25);margin:16px 0;}' +
'.jd-link-label{font-size:.92rem;margin:0 0 8px;font-weight:600;}' +
'.jd-link-row{display:flex;gap:8px;}' +
'.jd-link-input{flex:1;box-sizing:border-box;padding:11px 12px;border-radius:10px;border:2px solid rgba(56,232,255,.55);background:rgba(0,20,40,.85);color:#fff;font:600 1.05rem Kanit,sans-serif;letter-spacing:.1em;text-transform:uppercase;outline:none;}' +
'.jd-link-input:focus{border-color:#5ce9ff;box-shadow:0 0 0 3px rgba(92,233,255,.25);}' +
'.jd-link-go{padding:11px 18px;border-radius:10px;border:2px solid #7df4ff;background:linear-gradient(180deg,#2ee6ff,#0ea5c9);color:#042033;font:700 1rem Kanit,sans-serif;cursor:pointer;}' +
'.jd-link-warn{margin:10px 0 0;font-size:.78rem;color:#ffd27d;text-align:center;}' +
'.jd-link-msg{margin:10px 0 0;font-size:.85rem;text-align:center;min-height:1.1em;}' +
'.jd-link-msg.is-err{color:#ff8a8a;}.jd-link-msg.is-ok{color:#8effa6;}';
document.head.appendChild(st);
var ov = document.createElement('div');
ov.id = 'jd-link-overlay';
ov.className = 'jd-link-overlay is-hidden';
ov.setAttribute('aria-hidden', 'true');
ov.innerHTML = '' +
'<div class="jd-link-backdrop" id="jd-link-backdrop"></div>' +
'<div class="jd-link-dialog" role="dialog" aria-modal="true" aria-label="ซิงก์โปรไฟล์ข้ามเครื่อง">' +
' <button type="button" class="jd-link-close" id="jd-link-close" aria-label="ปิด">×</button>' +
' <h2 class="jd-link-title">ซิงก์โปรไฟล์ข้ามเครื่อง</h2>' +
' <p class="jd-link-sub">ใช้รหัสนี้เพื่อดึงสี / COINS / ชื่อ ไปยังเครื่องหรือ browser อื่น</p>' +
' <p class="jd-link-label">รหัสผู้เล่นของฉัน</p>' +
' <div class="jd-link-codebox"><span class="jd-link-code" id="jd-link-code">····-····</span><button type="button" class="jd-link-copy" id="jd-link-copy">คัดลอก</button></div>' +
' <p class="jd-link-sub" style="margin-top:6px">รหัสจะหมดอายุถ้าไม่ได้ใช้เกิน 30 วัน</p>' +
' <div class="jd-link-hr"></div>' +
' <p class="jd-link-label">มีรหัสจากเครื่องอื่น? กรอกที่นี่</p>' +
' <div class="jd-link-row"><input type="text" class="jd-link-input" id="jd-link-input" maxlength="9" placeholder="XXXX-XXXX" autocomplete="off" inputmode="latin"><button type="button" class="jd-link-go" id="jd-link-go">ดึงข้อมูล</button></div>' +
' <p class="jd-link-warn">การดึงข้อมูลจะแทนที่โปรไฟล์ในเครื่องนี้ (สี/COINS/ชื่อ)</p>' +
' <p class="jd-link-msg" id="jd-link-msg" role="alert"></p>' +
'</div>';
document.body.appendChild(ov);
linkRefs.overlay = ov;
linkRefs.codeEl = document.getElementById('jd-link-code');
linkRefs.input = document.getElementById('jd-link-input');
linkRefs.msgEl = document.getElementById('jd-link-msg');
document.getElementById('jd-link-backdrop').addEventListener('click', closeLinkModal);
document.getElementById('jd-link-close').addEventListener('click', closeLinkModal);
document.getElementById('jd-link-copy').addEventListener('click', copyMyCode);
document.getElementById('jd-link-go').addEventListener('click', redeemEnteredCode);
linkRefs.input.addEventListener('keydown', function (e) { if (e.key === 'Enter') { e.preventDefault(); redeemEnteredCode(); } });
// ปุ่มเปิด — ฝังในกล่องโปรไฟล์ (absolute, ไม่กระทบ layout เดิม)
if (refs.dialog && !document.getElementById('jd-link-trigger')) {
var btn = document.createElement('button');
btn.type = 'button';
btn.id = 'jd-link-trigger';
btn.className = 'jd-link-trigger';
btn.textContent = '🔗 ซิงก์เครื่อง';
btn.addEventListener('click', openLinkModal);
refs.dialog.appendChild(btn);
}
}
function setLinkMsg(text, kind) {
if (!linkRefs.msgEl) return;
linkRefs.msgEl.textContent = text || '';
linkRefs.msgEl.className = 'jd-link-msg' + (kind ? ' is-' + kind : '');
}
function openLinkModal() {
if (!linkRefs.overlay) return;
setLinkMsg('', '');
if (linkRefs.input) linkRefs.input.value = '';
if (linkRefs.codeEl) linkRefs.codeEl.textContent = '····-····';
linkRefs.overlay.classList.remove('is-hidden');
linkRefs.overlay.setAttribute('aria-hidden', 'false');
fetchMyCode();
}
function closeLinkModal() {
if (!linkRefs.overlay) return;
linkRefs.overlay.classList.add('is-hidden');
linkRefs.overlay.setAttribute('aria-hidden', 'true');
}
function fetchMyCode() {
var key = ensurePlayerKey();
var url = linkApiUrl() + '?action=code&playerKey=' + encodeURIComponent(key) +
'&displayName=' + encodeURIComponent(readName()) +
'&agentId=' + encodeURIComponent(readAgentIdRaw());
fetch(url, { credentials: 'omit', cache: 'no-store' })
.then(function (r) { return r.json(); })
.then(function (d) {
if (d && d.ok && d.codeDisplay && linkRefs.codeEl) linkRefs.codeEl.textContent = d.codeDisplay;
else setLinkMsg('โหลดรหัสไม่สำเร็จ ลองใหม่อีกครั้ง', 'err');
})
.catch(function () { setLinkMsg('ออฟไลน์ — โหลดรหัสไม่ได้', 'err'); });
}
function copyMyCode() {
var txt = linkRefs.codeEl ? linkRefs.codeEl.textContent : '';
if (!txt || txt.indexOf('·') >= 0) return;
function done() { setLinkMsg('คัดลอกรหัสแล้ว', 'ok'); }
try {
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(txt).then(done, function () { done(); });
return;
}
} catch (e) { /* ignore */ }
done();
}
function redeemEnteredCode() {
var raw = (linkRefs.input && linkRefs.input.value || '').toUpperCase().replace(/[^A-Z0-9]/g, '');
if (raw.length !== 8) { setLinkMsg('กรุณากรอกรหัส 8 ตัว', 'err'); return; }
setLinkMsg('กำลังดึงข้อมูล…', '');
fetch(linkApiUrl(), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'omit',
body: JSON.stringify({ action: 'redeem', code: raw })
})
.then(function (r) { return r.json().then(function (j) { return { status: r.status, j: j }; }); })
.then(function (res) {
var d = res.j || {};
if (!d.ok || !d.playerKey) {
setLinkMsg(d.error || 'ไม่พบรหัสนี้', 'err');
return;
}
applyRedeemedProfile(d);
})
.catch(function () { setLinkMsg('ออฟไลน์ — ดึงข้อมูลไม่ได้', 'err'); });
}
function applyRedeemedProfile(p) {
try {
localStorage.setItem('jdPlayerKey', String(p.playerKey));
if (p.displayName) { localStorage.setItem(DISPLAY_NAME_KEY, p.displayName); localStorage.setItem('playerName', p.displayName); }
if (p.agentId) localStorage.setItem('agentDisplayId', String(p.agentId));
if (p.colorThemeIndex) localStorage.setItem('lobbyThemeColor', String(p.colorThemeIndex));
if (p.skinToneIndex) localStorage.setItem('lobbySkinTone', String(p.skinToneIndex));
if (p.coins != null) localStorage.setItem('jdCoins', String(p.coins));
// ลบ avatar ที่ bake สีไว้ของเครื่องนี้ — บังคับ recompute จากสีที่ดึงมา
Object.keys(localStorage).forEach(function (k) {
if (k.indexOf('jdCharLobbyIdleDown:') === 0) localStorage.removeItem(k);
});
} catch (e) { /* ignore */ }
setLinkMsg('สำเร็จ! กำลังโหลดโปรไฟล์ใหม่…', 'ok');
setTimeout(function () { window.location.reload(); }, 500);
}
function isRoomLobbyPage() {
return /room-lobby\.html$/i.test(String(window.location.pathname || ''));
}
function init() {
if (isRoomLobbyPage()) {
return;
}
createMarkupIfNeeded();
bindRefs();
bindEvents();
buildLinkUi();
syncProfileScale();
setGroup(1);
if (window.jdAchievements) window.jdAchievements.load();
window.openProfilePopup = openPopup;
window.closeProfilePopup = closePopup;
window.roomLobbyProfileOverlay = { open: openPopup, close: closePopup };
window.mainLobbyProfileOverlay = { open: openPopup, close: closePopup };
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init, { once: true });
} else {
init();
}
})();