// ==UserScript==
// @name MWIFavorites
// @namespace http://tampermonkey.net/
// @version 1.1.6
// @description 收藏和数量跟踪
// @author xiaoshui05
// @match https://www.milkywayidle.com/*
// @match https://test.milkywayidle.com/*
// @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant none
// ==/UserScript==
(function() {
'use strict';
let itemFavoritesMaps = {};
let itemFavoritesMapsId={};
let itemFavoritesWorking=false;
let needUpdate=false;
let itemCount={};
let currentUrl = window.location.href;
let characterId = currentUrl.substring(currentUrl.indexOf('=')+1);
function getCharacterId(){
currentUrl = window.location.href;
characterId = currentUrl.substring(currentUrl.indexOf('=')+1);
}
function numberFormatter(num, digits = 1) {
if (num === null || num === undefined) {
return null;
}
if (num < 0) {
return "-" + numberFormatter(-num);
}
const lookup = [
{ value: 1, symbol: "" },
{ value: 1e3, symbol: "k" },
{ value: 1e6, symbol: "M" },
{ value: 1e9, symbol: "B" },
];
const rx = /\.0+$|(\.[0-9]*[1-9])0+$/;
var item = lookup
.slice()
.reverse()
.find(function (item) {
return num >= item.value;
});
return item ? (num / item.value).toFixed(digits).replace(rx, "$1") + item.symbol : "0";
}
/* 支持修改版汉化插件 */
function getOriTextFromElement(elem) {
if (!elem) {
console.error("getTextFromElement null elem");
return "";
}
const translatedfrom = elem.getAttribute("script_translatedfrom");
if (translatedfrom) {
return translatedfrom;
}
return elem.textContent;
}
hookWS();
function hookWS() {
const dataProperty = Object.getOwnPropertyDescriptor(MessageEvent.prototype, "data");
const oriGet = dataProperty.get;
dataProperty.get = hookedGet;
Object.defineProperty(MessageEvent.prototype, "data", dataProperty);
function hookedGet() {
const socket = this.currentTarget;
if (!(socket instanceof WebSocket)) {
return oriGet.call(this);
}
if (socket.url.indexOf("api.milkywayidle.com/ws") <= -1 && socket.url.indexOf("api-test.milkywayidle.com/ws") <= -1) {
return oriGet.call(this);
}
const message = oriGet.call(this);
Object.defineProperty(this, "data", { value: message }); // Anti-loop
return handleMessage(message);
}
}
function handleMessage(message) {
let obj = JSON.parse(message);
if (obj && obj.type === "init_character_data") {
itemCount=[];
itemFavoritesMaps={};
getCharacterId();
itemFavoritesMapsLoad();
for (const item of obj.characterItems) {
if (item.itemLocationHrid === "/item_locations/inventory") {
updateItemCount(item.itemHrid.slice(7),item.count);
}
}
} else if (obj && obj.type === "action_completed" && obj.endCharacterItems) {
for (const item of obj.endCharacterItems) {
if (item.itemLocationHrid === "/item_locations/inventory") {
updateItemCount(item.itemHrid.slice(7),item.count);
}
}
}else if (obj && obj.type === "items_updated" && obj.endCharacterItems) {
for (const item of obj.endCharacterItems) {
if (item.itemLocationHrid === "/item_locations/inventory") {
updateItemCount(item.itemHrid.slice(7),item.count);
}
}
};
return message;
}
function updateItemCount(id,count){
itemCount[id]=count;
if(itemFavoritesMapsId[id]){
needUpdate=true;
}
}
/* 添加收藏 展示*/
const itemFavoritesMapsSave = function(){
localStorage.setItem('itemFavoritesMaps'+characterId,JSON.stringify(itemFavoritesMaps));
};
const itemFavoritesMapsLoad = function(){
let temp = localStorage.getItem('itemFavoritesMaps'+characterId)
if(temp){
itemFavoritesMaps = JSON.parse(temp);
}
};
const itemFavoritesShow = function(){
let scriptFavoritesShowDiv = null;
let targetNode = document.querySelector('div.Inventory_items__6SXv0');
if(!targetNode){
setTimeout(itemFavoritesShow,1000);
}
if(itemFavoritesWorking || (!targetNode)){
return;
}
itemFavoritesWorking=true;
scriptFavoritesShowDiv = targetNode.querySelector('div.Script_favorites_show');
if(scriptFavoritesShowDiv){
targetNode.removeChild(scriptFavoritesShowDiv);
}
if((!itemFavoritesMaps) || Object.keys(itemFavoritesMaps).length===0){
itemFavoritesWorking=false;
return;
}
//获取图标地址
let firstItemItem2De2O = targetNode.querySelector('div.Item_item__2De2O');
let iconBasePath = firstItemItem2De2O.querySelector('use').href.baseVal.split('#')[0]
let itemFavoriteData =[];
itemFavoritesMapsId={};
for(const k in itemFavoritesMaps){
let temp={};
temp.name= k.trim();
temp.id=temp.name.toLowerCase().replaceAll(' ','_').replaceAll("'",'');
itemFavoritesMapsId[temp.id]=true;
temp.target=Number(itemFavoritesMaps[k]);
temp.count=itemCount[temp.id];
if(!temp.count){
temp.count=0;
}
temp.rate=temp.count/temp.target;
itemFavoriteData.push(temp);
}
itemFavoriteData.sort((a,b)=>a.rate-b.rate);
const insertElem = document.createElement("div");
insertElem.className='Inventory_itemGrid__20YAH';
insertElem.insertAdjacentHTML('beforeend','<div class="Inventory_label__XEOAx"><span class="Inventory_categoryButton__35s1x" script_translatedfrom="Currencies">收藏</span></div>');
for(let item of itemFavoriteData){
let shortage =item.count-item.target;
//if(shortage<=0){shortage='';}
insertElem.insertAdjacentHTML('beforeend',
`
<div class="Item_itemContainer__x7kH1">
<div>
<div class="Item_item__2De2O Item_clickable__3viV6">
<div class="Item_iconContainer__5z7j4">
<svg role="img" aria-label="${item.name}" class="Icon_icon__2LtL_" width="100%" height="100%">
<use href="${iconBasePath}#${item.id}"></use>
</svg>
</div>
<div class="Item_count__1HVvv">${numberFormatter(shortage)}</div>
<div class="script_itemLevel" style=" grid-area: 1/1;display: flex;align-items: flex-start;justify-content: flex-start;margin: 0 2px -1px 0;color:${item.rate<1?'red':'green'}">${(item.rate * 100).toFixed(0)}%</div>
</div>
</div>
</div>
`
);
}
//<div class="script_itemLevel" style="z-index: 1;position: absolute;top: 2px; right: 2px;text-align: right;color:${item.rate<1?'red':'green'}">${(item.rate * 100).toFixed(0)}%</div>
for(let item of insertElem.querySelectorAll('div.Item_itemContainer__x7kH1')){
item.addEventListener('click', function(event) {
let tipsElem = document.body.querySelector('#scripte_favorites_tips')
if(tipsElem){
tipsElem.parentNode.removeChild(tipsElem);
}
let name = event.currentTarget.children[0].children[0].children[0].children[0].ariaLabel.trim();
let id=name.toLowerCase().replaceAll(' ','_').replaceAll("'",'');
tipsElem = document.createElement('div');
tipsElem.id='scripte_favorites_tips';
tipsElem.insertAdjacentHTML('beforeend',
`
<div class="MuiTooltip-tooltip MuiTooltip-tooltipPlacementBottom css-1spb1s5" style="opacity: 1; transition: opacity cubic-bezier(0.4, 0, 0.2, 1);">
<div class="Item_actionMenu__2yUcG">
<div class="Item_itemInfo__3zAGf">
<span class="Item_name__2C42x" script_translatedfrom=" ${name} "> ${name} </span>
</div>
<div class="Item_itemInfo__3zAGf">
<span class="Item_name__2C42x" script_translatedfrom=" Amount: ${numberFormatter(itemCount[id])}/${numberFormatter(itemFavoritesMaps[name])}"> Amount: ${numberFormatter(itemCount[id])}/${numberFormatter(itemFavoritesMaps[name])}</span>
</div>
<button class="Button_button__1Fe9z Button_sell__3FNpM Button_fullWidth__17pVU" script_translatedfrom="Sell For 1000 Coins">取消收藏</button>
</div>
</div>
`
);
tipsElem.children[0].children[0].children[2].addEventListener('click',function(){
delete itemFavoritesMaps[name];
itemFavoritesMapsSave();
itemFavoritesShow();
document.body.removeChild(tipsElem);
})
tipsElem.style='position: absolute; inset: 0px auto auto 0px; margin: 0px;z-index:999;';
tipsElem.style.backgroundColor='#616133'
tipsElem.style.position = 'absolute';
let rect = event.currentTarget.getBoundingClientRect();
tipsElem.style.left = (rect.left-100) + 'px';
tipsElem.style.top = (rect.top+60) + 'px';
document.body.appendChild(tipsElem);
setTimeout(()=>{document.body.removeChild(tipsElem)},3000);
})
}
scriptFavoritesShowDiv = document.createElement("div");
scriptFavoritesShowDiv.className='Script_favorites_show';
scriptFavoritesShowDiv.appendChild(insertElem);
targetNode.insertAdjacentElement('afterbegin',scriptFavoritesShowDiv)
itemFavoritesWorking=false;
};
itemFavoritesMapsLoad();
itemFavoritesShow();
setInterval(()=>{
if(needUpdate){
needUpdate=false;
itemFavoritesShow();
}
},5000);
/* 添加收藏 按钮*/
const itemFavoritesObserver = new MutationObserver(async function (mutations) {
for (const mutation of mutations) {
for (const added of mutation.addedNodes) {
if (added.classList.contains("MuiTooltip-popper")) {
let item_amountInputContainer = added.querySelector("div.Item_amountInputContainer__1RT17");
let item_input=added.querySelector('input.Input_input__2-t98');
if(item_amountInputContainer && item_input){
let name = getOriTextFromElement(added.querySelector('span.Item_name__2C42x')).trim();
let favoritesDiv = document.createElement("div");
favoritesDiv.className='Item_amountInputContainer__1RT17';
let favoritesInput= document.createElement("input");
favoritesInput.className='Input_input__2-t98';
favoritesInput.type='number';
favoritesInput.value=0;
if(itemFavoritesMaps[name]){
favoritesInput.value=Number(itemFavoritesMaps[name]);
};
favoritesInput.setAttribute('maxlength','14');
let btnAdd = document.createElement("button");
btnAdd.className='Button_button__1Fe9z Button_fullWidth__17pVU';
btnAdd.innerText = "添加收藏";
btnAdd.onclick = () => {
if(Number(favoritesInput.value)<=0){return;}
itemFavoritesMaps[name]=Number(favoritesInput.value);
favoritesInput.value=Number(itemFavoritesMaps[name]);
itemFavoritesMapsSave();
itemFavoritesShow();
};
let btnDel = document.createElement("button");
btnDel.className='Button_button__1Fe9z Button_fullWidth__17pVU Button_sell__3FNpM';
btnDel.innerText = "取消收藏";
btnDel.onclick = () => {
delete itemFavoritesMaps[name];
favoritesInput.value=0;
itemFavoritesMapsSave();
itemFavoritesShow();
};
let favoritesInputDiv= document.createElement("div");
favoritesInputDiv.className='Input_inputContainer__22GnD Input_small__1-Eva';
favoritesInputDiv.appendChild(favoritesInput);
favoritesDiv.appendChild(favoritesInputDiv);
favoritesDiv.appendChild(btnAdd);
favoritesDiv.appendChild(btnDel);
item_amountInputContainer.parentNode.insertAdjacentElement('beforeend',favoritesDiv);
}
}
}
}
});
itemFavoritesObserver.observe(document.body, { attributes: false, childList: true, characterData: false });
})();