(function () { "use strict"; var API_BASE_URL = "https://jvapi.haiyihy.com"; var ASSET_ROOT = "../../assets/yumi/cp-ranking"; var DEFAULT_AVATAR = ASSET_ROOT + "/avatar-sample.png"; var DEBUG = /(?:^|[?&])debug=1(?:&|$)/.test(window.location.search); var PAGE_HEIGHTS = { plaza: 4560, rank: 3825, rewards: 4800 }; var commonHeaders = { authorization: "", "req-app-intel": "version=1.0.0;build=1;model=H5;sysVersion=web;channel=Web", "req-client": "H5", "req-imei": "H5-STATIC", "req-lang": "en", "req-sys-origin": "origin=ATYOU;originChild=ATYOU", "req-version": "V2", "req-zone": "Asia/Shanghai" }; var state = { activeTab: initialTab(), ready: false, rows: sampleRankRows(), lastRows: sampleRankRows().slice(0, 4), rewards: sampleRewards(), countdownTarget: resolveCountdownTarget() }; var dom = {}; var countdownTimer = null; init(); function init() { dom.page = document.getElementById("page"); dom.canvas = document.getElementById("canvas"); dom.tabs = Array.prototype.slice.call(document.querySelectorAll("[data-tab]")); dom.panels = Array.prototype.slice.call(document.querySelectorAll("[data-panel]")); dom.status = document.getElementById("status"); dom.plazaList = document.getElementById("plazaList"); dom.historyList = document.getElementById("historyList"); dom.rankList = document.getElementById("rankList"); dom.podium = document.getElementById("podium"); dom.rewardGroups = [ document.getElementById("rewardGroup1"), document.getElementById("rewardGroup2"), document.getElementById("rewardGroup3") ]; bindEvents(); syncScale(); applyTab(state.activeTab); render(); startCountdown(); initializeAppConnection().then(loadRemoteData).catch(function (error) { console.warn("CP ranking init failed:", error); state.ready = true; render(); }); } function bindEvents() { window.addEventListener("resize", syncScale); dom.tabs.forEach(function (button) { button.addEventListener("click", function () { applyTab(button.getAttribute("data-tab")); }); }); document.getElementById("rulesButton").addEventListener("click", function () { openRules(); }); dom.canvas.addEventListener("click", function (event) { var target = event.target.closest("[data-user-id]"); if (!target) return; openUser(target.getAttribute("data-user-id")); }); } function syncScale() { var viewport = Math.min(window.innerWidth || 375, 430); var scale = viewport / 1080; document.documentElement.style.setProperty("--scale", String(scale)); } function initialTab() { var value = getParam("tab") || "plaza"; return PAGE_HEIGHTS[value] ? value : "plaza"; } function applyTab(tab) { if (!PAGE_HEIGHTS[tab]) tab = "plaza"; state.activeTab = tab; document.documentElement.style.setProperty("--canvas-height", PAGE_HEIGHTS[tab] + "px"); dom.tabs.forEach(function (button) { button.classList.toggle("is-active", button.getAttribute("data-tab") === tab); }); dom.panels.forEach(function (panel) { panel.classList.toggle("is-active", panel.getAttribute("data-panel") === tab); }); var title = tab === "rank" ? "CP Rank" : tab === "rewards" ? "Rewards" : "CP Plaza"; setStatus(title); } async function initializeAppConnection() { if (!isAppEnvironment()) { setHeadersFromApp(defaultBrowserHeaders()); return; } try { var raw = await getAccessOriginFromApp(); var parsed = JSON.parse(raw); setHeadersFromApp(normalizeAccessOrigin(parsed)); } catch (error) { console.warn("Failed to parse app access origin:", error); setHeadersFromApp(defaultBrowserHeaders()); } } async function loadRemoteData() { try { var rankingResponse = await apiGet("/activity/week-cp-gift/ranking-data?type=WEEK"); var ranking = unwrapBody(rankingResponse) || {}; var rows = normalizeRankRows(ranking.thisWeekUserTop); var lastRows = normalizeRankRows(ranking.lastWeekUserTop); if (rows.length) state.rows = rows; if (lastRows.length) state.lastRows = lastRows; if (!lastRows.length && rows.length) state.lastRows = rows.slice(0, 4); } catch (error) { debugLog("CP ranking data failed:", error); } try { var rewardResponse = await apiGet("/activity/week-cp-gift/list-reward-pool?type=WEEK_CP_GIFT"); var rewards = normalizeRewardGroups(unwrapBody(rewardResponse)); if (rewards.length) state.rewards = rewards; } catch (error) { debugLog("CP reward pool failed:", error); } state.ready = true; render(); } function render() { renderRows(dom.plazaList, state.rows.slice(0, 4), "gift"); renderRows(dom.historyList, state.lastRows.slice(0, 4), "score"); renderRows(dom.rankList, state.rows.slice(3, 7), "score"); renderPodium(state.rows.slice(0, 3)); renderRewards(); } function renderRows(container, rows, mode) { if (!container) return; if (!rows.length) { container.innerHTML = '
No ranking data
'; return; } container.innerHTML = rows.map(function (row) { return renderCpRow(row, mode); }).join(""); } function renderCpRow(row, mode) { var left = normalizeUser(row.left); var right = normalizeUser(row.right); var total = row.total || "0"; var center = mode === "gift" ? '' : '
' + escapeHtml(total) + '
'; return '
' + '' + '' + '
' + '' + '' + escapeHtml(left.name) + '' + '
' + '
' + center + '
' + '
' + '' + '' + escapeHtml(right.name) + '' + '
' + '
'; } function renderPodium(rows) { if (!dom.podium) return; var padded = rows.slice(); while (padded.length < 3) padded.push(sampleRankRows()[padded.length]); var order = [padded[0], padded[1], padded[2]]; dom.podium.innerHTML = order.map(function (row, index) { return renderPodiumCard(row, index + 1); }).join(""); } function renderPodiumCard(row, rank) { var left = normalizeUser(row.left); var right = normalizeUser(row.right); var crown = ASSET_ROOT + "/rank-crown-" + rank + ".png"; return '
' + 'Rank ' + rank + '' + '
' + '' + '' + escapeHtml(left.name) + '' + '
' + '
' + '' + '' + escapeHtml(right.name) + '' + '
'; } function renderRewards() { dom.rewardGroups.forEach(function (container, index) { if (!container) return; var rewards = state.rewards[index] || []; container.innerHTML = rewards.map(renderRewardCard).join(""); }); } function renderRewardCard(item) { var icon = item.cover || ASSET_ROOT + "/reward-icon.png"; var note = item.note ? '
' + escapeHtml(item.note) + '
' : ""; return '
' + '
' + '
' + escapeHtml(item.title) + '
' + note + '
'; } function normalizeRankRows(list) { if (!Array.isArray(list)) return []; return list.map(function (item) { return { rank: item.rank, total: item.total || formatNumber(item.totalNumber), left: { id: item.userId, name: item.userNickname, avatar: item.userAvatar }, right: { id: item.cpUserId, name: item.cpUserNickname, avatar: item.cpUserAvatar } }; }).filter(function (row) { return row.left.id || row.right.id; }); } function normalizeRewardGroups(body) { if (!Array.isArray(body)) return []; return body.map(function (group) { var list = Array.isArray(group.activityRewardProps) ? group.activityRewardProps : []; return list.map(function (item) { return { title: rewardTitle(item), note: item.remark || "", cover: item.cover || "" }; }); }).filter(function (group) { return group.length; }).slice(0, 3); } function rewardTitle(item) { if (!item) return "Reward"; var name = item.content || item.detailType || item.type || "Reward"; var quantity = item.quantity || item.amount; if (quantity && String(name).indexOf("*") < 0) { return name + "*" + quantity; } return name; } function normalizeUser(user) { user = user || {}; return { id: user.id || "", name: user.name || "Namename", avatar: user.avatar || DEFAULT_AVATAR }; } function startCountdown() { updateCountdown(); if (countdownTimer) window.clearInterval(countdownTimer); countdownTimer = window.setInterval(updateCountdown, 1000); } function updateCountdown() { var remain = Math.max(0, state.countdownTarget - Date.now()); var seconds = Math.floor(remain / 1000); var days = Math.floor(seconds / 86400); seconds -= days * 86400; var hours = Math.floor(seconds / 3600); seconds -= hours * 3600; var minutes = Math.floor(seconds / 60); seconds -= minutes * 60; setCountdownValue("days", days); setCountdownValue("hours", hours); setCountdownValue("minutes", minutes); setCountdownValue("seconds", seconds); } function setCountdownValue(key, value) { document.querySelectorAll('[data-countdown="' + key + '"]').forEach(function (node) { node.textContent = pad2(value); }); } function resolveCountdownTarget() { var endTime = getParam("endTime") || getParam("endAt"); if (endTime) { var parsed = /^\d+$/.test(endTime) ? Number(endTime) : Date.parse(endTime); if (parsed && parsed < 1000000000000) parsed *= 1000; if (parsed && parsed > Date.now()) return parsed; } return Date.now() + (2 * 86400 + 2 * 3600 + 2 * 60 + 2) * 1000; } async function apiRequest(path, options) { var url = path.indexOf("http") === 0 ? path : API_BASE_URL + path; var init = Object.assign({ method: "GET" }, options || {}); init.headers = Object.assign({}, commonHeaders, init.headers || {}); if (init.body && typeof init.body !== "string" && !(init.body instanceof FormData)) { init.headers["Content-Type"] = "application/json"; init.body = JSON.stringify(init.body); } var response = await fetch(url, init); var data = await response.json(); if (!response.ok) throw new Error("HTTP " + response.status); if (data && data.status === false) throw new Error(data.errorMsg || data.message || "API Error"); return data; } function apiGet(path) { return apiRequest(path, { method: "GET" }); } function debugLog() { if (!DEBUG || !window.console) return; console.debug.apply(console, arguments); } function unwrapBody(response) { if (response && Object.prototype.hasOwnProperty.call(response, "body")) return response.body; return response; } function setHeadersFromApp(info) { var next = {}; if (info.authorization !== undefined) next.authorization = info.authorization; if (info.reqLang) next["req-lang"] = info.reqLang; if (info.appVersion || info.buildVersion || info.appChannel) { var appIntel = []; if (info.appVersion) appIntel.push("version=" + info.appVersion); if (info.buildVersion) appIntel.push("build=" + info.buildVersion); if (info.appChannel) appIntel.push("channel=" + info.appChannel); if (appIntel.length) next["req-app-intel"] = appIntel.join(";"); } if (info.reqImei) next["req-imei"] = info.reqImei; if (info.sysOrigin) { var origin = ["origin=" + info.sysOrigin]; if (info.sysOriginChild) origin.push("originChild=" + info.sysOriginChild); next["req-sys-origin"] = origin.join(";"); } commonHeaders = Object.assign({}, commonHeaders, next); } function defaultBrowserHeaders() { var lang = (navigator.language || "en").toLowerCase(); return { authorization: "", reqLang: lang.indexOf("ar") === 0 ? "ar" : lang.indexOf("tr") === 0 ? "tr" : lang.indexOf("bn") === 0 ? "bn" : "en", appVersion: "1.0.0", buildVersion: "1", appChannel: "Web", reqImei: "H5-STATIC", sysOrigin: "ATYOU", sysOriginChild: "ATYOU" }; } function getAccessOriginFromApp() { return new Promise(function (resolve) { var resolved = false; var finish = function (value) { if (resolved) return; resolved = true; resolve(value || "{}"); }; window.renderData = finish; window.getIosAccessOriginParam = finish; if (window.app && typeof window.app.getAccessOrigin === "function") { try { finish(window.app.getAccessOrigin()); } catch (error) { finish("{}"); } return; } if (window.FlutterApp && window.FlutterApp.postMessage) { window.FlutterApp.postMessage("requestAccessOrigin"); } window.setTimeout(function () { finish("{}"); }, 800); }); } function normalizeAccessOrigin(data) { if (!data) return {}; var appIntel = parseKeyValueList(data["Req-App-Intel"] || ""); var sysOrigin = parseKeyValueList(data["Req-Sys-Origin"] || ""); return { reqLang: data["Req-Lang"], authorization: data.Authorization, buildVersion: appIntel.build, appVersion: appIntel.version, appChannel: appIntel.channel, reqImei: appIntel["Req-Imei"], sysOrigin: sysOrigin.origin, sysOriginChild: sysOrigin.originChild || sysOrigin.child }; } function parseKeyValueList(value) { var result = {}; String(value || "").split(";").forEach(function (part) { var index = part.indexOf("="); if (index < 0) return; result[part.slice(0, index)] = part.slice(index + 1); }); return result; } function isAppEnvironment() { return !!(window.app || window.webkit || window.FlutterPageControl || window.FlutterApp); } function openUser(id) { if (!id) return; try { if (window.app && window.app.viewUserInfo) { window.app.viewUserInfo(id); } else if (window.FlutterPageControl && window.FlutterPageControl.postMessage) { window.FlutterPageControl.postMessage("view_user_info:" + id); } } catch (error) { console.warn("open user failed:", error); } } function openRules() { var message = "CP Ranking Rules\\n1. Gifts exchanged between CPs count toward the ranking.\\n2. CPs with at least 5000 gold exchanged can appear in CP Plaza.\\n3. Final rewards follow the activity settlement result."; try { if (window.app && window.app.openRuleDialog) { window.app.openRuleDialog(message); } else if (window.FlutterPageControl && window.FlutterPageControl.postMessage) { window.FlutterPageControl.postMessage("open_rules:" + message); } else { window.alert(message); } } catch (error) { window.alert(message); } } function getParam(name) { var hashQuery = ""; if (window.location.hash && window.location.hash.indexOf("?") >= 0) { hashQuery = window.location.hash.slice(window.location.hash.indexOf("?") + 1); } var hashValue = hashQuery ? new URLSearchParams(hashQuery).get(name) : null; var searchValue = new URLSearchParams(window.location.search).get(name); return hashValue || searchValue; } function setStatus(text) { if (dom.status) dom.status.textContent = text; } function pad2(value) { value = Math.max(0, Number(value) || 0); return value < 10 ? "0" + value : String(value); } function formatNumber(value) { if (value == null || value === "") return "0"; var number = Number(value); if (!Number.isFinite(number)) return String(value); if (number >= 1000000) return (number / 1000000).toFixed(1).replace(/\\.0$/, "") + "M"; if (number >= 1000) return (number / 1000).toFixed(1).replace(/\\.0$/, "") + "K"; return String(number); } function escapeHtml(value) { return String(value == null ? "" : value) .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } function escapeAttribute(value) { return escapeHtml(value); } function sampleRankRows() { var names = [ ["Ariya", "Mira"], ["Sana", "Lina"], ["Nora", "Yara"], ["Hana", "Rina"], ["Maya", "Luna"], ["Iris", "Zara"], ["Alia", "Nina"] ]; return names.map(function (pair, index) { return { rank: index + 1, total: ["1234567", "982340", "750120", "608880", "502340", "456700", "388200"][index], left: { id: "sample-left-" + index, name: pair[0], avatar: DEFAULT_AVATAR }, right: { id: "sample-right-" + index, name: pair[1], avatar: DEFAULT_AVATAR } }; }); } function sampleRewards() { return [ [ { title: "Medal*7 days", note: "(Permanent medal after getting 3 times)" }, { title: "Honor banner*7 days" }, { title: "Headwear*7days" }, { title: "Mount*7days" }, { title: "Coins*20000" }, { title: "Duke*7days" } ], [ { title: "Medal*7 days", note: "(Permanent medal after getting 3 times)" }, { title: "Honor banner*7 days" }, { title: "Mount*7days" }, { title: "Coins*15000" }, { title: "Marquis*7days" } ], [ { title: "Medal*7 days", note: "(Permanent medal after getting 3 times)" }, { title: "Honor banner*7 days" }, { title: "Coins*10000" }, { title: "Count*7days" } ] ]; } })();