-
-
Save Krytos/fcb435878b9ee214c8ef9c5d9a685861 to your computer and use it in GitHub Desktop.
| // ==UserScript== | |
| // @name PoE2 Trade Copy Item Button | |
| // @version 2.0 | |
| // @description Adds a button to the PoE2 Trade page that allows you to copy the item description and affixes to your clipboard. | |
| // @author Kevin M | |
| // @match https://www.pathofexile.com/trade2/* | |
| // @icon https://www.google.com/s2/favicons?domain=pathofexile.com | |
| // @downloadURL https://gist.github.com/Krytos/fcb435878b9ee214c8ef9c5d9a685861/raw/poe2trade.user.js | |
| // @grant none | |
| // ==/UserScript== | |
| (function () { | |
| 'use strict'; | |
| const processedRows = new Set(); | |
| function processRow(row) { | |
| if (processedRows.has(row)) return; | |
| try { | |
| const leftDiv = row.querySelector('div.left'); | |
| if (!leftDiv || !leftDiv.children || leftDiv.children.length < 2) { | |
| return; | |
| } | |
| var copy_button = leftDiv.children[1]; | |
| copy_button.className = "copy"; | |
| copy_button.removeAttribute("style"); | |
| copy_button.addEventListener('click', function () { | |
| const itemHeader = row.querySelector('div.itemHeader.doubleLine') ?? row.querySelector('div.itemHeader'); | |
| const content = row.querySelector('div.content'); | |
| if (!itemHeader || !content) { | |
| console.log('Item header or content not found'); | |
| return; | |
| } | |
| let outputText = ''; | |
| const typeLineElem = itemHeader.querySelector('.itemName.typeLine .lc'); | |
| const itemNameElem = itemHeader.querySelector('.itemName:not(.typeLine) .lc'); | |
| const normal = row.querySelector('.normalPopup'); | |
| const magic = row.querySelector('.magicPopup'); | |
| const rare = row.querySelector('.rarePopup'); | |
| const unique = row.querySelector('.uniquePopup'); | |
| if (normal) { | |
| console.log('normal'); | |
| outputText += `Rarity: Normal\n`; | |
| } | |
| else if (magic) { | |
| console.log('magic'); | |
| outputText += `Rarity: Magic\n`; | |
| } | |
| else if (rare) { | |
| console.log('rare'); | |
| outputText += `Rarity: Rare\n`; | |
| } | |
| else if (unique) { | |
| console.log('unique'); | |
| outputText += `Rarity: unique\n`; | |
| } | |
| if (itemNameElem) { | |
| outputText += `${itemNameElem.innerText}\n`; | |
| } | |
| // Add type line | |
| if (typeLineElem) { | |
| outputText += `${typeLineElem.innerText}\n`; | |
| } | |
| //Quality | |
| const quality = content.querySelector('span[data-field="quality"] .colourAugmented'); | |
| if (quality) { | |
| outputText += '--------\n'; | |
| outputText += `Quality: ` + quality.innerText + ' (augmented)\n'; | |
| } | |
| // Requirements | |
| const requirements = content.querySelector('.requirements'); | |
| if (requirements) { | |
| outputText += '--------\n'; | |
| outputText += 'Requirements:\n'; | |
| let level = requirements.querySelector('span[data-field="lvl"] .colourDefault').innerText; | |
| let str = requirements.querySelector('span[data-field="str"] .colourDefault'); | |
| let int = requirements.querySelector('span[data-field="int"] .colourDefault'); | |
| let dex = requirements.querySelector('span[data-field="dex"] .colourDefault'); | |
| outputText += `Level: ${level}\n`; | |
| if (str) { | |
| str = 'Str: ' + str.innerText; | |
| outputText += str + '\n'; | |
| } | |
| if (int) { | |
| int = 'Int: ' + int.innerText; | |
| outputText += int + '\n'; | |
| } | |
| if (dex) { | |
| dex = 'Dex: ' + dex.innerText; | |
| outputText += dex + '\n'; | |
| } | |
| outputText += '--------\n'; | |
| } | |
| // Sockets | |
| const socketsDiv = leftDiv.querySelector('.sockets'); | |
| if (socketsDiv && socketsDiv.children.length > 0) { | |
| outputText += `Sockets: ${"S ".repeat(socketsDiv.childElementCount)}\n`; | |
| outputText += '--------\n'; | |
| } | |
| // Item Level | |
| const itemLevel = content.querySelector('.itemLevel'); | |
| if (itemLevel) { | |
| outputText += itemLevel.innerText.trim() + '\n'; | |
| outputText += '--------\n'; | |
| } | |
| // Enchantments | |
| const enchantments = content.querySelectorAll('.enchantMod'); | |
| if (enchantments.length > 0) { | |
| let enchantSection = []; | |
| enchantments.forEach(enchant => { | |
| let text = enchant.innerText.trim(); | |
| enchantSection.push(text + ' (enchant)'); | |
| }); | |
| outputText += enchantSection.join('\n') + '\n'; | |
| outputText += '--------\n'; | |
| } | |
| // Implicit mods | |
| const runeMods = content.querySelectorAll('.runeMod'); | |
| if (runeMods.length > 0) { | |
| let implicitSection = []; | |
| runeMods.forEach(rune => { | |
| let text = rune.innerText.trim(); | |
| implicitSection.push(text + ' (rune)'); | |
| }); | |
| outputText += implicitSection.join('\n') + '\n'; | |
| outputText += '--------\n'; | |
| } | |
| // Implicit mods | |
| const implicitMods = content.querySelectorAll('.implicitMod'); | |
| if (implicitMods.length > 0) { | |
| let implicitSection = []; | |
| // outputText += `Implicits: ${implicitMods.length}\n`; | |
| implicitMods.forEach(implicit => { | |
| let text = implicit.innerText.trim(); | |
| implicitSection.push(text + ' (implicit)'); | |
| }); | |
| outputText += implicitSection.join('\n') + '\n'; | |
| outputText += '--------\n'; | |
| } | |
| // Explicit mods | |
| const explicitMods = content.querySelectorAll('.explicitMod'); | |
| if (explicitMods.length > 0) { | |
| let modSection = []; | |
| explicitMods.forEach(mod => { | |
| let text = mod.querySelector('.lc.s').innerText.trim(); | |
| modSection.push(text); | |
| }); | |
| outputText += modSection.join('\n') + '\n'; | |
| } | |
| // Corrupted state | |
| const corrupted = content.querySelector('.unmet'); | |
| if (corrupted) { | |
| outputText += '--------\n'; | |
| outputText += corrupted.innerText + '\n'; | |
| } | |
| // Desecrated mods | |
| const desecratedMods = content.querySelectorAll('.desecratedMod'); | |
| if (desecratedMods.length > 0) { | |
| let modSection = []; | |
| desecratedMods.forEach(mod => { | |
| let text = mod.querySelector('.lc.s').innerText.trim(); | |
| modSection.push(text); | |
| }); | |
| outputText += modSection.join('\n') + '\n'; | |
| } | |
| const priceNote = content.querySelector('.textCurrency'); | |
| if (priceNote && priceNote.innerText.includes('~price')) { | |
| outputText += '--------\n'; | |
| outputText += `Note: ${priceNote.innerText.replace('~price', '').trim()}`; | |
| outputText += '\n'; | |
| } | |
| navigator.clipboard.writeText(outputText); | |
| }); | |
| processedRows.add(row); | |
| } catch (e) { | |
| console.error('Error processing row:', e); | |
| } | |
| } | |
| const observer = new MutationObserver((mutations) => { | |
| for (const mutation of mutations) { | |
| for (const node of mutation.addedNodes) { | |
| if (node.nodeType === 1 && node.classList.contains('row')) { | |
| if (!node.querySelector('div.itemHeader')) continue; | |
| processRow(node); | |
| } | |
| } | |
| } | |
| }); | |
| document.querySelectorAll('div.row').forEach(row => { | |
| if (row.querySelector('div.itemHeader')) { | |
| processRow(row); | |
| } | |
| }); | |
| observer.observe(document.body, { | |
| childList: true, | |
| subtree: true | |
| }); | |
| })(); |
Your line 1 is causing an error. I also did some code refactoring. Feel free to merge or take anything below, you deserve all the credit.
https://gist.github.com/seanGSISG/d2efc1d4355456e3b7d7b89b1b804b54
With 0.3.0 the copy button isn't fully functional anymore, the desecrated mods have their own special mod area.
Please add:
// Desecrated mods
const desecratedMods = content.querySelectorAll('.desecratedMod');
if (desecratedMods.length > 0) {
let modSection = [];
desecratedMods.forEach(mod => {
let text = mod.querySelector('.lc.s').innerText.trim();
modSection.push(text);
});
outputText += modSection.join('\n') + '\n';
}
Done!
I noticed that the script doesn't account for fractured mods!
This should help (just add it before the explicits)
// Fractured mod
const fractured = content.querySelector('.fracturedMod');
if (fractured) {
let text = fractured.querySelector('.lc.s').innerText.trim();
outputText += text + ' (fractured) \n';
}
@Fahrengeit nice addition but the code breaks on multi line fractured mods!
@Krytos would you be so kindly to add the code below on line 162? Moving it above the Explicit mods keeps the typical Fractured -> Explicit -> Desecrated format.
// Fractured mods
const fracturedMods = content.querySelectorAll('.fracturedMod');
if (fracturedMods.length > 0) {
let modSection = [];
fracturedMods.forEach(mod => {
let text = mod.querySelector('.lc.s').innerText.trim();
modSection.push(text + ' (fractured)');
});
outputText += modSection.join('\n') + '\n';
}
Could you also change in // Desecrated mods the line modSection.push(text); to be modSection.push(text + ' (desecrated)'); so it retains the desecrated modifier even in PoB? It will show correctly in PoB.

Hey! Thanks for the script. I'm using Violentmonkey on Firefox and had to edit the script to the following so it could work: