MediaWiki:Common.css
Ir para navegação
Ir para pesquisar
Nota: Após salvar, você pode ter que limpar o "cache" do seu navegador para ver as alterações.
- Firefox / Safari: Pressione Shift enquanto clica Recarregar, ou pressione Ctrl-F5 ou Ctrl-R (⌘-R no Mac)
- Google Chrome: Pressione Ctrl-Shift-R (⌘-Shift-R no Mac)
- Internet Explorer/Edge: PressioneCtrl enquanto clica Recarregar, ou Pressione Ctrl-F5
- Opera: Pressione Ctrl-F5.
<!--
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>