Mudanças entre as edições de "Widget:Item"

De Wiki Gla
Ir para navegação Ir para pesquisar
m
m
 
(6 revisões intermediárias pelo mesmo usuário não estão sendo mostradas)
Linha 1: Linha 1:
<includeonly>
<includeonly>
     <!--
     <!--
       Tooltip lazy: passivas/descrição/valor NÃO vão no HTML da página.
       Tooltip: payload codificado em data-tip (Módulo:Item). Corpo vazio no HTML.
       No hover (mouse real), busca {{#invoke:Item|tooltip|key=...}} e monta na hora.
       JS decodifica no hover (rápido, sem API) e limpa no mouseout.
 
       Ícone: span.item-icon + data-i codificado — sem <img> no HTML; JS hidrata
       Defesa (Widget:Item): rate limit, eventos não confiáveis (console/automação),
       background-image e remove data-i. Anti-arrastar/cópia/clique-direito leve.
       tamper em fetch/XHR. Se disparar: apaga cache, remove data-item-key e tooltips
       Defesa leve: hover real + rate limit de decodes para automação.
      do DOM — ícones ficam. Staff/editor (wgUserGroups) não é afetado.
       Tooltip normal continua para quem usa mouse de verdade.
     -->
     -->
     <style>
     <style>
Linha 36: Linha 34:
             text-align: center;
             text-align: center;
             overflow: visible;
             overflow: visible;
            filter: none;
            -webkit-user-select: none;
            user-select: none;
            -webkit-touch-callout: none;
         }
         }


        .item-icon {
            display: block;
            margin: 0 auto;
            background-repeat: no-repeat;
            background-position: center;
            background-size: contain;
            filter: none;
            box-shadow: none;
            -webkit-user-select: none;
            user-select: none;
            -webkit-user-drag: none;
            -webkit-touch-callout: none;
            pointer-events: none;
        }
        /* Páginas em cache ainda com [[Arquivo:…]] → <img> legado */
         .item-wrapper img {
         .item-wrapper img {
             display: block;
             display: block;
             margin: 0 auto;
             margin: 0 auto;
            filter: none;
            box-shadow: none;
            -webkit-user-select: none;
            user-select: none;
            -webkit-user-drag: none;
            -webkit-touch-callout: none;
            pointer-events: none;
         }
         }


Linha 68: Linha 93:
             line-height: 1;
             line-height: 1;
             white-space: nowrap;
             white-space: nowrap;
             box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
             box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
             z-index: 10;
             z-index: 10;
         }
         }
Linha 78: Linha 103:
         }
         }


        /* Reward dentro de wikitable:
          cria espaço para o badge de quantidade (que é absolute/bottom negativo)
          não sobrepor a linha horizontal da tabela. */
         .wikitable td .reward-items .item-wrapper {
         .wikitable td .reward-items .item-wrapper {
             margin-bottom: 10px;
             margin-bottom: 10px;
         }
         }
        /* ===== TOOLTIP (estilo marrom / ouro — próximo ao jogo) ===== */


         .item-tooltip {
         .item-tooltip {
Linha 94: Linha 114:
             flex-direction: column;
             flex-direction: column;
             align-items: center;
             align-items: center;
             filter: drop-shadow(0 8px 24px rgba(0, 0, 0, 0.65));
             filter: none;
         }
         }


Linha 113: Linha 133:
             box-sizing: border-box;
             box-sizing: border-box;
             font-family: Tahoma, "Segoe UI", Arial, sans-serif;
             font-family: Tahoma, "Segoe UI", Arial, sans-serif;
            /* Brilho interno quase imperceptível (sem degradê no fundo) */
             box-shadow: 0 2px 8px rgba(0, 0, 0, 0.22);
             box-shadow: inset 0 1px 0 rgba(255, 245, 220, 0.035);
         }
         }


Linha 142: Linha 161:
             height: 1px;
             height: 1px;
             background: #5c4a31;
             background: #5c4a31;
            /* Encosta nas bordas internas da caixa (ignora padding lateral) */
             margin: 8px -14px 10px;
             margin: 8px -14px 10px;
             width: calc(100% + 28px);
             width: calc(100% + 28px);
