// ==UserScript==
// @name [MWI]personal use
// @name:zh-CN [银河奶牛]自用脚本
// @namespace http://tampermonkey.net/
// @version 0.1.20
// @description 个人脚本,适用于 Milky Way Idle 游戏
// @description:zh-CN 个人专用脚本,适用于 Milky Way Idle 游戏
// @author deric
// @license MIT
// @match https://www.milkywayidle.com/game*
// @grant GM_registerMenuCommand
// ==/UserScript==
(function() {
'use strict';
const MWI_SCRIPT_VERSION = '0.1.14';
// IndexedDB 封装
const DB_NAME = 'mwi_personal_use_db';
const DB_VERSION = 1;
const STORE_NAME = 'kv';
let dbPromise = null;
function openDB() {
if (dbPromise) return dbPromise;
dbPromise = new Promise((resolve, reject) => {
const req = window.indexedDB.open(DB_NAME, DB_VERSION);
req.onupgradeneeded = function(e) {
const db = e.target.result;
if (!db.objectStoreNames.contains(STORE_NAME)) {
db.createObjectStore(STORE_NAME);
}
};
req.onsuccess = function(e) {
resolve(e.target.result);
};
req.onerror = function(e) {
reject(e);
};
});
return dbPromise;
}
async function idbGet(key) {
const db = await openDB();
return new Promise((resolve, reject) => {
const tx = db.transaction(STORE_NAME, 'readonly');
const store = tx.objectStore(STORE_NAME);
const req = store.get(key);
req.onsuccess = () => resolve(req.result);
req.onerror = () => resolve(null);
});
}
async function idbSet(key, value) {
const db = await openDB();
return new Promise((resolve, reject) => {
const tx = db.transaction(STORE_NAME, 'readwrite');
const store = tx.objectStore(STORE_NAME);
const req = store.put(value, key);
req.onsuccess = () => resolve();
req.onerror = () => reject(req.error);
});
}
async function idbRemove(key) {
const db = await openDB();
return new Promise((resolve, reject) => {
const tx = db.transaction(STORE_NAME, 'readwrite');
const store = tx.objectStore(STORE_NAME);
const req = store.delete(key);
req.onsuccess = () => resolve();
req.onerror = () => reject(req.error);
});
}
// localStorage兼容迁移(仅首次)
async function migrateLocalStorageToIDB() {
if (window.localStorage && !(await idbGet('__migrated__'))) {
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
const value = localStorage.getItem(key);
try {
await idbSet(key, value);
} catch(e) {}
}
await idbSet('__migrated__', '1');
}
}
migrateLocalStorageToIDB();
// 用法替换示例:
// await idbSet('key', value)
// let v = await idbGet('key')
// await idbRemove('key')
// 下面所有localStorage.getItem/setItem/removeItem全部替换为await idbGet/idbSet/idbRemove
async function showSettingPanel() {
let exist = document.getElementById('mwiSettingPopup');
if (exist) exist.remove();
const popup = document.createElement('div');
popup.id = 'mwiSettingPopup';
popup.style.position = 'fixed';
popup.style.top = '120px';
popup.style.left = '50%';
popup.style.transform = 'translateX(-50%)';
popup.style.background = 'white';
popup.style.border = '2px solid #888';
popup.style.boxShadow = '0 2px 12px rgba(0,0,0,0.2)';
popup.style.zIndex = 99999;
popup.style.padding = '24px 32px';
popup.style.minWidth = '300px';
popup.innerHTML = `<div style='text-align:right;'><button id='closeMwiSettingPopup'>关闭</button></div><h3 style='margin:8px 0 16px 0;'>设置</h3>
<div style='margin-bottom:12px;'>
<label><input type='checkbox' id='mwiMonitorPlayer'/> 是否启动监控人数</label>
</div>
<div style='margin-bottom:12px;'>
<label><input type='checkbox' id='mwiMonitorNetWorth'/> 是否启动监控净资产</label>
</div>
<div style='margin-bottom:12px;'>
<label><input type='checkbox' id='mwiShowOrderTotalValue'/> 是否显示订单总价</label>
</div>
<div style='color:#888;font-size:12px;'>设置会自动保存</div>`;
document.body.appendChild(popup);
document.getElementById('closeMwiSettingPopup').onclick = async function() {
popup.remove();
await idbSet('mwiSettingPanelLastVersion', MWI_SCRIPT_VERSION);
};
// 初始化复选框状态
document.getElementById('mwiMonitorPlayer').checked = (await idbGet('mwiMonitorPlayer')) !== 'false';
document.getElementById('mwiMonitorNetWorth').checked = (await idbGet('mwiMonitorNetWorth')) === 'true';
document.getElementById('mwiShowOrderTotalValue').checked = (await idbGet('mwiShowOrderTotalValue')) !== 'false';
// 监听变更
document.getElementById('mwiMonitorPlayer').onchange = async function() {
await idbSet('mwiMonitorPlayer', this.checked);
};
document.getElementById('mwiMonitorNetWorth').onchange = async function() {
await idbSet('mwiMonitorNetWorth', this.checked);
};
document.getElementById('mwiShowOrderTotalValue').onchange = async function() {
await idbSet('mwiShowOrderTotalValue', this.checked);
window.dispatchEvent(new Event('mwiShowOrderTotalValueChanged'));
};
}
// 你的代码写在这里
async function savePlayerNumber() {
const el = document.querySelector('div.Header_playerCount__1TDTK');
if (!el) return;
const number = parseInt(el.textContent.replace(/\D/g, ''), 10);
if (isNaN(number)) {
setTimeout(savePlayerNumber, 3000);
return;
}
const now = new Date().toISOString();
const data = { time: now, number };
let arr = [];
try {
arr = JSON.parse(await idbGet('playernumber') || '[]');
if (!Array.isArray(arr)) arr = [];
} catch(e) { arr = []; }
arr.push(data);
await idbSet('playernumber', JSON.stringify(arr));
}
// 新增:根据当前网址获取key
function getNetWorthKey() {
const url = window.location.href;
const match = url.match(/(\d{6})(?!.*\d)/);
return match ? match[1] : 'default';
}
async function saveNetWorth() {
const el = document.querySelector('#toggleNetWorth');
if (!el) return;
// 提取数字和单位
const match = el.textContent.replace(/,/g, '').match(/([\d.]+)\s*([KMBT]?)/i);
let number = null;
if (match) {
number = parseFloat(match[1]);
const unit = match[2]?.toUpperCase();
if (unit === 'K') number *= 1e3;
else if (unit === 'M') number *= 1e6;
else if (unit === 'B') number *= 1e9;
else if (unit === 'T') number *= 1e12;
}
if (!number || isNaN(number)) {
setTimeout(saveNetWorth, 3000);
return;
}
const now = new Date().toISOString();
const data = { time: now, number };
let arr = [];
const key = 'networth_' + getNetWorthKey();
try {
arr = JSON.parse(await idbGet(key) || '[]');
if (!Array.isArray(arr)) arr = [];
} catch(e) { arr = []; }
arr.push(data);
if(arr.length > 5000) arr = arr.slice(-5000);
await idbSet(key, JSON.stringify(arr));
}
// 修改按钮显示逻辑,支持显示多条记录
async function createShowButton() {
if ((await idbGet('mwiMonitorPlayer')) === 'false') return;
let target = document.querySelector("#root > div > div > div.GamePage_headerPanel__1T_cA > div > div.Header_leftHeader__PkRWX > div.Header_navLogoAndPlayerCount__2earI > div.Header_playerCount__1TDTK");
if (!target) {
target = document.querySelector("#root > div > div > div.GamePage_headerPanel__3uNKN > div > div.Header_leftHeader__PkRWX > div.Header_navLogoAndPlayerCount__2earI > div.Header_playerCount__1TDTK");
}
if (!target || document.getElementById('showPlayerNumberBtn')) return;
const btn = document.createElement('button');
btn.id = 'showPlayerNumberBtn';
btn.textContent = '显示玩家人数记录';
btn.style.marginLeft = '8px';
btn.style.background = 'rgb(69,71,113)';
btn.style.color = '#fff';
btn.style.border = 'none';
btn.style.borderRadius = '4px';
btn.style.padding = '4px 10px';
btn.onclick = async function() {
let data = await idbGet('playernumber');
let arr = [];
if (data) {
try {
arr = JSON.parse(data);
if (!Array.isArray(arr)) arr = [];
} catch(e) { arr = []; }
}
let rangeDays = parseInt(await idbGet('mwiPlayerNumberRangeDays')) || 1;
const ranges = [1, 3, 7, 14, 30];
let rangeBtns = '<div style="margin-bottom:8px;">';
for(const d of ranges){
rangeBtns += `<button class='rangeBtn' data-days='${d}' style='margin-right:4px;'>${d}天</button>`;
}
rangeBtns += '</div>';
let html = rangeBtns;
html += `<button id='managePlayerNumberBtn' style='margin-bottom:8px;'>管理</button>`;
html += `<div id='chartContainer'></div>`;
html += `<div id='managePlayerNumberPanel' style='display:none;margin-top:12px;background:rgb(69,71,113);color:#fff;padding:12px;border-radius:4px;'></div>`;
let exist = document.getElementById('playerNumberPopup');
if (exist) exist.remove();
const popup = document.createElement('div');
popup.id = 'playerNumberPopup';
popup.style.position = 'fixed';
popup.style.top = '80px';
popup.style.left = '40px';
popup.style.background = 'rgb(69,71,113)';
popup.style.border = '2px solid #888';
popup.style.boxShadow = '0 2px 12px rgba(0,0,0,0.2)';
popup.style.zIndex = 9999;
popup.style.padding = '16px';
popup.style.maxHeight = '500px';
popup.style.overflow = 'auto';
popup.innerHTML = `<div style='text-align:right;'><button id='closePlayerNumberPopup' style='background:rgb(69,71,113);color:#fff;border:none;border-radius:4px;padding:4px 10px;'>关闭</button></div><div style='color:#fff;'>${html}</div>`;
document.body.appendChild(popup);
document.getElementById('closePlayerNumberPopup').onclick = function() {
popup.remove();
};
document.getElementById('managePlayerNumberBtn').onclick = function() {
const panel = document.getElementById('managePlayerNumberPanel');
if(panel.style.display==='none'){
renderManagePanel();
panel.style.display='block';
}else{
panel.style.display='none';
}
};
function renderManagePanel(){
const panel = document.getElementById('managePlayerNumberPanel');
if(arr.length===0){
panel.innerHTML = '暂无记录';
return;
}
let html = `<button id='clearAllPlayerNumber' style='color:red;margin-bottom:8px;'>全部清空</button><br/>`;
html += '<ul style="max-height:200px;overflow:auto;padding-left:0;">';
arr.forEach((item,idx)=>{
html += `<li style='list-style:none;margin-bottom:4px;'>${item.time.replace('T',' ').slice(0,16)} - ${item.number} <button data-idx='${idx}' class='delPlayerNumberBtn' style='color:red;'>删除</button></li>`;
});
html += '</ul>';
panel.innerHTML = html;
panel.querySelectorAll('.delPlayerNumberBtn').forEach(btn=>{
btn.onclick = async function(){
arr.splice(parseInt(this.getAttribute('data-idx')),1);
await idbSet('playernumber', JSON.stringify(arr));
drawChart(rangeDays);
};
});
panel.querySelector('#clearAllPlayerNumber').onclick = async function(){
if(confirm('确定要清空所有记录吗?')){
arr.length=0;
await idbSet('playernumber', '[]');
renderManagePanel();
drawChart(rangeDays);
}
};
}
function drawChart(days) {
const now = Date.now();
const ms = days * 24 * 60 * 60 * 1000;
const filtered = arr.filter(item => {
const t = new Date(item.time).getTime();
return t >= now - ms;
}).sort((a, b) => new Date(a.time) - new Date(b.time));
const container = document.getElementById('chartContainer');
if(filtered.length > 1){
container.innerHTML = `<canvas id='playerNumberChart' width='500' height='300' style='display:block;'></canvas>`;
}else if(filtered.length === 1){
container.innerHTML = `仅有一条数据:${filtered[0].time} - ${filtered[0].number}`;
return;
}else{
container.innerHTML = '暂无数据';
return;
}
setTimeout(() => {
const canvas = document.getElementById('playerNumberChart');
if (!canvas) return;
const ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
const times = filtered.map(item => new Date(item.time));
const numbers = filtered.map(item => item.number);
let anchorTimes = [];
let anchorLabels = [];
const minTime = times[0].getTime();
const maxTime = times[times.length-1].getTime();
if(days <= 1){
let t = new Date(times[0]);
t.setMinutes(0,0,0);
while (t.getTime() <= maxTime) {
anchorTimes.push(new Date(t));
anchorLabels.push(`${t.getHours()}:00`);
t.setHours(t.getHours()+1);
}
} else {
let t = new Date(times[0]);
t.setHours(0,0,0,0);
while (t.getTime() <= maxTime) {
anchorTimes.push(new Date(t));
anchorLabels.push(`${t.getMonth()+1}-${t.getDate()}`);
t.setDate(t.getDate()+1);
}
}
let minY = Math.min(...numbers);
let maxY = Math.max(...numbers);
const steps = 5;
const step = (maxY - minY) / steps || 1;
minY = Math.max(0, minY - step);
maxY = maxY + step;
const padding = 55;
const w = canvas.width - padding*2;
const h = canvas.height - padding*2;
ctx.font = '12px sans-serif';
ctx.fillStyle = '#fff';
ctx.strokeStyle = '#fff';
const timeSpan = maxTime - minTime || 1;
ctx.strokeStyle = '#fff';
ctx.beginPath();
ctx.moveTo(padding, padding);
ctx.lineTo(padding, padding + h);
ctx.lineTo(padding + w, padding + h);
ctx.stroke();
ctx.fillStyle = '#fff';
ctx.textAlign = 'right';
ctx.textBaseline = 'middle';
for(let i=0;i<=5;i++){
const y = padding + h - h*i/5;
const val = Math.round(minY + (maxY-minY)*i/5);
ctx.fillText(Math.round(val), padding-5, y);
ctx.strokeStyle = '#eee';
ctx.beginPath();
ctx.moveTo(padding, y);
ctx.lineTo(padding+w, y);
ctx.stroke();
}
ctx.textAlign = 'center';
ctx.textBaseline = 'top';
let anchorStep = 1;
if(anchorTimes.length > 10) anchorStep = Math.ceil(anchorTimes.length / 10);
for(let i=0;i<anchorTimes.length;i+=anchorStep){
const t = anchorTimes[i].getTime();
const x = padding + w*(t-minTime)/timeSpan;
ctx.fillStyle = '#fff';
ctx.fillText(anchorLabels[i], x, padding+h+5);
}
ctx.strokeStyle = '#007bff';
ctx.beginPath();
for(let i=0;i<numbers.length;i++){
const t = times[i].getTime();
const x = padding + w*(t-minTime)/timeSpan;
const y = padding + h - h*(numbers[i]-minY)/(maxY-minY||1);
if(i===0) ctx.moveTo(x, y);
else ctx.lineTo(x, y);
}
ctx.stroke();
ctx.fillStyle = '#007bff';
for(let i=0;i<numbers.length;i++){
const t = times[i].getTime();
const x = padding + w*(t-minTime)/timeSpan;
const y = padding + h - h*(numbers[i]-minY)/(maxY-minY||1);
ctx.beginPath();
ctx.arc(x, y, 3, 0, 2*Math.PI);
ctx.fill();
}
}, 0);
}
drawChart(rangeDays);
popup.querySelectorAll('.rangeBtn').forEach(btn => {
btn.onclick = async function(){
const days = parseInt(this.getAttribute('data-days'));
await idbSet('mwiPlayerNumberRangeDays', days);
drawChart(days);
highlightRangeBtn(days);
};
});
};
if (target.nextSibling) {
target.parentNode.insertBefore(btn, target.nextSibling);
} else {
target.parentNode.appendChild(btn);
}
}
// 新增:显示净资产历史折线图按钮(分key)
async function createShowNetWorthButton() {
if ((await idbGet('mwiMonitorNetWorth')) !== 'true') return;
const target = document.querySelector("#toggleNetWorth");
if (!target || document.getElementById('showNetWorthBtn')) return;
const btn = document.createElement('button');
btn.id = 'showNetWorthBtn';
const key = getNetWorthKey();
btn.textContent = '显示净资产记录';
btn.style.marginLeft = '8px';
btn.style.background = 'rgb(69,71,113)';
btn.style.color = '#fff';
btn.style.border = 'none';
btn.style.borderRadius = '4px';
btn.style.padding = '4px 10px';
btn.onclick = async function() {
const key = 'networth_' + getNetWorthKey();
let data = await idbGet(key);
let arr = [];
if (data) {
try {
arr = JSON.parse(data);
if (!Array.isArray(arr)) arr = [];
} catch(e) { arr = []; }
}
let rangeDays = parseInt(await idbGet('mwiNetWorthRangeDays')) || 1;
const ranges = [1, 3, 7, 14, 30];
let rangeBtns = '<div style="margin-bottom:8px;">';
for(const d of ranges){
rangeBtns += `<button class='rangeBtnNet' data-days='${d}' style='margin-right:4px;'>${d}天</button>`;
}
rangeBtns += '</div>';
let html = rangeBtns;
html += `<button id='manageNetWorthBtn' style='margin-bottom:8px;'>管理</button>`;
html += `<div id='chartNetContainer'></div>`;
html += `<div id='manageNetWorthPanel' style='display:none;margin-top:12px;background:rgb(69,71,113);color:#fff;padding:12px;border-radius:4px;'></div>`;
let exist = document.getElementById('netWorthPopup');
if (exist) exist.remove();
const popup = document.createElement('div');
popup.id = 'netWorthPopup';
popup.style.position = 'fixed';
popup.style.top = '80px';
popup.style.left = '40px';
popup.style.background = 'rgb(69,71,113)';
popup.style.border = '2px solid #888';
popup.style.boxShadow = '0 2px 12px rgba(0,0,0,0.2)';
popup.style.zIndex = 9999;
popup.style.padding = '16px';
popup.style.maxHeight = '500px';
popup.style.overflow = 'visible';
popup.innerHTML = `<div style='text-align:right;'><button id='closeNetWorthPopup' style='background:rgb(69,71,113);color:#fff;border:none;border-radius:4px;padding:4px 10px;'>关闭</button></div><h4 style='margin:8px 0 16px 0;color:#fff;'>净资产记录</h4><div style='color:#fff;'>${html}</div>`;
document.body.appendChild(popup);
document.getElementById('closeNetWorthPopup').onclick = function() {
popup.remove();
};
document.getElementById('manageNetWorthBtn').onclick = function() {
const panel = document.getElementById('manageNetWorthPanel');
if(panel.style.display==='none'){
renderManagePanelNet();
panel.style.display='block';
}else{
panel.style.display='none';
}
};
async function renderManagePanelNet(){
const panel = document.getElementById('manageNetWorthPanel');
const key = 'networth_' + getNetWorthKey();
let arr = [];
try {
arr = JSON.parse(await idbGet(key) || '[]');
if (!Array.isArray(arr)) arr = [];
} catch(e) { arr = []; }
const rangeDays = parseInt(await idbGet('mwiNetWorthRangeDays')) || 1;
if(arr.length===0){
panel.innerHTML = '暂无记录';
drawChart(rangeDays);
return;
}
let html = `<button id='clearAllNetWorth' style='color:red;margin-bottom:8px;'>全部清空</button><br/>`;
html += '<ul style="max-height:200px;overflow:auto;padding-left:0;">';
arr.forEach((item,idx)=>{
html += `<li style='list-style:none;margin-bottom:4px;'>${item.time.replace('T',' ').slice(0,16)} - ${Math.round(item.number/1e6)}M <button data-idx='${idx}' class='delNetWorthBtn' style='color:red;'>删除</button></li>`;
});
html += '</ul>';
panel.innerHTML = html;
panel.querySelectorAll('.delNetWorthBtn').forEach(btn=>{
btn.onclick = async function(){
const idx = parseInt(this.getAttribute('data-idx'));
arr.splice(idx,1);
await idbSet(key, JSON.stringify(arr));
renderManagePanelNet();
};
});
panel.querySelector('#clearAllNetWorth').onclick = async function(){
if(confirm('确定要清空所有记录吗?')){
arr = [];
await idbSet(key, '[]');
renderManagePanelNet();
}
};
}
function drawChart(days) {
const now = Date.now();
const ms = days * 24 * 60 * 60 * 1000;
const filtered = arr.filter(item => {
const t = new Date(item.time).getTime();
return t >= now - ms;
}).sort((a, b) => new Date(a.time) - new Date(b.time));
const container = document.getElementById('chartNetContainer');
if(filtered.length > 1){
container.innerHTML = `<canvas id='netWorthChart' width='550' height='320' style='display:block;'></canvas>`;
}else if(filtered.length === 1){
container.innerHTML = `仅有一条数据:${(filtered[0].time)} - ${Math.round(filtered[0].number/1e6)}M`;
return;
}else{
container.innerHTML = '暂无数据';
return;
}
setTimeout(() => {
const canvas = document.getElementById('netWorthChart');
if (!canvas) return;
const ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
const times = filtered.map(item => new Date(item.time));
const numbers = filtered.map(item => item.number/1e6);
let anchorTimes = [];
let anchorLabels = [];
const minTime = times[0].getTime();
const maxTime = times[times.length-1].getTime();
if(days <= 1){
let t = new Date(times[0]);
t.setMinutes(0,0,0);
while (t.getTime() <= maxTime) {
anchorTimes.push(new Date(t));
anchorLabels.push(`${t.getHours()}:00`);
t.setHours(t.getHours()+1);
}
} else {
let t = new Date(times[0]);
t.setHours(0,0,0,0);
while (t.getTime() <= maxTime) {
anchorTimes.push(new Date(t));
anchorLabels.push(`${t.getMonth()+1}-${t.getDate()}`);
t.setDate(t.getDate()+1);
}
}
let minY = Math.min(...numbers);
let maxY = Math.max(...numbers);
const steps = 5;
const step = (maxY - minY) / steps || 1;
minY = Math.max(0, minY - step);
maxY = maxY + step;
const padding = 55;
const w = canvas.width - padding*2;
const h = canvas.height - padding*2;
ctx.font = '12px sans-serif';
ctx.fillStyle = '#fff';
ctx.strokeStyle = '#fff';
const timeSpan = maxTime - minTime || 1;
ctx.strokeStyle = '#fff';
ctx.beginPath();
ctx.moveTo(padding, padding);
ctx.lineTo(padding, padding + h);
ctx.lineTo(padding + w, padding + h);
ctx.stroke();
ctx.fillStyle = '#fff';
ctx.textAlign = 'right';
ctx.textBaseline = 'middle';
for(let i=0;i<=5;i++){
const y = padding + h - h*i/5;
const val = minY + (maxY-minY)*i/5;
ctx.fillText(Math.round(val)+'M', padding-5, y);
ctx.strokeStyle = '#eee';
ctx.beginPath();
ctx.moveTo(padding, y);
ctx.lineTo(padding+w, y);
ctx.stroke();
}
ctx.textAlign = 'center';
ctx.textBaseline = 'top';
let anchorStep = 1;
if(anchorTimes.length > 10) anchorStep = Math.ceil(anchorTimes.length / 10);
for(let i=0;i<anchorTimes.length;i+=anchorStep){
const t = anchorTimes[i].getTime();
const x = padding + w*(t-minTime)/timeSpan;
ctx.fillStyle = '#fff';
ctx.fillText(anchorLabels[i], x, padding+h+5);
}
ctx.strokeStyle = '#28a745';
ctx.beginPath();
for(let i=0;i<numbers.length;i++){
const t = times[i].getTime();
const x = padding + w*(t-minTime)/timeSpan;
const y = padding + h - h*(numbers[i]-minY)/(maxY-minY||1);
if(i===0) ctx.moveTo(x, y);
else ctx.lineTo(x, y);
}
ctx.stroke();
ctx.fillStyle = '#28a745';
for(let i=0;i<numbers.length;i++){
const t = times[i].getTime();
const x = padding + w*(t-minTime)/timeSpan;
const y = padding + h - h*(numbers[i]-minY)/(maxY-minY||1);
ctx.beginPath();
ctx.arc(x, y, 3, 0, 2*Math.PI);
ctx.fill();
}
}, 0);
}
drawChart(rangeDays);
function highlightRangeBtnNet(days) {
popup.querySelectorAll('.rangeBtnNet').forEach(btn => {
if(parseInt(btn.getAttribute('data-days')) === days){
btn.style.background = '#e0e0e0';
}else{
btn.style.background = '';
}
});
}
highlightRangeBtnNet(rangeDays);
popup.querySelectorAll('.rangeBtnNet').forEach(btn => {
btn.onclick = async function(){
const days = parseInt(this.getAttribute('data-days'));
await idbSet('mwiNetWorthRangeDays', days);
drawChart(days);
highlightRangeBtnNet(days);
};
});
};
target.parentNode.appendChild(btn);
showNetWorthDayCompare();
}
// 工具函数:带单位字符串转数字
function parseNumberWithUnit(str) {
const match = str.replace(/,/g, '').match(/([\d.]+)\s*([KMBT]?)/i);
let num = null;
if (match) {
num = parseFloat(match[1]);
const unit = match[2]?.toUpperCase();
if (unit === 'K') num *= 1e3;
else if (unit === 'M') num *= 1e6;
else if (unit === 'B') num *= 1e9;
else if (unit === 'T') num *= 1e12;
}
return num;
}
// 工具函数:大数字格式化为K/M/B,保留1位小数,无小数不显示
function formatNumberWithUnit(num) {
if (num >= 1e9) {
let n = num / 1e9;
return (n % 1 === 0 ? n : n.toFixed(1)) + 'B';
} else if (num >= 1e6) {
let n = num / 1e6;
return (n % 1 === 0 ? n : n.toFixed(1)) + 'M';
} else if (num >= 1e3) {
let n = num / 1e3;
return (n % 1 === 0 ? n : n.toFixed(1)) + 'K';
} else {
return num.toString();
}
}
// 工具函数:最大值只显示到M为单位
function formatNumberToM(num) {
let n = num / 1e6;
return (n % 1 === 0 ? n : n.toFixed(2)) + 'M';
}
// 优化:库存面板数字显示,超过1万显示为K,超过1百万显示为M,超过10亿显示为B,低于1万正常显示
function formatNumberSmartUnit(num) {
if (num >= 1e9) {
let n = num / 1e9;
return (n % 1 === 0 ? n : n.toFixed(2)) + 'B';
} else if (num >= 1e6) {
let n = num / 1e6;
return (n % 1 === 0 ? n : n.toFixed(2)) + 'M';
} else if (num >= 1e4) {
let n = num / 1e3;
return (n % 1 === 0 ? n : n.toFixed(1)) + 'K';
} else {
return num.toString();
}
}
// 读取所有订单行的数量(取"/"后面的数字)
function getAllOrderQuantities() {
const nodes = document.querySelectorAll('div.MarketplacePanel_myListingsTableContainer__2s6pm > table > tbody > tr > td:nth-child(3) > div > div:nth-child(2)');
const quantities = [];
nodes.forEach(node => {
const text = node.textContent.trim();
const parts = text.split('/');
if(parts.length === 2) {
const qty = parseNumberWithUnit(parts[1]);
if(!isNaN(qty)) quantities.push(qty);
}
});
console.log('所有订单数量:', quantities);
return quantities;
}
// 读取所有订单行的价格
function getAllOrderPrices() {
const nodes = document.querySelectorAll('td.MarketplacePanel_price__hIzrY > span');
const prices = [];
nodes.forEach(node => {
const text = node.textContent.trim();
const price = parseNumberWithUnit(text);
if(!isNaN(price)) prices.push(price);
});
console.log('所有订单价格:', prices);
return prices;
}
// 在每个订单价格下方插入总价值
function showOrderTotalValues() {
// 获取所有订单行
const rows = document.querySelectorAll('div.MarketplacePanel_myListingsTableContainer__2s6pm > table > tbody > tr');
rows.forEach(row => {
// 获取数量
const qtyNode = row.querySelector('td:nth-child(3) > div > div:nth-child(2)');
let qty = 0;
if(qtyNode) {
const text = qtyNode.textContent.trim();
const parts = text.split('/');
if(parts.length === 2) {
qty = parseNumberWithUnit(parts[1]);
}
}
// 获取价格
const priceNode = row.querySelector('td.MarketplacePanel_price__hIzrY > span');
let price = 0;
if(priceNode) {
const text = priceNode.textContent.trim();
price = parseNumberWithUnit(text);
}
// 计算总价值
const total = (!isNaN(qty) && !isNaN(price)) ? qty * price : 0;
// 插入显示div
if(priceNode) {
// 避免重复插入
if(priceNode.nextSibling && priceNode.nextSibling.className === 'orderTotalValue') return;
const div = document.createElement('div');
div.className = 'orderTotalValue';
div.style.fontSize = '12px';
div.style.color = '#28a745';
div.textContent = `总价值:${formatNumberWithUnit(total)}`;
priceNode.parentNode.appendChild(div);
}
});
}
// 读取仓库内所有物品及数量(初步实现,具体功能待定)
function getWarehouseItems() {
// 假设仓库表格选择器如下(如有变动可调整):
// 例:div.WarehousePanel_tableContainer__xxx > table > tbody > tr
const rows = document.querySelectorAll('div.WarehousePanel_tableContainer__2Qw2d table tbody tr');
const items = [];
rows.forEach(row => {
// 假设第1列为物品名,第2列为数量
const nameCell = row.querySelector('td:nth-child(1)');
const qtyCell = row.querySelector('td:nth-child(2)');
if(nameCell && qtyCell) {
const name = nameCell.textContent.trim();
const qty = qtyCell.textContent.trim().replace(/,/g, '');
items.push({ name, qty });
}
});
console.log('仓库物品及数量:', items);
return items;
}
// 重构:读取背包所有物品及数量(Inventory_items__6SXv0.script_buildScore_added.script_invSort_added)
async function getInventoryItems() {
const items = [];
const itemDivs = document.querySelectorAll('#root > div > div > div.GamePage_gamePanel__3uNKN > div.GamePage_contentPanel__Zx4FH > div.GamePage_characterManagementPanel__3OYQL > div > div > div > div.TabsComponent_tabPanelsContainer__26mzo > div:nth-child(1) > div > div.Inventory_items__6SXv0.script_buildScore_added.script_invSort_added > div > div > div');
itemDivs.forEach(div => {
const svg = div.querySelector('div.Item_iconContainer__5z7j4 > svg');
let name = '';
let icon_url = '';
if(svg) {
name = svg.getAttribute('aria-label') || '';
const use = svg.querySelector('use');
if(use) icon_url = use.getAttribute('href') || '';
}
// 新增:查找强化等级
let enhance = '';
// 兼容不同class写法,优先找Item_enhancementLevel__19g-e
let enhanceDiv = div.querySelector('div.Item_enhancementLevel__19g-e');
if (!enhanceDiv) {
// 兜底:查找包含enhancementLevel的div
enhanceDiv = Array.from(div.querySelectorAll('div')).find(d => d.className && d.className.includes('enhancementLevel'));
}
if (enhanceDiv) {
enhance = enhanceDiv.textContent.trim();
}
// 拼接到名称
let displayName = name;
if (enhance) {
displayName += enhance;
}
const countDiv = div.querySelector('div.Item_count__1HVvv');
if(!countDiv) return;
const txt = countDiv.textContent.trim();
let qty = parseNumberWithUnit(txt);
if(!qty || isNaN(qty)) return;
// 查找物品种类
let category = '';
let parent = div.parentElement;
while(parent && !category) {
const labelDiv = parent.querySelector('div.Inventory_label__XEOAx > span.Inventory_categoryButton__35s1x');
if(labelDiv) category = labelDiv.textContent.trim();
parent = parent.parentElement;
}
if(name) items.push({ name: displayName, qty, category, icon_url, enhance: enhance || '' });
});
const now = new Date().toLocaleString();
const key = 'inventory_' + getNetWorthKey();
let arr = [];
try {
arr = JSON.parse(await idbGet(key) || '[]');
if (!Array.isArray(arr)) arr = [];
} catch(e) { arr = []; }
arr.push({ time: now, items });
// 只保留最新50条
if(arr.length > 50) arr = arr.slice(-50);
await idbSet(key, JSON.stringify(arr));
console.log('背包物品及数量:', items);
return items;
}
// 修改定时器和延时调用,根据设置决定是否监控
setInterval(async () => {
if((await idbGet('mwiMonitorPlayer')) !== 'false') savePlayerNumber();
if((await idbGet('mwiMonitorNetWorth')) === 'true') saveNetWorth();
}, 30 * 60 * 1000);
// 监听#toggleNetWorth出现后0.5秒再显示按钮
function waitForNetWorthAndShowBtns() {
const check = () => {
const el = document.querySelector('#toggleNetWorth');
if(el) {
setTimeout(() => {
// 先插入玩家人数按钮,再插入净资产和库存按钮,避免被覆盖
createShowButton();
createShowNetWorthButton();
createShowInventoryHistoryButton();
}, 500);
return true;
}
return false;
};
if(!check()) {
const observer = new MutationObserver(() => {
if(check()) observer.disconnect();
});
observer.observe(document.body, { childList: true, subtree: true });
}
}
waitForNetWorthAndShowBtns();
// 首次安装时默认开启所有功能
if (localStorage.getItem('mwiMonitorPlayer') === null) {
localStorage.setItem('mwiMonitorPlayer', 'true');
}
if (localStorage.getItem('mwiMonitorNetWorth') === null) {
localStorage.setItem('mwiMonitorNetWorth', 'true');
}
// 设置变更时刷新按钮显示
window.addEventListener('storage', function(e) {
if (e.key === 'mwiMonitorPlayer' || e.key === 'mwiMonitorNetWorth') {
// 移除旧按钮
const btn1 = document.getElementById('showPlayerNumberBtn');
if (btn1) btn1.remove();
const btn2 = document.getElementById('showNetWorthBtn');
if (btn2) btn2.remove();
// 重新判断并添加
createShowButton();
createShowNetWorthButton();
}
});
// 监听TabPanel_hidden出现时自动显示订单总价值
let orderTotalValueObserver = null;
function observeOrderTabAndShowTotal() {
if(orderTotalValueObserver) orderTotalValueObserver.disconnect();
if(localStorage.getItem('mwiShowOrderTotalValue') === 'false') return;
orderTotalValueObserver = new MutationObserver(() => {
const tab = document.querySelector('div.TabPanel_tabPanel__tXMJF.TabPanel_hidden__26UM3');
if(tab) {
showOrderTotalValues();
}
});
orderTotalValueObserver.observe(document.body, { childList: true, subtree: true });
}
// 页面加载后自动监听
setTimeout(() => {
observeOrderTabAndShowTotal();
}, 5000);
// 响应设置变更
window.addEventListener('mwiShowOrderTotalValueChanged', () => {
observeOrderTabAndShowTotal();
// 清理已显示的总价值
document.querySelectorAll('.orderTotalValue').forEach(e=>e.remove());
});
// 首次安装时默认开启
if (localStorage.getItem('mwiShowOrderTotalValue') === null) {
localStorage.setItem('mwiShowOrderTotalValue', 'true');
}
// 在"显示净资产记录"按钮左侧添加"显示库存记录"按钮
function createShowInventoryHistoryButton() {
const netBtn = document.getElementById('showNetWorthBtn');
if (!netBtn || document.getElementById('showInventoryHistoryBtn')) return;
// 按钮加载时保存一次库存
getInventoryItems();
const btn = document.createElement('button');
btn.id = 'showInventoryHistoryBtn';
btn.textContent = '显示库存记录';
btn.style.marginRight = '8px';
btn.style.background = 'rgb(69,71,113)';
btn.style.color = '#fff';
btn.style.border = 'none';
btn.style.borderRadius = '4px';
btn.style.padding = '4px 10px';
btn.onclick = function() {
getInventoryItems(); // 点击时保存一次库存
showInventoryHistory();
};
netBtn.parentNode.insertBefore(btn, netBtn);
}
// 监听净资产按钮生成后插入库存按钮
if (typeof observerInvBtn === 'undefined') {
var observerInvBtn = new MutationObserver(() => {
createShowInventoryHistoryButton();
});
observerInvBtn.observe(document.body, { childList: true, subtree: true });
}
// 显示库存记录弹窗(左侧时间点,右侧物品分类)
async function showInventoryHistory() {
const key = 'inventory_' + getNetWorthKey();
let arr = [];
try {
arr = JSON.parse(await idbGet(key) || '[]');
if (!Array.isArray(arr)) arr = [];
} catch(e) { arr = []; }
arr = arr.slice(-300);
let exist = document.getElementById('inventoryHistoryPopup');
if (exist) exist.remove();
const popup = document.createElement('div');
popup.id = 'inventoryHistoryPopup';
popup.style.position = 'fixed';
popup.style.top = '120px';
popup.style.left = '50%';
popup.style.transform = 'translateX(-50%)';
popup.style.background = 'rgb(69,71,113)';
popup.style.border = '2px solid #888';
popup.style.boxShadow = '0 2px 12px rgba(0,0,0,0.2)';
popup.style.zIndex = 99999;
popup.style.padding = '24px 32px';
popup.style.minWidth = '600px';
popup.innerHTML = `
<div style='position:absolute;top:18px;right:24px;display:flex;flex-direction:row;align-items:flex-start;z-index:1;'>
<button id='recordCurrentInventoryBtn' style='background:rgb(69,71,113);color:#fff;border:2px solid #28a745;border-radius:4px;padding:4px 10px;font-size:14px;cursor:pointer;margin-right:8px;'>记录当前库存</button>
<button id='showChangedItemsBtn' style='background:#28a745;color:#fff;border:2px solid #28a745;border-radius:4px;padding:4px 10px;font-size:14px;cursor:pointer;margin-right:8px;'>显示变动物品</button>
<button id='closeInventoryHistoryPopup' style='background:rgb(69,71,113);color:#fff;border:none;border-radius:4px;padding:4px 10px;'>关闭</button>
</div>
<h3 style='margin:0 0 12px 0;color:#fff;'>库存历史记录</h3>
<div style='height:1px;width:100%;background:#3a3a5a;margin-bottom:12px;'></div>
<div id='invHistoryPanel' style='display:flex;gap:24px;min-height:320px;'></div>`;
document.body.appendChild(popup);
document.getElementById('closeInventoryHistoryPopup').onclick = function() {
popup.remove();
};
// 读取上次位置和尺寸
let popupLeft = parseInt(await idbGet('mwiInvPopupLeft')) || window.innerWidth/2-320;
let popupTop = parseInt(await idbGet('mwiInvPopupTop')) || 120;
let popupWidth = parseInt(await idbGet('mwiInvPopupWidth')) || 640;
let popupHeight = parseInt(await idbGet('mwiInvPopupHeight')) || 420;
popup.style.left = popupLeft + 'px';
popup.style.top = popupTop + 'px';
popup.style.width = popupWidth + 'px';
popup.style.height = popupHeight + 'px';
popup.style.minWidth = '480px';
popup.style.minHeight = '320px';
popup.style.resize = 'none'; // 禁用原生resize
popup.style.boxSizing = 'border-box';
// 拖动功能
let isDragging = false, dragOffsetX = 0, dragOffsetY = 0;
const dragHeader = popup.querySelector('h3');
dragHeader.style.cursor = 'move';
dragHeader.addEventListener('mousedown', function(e){
isDragging = true;
dragOffsetX = e.clientX - popup.offsetLeft;
dragOffsetY = e.clientY - popup.offsetTop;
document.body.style.userSelect = 'none';
});
document.addEventListener('mousemove', function(e){
if(isDragging){
let newLeft = e.clientX - dragOffsetX;
let newTop = e.clientY - dragOffsetY;
newLeft = Math.max(0, Math.min(window.innerWidth-popup.offsetWidth, newLeft));
newTop = Math.max(0, Math.min(window.innerHeight-popup.offsetHeight, newTop));
popup.style.left = newLeft + 'px';
popup.style.top = newTop + 'px';
}
});
document.addEventListener('mouseup', async function(e){
if(isDragging){
isDragging = false;
await idbSet('mwiInvPopupLeft', parseInt(popup.style.left));
await idbSet('mwiInvPopupTop', parseInt(popup.style.top));
document.body.style.userSelect = '';
}
});
// 右下角拖拽大小
const resizeHandle = document.createElement('div');
resizeHandle.style.position = 'absolute';
resizeHandle.style.right = '2px';
resizeHandle.style.bottom = '2px';
resizeHandle.style.width = '22px';
resizeHandle.style.height = '22px';
resizeHandle.style.cursor = 'nwse-resize';
resizeHandle.style.zIndex = '11';
resizeHandle.innerHTML = `<svg width='22' height='22'><polyline points='6,20 20,20 20,6' style='fill:none;stroke:#28a745;stroke-width:3'/></svg>`;
popup.appendChild(resizeHandle);
let isResizing = false, resizeStartX=0, resizeStartY=0, startW=0, startH=0;
resizeHandle.addEventListener('mousedown', function(e){
isResizing = true;
resizeStartX = e.clientX;
resizeStartY = e.clientY;
startW = popup.offsetWidth;
startH = popup.offsetHeight;
e.stopPropagation();
document.body.style.userSelect = 'none';
// 只监听window,避免多次绑定
window.addEventListener('mousemove', doResize, true);
window.addEventListener('mouseup', stopResize, true);
});
function doResize(e) {
if (!isResizing) return;
let newW = Math.max(340, startW + (e.clientX - resizeStartX));
let newH = Math.max(260, startH + (e.clientY - resizeStartY));
popup.style.width = newW + 'px';
popup.style.height = newH + 'px';
setPanelSize();
}
function stopResize(e) {
if (!isResizing) return;
isResizing = false;
document.body.style.userSelect = '';
window.removeEventListener('mousemove', doResize, true);
window.removeEventListener('mouseup', stopResize, true);
idbSet('mwiInvPopupWidth', parseInt(popup.style.width));
idbSet('mwiInvPopupHeight', parseInt(popup.style.height));
}
// 让内容区自适应弹窗
const invPanel = popup.querySelector('#invHistoryPanel');
invPanel.style.height = (popupHeight-110) + 'px';
invPanel.style.maxHeight = 'none';
invPanel.style.minHeight = '180px';
invPanel.style.flex = '1 1 auto';
// 右侧内容区和左侧栏自适应
const setPanelSize = () => {
const h = popup.offsetHeight-110;
const w = popup.offsetWidth;
invPanel.style.height = h + 'px';
// 左侧栏高度
const leftCol = popup.querySelector('#invHistoryLeftCol');
if(leftCol) {
leftCol.style.maxHeight = h-10 + 'px';
leftCol.style.height = h-10 + 'px';
}
// 右侧栏宽高
const rightCol = popup.querySelector('#invHistoryRightCol');
if(rightCol) {
rightCol.style.maxHeight = h-10 + 'px';
rightCol.style.height = h-10 + 'px';
rightCol.style.width = (w-220) + 'px';
rightCol.style.maxWidth = '';
}
};
setPanelSize(); // 打开面板时立即自适应内容区和图标排列
// 左侧时间点
const panel = document.getElementById('invHistoryPanel');
if(arr.length === 0) {
panel.innerHTML = `<div style='color:#fff;'>暂无库存记录</div>`;
return;
}
let selectedIdx = null; // 首次打开不选中任何记录
// 处理日期分组
const dateMap = {};
arr.forEach((entry, idx) => {
const dateStr = entry.time.slice(0,10); // yyyy-mm-dd
if(!dateMap[dateStr]) dateMap[dateStr] = [];
dateMap[dateStr].push({entry, idx});
});
const allDates = Object.keys(dateMap).sort((a,b)=>b.localeCompare(a));
let selectedDate = allDates[0];
// 下拉栏HTML
let dateSelectHtml = `<select id='invHistoryDateSelect' style='width:100%;margin-bottom:8px;padding:2px 6px;border-radius:4px;'>`;
allDates.forEach(d=>{
dateSelectHtml += `<option value='${d}'>${d}</option>`;
});
dateSelectHtml += `</select>`;
// 生成左侧栏内容函数
function renderLeftCol(dateStr, activeIdx) {
let leftHtml = dateSelectHtml;
// 新到旧排序,时间字符串降序
const records = (dateMap[dateStr]||[]).slice().sort((a,b)=>b.entry.time.localeCompare(a.entry.time));
records.forEach(({entry, idx}) => {
// 拆分时间
const t = new Date(entry.time);
const monthDay = `${t.getMonth()+1}/${t.getDate()}`;
const hms = `${t.getHours().toString().padStart(2,'0')}:${t.getMinutes().toString().padStart(2,'0')}:${t.getSeconds().toString().padStart(2,'0')}`;
let highlight = (idx === activeIdx) ? "background:#444;border-radius:4px;" : "";
leftHtml += `<div class='invHistoryItem' data-idx='${idx}' style='margin-bottom:10px;border:1.5px solid #888;border-radius:6px;padding:4px 2px;background:rgba(255,255,255,0.04);${highlight}'>`;
leftHtml += `<div class='invTimeBtn' data-idx='${idx}' style='padding:4px 6px;cursor:pointer;color:#fff;font-size:13px;line-height:1.2;text-align:center;'>`;
leftHtml += `<div style='font-size:12px;'>${monthDay}</div><div style='font-size:11px;'>${hms}</div>`;
leftHtml += `</div>`;
leftHtml += `<div style='display:flex;gap:6px;justify-content:center;margin-top:2px;'>`;
leftHtml += `<button class='invBtn1' data-idx='${idx}' style='background:rgb(69,71,113);color:#fff;border:none;border-radius:4px;padding:2px 8px;cursor:pointer;'>对比</button>`;
leftHtml += `<button class='invBtn2' data-idx='${idx}' style='background:#d33;color:#fff;border:none;border-radius:4px;padding:2px 8px;cursor:pointer;'>删除</button>`;
leftHtml += `</div></div>`;
});
return leftHtml;
}
// 初始渲染,默认高亮最新一条
let activeIdx = (dateMap[selectedDate] && dateMap[selectedDate].length) ? dateMap[selectedDate][0].idx : 0;
let leftColHtml = `<div style='min-width:160px;max-height:400px;overflow:auto;border-right:1px solid #888;' id='invHistoryLeftCol'>`;
leftColHtml += renderLeftCol(selectedDate, activeIdx);
leftColHtml += `</div>`;
panel.innerHTML = `<div>${leftColHtml}</div><div id='invHistoryRightCol' style='flex:1;overflow-y:auto;max-width:520px;max-height:400px;'></div>`;
setPanelSize();
// 渲染右侧内容,默认显示最新一条
// renderRight(activeIdx); // 不再默认渲染右侧
// 监听日期下拉栏切换
panel.querySelector('#invHistoryDateSelect').onchange = function(){
selectedDate = this.value;
panel.querySelector('#invHistoryLeftCol').innerHTML = renderLeftCol(selectedDate, -1);
// 不自动高亮和切换右侧
bindLeftColEvents();
};
// 事件绑定函数
function bindLeftColEvents() {
panel.querySelectorAll('.invHistoryItem').forEach(itemDiv => {
itemDiv.addEventListener('click', function(e) {
if (e.target.tagName === 'BUTTON') return;
const idx = parseInt(this.getAttribute('data-idx'));
panel.querySelector('#invHistoryLeftCol').innerHTML = renderLeftCol(selectedDate, idx);
renderRight(idx); // 修正:点击后显示右侧库存
bindLeftColEvents();
});
});
// 对比按钮
panel.querySelectorAll('.invBtn1').forEach(btn => {
btn.onclick = function(e) {
e.stopPropagation();
const idx = parseInt(this.getAttribute('data-idx'));
// 当前右侧显示的selectedIdx
const curIdx = selectedIdx;
if(idx === curIdx) return; // 对比自己无意义
const baseItems = arr[idx].items;
const curItems = arr[curIdx].items;
// 以物品名+分类为key(不含强化)
const getKey = item => (item.name||'')+'|'+(item.category||'');
// 构建分组:同名同类不同强化的物品分别统计
function groupByEnhance(items) {
const map = {};
items.forEach(item => {
const k = (item.name||'')+'|'+(item.category||'')+'|'+(item.enhance||'');
if (!map[k]) map[k] = { ...item };
else map[k].qty += item.qty;
});
return Object.values(map);
}
const baseMap = {};
groupByEnhance(baseItems).forEach(item=>{ baseMap[getKey(item)+(item.enhance||'')] = item; });
const curMap = {};
groupByEnhance(curItems).forEach(item=>{ curMap[getKey(item)+(item.enhance||'')] = item; });
// 分类分组(先用当前的)
const group = {};
groupByEnhance(curItems).forEach(item => {
const cat = item.category || '未分类';
if(!group[cat]) group[cat] = [];
group[cat].push(item);
});
// 把历史有但当前没有的物品也加进去
groupByEnhance(baseItems).forEach(item => {
const key = getKey(item)+(item.enhance||'');
if(!curMap[key]){
const cat = item.category || '未分类';
if(!group[cat]) group[cat] = [];
// 构造一个qty为0的当前物品,icon等用历史的
group[cat].push({
name: item.name,
category: item.category,
icon_url: item.icon_url,
qty: 0,
enhance: item.enhance,
_baseQty: item.qty // 方便后面diff
});
}
});
// 记录本次对比分组和diff
lastCompareData = { group, baseMap, getKey };
// 渲染
renderCompareGroup(group, baseMap, getKey, false);
}
});
// 删除按钮
panel.querySelectorAll('.invBtn2').forEach(btn => {
btn.onclick = async function(e) {
e.stopPropagation();
const idx = parseInt(this.getAttribute('data-idx'));
showMwiConfirm('确定要删除该条库存历史记录吗?', async function(ok){
if(!ok) return;
arr.splice(idx, 1);
await idbSet(key, JSON.stringify(arr));
showInventoryHistory();
});
}
});
}
bindLeftColEvents();
// 右侧渲染函数
function renderRight(idx) {
selectedIdx = idx;
let rightHtml = '';
const items = arr[selectedIdx].items;
// 分类分组
const group = {};
items.forEach(item => {
const cat = item.category || '未分类';
if(!group[cat]) group[cat] = [];
group[cat].push(item);
});
for(const cat in group) {
rightHtml += `<div style='margin-bottom:18px;'><b style='color:#fff;'>${cat}</b><ul style='margin:8px 0 0 0;padding:0;display:flex;flex-wrap:wrap;gap:18px;max-width:100%;justify-content:flex-start;align-items:flex-start;'>`;
group[cat].forEach(item => {
rightHtml += `<li style='list-style:none;position:relative;width:44px;height:44px;display:flex;align-items:center;justify-content:center;'>`;
if(item.icon_url) {
rightHtml += `<svg width='40' height='40' style='display:block;'><use href='${item.icon_url}'></use></svg>`;
}
// 强化等级左上角
if(item.enhance) {
rightHtml += `<span style='position:absolute;left:-6px;top:-6px;font-size:13px;font-weight:bold;color:#fff;background:#28a745;padding:0 4px;border-radius:6px;'>${item.enhance}</span>`;
}
rightHtml += `<span style='position:absolute;right:0;bottom:0;background:rgba(0,0,0,0.7);color:#fff;font-size:13px;padding:0 4px;border-radius:8px 0 4px 0;'>${formatNumberSmartUnit(item.qty)}</span></li>`;
});
rightHtml += '</ul></div>';
}
panel.querySelector('#invHistoryRightCol').innerHTML = rightHtml;
setPanelSize(); // 渲染后立即自适应
}
// 默认渲染最新一条
// renderRight(0); // 不再默认渲染右侧
setPanelSize(); // 内容生成后立即自适应
// 默认高亮最新一条
// panel.querySelectorAll('.invTimeBtn')[0].style.background = '#444'; // 注释掉
// panel.querySelectorAll('.invTimeBtn')[0].style.borderRadius = '4px'; // 注释掉
// 绑定按钮1对比功能
let lastCompareData = null; // 存储上次对比渲染的分组和diff
panel.querySelectorAll('.invBtn1').forEach(btn => {
btn.onclick = function(e) {
e.stopPropagation();
const idx = parseInt(this.getAttribute('data-idx'));
// 当前右侧显示的selectedIdx
const curIdx = selectedIdx;
if(idx === curIdx) return; // 对比自己无意义
const baseItems = arr[idx].items;
const curItems = arr[curIdx].items;
// 以物品名+分类为key(不含强化)
const getKey = item => (item.name||'')+'|'+(item.category||'');
// 构建分组:同名同类不同强化的物品分别统计
function groupByEnhance(items) {
const map = {};
items.forEach(item => {
const k = (item.name||'')+'|'+(item.category||'')+'|'+(item.enhance||'');
if (!map[k]) map[k] = { ...item };
else map[k].qty += item.qty;
});
return Object.values(map);
}
const baseMap = {};
groupByEnhance(baseItems).forEach(item=>{ baseMap[getKey(item)+(item.enhance||'')] = item; });
const curMap = {};
groupByEnhance(curItems).forEach(item=>{ curMap[getKey(item)+(item.enhance||'')] = item; });
// 分类分组(先用当前的)
const group = {};
groupByEnhance(curItems).forEach(item => {
const cat = item.category || '未分类';
if(!group[cat]) group[cat] = [];
group[cat].push(item);
});
// 把历史有但当前没有的物品也加进去
groupByEnhance(baseItems).forEach(item => {
const key = getKey(item)+(item.enhance||'');
if(!curMap[key]){
const cat = item.category || '未分类';
if(!group[cat]) group[cat] = [];
// 构造一个qty为0的当前物品,icon等用历史的
group[cat].push({
name: item.name,
category: item.category,
icon_url: item.icon_url,
qty: 0,
enhance: item.enhance,
_baseQty: item.qty // 方便后面diff
});
}
});
// 记录本次对比分组和diff
lastCompareData = { group, baseMap, getKey };
// 渲染
renderCompareGroup(group, baseMap, getKey, false);
}
});
// 渲染对比分组的函数,支持只显示有变动的物品
function renderCompareGroup(group, baseMap, getKey, onlyChanged) {
let rightHtml = '';
for(const cat in group) {
let items = group[cat];
if(onlyChanged) {
items = items.filter(item => {
const baseQty = (baseMap[getKey(item)+(item.enhance||'')] && baseMap[getKey(item)+(item.enhance||'')].qty) || 0;
const diff = (item.qty||0) - baseQty;
return diff !== 0;
});
if(items.length === 0) continue;
}
rightHtml += `<div style='margin-bottom:18px;'><b style='color:#fff;'>${cat}</b><ul style='margin:8px 0 0 0;padding:0;display:flex;flex-wrap:wrap;gap:18px;max-width:100%;justify-content:flex-start;align-items:flex-start;'>`;
items.forEach(item => {
rightHtml += `<li style='list-style:none;position:relative;width:44px;height:44px;display:flex;align-items:center;justify-content:center;'>`;
if(item.icon_url) {
rightHtml += `<svg width='40' height='40' style='display:block;'><use href='${item.icon_url}'></use></svg>`;
}
// 强化等级左上角
if(item.enhance) {
rightHtml += `<span style='position:absolute;left:-6px;top:-6px;font-size:13px;font-weight:bold;color:#fff;background:#28a745;padding:0 4px;border-radius:6px;'>${item.enhance}</span>`;
}
// 数量
rightHtml += `<span style='position:absolute;right:0;bottom:0;background:rgba(0,0,0,0.7);color:#fff;font-size:13px;padding:0 4px;border-radius:8px 0 4px 0;'>${formatNumberSmartUnit(item.qty)}</span>`;
// 对比增减
const baseQty = (baseMap[getKey(item)+(item.enhance||'')] && baseMap[getKey(item)+(item.enhance||'')].qty) || 0;
const diff = (item.qty||0) - baseQty;
if(diff!==0){
rightHtml += `<span style='position:absolute;right:-8px;top:-8px;font-size:13px;font-weight:bold;color:${diff>0?'#28a745':'#d33'};background:rgba(0,0,0,0.85);padding:0 4px;border-radius:8px;'>${diff>0?'+':''}${formatNumberSmartUnit(diff)}</span>`;
}
rightHtml += `</li>`;
});
rightHtml += '</ul></div>';
}
panel.querySelector('#invHistoryRightCol').innerHTML = rightHtml;
setPanelSize(); // 渲染后立即自适应
}
// 绑定"显示变动物品"按钮功能
const showChangedBtn = document.getElementById('showChangedItemsBtn');
let onlyShowChanged = false;
showChangedBtn.onclick = function() {
if(!lastCompareData) return;
onlyShowChanged = !onlyShowChanged;
renderCompareGroup(lastCompareData.group, lastCompareData.baseMap, lastCompareData.getKey, onlyShowChanged);
showChangedBtn.textContent = onlyShowChanged ? '显示全部物品' : '显示变动物品';
};
// 绑定删除按钮功能
panel.querySelectorAll('.invBtn2').forEach(btn => {
btn.onclick = async function(e) {
e.stopPropagation();
const idx = parseInt(this.getAttribute('data-idx'));
showMwiConfirm('确定要删除该条库存历史记录吗?', async function(ok){
if(!ok) return;
arr.splice(idx, 1);
await idbSet(key, JSON.stringify(arr));
showInventoryHistory();
});
}
});
// 绑定"记录当前库存"按钮功能
document.getElementById('recordCurrentInventoryBtn').onclick = async function() {
const items = await getInventoryItems();
if(!items || items.length===0){
showMwiTip('未检测到背包物品', false);
return;
}
showMwiTip('已记录当前库存!', true);
showInventoryHistory();
};
// 内部弹窗提示函数
function showMwiTip(msg, success) {
let tip = document.createElement('div');
tip.style.position = 'fixed';
tip.style.left = '50%';
tip.style.top = '18%';
tip.style.transform = 'translateX(-50%)';
tip.style.background = 'rgba(60,60,60,0.85)';
tip.style.color = '#fff';
tip.style.fontSize = '18px';
tip.style.padding = '18px 36px';
tip.style.borderRadius = '10px';
tip.style.zIndex = 100000;
tip.style.boxShadow = '0 2px 12px rgba(0,0,0,0.18)';
tip.style.textAlign = 'center';
tip.textContent = msg;
if(success) tip.style.border = '2px solid #28a745';
else tip.style.border = '2px solid #d33';
document.body.appendChild(tip);
setTimeout(()=>{ tip.remove(); }, 1000);
}
}
// 在"显示净资产记录"按钮左侧添加"显示库存记录"按钮
function createShowInventoryHistoryButton() {
const netBtn = document.getElementById('showNetWorthBtn');
if (!netBtn || document.getElementById('showInventoryHistoryBtn')) return;
// 按钮加载时保存一次库存
getInventoryItems();
const btn = document.createElement('button');
btn.id = 'showInventoryHistoryBtn';
btn.textContent = '显示库存记录';
btn.style.marginRight = '8px';
btn.style.background = 'rgb(69,71,113)';
btn.style.color = '#fff';
btn.style.border = 'none';
btn.style.borderRadius = '4px';
btn.style.padding = '4px 10px';
btn.onclick = function() {
getInventoryItems(); // 点击时保存一次库存
showInventoryHistory();
};
netBtn.parentNode.insertBefore(btn, netBtn);
}
// 监听净资产按钮生成后插入库存按钮
if (typeof observerInvBtn === 'undefined') {
var observerInvBtn = new MutationObserver(() => {
createShowInventoryHistoryButton();
});
observerInvBtn.observe(document.body, { childList: true, subtree: true });
}
// 自动弹出设置面板(仅脚本更新后第一次)
setTimeout(() => {
if (localStorage.getItem('mwiSettingPanelLastVersion') !== MWI_SCRIPT_VERSION) {
showSettingPanel();
}
}, 1200);
// Tampermonkey菜单始终注册(不可用)"设置"按钮
if (typeof GM_registerMenuCommand !== 'undefined') {
setTimeout(() => {
GM_registerMenuCommand('设置', showSettingPanel);
}, 1000);
}
// 监听人数元素出现并有数字后,延迟3秒再执行相关操作(包括自动读取背包物品)
let lastInitUrl = location.href;
let playerCountInitObserver = null;
function waitForPlayerCountAndInit() {
if(playerCountInitObserver) playerCountInitObserver.disconnect();
let inited = false;
playerCountInitObserver = new MutationObserver(() => {
const el = document.querySelector('div.Header_playerCount__1TDTK');
if (el) {
const number = parseInt(el.textContent.replace(/\D/g, ''), 10);
if (!isNaN(number) && !inited) {
inited = true;
setTimeout(() => {
if(localStorage.getItem('mwiMonitorPlayer') !== 'false') savePlayerNumber();
if(localStorage.getItem('mwiMonitorNetWorth') === 'true') saveNetWorth();
// createShowButton(); // 删除自动调用
// createShowNetWorthButton(); // 删除自动调用
// getInventoryItems(); // 删除自动调用
}, 3000);
playerCountInitObserver.disconnect();
}
}
});
playerCountInitObserver.observe(document.body, { childList: true, subtree: true });
}
waitForPlayerCountAndInit();
// 监听网址变化
function onUrlChangeForPlayerCountInit() {
if(location.href !== lastInitUrl) {
lastInitUrl = location.href;
waitForPlayerCountAndInit();
waitForNetWorthAndShowBtns(); // 新增:切换账号后自动重建按钮
}
}
window.addEventListener('popstate', onUrlChangeForPlayerCountInit);
window.addEventListener('hashchange', onUrlChangeForPlayerCountInit);
// 兼容部分SPA主动pushState
let oldPushState = history.pushState;
history.pushState = function() {
oldPushState.apply(this, arguments);
setTimeout(onUrlChangeForPlayerCountInit, 10);
};
// 在"显示净资产记录"按钮旁显示前一天最大净资产和当前差值
async function showNetWorthDayCompare() {
const btn = document.getElementById('showNetWorthBtn');
if (!btn || document.getElementById('netWorthDayCompare')) return;
const key = 'networth_' + getNetWorthKey();
let arr = [];
try {
arr = JSON.parse(await idbGet(key) || '[]');
if (!Array.isArray(arr)) arr = [];
} catch(e) { arr = []; }
if(arr.length < 2) return;
const now = new Date();
const today0 = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0, 0).getTime();
const prevDayStart = today0 - 24 * 60 * 60 * 1000;
const prevDayEnd = today0;
const prevDayArr = arr.filter(item => {
const t = new Date(item.time).getTime();
return t >= prevDayStart && t < prevDayEnd && item.number;
});
if(prevDayArr.length === 0) return;
const maxPrevDay = Math.max(...prevDayArr.map(item => item.number));
// 读取当前净资产
const el = document.querySelector('#toggleNetWorth');
let currentNetWorth = 0;
if (el) {
const match = el.textContent.replace(/,/g, '').match(/([\d.]+)\s*([KMBT]?)/i);
if (match) {
currentNetWorth = parseFloat(match[1]);
const unit = match[2]?.toUpperCase();
if (unit === 'K') currentNetWorth *= 1e3;
else if (unit === 'M') currentNetWorth *= 1e6;
else if (unit === 'B') currentNetWorth *= 1e9;
else if (unit === 'T') currentNetWorth *= 1e12;
}
}
const diff = currentNetWorth - maxPrevDay;
// 格式化
const maxPrevDayStr = formatNumberToM(maxPrevDay);
const diffStr = (diff>=0?'+':'') + formatNumberToM(diff);
// 插入元素
const span = document.createElement('span');
span.id = 'netWorthDayCompare';
span.style.marginLeft = '12px';
span.style.fontSize = '13px';
span.style.color = diff>=0 ? '#28a745' : '#d33';
span.textContent = `前一天最大值:${maxPrevDayStr} 当前差值:${diffStr}`;
btn.parentNode.insertBefore(span, btn.nextSibling);
}
// 在市场面板创建按钮1
function createMarketButton1() {
const target = document.querySelector("#root > div > div > div.GamePage_gamePanel__3uNKN > div.GamePage_contentPanel__Zx4FH > div.GamePage_middlePanel__uDts7 > div.GamePage_mainPanel__2njyb > div > div:nth-child(1) > div > div.MarketplacePanel_tabsComponentContainer__3ctJH > div > div.TabsComponent_tabPanelsContainer__26mzo > div:nth-child(2) > div > div.MarketplacePanel_buttonContainer__vJQud > button");
if (!target || document.getElementById('marketButton1')) return;
const btn = document.createElement('button');
btn.id = 'marketButton1';
btn.textContent = '挂单记录';
btn.style.marginLeft = 'auto';
btn.style.marginRight = '8px';
btn.style.background = 'rgb(69,71,113)';
btn.style.color = '#fff';
btn.style.border = 'none';
btn.style.borderRadius = '4px';
btn.style.padding = '4px 10px';
btn.style.fontSize = '14px';
btn.onclick = function() {
showTradeHistory();
};
target.parentNode.insertBefore(btn, target);
}
// 监听市场面板按钮容器出现
if (typeof observerMarketBtn === 'undefined') {
var observerMarketBtn = new MutationObserver(() => {
createMarketButton1();
});
observerMarketBtn.observe(document.body, { childList: true, subtree: true });
}
// 监听市场交易按钮并记录交易信息
function observeMarketTradeButton() {
const tradeBtn = document.querySelector("#root > div > div > div.GamePage_gamePanel__3uNKN > div.GamePage_contentPanel__Zx4FH > div.GamePage_middlePanel__uDts7.GamePage_chatCollapsed__3pV19 > div.GamePage_mainPanel__2njyb > div > div:nth-child(1) > div > div.MarketplacePanel_tabsComponentContainer__3ctJH > div > div.TabsComponent_tabPanelsContainer__26mzo > div:nth-child(1) > div > div.Modal_modalContainer__3B80m > div.Modal_modal__1Jiep > div.MarketplacePanel_modalContent__3YhCo > div.MarketplacePanel_postButtonContainer__3KOIO > button");
if (!tradeBtn || tradeBtn.hasAttribute('data-mwi-trade-listener')) return;
// 标记已添加监听器
tradeBtn.setAttribute('data-mwi-trade-listener', 'true');
tradeBtn.addEventListener('click', function() {
recordTradeInfo();
});
}
// 记录交易信息
function recordTradeInfo() {
try {
// 获取物品名称
const itemIcon = document.querySelector("div.MarketplacePanel_itemContainer__2PwKY > div > div > div > div.Item_iconContainer__5z7j4 > svg");
const itemName = itemIcon ? itemIcon.getAttribute('aria-label') : '未知物品';
// 获取价格
const priceInput = document.querySelector("div.MarketplacePanel_input__3h1Yt.MarketplacePanel_priceInput__1HWq2");
const price = priceInput ? priceInput.textContent : '0';
// 获取数量
const quantityInput = document.querySelector("div.MarketplacePanel_input__3h1Yt > div > input");
const quantity = quantityInput ? quantityInput.value : '0';
// 获取交易类型
const header = document.querySelector(" div.MarketplacePanel_header__yahJo");
const tradeType = header ? header.textContent.trim() : '未知类型';
// 当前时间
const now = new Date().toISOString();
// 构建交易记录
const tradeRecord = {
time: now,
itemName: itemName,
price: price,
quantity: quantity,
tradeType: tradeType
};
// 保存到localStorage
const key = 'market_trades_' + getNetWorthKey();
let trades = [];
try {
trades = JSON.parse(localStorage.getItem(key) || '[]');
if (!Array.isArray(trades)) trades = [];
} catch(e) { trades = []; }
trades.push(tradeRecord);
// 只保留最新100条记录
if(trades.length > 1000) trades = trades.slice(-1000);
localStorage.setItem(key, JSON.stringify(trades));
console.log('交易记录已保存:', tradeRecord);
} catch(error) {
console.error('记录交易信息时出错:', error);
}
}
// 监听市场交易按钮出现
if (typeof observerTradeBtn === 'undefined') {
var observerTradeBtn = new MutationObserver(() => {
observeMarketTradeButton();
});
observerTradeBtn.observe(document.body, { childList: true, subtree: true });
}
// 显示交易记录
async function showTradeHistory() {
const key = 'market_trades_' + getNetWorthKey();
let trades = [];
try {
trades = JSON.parse(await idbGet(key) || '[]');
if (!Array.isArray(trades)) trades = [];
} catch(e) { trades = []; }
// 生成HTML
let html = '<div style="margin-bottom:12px;font-size:16px;font-weight:bold;color:#fff;">交易记录</div>';
if(trades.length === 0) {
html += '<div style="color:#ccc;text-align:center;padding:20px;">暂无交易记录</div>';
} else {
html += `<button id='clearAllTrades' style='color:red;margin-bottom:8px;background:none;border:none;cursor:pointer;'>全部清空</button><br/>`;
html += '<div style="max-height:400px;overflow:auto;">';
html += '<table style="width:100%;border-collapse:collapse;color:#fff;font-size:13px;">';
html += '<thead><tr style="background:rgba(255,255,255,0.1);">';
html += '<th style="padding:8px;text-align:left;border-bottom:1px solid #555;">时间</th>';
html += '<th style="padding:8px;text-align:left;border-bottom:1px solid #555;">物品</th>';
html += '<th style="padding:8px;text-align:left;border-bottom:1px solid #555;">价格</th>';
html += '<th style="padding:8px;text-align:left;border-bottom:1px solid #555;">数量</th>';
html += '<th style="padding:8px;text-align:left;border-bottom:1px solid #555;">类型</th>';
html += '<th style="padding:8px;text-align:left;border-bottom:1px solid #555;">操作</th>';
html += '</tr></thead><tbody>';
trades.forEach((trade, idx) => {
const time = new Date(trade.time).toLocaleString('zh-CN');
const rowColor = idx % 2 === 0 ? 'rgba(255,255,255,0.05)' : 'rgba(255,255,255,0.02)';
html += `<tr style="background:${rowColor};">`;
html += `<td style="padding:8px;border-bottom:1px solid #333;">${time}</td>`;
html += `<td style="padding:8px;border-bottom:1px solid #333;">${trade.itemName}</td>`;
html += `<td style="padding:8px;border-bottom:1px solid #333;">${trade.price}</td>`;
html += `<td style="padding:8px;border-bottom:1px solid #333;">${trade.quantity}</td>`;
html += `<td style="padding:8px;border-bottom:1px solid #333;">${trade.tradeType}</td>`;
html += `<td style="padding:8px;border-bottom:1px solid #333;"><button data-idx='${idx}' class='delTradeBtn' style='color:red;background:none;border:none;cursor:pointer;font-size:12px;'>删除</button></td>`;
html += '</tr>';
});
html += '</tbody></table></div>';
}
// 创建弹窗
let exist = document.getElementById('tradeHistoryPopup');
if (exist) exist.remove();
const popup = document.createElement('div');
popup.id = 'tradeHistoryPopup';
popup.style.position = 'fixed';
popup.style.top = '80px';
popup.style.left = '40px';
popup.style.background = 'rgb(69,71,113)';
popup.style.border = '2px solid #888';
popup.style.boxShadow = '0 2px 12px rgba(0,0,0,0.2)';
popup.style.zIndex = 9999;
popup.style.padding = '16px';
popup.style.maxWidth = '800px';
popup.style.maxHeight = '600px';
popup.style.overflow = 'auto';
popup.innerHTML = `<div style='text-align:right;margin-bottom:8px;'><button id='closeTradeHistoryPopup' style='background:rgb(69,71,113);color:#fff;border:none;border-radius:4px;padding:4px 10px;'>关闭</button></div>${html}`;
document.body.appendChild(popup);
// 关闭按钮
document.getElementById('closeTradeHistoryPopup').onclick = function() {
popup.remove();
};
// 单条删除
popup.querySelectorAll('.delTradeBtn').forEach(btn => {
btn.onclick = function() {
const idx = parseInt(this.getAttribute('data-idx'));
showMwiConfirm('确定要删除这条交易记录吗?', async function(ok) {
if(!ok) return;
trades.splice(idx, 1);
await idbSet(key, JSON.stringify(trades));
showTradeHistory();
});
};
});
// 全部清空
const clearAllBtn = popup.querySelector('#clearAllTrades');
if(clearAllBtn) {
clearAllBtn.onclick = function() {
showMwiConfirm('确定要清空所有交易记录吗?', async function(ok) {
if(!ok) return;
await idbSet(key, '[]');
showTradeHistory();
});
};
}
}
// 游戏内弹窗确认函数
function showMwiConfirm(msg, callback) {
let tip = document.createElement('div');
tip.style.position = 'fixed';
tip.style.left = '50%';
tip.style.top = '22%';
tip.style.transform = 'translateX(-50%)';
tip.style.background = 'rgba(60,60,60,0.92)';
tip.style.color = '#fff';
tip.style.fontSize = '18px';
tip.style.padding = '18px 36px';
tip.style.borderRadius = '10px';
tip.style.zIndex = 100000;
tip.style.boxShadow = '0 2px 12px rgba(0,0,0,0.18)';
tip.style.textAlign = 'center';
tip.style.border = '2px solid #d33';
tip.innerHTML = `<div style='margin-bottom:16px;'>${msg}</div><button id='mwiConfirmYes' style='background:#d33;color:#fff;border:none;border-radius:4px;padding:6px 22px;font-size:16px;cursor:pointer;margin-right:18px;'>删除</button><button id='mwiConfirmNo' style='background:#888;color:#fff;border:none;border-radius:4px;padding:6px 22px;font-size:16px;cursor:pointer;'>取消</button>`;
document.body.appendChild(tip);
document.getElementById('mwiConfirmYes').onclick = function(){
tip.remove();
callback(true);
};
document.getElementById('mwiConfirmNo').onclick = function(){
tip.remove();
callback(false);
};
}
// 初始化相关localStorage替换
(async function initMWISettings() {
if (await idbGet('mwiMonitorPlayer') === null) {
await idbSet('mwiMonitorPlayer', 'true');
}
if (await idbGet('mwiMonitorNetWorth') === null) {
await idbSet('mwiMonitorNetWorth', 'true');
}
if (await idbGet('mwiShowOrderTotalValue') === null) {
await idbSet('mwiShowOrderTotalValue', 'true');
}
})();
})();