您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds a filter to enter buy/sell amount and an additional column with the total price.
// ==UserScript== // @name Inara - Commodites Total Price // @name:de Inara - Waren Gesamt Preis // @namespace https://greasyfork.org/users/928242 // @version 1.1.0 // @description Adds a filter to enter buy/sell amount and an additional column with the total price. // @description:de Fügt einen Filter zur Eingabe des Kauf-/Verkaufsbetrags und eine zusätzliche Spalte mit dem Gesamtpreis hinzu. // @author Kamikaze (https://github.com/Kamiikaze) // @supportURL https://github.com/Kamiikaze/Tampermonkey/issues // @match https://inara.cz/elite/commodities/?formbrief=* // @icon https://www.google.com/s2/favicons?sz=64&domain=inara.cz // @grant none // @license MIT // ==/UserScript== // # # # # # # // CONFIG // # # # # # # // Row-Name for Total Price const totalPriceText = "Total Price" // Default sort by Total Price const sortTotalPrice = true /*** DO NOT CHANGE BELOW ***/ // 1. Input-Feld für die gewünschte Menge hinzufügen const mainblock = document.querySelector('.mainblock'); if (mainblock) { const filterContainer = document.createElement('div') filterContainer.id = "custom-filters" const savedAmount = localStorage.getItem("inara_filter-amount") const amountFilter = document.createElement('div') amountFilter.id = "custom-filter-amount" amountFilter.innerHTML = `<label class="formlabelside"> Buy/Sell Amount <span class="tooltip tooltipnoline" data-tooltiptext="Amount for total price calculations. If supply is less than amount, supply is highlighted red."> <span class="helpmark">?</span> </span></label>` const amountInput = document.createElement('input'); amountInput.type = 'number'; amountInput.value = savedAmount || 512; amountInput.id = 'desiredAmount'; amountInput.style.marginBottom = '10px'; amountFilter.append(amountInput) const savedBonus = localStorage.getItem("inara_filter-bonus") const bonusFilter = document.createElement('div') bonusFilter.id = "custom-filter-amount" bonusFilter.innerHTML = `<label class="formlabelside"> PP Bonus <span class="tooltip tooltipnoline" data-tooltiptext="Percentage of PP bonus profit. Will be included in 'Total Price' calculations."> <span class="helpmark">?</span> </span></label>` const bonusInput = document.createElement('input'); bonusInput.type = 'number'; bonusInput.value = savedBonus || 0; bonusInput.id = 'ppBonus'; bonusInput.style.marginBottom = '10px'; bonusFilter.append(bonusInput) filterContainer.append(amountFilter) filterContainer.append(bonusFilter) mainblock.insertBefore(filterContainer, mainblock.firstChild); } // 2. Gesamtpreis-Aktualisierungsfunktion function updateTotalPrices() { const desiredQuantity = parseInt(document.querySelector('#desiredAmount') .value, 10) || 0; const ppBonus = parseInt(document.querySelector('#ppBonus') .value, 10) || 0; const table = document.querySelector('table'); if (!table) return; localStorage.setItem("inara_filter-amount", desiredQuantity) localStorage.setItem("inara_filter-bonus", ppBonus) // Überschrift für Gesamtpreis einfügen, falls noch nicht vorhanden const headerRow = table.querySelector('thead tr'); if (!headerRow.querySelector('.total-header')) { const totalHeader = document.createElement('th'); totalHeader.textContent = totalPriceText; totalHeader.className = [ 'total-header', 'alignright', 'sorting', ].join(" ") totalHeader.setAttribute("tabindex", 0) totalHeader.setAttribute("rowspan", 1) totalHeader.setAttribute("colspan", 1) headerRow.insertBefore(totalHeader, headerRow.querySelector('th:nth-child(7)')); setTimeout(() => { if (!sortTotalPrice) return const headerCell = document.querySelector(".total-header") headerCell.click() headerCell.click() }, 2000) } // Funktion zum Erkennen des Tausender-Trennzeichens function detectThousandSeparator(priceStr) { priceStr = priceStr.trim(); const dotMatches = priceStr.match(/\./g); const commaMatches = priceStr.match(/,/g); if (dotMatches) { if (dotMatches.length > 1) return '.'; let parts = priceStr.split('.'); if (parts[1] && parts[1].length === 3) return '.'; } else if (commaMatches) { if (commaMatches.length > 1) return ','; let parts = priceStr.split(','); if (parts[1] && parts[1].length === 3) return ','; } return ','; } // Funktion zur Formatierung einer Zahl mit Tausender-Trennzeichen function formatWithThousandSeparator(num, thousandSeparator) { return num.toString() .replace(/\B(?=(\d{3})+(?!\d))/g, thousandSeparator); } // Function to calculate average of two numbers function calculateAverage(min, max) { return (min + max) / 2; } // Alle Zeilen im Tabellenkörper durchgehen const rows = table.querySelectorAll('tbody tr'); rows.forEach(row => { // 6. Zelle als Unit-Price const unitPriceCell = row.querySelector('td:nth-child(6)'); const supplyCell = row.querySelector('td:nth-child(5)'); const hasPPbonus = !!row.querySelector("td .ppbonusicon") if (unitPriceCell) { // Preis extrahieren (nur Ziffern, Komma und Punkt) const rawPrice = unitPriceCell.textContent.replace(/[^\d.,\s-]/g, '').trim() const rawSupply = supplyCell.getAttribute('data-order') const thousandSep = detectThousandSeparator(rawPrice); let numericPrice; let isApproximate = false; // Check if the price is a range if (rawPrice.includes('-')) { const [minPrice, maxPrice] = rawPrice.split('-').map(price => { const numericString = thousandSep ? price.split(thousandSep).join('') : price; return parseInt(numericString, 10); }); numericPrice = calculateAverage(minPrice, maxPrice); isApproximate = true; } else { const numericString = thousandSep ? rawPrice.split(thousandSep).join('') : rawPrice; numericPrice = parseInt(numericString, 10); } // Gesamtpreis berechnen const totalPrice = (() => { let price = (numericPrice * desiredQuantity); if (hasPPbonus && ppBonus > 0) { price = price + (price/100)*ppBonus } return price.toFixed(); }) const formattedTotal = thousandSep ? formatWithThousandSeparator(totalPrice(), thousandSep) + ' Cr' : totalPrice() + ' Cr'; // Append "~" if the price was a range const displayTotal = isApproximate ? `~${formattedTotal}` : formattedTotal; // Check if desiredQuantity > rawSupply let lowSupplyClass = "" if (desiredQuantity > rawSupply) { lowSupplyClass = " low-supply" } // Gesamtpreis-Zelle erstellen oder aktualisieren let totalPriceCell = row.querySelector('.total-price'); if (!totalPriceCell) { totalPriceCell = document.createElement('td'); totalPriceCell.className = 'alignright total-price'; totalPriceCell.setAttribute("data-order", totalPrice()) row.insertBefore(totalPriceCell, row.querySelector('td:nth-child(7)')); } totalPriceCell.textContent = displayTotal; supplyCell.className = [ 'alignright', lowSupplyClass ? 'low-supply' : "" ].join(" ") } }); } function GM_addStyle(css, important = true) { const head = document.head || document.getElementsByTagName('head')[0]; if (!head) return; const style = document.createElement('style'); style.type = 'text/css'; if (important) style.innerHTML = css.replace(/;/g, ' !important;') else style.innerHTML = css head.appendChild(style); } function debounce(func, delay) { let timeout; return function (...args) { clearTimeout(timeout); timeout = setTimeout(() => { func.apply(this, args); }, delay); }; } GM_addStyle(` #custom-filters { display: flex; flex-direction: row; flex-wrap: wrap; justify-content: center; gap: 5px 10px; padding: 10px; } #custom-filters > div { min-width: 140px; width: 19%; } td.low-supply { color: #ff8080; } `, false) // 3. Event-Listener mit debounce, um bei Änderung des Input-Felds die Tabelle zu aktualisieren const debouncedUpdate = debounce(updateTotalPrices, 500); document.querySelector('#desiredAmount') .addEventListener('input', debouncedUpdate); document.querySelector('#ppBonus') .addEventListener('input', debouncedUpdate); // Initiale Berechnung beim Laden updateTotalPrices();