497 lines
30 KiB
JavaScript
497 lines
30 KiB
JavaScript
(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();
|
||
}
|
||
})();
|