您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Подробная инфа артов/существ/навыков
// ==UserScript== // @name HWM_show_info // @author Мифист // @namespace Мифист // @version 1.0.0 // @description Подробная инфа артов/существ/навыков // @match https://www.heroeswm.ru/* // @match https://*.lordswm.com/* // @exclude */war.php* // @exclude */roulette.php* // @run-at document-end // @grant none // @license MIT // @noframes // ==/UserScript== (function initModule(view) { 'use strict'; if (document.visibilityState === 'hidden') { const handler = () => initModule(view); document.addEventListener('visibilitychange', handler, { once: true }); return; } if (document.readyState === 'loading') { const handler = () => initModule(view); document.addEventListener('DOMContentLoaded', handler, { once: true }); return; } // ========================== const MODULE_NAME = 'HWM_show_info'; const MODULE_VERSION = '1.0.0'; const modules = (function(symbol) { return view[symbol] || (view[symbol] = { stack: new Map, has(key) { return this.stack.has(key); }, delete(key) { return this.stack.delete(key); }, get(key) { return this.stack.get(key); }, add(key, version, exports) { if (this.stack.has(key)) return; this.stack.set(key, { version, exports }); } }); })(Symbol.for('__5781303__')); if (modules.has('HWM_auction_upd')) return; // ========================== let hideFrame = Function.prototype; const outerStyleSheet = document.createElement('style'); const $ = (selector, ctx = document) => ctx.querySelector(selector); function fetch(url) { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open('GET', url); xhr.responseType = 'document'; xhr.onload = () => { if (xhr.status === 200) return resolve(xhr.response); reject(new Error(`Error status: ${xhr.status}`)); }; xhr.onerror = () => reject(new Error('ERR_INTERNET_DISCONNECTED')); xhr.send(null); }); } // ========================== async function handleTarget(e) { const trg = e.target; const anchor = getElemAnchor(trg); if (!anchor) return; e.preventDefault(); hideFrame(); const self = InfoFrame.instances[anchor.flag]; let data = self.cache.get(anchor.id); if (!document.contains(self.frame)) { if (!navigator.onLine) return onInternetDisconnect(); await self.onConnect(self.getURL(anchor.id)); } if (data == null) { data = await pullRequest(self, anchor.id); if (data == null) return; } self.setHide(trg); self.render(data); self.shell.classList.add('__shown'); centerFrame(self, getClosestBlockElem(trg)); if (self === ArmyInfo) self.initHints(); } function onInternetDisconnect(err) { const msg = err ? err.message : 'ERR_INTERNET_DISCONNECTED'; alert(`${msg}\nНет подключения к Интернету`); } function pullRequest(self, id) { return self.request(id) .then(data => self.cache.set(id, data).get(id)) .catch(onInternetDisconnect); } // ========================== class InfoFrame { static __init__() { if (this !== InfoFrame) return; const instances = this.instances = [PerkInfo, ArtInfo, ArmyInfo]; instances.forEach((self, ind) => { self.flag = ind; if (!$(self.selector)) return; self.cache = new Map; self.shell = document.createElement('div'); self.frame = document.createElement('iframe'); self.shell.classList.add(`${MODULE_NAME}-shell`); self.frame.classList.add(`${MODULE_NAME}-frame`); }); if (instances.every(self => !self.hasOwnProperty('cache'))) return; outerStyleSheet.append(this.outerCSS); document.addEventListener('contextmenu', handleTarget); const destroyType = MODULE_NAME + '__destroy'; const destroyHandler = this.__destroy__.bind(this); document.addEventListener(destroyType, destroyHandler, { once: true }); modules.add(MODULE_NAME, MODULE_VERSION, this); } static __destroy__() { hideFrame(); this.instances.splice(0).forEach(self => { if (!self.hasOwnProperty('cache')) return; self.cache.clear(); self.shell.remove(); }); outerStyleSheet.remove(); document.removeEventListener('contextmenu', handleTarget); modules.delete(MODULE_NAME); } static get outerCSS() { return /*css*/` .${MODULE_NAME}-shell { --w: 0; --h: 0; --x: 0; --y: 0; width: var(--w); height: var(--h); display: none; position: absolute; left: var(--x); top: var(--y); z-index: 100; } .${MODULE_NAME}-shell.__shown { display: block; } .${MODULE_NAME}-shell::before, .${MODULE_NAME}-shell::after { content: ""; height: 5px; position: absolute; left: 0; right: 0; top: -5px; } .${MODULE_NAME}-shell::after { top: auto; bottom: -5px; } .${MODULE_NAME}-frame { width: 100%; height: 100%; display: block; border: none; outline: 2px solid #72787c; resize: none; overflow: hidden; user-select: none; } .hwm_hint_css, div[style^="z-index:1;top:0;right:0;"] { pointer-events: none; } `.replace(/^ +/gm, ''); } static get frameView() { return this.frame.contentWindow; } static get frameDoc() { return this.frame.contentDocument; } static onConnect(url) { const {frame, shell} = this; if (!document.contains(outerStyleSheet)) { document.head.append(outerStyleSheet); } frame.src = url; shell.append(frame); document.body.prepend(shell); return new Promise(resolve => { frame.onload = () => { this.onFrameLoad(); resolve(); }; }); } static onFrameLoad() { const {frame, frameView, frameDoc} = this; const target = this.target = frameDoc.createElement('div'); target.id = 'cont'; clearAsyncQueue(frameView); frameView.setTimeout(() => clearAsyncQueue(frameView), 200); frameView.addEventListener('error', (e) => { console.log(e); alert(`@${MODULE_NAME} => ${this.name}:\n${e.message}`); if (this === ArmyInfo) this.shell.remove(); }); frameDoc.head.innerHTML = /*html*/` <base target="_parent"> <style>${this.innerCCS}</style> `; frameDoc.body.replaceChildren(target); } static onFrameHide() {} static setHide(trg) { const {frame, shell, frameDoc} = this; const onKeyUp = (e) => void (e.key === 'Escape' && hide()); const hide = hideFrame = () => { toggleHandlers(false); shell.classList.remove('__shown'); shell.removeAttribute('style'); hideFrame = Function.prototype; this.onFrameHide(); }; toggleHandlers(true); function toggleHandlers(force) { const method = (force ? 'add' : 'remove') + 'EventListener'; document[method]('click', hide); document[method]('keyup', onKeyUp); frameDoc[method]('keyup', onKeyUp); shell[method]('mouseleave', hide); trg[method]('mouseleave', leave); trg[method]('click', preventClick, true); } function leave({type, relatedTarget}) { this.removeEventListener(type, leave); if (relatedTarget && !relatedTarget.contains(frame)) hide(); } function preventClick(e) { e.preventDefault(); e.stopPropagation(); hide(); } } static render(html) { this.target.innerHTML = html; } } // ========================== class PerkInfo extends InfoFrame { static get innerCCS() { return /*css*/` * { font-family: inherit; font-size: inherit; box-sizing: border-box; } :root { font-size: 10px; } body { font-family: Verdana, Arial, sans-serif; font-size: 1.3rem; line-height: 1.3; margin: 0; background-image: linear-gradient(45deg, #cdc9c0, #fff); overflow: hidden; user-select: none; } #cont { width: 60rem; display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: flex-start; padding: 1rem; } #cont > h1 { font-size: 1.1em; width: 100%; margin: 0 0 1rem; color: #435970; text-transform: uppercase; } #cont > div { flex: 1; padding-left: 1rem; } `.replace(/^ +/gm, ''); } static get selector() { return 'a[href*="showperkinfo."]'; } static getURL(id) { return `/showperkinfo.php?name=${id}`; } static getItemId(el) { return new URLSearchParams(el.search).get('name'); } static async request(id) { const responseDoc = await fetch(this.getURL(id)); const img = $('img[src*="/perks/"]', responseDoc); let elem = img && img.closest('td'); elem = elem && elem.nextElementSibling; if (!elem) return ''; return /*html*/` <h1>${img.alt.slice(7)}</h1> <img src="${img.src}"> <div>${elem.innerHTML.slice(20)}</div> `; } } // ========================== class ArtInfo extends InfoFrame { static get innerCCS() { return /*css*/` * { font-family: inherit; font-size: inherit; box-sizing: border-box; } :root { font-size: 10px; } body { font-family: Verdana, Arial, sans-serif; font-size: 1.3rem; line-height: 1.3; margin: 0; background-image: linear-gradient(45deg, #cdc9c0, #fff); overflow: hidden; user-select: none; } #cont { width: 74rem; max-height: 42rem; display: flex; position: relative; overflow-y: auto; scrollbar-width: thin; } .global_container_block_header { font-size: 1.1em; position: absolute; right: 2rem; top: 1rem; } .global_container_block_header h1 { line-height: normal !important; text-transform: uppercase; margin: 0; } .global_container_block_header b { color: #435970; } .art_info_left_block { padding: 2rem 1rem; } .s_art_prop_amount_icon { min-height: 2.8rem; display: flex; align-items: center; justify-content: center; color: #0a2b4b; background-image: linear-gradient(#eee, #bbc4b1); border: 1px solid #78878d; } .s_art_prop_amount_icon:hover { filter: saturate(1.5); } .s_art_prop_amount_icon img { width: 2rem; margin-right: .5rem; } .cre_mon_image1 { display: none; } .art_info_desc { padding: 3rem 1rem 1rem; background: transparent !important; } .rs { margin: 0 2px; } b { color: #332f2f; } i { color: #315473; } a[href*="=40#"] { font-weight: bold; font-style: normal; text-decoration: none; } `.replace(/^ +/gm, ''); } static get selector() { return location.pathname === '/inventory.php' ? '.inv_art_outside' : 'a[href*="art_info."]'; } static getURL(id) { return `/art_info.php?id=${id}`; } static getItemId(el) { if (el.tagName === 'A') return new URLSearchParams(el.search).get('id'); const imgPathReg = /\/artifacts\/([^.]+)/; const getArtName = (html) => html.match(imgPathReg)[1]; const artName = getArtName(el.outerHTML); const {arts = []} = view; const art = arts.find(art => getArtName(art.html) === artName) || {}; return art.art_id; } static async request(id) { const responseDoc = await fetch(this.getURL(id)); const elem = $('#set_mobile_max_width', responseDoc); return elem ? elem.innerHTML : ''; } } // ========================== class ArmyInfo extends InfoFrame { static get innerCCS() { return /*css*/` * { box-sizing: border-box; } :root { font-size: 10px; } body { font-family: Verdana, Arial, sans-serif; font-size: 1.2rem; margin: 0; background-image: linear-gradient(45deg, #dad1be, #fff); overflow: hidden; user-select: none; } .hwm_hint_css { font-size: inherit !important; max-width: 34rem !important; position: fixed; display: none; padding: .4em .7em; color: #ddd; background-color: #3a3a3a; border: 2px solid #888; z-index: 2; } #cont { width: 70rem; display: flex; flex-wrap: wrap; } .info_header_content { width: 100%; height: 3.5em; display: flex; align-items: center; justify-content: center; background: #afc2d747; border-bottom: 1px solid #757575; } a:first-child { font-size: 1.6em; color: #506263; text-decoration: none; } .info_text_content { width: calc(100% - 20rem); display: flex; flex-wrap: wrap; border-right: 1px solid #757575; } .info_text_content > div { font-size: 1.1em; width: 50%; display: flex; align-items: center; column-gap: .5rem; padding: 0 1.4rem; } .info_text_content img { width: 2.4rem; height: auto; } .info_text_content div:last-child { margin-left: auto; } canvas, #show_army, .konvajs-content { width: 20rem !important; height: 20rem !important; } .army_info_skills { font-size: 1.1em; width: 100%; display: flex; flex-wrap: wrap; column-gap: 0.4em; padding: 1rem 1.4rem; border-top: 1px solid #757575; } .army_info_skills > div { font-weight: bold; margin-right: -0.5em; } .army_info_skills > span:hover { color: brown; cursor: help; } `.replace(/^ +/gm, ''); } static get selector() { return 'a[href*="army_info."]'; } static getURL(id) { return `/army_info.php?name=${id}`; } static getItemId(el) { return new URLSearchParams(el.search).get('name'); } static async request(id) { const responseDoc = await fetch(this.getURL(id)); const linkHTML = `<a href="${this.getURL(id)}">$1</a>`; const html = $('.army_info', responseDoc).innerHTML.trim() .replaceAll('\n', '') .replace(/\s{2,}/g, ' ') .replace(' style="display: show;"', '') .replaceAll('> ', '>') .replaceAll(' width="48" height="48" alt="" title=""', '') .replace(/<div><h1 [^>]+>(.+?)<\/h1><\/div>/, linkHTML) .replace(/<div(?:><img| class="corner).+?div>/g, '') .replaceAll(' class="scroll_content_half"', ''); const reg = /info\((.+?)\);/; const script = [...responseDoc.scripts].pop(); const paramsStr = (script.text.match(reg) || ['', ''])[1]; return [html, paramsStr]; } static render([html, paramsStr]) { super.render(html); const {frameView} = this; const params = new Function(`return [${paramsStr}]`)(); frameView.setTimeout(() => frameView.init_army_info(...params)); } static onFrameLoad() { super.onFrameLoad(); const {frameView, target} = this; const hwmHint = frameView.hwm_hint; if (!(hwmHint instanceof frameView.HTMLElement)) return; target.after(hwmHint); } static onFrameHide() { const {frameView: ctx} = this; const stages = ctx.Konva && ctx.Konva.stages || []; stages.splice(0).forEach(stage => ctx.clearInterval(stage.interval)); } static initHints() { const initHwmHints = this.frameView.hwm_hints_init; if (typeof initHwmHints === 'function') initHwmHints(); } } // ========================== InfoFrame.__init__(); // ========================== function centerFrame({target, shell}, elem) { const {offsetHeight, offsetWidth} = target; const {left, right, top, bottom} = elem.getBoundingClientRect(); const offset = 5; const halfw = offsetWidth / 2; const height = offsetHeight + offset; const maxX = document.documentElement.clientWidth - offset; const centerX = left + (right - left) / 2; const x = Math.max(offset, Math.min(centerX - halfw, maxX - offsetWidth)); const y = (bottom + height < view.innerHeight || top - height <= 0) ? bottom + offset : top - height; const setCSS = shell.style.setProperty.bind(shell.style); setCSS('--w', `${offsetWidth >> 0}px`); setCSS('--h', `${offsetHeight >> 0}px`); setCSS('--x', `${x + view.scrollX >> 0}px`); setCSS('--y', `${y + view.scrollY >> 0}px`); } function getElemAnchor(el) { const propName = `__cachedInfoFrameAnchor`; if (el.hasOwnProperty(propName)) return el[propName]; const set = (val = null) => (el[propName] = val); const artElem = el.closest(ArtInfo.selector); const perkElem = artElem ? null : el.closest(PerkInfo.selector); const elem = artElem || perkElem || el.closest(ArmyInfo.selector); if (!elem) return set(); const self = perkElem ? PerkInfo : artElem ? ArtInfo : ArmyInfo; const id = self.getItemId(elem); return !id ? set() : set({ flag: self.flag, id }); } function getClosestBlockElem(elem) { if (elem.offsetWidth && elem.offsetHeight) return elem; return getClosestBlockElem(elem.parentNode); } function clearAsyncQueue(ctx) { let i = ctx.setTimeout(0); while (i) ctx.clearTimeout(i--); } })(document.defaultView);