Mudanças entre as edições de "Widget:Item"
Ir para navegação
Ir para pesquisar
m |
m |
||
| (Uma revisão intermediária pelo mesmo usuário não está sendo mostrada) | |||
| Linha 3: | Linha 3: | ||
Tooltip: payload codificado em data-tip (Módulo:Item). Corpo vazio no HTML. | Tooltip: payload codificado em data-tip (Módulo:Item). Corpo vazio no HTML. | ||
JS decodifica no hover (rápido, sem API) e limpa no mouseout. | 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 | |||
background-image e remove data-i. Anti-arrastar/cópia/clique-direito leve. | |||
Defesa leve: hover real + rate limit de decodes para automação. | Defesa leve: hover real + rate limit de decodes para automação. | ||
--> | --> | ||
| Linha 33: | Linha 35: | ||
overflow: visible; | overflow: visible; | ||
filter: none; | 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; | ||
| Linha 40: | Linha 61: | ||
filter: none; | filter: none; | ||
box-shadow: none; | box-shadow: none; | ||
-webkit-user-select: none; | |||
user-select: none; | |||
-webkit-user-drag: none; | |||
-webkit-touch-callout: none; | |||
pointer-events: none; | |||
} | } | ||
| Linha 358: | Linha 384: | ||
} | } | ||
} | } | ||
function getThumbUrl(filename, width) { | |||
var title = "Special:Redirect/file/" + String(filename).replace(/ /g, "_"); | |||
var scriptPath = ""; | |||
try { | |||
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 hydrateAllIcons(root) { | |||
var scope = root || document; | |||
var icons = scope.querySelectorAll ? scope.querySelectorAll(".item-icon[data-i]") : []; | |||
for (var i = 0; i < icons.length; i++) { | |||
hydrateIcon(icons[i]); | |||
} | |||
} | |||
function watchIconHydration() { | |||
if (!window.MutationObserver) return; | |||
var pending = null; | |||
var observer = new MutationObserver(function (mutations) { | |||
var need = false; | |||
for (var i = 0; i < mutations.length; i++) { | |||
var added = mutations[i].addedNodes; | |||
for (var j = 0; j < added.length; j++) { | |||
var node = added[j]; | |||
if (node.nodeType !== 1) continue; | |||
if (node.matches && node.matches(".item-icon[data-i]")) { | |||
need = true; | |||
break; | |||
} | |||
if (node.querySelector && node.querySelector(".item-icon[data-i]")) { | |||
need = true; | |||
break; | |||
} | |||
} | |||
if (need) break; | |||
} | |||
if (!need) return; | |||
if (pending) clearTimeout(pending); | |||
pending = setTimeout(function () { | |||
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); | |||
document.addEventListener("selectstart", function (e) { | |||
if (e.target.closest && e.target.closest(".item-wrapper")) { | |||
e.preventDefault(); | |||
} | |||
}, true); | |||
} | |||
if (document.readyState === "loading") { | |||
document.addEventListener("DOMContentLoaded", function () { | |||
hydrateAllIcons(); | |||
watchIconHydration(); | |||
}); | |||
} else { | |||
hydrateAllIcons(); | |||
watchIconHydration(); | |||
} | |||
protectItemWrapperEvents(); | |||
function appendText(el, text) { | function appendText(el, text) { | ||