您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
優化界面
// ==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]);
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址