Let you check monster's debuff in official Orna Codex page.
当前为
// ==UserScript== // @name Monster debuff checker for Orna.RPG // @namespace http://tampermonkey.net/ // @version 1.1.2 // @description Let you check monster's debuff in official Orna Codex page. // @author RplusTW // @match https://playorna.com/codex/raids/*/* // @match https://playorna.com/codex/bosses/*/* // @match https://playorna.com/codex/followers/*/* // @match https://playorna.com/codex/monsters/*/* // @icon https://www.google.com/s2/favicons?sz=64&domain=playorna.com // @require https://cdn.jsdelivr.net/npm/[email protected] // @grant GM_registerMenuCommand // @grant GM_setValue // @grant GM_getValue // @run-at document-end // @license MIT // ==/UserScript== let autoInit = GM_getValue('autoInit') || false; GM_registerMenuCommand('Auto Init. ?', toggleAutoInit, 'A'); function toggleAutoInit() { autoInit = window.confirm('Enable Auto initialize for debuff checker?') GM_setValue('autoInit', autoInit); } window.addEventListener('load', function() { if (autoInit) { init(); } else { document.querySelector('.codex-page-icon')?.addEventListener('dblclick', init, { once: true, }); } }, false); async function init() { let style = document.createElement('style'); style.textContent = `.cus-checker{opacity:.3}.cus-checker:checked{opacity:.75}.cus-checker:checked+*{opacity:.5}`; document.head.append(style); collapsePage(); let monster = await getEnInfo(); initEffects(monster.effects); } function collapsePage() { let tags = [...document.querySelectorAll('.codex-page h4, .codex-page h4 ~ div')]; if (!tags.length) { return; } let box = null; let sections = tags.reduce((all, tag) => { if (tag.tagName === 'H4') { all[all.length] = [ tag, [] ]; } else if (tag.tagName === 'DIV') { all[all.length - 1][1].push(genDetailsItem('', tag.innerHTML)); tag.remove(); } return all; }, []); sections.forEach(section => { section[0].insertAdjacentHTML( 'beforebegin', genDetailsWrapper( genDetails( section[0].textContent.trim(), section[1].join('') ) ) ); section[0].remove(); }); } function initEffects(effects) { let box = document.querySelector('.codex-page'); let html = ''; for (let prop in effects) { // effects[prop] = slimEffects(effects[prop]); html += genEffectHtml(prop, slimEffects(effects[prop])); }; box.innerHTML += `<hr>${genDetailsWrapper(html)}`; } function genEffectHtml(prop, effects) { let items = effects.map(eff => genDetailsItem(eff[0], ` <span> ${eff[0]}, <sub>${eff[1].join()}%</sub> </span> `)).join(''); return genDetails(prop, items); } function genDetailsItem(name, ctx) { return ` <li> <label> <input type="checkbox" name="${name}" class="cus-checker"> ${ctx} </label> </li> `; } function genDetailsWrapper(html) { return `<div style="display:flex;justify-content:space-evenly;flex-wrap:wrap;">${html}</div>` } function genDetails(title, listHtml) { return ` <details open style="width:fit-content;"> <summary style="text-transform:capitalize;"> ${title} </summary> <ul style="list-style:none;text-align:start;padding:0;">${listHtml}</ul> </details>` } function slimEffects(effects) { let eff = effects.reduce((all, e) => { let o = e.match(/^(\D+)\s\((\d+)/); all[o[1]] = all[o[1]] || []; all[o[1]].push(+o[2]); return all; }, {}); return Object.keys(eff).map(prop => { return [prop, [...new Set(eff[prop])].sort().reverse()]; }).sort((a, b) => a[0].localeCompare(b[0])); return eff; } async function getEnInfo() { let html = await getUrlSource(getURL()); let h1 = parseHtml(html, 'h1.herotext'); let data = itemParse(html); let skillWord = skillWords.find(str => data[str]); let skills = itemParse(html)[skillWord]; let effects = await parseSkillEffect(skills); return { title: h1[0].textContent.trim(), skills, effects, }; } async function parseSkillEffect(skills) { // getURL() let sources = await Promise.all( skills.map( skill => getUrlSource(getURL(skill.url)) ) ); let effects = skills.reduce((all, skill, index) => { skill.effect = itemParse(sources[index]); for (let prop in skill.effect) { if (!all[prop]) { all[prop] = []; } let _es = skill.effect[prop].map(e => e.title); all[prop] = all[prop].concat(_es); } return all; }, {}); return effects; } async function getUrlSource(url) { return fetch(url).then(res => res.text()); } function parseHtml(html, selectoor = '') { let doc = document.implementation.createHTMLDocument(); doc.body.innerHTML = html; return [...doc.querySelectorAll(selectoor)]; } function itemParse(html) { let dataDivs = parseHtml(html, '.codex-page h4, .codex-page h4 ~ div'); let data = dataDivs.reduce((all, div) => { if (div.tagName === 'H4') { let _prop = div.textContent.replace(/[::]/, '').trim().toLowerCase(); all.currentProp = _prop; all[_prop] = all[_prop] || []; } else if (div.tagName === 'DIV') { let icon = div.querySelector('img')?.src; all[all.currentProp].push({ icon: div.querySelector('img')?.src, url: div.querySelector('a')?.href, title: div.textContent.trim(), }); } return all; }, {}); delete data.currentProp; return data; } function getURL(url = location.href, lang = 'en') { return url; // let a = document.createElement('a'); // a.href = url; // a.search = `lang=${lang}`; // a.href = 'https://api.codetabs.com/v1/proxy?quest=' + a.href; // return a.href; } const skillWords = [ "Skills", "Compétences ", "Habilidades", "Fähigkeiten", "Умения", "技能", "Umiejętności", "Készségek", "Навички", "Abilità", "스킬", "スキル" ].map(str => str.toLowerCase());