11// ==UserScript==
22// @name 自动计算最大时利润
33// @namespace https://github.com/gangbaRuby
4- // @version 1.33.1
4+ // @version 1.33.2
55// @license AGPL-3.0
66// @description 在商店计算自动计算最大时利润,在合同、交易所展示最大时利润
77// @author Rabbit House
36263626 var state = {
36273627 hasNewVersion: void 0,
36283628 latestVersion: void 0,
3629- localVersion: typeof GM_info !== "undefined" ? GM_info.script.version : "1.33.1 ",
3629+ localVersion: typeof GM_info !== "undefined" ? GM_info.script.version : "1.33.2 ",
36303630 SCXXCS: 0,
36313631 PROFIT_PER_BUILDING_LEVEL: 370,
36323632 RETAIL_ADJUSTMENT: {
82598259 };
82608260 let openPanel = null;
82618261 let openButton = null;
8262+ let openInputGroup = null;
8263+ let layoutObserver = null;
8264+ let layoutTimer = null;
8265+ let buttonPositionTimer = null;
82628266 let variantPopup = null;
82638267 let scanTimer = null;
82648268 let started = false;
85378541 const gap = 6;
85388542 const panelWidth = Math.min(360, window.innerWidth - 16);
85398543 const panelHeight = panel.offsetHeight || 340;
8540- const maxHeight = Math.min(420, window.innerHeight - 16);
8541- let top = rect.bottom + gap;
8542- if (top + panelHeight > window.innerHeight - 8) {
8543- top = Math.max(8, rect.top - panelHeight - gap);
8544+ let input = null;
8545+ if (openInputGroup) {
8546+ const textareas = openInputGroup.querySelectorAll("textarea");
8547+ for (const ta of textareas) {
8548+ if (isChatInput(ta) && ta.getBoundingClientRect().height > 0) {
8549+ input = ta;
8550+ break;
8551+ }
8552+ }
8553+ if (!input) input = textareas[0] || null;
85448554 }
8555+ const anchorRect = input ? input.getBoundingClientRect() : rect;
8556+ const availableAbove = anchorRect.top - gap - 8;
8557+ const maxHeight = Math.max(0, Math.min(420, availableAbove, window.innerHeight - 16));
8558+ panel.style.maxHeight = maxHeight + "px";
8559+ const top = anchorRect.top - gap - Math.min(panelHeight, maxHeight);
85458560 const left = Math.max(8, Math.min(rect.left, window.innerWidth - panelWidth - 8));
85468561 panel.style.width = panelWidth + "px";
8547- panel.style.maxHeight = maxHeight + "px";
85488562 panel.style.left = left + "px";
85498563 panel.style.top = top + "px";
85508564 }
89108924 return panel;
89118925 }
89128926 function ensureButton(inputGroup) {
8913- if (inputGroup.querySelector(BUTTON_SELECTOR) ) return;
8927+ if (inputGroup.dataset.scEmojiPickerGroup === "1" ) return;
89148928 const btn = document.createElement("button");
89158929 btn.type = "button";
89168930 btn.className = "sc-chat-emoji-picker-btn";
89178931 btn.setAttribute("data-sc-emoji-picker-added", "1");
8932+ btn._inputGroup = inputGroup;
89188933 btn.title = "\u9009\u62E9\u8868\u60C5";
89198934 btn.textContent = "\u{1F642}";
8920- btn.style.cssText = "display:inline- flex;align-items:center;justify-content:center;flex:0 0 34px;width:34px;height :34px;min-width :34px;padding:0;margin:0 2px; border:1px solid rgba(128,128,128,0.55);border-radius:4px;background:transparent;color:inherit;cursor:pointer;font-size:18px;line-height:1;vertical-align:middle ;box-sizing:border-box;";
8935+ btn.style.cssText = "position:fixed;z-index:2147483645;display: flex;align-items:center;justify-content:center;width: 34px;height:34px;min- width:34px;max-width :34px;min-height :34px;max-height:34px; padding:0;margin:0;overflow:hidden; border:1px solid rgba(128,128,128,0.55);border-radius:4px;background:transparent;color:inherit;cursor:pointer;font-size:18px;line-height:1;box-sizing:border-box;";
89218936 btn.addEventListener("mouseenter", () => {
89228937 btn.style.background = "rgba(128,128,128,0.18)";
89238938 });
89298944 e.stopPropagation();
89308945 togglePanel(btn, inputGroup);
89318946 });
8932- const btnGroup = inputGroup.querySelector(".input-group-btn");
8933- if (btnGroup) {
8934- btnGroup.insertBefore(btn, btnGroup.firstChild);
8947+ inputGroup.dataset.scEmojiPickerGroup = "1";
8948+ document.body.appendChild(btn);
8949+ positionButton(btn, inputGroup);
8950+ }
8951+ function positionButton(btn, inputGroup) {
8952+ const sendBtn = inputGroup.querySelector(".input-group-btn button");
8953+ const anchor = sendBtn || inputGroup.querySelector(".input-group-btn") || inputGroup;
8954+ const rect = anchor.getBoundingClientRect();
8955+ if (sendBtn) {
8956+ btn.style.left = Math.max(4, rect.left - 42) + "px";
8957+ btn.style.top = Math.max(4, rect.bottom - 34) + "px";
89358958 } else {
8936- inputGroup.appendChild(btn);
8959+ btn.style.left = Math.max(4, rect.right - 42) + "px";
8960+ btn.style.top = Math.max(4, rect.bottom - 34) + "px";
8961+ }
8962+ }
8963+ function updateButtonPositions() {
8964+ document.querySelectorAll(BUTTON_SELECTOR).forEach((btn) => {
8965+ const group = btn._inputGroup;
8966+ if (!group || !group.isConnected) {
8967+ if (group) delete group.dataset.scEmojiPickerGroup;
8968+ btn.remove();
8969+ return;
8970+ }
8971+ positionButton(btn, group);
8972+ });
8973+ }
8974+ function stopLayoutObserver() {
8975+ if (layoutObserver) {
8976+ layoutObserver.disconnect();
8977+ layoutObserver = null;
8978+ }
8979+ if (layoutTimer) {
8980+ clearInterval(layoutTimer);
8981+ layoutTimer = null;
8982+ }
8983+ }
8984+ function startLayoutObserver() {
8985+ stopLayoutObserver();
8986+ layoutTimer = setInterval(() => {
8987+ if (openPanel && openButton) positionPanel(openPanel, openButton);
8988+ }, 250);
8989+ layoutObserver = new MutationObserver(() => {
8990+ if (openPanel && openButton) {
8991+ requestAnimationFrame(() => positionPanel(openPanel, openButton));
8992+ }
8993+ });
8994+ const seen = /* @__PURE__ */ new Set();
8995+ let el = openInputGroup;
8996+ while (el && !seen.has(el)) {
8997+ layoutObserver.observe(el, { attributes: true, attributeFilter: ["class", "style"] });
8998+ seen.add(el);
8999+ el = el.parentElement;
9000+ }
9001+ const textareas = openInputGroup.querySelectorAll("textarea");
9002+ for (const textarea of textareas) {
9003+ layoutObserver.observe(textarea, { attributes: true, attributeFilter: ["class", "style"] });
89379004 }
89389005 }
89399006 function closePanel() {
9007+ stopLayoutObserver();
89409008 closeVariantPopup();
89419009 if (openPanel) openPanel.remove();
89429010 openPanel = null;
89439011 openButton = null;
9012+ openInputGroup = null;
89449013 document.removeEventListener("pointerdown", onPointerDown, true);
89459014 document.removeEventListener("keydown", onKeyDown);
89469015 window.removeEventListener("resize", onViewportChange);
89729041 document.body.appendChild(panel);
89739042 openPanel = panel;
89749043 openButton = btn;
9044+ openInputGroup = inputGroup;
9045+ startLayoutObserver();
89759046 requestAnimationFrame(() => positionPanel(panel, btn));
89769047 document.addEventListener("pointerdown", onPointerDown, true);
89779048 document.addEventListener("keydown", onKeyDown);
89829053 }
89839054 function removeAll() {
89849055 closePanel();
8985- document.querySelectorAll(BUTTON_SELECTOR).forEach((btn) => btn.remove());
9056+ document.querySelectorAll(BUTTON_SELECTOR).forEach((btn) => {
9057+ if (btn._inputGroup) delete btn._inputGroup.dataset.scEmojiPickerGroup;
9058+ btn.remove();
9059+ });
89869060 }
89879061 function requestScan() {
89889062 if (scanTimer) return;
89969070 removeAll();
89979071 return;
89989072 }
9073+ updateButtonPositions();
89999074 document.querySelectorAll("textarea").forEach((textarea) => {
90009075 if (!isChatInput(textarea)) return;
90019076 const inputGroup = textarea.closest(".input-group");
90029077 if (inputGroup) ensureButton(inputGroup);
90039078 });
9079+ updateButtonPositions();
90049080 }
90059081 function start() {
90069082 if (started) return;
90239099 [document.documentElement, document.body].forEach((target) => {
90249100 themeObserver.observe(target, { attributes: true, attributeFilter: ["class", "style"] });
90259101 });
9102+ buttonPositionTimer = setInterval(updateButtonPositions, 300);
9103+ window.addEventListener("scroll", updateButtonPositions, true);
9104+ window.addEventListener("resize", updateButtonPositions);
90269105 window.scChatEmojiPickerRefresh = () => {
90279106 removeAll();
90289107 scan();
@@ -10403,16 +10482,19 @@
1040310482 }
1040410483 const SaturationDisplay = /* @__PURE__ */ (() => {
1040510484 let saturationTableElement = null;
10485+ const isNarrowViewport = () => window.innerWidth <= 480;
1040610486 const createTable = (list) => {
1040710487 const d = DM();
10488+ const isNarrow = isNarrowViewport();
10489+ const cellPadding = isNarrow ? "4px 5px" : "4px 8px";
1040810490 const table = document.createElement("table");
10409- table.style.cssText = `border-collapse:collapse;margin:10px 0;background:${d ? "#333" : "#f9f9f9"};color:${d ? "white" : "#333"};font-size:13px ;width:100% ;`;
10491+ table.style.cssText = `border-collapse:collapse;margin:10px 0;background:${d ? "#333" : "#f9f9f9"};color:${d ? "white" : "#333"};font-size:${isNarrow ? 12 : 13}px ;width:auto;table-layout:auto;white-space:nowrap ;`;
1041010492 const thead = document.createElement("thead");
1041110493 const headerRow = document.createElement("tr");
1041210494 ["\u7269\u54C1", "\u8D28\u91CF", "\u9971\u548C\u5EA6"].forEach((text) => {
1041310495 const th = document.createElement("th");
1041410496 th.textContent = text;
10415- th.style.cssText = `border:1px solid ${d ? "#666" : "#ccc"};padding:4px 8px ;`;
10497+ th.style.cssText = `border:1px solid ${d ? "#666" : "#ccc"};padding:${cellPadding};text-align:center;vertical-align:middle;white-space:nowrap ;`;
1041610498 headerRow.appendChild(th);
1041710499 });
1041810500 thead.appendChild(headerRow);
1042410506 [name, item.quality ?? "-", String(item.saturation)].forEach((text) => {
1042510507 const td = document.createElement("td");
1042610508 td.textContent = text;
10427- td.style.cssText = `border:1px solid ${d ? "#666" : "#ccc"};padding:4px 8px ;text-align:center;`;
10509+ td.style.cssText = `border:1px solid ${d ? "#666" : "#ccc"};padding:${cellPadding} ;text-align:center;vertical-align:middle;white-space:nowrap ;`;
1042810510 row.appendChild(td);
1042910511 });
1043010512 tbody.appendChild(row);
@@ -10440,20 +10522,39 @@
1044010522 return;
1044110523 }
1044210524 const d = DM();
10525+ const isNarrow = isNarrowViewport();
10526+ const containerPadding = isNarrow ? 8 : 12;
10527+ const containerMaxHeight = isNarrow ? "320px" : "400px";
10528+ const titleFont = isNarrow ? 13 : 14;
10529+ const subFont = isNarrow ? 12 : 13;
1044310530 const list = data2.ResourcesRetailInfo;
1044410531 const weatherMultiplier = data2.sellingSpeedMultiplier.sellingSpeedMultiplier;
10532+ const weatherData = data2.sellingSpeedMultiplier || {};
10533+ const weatherUntilRaw = weatherData.Until || weatherData.until || weatherData.weatherUntil || data2.weatherUntil && (data2.weatherUntil.Until || data2.weatherUntil.until);
10534+ const weatherUntilDate = weatherUntilRaw ? new Date(weatherUntilRaw) : null;
10535+ const weatherUntilText = weatherUntilDate && !isNaN(weatherUntilDate.getTime()) ? weatherUntilDate.toLocaleString([], {
10536+ year: "numeric",
10537+ month: "2-digit",
10538+ day: "2-digit",
10539+ hour: "2-digit",
10540+ minute: "2-digit",
10541+ hour12: false
10542+ }) : "\u672A\u77E5";
1044510543 saturationTableElement = document.createElement("div");
1044610544 saturationTableElement.style.cssText = `
10447- position:fixed; left:10px; top:50px; z-index:9998;
10448- background:${d ? "#2c2c2c" : "#fff"}; color:${d ? "#fff" : "#333"}; padding:12px ;
10449- border-radius:8px; max-height:400px ; overflow:auto;
10450- max-width: calc(100vw - 20px);
10545+ position:fixed; left:10px; top:50px; z-index:9998; box-sizing:border-box;
10546+ background:${d ? "#2c2c2c" : "#fff"}; color:${d ? "#fff" : "#333"}; padding:${containerPadding}px ;
10547+ border-radius:8px; max-height:${containerMaxHeight} ; overflow:auto;
10548+ width:max-content; max-width: calc(100vw - 20px);
1045110549 box-shadow:0 4px 15px rgba(0,0,0,0.5); font-family:Arial, sans-serif;
1045210550 `;
1045310551 const headerInfo = document.createElement("div");
10552+ headerInfo.style.paddingRight = "26px";
10553+ headerInfo.style.overflowWrap = "anywhere";
1045410554 headerInfo.innerHTML = `
10455- <div style="margin-bottom:6px; font-size:14px; font-weight:bold; color:${d ? "#f1c40f" : "#b8860b"};">\u5929\u6C14\u901F\u5EA6\u52A0\u6210: ${weatherMultiplier}</div>
10456- <div style="margin-bottom:6px; font-size:13px; color:${d ? "#ddd" : "#666"};">\u67E5\u8BE2\u5386\u53F2\u9971\u548C\u5EA6: <a href="https://marketsaturation.22-7.top/" target="_blank" style="color:#3498db; text-decoration:underline;">\u70B9\u51FB\u67E5\u770B</a></div>
10555+ <div style="margin-bottom:6px; font-size:${titleFont}px; font-weight:bold; color:${d ? "#f1c40f" : "#b8860b"};">\u5929\u6C14\u901F\u5EA6\u52A0\u6210: ${weatherMultiplier}</div>
10556+ <div style="margin-bottom:6px; font-size:${subFont}px; color:${d ? "#ddd" : "#666"};">\u4E0B\u6B21\u53D8\u66F4\u65F6\u95F4: ${weatherUntilText}</div>
10557+ <div style="margin-bottom:6px; font-size:${subFont}px; color:${d ? "#ddd" : "#666"};">\u67E5\u8BE2\u5386\u53F2\u9971\u548C\u5EA6: <a href="https://sc.22-7.top/marketsaturation" target="_blank" style="color:#3498db; text-decoration:underline;">\u70B9\u51FB\u67E5\u770B</a></div>
1045710558 `;
1045810559 const closeBtn = document.createElement("button");
1045910560 closeBtn.textContent = "\xD7";
1284712948 })();
1284812949})();
1284912950
12850- // @changelog 新增聊天表情选择器:运行时读取游戏当前前端包,自动展示最近使用、资源、建筑、彩蛋、领域、特殊和 Unicode 其它表情,点击直接插入;面板顶部显示测试提示。修复与旧建筑皮肤脚本同时启用时,更新提示按钮绑定冲突、忽略记录互相影响的问题 。
12951+ // @changelog 修复聊天表情选择器按钮与面板遮挡问题;优化饱和度表与天气信息显示;更新历史饱和度链接 。
0 commit comments