// ==UserScript==
// @name Sword Gale Online 介面優化
// @namespace http://tampermonkey.net/
// @version 1.37.4
// @description 優化界面
// @author Wind
// @match https://swordgale.online/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=swordgale.online
// @grant none
// @license MIT
// @run-at document-start
// ==/UserScript==
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
"use strict";
// import scriptLoader from "./pages/main";
const scriptLoader = require("./pages/main").default;
const commonUtil = require("./utils/common");
const uiUtil = require("./utils/ui");
const settingStorage = require("./storage/setting");
const globalVarsStorage = require("./storage/globalVars");
const eventUtil = require("./utils/event");
const pageScript = {
"/profile": () => {
scriptLoader.profile();
},
"/hunt": () => {
scriptLoader.hunt();
},
"/items": () => {
scriptLoader.items();
},
"/market"() {
this["/items"]();
},
"/forge": () => {
scriptLoader.forge();
}
};
if (settingStorage.get("UPDATE.LAST_CHECK_TIMESTAMP") + globalVarsStorage.get("UPDATE_CHECK_INTERVAEL") < new Date().getTime()) {
fetch("https://sgo-filter.wind-tech.tw/api/version").then(res => res.json()).then(data => {
// fetch("http://localhost/api/version").then(res => res.json()).then((data) => {
globalVarsStorage.set("LATEST_VERSION", data["version"]);
}).catch(err => {
console.error(err);
});
}
let container;
let debounce = 0;
let timer = setInterval(() => {
container = document.querySelector("#__next");
if (container) {
clearInterval(timer);
uiUtil.createOpenDialogButton();
if (commonUtil.isMobileDevice() && settingStorage.get("GENERAL.MOBILE_WRAP_NAVBAR")) uiUtil.wrapNavbar();
if (settingStorage.get("GENERAL.BACKGROUND_IMAGE_URL") !== "") {
const backgroundImageDiv = document.createElement("div");
backgroundImageDiv.style.cssText = `
background: #fff url(${settingStorage.get("GENERAL.BACKGROUND_IMAGE_URL")}) center center fixed no-repeat;
background-size: cover;
-webkit-background-size: cover;
width: 100%;
height: 100%;
position: fixed;
top: 0;
left: 0;
opacity: 0.5;
pointer-events: none;
`;
if (/Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent)) backgroundImageDiv.style.cssText += "background-attachment: scroll;";
backgroundImageDiv.id = "background-image-div";
document.body.insertBefore(backgroundImageDiv, document.body.firstChild);
// document.body.style.background = ``;
// document.body.style.backgroundSize = "cover";
}
// createSettingUI();
// registerSettingUIEvent();
loadObserver();
} else {
// console.log("test")
}
}, 10);
function loadObserver() {
const observer = new MutationObserver(function (e) {
//奇怪的DOM 導致forge UI產生兩次
if (e.length) {
let renderDiv = false;
for (let i = 0; i < e.length; i++) {
if (e[i].addedNodes.length && e[i].addedNodes[0].tagName === "DIV" || e[i].removedNodes.length && e[i].removedNodes[0].tagName === "DIV") {
renderDiv = true;
}
}
if (!renderDiv) return;
}
const pathname = location.pathname;
if (pageScript[pathname]) {
debounce++;
setTimeout(() => {
debounce--;
if (debounce === 0) {
//console.log(e);
commonUtil.clearObservers();
commonUtil.clearTimers();
eventUtil.clearSubscribeEvents();
pageScript[pathname]();
}
}, 500);
}
});
observer.observe(container, {
subtree: false,
childList: true
});
}
if (location.hash !== "") {
const token = location.hash.substring(1);
history.pushState(null, null, "/");
const _getItem = localStorage.getItem;
localStorage.getItem = key => {
if (key === "token") return token;
return _getItem.apply(localStorage, [key]);
};
}
},{"./pages/main":5,"./storage/globalVars":8,"./storage/setting":9,"./utils/common":10,"./utils/event":11,"./utils/ui":12}],2:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
const commonUtil = require("../utils/common");
const settingStorage = require("../storage/setting");
const eventUtil = require("../utils/event");
const forgeStorage = require("../storage/forge");
let recipeData = settingStorage.get("recipe");
let selectedMaterials = [];
let equipmentName = "";
const elementClassname = {};
let materialDiv, forgeContainer;
const filterRecipe = {};
function Init() {
commonUtil.bindEvent("/forge", () => {
forgeContainer = document.querySelectorAll(".chakra-container");
if (forgeContainer.length > 0) {
if (forgeContainer.length > 1) {
forgeContainer = forgeContainer[1];
} else {
forgeContainer = forgeContainer[0];
}
// if(!materialDiv)
materialDiv = forgeContainer.childNodes[2];
const forgeButton = forgeContainer.querySelector("button");
if (materialDiv.querySelector(".chakra-table__container") && forgeButton) {
commonUtil.clearTimers();
createUI();
refreshRecipeTable();
forgeContainer.querySelector("#addRecipeBtn").onclick = addRecipe;
forgeButton.onclick = forgeClick;
eventUtil.subscribeApi("forge", data => {
const time = new Date(data.profile.actionStart);
time.setSeconds(0);
time.setMilliseconds(0);
const base64 = btoa(encodeURIComponent(`${time.getTime()},${equipmentName},${data.profile.nickname}`));
forgeStorage.set(base64, selectedMaterials.join(","));
forgeStorage.save();
});
}
}
});
}
function createUI() {
const equipmentNameDiv = forgeContainer.querySelector("div[role='group']");
elementClassname["labelDiv"] = equipmentNameDiv.childNodes[1].className;
elementClassname["input"] = equipmentNameDiv.childNodes[2].className;
elementClassname["button"] = forgeContainer.querySelector("button").className;
materialDiv.before(createRecipeTable());
materialDiv.after(createAddRecipeBlock());
}
function createRecipeTable() {
const recipeDiv = document.createElement("div");
recipeDiv.id = "recipeDiv";
recipeDiv.style.marginBottom = "1.25rem";
const recipeH2 = materialDiv.querySelector("h2").cloneNode();
recipeH2.innerText = "合成配方(非官方功能)";
const recipeTable = materialDiv.querySelector(".chakra-table__container").cloneNode();
recipeTable.innerHTML = materialDiv.querySelector(".chakra-table__container").innerHTML;
const tableColumns = recipeTable.querySelectorAll("thead > tr > th");
tableColumns[0].innerText = "名稱";
tableColumns[1].innerText = "材料";
tableColumns[1].removeAttribute("data-is-numeric");
tableColumns[2].innerText = "操作";
tableColumns[2].style.minWidth = "1px";
tableColumns[2].style.width = "1px";
//清空Table
recipeTable.querySelector("tbody").innerHTML = "";
elementClassname["td"] = materialDiv.querySelector("tbody > tr > td").className;
elementClassname["tr"] = materialDiv.querySelector("tbody > tr").className;
recipeDiv.appendChild(recipeH2);
recipeDiv.appendChild(recipeTable);
return recipeDiv;
}
function createAddRecipeBlock() {
const div = document.createElement("div");
div.style.marginBottom = "1.25rem";
div.innerHTML = `
<div class="${elementClassname["labelDiv"]}">選擇完原料之後輸入配方名字,可將此次選擇的原料記錄在合成配方裡面</div>
<input id="recipeNameInput" type="text" class="${elementClassname["input"]}" style="width: 80%;">
<button id="addRecipeBtn" type="button" class="${elementClassname["button"]}" style="width: 18%; float: right;">新增配方</button>
`;
return div;
}
function refreshRecipeTable() {
const keyOfRecipeData = Object.keys(recipeData);
if (keyOfRecipeData.length > 0) {
let tbody = forgeContainer.querySelector("#recipeDiv");
if (!tbody) {
materialDiv.before(createRecipeTable());
tbody = forgeContainer.querySelector("#recipeDiv");
}
tbody = tbody.querySelector("tbody");
const recipeNamesDOM = tbody.querySelectorAll("tr > td:nth-child(1)");
if (recipeNamesDOM) {
recipeNamesDOM.forEach(recipeNamesDOM => {
const index = keyOfRecipeData.indexOf(recipeNamesDOM.innerText);
if (!!~index) {
keyOfRecipeData.splice(index, 1);
}
});
}
// tbody.innerHTML = "";
keyOfRecipeData.forEach(key => {
const tr = document.createElement("tr");
tr.innerHTML = `
<td class="${elementClassname["td"]}">${key}</td>
<td class="${elementClassname["td"]}" style="white-space: normal;">${recipeData[key]}</td>
<td class="${elementClassname["td"]}">
<button type="button" class="${elementClassname["button"]}"
style="height: 1.75rem; background-color: indianred;"
>X</button>
</td>
`;
tr.className = elementClassname["tr"];
tr.querySelector("button").onclick = removeRecipe;
tr.onclick = clickRecipe;
tbody.appendChild(tr);
});
}
}
function clickRecipe(e) {
if (e.target.tagName === "BUTTON") return;
let tr = e.currentTarget;
const recipeName = tr.querySelector("td").innerText;
if (tr.style.backgroundColor) {
tr.style.backgroundColor = "";
delete filterRecipe[recipeName];
} else {
tr.style.backgroundColor = "#1C4532";
filterRecipe[recipeName] = recipeData[recipeName];
}
const materialTable = materialDiv.querySelector("table");
const tableData = commonUtil.getTableData(materialTable, {
name: "名稱",
isNumeric: false
});
const materials = {};
const keyOfFilterRecipe = Object.keys(filterRecipe);
if (keyOfFilterRecipe.length === 0) {
tableData.forEach(row => {
row["DOM"].removeAttribute("hidden");
});
} else {
keyOfFilterRecipe.forEach(name => {
filterRecipe[name].replaceAll(/ × [0-9]+/g, "").split("、").forEach(material => {
if (!materials[material]) {
materials[material] = 1;
}
});
});
tableData.forEach(row => {
if (materials[row["名稱"]]) {
row["DOM"].removeAttribute("hidden");
} else {
row["DOM"].setAttribute("hidden", "");
}
});
}
}
function addRecipe() {
const recipeNameInput = document.querySelector("#recipeNameInput");
const recipeName = recipeNameInput.value;
if (recipeName !== "") {
const materials = [];
forgeContainer.childNodes[5].childNodes[2].childNodes.forEach(materialDOM => {
const materialName = materialDOM.innerText;
materials.push(materialName);
for (let i = 0; i < Number(materialName.split(" × ")[1]); i++) {
setTimeout(() => {
materialDOM.click();
}, 100);
}
});
recipeData = settingStorage.get("recipe");
if (!recipeData[recipeName] && materials.length > 0) {
recipeData[recipeName] = materials.join("、");
settingStorage.set("recipe", recipeData);
settingStorage.save();
refreshRecipeTable();
recipeNameInput.value = "";
} else {
alert("配方名稱與材料不可為空");
}
} else {
alert("配方名稱與材料不可為空");
}
}
function removeRecipe(e) {
const row = e.target.parentElement.parentElement;
const recipeName = row.querySelector("td").innerText;
delete recipeData[recipeName];
settingStorage.set("recipe", recipeData);
settingStorage.save();
row.remove();
}
function forgeClick() {
selectedMaterials.length = 0;
forgeContainer.querySelectorAll("p + div > div").forEach(div => {
selectedMaterials.push(div.textContent.replace(" × ", "x"));
});
equipmentName = forgeContainer.querySelector("input").value;
}
var _default = Init;
exports.default = _default;
},{"../storage/forge":7,"../storage/setting":9,"../utils/common":10,"../utils/event":11}],3:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
const commonUtil = require("../utils/common");
const settingStorage = require("../storage/setting");
const eventUtil = require("../utils/event");
const uiUtil = require("../utils/ui");
let currentZoneLevel;
function Init() {
commonUtil.bindEvent("/hunt", () => {
const huntTabButton = document.querySelector("button.chakra-tabs__tab[data-index='0']");
const playerListTabButton = document.querySelector("button.chakra-tabs__tab[data-index='1']");
if (huntTabButton && playerListTabButton) {
commonUtil.clearTimers();
huntTabButton.onclick = registerHuntLogOberserverAndHideRestButtons;
playerListTabButton.onclick = registerPlayerListObserverAndCreateSearchPlayerUI;
currentZoneLevel = getCurrentZoneLevel();
if (!localStorage.hunt_tabIndex || localStorage.hunt_tabIndex === "0") {
registerHuntLogOberserverAndHideRestButtons();
} else if (localStorage.hunt_tabIndex === "1") {
registerPlayerListObserverAndCreateSearchPlayerUI();
}
eventUtil.subscribeApi("hunt", apiEvent);
eventUtil.subscribeApi("boss", apiEvent);
}
});
}
function apiEvent(data) {
if (data?.statusCode === 400) return;
//百分比血體
// if(settingStorage.get("GENERAL.HUNT_STATUS_PERCENT")){
// data.profile.fullHp += ` (${Math.floor(data.profile.hp / data.profile.fullHp * 100)}%)`
// data.profile.fullSp += ` (${Math.floor(data.profile.sp / data.profile.fullSp * 100)}%)`
// }
//爬層提示
if (currentZoneLevel === undefined) currentZoneLevel = data.profile.huntStage;
if (data.profile.huntStage > currentZoneLevel) {
data.messages.push({
m: `爬到了${data.profile.zoneName} ${data.profile.huntStage}`,
s: "info"
});
}
currentZoneLevel = data.profile.huntStage;
//血量耗損提示
data.meta.teamA.forEach(player => {
const {
name,
hp
} = player;
const index = data.messages.findIndex(msg => msg.m.match(`^${name}還有 [0-9]+ 點HP`));
if (!!~index) {
const huntHp = Number(commonUtil.regexGetValue(`${name}還有 ([0-9]+) 點HP`, data.messages[index].m)[0]);
if (hp - huntHp !== 0) {
if (hp - huntHp > 0) {
data.messages[index].m += `(-${hp - huntHp})`;
} else {
data.messages[index].m += `(+${huntHp - hp})`;
}
}
}
});
//體力耗損提示
const nickname = data.profile.nickname;
const metaData = data.meta.teamA.find(player => player.name === nickname);
const index = data.messages.findIndex(msg => msg.m.match(`^${nickname}還有 [0-9]+ 點HP`));
if (!!~index) {
const msg = {
m: "",
s: "subInfo"
};
if (metaData.sp - data.profile.sp > 0) {
msg.m = `${nickname}還有 ${data.profile.sp} 點體力(-${metaData.sp - data.profile.sp})`;
} else {
msg.m = `${nickname}還有 ${data.profile.sp} 點體力(+${data.profile.sp - metaData.sp})`;
}
data.messages.splice(index + 1, 0, msg);
}
let findEquipmentBroken = false;
let settingChanged = false;
const playerNames = data.meta.teamA.map(player => player.name).join("|");
data.messages.forEach(message => {
//裝備損壞
if (/損壞了$/.test(message.m)) {
findEquipmentBroken = true;
}
//物品過濾器
//Wind獲得了鱗柄白鵝膏 × 2
//獲得了蜘蛛絲耗損了 1 點耐久
if (message.s === "info" && /獲得了.*/.test(message.m)) {
const itemData = message.m.replace(/.*獲得了/, "").split(" × "); // .replace(/\ \×\ [0-9]+/, "")
const itemName = itemData[0];
const itemQuatity = itemData.length > 1 ? Number(itemData[1]) : 1;
commonUtil.itemApplyFilter(itemName, {
playSound: true
});
//狩獵記錄
if (settingStorage.get("ITEM_RECORD.ENABLE")) {
const pattern = `(${playerNames}|)獲得了(.*)$`;
const regexResult = commonUtil.regexGetValue(pattern, message.m);
if (!/ [0-9]+ 點.*(經驗值|熟練度)/.test(regexResult[1])) {
//單人狩獵判斷
const currentPlayerName = regexResult[0] === "" ? nickname : regexResult[0];
const records = settingStorage.get("ITEM_RECORD.RECORDS");
if (!records[currentPlayerName]) records[currentPlayerName] = {};
if (!records[currentPlayerName][itemName]) {
records[currentPlayerName][itemName] = itemQuatity;
} else {
records[currentPlayerName][itemName] += itemQuatity;
}
settingStorage.set("ITEM_RECORD.RECORDS", records);
settingChanged = true;
}
}
}
});
settingStorage.save();
//裝備損壞超級提示
if (findEquipmentBroken && settingStorage.get("GENERAL.RED_BACKBROUND_WHEN_EQUIPMENT_BROKEN")) {
document.querySelector("#__next").style.backgroundColor = "var(--chakra-colors-red-500)";
} else {
document.querySelector("#__next").style.backgroundColor = "";
}
}
function getCurrentZoneLevel() {
const currentZone = document.querySelector("[zones]").textContent.split(":")[1].trim();
const reglevel = commonUtil.regexGetValue("([0-9]+)", currentZone);
if (reglevel.length) {
return Number(reglevel[0]);
}
return 0;
}
function createSearchPlayerUI() {
if (document.querySelector("#searchPlayerName")) return;
const playerListContainer = document.querySelector("[tabindex='0'] > .chakra-container > .css-0");
const [div, input] = uiUtil.createSearchUI("搜尋玩家", "searchPlayerName");
input.onchange = () => {
const name = input.value;
document.querySelectorAll("[tabindex='0'] > .chakra-container > .css-0 > div > div").forEach(row => {
checkPlayerName(row, name);
});
};
playerListContainer.querySelector("p").after(div);
}
function registerHuntLogOberserverAndHideRestButtons() {
// console.log("Register");
if (settingStorage.get("GENERAL.HIDE_REST_BUTTON")) hideRestButtons();
commonUtil.clearObservers();
const huntLogContainer = document.querySelector("[tabindex='0'] > .chakra-container").lastChild;
const observer = new MutationObserver(beautifyHuntLog);
observer.observe(huntLogContainer, {
childList: true
});
commonUtil.addObserver(observer);
beautifyHuntLog();
}
function registerPlayerListObserverAndCreateSearchPlayerUI() {
// console.log("Register");
createSearchPlayerUI();
commonUtil.clearObservers();
const playerListContainer = document.querySelector("[tabindex='0'] > .chakra-container > .css-0");
const observer = new MutationObserver(playerListRefreshEvent);
observer.observe(playerListContainer, {
childList: true,
subtree: true
});
commonUtil.addObserver(observer);
playerListRefreshEvent();
}
function hideRestButtons() {
document.querySelectorAll("[tabindex='0'] > .chakra-container > div > button").forEach(button => {
if (button.textContent === "休息") button.style.display = "none"; //button.style.marginLeft = "auto";
// if(button.textContent === "清空記錄") button.style.marginLeft = "var(--chakra-space-2)";
});
}
function beautifyHuntLog() {
if (localStorage.hunt_tabIndex === "1") return;
const huntLogContainer = document.querySelector("[tabindex='0'] > .chakra-container " // > .css-0"
).lastChild;
huntLogContainer.childNodes.forEach(node => {
const lines = node.querySelectorAll("[data-line-number]");
if (lines.length > 1 && !node.querySelector(".information")) {
node.style.justifyContent = "space-between";
const beforeHuntInformations = Array.from(node.childNodes[1].childNodes[0].childNodes);
const profiles = [];
const equipments = [];
let playerInformationClassname;
beforeHuntInformations.forEach(information => {
if (!playerInformationClassname) {
playerInformationClassname = information.childNodes[0].childNodes[0].className;
}
if (information.childNodes[0].childNodes[0].className === playerInformationClassname) {
const informationLines = Array.from(information.childNodes);
//get name
const profileText = informationLines[0].innerText;
profiles.push({
name: profileText.split("\n")[0],
hp: commonUtil.regexGetValue("HP: ([0-9]+)", profileText)[0],
die: false
});
// profiles.push(informationLines[0].innerText.split("\n")[0]);
informationLines.shift();
informationLines.forEach(information => {
const equipmentData = information.innerText;
const equipment = {
name: commonUtil.regexGetValue("的(.*)(", equipmentData)[0],
durability: Number(commonUtil.regexGetValue("耐([0-9]+)", equipmentData)[0]),
costDurablilty: 9999999,
msgClassname: ""
};
equipments.push(equipment);
});
}
});
const informationDiv = document.createElement("div");
informationDiv.className = "information";
informationDiv.style.marginLeft = "0.5em";
let battleLogEnd = false;
lines.forEach(line => {
const profile = profiles.find(profile => line.innerText === `${profile.name}被擊殺死亡了`);
if (profile) {
profile.die = true;
informationDiv.appendChild(line);
}
//單人戰鬥結束檢查
if (!!~line.innerText.indexOf("點HP")) {
battleLogEnd = true;
//組隊戰鬥結束檢查
} else if (profiles.filter(profile => !profile.die).length === 0) {
battleLogEnd = true;
}
if (battleLogEnd) {
//血量耗損計算
profiles.forEach(profile => {
const hpRegexMatch = commonUtil.regexGetValue(`${profile.name}還有 ([0-9]+) 點HP`, line.innerText);
if (hpRegexMatch.length > 0) {
const currentHp = Number(hpRegexMatch[0]);
const costHp = profile.hp - currentHp;
if (costHp / profile.hp >= settingStorage.get("WARNING.HP") / 100) {
line.style.color = settingStorage.get("COLOR.WARNING");
}
}
const spRegexMatch = commonUtil.regexGetValue(`${profile.name}還有 ([0-9]+) 點體力`, line.innerText);
if (spRegexMatch.length) {
const currentSp = Number(spRegexMatch[0]);
if (currentSp < settingStorage.get("WARNING.SP")) {
line.style.color = settingStorage.get("COLOR.WARNING");
}
}
});
//計算耐久
let findEquipment = false;
let equipmentBroken = false;
equipments.forEach(equipment => {
//同名武器耐久篩選
if (equipment.costDurablilty === 9999999 && !findEquipment) {
const matchArray = commonUtil.regexGetValue(`${equipment.name}耗損了 ([0-9]+) 點耐久`, line.innerText);
if (matchArray.length > 0) {
findEquipment = true;
equipment.msgClassname = line.className;
equipment.costDurablilty = Number(matchArray[0]);
}
if (RegExp(`${equipment.name}損壞了`).test(line.innerText)) {
equipment.costDurablilty = equipment.durability;
findEquipment = true;
equipmentBroken = true;
}
}
});
//武器損壞
if (equipmentBroken) {
informationDiv.insertBefore(line, informationDiv.firstChild);
} else {
informationDiv.appendChild(line);
}
//爬層提示
if (/^爬到了(.*)/.test(line.innerText)) {
line.style.color = settingStorage.get("COLOR.ZONE_LEVEL");
informationDiv.insertBefore(line, informationDiv.firstChild);
}
if (/獲得了.*/.test(line.innerText)) {
const itemName = line.innerText.replace(/.*獲得了/, "").replace(/\ \×\ [0-9]+/, "");
commonUtil.itemApplyFilter(itemName, {
highlight: true,
dom: line
});
}
}
});
//裝備耐久提示
equipments.forEach(equipment => {
const calcDurablilty = equipment.durability - equipment.costDurablilty;
if (calcDurablilty > 0) {
const equipmentMsgDiv = document.createElement("div");
equipmentMsgDiv.className = equipment.msgClassname;
equipmentMsgDiv.innerText = `${equipment.name}還有 ${calcDurablilty} 點耐久`;
equipmentMsgDiv.style.color = calcDurablilty <= settingStorage.get("WARNING.EQUIPMENT") ? settingStorage.get("COLOR.WARNING") : settingStorage.get("COLOR.TIPS");
informationDiv.appendChild(equipmentMsgDiv);
}
});
// if(currentZoneLevel && getCurrentZoneLevel() > currentZoneLevel){
// const zoneLevelChangeDiv = document.createElement("div");
// zoneLevelChangeDiv.style.display = "flex";
// zoneLevelChangeDiv.style.color = settingStorage.get("COLOR.ZONE_LEVEL");
// zoneLevelChangeDiv.innerText = `爬到了${document.querySelector("[zones]").textContent.split(":")[1]}`
// informationDiv.insertBefore(zoneLevelChangeDiv, informationDiv.firstChild);
// }
// currentZoneLevel = getCurrentZoneLevel();
const leftDiv = document.createElement("div");
const rightDiv = document.createElement("div");
leftDiv.style.display = "flex";
rightDiv.style.alignSelf = "flex-start";
leftDiv.appendChild(node.childNodes[0]);
if (commonUtil.isMobileDevice()) {
informationDiv.style.marginLeft = "";
if (settingStorage.get("GENERAL.MOBILE_HUNT_REPORT")) {
informationDiv.insertBefore(node.childNodes[0].childNodes[0], informationDiv.childNodes[0]);
node.childNodes[0].remove();
} else {
informationDiv.insertBefore(node.childNodes[0], informationDiv.childNodes[0]);
}
leftDiv.appendChild(informationDiv);
} else {
leftDiv.appendChild(node.childNodes[0]);
rightDiv.appendChild(informationDiv);
}
node.appendChild(leftDiv);
if (!commonUtil.isMobileDevice()) node.appendChild(rightDiv);
}
});
}
function checkPlayerName(row, name) {
//檢查是否為體力低下的提示row
if (new RegExp("^[0-9]{2}/[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}$").test(row.outerText)) return;
const playerName = row?.childNodes[1]?.childNodes[0]?.childNodes[1]?.childNodes[0].textContent;
if (playerName && !!~playerName.indexOf(name)) {
row.hidden = false;
} else {
row.hidden = true;
}
}
function playerListRefreshEvent() {
document.querySelectorAll("[tabindex='0'] > .chakra-container > .css-0 > div > div").forEach(row => {
//搜尋玩家
checkPlayerName(row, document.querySelector("#searchPlayerName").value);
//禁用壞按鍵
if (settingStorage.get("GENERAL.DISABLE_BAD_BUTTON")) {
const menuButtons = row.querySelectorAll("[role='menu'] > button");
menuButtons.forEach(button => {
if (["搶劫", "我要超渡你"].includes(button.textContent)) {
button.disabled = true;
}
});
}
// }
});
}
var _default = Init;
exports.default = _default;
},{"../storage/setting":9,"../utils/common":10,"../utils/event":11,"../utils/ui":12}],4:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
const commonUtil = require("../utils/common");
const settingStorage = require("../storage/setting");
const eventUtil = require("../utils/event");
const forgeStorage = require("../storage/forge");
const globalVarsStorage = require("../storage/globalVars");
const uiUtil = require("../utils/ui");
const quality = {
傳說: [2.3, 0.82],
神話: [2.1, 0.84],
史詩: [2.0, 0.85],
完美: [1.85, 0.87],
頂級: [1.65, 0.88],
精良: [1.5, 0.9],
高級: [1.33, 0.93],
上等: [1.15, 0.96],
普通: [1, 1],
次等: [0.9, 1.01],
劣質: [0.8, 1.02],
破爛: [0.7, 1.03],
垃圾般: [0.55, 1.06],
屎一般: [0.4, 1.1]
};
const tablesColumns = {};
const equipmentFilter = {
color: "",
type: ""
};
function Init() {
commonUtil.bindEvent(["/items", "/market"], () => {
//var targetContainer = document.querySelector(".chakra-tabs").childNodes[2];
const tables = document.querySelectorAll("table");
const targetContainer = document.querySelector(".chakra-tabs");
if (!tables || !targetContainer) return;
if (location.pathname === "/market" && tables.length < 3) {
return;
}
commonUtil.clearTimers();
const observer = new MutationObserver(e => {
if (e.length === 2 && e[1].addedNodes.length && e[1].addedNodes[0].innerHTML !== '') {
if (!settingStorage.get("GENERAL.DISABLE_TRUE_STATS")) onItemsDetail(e[1].addedNodes[0].childNodes[0].childNodes);
if (location.pathname === "/market" && !settingStorage.get("GENERAL.DISABLE_MARKET_FUNCTION")) createMarketButtons(e[1].addedNodes[0].childNodes[0].childNodes);
}
});
;
observer.observe(targetContainer, {
subtree: false,
childList: true
});
commonUtil.addObserver(observer);
if (!document.querySelector("#searchPlayerName") && location.pathname === "/market") {
const [div, input] = uiUtil.createSearchUI("搜尋販賣者", "searchPlayerName");
// targetContainer.before(targetContainer.firstChild, div);
div.querySelector("label").style.width = "96px";
div.style.maxWidth = "800px";
div.style.marginLeft = "auto";
div.style.marginRight = "auto";
div.style.width = "95%";
document.querySelector("[role=tablist]").before(div);
["equipments", "mines", "items"].forEach(category => {
eventUtil.subscribeApi(`trades?category=${category}`, data => {
data.trades = data.trades.filter(trade => trade.sellerName.match(input.value));
});
});
}
;
const types = ["equipments", "mines", "items"];
tables.forEach(table => {
const type = types.shift();
const tableId = `table${Object.keys(tablesColumns).length}-${type}`;
table.id = tableId;
tablesColumns[tableId] = commonUtil.getTableColumns(table, sortTable);
if (location.pathname === "/items") {
const tableData = commonUtil.getTableData(table, {
name: "類型",
isNumeric: false
}, tablesColumns[tableId]);
const types = [];
tableData.forEach(row => {
if (!types.includes(row["類型"])) types.push(row["類型"]);
});
createTypeFilter(table, types);
createQuickFilter(table);
} else if (location.pathname === "/market") {
const tbody = table.querySelector("tbody");
function highlightRow() {
const rows = tbody.querySelectorAll("[role=row]");
rows.forEach(row => {
row.style.border = "";
});
const highlightRowData = globalVarsStorage.get("HIGHTLIGHT_ROW");
highlightRowData[type].forEach(rowIndex => {
if (rows.length > 0) {
// console.log(tbody.childNodes, rowIndex, tbody.childNodes[rowIndex])
try {
rows[rowIndex].style.border = `solid ${settingStorage.get("COLOR.MARKET_WATCH")}`;
} catch (e) {
console.error(rowIndex, rows);
}
}
});
}
const observer = new MutationObserver(highlightRow);
// tbody.firstChild.remove();
observer.observe(tbody, {
subtree: false,
childList: true
});
commonUtil.addObserver(observer);
tbody.appendChild(document.createElement("tr"));
// highlightRow();
}
});
// document.querySelector(".chakra-container > div > button")?.click()
});
}
function filterTable(table) {
// if(equipmentFilter.color === "" && equipmentFilter.type === "") return;
const tableRow = table.querySelectorAll("tbody > tr");
tableRow.forEach(tr => {
const rowColor = getComputedStyle(tr.querySelector("td:nth-child(1) > div")).borderColor;
if (table.filter.color === "" || table.filter.color === rowColor) {
tr.style.display = "";
} else {
tr.style.display = "none";
return;
}
if (table.filter.type === undefined) return;
const equipmentType = tr.querySelector("td:nth-child(2)").textContent;
if (table.filter.type === "" || table.filter.type === equipmentType) {
tr.style.display = "";
} else {
tr.style.display = "none";
return;
}
});
}
function createQuickFilter(table) {
if (document.querySelectorAll(".quick-filter-container").length === 3) return;
const quickFilterContainer = document.createElement("div");
quickFilterContainer.classList.add("quick-filter-container");
quickFilterContainer.innerText = "快篩:";
if (table.filter) {
table.filter.color = "";
} else {
table.filter = {
color: ""
};
}
const colors = ["red", "blue", "cyan", "green", "teal", "orange", "yellow", "pink", "purple", "gray"];
colors.forEach(color => {
const circle = document.createElement("div");
circle.classList.add(`circle-${color}`);
circle.onclick = e => {
let lastClickCircle;
circle.parentNode.querySelectorAll("div").forEach(div => {
if (div.style.backgroundColor !== "") lastClickCircle = div;
div.style.backgroundColor = "";
});
let targetColor = "";
if (lastClickCircle !== circle) {
circle.style.backgroundColor = `var(--chakra-colors-${color}-500)`;
targetColor = color === "gray" ? "rgba(0, 0, 0, 0)" : getComputedStyle(circle).backgroundColor;
}
equipmentFilter.color = targetColor;
table.filter.color = targetColor;
filterTable(table);
// table.querySelectorAll("tr > td:nth-child(1) > div").forEach(div => {
// const tr = div.parentElement.parentElement;
// if(targetColor === "" || getComputedStyle(div).borderColor === targetColor){
// tr.style.display = "";
// }else{
// tr.style.display = "none";
// }
// });
};
quickFilterContainer.appendChild(circle);
});
table.before(quickFilterContainer);
}
function createTypeFilter(table, types) {
if (document.querySelector(`.type-filter-container`)) return;
const typeFilterContainer = document.createElement("div");
typeFilterContainer.classList.add("type-filter-container");
typeFilterContainer.innerText = "類型:";
// const types = ["單手劍", "細劍", "短刀", "單手錘", "盾牌", "雙手劍", "太刀", "雙手斧", "長槍", "大衣", "盔甲", "戒指"];
table.filter = {
type: ""
};
types.forEach(type => {
const choice = document.createElement("div");
const circle = document.createElement("div");
choice.classList.add(`choice`);
circle.classList.add("circle");
choice.appendChild(circle);
choice.append(type);
choice.onclick = e => {
if (!e.target.matches(".circle")) return;
// console.log(choice, circle);
let lastClickCircle;
choice.parentNode.querySelectorAll("div > .circle").forEach(div => {
if (div.style.backgroundColor !== "") lastClickCircle = div;
div.style.backgroundColor = "";
});
table.filter.type = "";
if (lastClickCircle !== circle) {
circle.style.backgroundColor = `var(--chakra-colors-gray-500)`;
table.filter.type = type;
}
filterTable(table);
};
typeFilterContainer.appendChild(choice);
});
table.before(typeFilterContainer);
}
function sortTable(e) {
const tableDOM = e.target.parentElement.parentElement.parentElement;
const sortClassDOM = tableDOM.querySelector(".sort");
if (["攻擊", "防禦", "耐久"].includes(e.target.innerText) && location.pathname === "/items") {
if (sortClassDOM && sortClassDOM.innerText.match("↓|↑")) {
sortClassDOM.innerText = sortClassDOM.innerText.slice(0, -1);
sortClassDOM.classList.remove("sort");
}
return;
}
;
let sortingMethod = "↓";
if (sortClassDOM) {
if (sortClassDOM === e.target) {
sortingMethod = !!~sortClassDOM.innerText.indexOf("↓") ? "↑" : "↓";
}
sortClassDOM.classList.remove("sort");
sortClassDOM.innerText = sortClassDOM.innerText.slice(0, -1);
}
const sortType = e.target.innerText;
const tableColumns = tablesColumns[tableDOM.id];
const targetColumn = tableColumns.find(column => column.name === sortType);
const tbodyDOM = tableDOM.querySelector("tbody");
if (targetColumn.isNumeric) {
const data = commonUtil.getTableData(tableDOM, targetColumn, tableColumns);
let sortedData = data.sort((a, b) => a[sortType] - b[sortType]);
if (sortingMethod === "↑") {
sortedData = sortedData.reverse();
}
sortedData.forEach(item => {
tbodyDOM.appendChild(item.DOM);
});
e.target.innerText += sortingMethod;
e.target.classList.add("sort");
} else {
const data = {};
tbodyDOM.querySelectorAll("tr[role=row]").forEach(rowDOM => {
const index = tableColumns.indexOf(targetColumn);
const key = rowDOM.childNodes[index].innerText;
if (data[key]) {
data[key].push(rowDOM);
} else {
data[key] = [rowDOM];
}
});
Object.keys(data).forEach(key => {
data[key].forEach(rowDOM => {
tbodyDOM.appendChild(rowDOM);
});
});
}
}
function onItemsDetail(containerNodes) {
if (containerNodes[1].tagName === "H2") return;
const targetDom = containerNodes[1];
if (targetDom.classList.contains("addedTrueStat")) return;
let equipmentNameMatch = commonUtil.regexGetValue("(傳說|神話|史詩|完美|頂級|精良|高級|上等|普通|次等|劣質|破爛|垃圾般|屎一般)的 (.*)", targetDom.querySelector("h2").innerText);
if (equipmentNameMatch.length < 2) return console.error("quality error");
//replace把強化次數移除
const equipmentName = equipmentNameMatch[1].replace(/\ \+\ [0-9]+/, "");
const ratio = quality[equipmentNameMatch[0]];
if (!ratio) return console.error("ratio error");
//市集的裝備顯示有較多資訊 故childNodes在7
let statDom, forgeDataDom;
if (targetDom.childNodes.length >= 6 && location.pathname === "/market") {
forgeDataDom = targetDom.querySelector("hr + div");
statDom = targetDom.childNodes[5].childNodes[0];
} else {
forgeDataDom = targetDom.childNodes[2];
statDom = targetDom.querySelector("hr + div").childNodes[0];
}
const [atk, def, luck, kg, dur] = statDom.innerHTML.split("<br>").map((s, index) => {
//有強化的裝備
let value = commonUtil.regexGetValue("([0-9]+) \\(([+-]{1}[0-9]+)\\)", s);
if (value.length === 2) {
return Number(value[0]) - Number(value[1]);
} else {
//耐久數值處理
if (index === 4) {
value = commonUtil.regexGetValue("[0-9]+ / ([0-9]+)", s);
} else {
value = commonUtil.regexGetValue("([0-9]+)", s);
}
//一般情況
if (value.length > 0) {
return Number(value[0]);
}
console.error("parse stat error");
return 0;
}
});
const trueStats = [atk, def, luck].map(num => {
return (num / ratio[0]).toFixed(2);
});
trueStats.push((kg / ratio[1]).toFixed(2));
trueStats.push((dur / ratio[0]).toFixed(2));
const colorSpan = document.createElement("span");
colorSpan.style.color = settingStorage.get("COLOR.TRUE_STATS");
const newStatHTML = statDom.innerHTML.split("<br>").map((s, index) => {
colorSpan.innerText = `(${trueStats[index]})`;
return `${s} ${colorSpan.outerHTML}`;
}).join("<br>");
const forger = forgeDataDom.childNodes[0].textContent.replace("鍛造者:", "");
const forgeTime = new Date(`${new Date().getFullYear()} ${forgeDataDom.childNodes[1].textContent.replace("鍛造時間:", "")}`);
forgeTime.setSeconds(0);
forgeTime.setMilliseconds(0);
const base64 = btoa(encodeURIComponent(`${forgeTime.getTime()},${equipmentName},${forger}`));
let forgeMaterial = "";
if (forgeStorage.get(base64)) {
forgeMaterial = `鍛造材料:${forgeStorage.get(base64)}\n`;
}
targetDom.classList.add("addedTrueStat");
statDom.innerHTML = newStatHTML;
const div = document.createElement("div");
div.innerText = `${forgeMaterial}括號內原始素質僅供參考,有研究出更好的算法可以聯絡插件作者`;
targetDom.appendChild(targetDom.querySelector("hr").cloneNode());
targetDom.appendChild(div);
}
function createMarketButtons(containerNodes) {
let seller = "";
//道具or礦物
if (containerNodes[1].tagName === "H2") {
//販賣者:XXXX
if (!containerNodes[3]?.childNodes[1]?.childNodes[1]?.textContent.startsWith("販賣者:")) return;
seller = containerNodes[3]?.childNodes[1]?.childNodes[1]?.textContent.substring(4);
} else {
//裝備類
// seller = containerNodes[1].childNodes[3].childNodes[0].textContent.substring(4)
if (!containerNodes[1].querySelector("h2 + div")?.childNodes[1]?.childNodes[1]?.textContent.startsWith("販賣者:")) return;
seller = containerNodes[1].querySelector("h2 + div").childNodes[1].childNodes[1].textContent.substring(4);
}
const buttonContainer = containerNodes[containerNodes.length - 1];
const buyButton = buttonContainer.querySelector("button");
if (buyButton === null || settingStorage.get("MARKET.WATCH_LIST").includes(seller) || settingStorage.get("MARKET.BLACK_LIST").includes(seller)) return;
const watchButton = buyButton.cloneNode();
const blacklistButton = buyButton.cloneNode();
watchButton.style.marginRight = "0.5rem";
watchButton.innerText = "關注賣家";
if (watchButton.getAttribute("disabled") === "") watchButton.removeAttribute("disabled");
watchButton.onclick = () => {
let list = settingStorage.get("MARKET.WATCH_LIST");
watchButton.remove();
blacklistButton.remove();
if (!list.includes(seller)) {
list.push(seller);
settingStorage.set("MARKET.WATCH_LIST", list);
settingStorage.save();
}
};
blacklistButton.innerText = "黑名單賣家";
blacklistButton.style.backgroundColor = settingStorage.get("COLOR.WARNING");
blacklistButton.style.marginRight = "0.5rem";
if (blacklistButton.getAttribute("disabled") === "") blacklistButton.removeAttribute("disabled");
blacklistButton.onclick = () => {
watchButton.remove();
blacklistButton.remove();
let list = settingStorage.get("MARKET.BLACK_LIST");
if (!list.includes(seller)) {
list.push(seller);
settingStorage.set("MARKET.BLACK_LIST", list);
settingStorage.save();
}
};
buyButton.before(watchButton);
watchButton.before(blacklistButton);
}
var _default = Init;
exports.default = _default;
},{"../storage/forge":7,"../storage/globalVars":8,"../storage/setting":9,"../utils/common":10,"../utils/event":11,"../utils/ui":12}],5:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _default = {
profile: require("./profile").default,
hunt: require("./hunt").default,
items: require("./items").default,
forge: require("./forge").default
};
exports.default = _default;
},{"./forge":2,"./hunt":3,"./items":4,"./profile":6}],6:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
const commonUtil = require("../utils/common");
function Init() {
commonUtil.bindEvent("/profile", () => {
const actionContainer = document.querySelectorAll(".chakra-container")[2];
if (actionContainer) {
actionContainer.querySelector("div > button:nth-child(3)").onclick = calcTime;
commonUtil.clearTimers();
}
});
}
function calcTime() {
const actionContainer = document.querySelectorAll(".chakra-container")[2];
const actionLogContainer = actionContainer.querySelector("div > :nth-child(12)");
if (actionLogContainer.tagName === "HR") return;
const actionTime = actionContainer.querySelector("div > span").innerText;
const observer = new MutationObserver(function (e) {
const row = actionLogContainer.querySelector("div:nth-child(1) > div:nth-child(2) > div");
// console.log("row", row, row.innerText);
row.innerText += ` ${actionTime}`;
setTimeout(() => {
const msgArray = JSON.parse(localStorage.generalActionMessages);
// console.log("msgArray", msgArray);
msgArray[0].messages[0].m = row.innerText;
localStorage.generalActionMessages = JSON.stringify(msgArray);
}, 1500);
observer.disconnect();
// console.log("disconnnnnnnnnnnn");
});
observer.observe(actionLogContainer, {
subtree: true,
childList: true,
characterData: true
});
commonUtil.addObserver(observer);
}
var _default = Init;
exports.default = _default;
},{"../utils/common":10}],7:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.deleteIfKeyExist = deleteIfKeyExist;
exports.get = get;
exports.load = load;
exports.save = save;
exports.set = set;
const FORGE_STORAGE_NAME = "forgeLog";
let FORGE_LOG = load();
function set(key, value) {
FORGE_LOG[key] = value;
}
function get(key) {
return FORGE_LOG[key] ?? "";
}
function load() {
if (localStorage[FORGE_STORAGE_NAME]) {
try {
return JSON.parse(localStorage[FORGE_STORAGE_NAME]);
} catch (e) {
console.error("load forge log failed", e);
}
}
return {};
}
function save() {
localStorage[FORGE_STORAGE_NAME] = JSON.stringify(FORGE_LOG);
}
function deleteIfKeyExist(key) {
if (FORGE_LOG[key]) {
delete FORGE_LOG[key];
save();
}
}
},{}],8:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.get = get;
exports.set = set;
const globalVars = {
VERSION: "1.37.4",
LATEST_VERSION: "",
UPDATE_CHECK_INTERVAEL: 3600,
HIGHTLIGHT_ROW: {
equipments: [],
mines: [],
items: []
}
};
function get(key) {
return globalVars[key];
}
function set(key, value) {
globalVars[key] = value;
}
},{}],9:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.get = get;
exports.save = save;
exports.set = set;
const commonUtil = require("../utils/common");
const STORAGE_NAME = "SGO_Interface_Optimization";
const DEFAULT_SETTINGS = {
COLOR: {
TIPS: "#9AE6B4",
//一般提示
WARNING: "#FC8181",
//紅色警告
TRUE_STATS: "#FEEBC8",
//裝備原始素質顏色
ZONE_LEVEL: "#FF95CA",
//樓層切換的顏色
MARKET_WATCH: "#ffff00",
// 關注中的訂單
EXP_BAR_BACKGROUND: "#57595b",
EXP_BAR_FILL: "#aadc1e",
EXP_BAR_FONT: "#fbfbfb"
},
WARNING: {
EQUIPMENT: 20,
//裝備低於多少耐久
HP: 60,
//血量單次耗損幾成
SP: 100 //體力低於多少
},
GENERAL: {
DISABLE_BAD_BUTTON: false,
//將 false 改成 true,即可禁用"搶劫"與"我要超渡你"按鍵
DISABLE_TRUE_STATS: false,
DISABLE_MARKET_FUNCTION: true,
HIDE_REST_BUTTON: false,
MOBILE_WRAP_NAVBAR: false,
MOBILE_HUNT_REPORT: true,
HUNT_STATUS_PERCENT: false,
SHOW_EXP_BAR: false,
RED_BACKBROUND_WHEN_EQUIPMENT_BROKEN: false,
FORGE_NOTIFICATION: false,
EXP_BAR_FILL_BACKGROUND_IMAGE_URL: "",
BACKGROUND_IMAGE_URL: "",
ITEM_FILTER_ENCODE: ""
},
MARKET: {
WATCH_LIST: [],
BLACK_LIST: []
},
ITEM_RECORD: {
ENABLE: false,
APPLY_FILTER: false,
RECORDS: {}
},
UPDATE: {
LAST_CHECK_TIMESTAMP: 0,
LATEST_VERSION: ""
},
recipe: {}
};
let SETTINGS = load();
function load() {
if (localStorage[STORAGE_NAME]) {
try {
return JSON.parse(localStorage[STORAGE_NAME]);
} catch (e) {}
}
return structuredClone(DEFAULT_SETTINGS);
}
function save() {
localStorage[STORAGE_NAME] = JSON.stringify(SETTINGS);
}
function get(key) {
//檢查是否有新的類別設定或缺少類別設定
if (key.split(".").length === 1) {
Object.keys(DEFAULT_SETTINGS).forEach(classKey => {
// if(getObjectValueByRecursiveKey(SETTINGS, `${key}.${k}`) !== undefined) return;
// commonUtil.setObjectValueByRecursiveKey(SETTINGS, `${key}.${k}`, DEFAULT_SETTINGS[key][k])
// SETTINGS[key][k] = DEFAULT_SETTINGS[key][k];
if (SETTINGS[classKey] !== undefined) return;
SETTINGS[classKey] = structuredClone(DEFAULT_SETTINGS[classKey]);
save();
});
}
if (commonUtil.getObjectValueByRecursiveKey(SETTINGS, key) === null) {
// commonUtil.setObjectValueByRecursiveKey(SETTINGS, key, commonUtil.getObjectValueByRecursiveKey(structuredClone(DEFAULT_SETTINGS), key))
set(key, commonUtil.getObjectValueByRecursiveKey(structuredClone(DEFAULT_SETTINGS), key));
// SETTINGS[key] = commonUtil.getObjectValueByRecursiveKey(structuredClone(DEFAULT_SETTINGS), key)
save();
} else if (key === "recipe" && typeof SETTINGS[key] === 'string') {
try {
SETTINGS[key] = JSON.parse(SETTINGS[key]);
} catch (e) {
SETTINGS[key] = {};
}
save();
}
return commonUtil.getObjectValueByRecursiveKey(SETTINGS, key);
}
function set(key, value) {
commonUtil.setObjectValueByRecursiveKey(SETTINGS, key, value);
}
},{"../utils/common":10}],10:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.addObserver = addObserver;
exports.bindEvent = bindEvent;
exports.clearObservers = clearObservers;
exports.clearTimers = clearTimers;
exports.getObjectValueByRecursiveKey = getObjectValueByRecursiveKey;
exports.getTableColumns = getTableColumns;
exports.getTableData = getTableData;
exports.isMobileDevice = isMobileDevice;
exports.itemApplyFilter = itemApplyFilter;
exports.regexGetValue = regexGetValue;
exports.setObjectValueByRecursiveKey = setObjectValueByRecursiveKey;
const settingStorage = require("../storage/setting");
let filter;
const observers = [];
const timers = [];
function getObjectValueByRecursiveKey(obj, recursiveKey) {
const keys = recursiveKey.split(".");
let tempObj = obj;
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
if (tempObj[key] === undefined) {
// tempObj = null;
return null;
}
tempObj = tempObj[key];
}
// keys.forEach(key => {
// });
return tempObj;
}
function setObjectValueByRecursiveKey(obj, recursiveKey, value) {
const keys = recursiveKey.split(".");
const lastKey = keys.pop();
let tempObj = obj;
keys.forEach(key => {
if (tempObj[key] === undefined || typeof tempObj[key] !== "object") tempObj[key] = {};
tempObj = tempObj[key];
});
tempObj[lastKey] = value;
return obj;
}
function isMobileDevice() {
const mobileDevices = ['Android', 'webOS', 'iPhone', 'iPad', 'iPod', 'BlackBerry', 'Windows Phone'];
for (let i = 0; i < mobileDevices.length; i++) {
if (navigator.userAgent.match(mobileDevices[i])) {
return true;
}
}
return false;
}
function itemApplyFilter(itemName, config) {
if (!Array.isArray(filter)) {
const itemFilterEncode = settingStorage.get("GENERAL.ITEM_FILTER_ENCODE");
try {
filter = JSON.parse(decodeURIComponent(atob(itemFilterEncode)));
} catch (e) {
// console.error("parse error", e);
filter = [];
}
}
let result = false;
filter.forEach(filter => {
if (filter.items.includes(itemName)) {
if (config.highlight) config.dom.style.color = filter.color;
if (config.playSound) {
const audio = new Audio(filter.sound);
audio.volume = filter.volume;
audio.play().catch(err => console.error(err));
}
result = true;
}
});
return result;
}
/**
* @returns {{name: string, isNumeric: boolean}[]}
*/
function getTableColumns(table, tableHeaderClickEvent) {
const ths = table.querySelectorAll("thead > tr > th");
const tableColumns = [];
ths.forEach(th => {
const column = {
name: th.innerText,
isNumeric: th.getAttribute("data-is-numeric") ? true : false
};
tableColumns.push(column);
if (tableHeaderClickEvent) th.onclick = tableHeaderClickEvent;
});
return tableColumns;
}
/**
* @param {HTMLTableElement} targetColumnName
* @param {{name: string, isNumeric: boolean}} targetColumn
* @param {{name: string, isNumeric: boolean}[]} columns
* @returns {{DOM: HTMLTableRowElement}[]}
*/
function getTableData(table, targetColumn, columns) {
const data = [];
if (!columns) {
columns = getTableColumns(table);
}
const index = columns.findIndex(column => column.name === targetColumn.name);
if (!!~index === false) return [];
table.querySelectorAll("tbody > tr[role=row]").forEach(row => {
const rowData = {};
//金錢逗號處理
const text = row.childNodes[index].innerText.replaceAll(",", "");
if (targetColumn.name === "耐久") {
rowData[targetColumn.name] = Number(regexGetValue("([0-9]+) / [0-9]+", text)[0]);
} else {
rowData[targetColumn.name] = targetColumn.isNumeric ? Number(text) : text;
}
rowData["DOM"] = row;
data.push(rowData);
});
return data;
}
function regexGetValue(pattern, str) {
const match = new RegExp(pattern).exec(str);
if (match) {
return match.slice(1);
} else {
return [];
}
}
function addObserver(observer) {
observers.push(observer);
}
function clearObservers() {
// console.log("clear")
observers.forEach(observer => {
observer.disconnect();
});
observers.length = 0;
}
function clearTimers() {
// console.log("clear")
timers.forEach(timer => {
clearInterval(timer);
});
timers.length = 0;
}
function bindEvent(pathname, timerEvent) {
const timer = setInterval(() => {
if (Array.isArray(pathname)) {
if (pathname.filter(path => path === location.pathname).length === 0) {
clearInterval(timer);
return;
}
} else {
if (location.pathname !== pathname) {
clearInterval(timer);
return;
}
}
timerEvent();
}, 100);
timers.push(timer);
}
},{"../storage/setting":9}],11:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.clearSubscribeEvents = clearSubscribeEvents;
exports.subscribeApi = subscribeApi;
const settingStorage = require("../storage/setting");
const commonUtil = require("./common");
const uiUtil = require("./ui");
const forgeStorage = require("../storage/forge");
const globalVarsStorage = require("../storage/globalVars");
let updateAnnouncement;
let forgeNotificationTimes = [];
const subscribeEvents = [];
const specialSubscribeEvents = {
profile: [data => {
if (location.pathname === "/hunt" && settingStorage.get("GENERAL.HUNT_STATUS_PERCENT")) {
data.fullHp += ` (${Math.floor(data.hp / data.fullHp * 100)}%)`;
data.fullSp += ` (${Math.floor(data.sp / data.fullSp * 100)}%)`;
}
}, data => {
if (settingStorage.get("GENERAL.SHOW_EXP_BAR")) {
if (!document.querySelector(".exp-container")) uiUtil.createExpBar();
const expBar = document.querySelector("#exp-bar");
const expBarFill = document.querySelector("#exp-bar-fill");
let percent = Math.floor(data.exp / data.nextExp * 1000) / 10.0;
document.querySelector("#exp-bar-level-label").textContent = `LV.${data.lv}`;
document.querySelector("#exp-bar-exp-label").textContent = `EXP:${data.exp} / ${data.nextExp} (${percent}%)`;
expBar.style.backgroundColor = settingStorage.get("COLOR.EXP_BAR_BACKGROUND");
expBarFill.style.backgroundColor = settingStorage.get("COLOR.EXP_BAR_FILL");
if (settingStorage.get("GENERAL.EXP_BAR_FILL_BACKGROUND_IMAGE_URL") !== "") {
expBar.style.backgroundImage = `url(${settingStorage.get("GENERAL.EXP_BAR_FILL_BACKGROUND_IMAGE_URL")})`;
expBar.style.backgroundSize = "100% 24px";
expBarFill.style.backgroundColor = settingStorage.get("COLOR.EXP_BAR_BACKGROUND");
expBarFill.style.left = "unset";
expBarFill.style.right = "0px";
percent = 100 - percent;
// expBarFill.style.backgroundImage = `url(${settingStorage.get("GENERAL.EXP_BAR_FILL_BACKGROUND_IMAGE_URL")})`
// expBarFill.style.backgroundSize = "100% 24px";
} else {
expBar.style.backgroundImage = ``;
expBarFill.style.left = "";
expBarFill.style.right = "";
}
expBarFill.style.width = `${percent}%`;
document.querySelector(".exp-container").style.color = settingStorage.get("COLOR.EXP_BAR_FONT");
}
}, data => {
if (settingStorage.get("GENERAL.FORGE_NOTIFICATION") && 'Notification' in window && Notification.permission === "granted") {
if (!data.forgingCompletionTime || data.forgingCompletionTime - new Date().getTime() < 0 || forgeNotificationTimes.includes(data.forgingCompletionTime)) return;
const currentForgeCompletionTime = data.forgingCompletionTime;
forgeNotificationTimes.push(currentForgeCompletionTime);
setTimeout(() => {
new Notification(`${data.nickname} 鍛造已完成!`);
forgeNotificationTimes = forgeNotificationTimes.filter(time => time !== currentForgeCompletionTime);
}, currentForgeCompletionTime - new Date().getTime());
}
}],
announcement: [data => {
if (data.announcement) return;
if (updateAnnouncement) {
data.announcement = structuredClone(updateAnnouncement);
return;
}
const latestVersion = globalVarsStorage.get("LATEST_VERSION");
let haveNewVersion = false;
if (!/[0-9]+\.[0-9]+\.[0-9]+/.test(latestVersion)) return;
/*
const setting = JSON.parse(localStorage.getItem("SGO_Interface_Optimization"))
setting.UPDATE.LAST_CHECK_TIMESTAMP = 0
setting.UPDATE.LATEST_VERSION = ""
localStorage.setItem("SGO_Interface_Optimization", JSON.stringify(setting));
*/
if (settingStorage.get("UPDATE.LAST_CHECK_TIMESTAMP") + globalVarsStorage.get("UPDATE_CHECK_INTERVAEL") < new Date().getTime() && settingStorage.get("UPDATE.LATEST_VERSION") !== latestVersion) {
settingStorage.set("UPDATE.LATEST_VERSION", latestVersion);
settingStorage.set("UPDATE.LAST_CHECK_TIMESTAMP", new Date().getTime());
settingStorage.save();
const currentVersionSplit = globalVarsStorage.get("VERSION").split(".");
const latestVersionSplit = latestVersion.split(".");
//依序比較大中小版本號
for (let i = 0; i < 3; i++) {
if (Number(latestVersionSplit[i]) > Number(currentVersionSplit[i])) {
haveNewVersion = true;
break;
}
}
if (haveNewVersion) {
data.announcement = {
status: "info",
message: `[SGO介面優化] 發現插件新版本 Ver ${latestVersion}`
};
updateAnnouncement = structuredClone(data.announcement);
}
}
}]
};
const apiData = {};
const _fetch = window.fetch;
window.fetch = async (url, fetchOptions) => {
try {
const originResp = await _fetch(url, fetchOptions);
const ab = await originResp.arrayBuffer();
const jsonObject = JSON.parse(new TextDecoder("utf-8").decode(ab));
//特殊問題 部分電腦的url不是字串而是requestInfo 需要取其中的url property來拿到api網址
const apiUrl = commonUtil.regexGetValue("api/(.*)", typeof url === "string" ? url : url.url);
if (apiUrl.length) {
if (jsonObject.profile) {
apiData["profile"] = jsonObject.profile;
triggerEventHook("profile");
jsonObject.profile = structuredClone(apiData["profile"]);
}
if (apiUrl[0].match("trades\\?category=[a-z]+")) {
//特例常駐subscribe
apiData[apiUrl[0]] = structuredClone(jsonObject);
triggerEventHook(apiUrl[0]);
const category = commonUtil.regexGetValue("trades\\?category=([a-z]+)", apiUrl[0])[0];
const highlightRow = globalVarsStorage.get("HIGHTLIGHT_ROW");
highlightRow[category].length = 0;
if (!settingStorage.get("GENERAL.DISABLE_MARKET_FUNCTION")) {
const blackList = settingStorage.get("MARKET.BLACK_LIST");
apiData[apiUrl[0]].trades = apiData[apiUrl[0]].trades.filter(trade => !blackList.includes(trade.sellerName));
const watchList = settingStorage.get("MARKET.WATCH_LIST");
for (let i = 0; i < apiData[apiUrl[0]].trades.length; i++) {
const trade = apiData[apiUrl[0]].trades[i];
if (watchList.includes(trade.sellerName)) {
highlightRow[category].push(i);
}
}
}
return new Response(new TextEncoder().encode(JSON.stringify(apiData[apiUrl[0]])));
} else if (apiUrl[0].match("equipment\/([0-9]+)\/recycle")) {
//清除回收裝備的鍛造資料
const equipmentId = commonUtil.regexGetValue("equipment\/([0-9]+)\/recycle", apiUrl[0])[0];
const equipment = apiData["items"]?.equipments.find(equipment => equipment.id === Number(equipmentId));
if (equipment && equipment.crafter !== null) {
const forgeTime = new Date(`${equipment.craftedTime}`);
forgeTime.setSeconds(0);
forgeTime.setMilliseconds(0);
const base64 = btoa(encodeURIComponent(`${forgeTime.getTime()},${equipment.name},${equipment.crafter}`));
forgeStorage.deleteIfKeyExist(base64);
}
}
apiData[apiUrl[0]] = structuredClone(jsonObject);
triggerEventHook(apiUrl[0]);
// console.log(apiUrl[0], apiData);
return new Response(new TextEncoder().encode(JSON.stringify(apiData[apiUrl[0]])));
} else {
const uint8Array = new TextEncoder().encode(JSON.stringify(jsonObject));
const newResp = new Response(uint8Array);
return newResp;
}
} catch (error) {
console.error(error);
}
};
function triggerEventHook(url) {
if (specialSubscribeEvents[url]) {
specialSubscribeEvents[url].forEach(e => {
e(apiData[url]);
});
}
if (subscribeEvents[url]) {
const removes = [];
subscribeEvents[url].forEach(element => {
if (!element.forever) removes.push(element);
element.event(apiData[url]);
});
if (removes.length) subscribeEvents[url] = subscribeEvents[url].filter(element => !removes.includes(element));
}
}
function subscribeApi(url, event, forever = true) {
if (!subscribeEvents[url]) {
subscribeEvents[url] = [];
}
subscribeEvents[url].push({
event,
forever
});
}
function clearSubscribeEvents() {
Object.keys(subscribeEvents).forEach(key => {
delete subscribeEvents[key];
});
}
},{"../storage/forge":7,"../storage/globalVars":8,"../storage/setting":9,"./common":10,"./ui":12}],12:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.createExpBar = createExpBar;
exports.createOpenDialogButton = createOpenDialogButton;
exports.createSearchUI = createSearchUI;
exports.createSettingUI = createSettingUI;
exports.registerSettingUIEvent = registerSettingUIEvent;
exports.wrapNavbar = wrapNavbar;
const commonUtil = require("../utils/common");
const settingStorage = require("../storage/setting");
const globalVarsStorage = require("../storage/globalVars");
//改變navbar
function wrapNavbar() {
const style = document.createElement("style");
style.innerHTML = `
nav {
flex-wrap: wrap;
min-width: auto !important;
}
#__next > div > div:nth-child(2) {
height: 120px;
}
`;
document.body.appendChild(style);
// document.querySelector("nav").style.flexWrap = "wrap";
// document.querySelector("nav").style.minWidth = "unset";
}
// 搜尋UI
function createSearchUI(labelText, inputId) {
const div = document.createElement("div");
div.style.display = "flex";
div.style.alignItems = "center";
div.style.marginBottom = "1rem";
div.innerHTML = `<label style="width: 84px;">${labelText}</label>`;
const input = document.createElement("input");
input.type = "text";
input.id = inputId;
input.autocomplete = "off";
input.style.cssText = `
width: var(--chakra-sizes-full);
min-width: 0px;
outline: transparent solid 2px;
outline-offset: 2px;
position: relative;
appearance: none;
transition-property: var(--chakra-transition-property-common);
transition-duration: var(--chakra-transition-duration-normal);
font-size: var(--chakra-fontSizes-md);
padding-inline-start: var(--chakra-space-4);
padding-inline-end: var(--chakra-space-4);
height: var(--chakra-sizes-10);
border-radius: var(--chakra-radii-md);
border-width: 2px;
border-style: solid;
border-image: initial;
border-color: var(--chakra-colors-transparent);
background: var(--chakra-colors-whiteAlpha-100);
`;
// input.onchange = inputEvent
div.appendChild(input);
return [div, input];
}
//開啟系統設定UI
function createOpenDialogButton() {
//開啟設定的按鍵
const openDialogBtn = document.createElement("button");
openDialogBtn.id = "open-dialog-btn";
openDialogBtn.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-settings" width="50" height="50" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M10.325 4.317c.426 -1.756 2.924 -1.756 3.35 0a1.724 1.724 0 0 0 2.573 1.066c1.543 -.94 3.31 .826 2.37 2.37a1.724 1.724 0 0 0 1.065 2.572c1.756 .426 1.756 2.924 0 3.35a1.724 1.724 0 0 0 -1.066 2.573c.94 1.543 -.826 3.31 -2.37 2.37a1.724 1.724 0 0 0 -2.572 1.065c-.426 1.756 -2.924 1.756 -3.35 0a1.724 1.724 0 0 0 -2.573 -1.066c-1.543 .94 -3.31 -.826 -2.37 -2.37a1.724 1.724 0 0 0 -1.065 -2.572c-1.756 -.426 -1.756 -2.924 0 -3.35a1.724 1.724 0 0 0 1.066 -2.573c-.94 -1.543 .826 -3.31 2.37 -2.37c1 .608 2.296 .07 2.572 -1.065z" />
<circle cx="12" cy="12" r="3" />
</svg>
`;
const style = document.createElement("style");
//scss
style.innerText = `
*{box-sizing:border-box}.wrapper{display:flex;align-items:center;justify-content:center;background-color:rgba(15,19,26,.8);height:100vh;position:fixed;width:100%;left:0;top:0;overflow:auto;z-index:9999}.header{display:flex;justify-content:space-between;padding:1rem 1rem 0 1rem;position:absolute;width:calc(100% - 56px);top:0;left:56px;border-bottom:1px solid #3c3f43}.header button{height:100%}.header h1{color:#fff}.header #close-dialog-btn{margin-left:auto}.content-container{padding-top:50px;margin-left:56px;height:100%}.content-container .content{display:flex;margin:0 1rem 1rem 1rem;flex-direction:column;height:100%;overflow-y:scroll}.content-container .content hr{width:100%}.panel{position:relative;width:100%;display:flex;flex-direction:column;display:none;opacity:0}.panel input[type=checkbox]{margin:.5rem}.panel input[type=text]{background-color:#1a1d24;background-image:none;border:1px solid #3c3f43;border-radius:6px;color:#e9ebf0;display:block;font-size:14px;line-height:1.42857143;padding:7px 11px;transition:border-color .3s ease-in-out;width:100px}.panel input[type=color]{background-color:#292d33;width:50px}.panel button{border-radius:.375rem;padding:.25rem}.panel button.warning{background-color:var(--chakra-colors-red-500)}.panel button.warning:hover{background-color:var(--chakra-colors-red-600)}.panel[expand]{display:block;opacity:1}.panel-header{width:100%;padding:20px}.panel-header span{color:#fff;font-size:16px;line-height:1.25}.panel-body{padding:0 20px 20px 20px}.panel-body .row{margin-top:1rem;display:flex;align-items:center}.panel-body .row label{color:#a4a9b3;margin-right:1rem}.panel-body .row input{margin-right:1rem}.panel-body .row a{color:#a4a9b3;margin-right:1rem;text-decoration:underline}.panel-body .row a:hover{background-color:#3c3f43}.panel-body .row.table{flex-direction:column;align-items:flex-start}.record{width:100%;border-bottom:1px solid #3c3f43}.record .record-header{margin-top:.25rem}.record .record-body{display:flex;flex-direction:column}.record .record-item{display:flex;width:80%;margin:.5rem 0}.record .record-quatity{margin-left:auto}.grid{margin-top:10px;width:100%;color:#a4a9b3;background-color:#1a1d24}.grid div{border-bottom:1px solid #292d33;width:100%;height:40px;padding:10px}.grid .grid-row{display:flex;align-items:center}.grid .grid-row:hover{background-color:#3c3f43}.grid .grid-row button{font-size:14px;border:none;background-color:rgba(0,0,0,0);color:#9146ff;margin-left:auto}.grid .grid-row button:hover{cursor:pointer}.description{margin:0px;color:#a4a9b3;line-height:1.5;font-size:8px}.dialog{width:800px;height:500px;position:relative;overflow:auto;z-index:9999;display:flex;background-color:#292d33;border-radius:6px;box-shadow:0 4px 4px rgba(0,0,0,.12),0 0 10px rgba(0,0,0,.06);display:block}.dialog .navbar{height:500px;background-color:#1a1d24;width:56px;position:fixed;display:flex;flex-direction:column}.dialog .navbar button{height:50px}.dialog .navbar button:hover{background-color:#292d33}.dialog .navbar button[active]{background-color:#292d33}.dialog .right-container{margin-left:56px}#open-dialog-btn{position:-webkit-sticky;position:sticky;left:0;bottom:20px;margin-right:1rem;z-index:9998;color:#7d7d7d;background-color:rgba(0,0,0,0);border:none}#open-dialog-btn:hover{color:#fff}#exp-bar{position:fixed;bottom:0px;width:100%;height:24px}#exp-bar-fill{position:fixed;bottom:0px;left:0px;height:24px}.exp-container{display:flex;justify-content:flex-end;position:fixed;width:100%;bottom:0px}.quick-filter-container{display:flex;margin-bottom:.5rem;align-items:center;-webkit-box-align:center}.quick-filter-container div{width:18px;height:18px;margin-right:var(--chakra-space-3);border-radius:50%;background:var(--chakra-colors-transparent);border-width:2px;border-style:solid;-o-border-image:initial;border-image:initial;cursor:pointer}.quick-filter-container .circle-red{border-color:var(--chakra-colors-red-500)}.quick-filter-container .circle-red:hover{background-color:var(--chakra-colors-red-300)}.quick-filter-container .circle-blue{border-color:var(--chakra-colors-blue-500)}.quick-filter-container .circle-blue:hover{background-color:var(--chakra-colors-blue-300)}.quick-filter-container .circle-cyan{border-color:var(--chakra-colors-cyan-500)}.quick-filter-container .circle-cyan:hover{background-color:var(--chakra-colors-cyan-300)}.quick-filter-container .circle-green{border-color:var(--chakra-colors-green-500)}.quick-filter-container .circle-green:hover{background-color:var(--chakra-colors-green-300)}.quick-filter-container .circle-teal{border-color:var(--chakra-colors-teal-500)}.quick-filter-container .circle-teal:hover{background-color:var(--chakra-colors-teal-300)}.quick-filter-container .circle-orange{border-color:var(--chakra-colors-orange-500)}.quick-filter-container .circle-orange:hover{background-color:var(--chakra-colors-orange-300)}.quick-filter-container .circle-yellow{border-color:var(--chakra-colors-yellow-500)}.quick-filter-container .circle-yellow:hover{background-color:var(--chakra-colors-yellow-300)}.quick-filter-container .circle-pink{border-color:var(--chakra-colors-pink-500)}.quick-filter-container .circle-pink:hover{background-color:var(--chakra-colors-pink-300)}.quick-filter-container .circle-purple{border-color:var(--chakra-colors-purple-500)}.quick-filter-container .circle-purple:hover{background-color:var(--chakra-colors-purple-300)}.quick-filter-container .circle-gray{border-color:var(--chakra-colors-gray-500)}.quick-filter-container .circle-gray:hover{background-color:var(--chakra-colors-gray-300)}.type-filter-container{display:flex;align-items:center;flex-wrap:wrap}.type-filter-container .choice{display:flex;align-items:center;margin-right:.5rem}.type-filter-container .choice .circle{width:18px;height:18px;margin-right:var(--chakra-space-1);border-radius:50%;background:var(--chakra-colors-transparent);border-width:2px;border-style:solid;-o-border-image:initial;border-image:initial;border-color:var(--chakra-colors-gray-500);cursor:pointer;display:block}.type-filter-container .choice .circle:hover{background-color:var(--chakra-colors-gray-300)}
`;
// document.querySelector("#open-dialog-btn").onclick = () => {createSettingUI(); registerSettingUIEvent();}
openDialogBtn.onclick = () => {
createSettingUI();
registerSettingUIEvent();
document.body.style.overflow = "hidden";
};
document.body.appendChild(style);
document.body.appendChild(openDialogBtn);
}
//系統設定UI
function createSettingUI() {
const wrapper = document.createElement("div");
wrapper.className = "wrapper";
wrapper.style.display = "";
wrapper.innerHTML = `
<div class="dialog">
<div class="navbar">
</div>
<div class="header">
<h1>SGO介面優化插件 Ver${globalVarsStorage.get("VERSION")}</h1>
<button id="reset-settings-btn" hidden>RESET</button>
<button id="close-dialog-btn">X</button>
</div>
<div class="content-container">
<div class="content">
</div>
</div>
</div>`;
const rowEvent = {
checkbox: e => {
const element = e.target;
settingStorage.set(element.getAttribute("bind-setting"), element.checked);
settingStorage.save();
},
colorInput: e => {
const element = e.target;
const bindSetting = element.getAttribute("bind-setting");
// if(!/^#[0-9a-fA-F]{6}$|transparent/.test(element.value)){
// element.value = settingStorage.get(bindSetting);
// }
settingStorage.set(bindSetting, element.value);
element.nextElementSibling.style.color = element.value;
settingStorage.save();
},
numberInput: e => {
const element = e.target;
const bindSetting = element.getAttribute("bind-setting");
if (element.value === "" || Number.isNaN(Number(element.value))) {
element.value = settingStorage.get(bindSetting);
return;
}
settingStorage.set(bindSetting, Number(element.value));
settingStorage.save();
},
input: e => {
const element = e.target;
const bindSetting = element.getAttribute("bind-setting");
settingStorage.set(bindSetting, element.value);
settingStorage.save();
}
};
const panel = [{
category: "一般",
description: "一般功能的開啟與關閉",
rows: [{
id: "bad-button",
type: "checkbox",
label: "禁用搶劫與超渡按鍵",
bindSetting: "GENERAL.DISABLE_BAD_BUTTON"
}, {
id: "rest-button",
type: "checkbox",
label: "隱藏狩獵頁面的休息按鍵",
bindSetting: "GENERAL.HIDE_REST_BUTTON"
}, {
id: "disable-true-stats",
type: "checkbox",
label: "關閉原始素質顯示",
bindSetting: "GENERAL.DISABLE_TRUE_STATS"
}, {
id: "disable-market-function",
type: "checkbox",
label: "關閉市場黑名單與關注名單功能",
bindSetting: "GENERAL.DISABLE_MARKET_FUNCTION"
}, {
id: "hunt-status-percent",
type: "checkbox",
label: "顯示血量、體力百分比(僅在狩獵頁有效)",
bindSetting: "GENERAL.HUNT_STATUS_PERCENT"
}, {
id: "show-exp-bar",
type: "checkbox",
label: "顯示經驗條",
bindSetting: "GENERAL.SHOW_EXP_BAR"
}, {
id: "red-backround-when-equipment-broken",
type: "checkbox",
label: "裝備損壞超級提示",
bindSetting: "GENERAL.RED_BACKBROUND_WHEN_EQUIPMENT_BROKEN"
}, {
id: "enable-forge-notification",
type: "checkbox",
label: "開啟鍛造完成通知",
bindSetting: "GENERAL.FORGE_NOTIFICATION",
event: e => {
if (!settingStorage.get("GENERAL.FORGE_NOTIFICATION")) return;
if ('Notification' in window) {
console.log('Notification permission default status:', Notification.permission);
Notification.requestPermission(function (status) {
console.log('Notification permission status:', status);
});
}
}
}, {
id: "exp-bar-fill-background-image-url",
type: "input",
label: "自訂經驗條填充圖片",
bindSetting: "GENERAL.EXP_BAR_FILL_BACKGROUND_IMAGE_URL"
}, {
id: "background-image-url",
type: "input",
label: "自訂背景圖片",
bindSetting: "GENERAL.BACKGROUND_IMAGE_URL"
}, {
id: "item-filter-encode",
type: "input",
label: "物品過濾器編碼",
bindSetting: "GENERAL.ITEM_FILTER_ENCODE"
}, {
type: "a",
label: "SGO-物品過濾器-Editor",
link: "https://sgo-filter.wind-tech.tw/"
}]
}, {
category: "手機",
description: "手機特別功能開啟與關閉",
mobile: true,
rows: [{
id: "mobile-wrap-navbar",
type: "checkbox",
label: "導覽列換行",
bindSetting: "GENERAL.MOBILE_WRAP_NAVBAR"
}, {
id: "mobile-hunt-report",
type: "checkbox",
label: "精簡狩獵結果",
bindSetting: "GENERAL.MOBILE_HUNT_REPORT"
}]
}, {
category: "顏色",
description: "設定插件各種提示的顏色",
rows: [{
id: "tips",
type: "colorInput",
label: "一般提示",
bindSetting: "COLOR.TIPS"
}, {
id: "warning",
type: "colorInput",
label: "紅色警告",
bindSetting: "COLOR.WARNING"
}, {
id: "true-stats",
type: "colorInput",
label: "裝備原始素質",
bindSetting: "COLOR.TRUE_STATS"
}, {
id: "zone-level",
type: "colorInput",
label: "到達新樓層",
bindSetting: "COLOR.ZONE_LEVEL"
}, {
id: "market-watch",
type: "colorInput",
label: "關注中的賣家外框顏色",
bindSetting: "COLOR.MARKET_WATCH"
}, {
id: "exp-bar-background",
type: "colorInput",
label: "經驗條背景色",
bindSetting: "COLOR.EXP_BAR_BACKGROUND"
}, {
id: "exp-bar-fill-color",
type: "colorInput",
label: "經驗條填充色",
bindSetting: "COLOR.EXP_BAR_FILL"
}, {
id: "exp-bar-font",
type: "colorInput",
label: "經驗條字體顏色",
bindSetting: "COLOR.EXP_BAR_FONT"
}]
}, {
category: "警示",
description: "設定警示功能的數值",
rows: [{
id: "equipment",
type: "numberInput",
label: "裝備耐久低於(數值)",
bindSetting: "WARNING.EQUIPMENT"
}, {
id: "hp",
type: "numberInput",
label: "血量單次耗損(百分比)",
bindSetting: "WARNING.HP"
}, {
id: "sp",
type: "numberInput",
label: "體力低於(數值)",
bindSetting: "WARNING.SP"
}]
}, {
category: "市場",
description: "刪除市場的關注名單與黑名單<BR>新增請至市場點擊訂單下方即可新增",
rows: [{
id: "watch-list",
type: "table",
label: "關注名單",
header: "名字",
bindSetting: "MARKET.WATCH_LIST"
}, {
id: "black-list",
type: "table",
label: "黑名單",
header: "名字",
bindSetting: "MARKET.BLACK_LIST"
}]
}, {
category: "狩獵記錄",
description: "記錄狩獵獲得的物品",
rows: [{
id: "item-record",
type: "checkbox",
label: "啟用狩獵記錄",
bindSetting: "ITEM_RECORD.ENABLE"
}, {
id: "reset-item-record",
type: "button",
class: "warning",
label: "重置狩獵記錄",
bindSetting: "ITEM_RECORD.RECORDS",
event: e => {
const currentPanel = e.target.closest(".panel");
currentPanel.querySelector(".row.table").innerHTML = `
<label>當前記錄</label>
<div style="margin: 1rem">無</div>
`;
settingStorage.set("ITEM_RECORD.RECORDS", {});
settingStorage.save();
}
}, {
id: "item-record-apply-filter",
type: "checkbox",
label: "狩獵記錄套用過濾器",
bindSetting: "ITEM_RECORD.APPLY_FILTER",
event: e => {
const checked = e.target.checked;
const currentPanel = e.target.closest(".panel");
currentPanel.querySelectorAll(".record-item").forEach(item => {
if (checked) {
const result = commonUtil.itemApplyFilter(item.innerText.split("\n")[0], {
highlight: true,
dom: item
});
if (!result) item.style.display = "none";
} else {
item.style.display = item.style.color = "";
}
});
}
}, {
id: "item-record-table",
type: "record-table",
label: "當前記錄",
bindSetting: "ITEM_RECORD.RECORDS"
}]
}, {
category: "贊助",
description: "",
rows: [{
type: "customize",
html: `
<p class="description" style="font-size: 1rem">
如果你覺得插件對你有幫助<BR>歡迎贊助我喝一杯飲料
</p>
<p class="description" style="font-size: 1rem; color: var(--chakra-colors-red-300);">
希望你已經贊助過茅場晶彥再來贊助我<BR>沒有茅場晶彥做遊戲就不會有此插件
</p>
<a target="_blank" style = "color: var(--chakra-colors-blue-300); margin-top: 0.5rem;" href="https://www.buymeacoffee.com/ourcastle">茅場晶彥的 Buy Me a Coffee</a>
<a target="_blank" style = "color: var(--chakra-colors-blue-300); margin-top: 0.5rem;" href="https://www.buymeacoffee.com/sgoeplugin">Wind 的 Buy Me a Coffee</a>
<a target="_blank" href="https://p.ecpay.com.tw/D6A6802" style="margin-top: 0.5rem;"><img src="https://payment.ecpay.com.tw/Content/themes/WebStyle20170517/images/ecgo.png" /></a>
`
}]
}];
function createRow(rowDiv, rowData) {
const type = {
checkbox: () => {
rowDiv.innerHTML = `
<input type="checkbox" id="${rowData.id}" bind-setting="${rowData.bindSetting}">
<label for="${rowData.id}">${rowData.label}</label>
`;
const mainElement = rowDiv.querySelector(`#${rowData.id}`);
mainElement.checked = settingStorage.get(rowData.bindSetting);
mainElement.onchange = rowEvent[rowData.type];
if (rowData.event) {
mainElement.addEventListener("change", rowData.event);
}
},
input: () => {
rowDiv.innerHTML = `
<label for="${rowData.id}">${rowData.label}</label>
<input type="text" id="${rowData.id}" bind-setting="${rowData.bindSetting}">
`;
const mainElement = rowDiv.querySelector(`#${rowData.id}`);
mainElement.value = settingStorage.get(rowData.bindSetting);
mainElement.onchange = rowEvent[rowData.type];
},
numberInput: () => {
rowDiv.innerHTML = `
<label for="${rowData.id}">${rowData.label}</label>
<input type="text" id="${rowData.id}" bind-setting="${rowData.bindSetting}">
`;
const mainElement = rowDiv.querySelector(`#${rowData.id}`);
mainElement.value = settingStorage.get(rowData.bindSetting);
mainElement.onchange = rowEvent[rowData.type];
},
colorInput: () => {
rowDiv.innerHTML = `
<label for="${rowData.id}">${rowData.label}</label>
<input type="color" id="${rowData.id}" bind-setting="${rowData.bindSetting}">
<p>我是顏文字</p>
`;
const mainElement = rowDiv.querySelector(`#${rowData.id}`);
mainElement.value = settingStorage.get(rowData.bindSetting);
rowDiv.querySelector("p").style.color = settingStorage.get(rowData.bindSetting);
mainElement.onchange = mainElement.oninput = rowEvent[rowData.type];
},
table: () => {
const tableData = settingStorage.get(rowData.bindSetting);
let gridRowHTML = "";
if (!tableData.length) {
gridRowHTML = `
<div class="grid-row">
<label>空</label>
</div>`;
} else {
tableData.forEach(name => {
const div = document.createElement("div");
div.innerHTML = `
<label>${name}</label>
<button>
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-x" width="19" height="19" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<line x1="18" y1="6" x2="6" y2="18" />
<line x1="6" y1="6" x2="18" y2="18" />
</svg>
</button>
`;
div.className = "grid-row";
const deleteButton = document.createElement("button");
deleteButton.innerHTML = ` <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-x" width="19" height="19" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<line x1="18" y1="6" x2="6" y2="18" />
<line x1="6" y1="6" x2="18" y2="18" />
</svg>`;
gridRowHTML += div.outerHTML;
});
}
rowDiv.innerHTML = `
<label>${rowData.label}</label>
<div class="grid" id="${rowData.id}" bind-setting="${rowData.bindSetting}">
<div class="grid-row-header">${rowData.header}</div>
${gridRowHTML}
</div>`;
},
"record-table": () => {
/*
{
name: {
itemName: quatity
}
}
*/
const tableData = settingStorage.get(rowData.bindSetting);
const names = Object.keys(tableData);
let recordsHTML = "";
if (!names.length) {
recordsHTML = `<div style="margin: 1rem">無</div>`;
} else {
const itemRecordApplyFilter = settingStorage.get("ITEM_RECORD.APPLY_FILTER");
names.forEach(name => {
const record = document.createElement("div");
record.innerHTML = `
<div class="record-header">${name} 獲得了</div>
`;
record.className = "record";
const recordBody = document.createElement("div");
recordBody.className = "record-body";
Object.keys(tableData[name]).forEach(itemName => {
const recordItem = document.createElement("div");
recordItem.className = "record-item";
recordItem.innerHTML = `
<div class="record-name">${itemName}</div>
<div class="record-quatity"> x ${tableData[name][itemName]}</div>
`;
if (itemRecordApplyFilter) {
const result = commonUtil.itemApplyFilter(itemName, {
highlight: true,
dom: recordItem
});
if (!result) recordItem.style.display = "none";
}
recordBody.appendChild(recordItem);
});
record.appendChild(recordBody);
recordsHTML += record.outerHTML;
});
}
rowDiv.innerHTML = `
<label>${rowData.label}</label>
${recordsHTML}
`;
},
a: () => {
rowDiv.innerHTML = `
<a href="${rowData.link}" target="_blank" rel="noopener noreferrer">${rowData.label}</a>
`;
},
button: () => {
rowDiv.innerHTML = `
<button class=${rowData.class}>${rowData.label} </button>
`;
rowDiv.querySelector("button").onclick = rowData.event;
},
customize: () => {
rowDiv.className = "row table";
rowDiv.innerHTML = rowData.html;
}
};
type[rowData.type]();
}
const content = wrapper.querySelector(".content");
const navbar = wrapper.querySelector(".navbar");
panel.forEach((panel, index) => {
if (panel.mobile && !commonUtil.isMobileDevice()) return;
const panelDiv = document.createElement("div");
panelDiv.className = "panel";
panelDiv.innerHTML = `
<div class="panel-header">
<span>${panel.category}</span>
</div>
<div class="panel-body">
<p class="description">${panel.description}</p>
</div>
`;
const panelBody = panelDiv.querySelector(".panel-body");
panel.rows.forEach(row => {
const rowDiv = document.createElement("div");
rowDiv.className = /table/.test(row.type) ? "row table" : "row";
createRow(rowDiv, row);
panelBody.appendChild(rowDiv);
});
content.appendChild(panelDiv);
panelDiv.setAttribute("panel-index", index);
//panel 切換
const button = document.createElement('button');
button.innerHTML = panel.category.length > 2 ? `${panel.category.substring(0, 2)}<br>${panel.category.substring(2)}` : panel.category;
button.setAttribute("bind-panel-index", index);
button.onclick = e => {
content.querySelector("[expand]")?.removeAttribute("expand");
content.querySelector(`.panel[panel-index="${button.getAttribute("bind-panel-index")}"]`)?.toggleAttribute("expand");
navbar.querySelector("[active]")?.removeAttribute("active");
button.toggleAttribute("active");
};
if (index === 0) {
panelDiv.toggleAttribute("expand");
button.toggleAttribute("active");
}
navbar.appendChild(button);
});
document.body.appendChild(wrapper);
}
function createExpBar() {
const expContainer = document.createElement("div");
expContainer.className = "exp-container";
expContainer.innerHTML = `
<div style="margin-right: 1rem;z-index: 1;" id="exp-bar-level-label"></div>
<div style="margin-right: 1rem;z-index: 1;" id="exp-bar-exp-label"></div>
<div id="exp-bar"></div>
<div id="exp-bar-fill"></div>
`;
// const expBar = document.createElement("div");
// const expBarFill = document.createElement("div");
// expBar.id = "exp-bar"
// expBarFill.id = "exp-bar-fill"
// document.body.appendChild(expBar)
document.body.appendChild(expContainer);
}
function registerSettingUIEvent() {
// document.querySelector("#open-dialog-btn").onclick = () => {document.querySelector(".wrapper").style.display = ""}
// document.querySelector("#close-dialog-btn").onclick = () => {document.querySelector(".wrapper").style.display = "none"}
document.querySelector("#close-dialog-btn").onclick = () => {
document.querySelector(".wrapper").remove();
document.body.style.overflow = "";
};
document.querySelectorAll(".grid-row > button").forEach(btn => {
btn.onclick = e => {
const grid = e.currentTarget.parentElement.parentElement;
const name = e.currentTarget.parentElement.querySelector("label").textContent;
const bindSetting = grid.getAttribute("bind-setting");
const tableList = settingStorage.get(bindSetting);
settingStorage.set(bindSetting, tableList.filter(row => row !== name));
settingStorage.save();
e.currentTarget.parentElement.remove();
if (grid.querySelectorAll(`.grid-row`).length === 0) {
const spaceGridRow = document.createElement("div");
spaceGridRow.innerHTML = "<label>空</label>";
spaceGridRow.className = "grid-row";
grid.appendChild(spaceGridRow);
}
};
});
document.querySelector("#reset-settings-btn").onclick = () => {
SETTINGS = structuredClone(DEFAULT_SETTINGS);
settingStorage.save();
registerSettingUIEvent();
};
document.querySelector(".wrapper").onclick = e => {
// if(e.target.className === "wrapper") e.target.style.display = "none";
if (e.target.matches(".wrapper")) {
e.target.remove();
document.body.style.overflow = "";
}
};
}
},{"../storage/globalVars":8,"../storage/setting":9,"../utils/common":10}]},{},[1]);