Skip to content

Instantly share code, notes, and snippets.

@alt0172
Last active March 25, 2019 04:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alt0172/4e88d7d7dde33d945242cacc95a37dbf to your computer and use it in GitHub Desktop.
Save alt0172/4e88d7d7dde33d945242cacc95a37dbf to your computer and use it in GitHub Desktop.
// ==UserScript==
// @name poedb synth mod helper
// @namespace http://tampermonkey.net/
// @version 0.18
// @description adds icon near each mod, on click shows what synthesised mods can be crafted from items with this mod. if mod isn't used in crafting, it's marked too
// @author alt0172
// @match http://poedb.tw/*mod*
// @match https://poedb.tw/*mod*
// @run-at document-idle
// @grant GM_addStyle
// @require https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js
// @updateURL https://gist.github.com/alt0172/4e88d7d7dde33d945242cacc95a37dbf/raw/poedb_synth_mod_helper.user.js
// @downloadURL https://gist.github.com/alt0172/4e88d7d7dde33d945242cacc95a37dbf/raw/poedb_synth_mod_helper.user.js
// ==/UserScript==
// v 0.18 - fixed things broken in 0.17
// v 0.17 - should not cause errors when run on wrong pages
// v 0.16 - reworked language handling
(function() {
// synth mods list (bottom of the page) styles
GM_addStyle('tr.hiddenSynthRow{display:none;}' +
'.synthFilter{float: left; margin-right: 10px !important; visibility:hidden;}' +
'tr:hover .synthFilter, .visibleSynthRow .synthFilter{ visibility:visible;}');
// new icon (search/dead mod) styles
GM_addStyle('.synth-search { float: left; width: 16px; margin-left: -26px; margin-right: 8px; text-align: center; visibility:hidden; color: #78c4ea;}'+
'.mod-title:hover .synth-search { visibility:visible;}'+
'.dead-mod-2 { float: left; width: 16px; margin-left: -26px; margin-right: 10px; text-align: center; color: #c22;}');
var timerMid, timerStart = performance.now();
var allMods, synthId, i, i2, l, obj, shortText, tmp;
var synthA, synthCaption, synthRows ;
var allSynthMods = [];
// language related vars
var lang, supported;
var translation = {
tw:{
in:[],
out:[],
synthModText: '變動詞綴'
},
cn:{
in:[],
out:[],
synthModText: '变动词缀'
},
us:{
in:['reduced', 'Trigger a Socketed'],
out:['increased', '#% chance to Trigger a Socketed'],
synthModText: 'Explicit Mod Stat'
},
ru:{
in:['снижение', 'Вызывает срабатывание'],
out:['увеличение', '#% шанс вызвать срабатывание'],
synthModText: 'Explicit Mod Stat'
},
pt:{
in:[],
out:[],
synthModText: 'Explicit Mod Stat'
},
th:{
in:[],
out:[],
synthModText: 'Explicit Mod Stat'
},
fr:{
in:[],
out:[],
synthModText: 'Explicit Mod Stat'
},
de:{
in:[],
out:[],
synthModText: 'Explicit Mod Stat'
},
sp:{
in:[],
out:[],
synthModText: 'Explicit Mod Stat'
}
};
// end of language related vars
/* ------------------------------------- functions ------------------------------------- */
function removePlus(str) {
// # to level of socketed gems <-- in item mods
// +# to level of socketed gems <-- in synthesis mods
// to fix this better remove '+' from everywhere if it's the first character
if (str[0] == '+') {
return str.substr(1);
} else {
return str;
}
}
function figureLanguage() {
var langCode;
var txt2;
// check text of "gems" item in the top menu
switch ( document.querySelector('body>.navbar-top ul.navbar-nav>li.dropdown>a').textContent ) {
case '技能 ':
//tw
langCode = 'tw';
break;
case '宝石 ':
//cn
langCode = 'cn';
break;
case 'Gema ':
//pt OR sp
// submenu item
// pt: Gemas de Habilidade Ativa
// sp: Gemas de Habilidades Activas
txt2 = document.querySelector('body>.navbar-top ul.navbar-nav>li.dropdown>ul>li:first-child>a').textContent;
if ( txt2 == "Gemas de Habilidade Ativa" ) {
langCode = 'pt';
} else {
langCode = 'sp';
}
break;
case 'เจ็ม ':
//th
langCode = 'th';
break;
case 'Gemme ':
//fr OR de
// submenu item
// fr: Gemmes d'aptitude
// de: Aktive Fertigkeitengemmen
txt2 = document.querySelector('body>.navbar-top ul.navbar-nav>li.dropdown>ul>li:first-child>a').textContent;
if ( txt2 == "Gemmes d'aptitude" ) {
langCode = 'fr';
} else {
langCode = 'de';
}
break;
case 'Камень ':
//ru
langCode = 'ru';
break;
case 'Gem ':
//us (english)
default:
langCode = 'us';
break;
}
return langCode;
}
function isLangSupported( langCode ){
var res = false;
switch (langCode) {
case 'tw':
case 'cn':
case 'pt':
case 'th':
case 'fr':
case 'de':
case 'sp':
res = false;
break;
case 'ru':
res = true;
break;
case 'us':
default:
res = true;
break;
}
return res;
}
function langBasedEdits(str) {
// example: in item mods section mod is
// #% reduced Soul Cost of Vaal Skills
// in synth. mods table similar mod is worded as
// #% increased Soul Cost of Vaal Skills
// here all edit required to count these mods as the same are done
var res = str;
if (supported) {
for (var i = 0; i < lang.in.length; i++) {
res = res.replace(lang.in[i], lang.out[i]);
}
}
return res;
}
function getAllIndexes(arr, val) {
var indexes = [], i = -1;
while ((i = arr.indexOf(val, i + 1)) != -1) {
indexes.push(i);
}
return indexes;
}
function filterSynthTable(obj) {
//var tmp = obj.parentNode.dataset.synthFilter;
//var arr = tmp.split(',');
var arr = obj.parentNode.dataset.synthFilter.split(',');
var l = synthRows.length;
for (var i = 0; i < l; i++) {
synthRows[i].classList.remove('hiddenSynthRow', 'visibleSynthRow'); // reset row visibility
if (arr.includes(i+'')) {
// mod should be shown
synthRows[i].classList.add('visibleSynthRow');
synthRows[i].getElementsByClassName('synthFilter')[0].checked = true;
} else {
// mod should be hidden
synthRows[i].classList.add('hiddenSynthRow');
synthRows[i].getElementsByClassName('synthFilter')[0].checked = false;
}
}
synthA.scrollIntoView();
}
function filterSynthMods() {
// this <-- span
var targetText = this.parentNode.textContent;
var l = synthRows.length;
if (this.parentNode.parentNode.classList.contains('visibleSynthRow')) {
// click on visible row = uhnide all
for (i2 = 0; i2 < l; i2++) {
synthRows[i2].classList.remove('hiddenSynthRow', 'visibleSynthRow');
synthRows[i2].getElementsByClassName('synthFilter')[0].checked = false;
}
} else {
//click on another row (?!) = filter
for (i2 = 0; i2 < l; i2++) {
if (synthRows[i2].children[synthId].textContent == targetText) {
synthRows[i2].classList.add('visibleSynthRow');
synthRows[i2].getElementsByClassName('synthFilter')[0].checked = true;
} else {
synthRows[i2].classList.add('hiddenSynthRow');
}
}
}
}
/* ------------------------------------- /functions ------------------------------------- */
var timerId2 = setInterval(function() {
synthA = document.querySelector('a[id^=SynthesisItemSynthesisMods]');
if ( synthA !== null && typeof synthA.nextElementSibling.children[1] != undefined ) {
clearInterval(timerId2);
// only do things after synth mods table has been loaded
timerMid = performance.now()
console.log('Synth helper - loading took '+ timerMid +'ms');
// 0 - language handling
//
if (unsafeWindow.language == undefined) {
//language handling hasn't been done yet
unsafeWindow.language = figureLanguage();
}
lang = translation[unsafeWindow.language];
supported = isLangSupported(unsafeWindow.language);
// 1 - find what TD synth mod is written in
//
synthCaption = synthA.nextElementSibling.children[1].children[1].children[0];
// TABLE THEAD TR
synthRows = synthA.nextElementSibling.children[1].children[2].children;
// TABLE TBODY TR collection
for (i = 0; i < synthCaption.children.length; i++) {
if (synthCaption.children[i].innerHTML == lang.synthModText) {
// TR TH text content
synthId = i;
break
}
}
// 2 - Go through synthesised mod list table and:
// - make 1 (huge) array of all mods used in synthesis (to not check dom tree every time)
// - remove "+" if mod starts with it
// - add checkbox in each row + on click event
l = synthRows.length;
for (i = 0; i < l; i++) {
allSynthMods.push(removePlus(synthRows[i].children[synthId].textContent));
// TR TD
obj = document.createElement('input');
obj.type = 'checkbox';
obj.checked = false;
obj.classList.add('synthFilter');
synthRows[i].children[synthId].appendChild(obj);
obj.addEventListener("click", filterSynthMods);
}
// 3 - Go through all mods and
// - get text representing this mod
// - compare it to synthesised mods
// - add icon based on comparsion above
// only do things below if language is supported
if (!supported) {return;}
var currentPadding = 5;
try {
currentPadding = window.getComputedStyle( document.getElementsByClassName('mod-title')[0] ).getPropertyValue('padding-left').replace('px','');
} catch (err){}
GM_addStyle('.mod-title { padding-left: '+(24+(1*currentPadding))+'px !important; }'+
'.ilvl-test { margin-left: -'+(19+(1*currentPadding))+'px;}');
allMods = document.querySelectorAll('.col-lg-6 .mod-title');
l = allMods.length;
for (i = 0; i < l; i++) {
// 1 - get text
obj = allMods[i].firstChild;
shortText = [''];
i2 = 0;
while (obj) {
// obj - child node (element or text) of div.mod-title
if (obj.nodeType === 3) {
// text node
shortText[i2] += obj.textContent;
} else if (obj.nodeType === 1) {
// element (SPAN or DIV or BR)
if (obj.nodeName === 'BR') {
// for two line mods check each line
i2 = 1;
shortText[i2] = '';
}
if (obj.classList.contains("mod-value")) {
shortText[i2] += obj.textContent;
}
}
obj = obj.nextSibling;
}
for (i2 = 0; i2 < shortText.length; i2++) {
// remove whitespaces
// remove "+"
// replace some words to consider same mods with different phrasing the same (example: "reduced attribute requirements" and "increased attribute requirements")
shortText[i2] = langBasedEdits( removePlus(shortText[i2].trim()) );
}
// 2 - compare
// check first line - is it in synth mods array?
tmp = getAllIndexes(allSynthMods, shortText[0]);
if (shortText.length > 1) {
// check second line (if there is one)
tmp = tmp.concat(getAllIndexes(allSynthMods, shortText[1]));
}
// 3 - create new element
//
obj = document.createElement('div');
allMods[i].insertBefore(obj, allMods[i].children[0]);
if (tmp.length > 1) {
// mod that is used in synth crafting
obj.appendChild(document.createTextNode('🔍'));
obj.classList.add("synth-search");
allMods[i].dataset.synthFilter = tmp.toString();
obj.addEventListener("click", function(e) {
e.stopPropagation();
filterSynthTable(this);
});
} else {
// "dead" mod
obj.appendChild(document.createTextNode('🚫'));
obj.classList.add("dead-mod-2");
}
} // end of allMods iteration
console.log('Synth helper took ' + (performance.now() - timerMid) + 'ms');
}
}, 300);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment