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
*/