Mudanças entre as edições de "MediaWiki:Common.css"
Ir para navegação
Ir para pesquisar
m |
m (discussão removida) |
||
| Linha 1: | Linha 1: | ||
/ | <!-- | ||
Widget:TierList | |||
UI da tier list (board, pool, filtros, drag-and-drop, download). | |||
Uso: {{TierList}} (predefinição) ou | |||
{{#widget:TierList}}{{#invoke:TierList|renderAll}} | |||
Dados: textarea.gla-tierlist-data (JSON do Módulo:TierList, roster embutido). | |||
Medalhas: Arquivo:«Nome» medal.png via /images/… (fallback Especial:FilePath). | |||
--> | |||
<div class="gla-tierlist-root mw-no-invert" id="gla-tierlist-root"> | |||
<section | |||
class="gla-tierlist-toolbar" | |||
data-i18n-aria="filtersAria" | |||
aria-label="Filtros" | |||
> | |||
<input | |||
type="search" | |||
class="gla-tierlist-search" | |||
data-i18n-placeholder="searchPlaceholder" | |||
placeholder="Filtrar personagem..." | |||
autocomplete="off" | |||
spellcheck="false" | |||
/> | |||
<div class="gla-tierlist-filters" data-filters></div> | |||
</section> | |||
<section | |||
class="gla-tierlist-classbar" | |||
data-i18n-aria="classFiltersAria" | |||
aria-label="Filtros de classe" | |||
> | |||
<div class="gla-tierlist-class-filters" data-class-filters></div> | |||
</section> | |||
<section class="gla-tierlist-namebar"> | |||
<input | |||
type="text" | |||
class="gla-tierlist-list-name" | |||
data-list-name | |||
data-i18n-placeholder="listNamePlaceholder" | |||
placeholder="Nome da sua tier list..." | |||
maxlength="60" | |||
autocomplete="off" | |||
spellcheck="false" | |||
/> | |||
<div class="gla-tierlist-actions"> | |||
<button | |||
type="button" | |||
class="gla-tierlist-pill gla-tierlist-pill--btn" | |||
data-download | |||
data-i18n="download" | |||
> | |||
Baixar imagem | |||
</button> | |||
<button | |||
type="button" | |||
class="gla-tierlist-pill gla-tierlist-pill--btn" | |||
data-reset | |||
data-i18n="reset" | |||
> | |||
Limpar tier list | |||
</button> | |||
</div> | |||
</section> | |||
<section | |||
class="gla-tierlist-board" | |||
data-board | |||
aria-label="Tier list" | |||
></section> | |||
<section class="gla-tierlist-pool-wrap"> | |||
<div class="gla-tierlist-pool-head"> | |||
<h3 data-i18n="poolTitle">Não ranqueados</h3> | |||
<span class="gla-tierlist-pool-count" data-pool-count>0</span> | |||
</div> | |||
<div class="gla-tierlist-pool" data-pool></div> | |||
<div class="gla-tierlist-empty" hidden> | |||
<p data-i18n="empty">Nenhum personagem encontrado.</p> | |||
<button | |||
type="button" | |||
class="gla-tierlist-reset-filters" | |||
data-reset-filters | |||
data-i18n="resetFilters" | |||
> | |||
Limpar filtros | |||
</button> | |||
</div> | |||
</section> | |||
</div> | |||
<style> | |||
@import url("https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@500;600;700&display=swap"); | |||
/* | /* ============ Tier List: design wiki-nativo refinado ============ */ | ||
# | .gla-tierlist-root { | ||
color-scheme: light; | |||
} | --gla-ink: #0f172a; | ||
--gla-ink-2: #475569; | |||
--gla-dim: #94a3b8; | |||
--gla-paper: #eef3f7; /* painéis (header, rows, pool) */ | |||
--gla-paper-2: #ffffff; /* inputs, pills */ | |||
--gla-rule: #e2e8f0; | |||
--gla-accent: #156bc7; | |||
--gla-accent-soft: #e8f2fc; | |||
--gla-ok: #0d9488; | |||
--gla-rar-diamond: #0391c5; | |||
--gla-rar-gold: #b8860b; | |||
--gla-rar-silver: #6b7280; | |||
--gla-rar-bronze: #c56e04; | |||
/* Controles da toolbar: altura/borda/tipo únicos (filtros, pills, inputs) */ | |||
--gla-ctrl-h: 34px; | |||
--gla-ctrl-gap: 6px; | |||
--gla-ctrl-radius: 999px; | |||
--gla-ctrl-radius-field: 10px; | |||
--gla-ctrl-border-w: 1.5px; | |||
/* Borda de controle: um tom acima de --gla-rule pra nao sumir no fundo */ | |||
--gla-ctrl-border: #c5ced9; | |||
--gla-ctrl-pad-x: 12px; | |||
--gla-ctrl-fs: 13px; | |||
--gla-ctrl-fw: 500; | |||
--gla-ctrl-lh: 1; | |||
--gla-ctrl-font: "Noto Sans", "Segoe UI", sans-serif; | |||
font-family: "Noto Sans", sans-serif; | |||
color: var(--gla-ink); | |||
margin: 0 0 1.5rem; | |||
} | |||
.gla-tierlist-root *, | |||
.gla-tierlist-root *::before, | |||
} | .gla-tierlist-root *::after { | ||
box-sizing: border-box; | |||
} | |||
# | html.client-darkmode .gla-tierlist-root, | ||
html.skin-theme-clientpref-night .gla-tierlist-root { | |||
color-scheme: dark; | |||
--gla-ink: #e8eef5; | |||
} | --gla-ink-2: #9aa6b5; | ||
--gla-dim: #6b7788; | |||
--gla-paper: #171c26; | |||
--gla-paper-2: #1e2430; | |||
--gla-rule: #3a4252; | |||
--gla-ctrl-border: #5c667a; | |||
--gla-accent: #5ba3e0; | |||
--gla-accent-soft: rgba(91, 163, 224, 0.18); | |||
--gla-ok: #2dd4bf; | |||
--gla-rar-diamond: #4cc4ea; | |||
--gla-rar-gold: #e8b53a; | |||
--gla-rar-silver: #9aa6b5; | |||
--gla-rar-bronze: #f18a05; | |||
} | |||
/* DarkMode da wiki invert+hue-rotate no documento; mw-no-invert no root | |||
devolve as cores reais. Imagens precisam de filter:none pra nao inverter 2x. */ | |||
html.client-darkmode .gla-tierlist-root.mw-no-invert img, | |||
} | html.skin-theme-clientpref-night .gla-tierlist-root.mw-no-invert img, | ||
html.client-darkmode .gla-tierlist-root.mw-no-invert video, | |||
html.skin-theme-clientpref-night .gla-tierlist-root.mw-no-invert video { | |||
filter: none; | |||
-webkit-filter: none; | |||
} | |||
/* --- | /* Pills de ação: mesmo sistema visual dos filtros de raridade. | ||
#content.mw-body … sobrescreve o skin da wiki em <button> | |||
(padding/line-height/display), que empurra o texto pra cima. */ | |||
} | .gla-tierlist-pill, | ||
#content.mw-body .gla-tierlist-root .gla-tierlist-pill { | |||
display: inline-flex; | |||
align-items: center; | |||
justify-content: center; | |||
box-sizing: border-box; | |||
margin: 0; | |||
min-height: var(--gla-ctrl-h); | |||
height: var(--gla-ctrl-h); | |||
padding: 0 var(--gla-ctrl-pad-x); | |||
border-radius: var(--gla-ctrl-radius); | |||
border: var(--gla-ctrl-border-w) solid var(--gla-ctrl-border); | |||
background: var(--gla-paper-2); | |||
color: var(--gla-ink-2); | |||
font-family: var(--gla-ctrl-font); | |||
font-size: var(--gla-ctrl-fs); | |||
font-weight: var(--gla-ctrl-fw); | |||
letter-spacing: 0; | |||
line-height: var(--gla-ctrl-lh); | |||
white-space: nowrap; | |||
vertical-align: middle; | |||
} | |||
.gla-tierlist-actions { | |||
display: flex; | |||
flex-wrap: wrap; | |||
align-items: center; | |||
gap: var(--gla-ctrl-gap); | |||
flex: 0 0 auto; | |||
} | justify-content: center; | ||
} | |||
# | .gla-tierlist-pill--btn, | ||
#content.mw-body .gla-tierlist-root .gla-tierlist-pill--btn { | |||
cursor: pointer; | |||
appearance: none; | |||
-webkit-appearance: none; | |||
margin: 0; | |||
} | display: inline-flex; | ||
align-items: center; | |||
justify-content: center; | |||
box-sizing: border-box; | |||
min-height: var(--gla-ctrl-h); | |||
height: var(--gla-ctrl-h); | |||
padding: 0 var(--gla-ctrl-pad-x); | |||
border: var(--gla-ctrl-border-w) solid var(--gla-ctrl-border); | |||
border-radius: var(--gla-ctrl-radius); | |||
background: var(--gla-paper-2); | |||
color: var(--gla-ink-2); | |||
font-family: var(--gla-ctrl-font); | |||
font-size: var(--gla-ctrl-fs); | |||
font-weight: var(--gla-ctrl-fw); | |||
line-height: var(--gla-ctrl-lh); | |||
transition: | |||
background 0.15s, | |||
border-color 0.15s, | |||
color 0.15s, | |||
opacity 0.15s; | |||
flex: 0 0 auto; | |||
} | |||
.gla-tierlist-pill--btn::-moz-focus-inner { | |||
border: 0; | |||
padding: 0; | |||
} | |||
} | |||
.gla-tierlist-pill--btn:hover, | |||
#content.mw-body .gla-tierlist-root .gla-tierlist-pill--btn:hover { | |||
border-color: var(--gla-accent); | |||
color: var(--gla-accent); | |||
} | |||
} | |||
.gla-tierlist-pill--btn:disabled { | |||
opacity: 0.55; | |||
} | cursor: wait; | ||
} | |||
/* ---------- Toolbar ---------- */ | |||
.gla-tierlist-toolbar { | |||
display: flex; | |||
flex-wrap: wrap; | |||
gap: var(--gla-ctrl-gap) 10px; | |||
align-items: center; | |||
} | margin-bottom: 0.55rem; | ||
} | |||
.gla-tierlist-search { | |||
flex: 1 1 14rem; | |||
} | min-width: 11rem; | ||
box-sizing: border-box; | |||
height: var(--gla-ctrl-h); | |||
min-height: var(--gla-ctrl-h); | |||
padding: 0 14px; | |||
border: var(--gla-ctrl-border-w) solid var(--gla-ctrl-border); | |||
border-radius: var(--gla-ctrl-radius-field); | |||
font-family: var(--gla-ctrl-font); | |||
font-size: var(--gla-ctrl-fs); | |||
font-weight: var(--gla-ctrl-fw); | |||
line-height: var(--gla-ctrl-lh); | |||
background: var(--gla-paper-2); | |||
color: var(--gla-ink); | |||
transition: | |||
border-color 0.15s, | |||
box-shadow 0.15s; | |||
} | |||
.gla-tierlist-search::placeholder { | |||
color: var(--gla-dim); | |||
font-weight: var(--gla-ctrl-fw); | |||
} | } | ||
.gla-tierlist-search:focus { | |||
outline: none; | |||
border-color: var(--gla-accent); | |||
box-shadow: 0 0 0 3px var(--gla-accent-soft); | |||
} | } | ||
.gla-tierlist-filters { | |||
display: flex; | |||
flex-wrap: wrap; | |||
} | align-items: center; | ||
gap: var(--gla-ctrl-gap); | |||
} | |||
# | .gla-tierlist-filter, | ||
#content.mw-body .gla-tierlist-root .gla-tierlist-filter { | |||
cursor: pointer; | |||
appearance: none; | |||
-webkit-appearance: none; | |||
margin: 0; | |||
} | display: inline-flex; | ||
align-items: center; | |||
justify-content: center; | |||
gap: 6px; | |||
box-sizing: border-box; | |||
min-height: var(--gla-ctrl-h); | |||
height: var(--gla-ctrl-h); | |||
padding: 0 var(--gla-ctrl-pad-x); | |||
border: var(--gla-ctrl-border-w) solid var(--gla-ctrl-border); | |||
border-radius: var(--gla-ctrl-radius); | |||
background: var(--gla-paper-2); | |||
color: var(--gla-ink-2); | |||
font-family: var(--gla-ctrl-font); | |||
font-size: var(--gla-ctrl-fs); | |||
font-weight: var(--gla-ctrl-fw); | |||
letter-spacing: 0; | |||
line-height: var(--gla-ctrl-lh); | |||
white-space: nowrap; | |||
vertical-align: middle; | |||
transition: | |||
background 0.15s, | |||
border-color 0.15s, | |||
color 0.15s; | |||
} | |||
.gla-tierlist-filter::-moz-focus-inner { | |||
border: 0; | |||
padding: 0; | |||
} | |||
} | |||
.gla-tierlist-filter:hover, | |||
#content.mw-body .gla-tierlist-root .gla-tierlist-filter:hover { | |||
border-color: var(--gla-accent); | |||
} | |||
} | |||
.gla-tierlist-filter-label { | |||
font-family: inherit; | |||
font-size: inherit; | |||
font-weight: inherit; | |||
} | line-height: 1; | ||
color: inherit; | |||
} | |||
.gla-tierlist-filter-dot { | |||
width: 8px; | |||
height: 8px; | |||
border-radius: 50%; | |||
flex-shrink: 0; | |||
background: var(--gla-dim); | |||
} | |||
} | |||
.gla-tierlist-filter[data-tier="diamond"] .gla-tierlist-filter-dot { | |||
background: var(--gla-rar-diamond); | |||
} | |||
.gla-tierlist-filter[data-tier="gold"] .gla-tierlist-filter-dot { | |||
background: var(--gla-rar-gold); | |||
} | |||
.gla-tierlist-filter[data-tier="silver"] .gla-tierlist-filter-dot { | |||
background: var(--gla-rar-silver); | |||
} | |||
.gla-tierlist-filter[data-tier="bronze"] .gla-tierlist-filter-dot { | |||
background: var(--gla-rar-bronze); | |||
} | |||
} | |||
# | .gla-tierlist-filter.is-active, | ||
#content.mw-body .gla-tierlist-root .gla-tierlist-filter.is-active { | |||
} | border-color: var(--gla-accent); | ||
background: var(--gla-accent-soft); | |||
color: var(--gla-accent); | |||
} | |||
# | .gla-tierlist-filter[data-tier="diamond"].is-active, | ||
#content.mw-body | |||
} | .gla-tierlist-root | ||
.gla-tierlist-filter[data-tier="diamond"].is-active { | |||
border-color: var(--gla-rar-diamond); | |||
color: var(--gla-rar-diamond); | |||
background: rgba(3, 145, 197, 0.14); | |||
} | |||
.gla-tierlist-filter[data-tier="gold"].is-active, | |||
#content.mw-body | |||
.gla-tierlist-root | |||
.gla-tierlist-filter[data-tier="gold"].is-active { | |||
border-color: var(--gla-rar-gold); | |||
color: var(--gla-rar-gold); | |||
background: rgba(212, 160, 23, 0.14); | |||
} | |||
.gla-tierlist-filter[data-tier="silver"].is-active, | |||
#content.mw-body | |||
.gla-tierlist-root | |||
.gla-tierlist-filter[data-tier="silver"].is-active { | |||
border-color: var(--gla-rar-silver); | |||
color: var(--gla-rar-silver); | |||
background: rgba(140, 140, 140, 0.14); | |||
} | |||
.gla-tierlist-filter[data-tier="bronze"].is-active, | |||
#content.mw-body | |||
.gla-tierlist-root | |||
.gla-tierlist-filter[data-tier="bronze"].is-active { | |||
border-color: var(--gla-rar-bronze); | |||
color: var(--gla-rar-bronze); | |||
background: rgba(241, 138, 5, 0.14); | |||
} | |||
/* ---------- Class filters ---------- */ | |||
.gla-tierlist-classbar { | |||
margin: 0 0 0.55rem; | |||
} | } | ||
.gla-tierlist-namebar { | |||
margin: 0 0 0.85rem; | |||
} | display: flex; | ||
flex-wrap: wrap; | |||
align-items: center; | |||
justify-content: center; | |||
gap: var(--gla-ctrl-gap) 10px; | |||
} | |||
.gla-tierlist-list-name { | |||
flex: 1 1 14rem; | |||
width: auto; | |||
} | max-width: 22rem; | ||
min-width: 11rem; | |||
box-sizing: border-box; | |||
height: var(--gla-ctrl-h); | |||
min-height: var(--gla-ctrl-h); | |||
padding: 0 14px; | |||
border: var(--gla-ctrl-border-w) solid var(--gla-ctrl-border); | |||
border-radius: var(--gla-ctrl-radius-field); | |||
font-family: var(--gla-ctrl-font); | |||
font-size: var(--gla-ctrl-fs); | |||
font-weight: var(--gla-ctrl-fw); | |||
line-height: var(--gla-ctrl-lh); | |||
text-align: center; | |||
background: var(--gla-paper-2); | |||
color: var(--gla-ink); | |||
transition: | |||
border-color 0.15s, | |||
box-shadow 0.15s; | |||
} | |||
.gla-tierlist-list-name::placeholder { | |||
color: var(--gla-dim); | |||
font-weight: var(--gla-ctrl-fw); | |||
} | |||
} | |||
. | .gla-tierlist-list-name:focus { | ||
outline: none; | |||
border-color: var(--gla-accent); | |||
box-shadow: 0 0 0 3px var(--gla-accent-soft); | |||
} | |||
} | |||
. | /* Chips de classe no idioma visual da Dima (.gla-dima-filter). */ | ||
. | .gla-tierlist-class-filters { | ||
display: flex; | |||
} | flex-wrap: wrap; | ||
justify-content: center; | |||
gap: 6px; | |||
min-width: 0; | |||
width: 100%; | |||
} | |||
. | .gla-tierlist-class-filter, | ||
#content.mw-body .gla-tierlist-root .gla-tierlist-class-filter { | |||
display: inline-flex; | |||
} | align-items: center; | ||
gap: 6px; | |||
flex: 0 0 auto; | |||
margin: 0; | |||
padding: 4px 12px 4px 6px; | |||
border-radius: 999px; | |||
border: var(--gla-ctrl-border-w) solid var(--gla-ctrl-border); | |||
background: var(--gla-paper-2); | |||
cursor: pointer; | |||
appearance: none; | |||
-webkit-appearance: none; | |||
box-sizing: border-box; | |||
font-family: "Noto Sans", "Segoe UI", sans-serif; | |||
font-size: 13px; | |||
font-weight: 500; | |||
letter-spacing: 0; | |||
color: var(--gla-ink-2); | |||
line-height: 1; | |||
transition: | |||
border-color 0.15s, | |||
background 0.15s, | |||
color 0.15s; | |||
} | |||
. | .gla-tierlist-class-filter::-moz-focus-inner { | ||
border: 0; | |||
padding: 0; | |||
} | |||
} | |||
. | .gla-tierlist-class-filter:hover, | ||
. | #content.mw-body .gla-tierlist-root .gla-tierlist-class-filter:hover { | ||
border-color: var(--gla-accent); | |||
} | |||
} | |||
.gla-tierlist-class-filter img { | |||
width: 32px; | |||
height: 32px; | |||
flex-shrink: 0; | |||
object-fit: contain; | |||
opacity: 0.55; | |||
transition: opacity 0.15s; | |||
filter: none; | |||
-webkit-filter: none; | |||
} | |||
.gla-tierlist-class-filter img.is-missing { | |||
display: none; | |||
} | |||
} | |||
.gla-tierlist-class-filter--text-only { | |||
padding-left: 12px; | |||
padding-right: 12px; | |||
} | } | ||
.gla-tierlist-class-filter span { | |||
font-family: "Noto Sans", "Segoe UI", sans-serif; | |||
font-size: 13px; | |||
} | font-weight: 500; | ||
color: inherit; | |||
white-space: nowrap; | |||
} | |||
.gla-tierlist-class-filter.is-active, | |||
#content.mw-body .gla-tierlist-root .gla-tierlist-class-filter.is-active { | |||
content | border-color: var(--gla-accent); | ||
background: var(--gla-accent-soft); | |||
color: var(--gla-accent); | |||
} | |||
} | |||
.gla-tierlist-class-filter.is-active img { | |||
opacity: 1; | |||
} | |||
} | |||
/* | /* ---------- Board ---------- */ | ||
.gla-tierlist-board { | |||
display: flex; | |||
flex-direction: column; | |||
gap: 0.5rem; | |||
margin-bottom: 0.85rem; | |||
overflow: visible; | |||
} | |||
} | |||
.gla-tierlist-row { | |||
display: grid; | |||
grid-template-columns: 4rem 1fr; | |||
min-height: 6.5rem; | |||
} | border: 1px solid var(--gla-rule); | ||
border-radius: 12px; | |||
/* visible: overflow:hidden clipava o tooltip do nome acima da medalha */ | |||
overflow: visible; | |||
background: var(--gla-paper); | |||
} | |||
.gla-tierlist-row.is-drop-target { | |||
outline: 2px solid var(--gla-accent); | |||
outline-offset: 1px; | |||
} | } | ||
.gla-tierlist-label { | |||
display: flex; | |||
align-items: center; | |||
} | justify-content: center; | ||
font-family: "Space Grotesk", sans-serif; | |||
font-weight: 700; | |||
font-size: 1.4rem; | |||
letter-spacing: 0.04em; | |||
color: #111827; | |||
user-select: none; | |||
cursor: pointer; | |||
/* cantos arredondados no label (row não usa mais overflow:hidden) */ | |||
border-radius: 11px 0 0 11px; | |||
} | |||
# | .gla-tierlist-row[data-rank="S"] .gla-tierlist-label { | ||
background- | background: #ff7f7f; | ||
} | } | ||
.gla-tierlist-row[data-rank="A"] .gla-tierlist-label { | |||
background: #ffbf7f; | |||
} | |||
.gla-tierlist-row[data-rank="B"] .gla-tierlist-label { | |||
background: #ffdf7f; | |||
} | |||
.gla-tierlist-row[data-rank="C"] .gla-tierlist-label { | |||
background: #ffff7f; | |||
} | |||
.gla-tierlist-row[data-rank="D"] .gla-tierlist-label { | |||
background: #bfff7f; | |||
} | |||
.gla-tierlist-slots { | |||
display: flex; | |||
} | flex-wrap: wrap; | ||
gap: 0.25rem; | |||
align-content: flex-start; | |||
padding: 0.5rem; | |||
min-height: 6.5rem; | |||
overflow: visible; | |||
} | |||
/* ---------- Pool ---------- */ | |||
.gla-tierlist-pool-wrap { | |||
background: var(--gla-paper); | |||
} | border: 1px solid var(--gla-rule); | ||
border-radius: 14px; | |||
padding: 0.9rem 1rem 1rem; | |||
overflow: visible; | |||
} | |||
.gla-tierlist-pool-head { | |||
display: flex; | |||
} | align-items: baseline; | ||
gap: 0.65rem; | |||
margin-bottom: 0.65rem; | |||
} | |||
.gla-tierlist-pool-head h3 { | |||
margin: 0; | |||
font-family: "Space Grotesk", sans-serif; | |||
font-size: 0.95rem; | |||
font-weight: 700; | |||
} | |||
} | |||
.gla-tierlist-pool-count { | |||
color: var(--gla-dim); | |||
} | font-size: 0.8rem; | ||
font-weight: 600; | |||
} | |||
.gla-tierlist-pool { | |||
display: flex; | |||
} | flex-wrap: wrap; | ||
justify-content: center; | |||
gap: 0.25rem; | |||
min-height: 4.5rem; | |||
overflow: visible; | |||
} | |||
.gla-tierlist-pool.is-drop-target { | |||
outline: 2px dashed var(--gla-accent); | |||
outline-offset: 2px; | |||
border-radius: 10px; | |||
} | |||
} | |||
/* | /* Pool: medalhas um pouco mais suaves que as da board (modo claro incomoda). */ | ||
.gla-tierlist-pool .gla-tierlist-char-art img { | |||
opacity: 0.9; | |||
filter: saturate(0.88) brightness(0.98); | |||
transition: | |||
opacity 0.15s ease, | |||
filter 0.15s ease; | |||
} | } | ||
.gla-tierlist-pool .gla-tierlist-char:hover .gla-tierlist-char-art img, | |||
.gla-tierlist-pool | |||
} | .gla-tierlist-char:focus-visible | ||
.gla-tierlist-char-art | |||
img, | |||
.gla-tierlist-pool .gla-tierlist-char.is-selected .gla-tierlist-char-art img { | |||
opacity: 1; | |||
filter: none; | |||
} | |||
/* ---------- Character card ---------- */ | |||
.gla-tierlist-char { | |||
position: relative; | |||
flex: 0 0 104px; | |||
width: 104px; | |||
} | min-width: 104px; | ||
cursor: grab; | |||
border: 0; | |||
background: transparent; | |||
padding: 4px; | |||
display: flex; | |||
flex-direction: column; | |||
align-items: center; | |||
gap: 0; | |||
transition: | |||
transform 0.12s ease, | |||
opacity 0.15s; | |||
touch-action: none; | |||
user-select: none; | |||
-webkit-user-select: none; | |||
/* sem content-visibility: auto — paint containment clipava o tooltip fora do box */ | |||
overflow: visible; | |||
z-index: 0; | |||
} | |||
.gla-tierlist-char:active { | |||
cursor: grabbing; | |||
} | |||
} | |||
.gla-tierlist-char:focus, | |||
.gla-tierlist-char:focus-visible, | |||
.gla-tierlist-char:focus-within, | |||
.gla-tierlist-char.is-selected, | |||
.gla-tierlist-char.is-selected .gla-tierlist-char-art, | |||
} | .gla-tierlist-char.is-selected .gla-tierlist-char-art img { | ||
outline: none !important; | |||
outline-offset: 0 !important; | |||
box-shadow: none !important; | |||
} | |||
/* Sem scale/filter no hover: Chromium re-rasteriza a medalha e perde nitidez. */ | |||
.gla-tierlist-char:hover, | |||
} | .gla-tierlist-char:focus-visible, | ||
.gla-tierlist-char.is-selected { | |||
transform: translate3d(0, -2px, 0); | |||
z-index: 30; | |||
} | |||
.gla-tierlist-char.is-hidden { | |||
display: none; | |||
} | } | ||
.gla-tierlist-char.is-dragging { | |||
opacity: 0.45; | |||
z-index: 40; | |||
} | } | ||
.gla-tierlist-char-art { | |||
width: 96px; | |||
height: 96px; | |||
background: transparent; | |||
overflow: visible; | |||
line-height: 0; | |||
flex-shrink: 0; | |||
} | } | ||
.gla-tierlist-char-art img { | |||
width: 100%; | |||
height: 100%; | |||
object-fit: contain; | |||
display: block; | |||
background: transparent; | |||
pointer-events: none; | |||
transform: translateZ(0); | |||
backface-visibility: hidden; | |||
-webkit-backface-visibility: hidden; | |||
} | } | ||
/* Nome só no tooltip (hover/foco/seleção). Texto sempre preto; pontinha pro medal. */ | |||
.gla-tierlist-char-name { | |||
position: absolute; | |||
left: 50%; | |||
bottom: calc(100% + 6px); | |||
transform: translateX(-50%) translateY(4px); | |||
margin: 0; | |||
} | padding: 0.45rem 0.8rem; | ||
max-width: 16rem; | |||
border-radius: 10px; | |||
border: 1px solid var(--gla-rule); | |||
background: #ffffff; | |||
box-shadow: 0 8px 22px rgba(15, 23, 42, 0.18); | |||
font-family: "Noto Sans", "Segoe UI", sans-serif; | |||
font-size: 0.9375rem; | |||
font-weight: 700; | |||
letter-spacing: 0; | |||
line-height: 1.25; | |||
text-align: center; | |||
color: #0f172a !important; | |||
white-space: nowrap; | |||
overflow: visible; | |||
text-overflow: ellipsis; | |||
opacity: 0; | |||
visibility: hidden; | |||
pointer-events: none; | |||
transition: | |||
opacity 0.12s ease, | |||
transform 0.12s ease, | |||
visibility 0.12s; | |||
z-index: 50; | |||
} | |||
.gla-tierlist-char-name::after { | |||
content: ""; | |||
position: absolute; | |||
left: 50%; | |||
top: 100%; | |||
width: 10px; | |||
height: 10px; | |||
} | margin-left: -5px; | ||
margin-top: -5px; | |||
background: #ffffff; | |||
border-right: 1px solid var(--gla-rule); | |||
border-bottom: 1px solid var(--gla-rule); | |||
transform: rotate(45deg); | |||
box-shadow: 2px 2px 4px rgba(15, 23, 42, 0.06); | |||
} | |||
html.client-darkmode .gla-tierlist-char-name, | |||
html.skin-theme-clientpref-night .gla-tierlist-char-name { | |||
background: #1e2430; | |||
color: #e8eef5 !important; | |||
border-color: #3a4252; | |||
} | |||
} | |||
# | html.client-darkmode .gla-tierlist-char-name::after, | ||
html.skin-theme-clientpref-night .gla-tierlist-char-name::after { | |||
} | background: #1e2430; | ||
border-right-color: #3a4252; | |||
border-bottom-color: #3a4252; | |||
} | |||
.gla-tierlist-char:hover .gla-tierlist-char-name, | |||
.gla-tierlist-char:focus-visible .gla-tierlist-char-name, | |||
.gla-tierlist-char.is-selected .gla-tierlist-char-name { | |||
opacity: 1; | |||
visibility: visible; | |||
} | transform: translateX(-50%) translateY(0); | ||
} | |||
.gla-tierlist-char.is-dragging .gla-tierlist-char-name { | |||
opacity: 0; | |||
} | visibility: hidden; | ||
} | |||
/* | /* ---------- Empty state ---------- */ | ||
.gla-tierlist-empty { | |||
text-align: center; | |||
padding: 1.5rem 0.5rem 0.5rem; | |||
color: var(--gla-ink-2); | |||
} | |||
. | .gla-tierlist-empty p { | ||
margin: 0 0 0.65rem; | |||
} | |||
} | |||
. | .gla-tierlist-reset-filters { | ||
cursor: pointer; | |||
border: 1px solid var(--gla-rule); | |||
background: var(--gla-paper-2); | |||
color: var(--gla-accent); | |||
} | font-weight: 600; | ||
font-family: inherit; | |||
padding: 0.4rem 0.85rem; | |||
border-radius: 8px; | |||
} | |||
. | .gla-tierlist-reset-filters:hover { | ||
background: var(--gla-accent-soft); | |||
border-color: var(--gla-accent); | |||
} | |||
} | |||
/* ---------- Responsivo ---------- */ | |||
@media (max-width: 640px) { | |||
. | .gla-tierlist-pill--btn { | ||
margin-left: 0; | |||
} | |||
} | |||
. | /* Igual Dima: só ícone no mobile; title/aria no botão mantém o rótulo. */ | ||
. | .gla-tierlist-class-filter span { | ||
display: none; | |||
} | |||
} | |||
. | .gla-tierlist-class-filter { | ||
padding: 4px; | |||
} | |||
} | |||
. | .gla-tierlist-row { | ||
grid-template-columns: 3rem 1fr; | |||
} | |||
} | |||
. | .gla-tierlist-label { | ||
font-size: 1.05rem; | |||
} | |||
} | |||
. | .gla-tierlist-char { | ||
flex: 0 0 88px; | |||
width: 88px; | |||
min-width: 88px; | |||
} | |||
} | |||
.gla-tierlist-char-art { | |||
. | width: 80px; | ||
height: 80px; | |||
} | |||
} | |||
. | .gla-tierlist-char-name { | ||
font-size: 0.875rem; | |||
max-width: 13rem; | |||
padding: 0.4rem 0.7rem; | |||
} | |||
} | |||
</style> | |||
} | |||
} | |||
/ | |||
<script> | |||
(function () { | |||
function boot() { | |||
var root = document.querySelector(".gla-tierlist-root"); | |||
if (!root || root.__glaTierListInit) return; | |||
root.__glaTierListInit = true; | |||
var STORAGE_KEY = "gla-tierlist"; | |||
. | var STORAGE_NAME_KEY = "gla-tierlist-name"; | ||
. | var ONLINE_WIKI = "https://wiki.gla.com.br"; | ||
var lang = "pt"; | |||
var t = null; | |||
var RANKS = ["S", "A", "B", "C", "D"]; | |||
var GAME_TIERS = [ | |||
{ id: "all", labelKey: "tierAll" }, | |||
{ id: "diamond", labelKey: "tierDiamond" }, | |||
} | { id: "gold", labelKey: "tierGold" }, | ||
{ id: "silver", labelKey: "tierSilver" }, | |||
{ id: "bronze", labelKey: "tierBronze" }, | |||
]; | |||
var RARITY_ORDER = { diamond: 0, gold: 1, silver: 2, bronze: 3 }; | |||
. | /* IDs de filters no Módulo:TierList (legenda no topo do .lua). */ | ||
. | var CLASS_FILTERS = [ | ||
{ id: "tank", icon: "tank.png" }, | |||
{ id: "dps", icon: "dps.png" }, | |||
{ id: "support", icon: "support.png" }, | |||
{ id: "fighter", icon: "fighter.png" }, | |||
{ id: "shooter", icon: "shooter.png" }, | |||
{ id: "slasher", icon: "slasher.png" }, | |||
{ id: "especialist", icon: "especialist.png" }, | |||
]; | |||
var CLASS_LABELS = { | |||
pt: { | |||
tank: "Tanque", | |||
} | dps: "DPS", | ||
support: "Suporte", | |||
fighter: "Lutador", | |||
shooter: "Atirador", | |||
slasher: "Cortante", | |||
especialist: "Especialista", | |||
}, | |||
en: { | |||
tank: "Tank", | |||
dps: "DPS", | |||
support: "Support", | |||
fighter: "Fighter", | |||
shooter: "Shooter", | |||
slasher: "Slasher", | |||
especialist: "Specialist", | |||
}, | |||
}; | |||
. | var STRINGS = { | ||
. | pt: { | ||
title: "Tier List", | |||
} | reset: "Limpar tier list", | ||
download: "Baixar imagem", | |||
downloadBusy: "Gerando…", | |||
downloadError: "Não foi possível gerar a imagem. Tente de novo.", | |||
listNamePlaceholder: "Nome da sua tier list...", | |||
ranked0: "0 ranqueados", | |||
ranked1: "1 ranqueado", | |||
rankedN: "{n} ranqueados", | |||
searchPlaceholder: "Filtrar personagem...", | |||
filtersAria: "Filtros", | |||
classFiltersAria: "Filtros de classe", | |||
poolTitle: "Não ranqueados", | |||
empty: "Nenhum personagem encontrado.", | |||
resetFilters: "Limpar filtros", | |||
tierAll: "Todos", | |||
tierDiamond: "Diamante", | |||
tierGold: "Ouro", | |||
tierSilver: "Prata", | |||
tierBronze: "Bronze", | |||
poolCount: "{n}", | |||
}, | |||
en: { | |||
title: "Tier List", | |||
reset: "Clear tier list", | |||
download: "Download image", | |||
downloadBusy: "Generating…", | |||
downloadError: "Could not generate the image. Please try again.", | |||
listNamePlaceholder: "Name your tier list...", | |||
ranked0: "0 ranked", | |||
ranked1: "1 ranked", | |||
rankedN: "{n} ranked", | |||
searchPlaceholder: "Filter character...", | |||
filtersAria: "Filters", | |||
classFiltersAria: "Class filters", | |||
poolTitle: "Unranked", | |||
empty: "No characters found.", | |||
resetFilters: "Clear filters", | |||
tierAll: "All", | |||
tierDiamond: "Diamond", | |||
tierGold: "Gold", | |||
tierSilver: "Silver", | |||
tierBronze: "Bronze", | |||
poolCount: "{n}", | |||
}, | |||
}; | |||
function getWidgetLang() { | |||
if ( | |||
window.glaLang && | |||
typeof window.glaLang.getWidgetLang === "function" | |||
) { | |||
return window.glaLang.getWidgetLang() === "en" ? "en" : "pt"; | |||
} | |||
var attr = ( | |||
document.documentElement.getAttribute("data-gla-lang") || "" | |||
).toLowerCase(); | |||
if (attr === "en") return "en"; | |||
try { | |||
var saved = (localStorage.getItem("glaLang") || "").toLowerCase(); | |||
if (saved.indexOf("en") === 0) return "en"; | |||
} catch (e) {} | |||
return "pt"; | |||
} | |||
var boardEl = root.querySelector("[data-board]"); | |||
var poolEl = root.querySelector("[data-pool]"); | |||
var filtersEl = root.querySelector("[data-filters]"); | |||
var classFiltersEl = root.querySelector("[data-class-filters]"); | |||
var classBarEl = root.querySelector(".gla-tierlist-classbar"); | |||
var emptyEl = root.querySelector(".gla-tierlist-empty"); | |||
var countEl = root.querySelector("[data-count]"); | |||
var poolCountEl = root.querySelector("[data-pool-count]"); | |||
var searchInput = root.querySelector(".gla-tierlist-search"); | |||
var resetBtn = root.querySelector("[data-reset]"); | |||
var downloadBtn = root.querySelector("[data-download]"); | |||
var listNameInput = root.querySelector("[data-list-name]"); | |||
var resetFiltersBtn = root.querySelector("[data-reset-filters]"); | |||
var characters = []; | |||
var placement = {}; | |||
var tierOrder = { S: [], A: [], B: [], C: [], D: [] }; | |||
var charNodes = {}; | |||
var rowSlots = {}; | |||
var activeGameTier = "all"; | |||
var activeClassFilters = []; | |||
var searchTerm = ""; | |||
var searchTimeout = null; | |||
var selectedId = null; | |||
var dragId = null; | |||
var imgObserver = null; | |||
if ("IntersectionObserver" in window) { | |||
imgObserver = new IntersectionObserver( | |||
function (entries) { | |||
entries.forEach(function (entry) { | |||
} | if (!entry.isIntersecting) return; | ||
var img = entry.target; | |||
var src = img.getAttribute("data-src"); | |||
if (src) { | |||
img.src = src; | |||
img.removeAttribute("data-src"); | |||
} | |||
imgObserver.unobserve(img); | |||
}); | |||
}, | |||
{ rootMargin: "200px 0px", threshold: 0.01 }, | |||
); | |||
} | |||
function normalize(s) { | |||
return (s || "") | |||
.toLowerCase() | |||
.normalize("NFD") | |||
.replace(/[\u0300-\u036f]/g, ""); | |||
} | |||
function readCharacterList() { | |||
} | var source = document.querySelector(".gla-tierlist-source"); | ||
if (!source) return []; | |||
var dataEl = source.querySelector(".gla-tierlist-data"); | |||
var raw = dataEl | |||
? dataEl.value || dataEl.textContent || "" | |||
: source.textContent; | |||
raw = (raw || "").trim(); | |||
if (!raw) return []; | |||
try { | |||
return JSON.parse(raw); | |||
} catch (e1) { | |||
var start = raw.indexOf("["); | |||
var end = raw.lastIndexOf("]"); | |||
if (start === -1 || end === -1) return []; | |||
try { | |||
return JSON.parse(raw.slice(start, end + 1)); | |||
} catch (e2) { | |||
console.error("[TierList] JSON invalido", e2); | |||
return []; | |||
} | |||
} | |||
} | |||
. | function cleanFileName(fileName) { | ||
var clean = String(fileName || "").replace(/^Arquivo:|^File:/i, ""); | |||
} | try { | ||
clean = decodeURIComponent(clean); | |||
} catch (e) {} | |||
return clean.replace(/ /g, "_"); | |||
} | |||
. | /* Path direto /images/a/ab/File.png (MD5 do nome) — evita thumb.php (quebrado) e redirect do FilePath. */ | ||
function md5hex(string) { | |||
} | function add(x, y) { | ||
var l = (x & 0xffff) + (y & 0xffff); | |||
var m = (x >> 16) + (y >> 16) + (l >> 16); | |||
return (m << 16) | (l & 0xffff); | |||
} | |||
function rol(n, c) { | |||
return (n << c) | (n >>> (32 - c)); | |||
} | |||
function cmn(q, a, b, x, s, t) { | |||
return add(rol(add(add(a, q), add(x, t)), s), b); | |||
} | |||
function ff(a, b, c, d, x, s, t) { | |||
return cmn((b & c) | (~b & d), a, b, x, s, t); | |||
} | |||
function gg(a, b, c, d, x, s, t) { | |||
return cmn((b & d) | (c & ~d), a, b, x, s, t); | |||
} | |||
function hh(a, b, c, d, x, s, t) { | |||
return cmn(b ^ c ^ d, a, b, x, s, t); | |||
} | |||
function ii(a, b, c, d, x, s, t) { | |||
return cmn(c ^ (b | ~d), a, b, x, s, t); | |||
} | |||
var raw = unescape(encodeURIComponent(string)); | |||
var x = [], | |||
i; | |||
for (i = 0; i < raw.length * 8; i += 8) | |||
x[i >> 5] |= (raw.charCodeAt(i / 8) & 255) << (i % 32); | |||
x[(raw.length * 8) >> 5] |= 128 << ((raw.length * 8) % 32); | |||
x[(((raw.length + 64) >>> 9) << 4) + 14] = raw.length * 8; | |||
var a = 1732584193, | |||
b = -271733879, | |||
c = -1732584194, | |||
d = 271733878; | |||
for (i = 0; i < x.length; i += 16) { | |||
var oa = a, | |||
ob = b, | |||
oc = c, | |||
od = d; | |||
a = ff(a, b, c, d, x[i], 7, -680876936); | |||
d = ff(d, a, b, c, x[i + 1], 12, -389564586); | |||
c = ff(c, d, a, b, x[i + 2], 17, 606105819); | |||
b = ff(b, c, d, a, x[i + 3], 22, -1044525330); | |||
a = ff(a, b, c, d, x[i + 4], 7, -176418897); | |||
d = ff(d, a, b, c, x[i + 5], 12, 1200080426); | |||
c = ff(c, d, a, b, x[i + 6], 17, -1473231341); | |||
b = ff(b, c, d, a, x[i + 7], 22, -45705983); | |||
a = ff(a, b, c, d, x[i + 8], 7, 1770035416); | |||
d = ff(d, a, b, c, x[i + 9], 12, -1958414417); | |||
c = ff(c, d, a, b, x[i + 10], 17, -42063); | |||
b = ff(b, c, d, a, x[i + 11], 22, -1990404162); | |||
a = ff(a, b, c, d, x[i + 12], 7, 1804603682); | |||
d = ff(d, a, b, c, x[i + 13], 12, -40341101); | |||
c = ff(c, d, a, b, x[i + 14], 17, -1502002290); | |||
b = ff(b, c, d, a, x[i + 15], 22, 1236535329); | |||
a = gg(a, b, c, d, x[i + 1], 5, -165796510); | |||
d = gg(d, a, b, c, x[i + 6], 9, -1069501632); | |||
c = gg(c, d, a, b, x[i + 11], 14, 643717713); | |||
b = gg(b, c, d, a, x[i], 20, -373897302); | |||
a = gg(a, b, c, d, x[i + 5], 5, -701558691); | |||
d = gg(d, a, b, c, x[i + 10], 9, 38016083); | |||
c = gg(c, d, a, b, x[i + 15], 14, -660478335); | |||
b = gg(b, c, d, a, x[i + 4], 20, -405537848); | |||
a = gg(a, b, c, d, x[i + 9], 5, 568446438); | |||
d = gg(d, a, b, c, x[i + 14], 9, -1019803690); | |||
c = gg(c, d, a, b, x[i + 3], 14, -187363961); | |||
b = gg(b, c, d, a, x[i + 8], 20, 1163531501); | |||
a = gg(a, b, c, d, x[i + 13], 5, -1444681467); | |||
d = gg(d, a, b, c, x[i + 2], 9, -51403784); | |||
c = gg(c, d, a, b, x[i + 7], 14, 1735328473); | |||
b = gg(b, c, d, a, x[i + 12], 20, -1926607734); | |||
a = hh(a, b, c, d, x[i + 5], 4, -378558); | |||
d = hh(d, a, b, c, x[i + 8], 11, -2022574463); | |||
c = hh(c, d, a, b, x[i + 11], 16, 1839030562); | |||
b = hh(b, c, d, a, x[i + 14], 23, -35309556); | |||
a = hh(a, b, c, d, x[i + 1], 4, -1530992060); | |||
d = hh(d, a, b, c, x[i + 4], 11, 1272893353); | |||
c = hh(c, d, a, b, x[i + 7], 16, -155497632); | |||
b = hh(b, c, d, a, x[i + 10], 23, -1094730640); | |||
a = hh(a, b, c, d, x[i + 13], 4, 681279174); | |||
d = hh(d, a, b, c, x[i], 11, -358537222); | |||
c = hh(c, d, a, b, x[i + 3], 16, -722521979); | |||
b = hh(b, c, d, a, x[i + 6], 23, 76029189); | |||
a = hh(a, b, c, d, x[i + 9], 4, -640364487); | |||
d = hh(d, a, b, c, x[i + 12], 11, -421815835); | |||
c = hh(c, d, a, b, x[i + 15], 16, 530742520); | |||
b = hh(b, c, d, a, x[i + 2], 23, -995338651); | |||
a = ii(a, b, c, d, x[i], 6, -198630844); | |||
d = ii(d, a, b, c, x[i + 7], 10, 1126891415); | |||
c = ii(c, d, a, b, x[i + 14], 15, -1416354905); | |||
b = ii(b, c, d, a, x[i + 5], 21, -57434055); | |||
a = ii(a, b, c, d, x[i + 12], 6, 1700485571); | |||
d = ii(d, a, b, c, x[i + 3], 10, -1894986606); | |||
c = ii(c, d, a, b, x[i + 10], 15, -1051523); | |||
b = ii(b, c, d, a, x[i + 1], 21, -2054922799); | |||
a = ii(a, b, c, d, x[i + 8], 6, 1873313359); | |||
d = ii(d, a, b, c, x[i + 15], 10, -30611744); | |||
c = ii(c, d, a, b, x[i + 6], 15, -1560198380); | |||
b = ii(b, c, d, a, x[i + 13], 21, 1309151649); | |||
a = ii(a, b, c, d, x[i + 4], 6, -145523070); | |||
d = ii(d, a, b, c, x[i + 11], 10, -1120210379); | |||
c = ii(c, d, a, b, x[i + 2], 15, 718787259); | |||
b = ii(b, c, d, a, x[i + 9], 21, -343485551); | |||
a = add(a, oa); | |||
b = add(b, ob); | |||
c = add(c, oc); | |||
d = add(d, od); | |||
} | |||
function hex(n) { | |||
var s = "", | |||
j, | |||
hexTab = "0123456789abcdef"; | |||
for (j = 0; j < 4; j++) | |||
s += | |||
hexTab.charAt((n >> (j * 8 + 4)) & 15) + | |||
hexTab.charAt((n >> (j * 8)) & 15); | |||
return s; | |||
} | |||
return hex(a) + hex(b) + hex(c) + hex(d); | |||
} | |||
. | function medalDirectURL(fileName) { | ||
var clean = cleanFileName(fileName); | |||
} | if (!clean) return ""; | ||
var h = md5hex(clean); | |||
return ( | |||
ONLINE_WIKI + | |||
"/images/" + | |||
h.charAt(0) + | |||
"/" + | |||
h.slice(0, 2) + | |||
"/" + | |||
encodeURIComponent(clean) | |||
); | |||
} | |||
function medalFilePathURL(fileName) { | |||
var clean = cleanFileName(fileName); | |||
if (!clean) return ""; | |||
return ( | |||
ONLINE_WIKI + | |||
"/index.php?title=Especial:FilePath/" + | |||
encodeURIComponent(clean) | |||
); | |||
} | |||
. | function bindCharImage(img, ch) { | ||
. | if (!img) return; | ||
var medal = (ch.medal || "").trim(); | |||
} | if (!medal) return; | ||
var direct = medalDirectURL(medal); | |||
var fallback = medalFilePathURL(medal); | |||
img.decoding = "async"; | |||
img.alt = ch.name || ""; | |||
img.onerror = function () { | |||
if (img.dataset.fb === "1") return; | |||
img.dataset.fb = "1"; | |||
img.src = fallback; | |||
}; | |||
if (imgObserver) { | |||
img.loading = "lazy"; | |||
img.setAttribute("data-src", direct); | |||
imgObserver.observe(img); | |||
} else { | |||
img.loading = "lazy"; | |||
img.src = direct; | |||
} | |||
} | |||
. | function sameOriginImageUrl(src) { | ||
if (!src) return ""; | |||
} | try { | ||
var u = new URL(src, location.href); | |||
if (u.origin === location.origin) return u.pathname + u.search; | |||
} catch (e) {} | |||
return src; | |||
} | |||
. | function loadImageForExport(src) { | ||
return new Promise(function (resolve) { | |||
} | if (!src) { | ||
resolve(null); | |||
return; | |||
} | |||
var url = sameOriginImageUrl(src); | |||
var img = new Image(); | |||
img.onload = function () { | |||
resolve(img); | |||
}; | |||
img.onerror = function () { | |||
if (url !== src) { | |||
var img2 = new Image(); | |||
img2.onload = function () { | |||
resolve(img2); | |||
}; | |||
img2.onerror = function () { | |||
resolve(null); | |||
}; | |||
img2.src = src; | |||
return; | |||
} | |||
resolve(null); | |||
}; | |||
img.src = url; | |||
}); | |||
} | |||
. | function ensureRankedImagesReady() { | ||
var tasks = []; | |||
} | RANKS.forEach(function (rank) { | ||
var slots = rowSlots[rank]; | |||
if (!slots) return; | |||
Array.prototype.forEach.call( | |||
slots.querySelectorAll(".gla-tierlist-char img"), | |||
function (img) { | |||
var src = | |||
img.getAttribute("src") || img.getAttribute("data-src") || ""; | |||
if (!src) return; | |||
if (img.getAttribute("data-src")) { | |||
img.src = src; | |||
img.removeAttribute("data-src"); | |||
if (imgObserver) { | |||
try { | |||
imgObserver.unobserve(img); | |||
} catch (e) {} | |||
} | |||
} | |||
if (img.complete && img.naturalWidth > 0) return; | |||
tasks.push( | |||
new Promise(function (resolve) { | |||
var done = function () { | |||
resolve(); | |||
}; | |||
img.addEventListener("load", done, { once: true }); | |||
img.addEventListener("error", done, { once: true }); | |||
}), | |||
); | |||
}, | |||
); | |||
}); | |||
return Promise.all(tasks); | |||
} | |||
. | function gatherExportRows() { | ||
return RANKS.map(function (rank) { | |||
} | var slots = rowSlots[rank]; | ||
var chars = []; | |||
if (slots) { | |||
Array.prototype.forEach.call( | |||
slots.querySelectorAll(".gla-tierlist-char"), | |||
function (btn) { | |||
if (btn.classList.contains("is-hidden")) return; | |||
var ch = btn.__ch || {}; | |||
var img = btn.querySelector("img"); | |||
var src = | |||
(img && | |||
(img.currentSrc || | |||
img.src || | |||
img.getAttribute("data-src"))) || | |||
""; | |||
chars.push({ | |||
name: ch.shortName || ch.name || btn.dataset.id || "", | |||
src: src, | |||
domImg: | |||
img && img.complete && img.naturalWidth > 0 ? img : null, | |||
}); | |||
}, | |||
); | |||
} | |||
return { rank: rank, chars: chars }; | |||
}); | |||
} | |||
. | function getListTitle() { | ||
var custom = listNameInput | |||
} | ? String(listNameInput.value || "").trim() | ||
: ""; | |||
if (custom) return custom; | |||
return t && t.title ? t.title : "Tier List"; | |||
} | |||
. | function sanitizeDownloadName(name) { | ||
var s = String(name || "gla-tier-list") | |||
} | .normalize("NFD") | ||
.replace(/[\u0300-\u036f]/g, "") | |||
.replace(/[<>:"/\\|?*\x00-\x1f]+/g, "") | |||
.replace(/\s+/g, "-") | |||
.replace(/-+/g, "-") | |||
.replace(/^-|-$/g, ""); | |||
if (!s) s = "gla-tier-list"; | |||
if (s.length > 60) s = s.slice(0, 60).replace(/-$/, ""); | |||
return s + ".png"; | |||
} | |||
. | function loadListName() { | ||
if (!listNameInput) return; | |||
} | try { | ||
var saved = localStorage.getItem(STORAGE_NAME_KEY); | |||
if (saved) listNameInput.value = saved; | |||
} catch (e) {} | |||
} | |||
function persistListName() { | |||
if (!listNameInput) return; | |||
} | try { | ||
var v = String(listNameInput.value || "").trim(); | |||
if (v) localStorage.setItem(STORAGE_NAME_KEY, v); | |||
else localStorage.removeItem(STORAGE_NAME_KEY); | |||
} catch (e) {} | |||
} | |||
. | function exportTierListPng() { | ||
if (!downloadBtn || downloadBtn.disabled) return; | |||
var label = t && t.download ? t.download : "Baixar imagem"; | |||
var busy = t && t.downloadBusy ? t.downloadBusy : "Gerando…"; | |||
var listTitle = getListTitle(); | |||
var fileName = sanitizeDownloadName(listTitle); | |||
downloadBtn.disabled = true; | |||
downloadBtn.textContent = busy; | |||
. | ensureRankedImagesReady() | ||
.then(function () { | |||
} | var rows = gatherExportRows(); | ||
/* Export sempre no visual do modo escuro (fundo + contraste). */ | |||
var colors = { | |||
bg: "#0f141c", | |||
paper: "#171c26", | |||
ink: "#e8eef5", | |||
ink2: "#9aa6b5", | |||
rule: "#3a4252", | |||
labels: { | |||
S: "#ff7f7f", | |||
A: "#ffbf7f", | |||
B: "#ffdf7f", | |||
C: "#ffff7f", | |||
D: "#bfff7f", | |||
}, | |||
}; | |||
. | var pad = 28; | ||
var titleH = 90; | |||
var labelW = 72; | |||
var medal = 84; | |||
var gap = 10; | |||
var nameH = 18; | |||
var cellW = medal + gap; | |||
var cellH = medal + nameH + 10; | |||
var rowPad = 14; | |||
var contentW = 980; | |||
var cols = Math.max( | |||
1, | |||
Math.floor((contentW - labelW - rowPad * 2) / cellW), | |||
); | |||
. | var rowHeights = rows.map(function (row) { | ||
var lines = Math.max( | |||
1, | |||
Math.ceil(Math.max(row.chars.length, 1) / cols), | |||
); | |||
return Math.max(112, lines * cellH + rowPad * 2); | |||
}); | |||
var width = pad * 2 + contentW; | |||
var height = | |||
pad * 2 + | |||
titleH + | |||
rowHeights.reduce(function (a, b) { | |||
return a + b + 12; | |||
}, 0); | |||
var scale = 2; | |||
var canvas = document.createElement("canvas"); | |||
canvas.width = Math.round(width * scale); | |||
canvas.height = Math.round(height * scale); | |||
var ctx = canvas.getContext("2d"); | |||
ctx.scale(scale, scale); | |||
. | ctx.fillStyle = colors.bg; | ||
ctx.fillRect(0, 0, width, height); | |||
. | var titleSize = 28; | ||
var logoFile = "WIKIGLAlogo.png"; | |||
var bgFile = "Fundodawikiglatest.png"; | |||
function loadWikiExportImage(fileName) { | |||
return loadImageForExport(medalDirectURL(fileName)).then( | |||
function (img) { | |||
if (img) return img; | |||
return loadImageForExport(medalFilePathURL(fileName)); | |||
}, | |||
); | |||
} | |||
var loads = [ | |||
loadWikiExportImage(logoFile), | |||
loadWikiExportImage(bgFile), | |||
]; | |||
rows.forEach(function (row) { | |||
row.chars.forEach(function (ch) { | |||
if (ch.domImg) { | |||
ch.image = ch.domImg; | |||
return; | |||
} | |||
loads.push( | |||
loadImageForExport(ch.src).then(function (img) { | |||
ch.image = img; | |||
}), | |||
); | |||
}); | |||
}); | |||
return Promise.all(loads).then(function (results) { | |||
var wikiLogo = results[0] || null; | |||
var wikiBg = results[1] || null; | |||
if (wikiBg) { | |||
ctx.save(); | |||
ctx.globalAlpha = 0.3; | |||
drawCover(ctx, wikiBg, 0, 0, width, height); | |||
} | ctx.restore(); | ||
} | |||
. | if (wikiLogo && wikiLogo.naturalHeight) { | ||
var logoH = 78; | |||
} | var logoW = Math.max( | ||
1, | |||
Math.round( | |||
(wikiLogo.naturalWidth / wikiLogo.naturalHeight) * logoH, | |||
), | |||
); | |||
ctx.drawImage(wikiLogo, pad, pad, logoW, logoH); | |||
} | |||
/ | ctx.fillStyle = colors.ink; | ||
ctx.font = | |||
"700 " + | |||
titleSize + | |||
"px 'Space Grotesk', 'Noto Sans', sans-serif"; | |||
ctx.textAlign = "center"; | |||
ctx.textBaseline = "middle"; | |||
ctx.fillText( | |||
truncateText(ctx, listTitle, contentW), | |||
pad + contentW / 2, | |||
pad + 39, | |||
); | |||
ctx.textAlign = "left"; | |||
ctx.textBaseline = "top"; | |||
. | var y = pad + titleH; | ||
rows.forEach(function (row, idx) { | |||
var h = rowHeights[idx]; | |||
var x0 = pad; | |||
roundRect(ctx, x0, y, contentW, h, 14); | |||
ctx.fillStyle = colors.paper; | |||
ctx.fill(); | |||
ctx.strokeStyle = colors.rule; | |||
ctx.lineWidth = 1; | |||
ctx.stroke(); | |||
. | ctx.fillStyle = colors.labels[row.rank] || "#ccc"; | ||
ctx.fillRect(x0, y, labelW, h); | |||
ctx.fillStyle = "#111827"; | |||
ctx.font = "700 26px 'Space Grotesk', 'Noto Sans', sans-serif"; | |||
ctx.textAlign = "center"; | |||
ctx.textBaseline = "middle"; | |||
ctx.fillText(row.rank, x0 + labelW / 2, y + h / 2); | |||
ctx.textAlign = "left"; | |||
ctx.textBaseline = "top"; | |||
. | row.chars.forEach(function (ch, i) { | ||
var col = i % cols; | |||
var line = Math.floor(i / cols); | |||
var cx = x0 + labelW + rowPad + col * cellW + medal / 2; | |||
var cy = y + rowPad + line * cellH + medal / 2; | |||
var ix = cx - medal / 2; | |||
var iy = cy - medal / 2; | |||
. | /* Medalhas já são redondas com fundo transparente — sem círculo extra. */ | ||
if (ch.image) { | |||
drawContain(ctx, ch.image, ix, iy, medal, medal); | |||
} | |||
} | |||
. | ctx.fillStyle = colors.ink2; | ||
. | ctx.font = "600 11px 'Noto Sans', sans-serif"; | ||
ctx.textAlign = "center"; | |||
ctx.textBaseline = "top"; | |||
var labelTxt = truncateText(ctx, ch.name, medal + 4); | |||
} | ctx.fillText(labelTxt, cx, cy + medal / 2 + 4); | ||
ctx.textAlign = "left"; | |||
}); | |||
y += h + 12; | |||
}); | |||
. | return new Promise(function (resolve, reject) { | ||
if (canvas.toBlob) { | |||
} | canvas.toBlob(function (blob) { | ||
if (!blob) reject(new Error("blob")); | |||
else resolve(blob); | |||
}, "image/png"); | |||
} else { | |||
try { | |||
var data = canvas.toDataURL("image/png"); | |||
var bin = atob(data.split(",")[1]); | |||
var arr = new Uint8Array(bin.length); | |||
for (var i = 0; i < bin.length; i++) | |||
arr[i] = bin.charCodeAt(i); | |||
resolve(new Blob([arr], { type: "image/png" })); | |||
} catch (err) { | |||
reject(err); | |||
} | |||
} | |||
}); | |||
}); | |||
}) | |||
.then(function (blob) { | |||
var url = URL.createObjectURL(blob); | |||
var a = document.createElement("a"); | |||
a.href = url; | |||
a.download = fileName; | |||
document.body.appendChild(a); | |||
a.click(); | |||
a.remove(); | |||
setTimeout(function () { | |||
URL.revokeObjectURL(url); | |||
}, 1500); | |||
}) | |||
.catch(function () { | |||
window.alert( | |||
t && t.downloadError ? t.downloadError : "Erro ao gerar imagem.", | |||
); | |||
}) | |||
.then(function () { | |||
downloadBtn.disabled = false; | |||
downloadBtn.textContent = label; | |||
}); | |||
} | |||
. | function roundRect(ctx, x, y, w, h, r) { | ||
var rr = Math.min(r, w / 2, h / 2); | |||
ctx.beginPath(); | |||
ctx.moveTo(x + rr, y); | |||
ctx.arcTo(x + w, y, x + w, y + h, rr); | |||
ctx.arcTo(x + w, y + h, x, y + h, rr); | |||
ctx.arcTo(x, y + h, x, y, rr); | |||
ctx.arcTo(x, y, x + w, y, rr); | |||
ctx.closePath(); | |||
} | |||
function drawContain(ctx, img, x, y, w, h) { | |||
} | var iw = img.naturalWidth || img.width; | ||
var ih = img.naturalHeight || img.height; | |||
if (!iw || !ih) return; | |||
var scale = Math.min(w / iw, h / ih); | |||
var dw = iw * scale; | |||
var dh = ih * scale; | |||
ctx.drawImage(img, x + (w - dw) / 2, y + (h - dh) / 2, dw, dh); | |||
} | |||
. | function drawCover(ctx, img, x, y, w, h) { | ||
var iw = img.naturalWidth || img.width; | |||
var ih = img.naturalHeight || img.height; | |||
if (!iw || !ih) return; | |||
var scale = Math.max(w / iw, h / ih); | |||
var dw = iw * scale; | |||
var dh = ih * scale; | |||
ctx.drawImage(img, x + (w - dw) / 2, y + (h - dh) / 2, dw, dh); | |||
} | |||
function truncateText(ctx, text, maxW) { | |||
} | var s = String(text || ""); | ||
if (ctx.measureText(s).width <= maxW) return s; | |||
while (s.length > 1 && ctx.measureText(s + "…").width > maxW) { | |||
s = s.slice(0, -1); | |||
} | |||
return s + "…"; | |||
} | |||
function emptyTierOrder() { | |||
return { S: [], A: [], B: [], C: [], D: [] }; | |||
} | |||
} | |||
function loadPlacement() { | |||
placement = {}; | |||
tierOrder = emptyTierOrder(); | |||
try { | |||
var raw = localStorage.getItem(STORAGE_KEY); | |||
var parsed = raw ? JSON.parse(raw) : null; | |||
if (!parsed || typeof parsed !== "object") return; | |||
. | /* Formato novo: { S: [id, ...], A: [...], ... } */ | ||
. | if ( | ||
Array.isArray(parsed.S) || | |||
} | Array.isArray(parsed.A) || | ||
Array.isArray(parsed.B) | |||
) { | |||
RANKS.forEach(function (rank) { | |||
var list = Array.isArray(parsed[rank]) ? parsed[rank] : []; | |||
list.forEach(function (id) { | |||
if (typeof id === "string" && id && !placement[id]) { | |||
placement[id] = rank; | |||
tierOrder[rank].push(id); | |||
} | |||
}); | |||
}); | |||
return; | |||
} | |||
. | /* Legado: { charId: "S", ... } */ | ||
. | Object.keys(parsed).forEach(function (id) { | ||
var rank = parsed[id]; | |||
} | if ( | ||
typeof rank === "string" && | |||
RANKS.indexOf(rank) !== -1 && | |||
!placement[id] | |||
) { | |||
placement[id] = rank; | |||
tierOrder[rank].push(id); | |||
} | |||
}); | |||
} catch (e) { | |||
placement = {}; | |||
tierOrder = emptyTierOrder(); | |||
} | |||
} | |||
. | function persistPlacement() { | ||
. | var data = emptyTierOrder(); | ||
placement = {}; | |||
} | RANKS.forEach(function (rank) { | ||
var slots = rowSlots[rank]; | |||
if (!slots) return; | |||
slots.querySelectorAll(".gla-tierlist-char").forEach(function (node) { | |||
var id = node.dataset.id; | |||
if (!id) return; | |||
data[rank].push(id); | |||
placement[id] = rank; | |||
}); | |||
}); | |||
try { | |||
localStorage.setItem(STORAGE_KEY, JSON.stringify(data)); | |||
} catch (e) {} | |||
} | |||
. | /* Posição de inserção no flex-wrap: metade esquerda do card = antes; direita = depois. */ | ||
. | function getInsertBeforeNode(container, x, y) { | ||
if (!container) return null; | |||
var elems = Array.prototype.slice.call( | |||
container.querySelectorAll(".gla-tierlist-char:not(.is-dragging)"), | |||
); | |||
if (!elems.length) return null; | |||
var best = null; | |||
var bestDist = Infinity; | |||
var i, box, cx, cy, dx, dy, dist, candidate; | |||
. | for (i = 0; i < elems.length; i++) { | ||
box = elems[i].getBoundingClientRect(); | |||
cx = box.left + box.width / 2; | |||
} | cy = box.top + box.height / 2; | ||
dx = x - cx; | |||
dy = y - cy; | |||
dist = dx * dx + dy * dy; | |||
if (dist < bestDist) { | |||
bestDist = dist; | |||
candidate = dx < 0 ? elems[i] : elems[i].nextElementSibling; | |||
if ( | |||
candidate && | |||
candidate.dataset && | |||
candidate.dataset.id === dragId | |||
) { | |||
candidate = candidate.nextElementSibling; | |||
} | |||
best = candidate; | |||
} | |||
} | |||
return best; | |||
} | |||
function applyStrings() { | |||
lang = getWidgetLang(); | |||
t = STRINGS[lang] || STRINGS.pt; | |||
} | root.querySelectorAll("[data-i18n]").forEach(function (el) { | ||
var key = el.getAttribute("data-i18n"); | |||
if (t[key] != null) el.textContent = t[key]; | |||
}); | |||
root.querySelectorAll("[data-i18n-placeholder]").forEach(function (el) { | |||
var key = el.getAttribute("data-i18n-placeholder"); | |||
if (t[key] != null) el.setAttribute("placeholder", t[key]); | |||
}); | |||
root.querySelectorAll("[data-i18n-aria]").forEach(function (el) { | |||
var key = el.getAttribute("data-i18n-aria"); | |||
if (t[key] != null) el.setAttribute("aria-label", t[key]); | |||
}); | |||
. | GAME_TIERS.forEach(function (gt) { | ||
var btn = filtersEl.querySelector('[data-tier="' + gt.id + '"]'); | |||
} | if (btn) { | ||
var lbl = btn.querySelector(".gla-tierlist-filter-label"); | |||
if (lbl) lbl.textContent = t[gt.labelKey] || gt.id; | |||
else btn.textContent = t[gt.labelKey] || gt.id; | |||
} | |||
}); | |||
. | var classLabels = CLASS_LABELS[lang] || CLASS_LABELS.pt; | ||
CLASS_FILTERS.forEach(function (f) { | |||
var btn = | |||
classFiltersEl && | |||
classFiltersEl.querySelector('[data-class="' + f.id + '"]'); | |||
if (!btn) return; | |||
} | var label = classLabels[f.id] || f.id; | ||
btn.title = label; | |||
var span = btn.querySelector("span"); | |||
if (span) span.textContent = label; | |||
var img = btn.querySelector("img"); | |||
if (img) img.alt = label; | |||
}); | |||
if (classBarEl && t.classFiltersAria) { | |||
classBarEl.setAttribute("aria-label", t.classFiltersAria); | |||
} | |||
updateCounts(); | |||
} | |||
} | |||
function updateCounts() { | |||
if (!t) return; | |||
var ranked = 0; | |||
var poolVisible = 0; | |||
var anyVisible = 0; | |||
. | Object.keys(charNodes).forEach(function (id) { | ||
var node = charNodes[id]; | |||
var visible = !node.classList.contains("is-hidden"); | |||
if (visible) anyVisible++; | |||
if (placement[id] && RANKS.indexOf(placement[id]) !== -1) { | |||
if (visible) ranked++; | |||
} else if (visible) { | |||
poolVisible++; | |||
} | |||
}); | |||
. | if (countEl) { | ||
if (ranked === 0) countEl.textContent = t.ranked0; | |||
else if (ranked === 1) countEl.textContent = t.ranked1; | |||
else countEl.textContent = t.rankedN.replace("{n}", ranked); | |||
} | |||
if (poolCountEl) { | |||
} | poolCountEl.textContent = t.poolCount.replace("{n}", poolVisible); | ||
} | |||
if (emptyEl) { | |||
emptyEl.hidden = anyVisible > 0 || !characters.length; | |||
} | |||
} | |||
function hasClassFilter(filters, classId) { | |||
if (filters.indexOf(classId) !== -1) return true; | |||
/* bruiser legado conta como DPS */ | |||
if (classId === "dps" && filters.indexOf("bruiser") !== -1) return true; | |||
return false; | |||
} | |||
function matchesFilters(ch) { | |||
if (activeGameTier !== "all" && ch.tier !== activeGameTier) | |||
return false; | |||
if (activeClassFilters.length) { | |||
var filters = ch.filters || []; | |||
for (var f = 0; f < activeClassFilters.length; f++) { | |||
if (!hasClassFilter(filters, activeClassFilters[f])) return false; | |||
} | |||
} | |||
if (!searchTerm) return true; | |||
var hay = ch.__hay || ""; | |||
var tokens = normalize(searchTerm).split(/\s+/).filter(Boolean); | |||
for (var i = 0; i < tokens.length; i++) { | |||
if (hay.indexOf(tokens[i]) === -1) return false; | |||
} | |||
return true; | |||
} | |||
function classLabel(id) { | |||
var map = CLASS_LABELS[lang] || CLASS_LABELS.pt; | |||
return map[id] || id; | |||
} | |||
} | |||
function toggleClassFilter(id) { | |||
var idx = activeClassFilters.indexOf(id); | |||
if (idx >= 0) activeClassFilters.splice(idx, 1); | |||
else activeClassFilters.push(id); | |||
if (classFiltersEl) { | |||
} | classFiltersEl | ||
.querySelectorAll(".gla-tierlist-class-filter") | |||
.forEach(function (btn) { | |||
btn.classList.toggle( | |||
"is-active", | |||
activeClassFilters.indexOf(btn.dataset.class) !== -1, | |||
); | |||
}); | |||
} | |||
applyVisibility(); | |||
} | |||
function applyVisibility() { | |||
var id; | |||
for (id in charNodes) { | |||
if (!Object.prototype.hasOwnProperty.call(charNodes, id)) continue; | |||
} | var node = charNodes[id]; | ||
var ch = node.__ch; | |||
if (!ch) continue; | |||
node.classList.toggle("is-hidden", !matchesFilters(ch)); | |||
} | |||
updateCounts(); | |||
} | |||
function rarityRank(ch) { | |||
if (!ch) return 99; | |||
var n = RARITY_ORDER[ch.tier]; | |||
return typeof n === "number" ? n : 99; | |||
} | |||
} | |||
. | function poolCompare(a, b) { | ||
var ca = a && a.__ch; | |||
} | var cb = b && b.__ch; | ||
var ra = rarityRank(ca); | |||
var rb = rarityRank(cb); | |||
if (ra !== rb) return ra - rb; | |||
var na = normalize( | |||
(ca && ca.name) || (a && a.dataset && a.dataset.id) || "", | |||
); | |||
var nb = normalize( | |||
(cb && cb.name) || (b && b.dataset && b.dataset.id) || "", | |||
); | |||
if (na < nb) return -1; | |||
if (na > nb) return 1; | |||
return 0; | |||
} | |||
. | /* Pool sempre ordenado: Diamante → Ouro → Prata → Bronze (e nome dentro da raridade). */ | ||
function insertIntoPool(node) { | |||
if (!node || !poolEl) return; | |||
} | var siblings = poolEl.children; | ||
var i, other; | |||
for (i = 0; i < siblings.length; i++) { | |||
other = siblings[i]; | |||
if (other === node) continue; | |||
if ( | |||
!other.classList || | |||
!other.classList.contains("gla-tierlist-char") | |||
) | |||
continue; | |||
if (poolCompare(node, other) < 0) { | |||
poolEl.insertBefore(node, other); | |||
return; | |||
} | |||
} | |||
poolEl.appendChild(node); | |||
} | |||
function sortPool() { | |||
if (!poolEl) return; | |||
var nodes = Array.prototype.slice.call( | |||
poolEl.querySelectorAll(".gla-tierlist-char"), | |||
} | ); | ||
nodes.sort(poolCompare); | |||
nodes.forEach(function (n) { | |||
poolEl.appendChild(n); | |||
}); | |||
} | |||
function placeChar(id, rank, beforeNode) { | |||
if (!charNodes[id]) return; | |||
var node = charNodes[id]; | |||
if (beforeNode === node) { | |||
beforeNode = node.nextElementSibling; | |||
} | |||
} | if (rank && RANKS.indexOf(rank) !== -1) { | ||
placement[id] = rank; | |||
var slots = rowSlots[rank]; | |||
if (beforeNode && beforeNode.parentNode === slots) { | |||
slots.insertBefore(node, beforeNode); | |||
} else { | |||
slots.appendChild(node); | |||
} | |||
} else { | |||
delete placement[id]; | |||
insertIntoPool(node); | |||
} | |||
persistPlacement(); | |||
updateCounts(); | |||
} | |||
function clearSelection() { | |||
selectedId = null; | |||
} | } | ||
function selectChar(id) { | |||
if (selectedId === id) { | |||
clearSelection(); | |||
return; | |||
} | |||
} | selectedId = id; | ||
} | |||
function buildCharNode(ch) { | |||
ch.__hay = normalize( | |||
(ch.name || "") + " " + (ch.shortName || "") + " " + (ch.id || ""), | |||
); | |||
var btn = document.createElement("button"); | |||
btn.type = "button"; | |||
btn.className = "gla-tierlist-char"; | |||
btn.draggable = true; | |||
btn.dataset.id = ch.id; | |||
btn.dataset.gameTier = ch.tier || ""; | |||
btn.setAttribute("aria-label", ch.name); | |||
btn.__ch = ch; | |||
btn.innerHTML = | |||
'<div class="gla-tierlist-char-art"><img alt="" width="96" height="96"></div>' + | |||
'<span class="gla-tierlist-char-name" aria-hidden="true"></span>'; | |||
btn.querySelector(".gla-tierlist-char-name").textContent = ch.name; | |||
bindCharImage(btn.querySelector("img"), ch); | |||
. | |||
. | |||
. | |||
. | |||
. | |||
. | |||
. | |||
. | |||
. | |||
. | |||
. | |||
. | |||
. | |||
. | |||
. | |||
} | btn.addEventListener("dragstart", function (ev) { | ||
dragId = ch.id; | |||
btn.classList.add("is-dragging"); | |||
try { | |||
ev.dataTransfer.setData("text/plain", ch.id); | |||
ev.dataTransfer.effectAllowed = "move"; | |||
} catch (e) {} | |||
}); | |||
btn.addEventListener("dragend", function () { | |||
btn.classList.remove("is-dragging"); | |||
dragId = null; | |||
. | root.querySelectorAll(".is-drop-target").forEach(function (el) { | ||
el.classList.remove("is-drop-target"); | |||
}); | |||
}); | |||
} | |||
return btn; | |||
} | |||
} | |||
function resolveDragId(ev) { | |||
var id = dragId; | |||
try { | |||
id = ev.dataTransfer.getData("text/plain") || dragId; | |||
} | } catch (e) {} | ||
return id; | |||
} | |||
. | function bindPoolDrop(el) { | ||
el.addEventListener("dragover", function (ev) { | |||
ev.preventDefault(); | |||
el.classList.add("is-drop-target"); | |||
try { | |||
} | ev.dataTransfer.dropEffect = "move"; | ||
} catch (e) {} | |||
}); | |||
el.addEventListener("dragleave", function (ev) { | |||
if (!el.contains(ev.relatedTarget)) { | |||
el.classList.remove("is-drop-target"); | |||
} | |||
}); | |||
el.addEventListener("drop", function (ev) { | |||
ev.preventDefault(); | |||
el.classList.remove("is-drop-target"); | |||
var id = resolveDragId(ev); | |||
if (id) { | |||
placeChar(id, null); | |||
clearSelection(); | |||
} | |||
}); | |||
} | |||
function bindTierDrop(row, slots, rank) { | |||
function highlight(on) { | |||
row.classList.toggle("is-drop-target", on); | |||
slots.classList.toggle("is-drop-target", on); | |||
} | } | ||
. | slots.addEventListener("dragover", function (ev) { | ||
ev.preventDefault(); | |||
ev.stopPropagation(); | |||
highlight(true); | |||
try { | |||
ev.dataTransfer.dropEffect = "move"; | |||
} catch (e) {} | |||
}); | |||
slots.addEventListener("dragleave", function (ev) { | |||
} | if (!slots.contains(ev.relatedTarget)) { | ||
highlight(false); | |||
} | |||
}); | |||
slots.addEventListener("drop", function (ev) { | |||
ev.preventDefault(); | |||
ev.stopPropagation(); | |||
highlight(false); | |||
var id = resolveDragId(ev); | |||
var before = getInsertBeforeNode(slots, ev.clientX, ev.clientY); | |||
if (id) { | |||
placeChar(id, rank, before); | |||
clearSelection(); | |||
} | |||
}); | |||
. | /* Drop no rótulo S/A/B/C/D → final da linha */ | ||
row.addEventListener("dragover", function (ev) { | |||
if (slots.contains(ev.target)) return; | |||
} | ev.preventDefault(); | ||
highlight(true); | |||
try { | |||
ev.dataTransfer.dropEffect = "move"; | |||
} catch (e) {} | |||
}); | |||
row.addEventListener("dragleave", function (ev) { | |||
if (!row.contains(ev.relatedTarget)) { | |||
highlight(false); | |||
} | |||
}); | |||
row.addEventListener("drop", function (ev) { | |||
if (slots.contains(ev.target)) return; | |||
ev.preventDefault(); | |||
highlight(false); | |||
var id = resolveDragId(ev); | |||
if (id) { | |||
placeChar(id, rank, null); | |||
clearSelection(); | |||
} | |||
}); | |||
} | |||
. | function renderBoard() { | ||
boardEl.textContent = ""; | |||
RANKS.forEach(function (rank) { | |||
var row = document.createElement("div"); | |||
row.className = "gla-tierlist-row"; | |||
row.dataset.rank = rank; | |||
. | var label = document.createElement("div"); | ||
label.className = "gla-tierlist-label"; | |||
label.textContent = rank; | |||
label.title = rank; | |||
label.addEventListener("click", function () { | |||
if (selectedId) { | |||
placeChar(selectedId, rank, null); | |||
clearSelection(); | |||
} | } | ||
}); | |||
. | var slots = document.createElement("div"); | ||
slots.className = "gla-tierlist-slots"; | |||
slots.dataset.slots = rank; | |||
bindTierDrop(row, slots, rank); | |||
. | row.appendChild(label); | ||
row.appendChild(slots); | |||
boardEl.appendChild(row); | |||
} | rowSlots[rank] = slots; | ||
}); | |||
} | |||
. | function renderFilters() { | ||
filtersEl.textContent = ""; | |||
GAME_TIERS.forEach(function (gt) { | |||
var btn = document.createElement("button"); | |||
btn.type = "button"; | |||
btn.className = | |||
} | "gla-tierlist-filter" + (gt.id === "all" ? " is-active" : ""); | ||
btn.dataset.tier = gt.id; | |||
if (gt.id === "all") { | |||
btn.innerHTML = '<span class="gla-tierlist-filter-label"></span>'; | |||
} else { | |||
btn.innerHTML = | |||
'<span class="gla-tierlist-filter-dot"></span><span class="gla-tierlist-filter-label"></span>'; | |||
} | |||
btn.querySelector(".gla-tierlist-filter-label").textContent = gt.id; | |||
btn.addEventListener("click", function () { | |||
activeGameTier = gt.id; | |||
filtersEl | |||
.querySelectorAll(".gla-tierlist-filter") | |||
.forEach(function (b) { | |||
b.classList.toggle("is-active", b.dataset.tier === gt.id); | |||
}); | |||
applyVisibility(); | |||
}); | |||
filtersEl.appendChild(btn); | |||
}); | |||
} | |||
. | function renderClassFilters() { | ||
if (!classFiltersEl) return; | |||
classFiltersEl.textContent = ""; | |||
CLASS_FILTERS.forEach(function (f) { | |||
} | var label = classLabel(f.id); | ||
var btn = document.createElement("button"); | |||
btn.type = "button"; | |||
btn.className = "gla-tierlist-class-filter"; | |||
btn.dataset.class = f.id; | |||
btn.title = label; | |||
btn.innerHTML = '<img alt=""><span></span>'; | |||
var img = btn.querySelector("img"); | |||
var span = btn.querySelector("span"); | |||
img.alt = label; | |||
img.decoding = "async"; | |||
img.loading = "eager"; | |||
/* Mesmos arquivos da Dima (Arquivo:tank.png etc. na wiki oficial). */ | |||
img.src = medalFilePathURL(f.icon); | |||
img.onerror = function () { | |||
img.classList.add("is-missing"); | |||
btn.classList.add("gla-tierlist-class-filter--text-only"); | |||
}; | |||
span.textContent = label; | |||
btn.addEventListener("click", function () { | |||
toggleClassFilter(f.id); | |||
}); | |||
classFiltersEl.appendChild(btn); | |||
}); | |||
} | |||
function mountCharacters() { | |||
characters = readCharacterList(); | |||
var source = document.querySelector(".gla-tierlist-source"); | |||
if (source && source.parentNode) source.parentNode.removeChild(source); | |||
. | var fragPool = document.createDocumentFragment(); | ||
characters.forEach(function (ch) { | |||
if (!ch.id) return; | |||
} | var node = buildCharNode(ch); | ||
charNodes[ch.id] = node; | |||
fragPool.appendChild(node); | |||
}); | |||
poolEl.appendChild(fragPool); | |||
RANKS.forEach(function (rank) { | |||
var order = tierOrder[rank] || []; | |||
order.forEach(function (id) { | |||
if (!charNodes[id]) return; | |||
placement[id] = rank; | |||
rowSlots[rank].appendChild(charNodes[id]); | |||
}); | |||
}); | |||
sortPool(); | |||
applyVisibility(); | |||
} | |||
bindPoolDrop(poolEl); | |||
/* Um listener pra todos os bonecos (em vez de 100+ clicks). */ | |||
root.addEventListener("click", function (ev) { | |||
var btn = ev.target.closest(".gla-tierlist-char"); | |||
} | if (btn && root.contains(btn)) { | ||
ev.preventDefault(); | |||
if (dragId) return; | |||
var targetId = btn.dataset.id; | |||
if (selectedId && selectedId !== targetId) { | |||
var slots = btn.closest(".gla-tierlist-slots"); | |||
if ( | |||
slots && | |||
slots.dataset.slots && | |||
RANKS.indexOf(slots.dataset.slots) !== -1 | |||
) { | |||
placeChar(selectedId, slots.dataset.slots, btn); | |||
clearSelection(); | |||
return; | |||
} | |||
} | |||
selectChar(targetId); | |||
return; | |||
} | |||
var slotsEl = ev.target.closest(".gla-tierlist-slots"); | |||
if ( | |||
slotsEl && | |||
ev.target === slotsEl && | |||
selectedId && | |||
slotsEl.dataset.slots | |||
) { | |||
placeChar(selectedId, slotsEl.dataset.slots, null); | |||
clearSelection(); | |||
return; | |||
} | |||
if (ev.target === poolEl && selectedId) { | |||
placeChar(selectedId, null); | |||
clearSelection(); | |||
} | |||
}); | |||
if (searchInput) { | |||
searchInput.addEventListener("input", function () { | |||
clearTimeout(searchTimeout); | |||
searchTimeout = setTimeout(function () { | |||
searchTerm = searchInput.value || ""; | |||
} | applyVisibility(); | ||
}, 120); | |||
}); | |||
} | |||
if (downloadBtn) { | |||
downloadBtn.addEventListener("click", function () { | |||
exportTierListPng(); | |||
}); | |||
} | |||
} | |||
if (listNameInput) { | |||
listNameInput.addEventListener("input", persistListName); | |||
listNameInput.addEventListener("change", persistListName); | |||
} | |||
} | |||
if (resetBtn) { | |||
resetBtn.addEventListener("click", function () { | |||
Object.keys(placement).forEach(function (id) { | |||
} | delete placement[id]; | ||
if (charNodes[id]) insertIntoPool(charNodes[id]); | |||
}); | |||
sortPool(); | |||
persistPlacement(); | |||
if (listNameInput) { | |||
listNameInput.value = ""; | |||
persistListName(); | |||
} | |||
clearSelection(); | |||
applyVisibility(); | |||
}); | |||
} | |||
if (resetFiltersBtn) { | |||
resetFiltersBtn.addEventListener("click", function () { | |||
activeGameTier = "all"; | |||
} | activeClassFilters = []; | ||
searchTerm = ""; | |||
if (searchInput) searchInput.value = ""; | |||
filtersEl | |||
.querySelectorAll(".gla-tierlist-filter") | |||
.forEach(function (b) { | |||
b.classList.toggle("is-active", b.dataset.tier === "all"); | |||
}); | |||
if (classFiltersEl) { | |||
classFiltersEl | |||
.querySelectorAll(".gla-tierlist-class-filter") | |||
.forEach(function (b) { | |||
b.classList.remove("is-active"); | |||
}); | |||
} | |||
applyVisibility(); | |||
}); | |||
} | |||
. | window.addEventListener("gla:langChanged", function () { | ||
applyStrings(); | |||
}); | |||
loadPlacement(); | |||
loadListName(); | |||
renderBoard(); | |||
renderFilters(); | |||
} | renderClassFilters(); | ||
applyStrings(); | |||
mountCharacters(); | |||
} | |||
. | if (document.readyState === "loading") { | ||
document.addEventListener("DOMContentLoaded", boot); | |||
} | } else { | ||
boot(); | |||
} | |||
if (window.mw && mw.hook) { | |||
mw.hook("wikipage.content").add(boot); | |||
} | |||
})(); | |||
</script> | |||
Edição das 09h57min de 4 de agosto de 2026
<!--
Widget:TierList
UI da tier list (board, pool, filtros, drag-and-drop, download).
Uso: {{TierList}} (predefinição) ou
{{#widget:TierList}}{{#invoke:TierList|renderAll}}
Dados: textarea.gla-tierlist-data (JSON do Módulo:TierList, roster embutido).
Medalhas: Arquivo:«Nome» medal.png via /images/… (fallback Especial:FilePath).
-->
<div class="gla-tierlist-root mw-no-invert" id="gla-tierlist-root">
<section
class="gla-tierlist-toolbar"
data-i18n-aria="filtersAria"
aria-label="Filtros"
>
<input
type="search"
class="gla-tierlist-search"
data-i18n-placeholder="searchPlaceholder"
placeholder="Filtrar personagem..."
autocomplete="off"
spellcheck="false"
/>
<div class="gla-tierlist-filters" data-filters></div>
</section>
<section
class="gla-tierlist-classbar"
data-i18n-aria="classFiltersAria"
aria-label="Filtros de classe"
>
<div class="gla-tierlist-class-filters" data-class-filters></div>
</section>
<section class="gla-tierlist-namebar">
<input
type="text"
class="gla-tierlist-list-name"
data-list-name
data-i18n-placeholder="listNamePlaceholder"
placeholder="Nome da sua tier list..."
maxlength="60"
autocomplete="off"
spellcheck="false"
/>
<div class="gla-tierlist-actions">
<button
type="button"
class="gla-tierlist-pill gla-tierlist-pill--btn"
data-download
data-i18n="download"
>
Baixar imagem
</button>
<button
type="button"
class="gla-tierlist-pill gla-tierlist-pill--btn"
data-reset
data-i18n="reset"
>
Limpar tier list
</button>
</div>
</section>
<section
class="gla-tierlist-board"
data-board
aria-label="Tier list"
></section>
<section class="gla-tierlist-pool-wrap">
<div class="gla-tierlist-pool-head">
<h3 data-i18n="poolTitle">Não ranqueados</h3>
<span class="gla-tierlist-pool-count" data-pool-count>0</span>
</div>
<div class="gla-tierlist-pool" data-pool></div>
<div class="gla-tierlist-empty" hidden>
<p data-i18n="empty">Nenhum personagem encontrado.</p>
<button
type="button"
class="gla-tierlist-reset-filters"
data-reset-filters
data-i18n="resetFilters"
>
Limpar filtros
</button>
</div>
</section>
</div>
<style>
@import url("https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@500;600;700&display=swap");
/* ============ Tier List: design wiki-nativo refinado ============ */
.gla-tierlist-root {
color-scheme: light;
--gla-ink: #0f172a;
--gla-ink-2: #475569;
--gla-dim: #94a3b8;
--gla-paper: #eef3f7; /* painéis (header, rows, pool) */
--gla-paper-2: #ffffff; /* inputs, pills */
--gla-rule: #e2e8f0;
--gla-accent: #156bc7;
--gla-accent-soft: #e8f2fc;
--gla-ok: #0d9488;
--gla-rar-diamond: #0391c5;
--gla-rar-gold: #b8860b;
--gla-rar-silver: #6b7280;
--gla-rar-bronze: #c56e04;
/* Controles da toolbar: altura/borda/tipo únicos (filtros, pills, inputs) */
--gla-ctrl-h: 34px;
--gla-ctrl-gap: 6px;
--gla-ctrl-radius: 999px;
--gla-ctrl-radius-field: 10px;
--gla-ctrl-border-w: 1.5px;
/* Borda de controle: um tom acima de --gla-rule pra nao sumir no fundo */
--gla-ctrl-border: #c5ced9;
--gla-ctrl-pad-x: 12px;
--gla-ctrl-fs: 13px;
--gla-ctrl-fw: 500;
--gla-ctrl-lh: 1;
--gla-ctrl-font: "Noto Sans", "Segoe UI", sans-serif;
font-family: "Noto Sans", sans-serif;
color: var(--gla-ink);
margin: 0 0 1.5rem;
}
.gla-tierlist-root *,
.gla-tierlist-root *::before,
.gla-tierlist-root *::after {
box-sizing: border-box;
}
html.client-darkmode .gla-tierlist-root,
html.skin-theme-clientpref-night .gla-tierlist-root {
color-scheme: dark;
--gla-ink: #e8eef5;
--gla-ink-2: #9aa6b5;
--gla-dim: #6b7788;
--gla-paper: #171c26;
--gla-paper-2: #1e2430;
--gla-rule: #3a4252;
--gla-ctrl-border: #5c667a;
--gla-accent: #5ba3e0;
--gla-accent-soft: rgba(91, 163, 224, 0.18);
--gla-ok: #2dd4bf;
--gla-rar-diamond: #4cc4ea;
--gla-rar-gold: #e8b53a;
--gla-rar-silver: #9aa6b5;
--gla-rar-bronze: #f18a05;
}
/* DarkMode da wiki invert+hue-rotate no documento; mw-no-invert no root
devolve as cores reais. Imagens precisam de filter:none pra nao inverter 2x. */
html.client-darkmode .gla-tierlist-root.mw-no-invert img,
html.skin-theme-clientpref-night .gla-tierlist-root.mw-no-invert img,
html.client-darkmode .gla-tierlist-root.mw-no-invert video,
html.skin-theme-clientpref-night .gla-tierlist-root.mw-no-invert video {
filter: none;
-webkit-filter: none;
}
/* Pills de ação: mesmo sistema visual dos filtros de raridade.
#content.mw-body … sobrescreve o skin da wiki em <button>
(padding/line-height/display), que empurra o texto pra cima. */
.gla-tierlist-pill,
#content.mw-body .gla-tierlist-root .gla-tierlist-pill {
display: inline-flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
margin: 0;
min-height: var(--gla-ctrl-h);
height: var(--gla-ctrl-h);
padding: 0 var(--gla-ctrl-pad-x);
border-radius: var(--gla-ctrl-radius);
border: var(--gla-ctrl-border-w) solid var(--gla-ctrl-border);
background: var(--gla-paper-2);
color: var(--gla-ink-2);
font-family: var(--gla-ctrl-font);
font-size: var(--gla-ctrl-fs);
font-weight: var(--gla-ctrl-fw);
letter-spacing: 0;
line-height: var(--gla-ctrl-lh);
white-space: nowrap;
vertical-align: middle;
}
.gla-tierlist-actions {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--gla-ctrl-gap);
flex: 0 0 auto;
justify-content: center;
}
.gla-tierlist-pill--btn,
#content.mw-body .gla-tierlist-root .gla-tierlist-pill--btn {
cursor: pointer;
appearance: none;
-webkit-appearance: none;
margin: 0;
display: inline-flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
min-height: var(--gla-ctrl-h);
height: var(--gla-ctrl-h);
padding: 0 var(--gla-ctrl-pad-x);
border: var(--gla-ctrl-border-w) solid var(--gla-ctrl-border);
border-radius: var(--gla-ctrl-radius);
background: var(--gla-paper-2);
color: var(--gla-ink-2);
font-family: var(--gla-ctrl-font);
font-size: var(--gla-ctrl-fs);
font-weight: var(--gla-ctrl-fw);
line-height: var(--gla-ctrl-lh);
transition:
background 0.15s,
border-color 0.15s,
color 0.15s,
opacity 0.15s;
flex: 0 0 auto;
}
.gla-tierlist-pill--btn::-moz-focus-inner {
border: 0;
padding: 0;
}
.gla-tierlist-pill--btn:hover,
#content.mw-body .gla-tierlist-root .gla-tierlist-pill--btn:hover {
border-color: var(--gla-accent);
color: var(--gla-accent);
}
.gla-tierlist-pill--btn:disabled {
opacity: 0.55;
cursor: wait;
}
/* ---------- Toolbar ---------- */
.gla-tierlist-toolbar {
display: flex;
flex-wrap: wrap;
gap: var(--gla-ctrl-gap) 10px;
align-items: center;
margin-bottom: 0.55rem;
}
.gla-tierlist-search {
flex: 1 1 14rem;
min-width: 11rem;
box-sizing: border-box;
height: var(--gla-ctrl-h);
min-height: var(--gla-ctrl-h);
padding: 0 14px;
border: var(--gla-ctrl-border-w) solid var(--gla-ctrl-border);
border-radius: var(--gla-ctrl-radius-field);
font-family: var(--gla-ctrl-font);
font-size: var(--gla-ctrl-fs);
font-weight: var(--gla-ctrl-fw);
line-height: var(--gla-ctrl-lh);
background: var(--gla-paper-2);
color: var(--gla-ink);
transition:
border-color 0.15s,
box-shadow 0.15s;
}
.gla-tierlist-search::placeholder {
color: var(--gla-dim);
font-weight: var(--gla-ctrl-fw);
}
.gla-tierlist-search:focus {
outline: none;
border-color: var(--gla-accent);
box-shadow: 0 0 0 3px var(--gla-accent-soft);
}
.gla-tierlist-filters {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--gla-ctrl-gap);
}
.gla-tierlist-filter,
#content.mw-body .gla-tierlist-root .gla-tierlist-filter {
cursor: pointer;
appearance: none;
-webkit-appearance: none;
margin: 0;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px;
box-sizing: border-box;
min-height: var(--gla-ctrl-h);
height: var(--gla-ctrl-h);
padding: 0 var(--gla-ctrl-pad-x);
border: var(--gla-ctrl-border-w) solid var(--gla-ctrl-border);
border-radius: var(--gla-ctrl-radius);
background: var(--gla-paper-2);
color: var(--gla-ink-2);
font-family: var(--gla-ctrl-font);
font-size: var(--gla-ctrl-fs);
font-weight: var(--gla-ctrl-fw);
letter-spacing: 0;
line-height: var(--gla-ctrl-lh);
white-space: nowrap;
vertical-align: middle;
transition:
background 0.15s,
border-color 0.15s,
color 0.15s;
}
.gla-tierlist-filter::-moz-focus-inner {
border: 0;
padding: 0;
}
.gla-tierlist-filter:hover,
#content.mw-body .gla-tierlist-root .gla-tierlist-filter:hover {
border-color: var(--gla-accent);
}
.gla-tierlist-filter-label {
font-family: inherit;
font-size: inherit;
font-weight: inherit;
line-height: 1;
color: inherit;
}
.gla-tierlist-filter-dot {
width: 8px;
height: 8px;
border-radius: 50%;
flex-shrink: 0;
background: var(--gla-dim);
}
.gla-tierlist-filter[data-tier="diamond"] .gla-tierlist-filter-dot {
background: var(--gla-rar-diamond);
}
.gla-tierlist-filter[data-tier="gold"] .gla-tierlist-filter-dot {
background: var(--gla-rar-gold);
}
.gla-tierlist-filter[data-tier="silver"] .gla-tierlist-filter-dot {
background: var(--gla-rar-silver);
}
.gla-tierlist-filter[data-tier="bronze"] .gla-tierlist-filter-dot {
background: var(--gla-rar-bronze);
}
.gla-tierlist-filter.is-active,
#content.mw-body .gla-tierlist-root .gla-tierlist-filter.is-active {
border-color: var(--gla-accent);
background: var(--gla-accent-soft);
color: var(--gla-accent);
}
.gla-tierlist-filter[data-tier="diamond"].is-active,
#content.mw-body
.gla-tierlist-root
.gla-tierlist-filter[data-tier="diamond"].is-active {
border-color: var(--gla-rar-diamond);
color: var(--gla-rar-diamond);
background: rgba(3, 145, 197, 0.14);
}
.gla-tierlist-filter[data-tier="gold"].is-active,
#content.mw-body
.gla-tierlist-root
.gla-tierlist-filter[data-tier="gold"].is-active {
border-color: var(--gla-rar-gold);
color: var(--gla-rar-gold);
background: rgba(212, 160, 23, 0.14);
}
.gla-tierlist-filter[data-tier="silver"].is-active,
#content.mw-body
.gla-tierlist-root
.gla-tierlist-filter[data-tier="silver"].is-active {
border-color: var(--gla-rar-silver);
color: var(--gla-rar-silver);
background: rgba(140, 140, 140, 0.14);
}
.gla-tierlist-filter[data-tier="bronze"].is-active,
#content.mw-body
.gla-tierlist-root
.gla-tierlist-filter[data-tier="bronze"].is-active {
border-color: var(--gla-rar-bronze);
color: var(--gla-rar-bronze);
background: rgba(241, 138, 5, 0.14);
}
/* ---------- Class filters ---------- */
.gla-tierlist-classbar {
margin: 0 0 0.55rem;
}
.gla-tierlist-namebar {
margin: 0 0 0.85rem;
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;
gap: var(--gla-ctrl-gap) 10px;
}
.gla-tierlist-list-name {
flex: 1 1 14rem;
width: auto;
max-width: 22rem;
min-width: 11rem;
box-sizing: border-box;
height: var(--gla-ctrl-h);
min-height: var(--gla-ctrl-h);
padding: 0 14px;
border: var(--gla-ctrl-border-w) solid var(--gla-ctrl-border);
border-radius: var(--gla-ctrl-radius-field);
font-family: var(--gla-ctrl-font);
font-size: var(--gla-ctrl-fs);
font-weight: var(--gla-ctrl-fw);
line-height: var(--gla-ctrl-lh);
text-align: center;
background: var(--gla-paper-2);
color: var(--gla-ink);
transition:
border-color 0.15s,
box-shadow 0.15s;
}
.gla-tierlist-list-name::placeholder {
color: var(--gla-dim);
font-weight: var(--gla-ctrl-fw);
}
.gla-tierlist-list-name:focus {
outline: none;
border-color: var(--gla-accent);
box-shadow: 0 0 0 3px var(--gla-accent-soft);
}
/* Chips de classe no idioma visual da Dima (.gla-dima-filter). */
.gla-tierlist-class-filters {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 6px;
min-width: 0;
width: 100%;
}
.gla-tierlist-class-filter,
#content.mw-body .gla-tierlist-root .gla-tierlist-class-filter {
display: inline-flex;
align-items: center;
gap: 6px;
flex: 0 0 auto;
margin: 0;
padding: 4px 12px 4px 6px;
border-radius: 999px;
border: var(--gla-ctrl-border-w) solid var(--gla-ctrl-border);
background: var(--gla-paper-2);
cursor: pointer;
appearance: none;
-webkit-appearance: none;
box-sizing: border-box;
font-family: "Noto Sans", "Segoe UI", sans-serif;
font-size: 13px;
font-weight: 500;
letter-spacing: 0;
color: var(--gla-ink-2);
line-height: 1;
transition:
border-color 0.15s,
background 0.15s,
color 0.15s;
}
.gla-tierlist-class-filter::-moz-focus-inner {
border: 0;
padding: 0;
}
.gla-tierlist-class-filter:hover,
#content.mw-body .gla-tierlist-root .gla-tierlist-class-filter:hover {
border-color: var(--gla-accent);
}
.gla-tierlist-class-filter img {
width: 32px;
height: 32px;
flex-shrink: 0;
object-fit: contain;
opacity: 0.55;
transition: opacity 0.15s;
filter: none;
-webkit-filter: none;
}
.gla-tierlist-class-filter img.is-missing {
display: none;
}
.gla-tierlist-class-filter--text-only {
padding-left: 12px;
padding-right: 12px;
}
.gla-tierlist-class-filter span {
font-family: "Noto Sans", "Segoe UI", sans-serif;
font-size: 13px;
font-weight: 500;
color: inherit;
white-space: nowrap;
}
.gla-tierlist-class-filter.is-active,
#content.mw-body .gla-tierlist-root .gla-tierlist-class-filter.is-active {
border-color: var(--gla-accent);
background: var(--gla-accent-soft);
color: var(--gla-accent);
}
.gla-tierlist-class-filter.is-active img {
opacity: 1;
}
/* ---------- Board ---------- */
.gla-tierlist-board {
display: flex;
flex-direction: column;
gap: 0.5rem;
margin-bottom: 0.85rem;
overflow: visible;
}
.gla-tierlist-row {
display: grid;
grid-template-columns: 4rem 1fr;
min-height: 6.5rem;
border: 1px solid var(--gla-rule);
border-radius: 12px;
/* visible: overflow:hidden clipava o tooltip do nome acima da medalha */
overflow: visible;
background: var(--gla-paper);
}
.gla-tierlist-row.is-drop-target {
outline: 2px solid var(--gla-accent);
outline-offset: 1px;
}
.gla-tierlist-label {
display: flex;
align-items: center;
justify-content: center;
font-family: "Space Grotesk", sans-serif;
font-weight: 700;
font-size: 1.4rem;
letter-spacing: 0.04em;
color: #111827;
user-select: none;
cursor: pointer;
/* cantos arredondados no label (row não usa mais overflow:hidden) */
border-radius: 11px 0 0 11px;
}
.gla-tierlist-row[data-rank="S"] .gla-tierlist-label {
background: #ff7f7f;
}
.gla-tierlist-row[data-rank="A"] .gla-tierlist-label {
background: #ffbf7f;
}
.gla-tierlist-row[data-rank="B"] .gla-tierlist-label {
background: #ffdf7f;
}
.gla-tierlist-row[data-rank="C"] .gla-tierlist-label {
background: #ffff7f;
}
.gla-tierlist-row[data-rank="D"] .gla-tierlist-label {
background: #bfff7f;
}
.gla-tierlist-slots {
display: flex;
flex-wrap: wrap;
gap: 0.25rem;
align-content: flex-start;
padding: 0.5rem;
min-height: 6.5rem;
overflow: visible;
}
/* ---------- Pool ---------- */
.gla-tierlist-pool-wrap {
background: var(--gla-paper);
border: 1px solid var(--gla-rule);
border-radius: 14px;
padding: 0.9rem 1rem 1rem;
overflow: visible;
}
.gla-tierlist-pool-head {
display: flex;
align-items: baseline;
gap: 0.65rem;
margin-bottom: 0.65rem;
}
.gla-tierlist-pool-head h3 {
margin: 0;
font-family: "Space Grotesk", sans-serif;
font-size: 0.95rem;
font-weight: 700;
}
.gla-tierlist-pool-count {
color: var(--gla-dim);
font-size: 0.8rem;
font-weight: 600;
}
.gla-tierlist-pool {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 0.25rem;
min-height: 4.5rem;
overflow: visible;
}
.gla-tierlist-pool.is-drop-target {
outline: 2px dashed var(--gla-accent);
outline-offset: 2px;
border-radius: 10px;
}
/* Pool: medalhas um pouco mais suaves que as da board (modo claro incomoda). */
.gla-tierlist-pool .gla-tierlist-char-art img {
opacity: 0.9;
filter: saturate(0.88) brightness(0.98);
transition:
opacity 0.15s ease,
filter 0.15s ease;
}
.gla-tierlist-pool .gla-tierlist-char:hover .gla-tierlist-char-art img,
.gla-tierlist-pool
.gla-tierlist-char:focus-visible
.gla-tierlist-char-art
img,
.gla-tierlist-pool .gla-tierlist-char.is-selected .gla-tierlist-char-art img {
opacity: 1;
filter: none;
}
/* ---------- Character card ---------- */
.gla-tierlist-char {
position: relative;
flex: 0 0 104px;
width: 104px;
min-width: 104px;
cursor: grab;
border: 0;
background: transparent;
padding: 4px;
display: flex;
flex-direction: column;
align-items: center;
gap: 0;
transition:
transform 0.12s ease,
opacity 0.15s;
touch-action: none;
user-select: none;
-webkit-user-select: none;
/* sem content-visibility: auto — paint containment clipava o tooltip fora do box */
overflow: visible;
z-index: 0;
}
.gla-tierlist-char:active {
cursor: grabbing;
}
.gla-tierlist-char:focus,
.gla-tierlist-char:focus-visible,
.gla-tierlist-char:focus-within,
.gla-tierlist-char.is-selected,
.gla-tierlist-char.is-selected .gla-tierlist-char-art,
.gla-tierlist-char.is-selected .gla-tierlist-char-art img {
outline: none !important;
outline-offset: 0 !important;
box-shadow: none !important;
}
/* Sem scale/filter no hover: Chromium re-rasteriza a medalha e perde nitidez. */
.gla-tierlist-char:hover,
.gla-tierlist-char:focus-visible,
.gla-tierlist-char.is-selected {
transform: translate3d(0, -2px, 0);
z-index: 30;
}
.gla-tierlist-char.is-hidden {
display: none;
}
.gla-tierlist-char.is-dragging {
opacity: 0.45;
z-index: 40;
}
.gla-tierlist-char-art {
width: 96px;
height: 96px;
background: transparent;
overflow: visible;
line-height: 0;
flex-shrink: 0;
}
.gla-tierlist-char-art img {
width: 100%;
height: 100%;
object-fit: contain;
display: block;
background: transparent;
pointer-events: none;
transform: translateZ(0);
backface-visibility: hidden;
-webkit-backface-visibility: hidden;
}
/* Nome só no tooltip (hover/foco/seleção). Texto sempre preto; pontinha pro medal. */
.gla-tierlist-char-name {
position: absolute;
left: 50%;
bottom: calc(100% + 6px);
transform: translateX(-50%) translateY(4px);
margin: 0;
padding: 0.45rem 0.8rem;
max-width: 16rem;
border-radius: 10px;
border: 1px solid var(--gla-rule);
background: #ffffff;
box-shadow: 0 8px 22px rgba(15, 23, 42, 0.18);
font-family: "Noto Sans", "Segoe UI", sans-serif;
font-size: 0.9375rem;
font-weight: 700;
letter-spacing: 0;
line-height: 1.25;
text-align: center;
color: #0f172a !important;
white-space: nowrap;
overflow: visible;
text-overflow: ellipsis;
opacity: 0;
visibility: hidden;
pointer-events: none;
transition:
opacity 0.12s ease,
transform 0.12s ease,
visibility 0.12s;
z-index: 50;
}
.gla-tierlist-char-name::after {
content: "";
position: absolute;
left: 50%;
top: 100%;
width: 10px;
height: 10px;
margin-left: -5px;
margin-top: -5px;
background: #ffffff;
border-right: 1px solid var(--gla-rule);
border-bottom: 1px solid var(--gla-rule);
transform: rotate(45deg);
box-shadow: 2px 2px 4px rgba(15, 23, 42, 0.06);
}
html.client-darkmode .gla-tierlist-char-name,
html.skin-theme-clientpref-night .gla-tierlist-char-name {
background: #1e2430;
color: #e8eef5 !important;
border-color: #3a4252;
}
html.client-darkmode .gla-tierlist-char-name::after,
html.skin-theme-clientpref-night .gla-tierlist-char-name::after {
background: #1e2430;
border-right-color: #3a4252;
border-bottom-color: #3a4252;
}
.gla-tierlist-char:hover .gla-tierlist-char-name,
.gla-tierlist-char:focus-visible .gla-tierlist-char-name,
.gla-tierlist-char.is-selected .gla-tierlist-char-name {
opacity: 1;
visibility: visible;
transform: translateX(-50%) translateY(0);
}
.gla-tierlist-char.is-dragging .gla-tierlist-char-name {
opacity: 0;
visibility: hidden;
}
/* ---------- Empty state ---------- */
.gla-tierlist-empty {
text-align: center;
padding: 1.5rem 0.5rem 0.5rem;
color: var(--gla-ink-2);
}
.gla-tierlist-empty p {
margin: 0 0 0.65rem;
}
.gla-tierlist-reset-filters {
cursor: pointer;
border: 1px solid var(--gla-rule);
background: var(--gla-paper-2);
color: var(--gla-accent);
font-weight: 600;
font-family: inherit;
padding: 0.4rem 0.85rem;
border-radius: 8px;
}
.gla-tierlist-reset-filters:hover {
background: var(--gla-accent-soft);
border-color: var(--gla-accent);
}
/* ---------- Responsivo ---------- */
@media (max-width: 640px) {
.gla-tierlist-pill--btn {
margin-left: 0;
}
/* Igual Dima: só ícone no mobile; title/aria no botão mantém o rótulo. */
.gla-tierlist-class-filter span {
display: none;
}
.gla-tierlist-class-filter {
padding: 4px;
}
.gla-tierlist-row {
grid-template-columns: 3rem 1fr;
}
.gla-tierlist-label {
font-size: 1.05rem;
}
.gla-tierlist-char {
flex: 0 0 88px;
width: 88px;
min-width: 88px;
}
.gla-tierlist-char-art {
width: 80px;
height: 80px;
}
.gla-tierlist-char-name {
font-size: 0.875rem;
max-width: 13rem;
padding: 0.4rem 0.7rem;
}
}
</style>
<script>
(function () {
function boot() {
var root = document.querySelector(".gla-tierlist-root");
if (!root || root.__glaTierListInit) return;
root.__glaTierListInit = true;
var STORAGE_KEY = "gla-tierlist";
var STORAGE_NAME_KEY = "gla-tierlist-name";
var ONLINE_WIKI = "https://wiki.gla.com.br";
var lang = "pt";
var t = null;
var RANKS = ["S", "A", "B", "C", "D"];
var GAME_TIERS = [
{ id: "all", labelKey: "tierAll" },
{ id: "diamond", labelKey: "tierDiamond" },
{ id: "gold", labelKey: "tierGold" },
{ id: "silver", labelKey: "tierSilver" },
{ id: "bronze", labelKey: "tierBronze" },
];
var RARITY_ORDER = { diamond: 0, gold: 1, silver: 2, bronze: 3 };
/* IDs de filters no Módulo:TierList (legenda no topo do .lua). */
var CLASS_FILTERS = [
{ id: "tank", icon: "tank.png" },
{ id: "dps", icon: "dps.png" },
{ id: "support", icon: "support.png" },
{ id: "fighter", icon: "fighter.png" },
{ id: "shooter", icon: "shooter.png" },
{ id: "slasher", icon: "slasher.png" },
{ id: "especialist", icon: "especialist.png" },
];
var CLASS_LABELS = {
pt: {
tank: "Tanque",
dps: "DPS",
support: "Suporte",
fighter: "Lutador",
shooter: "Atirador",
slasher: "Cortante",
especialist: "Especialista",
},
en: {
tank: "Tank",
dps: "DPS",
support: "Support",
fighter: "Fighter",
shooter: "Shooter",
slasher: "Slasher",
especialist: "Specialist",
},
};
var STRINGS = {
pt: {
title: "Tier List",
reset: "Limpar tier list",
download: "Baixar imagem",
downloadBusy: "Gerando…",
downloadError: "Não foi possível gerar a imagem. Tente de novo.",
listNamePlaceholder: "Nome da sua tier list...",
ranked0: "0 ranqueados",
ranked1: "1 ranqueado",
rankedN: "{n} ranqueados",
searchPlaceholder: "Filtrar personagem...",
filtersAria: "Filtros",
classFiltersAria: "Filtros de classe",
poolTitle: "Não ranqueados",
empty: "Nenhum personagem encontrado.",
resetFilters: "Limpar filtros",
tierAll: "Todos",
tierDiamond: "Diamante",
tierGold: "Ouro",
tierSilver: "Prata",
tierBronze: "Bronze",
poolCount: "{n}",
},
en: {
title: "Tier List",
reset: "Clear tier list",
download: "Download image",
downloadBusy: "Generating…",
downloadError: "Could not generate the image. Please try again.",
listNamePlaceholder: "Name your tier list...",
ranked0: "0 ranked",
ranked1: "1 ranked",
rankedN: "{n} ranked",
searchPlaceholder: "Filter character...",
filtersAria: "Filters",
classFiltersAria: "Class filters",
poolTitle: "Unranked",
empty: "No characters found.",
resetFilters: "Clear filters",
tierAll: "All",
tierDiamond: "Diamond",
tierGold: "Gold",
tierSilver: "Silver",
tierBronze: "Bronze",
poolCount: "{n}",
},
};
function getWidgetLang() {
if (
window.glaLang &&
typeof window.glaLang.getWidgetLang === "function"
) {
return window.glaLang.getWidgetLang() === "en" ? "en" : "pt";
}
var attr = (
document.documentElement.getAttribute("data-gla-lang") || ""
).toLowerCase();
if (attr === "en") return "en";
try {
var saved = (localStorage.getItem("glaLang") || "").toLowerCase();
if (saved.indexOf("en") === 0) return "en";
} catch (e) {}
return "pt";
}
var boardEl = root.querySelector("[data-board]");
var poolEl = root.querySelector("[data-pool]");
var filtersEl = root.querySelector("[data-filters]");
var classFiltersEl = root.querySelector("[data-class-filters]");
var classBarEl = root.querySelector(".gla-tierlist-classbar");
var emptyEl = root.querySelector(".gla-tierlist-empty");
var countEl = root.querySelector("[data-count]");
var poolCountEl = root.querySelector("[data-pool-count]");
var searchInput = root.querySelector(".gla-tierlist-search");
var resetBtn = root.querySelector("[data-reset]");
var downloadBtn = root.querySelector("[data-download]");
var listNameInput = root.querySelector("[data-list-name]");
var resetFiltersBtn = root.querySelector("[data-reset-filters]");
var characters = [];
var placement = {};
var tierOrder = { S: [], A: [], B: [], C: [], D: [] };
var charNodes = {};
var rowSlots = {};
var activeGameTier = "all";
var activeClassFilters = [];
var searchTerm = "";
var searchTimeout = null;
var selectedId = null;
var dragId = null;
var imgObserver = null;
if ("IntersectionObserver" in window) {
imgObserver = new IntersectionObserver(
function (entries) {
entries.forEach(function (entry) {
if (!entry.isIntersecting) return;
var img = entry.target;
var src = img.getAttribute("data-src");
if (src) {
img.src = src;
img.removeAttribute("data-src");
}
imgObserver.unobserve(img);
});
},
{ rootMargin: "200px 0px", threshold: 0.01 },
);
}
function normalize(s) {
return (s || "")
.toLowerCase()
.normalize("NFD")
.replace(/[\u0300-\u036f]/g, "");
}
function readCharacterList() {
var source = document.querySelector(".gla-tierlist-source");
if (!source) return [];
var dataEl = source.querySelector(".gla-tierlist-data");
var raw = dataEl
? dataEl.value || dataEl.textContent || ""
: source.textContent;
raw = (raw || "").trim();
if (!raw) return [];
try {
return JSON.parse(raw);
} catch (e1) {
var start = raw.indexOf("[");
var end = raw.lastIndexOf("]");
if (start === -1 || end === -1) return [];
try {
return JSON.parse(raw.slice(start, end + 1));
} catch (e2) {
console.error("[TierList] JSON invalido", e2);
return [];
}
}
}
function cleanFileName(fileName) {
var clean = String(fileName || "").replace(/^Arquivo:|^File:/i, "");
try {
clean = decodeURIComponent(clean);
} catch (e) {}
return clean.replace(/ /g, "_");
}
/* Path direto /images/a/ab/File.png (MD5 do nome) — evita thumb.php (quebrado) e redirect do FilePath. */
function md5hex(string) {
function add(x, y) {
var l = (x & 0xffff) + (y & 0xffff);
var m = (x >> 16) + (y >> 16) + (l >> 16);
return (m << 16) | (l & 0xffff);
}
function rol(n, c) {
return (n << c) | (n >>> (32 - c));
}
function cmn(q, a, b, x, s, t) {
return add(rol(add(add(a, q), add(x, t)), s), b);
}
function ff(a, b, c, d, x, s, t) {
return cmn((b & c) | (~b & d), a, b, x, s, t);
}
function gg(a, b, c, d, x, s, t) {
return cmn((b & d) | (c & ~d), a, b, x, s, t);
}
function hh(a, b, c, d, x, s, t) {
return cmn(b ^ c ^ d, a, b, x, s, t);
}
function ii(a, b, c, d, x, s, t) {
return cmn(c ^ (b | ~d), a, b, x, s, t);
}
var raw = unescape(encodeURIComponent(string));
var x = [],
i;
for (i = 0; i < raw.length * 8; i += 8)
x[i >> 5] |= (raw.charCodeAt(i / 8) & 255) << (i % 32);
x[(raw.length * 8) >> 5] |= 128 << ((raw.length * 8) % 32);
x[(((raw.length + 64) >>> 9) << 4) + 14] = raw.length * 8;
var a = 1732584193,
b = -271733879,
c = -1732584194,
d = 271733878;
for (i = 0; i < x.length; i += 16) {
var oa = a,
ob = b,
oc = c,
od = d;
a = ff(a, b, c, d, x[i], 7, -680876936);
d = ff(d, a, b, c, x[i + 1], 12, -389564586);
c = ff(c, d, a, b, x[i + 2], 17, 606105819);
b = ff(b, c, d, a, x[i + 3], 22, -1044525330);
a = ff(a, b, c, d, x[i + 4], 7, -176418897);
d = ff(d, a, b, c, x[i + 5], 12, 1200080426);
c = ff(c, d, a, b, x[i + 6], 17, -1473231341);
b = ff(b, c, d, a, x[i + 7], 22, -45705983);
a = ff(a, b, c, d, x[i + 8], 7, 1770035416);
d = ff(d, a, b, c, x[i + 9], 12, -1958414417);
c = ff(c, d, a, b, x[i + 10], 17, -42063);
b = ff(b, c, d, a, x[i + 11], 22, -1990404162);
a = ff(a, b, c, d, x[i + 12], 7, 1804603682);
d = ff(d, a, b, c, x[i + 13], 12, -40341101);
c = ff(c, d, a, b, x[i + 14], 17, -1502002290);
b = ff(b, c, d, a, x[i + 15], 22, 1236535329);
a = gg(a, b, c, d, x[i + 1], 5, -165796510);
d = gg(d, a, b, c, x[i + 6], 9, -1069501632);
c = gg(c, d, a, b, x[i + 11], 14, 643717713);
b = gg(b, c, d, a, x[i], 20, -373897302);
a = gg(a, b, c, d, x[i + 5], 5, -701558691);
d = gg(d, a, b, c, x[i + 10], 9, 38016083);
c = gg(c, d, a, b, x[i + 15], 14, -660478335);
b = gg(b, c, d, a, x[i + 4], 20, -405537848);
a = gg(a, b, c, d, x[i + 9], 5, 568446438);
d = gg(d, a, b, c, x[i + 14], 9, -1019803690);
c = gg(c, d, a, b, x[i + 3], 14, -187363961);
b = gg(b, c, d, a, x[i + 8], 20, 1163531501);
a = gg(a, b, c, d, x[i + 13], 5, -1444681467);
d = gg(d, a, b, c, x[i + 2], 9, -51403784);
c = gg(c, d, a, b, x[i + 7], 14, 1735328473);
b = gg(b, c, d, a, x[i + 12], 20, -1926607734);
a = hh(a, b, c, d, x[i + 5], 4, -378558);
d = hh(d, a, b, c, x[i + 8], 11, -2022574463);
c = hh(c, d, a, b, x[i + 11], 16, 1839030562);
b = hh(b, c, d, a, x[i + 14], 23, -35309556);
a = hh(a, b, c, d, x[i + 1], 4, -1530992060);
d = hh(d, a, b, c, x[i + 4], 11, 1272893353);
c = hh(c, d, a, b, x[i + 7], 16, -155497632);
b = hh(b, c, d, a, x[i + 10], 23, -1094730640);
a = hh(a, b, c, d, x[i + 13], 4, 681279174);
d = hh(d, a, b, c, x[i], 11, -358537222);
c = hh(c, d, a, b, x[i + 3], 16, -722521979);
b = hh(b, c, d, a, x[i + 6], 23, 76029189);
a = hh(a, b, c, d, x[i + 9], 4, -640364487);
d = hh(d, a, b, c, x[i + 12], 11, -421815835);
c = hh(c, d, a, b, x[i + 15], 16, 530742520);
b = hh(b, c, d, a, x[i + 2], 23, -995338651);
a = ii(a, b, c, d, x[i], 6, -198630844);
d = ii(d, a, b, c, x[i + 7], 10, 1126891415);
c = ii(c, d, a, b, x[i + 14], 15, -1416354905);
b = ii(b, c, d, a, x[i + 5], 21, -57434055);
a = ii(a, b, c, d, x[i + 12], 6, 1700485571);
d = ii(d, a, b, c, x[i + 3], 10, -1894986606);
c = ii(c, d, a, b, x[i + 10], 15, -1051523);
b = ii(b, c, d, a, x[i + 1], 21, -2054922799);
a = ii(a, b, c, d, x[i + 8], 6, 1873313359);
d = ii(d, a, b, c, x[i + 15], 10, -30611744);
c = ii(c, d, a, b, x[i + 6], 15, -1560198380);
b = ii(b, c, d, a, x[i + 13], 21, 1309151649);
a = ii(a, b, c, d, x[i + 4], 6, -145523070);
d = ii(d, a, b, c, x[i + 11], 10, -1120210379);
c = ii(c, d, a, b, x[i + 2], 15, 718787259);
b = ii(b, c, d, a, x[i + 9], 21, -343485551);
a = add(a, oa);
b = add(b, ob);
c = add(c, oc);
d = add(d, od);
}
function hex(n) {
var s = "",
j,
hexTab = "0123456789abcdef";
for (j = 0; j < 4; j++)
s +=
hexTab.charAt((n >> (j * 8 + 4)) & 15) +
hexTab.charAt((n >> (j * 8)) & 15);
return s;
}
return hex(a) + hex(b) + hex(c) + hex(d);
}
function medalDirectURL(fileName) {
var clean = cleanFileName(fileName);
if (!clean) return "";
var h = md5hex(clean);
return (
ONLINE_WIKI +
"/images/" +
h.charAt(0) +
"/" +
h.slice(0, 2) +
"/" +
encodeURIComponent(clean)
);
}
function medalFilePathURL(fileName) {
var clean = cleanFileName(fileName);
if (!clean) return "";
return (
ONLINE_WIKI +
"/index.php?title=Especial:FilePath/" +
encodeURIComponent(clean)
);
}
function bindCharImage(img, ch) {
if (!img) return;
var medal = (ch.medal || "").trim();
if (!medal) return;
var direct = medalDirectURL(medal);
var fallback = medalFilePathURL(medal);
img.decoding = "async";
img.alt = ch.name || "";
img.onerror = function () {
if (img.dataset.fb === "1") return;
img.dataset.fb = "1";
img.src = fallback;
};
if (imgObserver) {
img.loading = "lazy";
img.setAttribute("data-src", direct);
imgObserver.observe(img);
} else {
img.loading = "lazy";
img.src = direct;
}
}
function sameOriginImageUrl(src) {
if (!src) return "";
try {
var u = new URL(src, location.href);
if (u.origin === location.origin) return u.pathname + u.search;
} catch (e) {}
return src;
}
function loadImageForExport(src) {
return new Promise(function (resolve) {
if (!src) {
resolve(null);
return;
}
var url = sameOriginImageUrl(src);
var img = new Image();
img.onload = function () {
resolve(img);
};
img.onerror = function () {
if (url !== src) {
var img2 = new Image();
img2.onload = function () {
resolve(img2);
};
img2.onerror = function () {
resolve(null);
};
img2.src = src;
return;
}
resolve(null);
};
img.src = url;
});
}
function ensureRankedImagesReady() {
var tasks = [];
RANKS.forEach(function (rank) {
var slots = rowSlots[rank];
if (!slots) return;
Array.prototype.forEach.call(
slots.querySelectorAll(".gla-tierlist-char img"),
function (img) {
var src =
img.getAttribute("src") || img.getAttribute("data-src") || "";
if (!src) return;
if (img.getAttribute("data-src")) {
img.src = src;
img.removeAttribute("data-src");
if (imgObserver) {
try {
imgObserver.unobserve(img);
} catch (e) {}
}
}
if (img.complete && img.naturalWidth > 0) return;
tasks.push(
new Promise(function (resolve) {
var done = function () {
resolve();
};
img.addEventListener("load", done, { once: true });
img.addEventListener("error", done, { once: true });
}),
);
},
);
});
return Promise.all(tasks);
}
function gatherExportRows() {
return RANKS.map(function (rank) {
var slots = rowSlots[rank];
var chars = [];
if (slots) {
Array.prototype.forEach.call(
slots.querySelectorAll(".gla-tierlist-char"),
function (btn) {
if (btn.classList.contains("is-hidden")) return;
var ch = btn.__ch || {};
var img = btn.querySelector("img");
var src =
(img &&
(img.currentSrc ||
img.src ||
img.getAttribute("data-src"))) ||
"";
chars.push({
name: ch.shortName || ch.name || btn.dataset.id || "",
src: src,
domImg:
img && img.complete && img.naturalWidth > 0 ? img : null,
});
},
);
}
return { rank: rank, chars: chars };
});
}
function getListTitle() {
var custom = listNameInput
? String(listNameInput.value || "").trim()
: "";
if (custom) return custom;
return t && t.title ? t.title : "Tier List";
}
function sanitizeDownloadName(name) {
var s = String(name || "gla-tier-list")
.normalize("NFD")
.replace(/[\u0300-\u036f]/g, "")
.replace(/[<>:"/\\|?*\x00-\x1f]+/g, "")
.replace(/\s+/g, "-")
.replace(/-+/g, "-")
.replace(/^-|-$/g, "");
if (!s) s = "gla-tier-list";
if (s.length > 60) s = s.slice(0, 60).replace(/-$/, "");
return s + ".png";
}
function loadListName() {
if (!listNameInput) return;
try {
var saved = localStorage.getItem(STORAGE_NAME_KEY);
if (saved) listNameInput.value = saved;
} catch (e) {}
}
function persistListName() {
if (!listNameInput) return;
try {
var v = String(listNameInput.value || "").trim();
if (v) localStorage.setItem(STORAGE_NAME_KEY, v);
else localStorage.removeItem(STORAGE_NAME_KEY);
} catch (e) {}
}
function exportTierListPng() {
if (!downloadBtn || downloadBtn.disabled) return;
var label = t && t.download ? t.download : "Baixar imagem";
var busy = t && t.downloadBusy ? t.downloadBusy : "Gerando…";
var listTitle = getListTitle();
var fileName = sanitizeDownloadName(listTitle);
downloadBtn.disabled = true;
downloadBtn.textContent = busy;
ensureRankedImagesReady()
.then(function () {
var rows = gatherExportRows();
/* Export sempre no visual do modo escuro (fundo + contraste). */
var colors = {
bg: "#0f141c",
paper: "#171c26",
ink: "#e8eef5",
ink2: "#9aa6b5",
rule: "#3a4252",
labels: {
S: "#ff7f7f",
A: "#ffbf7f",
B: "#ffdf7f",
C: "#ffff7f",
D: "#bfff7f",
},
};
var pad = 28;
var titleH = 90;
var labelW = 72;
var medal = 84;
var gap = 10;
var nameH = 18;
var cellW = medal + gap;
var cellH = medal + nameH + 10;
var rowPad = 14;
var contentW = 980;
var cols = Math.max(
1,
Math.floor((contentW - labelW - rowPad * 2) / cellW),
);
var rowHeights = rows.map(function (row) {
var lines = Math.max(
1,
Math.ceil(Math.max(row.chars.length, 1) / cols),
);
return Math.max(112, lines * cellH + rowPad * 2);
});
var width = pad * 2 + contentW;
var height =
pad * 2 +
titleH +
rowHeights.reduce(function (a, b) {
return a + b + 12;
}, 0);
var scale = 2;
var canvas = document.createElement("canvas");
canvas.width = Math.round(width * scale);
canvas.height = Math.round(height * scale);
var ctx = canvas.getContext("2d");
ctx.scale(scale, scale);
ctx.fillStyle = colors.bg;
ctx.fillRect(0, 0, width, height);
var titleSize = 28;
var logoFile = "WIKIGLAlogo.png";
var bgFile = "Fundodawikiglatest.png";
function loadWikiExportImage(fileName) {
return loadImageForExport(medalDirectURL(fileName)).then(
function (img) {
if (img) return img;
return loadImageForExport(medalFilePathURL(fileName));
},
);
}
var loads = [
loadWikiExportImage(logoFile),
loadWikiExportImage(bgFile),
];
rows.forEach(function (row) {
row.chars.forEach(function (ch) {
if (ch.domImg) {
ch.image = ch.domImg;
return;
}
loads.push(
loadImageForExport(ch.src).then(function (img) {
ch.image = img;
}),
);
});
});
return Promise.all(loads).then(function (results) {
var wikiLogo = results[0] || null;
var wikiBg = results[1] || null;
if (wikiBg) {
ctx.save();
ctx.globalAlpha = 0.3;
drawCover(ctx, wikiBg, 0, 0, width, height);
ctx.restore();
}
if (wikiLogo && wikiLogo.naturalHeight) {
var logoH = 78;
var logoW = Math.max(
1,
Math.round(
(wikiLogo.naturalWidth / wikiLogo.naturalHeight) * logoH,
),
);
ctx.drawImage(wikiLogo, pad, pad, logoW, logoH);
}
ctx.fillStyle = colors.ink;
ctx.font =
"700 " +
titleSize +
"px 'Space Grotesk', 'Noto Sans', sans-serif";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText(
truncateText(ctx, listTitle, contentW),
pad + contentW / 2,
pad + 39,
);
ctx.textAlign = "left";
ctx.textBaseline = "top";
var y = pad + titleH;
rows.forEach(function (row, idx) {
var h = rowHeights[idx];
var x0 = pad;
roundRect(ctx, x0, y, contentW, h, 14);
ctx.fillStyle = colors.paper;
ctx.fill();
ctx.strokeStyle = colors.rule;
ctx.lineWidth = 1;
ctx.stroke();
ctx.fillStyle = colors.labels[row.rank] || "#ccc";
ctx.fillRect(x0, y, labelW, h);
ctx.fillStyle = "#111827";
ctx.font = "700 26px 'Space Grotesk', 'Noto Sans', sans-serif";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText(row.rank, x0 + labelW / 2, y + h / 2);
ctx.textAlign = "left";
ctx.textBaseline = "top";
row.chars.forEach(function (ch, i) {
var col = i % cols;
var line = Math.floor(i / cols);
var cx = x0 + labelW + rowPad + col * cellW + medal / 2;
var cy = y + rowPad + line * cellH + medal / 2;
var ix = cx - medal / 2;
var iy = cy - medal / 2;
/* Medalhas já são redondas com fundo transparente — sem círculo extra. */
if (ch.image) {
drawContain(ctx, ch.image, ix, iy, medal, medal);
}
ctx.fillStyle = colors.ink2;
ctx.font = "600 11px 'Noto Sans', sans-serif";
ctx.textAlign = "center";
ctx.textBaseline = "top";
var labelTxt = truncateText(ctx, ch.name, medal + 4);
ctx.fillText(labelTxt, cx, cy + medal / 2 + 4);
ctx.textAlign = "left";
});
y += h + 12;
});
return new Promise(function (resolve, reject) {
if (canvas.toBlob) {
canvas.toBlob(function (blob) {
if (!blob) reject(new Error("blob"));
else resolve(blob);
}, "image/png");
} else {
try {
var data = canvas.toDataURL("image/png");
var bin = atob(data.split(",")[1]);
var arr = new Uint8Array(bin.length);
for (var i = 0; i < bin.length; i++)
arr[i] = bin.charCodeAt(i);
resolve(new Blob([arr], { type: "image/png" }));
} catch (err) {
reject(err);
}
}
});
});
})
.then(function (blob) {
var url = URL.createObjectURL(blob);
var a = document.createElement("a");
a.href = url;
a.download = fileName;
document.body.appendChild(a);
a.click();
a.remove();
setTimeout(function () {
URL.revokeObjectURL(url);
}, 1500);
})
.catch(function () {
window.alert(
t && t.downloadError ? t.downloadError : "Erro ao gerar imagem.",
);
})
.then(function () {
downloadBtn.disabled = false;
downloadBtn.textContent = label;
});
}
function roundRect(ctx, x, y, w, h, r) {
var rr = Math.min(r, w / 2, h / 2);
ctx.beginPath();
ctx.moveTo(x + rr, y);
ctx.arcTo(x + w, y, x + w, y + h, rr);
ctx.arcTo(x + w, y + h, x, y + h, rr);
ctx.arcTo(x, y + h, x, y, rr);
ctx.arcTo(x, y, x + w, y, rr);
ctx.closePath();
}
function drawContain(ctx, img, x, y, w, h) {
var iw = img.naturalWidth || img.width;
var ih = img.naturalHeight || img.height;
if (!iw || !ih) return;
var scale = Math.min(w / iw, h / ih);
var dw = iw * scale;
var dh = ih * scale;
ctx.drawImage(img, x + (w - dw) / 2, y + (h - dh) / 2, dw, dh);
}
function drawCover(ctx, img, x, y, w, h) {
var iw = img.naturalWidth || img.width;
var ih = img.naturalHeight || img.height;
if (!iw || !ih) return;
var scale = Math.max(w / iw, h / ih);
var dw = iw * scale;
var dh = ih * scale;
ctx.drawImage(img, x + (w - dw) / 2, y + (h - dh) / 2, dw, dh);
}
function truncateText(ctx, text, maxW) {
var s = String(text || "");
if (ctx.measureText(s).width <= maxW) return s;
while (s.length > 1 && ctx.measureText(s + "…").width > maxW) {
s = s.slice(0, -1);
}
return s + "…";
}
function emptyTierOrder() {
return { S: [], A: [], B: [], C: [], D: [] };
}
function loadPlacement() {
placement = {};
tierOrder = emptyTierOrder();
try {
var raw = localStorage.getItem(STORAGE_KEY);
var parsed = raw ? JSON.parse(raw) : null;
if (!parsed || typeof parsed !== "object") return;
/* Formato novo: { S: [id, ...], A: [...], ... } */
if (
Array.isArray(parsed.S) ||
Array.isArray(parsed.A) ||
Array.isArray(parsed.B)
) {
RANKS.forEach(function (rank) {
var list = Array.isArray(parsed[rank]) ? parsed[rank] : [];
list.forEach(function (id) {
if (typeof id === "string" && id && !placement[id]) {
placement[id] = rank;
tierOrder[rank].push(id);
}
});
});
return;
}
/* Legado: { charId: "S", ... } */
Object.keys(parsed).forEach(function (id) {
var rank = parsed[id];
if (
typeof rank === "string" &&
RANKS.indexOf(rank) !== -1 &&
!placement[id]
) {
placement[id] = rank;
tierOrder[rank].push(id);
}
});
} catch (e) {
placement = {};
tierOrder = emptyTierOrder();
}
}
function persistPlacement() {
var data = emptyTierOrder();
placement = {};
RANKS.forEach(function (rank) {
var slots = rowSlots[rank];
if (!slots) return;
slots.querySelectorAll(".gla-tierlist-char").forEach(function (node) {
var id = node.dataset.id;
if (!id) return;
data[rank].push(id);
placement[id] = rank;
});
});
try {
localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
} catch (e) {}
}
/* Posição de inserção no flex-wrap: metade esquerda do card = antes; direita = depois. */
function getInsertBeforeNode(container, x, y) {
if (!container) return null;
var elems = Array.prototype.slice.call(
container.querySelectorAll(".gla-tierlist-char:not(.is-dragging)"),
);
if (!elems.length) return null;
var best = null;
var bestDist = Infinity;
var i, box, cx, cy, dx, dy, dist, candidate;
for (i = 0; i < elems.length; i++) {
box = elems[i].getBoundingClientRect();
cx = box.left + box.width / 2;
cy = box.top + box.height / 2;
dx = x - cx;
dy = y - cy;
dist = dx * dx + dy * dy;
if (dist < bestDist) {
bestDist = dist;
candidate = dx < 0 ? elems[i] : elems[i].nextElementSibling;
if (
candidate &&
candidate.dataset &&
candidate.dataset.id === dragId
) {
candidate = candidate.nextElementSibling;
}
best = candidate;
}
}
return best;
}
function applyStrings() {
lang = getWidgetLang();
t = STRINGS[lang] || STRINGS.pt;
root.querySelectorAll("[data-i18n]").forEach(function (el) {
var key = el.getAttribute("data-i18n");
if (t[key] != null) el.textContent = t[key];
});
root.querySelectorAll("[data-i18n-placeholder]").forEach(function (el) {
var key = el.getAttribute("data-i18n-placeholder");
if (t[key] != null) el.setAttribute("placeholder", t[key]);
});
root.querySelectorAll("[data-i18n-aria]").forEach(function (el) {
var key = el.getAttribute("data-i18n-aria");
if (t[key] != null) el.setAttribute("aria-label", t[key]);
});
GAME_TIERS.forEach(function (gt) {
var btn = filtersEl.querySelector('[data-tier="' + gt.id + '"]');
if (btn) {
var lbl = btn.querySelector(".gla-tierlist-filter-label");
if (lbl) lbl.textContent = t[gt.labelKey] || gt.id;
else btn.textContent = t[gt.labelKey] || gt.id;
}
});
var classLabels = CLASS_LABELS[lang] || CLASS_LABELS.pt;
CLASS_FILTERS.forEach(function (f) {
var btn =
classFiltersEl &&
classFiltersEl.querySelector('[data-class="' + f.id + '"]');
if (!btn) return;
var label = classLabels[f.id] || f.id;
btn.title = label;
var span = btn.querySelector("span");
if (span) span.textContent = label;
var img = btn.querySelector("img");
if (img) img.alt = label;
});
if (classBarEl && t.classFiltersAria) {
classBarEl.setAttribute("aria-label", t.classFiltersAria);
}
updateCounts();
}
function updateCounts() {
if (!t) return;
var ranked = 0;
var poolVisible = 0;
var anyVisible = 0;
Object.keys(charNodes).forEach(function (id) {
var node = charNodes[id];
var visible = !node.classList.contains("is-hidden");
if (visible) anyVisible++;
if (placement[id] && RANKS.indexOf(placement[id]) !== -1) {
if (visible) ranked++;
} else if (visible) {
poolVisible++;
}
});
if (countEl) {
if (ranked === 0) countEl.textContent = t.ranked0;
else if (ranked === 1) countEl.textContent = t.ranked1;
else countEl.textContent = t.rankedN.replace("{n}", ranked);
}
if (poolCountEl) {
poolCountEl.textContent = t.poolCount.replace("{n}", poolVisible);
}
if (emptyEl) {
emptyEl.hidden = anyVisible > 0 || !characters.length;
}
}
function hasClassFilter(filters, classId) {
if (filters.indexOf(classId) !== -1) return true;
/* bruiser legado conta como DPS */
if (classId === "dps" && filters.indexOf("bruiser") !== -1) return true;
return false;
}
function matchesFilters(ch) {
if (activeGameTier !== "all" && ch.tier !== activeGameTier)
return false;
if (activeClassFilters.length) {
var filters = ch.filters || [];
for (var f = 0; f < activeClassFilters.length; f++) {
if (!hasClassFilter(filters, activeClassFilters[f])) return false;
}
}
if (!searchTerm) return true;
var hay = ch.__hay || "";
var tokens = normalize(searchTerm).split(/\s+/).filter(Boolean);
for (var i = 0; i < tokens.length; i++) {
if (hay.indexOf(tokens[i]) === -1) return false;
}
return true;
}
function classLabel(id) {
var map = CLASS_LABELS[lang] || CLASS_LABELS.pt;
return map[id] || id;
}
function toggleClassFilter(id) {
var idx = activeClassFilters.indexOf(id);
if (idx >= 0) activeClassFilters.splice(idx, 1);
else activeClassFilters.push(id);
if (classFiltersEl) {
classFiltersEl
.querySelectorAll(".gla-tierlist-class-filter")
.forEach(function (btn) {
btn.classList.toggle(
"is-active",
activeClassFilters.indexOf(btn.dataset.class) !== -1,
);
});
}
applyVisibility();
}
function applyVisibility() {
var id;
for (id in charNodes) {
if (!Object.prototype.hasOwnProperty.call(charNodes, id)) continue;
var node = charNodes[id];
var ch = node.__ch;
if (!ch) continue;
node.classList.toggle("is-hidden", !matchesFilters(ch));
}
updateCounts();
}
function rarityRank(ch) {
if (!ch) return 99;
var n = RARITY_ORDER[ch.tier];
return typeof n === "number" ? n : 99;
}
function poolCompare(a, b) {
var ca = a && a.__ch;
var cb = b && b.__ch;
var ra = rarityRank(ca);
var rb = rarityRank(cb);
if (ra !== rb) return ra - rb;
var na = normalize(
(ca && ca.name) || (a && a.dataset && a.dataset.id) || "",
);
var nb = normalize(
(cb && cb.name) || (b && b.dataset && b.dataset.id) || "",
);
if (na < nb) return -1;
if (na > nb) return 1;
return 0;
}
/* Pool sempre ordenado: Diamante → Ouro → Prata → Bronze (e nome dentro da raridade). */
function insertIntoPool(node) {
if (!node || !poolEl) return;
var siblings = poolEl.children;
var i, other;
for (i = 0; i < siblings.length; i++) {
other = siblings[i];
if (other === node) continue;
if (
!other.classList ||
!other.classList.contains("gla-tierlist-char")
)
continue;
if (poolCompare(node, other) < 0) {
poolEl.insertBefore(node, other);
return;
}
}
poolEl.appendChild(node);
}
function sortPool() {
if (!poolEl) return;
var nodes = Array.prototype.slice.call(
poolEl.querySelectorAll(".gla-tierlist-char"),
);
nodes.sort(poolCompare);
nodes.forEach(function (n) {
poolEl.appendChild(n);
});
}
function placeChar(id, rank, beforeNode) {
if (!charNodes[id]) return;
var node = charNodes[id];
if (beforeNode === node) {
beforeNode = node.nextElementSibling;
}
if (rank && RANKS.indexOf(rank) !== -1) {
placement[id] = rank;
var slots = rowSlots[rank];
if (beforeNode && beforeNode.parentNode === slots) {
slots.insertBefore(node, beforeNode);
} else {
slots.appendChild(node);
}
} else {
delete placement[id];
insertIntoPool(node);
}
persistPlacement();
updateCounts();
}
function clearSelection() {
selectedId = null;
}
function selectChar(id) {
if (selectedId === id) {
clearSelection();
return;
}
selectedId = id;
}
function buildCharNode(ch) {
ch.__hay = normalize(
(ch.name || "") + " " + (ch.shortName || "") + " " + (ch.id || ""),
);
var btn = document.createElement("button");
btn.type = "button";
btn.className = "gla-tierlist-char";
btn.draggable = true;
btn.dataset.id = ch.id;
btn.dataset.gameTier = ch.tier || "";
btn.setAttribute("aria-label", ch.name);
btn.__ch = ch;
btn.innerHTML =
'<div class="gla-tierlist-char-art"><img alt="" width="96" height="96"></div>' +
'<span class="gla-tierlist-char-name" aria-hidden="true"></span>';
btn.querySelector(".gla-tierlist-char-name").textContent = ch.name;
bindCharImage(btn.querySelector("img"), ch);
btn.addEventListener("dragstart", function (ev) {
dragId = ch.id;
btn.classList.add("is-dragging");
try {
ev.dataTransfer.setData("text/plain", ch.id);
ev.dataTransfer.effectAllowed = "move";
} catch (e) {}
});
btn.addEventListener("dragend", function () {
btn.classList.remove("is-dragging");
dragId = null;
root.querySelectorAll(".is-drop-target").forEach(function (el) {
el.classList.remove("is-drop-target");
});
});
return btn;
}
function resolveDragId(ev) {
var id = dragId;
try {
id = ev.dataTransfer.getData("text/plain") || dragId;
} catch (e) {}
return id;
}
function bindPoolDrop(el) {
el.addEventListener("dragover", function (ev) {
ev.preventDefault();
el.classList.add("is-drop-target");
try {
ev.dataTransfer.dropEffect = "move";
} catch (e) {}
});
el.addEventListener("dragleave", function (ev) {
if (!el.contains(ev.relatedTarget)) {
el.classList.remove("is-drop-target");
}
});
el.addEventListener("drop", function (ev) {
ev.preventDefault();
el.classList.remove("is-drop-target");
var id = resolveDragId(ev);
if (id) {
placeChar(id, null);
clearSelection();
}
});
}
function bindTierDrop(row, slots, rank) {
function highlight(on) {
row.classList.toggle("is-drop-target", on);
slots.classList.toggle("is-drop-target", on);
}
slots.addEventListener("dragover", function (ev) {
ev.preventDefault();
ev.stopPropagation();
highlight(true);
try {
ev.dataTransfer.dropEffect = "move";
} catch (e) {}
});
slots.addEventListener("dragleave", function (ev) {
if (!slots.contains(ev.relatedTarget)) {
highlight(false);
}
});
slots.addEventListener("drop", function (ev) {
ev.preventDefault();
ev.stopPropagation();
highlight(false);
var id = resolveDragId(ev);
var before = getInsertBeforeNode(slots, ev.clientX, ev.clientY);
if (id) {
placeChar(id, rank, before);
clearSelection();
}
});
/* Drop no rótulo S/A/B/C/D → final da linha */
row.addEventListener("dragover", function (ev) {
if (slots.contains(ev.target)) return;
ev.preventDefault();
highlight(true);
try {
ev.dataTransfer.dropEffect = "move";
} catch (e) {}
});
row.addEventListener("dragleave", function (ev) {
if (!row.contains(ev.relatedTarget)) {
highlight(false);
}
});
row.addEventListener("drop", function (ev) {
if (slots.contains(ev.target)) return;
ev.preventDefault();
highlight(false);
var id = resolveDragId(ev);
if (id) {
placeChar(id, rank, null);
clearSelection();
}
});
}
function renderBoard() {
boardEl.textContent = "";
RANKS.forEach(function (rank) {
var row = document.createElement("div");
row.className = "gla-tierlist-row";
row.dataset.rank = rank;
var label = document.createElement("div");
label.className = "gla-tierlist-label";
label.textContent = rank;
label.title = rank;
label.addEventListener("click", function () {
if (selectedId) {
placeChar(selectedId, rank, null);
clearSelection();
}
});
var slots = document.createElement("div");
slots.className = "gla-tierlist-slots";
slots.dataset.slots = rank;
bindTierDrop(row, slots, rank);
row.appendChild(label);
row.appendChild(slots);
boardEl.appendChild(row);
rowSlots[rank] = slots;
});
}
function renderFilters() {
filtersEl.textContent = "";
GAME_TIERS.forEach(function (gt) {
var btn = document.createElement("button");
btn.type = "button";
btn.className =
"gla-tierlist-filter" + (gt.id === "all" ? " is-active" : "");
btn.dataset.tier = gt.id;
if (gt.id === "all") {
btn.innerHTML = '<span class="gla-tierlist-filter-label"></span>';
} else {
btn.innerHTML =
'<span class="gla-tierlist-filter-dot"></span><span class="gla-tierlist-filter-label"></span>';
}
btn.querySelector(".gla-tierlist-filter-label").textContent = gt.id;
btn.addEventListener("click", function () {
activeGameTier = gt.id;
filtersEl
.querySelectorAll(".gla-tierlist-filter")
.forEach(function (b) {
b.classList.toggle("is-active", b.dataset.tier === gt.id);
});
applyVisibility();
});
filtersEl.appendChild(btn);
});
}
function renderClassFilters() {
if (!classFiltersEl) return;
classFiltersEl.textContent = "";
CLASS_FILTERS.forEach(function (f) {
var label = classLabel(f.id);
var btn = document.createElement("button");
btn.type = "button";
btn.className = "gla-tierlist-class-filter";
btn.dataset.class = f.id;
btn.title = label;
btn.innerHTML = '<img alt=""><span></span>';
var img = btn.querySelector("img");
var span = btn.querySelector("span");
img.alt = label;
img.decoding = "async";
img.loading = "eager";
/* Mesmos arquivos da Dima (Arquivo:tank.png etc. na wiki oficial). */
img.src = medalFilePathURL(f.icon);
img.onerror = function () {
img.classList.add("is-missing");
btn.classList.add("gla-tierlist-class-filter--text-only");
};
span.textContent = label;
btn.addEventListener("click", function () {
toggleClassFilter(f.id);
});
classFiltersEl.appendChild(btn);
});
}
function mountCharacters() {
characters = readCharacterList();
var source = document.querySelector(".gla-tierlist-source");
if (source && source.parentNode) source.parentNode.removeChild(source);
var fragPool = document.createDocumentFragment();
characters.forEach(function (ch) {
if (!ch.id) return;
var node = buildCharNode(ch);
charNodes[ch.id] = node;
fragPool.appendChild(node);
});
poolEl.appendChild(fragPool);
RANKS.forEach(function (rank) {
var order = tierOrder[rank] || [];
order.forEach(function (id) {
if (!charNodes[id]) return;
placement[id] = rank;
rowSlots[rank].appendChild(charNodes[id]);
});
});
sortPool();
applyVisibility();
}
bindPoolDrop(poolEl);
/* Um listener pra todos os bonecos (em vez de 100+ clicks). */
root.addEventListener("click", function (ev) {
var btn = ev.target.closest(".gla-tierlist-char");
if (btn && root.contains(btn)) {
ev.preventDefault();
if (dragId) return;
var targetId = btn.dataset.id;
if (selectedId && selectedId !== targetId) {
var slots = btn.closest(".gla-tierlist-slots");
if (
slots &&
slots.dataset.slots &&
RANKS.indexOf(slots.dataset.slots) !== -1
) {
placeChar(selectedId, slots.dataset.slots, btn);
clearSelection();
return;
}
}
selectChar(targetId);
return;
}
var slotsEl = ev.target.closest(".gla-tierlist-slots");
if (
slotsEl &&
ev.target === slotsEl &&
selectedId &&
slotsEl.dataset.slots
) {
placeChar(selectedId, slotsEl.dataset.slots, null);
clearSelection();
return;
}
if (ev.target === poolEl && selectedId) {
placeChar(selectedId, null);
clearSelection();
}
});
if (searchInput) {
searchInput.addEventListener("input", function () {
clearTimeout(searchTimeout);
searchTimeout = setTimeout(function () {
searchTerm = searchInput.value || "";
applyVisibility();
}, 120);
});
}
if (downloadBtn) {
downloadBtn.addEventListener("click", function () {
exportTierListPng();
});
}
if (listNameInput) {
listNameInput.addEventListener("input", persistListName);
listNameInput.addEventListener("change", persistListName);
}
if (resetBtn) {
resetBtn.addEventListener("click", function () {
Object.keys(placement).forEach(function (id) {
delete placement[id];
if (charNodes[id]) insertIntoPool(charNodes[id]);
});
sortPool();
persistPlacement();
if (listNameInput) {
listNameInput.value = "";
persistListName();
}
clearSelection();
applyVisibility();
});
}
if (resetFiltersBtn) {
resetFiltersBtn.addEventListener("click", function () {
activeGameTier = "all";
activeClassFilters = [];
searchTerm = "";
if (searchInput) searchInput.value = "";
filtersEl
.querySelectorAll(".gla-tierlist-filter")
.forEach(function (b) {
b.classList.toggle("is-active", b.dataset.tier === "all");
});
if (classFiltersEl) {
classFiltersEl
.querySelectorAll(".gla-tierlist-class-filter")
.forEach(function (b) {
b.classList.remove("is-active");
});
}
applyVisibility();
});
}
window.addEventListener("gla:langChanged", function () {
applyStrings();
});
loadPlacement();
loadListName();
renderBoard();
renderFilters();
renderClassFilters();
applyStrings();
mountCharacters();
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", boot);
} else {
boot();
}
if (window.mw && mw.hook) {
mw.hook("wikipage.content").add(boot);
}
})();
</script>