Linha 233: Linha 251:
         }
         }


        /* Seta — cor sólida igual ao fundo */
         .item-tooltip-arrow {
         .item-tooltip-arrow {
             display: block;
             display: block;
Linha 254: Linha 271:
         }
         }


        /* Deslocamento horizontal */
         .item-tooltip.tooltip-shift-right {
         .item-tooltip.tooltip-shift-right {
             align-items: flex-start;
             align-items: flex-start;
Linha 269: Linha 285:
         .item-tooltip.tooltip-shift-left .item-tooltip-arrow {
         .item-tooltip.tooltip-shift-left .item-tooltip-arrow {
             margin-right: var(--arrow-offset, 12px);
             margin-right: var(--arrow-offset, 12px);
        }
        .item-wrapper.item-tooltip-loading {
            opacity: 0.85;
         }
         }
     </style>
     </style>
Linha 281: Linha 293:
             var MARGIN = 8;
             var MARGIN = 8;
             var GAP = 6;
             var GAP = 6;
            var XOR_KEY = 0x5A; /* legado hex — páginas em cache antes do b64 */
             var activeTip = null;
             var activeTip = null;
             var activeWrapper = null;
             var activeWrapper = null;
             var tooltipGen = 0;
             var decodeCache = Object.create(null);
            var tooltipHtmlCache = Object.create(null);
             var decodeLog = [];
 
            /* ===== Defesa anti-scrape (tooltip continua normal pro usuário) ===== */
             var defenseTripped = false;
             var untrustedStrikes = 0;
             var untrustedStrikes = 0;
             var tooltipFetchLog = [];
             var decodeBlocked = false;
             var RATE_WINDOW_MS = 120000;
             var RATE_WINDOW = 120000;
             var RATE_MAX = 45;
             var RATE_MAX = 120;
            var BURST_WINDOW_MS = 15000;
            var BURST_MAX = 18;
            var UNTRUSTED_MAX = 2;
            var nativeFetch = window.fetch ? window.fetch.bind(window) : null;
            var nativeXHROpen = XMLHttpRequest.prototype.open;


             function isStaffUser() {
             function isStaff() {
                 try {
                 try {
                     if (!window.mw || !mw.config) return false;
                     if (!window.mw || !mw.config) return false;
Linha 312: Linha 317:
             }
             }


             function clearTooltipCache() {
             function allowDecode() {
                 Object.keys(tooltipHtmlCache).forEach(function (k) {
                 if (decodeBlocked || !isStaff() && untrustedStrikes >= 4) {
                     delete tooltipHtmlCache[k];
                    decodeBlocked = true;
                 });
                    return false;
                }
                if (isStaff()) return true;
                var now = Date.now();
                while (decodeLog.length && now - decodeLog[0] > RATE_WINDOW) {
                    decodeLog.shift();
                }
                if (decodeLog.length >= RATE_MAX) {
                     decodeBlocked = true;
                    return false;
                 }
                decodeLog.push(now);
                return true;
             }
             }


             function purgeTooltipData() {
             function bytesToUtf8String(bytes) {
                 hideTooltip();
                 if (typeof TextDecoder !== "undefined") {
                clearTooltipCache();
                    return new TextDecoder("utf-8").decode(
                var tips = document.querySelectorAll(".item-tooltip");
                        bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes)
                for (var i = 0; i < tips.length; i++) {
                     );
                     tips[i].remove();
                 }
                 }
                 var wrappers = document.querySelectorAll(".item-has-tooltip");
                 var bin = "";
                 for (var j = 0; j < wrappers.length; j++) {
                 for (var j = 0; j < bytes.length; j++) {
                     wrappers[j].removeAttribute("data-item-key");
                     bin += String.fromCharCode(bytes[j]);
                    wrappers[j].removeAttribute("data-item-lang");
                    wrappers[j].classList.remove("item-has-tooltip", "item-tooltip-loading");
                 }
                 }
                return decodeURIComponent(escape(bin));
             }
             }


             function triggerDefense() {
             function base64ToUtf8String(b64) {
                 if (defenseTripped || isStaffUser()) return;
                 var bin = atob(b64);
                 defenseTripped = true;
                var bytes = new Uint8Array(bin.length);
                 purgeTooltipData();
                 for (var i = 0; i < bin.length; i++) {
                    bytes[i] = bin.charCodeAt(i);
                 }
                return bytesToUtf8String(bytes);
             }
             }


             function registerTooltipFetch() {
             function hexToUtf8String(hex) {
                if (defenseTripped || isStaffUser()) return true;
                 var bytes = [];
                 var now = Date.now();
                 for (var i = 0; i < hex.length; i += 2) {
                 tooltipFetchLog.push(now);
                     bytes.push(parseInt(hex.substr(i, 2), 16) ^ XOR_KEY);
                var i = 0;
                while (i < tooltipFetchLog.length) {
                     if (now - tooltipFetchLog[i] > RATE_WINDOW_MS) {
                        tooltipFetchLog.splice(i, 1);
                    } else {
                        i++;
                    }
                 }
                 }
                 if (tooltipFetchLog.length > RATE_MAX) {
                 return bytesToUtf8String(bytes);
                    triggerDefense();
            }
                     return false;
 
            function payloadToJson(raw) {
                if (!raw) return null;
                if (raw.indexOf("b64:") === 0) {
                     return base64ToUtf8String(raw.slice(4));
                 }
                 }
                 var recent = 0;
                 return hexToUtf8String(raw);
                 for (var j = tooltipFetchLog.length - 1; j >= 0; j--) {
            }
                     if (now - tooltipFetchLog[j] <= BURST_WINDOW_MS) recent++;
 
            function decodePayload(raw) {
                if (!raw) return null;
                 if (decodeCache[raw]) return decodeCache[raw];
                try {
                    var obj = JSON.parse(payloadToJson(raw));
                    decodeCache[raw] = obj;
                    return obj;
                } catch (e) {
                     return null;
                 }
                 }
                if (recent > BURST_MAX) {
                    triggerDefense();
                    return false;
                }
                return true;
             }
             }


             function noteUntrustedEvent() {
             function getThumbUrl(filename, width) {
                 if (defenseTripped || isStaffUser()) return;
                var title = "Special:Redirect/file/" + String(filename).replace(/ /g, "_");
                 untrustedStrikes++;
                var scriptPath = "";
                 if (untrustedStrikes >= UNTRUSTED_MAX) {
                try {
                    triggerDefense();
                    if (window.mw && mw.config) {
                 }
                        scriptPath = mw.config.get("wgScriptPath") || "";
                    }
                } catch (e) { /* ignore */ }
                /* Caminho relativo ao origin da página (HTTPS). mw.util.getUrl
                  pode gerar http://IP/… e o browser bloqueia Mixed Content. */
                return scriptPath + "/index.php?title=" + encodeURIComponent(title) + "&width=" + width;
            }
 
            function hydrateIcon(el) {
                 if (!el || el.getAttribute("data-ready") === "1") return;
                var raw = el.getAttribute("data-i");
                if (!raw) return;
                var p = decodePayload(raw);
                if (!p || !p.i) return;
                 var w = p.w || 32;
                var h = p.h || 32;
                el.style.width = w + "px";
                el.style.height = h + "px";
                 el.style.backgroundImage = 'url("' + getThumbUrl(p.i, w) + '")';
                el.setAttribute("data-ready", "1");
                 el.removeAttribute("data-i");
             }
             }


             function checkIntegrity() {
             function hydrateAllIcons(root) {
                 if (defenseTripped || isStaffUser()) return;
                 var scope = root || document;
                 if (nativeFetch && window.fetch !== nativeFetch) {
                 var icons = scope.querySelectorAll ? scope.querySelectorAll(".item-icon[data-i]") : [];
                    triggerDefense();
                 for (var i = 0; i < icons.length; i++) {
                    return;
                     hydrateIcon(icons[i]);
                 }
                if (XMLHttpRequest.prototype.open !== nativeXHROpen) {
                     triggerDefense();
                 }
                 }
             }
             }


             setInterval(checkIntegrity, 2500);
             function watchIconHydration() {
 
                 if (!window.MutationObserver) return;
            /* Console aberto: não desliga tooltip sozinho — só se combinar com abuso */
                 var pending = null;
            (function watchConsoleAccess() {
                 var observer = new MutationObserver(function (mutations) {
                 if (isStaffUser()) return;
                    var need = false;
                 var bait = {};
                    for (var i = 0; i < mutations.length; i++) {
                 var tripped = false;
                        var added = mutations[i].addedNodes;
                try {
                         for (var j = 0; j < added.length; j++) {
                    Object.defineProperty(bait, "id", {
                            var node = added[j];
                         get: function () {
                             if (node.nodeType !== 1) continue;
                             if (!tripped && !isStaffUser()) {
                            if (node.matches && node.matches(".item-icon[data-i]")) {
                                 tripped = true;
                                 need = true;
                                 if (tooltipFetchLog.length >= 8 || untrustedStrikes > 0) {
                                 break;
                                    triggerDefense();
                            }
                                 }
                            if (node.querySelector && node.querySelector(".item-icon[data-i]")) {
                                need = true;
                                 break;
                             }
                             }
                            return "";
                         }
                         }
                     });
                        if (need) break;
                 } catch (e) { return; }
                    }
                 setInterval(function () {
                    if (!need) return;
                     if (defenseTripped || isStaffUser()) return;
                    if (pending) clearTimeout(pending);
                     try { console.debug(bait); } catch (e2) { /* ignore */ }
                    pending = setTimeout(function () {
                 }, 4000);
                        pending = null;
            })();
                        hydrateAllIcons();
                     }, 0);
                 });
                observer.observe(document.documentElement, { childList: true, subtree: true });
            }
 
            window.glaItemHydrateIcons = hydrateAllIcons;
 
            function protectItemWrapperEvents() {
                 document.addEventListener("contextmenu", function (e) {
                     if (e.target.closest && e.target.closest(".item-wrapper")) {
                        e.preventDefault();
                     }
                }, true);
 
                document.addEventListener("dragstart", function (e) {
                    if (e.target.closest && e.target.closest(".item-wrapper")) {
                        e.preventDefault();
                    }
                 }, true);
 
                document.addEventListener("copy", function (e) {
                    if (e.target.closest && e.target.closest(".item-wrapper")) {
                        e.preventDefault();
                    }
                }, true);


            function getApiUrl() {
                document.addEventListener("selectstart", function (e) {
                if (window.mw && mw.util && mw.util.wikiScript) {
                    if (e.target.closest && e.target.closest(".item-wrapper")) {
                    return mw.util.wikiScript("api");
                        e.preventDefault();
                 }
                    }
                var path = window.location.pathname || "";
                 }, true);
                 if (path.indexOf("/index.php") !== -1) {
            }
                     return path.split("/index.php")[0] + "/api.php";
 
                 }
            if (document.readyState === "loading") {
                 return "/api.php";
                 document.addEventListener("DOMContentLoaded", function () {
                    hydrateAllIcons();
                     watchIconHydration();
                 });
            } else {
                 hydrateAllIcons();
                watchIconHydration();
             }
             }
            protectItemWrapperEvents();


             function buildTooltipInvoke(key, lang) {
             function appendText(el, text) {
                 var safeKey = String(key).replace(/\|/g, "").replace(/\}/g, "").replace(/\{/g, "");
                 if (!text) return;
                 var safeLang = String(lang || "pt-br").replace(/\|/g, "");
                 el.appendChild(document.createTextNode(text));
                return "{{#invoke:Item|tooltip|key=" + safeKey + "|lang=" + safeLang + "}}";
             }
             }


             function fetchParse(wikitext) {
             function fillTooltipBody(tip) {
                 if (window.mw && mw.Api) {
                 if (tip.querySelector(".item-tooltip-name")) return true;
                    return new mw.Api().post({
 
                        action: "parse",
                var hex = tip.getAttribute("data-tip");
                        text: wikitext,
                if (!hex) return true;
                        contentmodel: "wikitext",
 
                        disablelimitreport: true
                if (!allowDecode()) return false;
                     }).then(function (data) {
 
                        return data.parse.text["*"];
                var p = decodePayload(hex);
                     });
                if (!p) return false;
 
                var body = tip.querySelector(".item-tooltip-body");
                if (!body) return false;
 
                var nameEl = document.createElement("div");
                nameEl.className = "item-tooltip-name";
                appendText(nameEl, p.n);
                body.appendChild(nameEl);
 
                if (p.c) {
                    var catEl = document.createElement("div");
                    catEl.className = "item-tooltip-cat";
                    appendText(catEl, p.c);
                    body.appendChild(catEl);
                }
 
                var hasDetails = p.lv || p.sp || p.d || p.p1 || p.p2 || p.v;
                if (hasDetails) {
                     var sep = document.createElement("div");
                    sep.className = "item-tooltip-sep";
                    body.appendChild(sep);
                }
 
                if (p.lv) {
                    var lvEl = document.createElement("div");
                    lvEl.className = "item-tooltip-level";
                     appendText(lvEl, p.lv);
                    body.appendChild(lvEl);
                 }
                 }
                var body = new URLSearchParams();
                body.set("action", "parse");
                body.set("format", "json");
                body.set("text", wikitext);
                body.set("contentmodel", "wikitext");
                body.set("disablelimitreport", "1");
                return fetch(getApiUrl(), {
                    method: "POST",
                    credentials: "same-origin",
                    headers: { "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8" },
                    body: body.toString()
                })
                    .then(function (res) { return res.json(); })
                    .then(function (data) {
                        if (data.error) throw new Error(data.error.info || "API parse failed");
                        return data.parse.text["*"];
                    });
            }


            function fetchTooltipHtml(key, lang) {
                if (p.sp && (p.p1 || p.p2)) {
                if (defenseTripped) return Promise.reject(new Error("defense"));
                    var passWrap = document.createElement("div");
                var cacheKey = key + "\0" + (lang || "pt-br");
                    passWrap.className = "item-tooltip-passives";
                 if (tooltipHtmlCache[cacheKey]) {
                    if (p.p1) {
                     return Promise.resolve(tooltipHtmlCache[cacheKey]);
                        var row1 = document.createElement("div");
                        row1.className = "item-tooltip-passive-row";
                        var b1 = document.createElement("span");
                        b1.className = "item-tooltip-passive-badge";
                        appendText(b1, "2");
                        var t1 = document.createElement("span");
                        t1.className = "item-tooltip-passive-text";
                        appendText(t1, p.p1);
                        row1.appendChild(b1);
                        row1.appendChild(t1);
                        passWrap.appendChild(row1);
                    }
                    if (p.p2) {
                        var row2 = document.createElement("div");
                        row2.className = "item-tooltip-passive-row";
                        var b2 = document.createElement("span");
                        b2.className = "item-tooltip-passive-badge";
                        appendText(b2, "5");
                        var t2 = document.createElement("span");
                        t2.className = "item-tooltip-passive-text";
                        appendText(t2, p.p2);
                        row2.appendChild(b2);
                        row2.appendChild(t2);
                        passWrap.appendChild(row2);
                    }
                    body.appendChild(passWrap);
                 } else if (p.d) {
                     var descEl = document.createElement("div");
                    descEl.className = "item-tooltip-desc";
                    appendText(descEl, p.d);
                    body.appendChild(descEl);
                 }
                 }
                 if (!registerTooltipFetch()) {
 
                     return Promise.reject(new Error("rate"));
                 if (p.v) {
                     var footer = document.createElement("div");
                    footer.className = "item-tooltip-footer";
                    var valEl = document.createElement("span");
                    valEl.className = "item-tooltip-val";
                    appendText(valEl, p.v);
                    footer.appendChild(valEl);
                    body.appendChild(footer);
                 }
                 }
                 return fetchParse(buildTooltipInvoke(key, lang)).then(function (html) {
 
                    if (defenseTripped) return "";
                 return true;
                    tooltipHtmlCache[cacheKey] = html || "";
            }
                    return tooltipHtmlCache[cacheKey];
 
                 });
            function wipeTooltipBody(tip) {
                if (!tip || !tip.getAttribute("data-tip")) return;
                var body = tip.querySelector(".item-tooltip-body");
                 if (body) body.innerHTML = "";
             }
             }


Linha 479: Linha 597:
                 tip.style.removeProperty("--arrow-offset");
                 tip.style.removeProperty("--arrow-offset");


                tip.style.visibility = "hidden";
                 var rect = wrapper.getBoundingClientRect();
                 var rect = wrapper.getBoundingClientRect();
                 var tipH = tip.offsetHeight;
                 var tipH = tip.offsetHeight;
Linha 511: Linha 630:
                 tip.style.top = top + "px";
                 tip.style.top = top + "px";
                 tip.style.left = left + "px";
                 tip.style.left = left + "px";
            }
                 tip.style.visibility = "";
 
            function destroyTooltip() {
                 if (activeTip) {
                    activeTip.remove();
                    activeTip = null;
                }
                if (activeWrapper) {
                    activeWrapper.classList.remove("item-tooltip-loading");
                    activeWrapper = null;
                }
            }
 
            function hideTooltip() {
                tooltipGen++;
                destroyTooltip();
             }
             }


             function showTooltip(wrapper) {
             function showTooltip(wrapper) {
                if (defenseTripped) return;
                if (wrapper === activeWrapper && activeTip) return;
                 hideTooltip();
                 hideTooltip();


                 var key = wrapper.getAttribute("data-item-key");
                 var tip = wrapper.querySelector(".item-tooltip");
                 if (!key) return;
                 if (!tip) return;


                 var lang = wrapper.getAttribute("data-item-lang") || "pt-br";
                 if (tip.getAttribute("data-tip") && !fillTooltipBody(tip)) return;
                var gen = ++tooltipGen;


                 activeWrapper = wrapper;
                 activeWrapper = wrapper;
                 wrapper.classList.add("item-tooltip-loading");
                 activeTip = tip;
                document.body.appendChild(tip);
                positionTooltip(tip, wrapper);
            }


                fetchTooltipHtml(key, lang).then(function (html) {
            function hideTooltip() {
                    if (defenseTripped || gen !== tooltipGen || activeWrapper !== wrapper) return;
                if (activeTip && activeWrapper) {
 
                     activeTip.classList.remove("tooltip-visible", "tooltip-below", "tooltip-shift-right", "tooltip-shift-left");
                     wrapper.classList.remove("item-tooltip-loading");
                     activeTip.style.top = "";
                     if (!html || !html.trim()) return;
                     activeTip.style.left = "";
 
                     activeTip.style.visibility = "";
                     var tmp = document.createElement("div");
                     activeTip.style.removeProperty("--arrow-offset");
                     tmp.innerHTML = html.trim();
                     wipeTooltipBody(activeTip);
                     var tip = tmp.querySelector(".item-tooltip") || tmp.firstElementChild;
                     activeWrapper.appendChild(activeTip);
                     if (!tip) return;
                     activeTip = null;
 
                     activeWrapper = null;
                     activeTip = tip;
                 }
                    document.body.appendChild(tip);
                     positionTooltip(tip, wrapper);
                }).catch(function () {
                     if (gen === tooltipGen && activeWrapper === wrapper) {
                        wrapper.classList.remove("item-tooltip-loading");
                        activeWrapper = null;
                    }
                 });
             }
             }


             document.addEventListener("mouseover", function (e) {
             document.addEventListener("mouseover", function (e) {
                 if (e.isTrusted === false) {
                 if (e.isTrusted === false) {
                     noteUntrustedEvent();
                     untrustedStrikes++;
                     return;
                     return;
                 }
                 }
                 var wrapper = e.target.closest ? e.target.closest(".item-wrapper.item-has-tooltip") : null;
                 var wrapper = e.target.closest ? e.target.closest(".item-wrapper") : null;
                 if (wrapper && wrapper !== activeWrapper) {
                 if (wrapper && wrapper !== activeWrapper) {
                     showTooltip(wrapper);
                     showTooltip(wrapper);
Linha 578: Linha 673:


             document.addEventListener("mouseout", function (e) {
             document.addEventListener("mouseout", function (e) {
                if (e.isTrusted === false) return;
                 if (!activeWrapper) return;
                 if (!activeWrapper) return;
                 var wrapper = e.target.closest ? e.target.closest(".item-wrapper") : null;
                 var wrapper = e.target.closest ? e.target.closest(".item-wrapper") : null;

Edição atual tal como às 22h24min de 28 de maio de 2026