NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name dim-mod // @namespace dim-mod // @version 1.3 // @description - // @author orax // @match http*://app.destinyitemmanager.com/*/d2/inventory // @grant GM_getValue // @grant GM_setValue // @grant GM_getResourceText // @require https://code.jquery.com/jquery-3.4.1.min.js // @require https://openuserjs.org/src/libs/sizzle/GM_config.js // @license MIT // ==/UserScript== /* https://github.com/mturco/context-menu */ (function () { 'use strict'; const ID_HEADER_DIV = '_dim-mod'; let mode_filtreAjouter = false; let mode_filtreSupprimer = false; let headerDiv; let headerRight; let filterInput; let toggleButton; let cfg; GM_config.init({ id: 'dim-mod-config', // The id used for this instance of GM_config // Fields object fields: { // This is the id of the field barreHaut: { label: 'Barre en haut', // Appears next to field type: 'textarea', // Makes this setting a text field cols: 140, rows: 25, // Default value if user doesn't change it default: `# CONFIGURATION PAR DÉFAUT # perso actuel action = TR_PERSO_1 # filtre* action = EFFACE_FILTRE # filtre+ action = MODE_FILTRE_AJOUTER # filtre− action = MODE_FILTRE_SUPPRIMER # +classe action = AJOUTE_CLASSE_ACTIVE # pve action = FILTRE wishlistnotes:pve8 or wishlistnotes:pve9 or wishlistnotes:pve10 # corrompus action = FILTRE holdsmod:outlaw # déchus action = FILTRE holdsmod:forge # ruche action = FILTRE modslot:opulent # vex action = FILTRE holdsmod:undying # armures PM action = FILTRE is:armor masterwork:>=9 # pas équipé action = FILTRE is:incurrentchar not:equipped is:haspower not:invault # armes action = FILTRE is:weapon not:locked not:tagged or tag:junk not:maxpower not:exotic not:masterwork # armures < 60 action = FILTRE is:armor not:classitem not:locked not:tagged or tag:junk not:maxpower not:exotic not:white basestat:total:<60 # équip. classe action = FILTRE is:armor is:classitem not:locked not:tagged or tag:junk not:maxpower not:exotic not:masterwork energycapacity:<5 # bleus action = FILTRE is:blue is:haspower not:maxpower # poubelle action = FILTRE is:armor not:tagged or tag:junk not:maxpower not:exotic not:masterwork modslot:none # tag:junk action = FILTRE (tag:junk or is:blue) not:maxpower is:haspower #nv action = FILTRE is:new not:maxpower is:haspower not:exotic not:locked #nv armes action = FILTRE is:new is:weapon not:maxpower is:haspower not:exotic not:locked # COFFRE action = TR_COFFRE # PVE 7 action = FILTRE wishlistnotes:pve7 # PVE 8 action = FILTRE wishlistnotes:pve8 # PVE 9 action = FILTRE wishlistnotes:pve9 # PVE 10 action = FILTRE wishlistnotes:pve10 # PVE ≥7 action = FILTRE wishlistnotes:pve10 or wishlistnotes:pve9 or wishlistnotes:pve8 or wishlistnotes:pve7`, }, }, // https://github.com/sizzlemctwizzle/GM_config/wiki/Event-Callbacks events: { close: start, }, }); // attend que DIM soit chargé et exécute l'initialisation start(); function x(xpath) { return document.evaluate( xpath, document.body, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null ).singleNodeValue; } async function waitXPathElementLoaded(xpath) { while (x(xpath) === null) { await new Promise((resolve) => requestAnimationFrame(resolve)); } return x(xpath); } async function waitElementLoaded(selector) { while (document.querySelector(selector) === null) { await new Promise((resolve) => requestAnimationFrame(resolve)); } return document.querySelector(selector); } function dim_clickOnCharacter(charNumber) { switch (charNumber) { case 1: $('div.character.current.destiny2').click(); break; case 2: break; case 3: break; } } function dim_clickOnCharacterMenuItem(itemText) { dim_clickOnCharacter(1); $('span[title="' + itemText + '"]').click(); } function dim_getCurrentCharacterClass() { return dim_normalizeClassName( $('div.character.current.destiny2 div.class').text() ); } function dim_getCharacterClass(index) { let charClass = document.querySelectorAll( 'div.character.destiny2 div.class' )[index].textContent; return dim_normalizeClassName(charClass); } function dim_addCharacterClassToFilter(characterClass) { let currClass = characterClass.toLowerCase(); dim_addFilter('is:' + currClass); } function dim_clickOnRightMenu(item) { $('button#downshift-1-toggle-button').click(); // clique sur les trois points verticaux if (typeof item === 'number') { $('div#downshift-1-item-' + item).click(); // le premier index est 0 } else if (typeof item === 'string') { $('div#downshift-1-menu div:contains(' + item + ')').click(); } } function dim_activateFarming(charNumber) { dim_clickOnCharacter(charNumber); waitXPathElementLoaded('//span[text() = "Mode farming"]').then((e) => { e.click(); }); } function dim_setFilter(filter) { filterInput.value = filter + ' \n'; filterInput.dispatchEvent( new Event('change', { bubbles: true, }) ); toggleButton.click(); } function dim_addFilter(filter) { let newFilter = dim_getFilter(); if (newFilter.length) { newFilter += ' '; } newFilter += filter; dim_setFilter(newFilter); } function dim_removeFilter(filter) { let newFilter = dim_getFilter().replace(filter, ''); if (newFilter.match(/ +/g)) { dim_clearFilterInputbox(); } else { dim_setFilter(newFilter); } } function dim_clearFilterInputbox() { let clearSearchFilterButton = document.querySelectorAll( 'div.search-filter._2IHK9 button.filter-bar-button' )[1]; if (clearSearchFilterButton !== undefined) { clearSearchFilterButton.click(); } } function dim_getFilter() { return filterInput.value.trim(); } function dim_normalizeClassName(className) { return className .replace('Arcaniste', 'Warlock') .replace('Chasseur', 'Hunter'); } function normalizeNewline(str) { return str.replace(/\r\n|\r/g, '\n'); } // réexécute le script s'il a été déchargé lors d'un changement de page par exemple function checkAndReloadMod() { // si le mod n'est plus chargé if ( !document.getElementById(ID_HEADER_DIV) && document.location.href.match('/inventory') ) { console.log('DIM mod : le script va être rechargé'); start(); } else { setTimeout(checkAndReloadMod, 5000); } } // supprime ce qui a été ajouté par ce script function clean() { let el = document.getElementById(ID_HEADER_DIV); if (el !== null) { el.remove(); } } function setup_addInventoryAnchors(offset) { /* Commis des postes Armes Armures ... */ $('div.store-row.inventory-title').each(function (index) { $(this).before($('<a id="_dimMod_inventory' + index + '"></a>')); }); addStyle('html { scroll-padding-top:' + offset + 'px; }'); } function setup_add_barreGauche() { let header = $('header#header'); // https://codepen.io/chriscoyier/pen/NJJERg let offset = header.outerHeight() + $('div.store-row.store-header').outerHeight(); let elems = $(` <nav id="_dimMod_barreGauche"> <ul> <li><a href="#"></a></li> <li><a href="#_dimMod_inventory1"></a></li> <li><a href="#_dimMod_inventory2"></a></li> <li><a href="#_dimMod_inventory3"></a></li> <li><a href="#_dimMod_inventory4"></a></li> </ul> </nav>`); addStyle(` nav#_dimMod_barreGauche { position: fixed; top: ${offset}px; } nav#_dimMod_barreGauche ul { width: 10px; list-style: none; margin: 0; padding: 0; height: 100px; } nav#_dimMod_barreGauche li { height: 100%; } nav#_dimMod_barreGauche li a { display: block; height: 100%; } nav#_dimMod_barreGauche li:nth-child(odd) a { background-color: rgba(185, 255, 0, 0.2); } nav#_dimMod_barreGauche li:nth-child(even) a { background-color: rgba(40, 255, 0, 0.1); } `); header.append(elems); setup_addInventoryAnchors(offset); } function addStyle(css) { let doc = document; let elem = doc.createElement('style'); let target = doc.getElementsByTagName('head')[0] || doc.body || doc.documentElement; elem.textContent = css; target.appendChild(elem); } function start() { console.log('Tentative de chargement de « DIM mod »'); let el = document.querySelector('div.equipped-item'); if (el) { clean(); init(); } else { setTimeout(start, 2500); // try again in ... milliseconds } } function setup() { headerDiv = $( '<div id="' + ID_HEADER_DIV + '" style="display: flex;flex-wrap: wrap;" class="BcBfoaH1"></div>' ); headerRight = $('header span.search-button'); filterInput = document.querySelector('input.filter-input'); // <input> recherche toggleButton = document.querySelector( 'button#downshift-0-toggle-button' ); // récupère la configuration créé par l'utilisateur cfg = { barreHaut: normalizeNewline(GM_config.get('barreHaut')) .replace(/\B\/\/.*/g, '') // supprime commentaires .replace(/^ +| +$/gm, ''), }; } function init() { setup(); console.log('DIM mod : init'); $('header').append(headerDiv); // bouton « cfg » if (!document.getElementById('dim-mod-cfg')) { console.log('app'); // debugger; headerRight.after( $('<button id="dim-mod-cfg">cfg</button>').click(function () { GM_config.open(); }) ); // debugger; } // barre en haut // analyse la configuration et ajoute les boutons let sections = cfg.barreHaut.matchAll(/^#.+(?:\n(?:.+\n)+)/gm); for (const section of sections) { let btnName = section[0].match(/# *(.+)/)[1]; let matches = section[0].matchAll(/(\w+) *= *(.*)/g); let btn = $('<button>' + btnName + '</button>'); // m[1] : avant le « = » // m[2] : après le « = » for (const m of matches) { let key = m[1]; if (key.match(/action/i)) { let params = m[2].match(/(\w+)(?: +(.+))?/); switch (params[1]) { case 'FILTRE': // évènement "input" pour notifier DIM du changement de la valeur de l'input btn.on('click', function () { console.log('FILTRE'); if (mode_filtreAjouter) { dim_addFilter(params[2]); } else if (mode_filtreSupprimer) { dim_removeFilter(params[2]); } else { dim_setFilter(params[2]); } }); break; case 'CLIC_MENU': { btn.on('click', function () { console.log('CLIC_MENU : "' + params[2] + '"'); dim_clickOnCharacterMenuItem(params[2]); }); break; } case 'EFFACE_FILTRE': { btn.on('click', function () { console.log('EFFACE_FILTRE'); dim_clearFilterInputbox(); }); break; } case 'AJOUTE_CLASSE_ACTIVE': { btn.on('click', function () { console.log('AJOUTE_CLASSE'); dim_addCharacterClassToFilter( dim_getCurrentCharacterClass() ); }); break; } case 'AJOUTE_CLASSE': { btn.on('click', function () { dim_addCharacterClassToFilter( dim_getCharacterClass(params[2] - 1) ); }); break; } case 'MODE_FILTRE_AJOUTER': { btn.on('click', function () { console.log('MODE_FILTRE_AJOUTER'); mode_filtreAjouter = !mode_filtreAjouter; if (mode_filtreAjouter) { this.style = 'color: red'; } else { this.style = ''; } console.log(mode_filtreAjouter); }); break; } case 'MODE_FILTRE_SUPPRIMER': { btn.on('click', function () { console.log('MODE_FILTRE_SUPPRIMER'); mode_filtreSupprimer = !mode_filtreSupprimer; if (mode_filtreSupprimer) { this.style = 'color: red'; } else { this.style = ''; } console.log(mode_filtreAjouter); }); break; } case 'TR_COFFRE': btn.on('click', function () { console.log('TR_COFFRE'); dim_clickOnRightMenu('Envoyer au Coffre'); }); break; case 'CLIC_MENU_DROITE': btn.on('click', function () { console.log('CLIC_MENU_DROITE ' + params[2]); dim_clickOnRightMenu(params[2]); }); break; case 'FARM_PERSO_1': { btn.on('click', function () { dim_activateFarming(1); }); break; } case 'TR_PERSO_1': btn.on('click', function () { console.log('TR_PERSO_1'); dim_clickOnRightMenu(0); }); break; case 'AFFICHE_CONFIG': btn.on('click', function () { console.log('AFFICHE_CONFIG'); GM_config.open(); }); break; } } if (key.match(/css/i)) { btn[0].style.cssText = m[2]; } headerDiv.append(btn); } } // barre à gauche setup_add_barreGauche(); dim_activateFarming(1); setTimeout(checkAndReloadMod, 5000); } /* // attend que DIM soit chargé et exécute l'initialisation let observer = new MutationObserver(function (mutations, observer) { for (let i = 0; i < mutations.length; i++) { for (let j = 0; j < mutations[i].addedNodes.length; j++) { if (mutations[i].addedNodes[j].matches('div#content')) { //div#content div.inventory-content //item-drag-container item-type-Kinetic console.write("DIM mod"); observer.disconnect(); init(); } } } }); observer.observe(document.documentElement, { childList: true, subtree: true }); */ })(); /* # perso actuel action = TR_PERSO_1 # filtre* action = EFFACE_FILTRE # filtre+ action = MODE_FILTRE_AJOUTER # filtre− action = MODE_FILTRE_SUPPRIMER # +classe action = AJOUTE_CLASSE_ACTIVE # pve action = FILTRE wishlistnotes:pve8 or wishlistnotes:pve9 or wishlistnotes:pve10 # corrompus action = FILTRE holdsmod:outlaw # déchus action = FILTRE holdsmod:forge # ruche action = FILTRE modslot:opulent # vex action = FILTRE holdsmod:undying # armures PM action = FILTRE is:armor masterwork:>=9 # pas équipé action = FILTRE is:incurrentchar not:equipped is:haspower not:invault # armes action = FILTRE is:weapon not:locked not:tagged or tag:junk not:maxpower not:exotic not:masterwork # armures < 60 action = FILTRE is:armor not:classitem not:locked not:tagged or tag:junk not:maxpower not:exotic not:white basestat:total:<60 # équip. classe action = FILTRE is:armor is:classitem not:locked not:tagged or tag:junk not:maxpower not:exotic not:masterwork energycapacity:<5 # bleus action = FILTRE is:blue is:haspower not:maxpower # poubelle action = FILTRE is:armor not:tagged or tag:junk not:maxpower not:exotic not:masterwork modslot:none # tag:junk action = FILTRE (tag:junk or is:blue) not:maxpower is:haspower #nv action = FILTRE is:new not:maxpower is:haspower not:exotic not:locked #nv armes action = FILTRE is:new is:weapon not:maxpower is:haspower not:exotic not:locked # COFFRE action = TR_COFFRE # PVE 7 action = FILTRE wishlistnotes:pve7 # PVE 8 action = FILTRE wishlistnotes:pve8 # PVE 9 action = FILTRE wishlistnotes:pve9 # PVE 10 action = FILTRE wishlistnotes:pve10 # PVE ≥7 action = FILTRE wishlistnotes:pve10 or wishlistnotes:pve9 or wishlistnotes:pve8 or wishlistnotes:pve7 */