Last active
March 25, 2019 04:46
-
-
Save alt0172/4e88d7d7dde33d945242cacc95a37dbf to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==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