- // ==UserScript==
- // @name POE Trade Improvements
- // @namespace Kylixen
- // @version 2025-01-27
- // @description Improvements to the trade site. Adds a button for "add resistances" and "add attributes" to create stat weight groups
- // @author Kylixen
- // @match https://www.pathofexile.com/trade2/search/poe2/Standard
- // @match https://www.pathofexile.com/trade2/search/poe2/Standard/*
- // @icon https://www.google.com/s2/favicons?sz=64&domain=pathofexile.com
- // @grant none
- // @license GNU GPLv3
- // ==/UserScript==
-
- // debugger;
-
- var typingSpeed = 100; // milliseconds per character
- async function simulateTyping(element, text, delay = 100) {
- let index = 0;
-
- function fireEvent(eventType, key) {
- const event = new KeyboardEvent(eventType, {
- key: key,
- code: key.charCodeAt(0),
- bubbles: true,
- cancelable: true
- });
- element.dispatchEvent(event);
- }
-
- function fireInputEvent() {
- const inputEvent = new InputEvent("input", {
- bubbles: true,
- cancelable: true
- });
- element.dispatchEvent(inputEvent);
- }
-
- function fireChangeEvent() {
- const inputEvent = new InputEvent("change", {
- bubbles: true,
- cancelable: true
- });
- element.dispatchEvent(inputEvent);
- }
-
- async function typeNextChar() {
- if (index < text.length) {
- const char = text[index];
- fireEvent("keydown", char);
- fireEvent("keypress", char);
-
- if (element.isContentEditable) {
- element.textContent += char;
- } else {
- element.value += char;
- }
-
- fireInputEvent();
- fireEvent("keyup", char);
-
- index++;
- await setTimeout(null, delay)
- await typeNextChar();
- } else {
- fireChangeEvent();
- }
- }
-
- await typeNextChar();
- }
-
- function errorOnUndef(f, errorMessage) {
- return function (...args) {
- var res = f(...args)
- if(res === undefined)
- throw errorMessage
- return res;
- }
- }
-
- function errorHandled(f) {
- return function(...args) {
- try {
- return f(...args);
- } catch(error) {
- console.error(error);
- }
- }
- }
-
- function asyncErrorOnUndef(f, errorMessage) {
- return async function (...args) {
- var res = await f(...args)
- if(res === undefined)
- throw errorMessage
- return res;
- }
- }
-
- function asyncErrorHandled(f) {
- return async function(...args) {
- try {
- return await f(...args);
- } catch(error) {
- console.error(error);
- }
- }
- }
-
- function first(iterable) {
- if(!iterable || iterable.length == 0)
- return undefined;
- return iterable[0];
- }
-
- function last(iterable) {
- if(!iterable || iterable.length == 0)
- return undefined;
- return iterable[iterable.length -1 ];
- }
-
- const getAddStatGroupDiv = errorOnUndef(function() {
- return document.querySelector(".filter-group-select")
- }, "Couldn't find the +Add Stat Group <div>");
-
- async function addNewStatGroup(statGroupType) {
-
- var divAddStatGroup = getAddStatGroupDiv();
-
- var options = Array.from(divAddStatGroup.querySelectorAll("li>span") || []);
-
- if(!options)
- throw "Couldn't find options in Add Stat Group div";
-
- var groupMatch = new RegExp(`^\s*${statGroupType}\s*$`, "i");
-
- var option = first(options.filter(function(opt) {
- if(groupMatch.test(opt.textContent))
- return true;
- return false;
- }))
-
- if(!option)
- throw "Couldn't find the stat group type " + statGroupType;
-
- option.click();
-
- await setTimeout(null, 3);
-
- return last(document.querySelectorAll("div.filter-group"));
- };
-
- const addWeightedResistances = asyncErrorHandled(async function() {
-
- // Add the new panel
- const statGroupDiv = await addNewStatGroup("weighted sum v2");
-
- // type "resistance" into the input so we get all the elements we need)
- const statFilterDiv = last(document.querySelectorAll("div.filter-select-mutate"));
- const statInput = statFilterDiv.querySelector("input");
-
- statInput.focus();
- await simulateTyping(statInput, "resistance", 0);
-
- // click all the resistances
- statFilterDiv.querySelectorAll("span.multiselect__option").forEach((optionSpan) => {
- if(!((optionSpan.textContent || "").match(/^(explicit|implicit) #% to (Cold|Fire|Lightning|Chaos|All Elemental) resistances?$/i)))
- return;
- console.debug(optionSpan.textContent);
- optionSpan.click();
- });
-
- await setTimeout(null, 6);
-
- // Set the weight for all/chaos
- statGroupDiv.querySelectorAll("span.filter-body:has(.weight)").forEach(async (optionSpan) => {
- if((optionSpan.innerText || "").match(/All Elemental/i)) {
- const weightInput = optionSpan.querySelector("input.weight");
- weightInput.focus();
- await simulateTyping(weightInput, "3", 0);
- await setTimeout(null, 6);
- weightInput.blur();
- }
- });
- });
-
- const addWeightedAttributes = asyncErrorHandled(async function() {
-
- // Add the new panel
- const statGroupDiv = await addNewStatGroup("weighted sum v2");
-
- // type "resistance" into the input so we get all the elements we need)
- const statFilterDiv = last(document.querySelectorAll("div.filter-select-mutate"));
- const statInput = statFilterDiv.querySelector("input");
-
- // statInput.focus();
- // await simulateTyping(statInput, "resistance", 0);
-
- // click all the resistances
- statFilterDiv.querySelectorAll("span.multiselect__option").forEach((optionSpan) => {
- if(!((optionSpan.textContent || "").match(/^(explicit|implicit) #% to (Cold|Fire|Lightning|Chaos|All Elemental) resistances?$/i)))
- return;
- console.debug(optionSpan.textContent);
- optionSpan.click();
- });
-
- await setTimeout(null, 6);
-
- // Set the weight for all/chaos
- statGroupDiv.querySelectorAll("span.filter-body:has(.weight)").forEach(async (optionSpan) => {
- if((optionSpan.innerText || "").match(/All Elemental/i)) {
- const weightInput = optionSpan.querySelector("input.weight");
- weightInput.focus();
- await simulateTyping(weightInput, "3", 0);
- await setTimeout(null, 6);
- weightInput.blur();
- }
- });
-
- });
- ;
- function addResistancesClick() {
- addWeightedResistances();
- // Clean up button
- }
-
- function addAttributesClick() {
- addWeightedAttributes();
- // Clean up button
- }
-
- function waitFor(waitForFunction, successCallback) {
- (new MutationObserver(function (changes, observer) {
- if (!waitForFunction())
- return;
- observer.disconnect();
- successCallback();
- })).observe(document, { childList: true, subtree: true });
- }
-
- (function () {
- 'use strict';
- (new MutationObserver(function (changes, observer) {
- if (!document.querySelector('.filter-group-select'))
- return;
- observer.disconnect();
- onLoad();
- })).observe(document, { childList: true, subtree: true });
-
- function onLoad() {
- const searchDiv = document.querySelector("div.search-advanced-pane");
- const newElement = document.createElement('div');
- newElement.className = "filter-group expanded";
- newElement.innerHTML = `
- <div class="filter-group-body">
- <button id='add-resistance-button' type="button" class='btn search-btn' onclick='addResistancesClick()'>
- <span>Add Resistances</span>
- </button>
- <button id='add-attributes-button' type="button" class='btn search-btn' onclick='addAttributesClick()'>
- <span>Add Attributes</span>
- </button>
- </div>
- `;
- searchDiv.prepend(newElement);
- }
- })();
-
- window.addResistancesClick = addResistancesClick;
- window.addAttributesClick = addAttributesClick;
-
- window.ky = {
- addResistancesClick,
- addWeightedResistances,
- addWeightedAttributes,
- addNewStatGroup,
- simulateTyping,
- last,
- first,
- }