Files
sao-items/sao.html
2026-06-06 15:54:47 +00:00

783 lines
48 KiB
HTML

<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SAO Items</title>
<link href="https://fonts.googleapis.com/css2?family=Cinzel:wght@400;600&family=Crimson+Pro:ital,wght@0,300;0,400;0,600;1,400&display=swap" rel="stylesheet">
<style>
*{box-sizing:border-box;margin:0;padding:0}
:root{
--bg:#0a0c0f;--bg2:#111418;--bg3:#181c22;--bg4:#1e242d;
--border:#2a2f3a;--border2:#3d4558;
--text:#ede8dc;--text2:#a09890;--text3:#5c5850;
--accent:#c8a96e;--accent2:#8b6d3e;
--c:#aaa;--b:#5aadff;--a:#cc88ff;--s:#ff7040;--ss:#ff3366;--splus:#ffaa00;--splusplus:#ff00aa;
}
body{background:var(--bg);color:var(--text);font-family:'Crimson Pro',Georgia,serif;min-height:100vh;font-size:16px}
/* UPLOAD SCREEN */
#upload-screen{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:100vh;padding:2rem}
.drop-zone{width:100%;max-width:500px;border:2px dashed var(--border2);border-radius:14px;padding:3rem 2rem;text-align:center;cursor:pointer;transition:all .2s;background:var(--bg2)}
.drop-zone:hover,.drop-zone.drag{border-color:var(--accent);background:var(--bg3)}
.drop-zone h2{font-family:'Cinzel',serif;font-size:1.3rem;color:var(--accent);margin-bottom:.6rem;letter-spacing:.08em}
.drop-zone p{font-size:1rem;color:var(--text3);margin-bottom:1.75rem;line-height:1.6}
.drop-zone input[type=file]{display:none}
.upload-btn{display:inline-block;padding:.65rem 1.6rem;background:var(--accent);color:#0a0c0f;border:none;border-radius:8px;font-size:.95rem;font-weight:700;cursor:pointer;font-family:'Cinzel',serif;letter-spacing:.05em;transition:background .15s}
.upload-btn:hover{background:#e0c080}
.upload-error{margin-top:1rem;color:var(--ss);font-size:.9rem;display:none}
/* EXPLORER */
#explorer-screen{display:none;flex-direction:column;height:100vh}
header{background:var(--bg2);border-bottom:1px solid var(--border);padding:1rem 1.75rem;display:flex;align-items:center;justify-content:space-between;gap:1rem;flex-shrink:0}
.logo{font-family:'Cinzel',serif;font-size:1.2rem;color:var(--accent);letter-spacing:.1em;white-space:nowrap}
.back-btn{color:var(--text3);font-size:.8rem;font-family:'Crimson Pro',serif;margin-left:1.25rem;text-decoration:none;border:1px solid var(--border);padding:4px 12px;border-radius:5px;cursor:pointer;background:none;transition:all .15s}
.back-btn:hover{border-color:var(--border2);color:var(--text2)}
#count{font-size:.9rem;color:var(--text3);white-space:nowrap}
.layout{display:flex;flex:1;overflow:hidden}
/* SIDEBAR */
aside{width:280px;flex-shrink:0;background:var(--bg2);border-right:1px solid var(--border);overflow-y:auto;padding:1.25rem 1rem}
aside::-webkit-scrollbar{width:3px}
aside::-webkit-scrollbar-thumb{background:var(--border2);border-radius:2px}
.fs{margin-bottom:1.4rem}
.ft{font-family:'Cinzel',serif;font-size:.62rem;letter-spacing:.22em;color:var(--accent2);text-transform:uppercase;margin-bottom:.65rem;padding-bottom:.4rem;border-bottom:1px solid var(--border)}
.search-wrap{position:relative;margin-bottom:1.4rem}
.search-wrap input{width:100%;background:var(--bg3);border:1px solid var(--border);border-radius:7px;padding:.65rem .65rem .65rem 2.2rem;color:var(--text);font-family:'Crimson Pro',serif;font-size:1rem;outline:none;transition:border-color .15s}
.search-wrap input:focus{border-color:var(--accent2)}
.search-wrap input::placeholder{color:var(--text3)}
.si{position:absolute;left:.65rem;top:50%;transform:translateY(-50%);color:var(--text3);pointer-events:none}
select{width:100%;background:var(--bg3);border:1px solid var(--border);border-radius:7px;padding:.55rem .75rem;color:var(--text);font-family:'Crimson Pro',serif;font-size:.95rem;outline:none;cursor:pointer;transition:border-color .15s}
select:focus{border-color:var(--accent2)}
select option{background:var(--bg3)}
.cg{display:flex;flex-direction:column;gap:2px;max-height:165px;overflow-y:auto}
.cg::-webkit-scrollbar{width:2px}
.cg::-webkit-scrollbar-thumb{background:var(--border);border-radius:2px}
.ci{display:flex;align-items:center;gap:8px;cursor:pointer;padding:4px 5px;border-radius:5px;transition:background .1s}
.ci:hover{background:var(--bg3)}
.ci input[type=checkbox]{width:14px;height:14px;accent-color:var(--accent);cursor:pointer;flex-shrink:0}
.ci label{font-size:.95rem;color:var(--text2);cursor:pointer;display:flex;align-items:center;width:100%;gap:5px}
.ci label .cnt{margin-left:auto;font-size:.75rem;color:var(--text3)}
.rw{display:flex;flex-direction:column;gap:7px}
.rr{display:flex;align-items:center;gap:8px}
.rr label{font-size:.82rem;color:var(--text3);min-width:30px}
.rr input[type=range]{flex:1;accent-color:var(--accent)}
.rr span{font-size:.85rem;color:var(--accent);min-width:22px;text-align:right;font-weight:600}
.sf-row{display:flex;gap:5px;align-items:center;margin-bottom:5px}
.sf-row select{flex:1;font-size:.8rem;padding:.32rem .4rem}
.sf-row input[type=number]{width:58px;background:var(--bg3);border:1px solid var(--border);border-radius:6px;padding:.32rem .4rem;color:var(--text);font-family:'Crimson Pro',serif;font-size:.8rem;outline:none}
.sf-row input[type=number]:focus{border-color:var(--accent2)}
.sf-row .del-sf{background:none;border:none;color:var(--ss);cursor:pointer;font-size:1rem;padding:0 2px;line-height:1;opacity:.7;transition:opacity .15s}
.sf-row .del-sf:hover{opacity:1}
.btn-add{background:none;border:1px dashed var(--border2);border-radius:6px;color:var(--text3);padding:.35rem .6rem;font-size:.85rem;cursor:pointer;width:100%;margin-top:4px;font-family:'Crimson Pro',serif;transition:all .15s}
.btn-add:hover{border-color:var(--accent2);color:var(--accent)}
.btn-reset{background:none;border:1px solid var(--border);border-radius:6px;color:var(--text3);padding:.45rem .9rem;font-size:.9rem;cursor:pointer;width:100%;font-family:'Crimson Pro',serif;transition:all .15s}
.btn-reset:hover{border-color:var(--ss);color:var(--ss)}
.r-c{color:var(--c)}.r-b{color:var(--b)}.r-a{color:var(--a)}.r-s{color:var(--s)}.r-ss{color:var(--ss)}.r-splus{color:var(--splus)}.r-splusplus{color:var(--splusplus)}
/* MAIN */
main{flex:1;overflow-y:auto;padding:1.25rem 1.5rem}
main::-webkit-scrollbar{width:4px}
main::-webkit-scrollbar-thumb{background:var(--border2);border-radius:2px}
.toolbar{display:flex;align-items:center;gap:10px;margin-bottom:1rem;flex-wrap:wrap}
.ti{font-size:.95rem;color:var(--text2);flex:1}
.ti strong{color:var(--accent)}
.vb{background:none;border:1px solid var(--border);border-radius:6px;padding:.38rem .65rem;color:var(--text3);cursor:pointer;font-size:.88rem;transition:all .12s}
.vb.active,.vb:hover{border-color:var(--accent2);color:var(--accent)}
/* GRID */
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr));gap:12px}
.card{background:var(--bg2);border:1px solid var(--border);border-radius:10px;padding:1rem;cursor:pointer;transition:border-color .15s,transform .15s;display:flex;flex-direction:column;gap:10px}
.card:hover{border-color:var(--border2);transform:translateY(-2px)}
.card-top{display:flex;align-items:center;gap:12px}
.item-img-box{width:64px;height:64px;flex-shrink:0;background:var(--bg4);border-radius:8px;border:1px solid var(--border);overflow:hidden;display:flex;align-items:center;justify-content:center}
.item-img-box img{width:64px;height:64px;object-fit:contain;display:block;image-rendering:pixelated;image-rendering:crisp-edges}
.item-img-box .no-img{font-size:28px;opacity:.2}
.card-info{flex:1;min-width:0}
.cn{font-family:'Cinzel',serif;font-size:.88rem;font-weight:600;color:var(--text);line-height:1.35;margin-bottom:3px}
.cc{font-size:.8rem;color:var(--text3);margin-bottom:6px}
.cb{display:flex;gap:4px;flex-wrap:wrap}
.rb{font-size:.68rem;padding:3px 7px;border-radius:4px;font-family:'Cinzel',serif;font-weight:600;letter-spacing:.04em;border:1px solid}
.rb-c{color:var(--c);border-color:rgba(170,170,170,.3);background:rgba(170,170,170,.07)}
.rb-b{color:var(--b);border-color:rgba(90,173,255,.3);background:rgba(90,173,255,.07)}
.rb-a{color:var(--a);border-color:rgba(204,136,255,.3);background:rgba(204,136,255,.07)}
.rb-s{color:var(--s);border-color:rgba(255,112,64,.3);background:rgba(255,112,64,.07)}
.rb-ss{color:var(--ss);border-color:rgba(255,51,102,.3);background:rgba(255,51,102,.07)}
.rb-splus{color:var(--splus);border-color:rgba(255,170,0,.3);background:rgba(255,170,0,.07)}
.rb-splusplus{color:var(--splusplus);border-color:rgba(255,0,170,.3);background:rgba(255,0,170,.07)}
.lb{font-size:.68rem;padding:3px 7px;border-radius:4px;background:rgba(200,169,110,.1);color:var(--accent);border:1px solid rgba(200,169,110,.22)}
.card-stats{display:flex;gap:5px;flex-wrap:wrap}
.cs{font-size:.75rem;padding:2px 7px;border-radius:4px;background:var(--bg4);color:var(--text2);border:1px solid var(--border)}
/* LIST */
.list{display:flex;flex-direction:column;gap:4px}
.li{background:var(--bg2);border:1px solid var(--border);border-radius:7px;padding:.65rem 1.1rem;cursor:pointer;display:flex;align-items:center;gap:12px;transition:all .12s}
.li:hover{border-color:var(--border2);background:var(--bg3)}
.li-img{width:48px;height:48px;background:var(--bg4);border-radius:6px;border:1px solid var(--border);overflow:hidden;display:flex;align-items:center;justify-content:center;flex-shrink:0}
.li-img img{width:48px;height:48px;object-fit:contain;display:block;image-rendering:pixelated;image-rendering:crisp-edges}
.li-img .no-img{font-size:18px;opacity:.2}
.ln{font-family:'Cinzel',serif;font-size:.9rem;color:var(--text);flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.lm{display:flex;align-items:center;gap:7px;flex-shrink:0}
.lc{font-size:.78rem;color:var(--text3);min-width:95px;text-align:right}
/* PAGINATION */
.empty{text-align:center;padding:5rem;color:var(--text3);font-family:'Cinzel',serif;font-size:1rem}
.empty p{font-family:'Crimson Pro',serif;font-size:.95rem;margin-top:.5rem;font-weight:300}
.load-more{text-align:center;padding:2rem;color:var(--text3);font-size:.9rem}
/* MODAL */
.mo{display:none;position:fixed;inset:0;background:rgba(0,0,0,.82);z-index:200;align-items:center;justify-content:center;backdrop-filter:blur(4px)}
.mo.open{display:flex}
.md{background:var(--bg2);border:1px solid var(--border2);border-radius:14px;width:600px;max-width:96vw;max-height:90vh;overflow-y:auto}
.md::-webkit-scrollbar{width:3px}
.md::-webkit-scrollbar-thumb{background:var(--border2);border-radius:2px}
.mh{padding:1.5rem 1.5rem 0;display:flex;gap:1.1rem;align-items:flex-start}
.m-img-box{width:96px;height:96px;background:var(--bg4);border-radius:10px;border:1px solid var(--border2);overflow:hidden;display:flex;align-items:center;justify-content:center;flex-shrink:0}
.m-img-box img{width:96px;height:96px;object-fit:contain;display:block;image-rendering:pixelated;image-rendering:crisp-edges}
.m-img-box .no-img{font-size:40px;opacity:.2}
.mti{font-family:'Cinzel',serif;font-size:1.3rem;color:var(--text);margin-bottom:5px;line-height:1.2}
.msu{font-size:.95rem;color:var(--text2);margin-bottom:8px}
.mcl{margin-left:auto;background:none;border:none;color:var(--text3);font-size:1.5rem;cursor:pointer;line-height:1;flex-shrink:0;transition:color .15s}
.mcl:hover{color:var(--text)}
.mb{padding:1.25rem 1.5rem 1.5rem;display:flex;flex-direction:column;gap:1.25rem}
.lore{font-style:italic;color:var(--text2);font-size:1rem;line-height:1.75;padding:.85rem 1.1rem;background:var(--bg3);border-radius:7px;border-left:3px solid var(--accent2)}
.sec-title{font-family:'Cinzel',serif;font-size:.62rem;letter-spacing:.2em;color:var(--accent2);text-transform:uppercase;margin-bottom:.65rem}
.stat-block{background:var(--bg3);border-radius:8px;overflow:hidden;border:1px solid var(--border)}
.stat-row-m{display:flex;align-items:center;justify-content:space-between;padding:.65rem 1rem;border-bottom:1px solid var(--border);transition:background .1s}
.stat-row-m:last-child{border-bottom:none}
.stat-row-m:hover{background:var(--bg4)}
.slabel{font-size:.95rem;color:var(--text2);display:flex;align-items:center;gap:.5rem}
.sicon{font-size:1rem}
.sval{font-size:1rem;font-weight:600;font-family:'Cinzel',serif}
.sval.pos{color:#5aff9a}
.sval.neg{color:var(--ss)}
.sval.neu{color:var(--text)}
.id-grid{display:flex;flex-wrap:wrap;gap:5px}
.id-tag{font-size:.85rem;padding:4px 10px;background:var(--bg4);border:1px solid var(--border);border-radius:5px;color:var(--text2)}
.id-tag strong{color:var(--text)}
.tech-grid{display:grid;grid-template-columns:1fr 1fr;gap:5px}
.tech-item{background:var(--bg4);border-radius:6px;padding:.6rem .85rem;border:1px solid var(--border)}
.tech-label{font-size:.68rem;color:var(--text3);text-transform:uppercase;letter-spacing:.07em;margin-bottom:3px}
.tech-val{font-size:.9rem;color:var(--text);word-break:break-all;font-family:'Cinzel',serif}
/* COMPARE BAR */
.cmp-bar{position:fixed;bottom:0;left:0;right:0;background:var(--bg2);border-top:1px solid var(--border2);z-index:100;padding:.75rem 1.5rem;display:none;gap:1rem;align-items:center}
.cmp-bar.visible{display:flex}
.cmp-slot{flex:1;background:var(--bg3);border:1px dashed var(--border2);border-radius:10px;padding:.5rem .75rem;display:flex;align-items:center;gap:10px;min-height:56px;transition:border-color .15s}
.cmp-slot.filled{border-style:solid;border-color:var(--accent2)}
.cmp-slot-img{width:36px;height:36px;background:var(--bg4);border-radius:6px;overflow:hidden;display:flex;align-items:center;justify-content:center;flex-shrink:0}
.cmp-slot-img img{width:36px;height:36px;object-fit:contain;image-rendering:pixelated;image-rendering:crisp-edges}
.cmp-slot-info{flex:1;min-width:0}
.cmp-slot-name{font-family:'Cinzel',serif;font-size:.8rem;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cmp-slot-sub{font-size:.72rem;color:var(--text3);margin-top:2px}
.cmp-slot-empty{font-size:.85rem;color:var(--text3);font-style:italic}
.cmp-slot-clear{background:none;border:none;color:var(--text3);cursor:pointer;font-size:.95rem;padding:2px 4px;flex-shrink:0;transition:color .15s}
.cmp-slot-clear:hover{color:var(--ss)}
.cmp-vs{font-family:'Cinzel',serif;font-size:1.1rem;color:var(--accent2);letter-spacing:.1em;flex-shrink:0}
.cmp-btn{background:var(--accent);color:#0a0c0f;border:none;border-radius:8px;padding:.55rem 1.25rem;font-family:'Cinzel',serif;font-size:.8rem;font-weight:700;letter-spacing:.06em;cursor:pointer;white-space:nowrap;transition:background .15s;flex-shrink:0}
.cmp-btn:hover{background:#e0c080}
.cmp-btn:disabled{opacity:.35;cursor:default}
.cmp-close{background:none;border:1px solid var(--border);border-radius:8px;padding:.55rem .85rem;color:var(--text3);font-size:.85rem;cursor:pointer;transition:all .15s;flex-shrink:0}
.cmp-close:hover{border-color:var(--ss);color:var(--ss)}
.card.pinned{border-color:var(--accent)}
.card.pinned::after{content:'✦';position:absolute;top:6px;right:8px;font-size:.7rem;color:var(--accent);font-family:'Cinzel',serif}
.card{position:relative}
.card-compare-btn{background:none;border:1px solid var(--border2);border-radius:5px;color:var(--text3);font-size:.7rem;padding:2px 8px;cursor:pointer;font-family:'Cinzel',serif;margin-top:2px;transition:all .15s;width:100%}
.card-compare-btn:hover,.card.pinned .card-compare-btn{border-color:var(--accent2);color:var(--accent)}
/* COMPARE MODAL */
.cmo{display:none;position:fixed;inset:0;background:rgba(0,0,0,.87);z-index:300;align-items:center;justify-content:center;backdrop-filter:blur(4px);padding:1.5rem}
.cmo.open{display:flex}
.cmd{background:var(--bg2);border:1px solid var(--border2);border-radius:14px;width:100%;max-width:820px;max-height:90vh;overflow:hidden;display:flex;flex-direction:column}
.cmd-header{padding:1rem 1.5rem;border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between;flex-shrink:0}
.cmd-title{font-family:'Cinzel',serif;font-size:.9rem;color:var(--accent);letter-spacing:.1em}
.cmd-close{background:none;border:none;color:var(--text3);font-size:1.4rem;cursor:pointer;transition:color .15s;line-height:1}
.cmd-close:hover{color:var(--text)}
/* Scrollable body */
.cmd-scroll{overflow-y:auto;flex:1}
.cmd-scroll::-webkit-scrollbar{width:3px}
.cmd-scroll::-webkit-scrollbar-thumb{background:var(--border2);border-radius:2px}
/* Item headers side by side */
.cmp-items{display:grid;grid-template-columns:1fr 1fr;border-bottom:1px solid var(--border)}
.cmp-item{padding:1rem 1.25rem;display:flex;gap:12px;align-items:center}
.cmp-item:first-child{border-right:1px solid var(--border)}
.cmp-img{width:64px;height:64px;background:var(--bg4);border-radius:8px;border:1px solid var(--border2);overflow:hidden;display:flex;align-items:center;justify-content:center;flex-shrink:0}
.cmp-img img{width:64px;height:64px;object-fit:contain;image-rendering:pixelated;image-rendering:crisp-edges}
.cmp-iname{font-family:'Cinzel',serif;font-size:.95rem;color:var(--text);line-height:1.25;margin-bottom:3px}
.cmp-isub{font-size:.8rem;color:var(--text2);margin-bottom:6px}
/* Lore side by side */
.cmp-lores{display:grid;grid-template-columns:1fr 1fr;border-bottom:1px solid var(--border)}
.cmp-lore{padding:.8rem 1.25rem;font-style:italic;color:var(--text2);font-size:.85rem;line-height:1.65;border-right:1px solid var(--border)}
.cmp-lore:last-child{border-right:none}
/* Stat rows: 3 cols [val A] [label] [val B] */
.cmp-sec-hdr{display:grid;grid-template-columns:1fr 200px 1fr;background:var(--bg4);border-top:1px solid var(--border)}
.cmp-sec-hdr span{padding:.4rem .85rem;font-family:'Cinzel',serif;font-size:.58rem;letter-spacing:.18em;color:var(--accent2);text-transform:uppercase}
.cmp-sec-hdr span.mid{text-align:center;background:var(--bg3);border-left:1px solid var(--border);border-right:1px solid var(--border)}
.cmp-row{display:grid;grid-template-columns:1fr 200px 1fr;border-top:1px solid rgba(255,255,255,.035)}
.cmp-row:hover{background:rgba(255,255,255,.02)}
.cmp-vl{padding:.45rem .85rem;font-family:'Cinzel',serif;font-size:.92rem;font-weight:600;display:flex;align-items:center;justify-content:flex-end}
.cmp-vr{padding:.45rem .85rem;font-family:'Cinzel',serif;font-size:.92rem;font-weight:600;display:flex;align-items:center;justify-content:flex-start}
.cmp-lbl{padding:.45rem .6rem;display:flex;align-items:center;justify-content:center;background:var(--bg3);border-left:1px solid var(--border);border-right:1px solid var(--border);font-size:.76rem;color:var(--text3);text-align:center;line-height:1.3}
.cv-better{color:#5aff9a}
.cv-worse{color:var(--ss)}
.cv-equal{color:var(--text2)}
.cv-absent{color:var(--text3);opacity:.3}
/* Summary bar */
.cmp-summary{display:flex;gap:10px;padding:.65rem 1.25rem;background:var(--bg4);border-top:1px solid var(--border);align-items:center;flex-wrap:wrap;flex-shrink:0}
.dsv{font-family:'Cinzel',serif;font-weight:600}
.dsv.pos{color:#5aff9a}
.winner-badge{font-size:.68rem;padding:2px 10px;border-radius:4px;font-family:'Cinzel',serif;font-weight:600;background:rgba(200,169,110,.12);color:var(--accent);border:1px solid rgba(200,169,110,.2);margin-left:auto}
</style>
</head>
<body>
<!-- UPLOAD SCREEN -->
<div id="upload-screen">
<div class="drop-zone" id="drop-zone">
<h2>⚔ SAO Items Explorer</h2>
<p>Glisse ton fichier <code>items.json</code> ici<br>ou clique pour le sélectionner</p>
<label class="upload-btn">Parcourir…<input type="file" id="file-input" accept=".json"></label>
<div class="upload-error" id="upload-error">Fichier invalide — assure-toi que c'est un tableau JSON d'items.</div>
</div>
</div>
<!-- EXPLORER SCREEN -->
<div id="explorer-screen">
<header>
<div class="logo">⚔ SAO Items <button class="back-btn" id="back-btn">← Changer le JSON</button></div>
<div id="count"></div>
</header>
<div class="layout">
<aside>
<div class="search-wrap">
<span class="si">🔍</span>
<input type="text" id="search" placeholder="Nom, clé MMO, lore…">
</div>
<div class="fs"><div class="ft">Tri</div>
<select id="sort">
<option value="name-asc">Nom A → Z</option>
<option value="name-desc">Nom Z → A</option>
<option value="level-desc">Niveau décroissant</option>
<option value="level-asc">Niveau croissant</option>
<option value="rarity-desc">Rareté décroissante</option>
<option value="rarity-asc">Rareté croissante</option>
<option value="id-asc">ID croissant</option>
<option value="id-desc">ID décroissant</option>
</select>
</div>
<div class="fs"><div class="ft">Rareté</div><div class="cg" id="f-rarity"></div></div>
<div class="fs"><div class="ft">Catégorie</div><div class="cg" id="f-cat"></div></div>
<div class="fs"><div class="ft">Niveau</div>
<div class="rw">
<div class="rr"><label>Min</label><input type="range" id="lmin" min="1" max="30" value="1"><span id="lminv">1</span></div>
<div class="rr"><label>Max</label><input type="range" id="lmax" min="1" max="30" value="30"><span id="lmaxv">30</span></div>
</div>
</div>
<div class="fs"><div class="ft">Filtre stats</div>
<div id="sf"></div>
<button class="btn-add" id="add-sf">+ Ajouter un filtre</button>
</div>
<div class="fs"><div class="ft">Options</div>
<div class="cg">
<div class="ci"><input type="checkbox" id="o-lore"><label for="o-lore">Avec lore</label></div>
<div class="ci"><input type="checkbox" id="o-img"><label for="o-img">Avec image</label></div>
<div class="ci"><input type="checkbox" id="o-stats"><label for="o-stats">Avec stats combat</label></div>
<div class="ci"><input type="checkbox" id="o-ids"><label for="o-ids">Avec identifications</label></div>
</div>
</div>
<button class="btn-reset" id="reset">↺ Réinitialiser</button>
</aside>
<main>
<div class="toolbar">
<div class="ti" id="ti"></div>
<button class="vb active" id="vg">⊞ Grille</button>
<button class="vb" id="vl">☰ Liste</button>
</div>
<div id="items"></div>
<div id="sentinel" style="height:1px"></div>
</main>
</div>
</div>
<!-- COMPARE BAR -->
<div class="cmp-bar" id="cmp-bar">
<div class="cmp-slot" id="cmp-slot-0"><span class="cmp-slot-empty">Clique ⊕ sur un item</span></div>
<div class="cmp-vs">VS</div>
<div class="cmp-slot" id="cmp-slot-1"><span class="cmp-slot-empty">Clique ⊕ sur un item</span></div>
<button class="cmp-btn" id="cmp-go" disabled>⚔ Comparer</button>
<button class="cmp-close" id="cmp-close-bar">✕ Annuler</button>
</div>
<!-- COMPARE MODAL -->
<div class="cmo" id="cmp-modal">
<div class="cmd">
<div class="cmd-header">
<span class="cmd-title">⚔ Comparaison</span>
<button class="cmd-close" id="cmp-modal-close"></button>
</div>
<div class="cmd-scroll">
<div id="cmp-table"></div>
</div>
<div class="cmp-summary" id="cmp-summary"></div>
</div>
</div>
<!-- MODAL -->
<div class="mo" id="modal"><div class="md" id="mc"></div></div>
<script>
const IMG='https://saominecraft.com';
const RO={c:0,b:1,a:2,s:3,ss:4,splus:5,splusplus:6};
const RL={c:'Common',b:'Rare',a:'Artefact',s:'Unique S',ss:'S+',splus:'S++',splusplus:'S★'};
const CL={amulet:'Amulette',armor:'Armure',artifact:'Artefact',bow:'Arc',bracelet:'Bracelet',catalyst:'Catalyst',consumable:'Consommable',consumable_item:'Consommable',crossbow:'Arbalète',crystal:'Cristal',dagger:'Dague',dungeon_key:'Clé Donjon',farmer_job:'Agriculture',fishing_job:'Pêche',food:'Nourriture',fortifier:'Fortifiant',gem_stone:'Gemme',gloves:'Gants',halloween:'Halloween',key_crate:'Clé Caisse',lumberjack_job:'Bûcheron',lunar:'Lunaire',material:'Matériau',miner_job:'Mineur',noel2025:'Noël 2025',potion_heal:'Potion Soin',potion_stamina:'Potion Stamina',ring:'Anneau',saint_valentin:'St-Valentin',shield:'Bouclier',staff_mage:'Bâton Mage',staff_shaman:'Bâton Chaman',sword:'Épée',tool:'Outil'};
const SICON={'Attack Speed':'⚡','Max Health':'❤','Defense':'🛡','Movement Speed':'💨','Critical Strike Chance':'🎯','Critical Strike Power':'💥','Cooldown Reduction':'⏱','Max Mana':'🔮','Mana Regeneration':'✨','Stamina Regeneration':'🔋','Physical Damage':'⚔','Magic Damage':'🌀','Skill Damage':'🔥','Lifesteal':'🩸','Health Regeneration':'💚','Max Stamina':'💪','Skill Critical Strike Chance':'🎯','Skill Critical Strike Power':'💥','HEAL_BONUS':'💊','Item Cooldown':'⏰','Projectile Damage':'🏹'};
const BORING=new Set(['Disable Crafting','Disable Smelting','Disable Interaction','Disable Smithing','Disable Repairing','Disable Enchanting','Disable Droping','Arme à deux mains','Hide Enchants']);
let ITEMS=[], filtered=[], rendered=0, BATCH=30, view='grid', sfCount=0, loading=false;
let ALL_STAT_KEYS=[], observer;
// ── IMAGE HELPER ──────────────────────────────────────────
function makeImgBox(item, cssClass) {
const box = document.createElement('div');
box.className = cssClass;
if (item.image_url) {
const img = document.createElement('img');
img.src = IMG + item.image_url;
img.alt = item.name;
img.style.cssText = 'width:100%;height:100%;object-fit:contain;display:block;image-rendering:pixelated;image-rendering:crisp-edges';
img.addEventListener('error', () => {
box.innerHTML = '<span class="no-img">📦</span>';
});
box.appendChild(img);
} else {
box.innerHTML = '<span class="no-img">📦</span>';
}
return box;
}
// ── UPLOAD ────────────────────────────────────────────────
function loadJSON(file) {
const reader = new FileReader();
reader.onload = e => {
try {
const data = JSON.parse(e.target.result);
const items = Array.isArray(data) ? data : data?.data?.items;
if (!items || !items.length) throw new Error();
ITEMS = items;
initExplorer();
} catch {
document.getElementById('upload-error').style.display = 'block';
}
};
reader.readAsText(file);
}
const dz = document.getElementById('drop-zone');
const fi = document.getElementById('file-input');
dz.addEventListener('dragover', e => { e.preventDefault(); dz.classList.add('drag'); });
dz.addEventListener('dragleave', () => dz.classList.remove('drag'));
dz.addEventListener('drop', e => { e.preventDefault(); dz.classList.remove('drag'); if (e.dataTransfer.files[0]) loadJSON(e.dataTransfer.files[0]); });
fi.addEventListener('change', () => { if (fi.files[0]) loadJSON(fi.files[0]); });
dz.addEventListener('click', e => { if (!e.target.closest('label')) fi.click(); });
document.getElementById('back-btn').addEventListener('click', () => {
document.getElementById('explorer-screen').style.display = 'none';
document.getElementById('upload-screen').style.display = 'flex';
});
// ── INIT EXPLORER ─────────────────────────────────────────
function initExplorer() {
document.getElementById('upload-screen').style.display = 'none';
document.getElementById('explorer-screen').style.display = 'flex';
ALL_STAT_KEYS = [...new Set(ITEMS.flatMap(i => Object.keys(i.identifications || {})))].sort();
const rarities = [...new Set(ITEMS.map(i => i.rarity))].sort((a,b) => (RO[a]||0)-(RO[b]||0));
const rc = {}; ITEMS.forEach(i => rc[i.rarity] = (rc[i.rarity]||0)+1);
document.getElementById('f-rarity').innerHTML = rarities.map(r =>
`<div class="ci"><input type="checkbox" class="fr" id="r-${r}" value="${r}"><label for="r-${r}"><span class="r-${r}">${RL[r]||r}</span><span class="cnt">${rc[r]}</span></label></div>`).join('');
const cats = [...new Set(ITEMS.map(i => i.category))].sort();
const cc = {}; ITEMS.forEach(i => cc[i.category] = (cc[i.category]||0)+1);
document.getElementById('f-cat').innerHTML = cats.map(c =>
`<div class="ci"><input type="checkbox" class="fc" id="c-${c}" value="${c}"><label for="c-${c}">${CL[c]||c}<span class="cnt">${cc[c]}</span></label></div>`).join('');
document.querySelectorAll('.fr,.fc,#o-lore,#o-img,#o-stats,#o-ids').forEach(el => el.addEventListener('change', applyF));
setupObserver();
applyF();
}
// ── STAT FILTER ───────────────────────────────────────────
function addSF() {
const id = ++sfCount;
const d = document.createElement('div');
d.className = 'sf-row'; d.dataset.id = id;
d.innerHTML = `<select class="sk"><option value="">— Stat —</option>${ALL_STAT_KEYS.map(k=>`<option value="${k}">${k}</option>`).join('')}</select><select class="so"><option value="gte">≥</option><option value="lte">≤</option><option value="eq">=</option></select><input type="number" class="sv" value="0" step="0.1"><button class="del-sf">✕</button>`;
d.querySelector('.del-sf').addEventListener('click', () => { d.remove(); applyF(); });
d.querySelectorAll('select,input').forEach(el => el.addEventListener('change', applyF));
document.getElementById('sf').appendChild(d);
}
document.getElementById('add-sf').addEventListener('click', addSF);
const lmin = document.getElementById('lmin'), lmax = document.getElementById('lmax');
const lminv = document.getElementById('lminv'), lmaxv = document.getElementById('lmaxv');
lmin.addEventListener('input', () => { lminv.textContent = lmin.value; if (+lmin.value > +lmax.value) { lmax.value = lmin.value; lmaxv.textContent = lmin.value; } applyF(); });
lmax.addEventListener('input', () => { lmaxv.textContent = lmax.value; if (+lmax.value < +lmin.value) { lmin.value = lmax.value; lminv.textContent = lmax.value; } applyF(); });
let deb;
document.getElementById('search').addEventListener('input', () => { clearTimeout(deb); deb = setTimeout(applyF, 220); });
document.getElementById('sort').addEventListener('change', applyF);
// ── FILTER + SORT ─────────────────────────────────────────
function applyF() {
const q = document.getElementById('search').value.trim().toLowerCase();
const sr = [...document.querySelectorAll('.fr:checked')].map(e => e.value);
const sc = [...document.querySelectorAll('.fc:checked')].map(e => e.value);
const mn = +lmin.value, mx = +lmax.value;
const oL = document.getElementById('o-lore').checked;
const oI = document.getElementById('o-img').checked;
const oS = document.getElementById('o-stats').checked;
const oD = document.getElementById('o-ids').checked;
const sfs = [...document.querySelectorAll('#sf .sf-row')].map(r => ({key:r.querySelector('.sk').value,op:r.querySelector('.so').value,val:+r.querySelector('.sv').value})).filter(f => f.key);
const sort = document.getElementById('sort').value;
filtered = ITEMS.filter(item => {
if (q && !item.name.toLowerCase().includes(q) && !(item.mmo_key||'').toLowerCase().includes(q) && !(item.lore||'').toLowerCase().includes(q)) return false;
if (sr.length && !sr.includes(item.rarity)) return false;
if (sc.length && !sc.includes(item.category)) return false;
if (item.level < mn || item.level > mx) return false;
if (oL && !(item.lore && item.lore.trim())) return false;
if (oI && !item.image_url) return false;
if (oS && !item.damage && !item.health && !item.defense) return false;
if (oD && !Object.keys(item.identifications||{}).length) return false;
for (const f of sfs) { const v = item.identifications[f.key]; if (v===undefined||v===null) return false; if (f.op==='gte'&&v<f.val) return false; if (f.op==='lte'&&v>f.val) return false; if (f.op==='eq'&&v!==f.val) return false; }
return true;
});
filtered.sort((a, b) => {
switch (sort) {
case 'name-asc': return a.name.localeCompare(b.name, 'fr');
case 'name-desc': return b.name.localeCompare(a.name, 'fr');
case 'level-asc': return a.level - b.level;
case 'level-desc': return b.level - a.level;
case 'rarity-asc': return (RO[a.rarity]||0)-(RO[b.rarity]||0);
case 'rarity-desc': return (RO[b.rarity]||0)-(RO[a.rarity]||0);
case 'id-asc': return a.id - b.id;
case 'id-desc': return b.id - a.id;
} return 0;
});
render();
}
// ── HELPERS ───────────────────────────────────────────────
function rb(r) { return `<span class="rb rb-${r}">${RL[r]||r}</span>`; }
function getCardStats(item) {
return Object.entries(item.identifications||{}).filter(([k]) => !BORING.has(k) && !k.startsWith('Required ') && k !== 'Classe requise').slice(0,3)
.map(([k,v]) => `<span class="cs">${SICON[k]||'◆'} ${k}: <strong>${v>0?'+':''}${v}</strong></span>`).join('');
}
// ── RENDER (infinite scroll) ──────────────────────────────
function render() {
rendered = 0;
document.getElementById('ti').innerHTML = `<strong>${filtered.length.toLocaleString('fr')}</strong> item${filtered.length>1?'s':''}`;
document.getElementById('count').textContent = `${filtered.length} / ${ITEMS.length} items`;
const cont = document.getElementById('items');
cont.innerHTML = '';
if (!filtered.length) {
cont.innerHTML = `<div class="empty">✦ Aucun résultat<p>Modifiez vos filtres</p></div>`;
return;
}
const wrap = document.createElement('div');
wrap.id = 'item-wrap';
wrap.className = view === 'grid' ? 'grid' : 'list';
cont.appendChild(wrap);
loadMore();
}
function loadMore() {
if (loading || rendered >= filtered.length) return;
loading = true;
const wrap = document.getElementById('item-wrap');
if (!wrap) { loading = false; return; }
const batch = filtered.slice(rendered, rendered + BATCH);
batch.forEach(item => {
if (view === 'grid') {
const card = document.createElement('div');
card.className = 'card';
card.addEventListener('click', () => openM(item.id));
const top = document.createElement('div');
top.className = 'card-top';
top.appendChild(makeImgBox(item, 'item-img-box'));
const info = document.createElement('div');
info.className = 'card-info';
info.innerHTML = `<div class="cn">${item.name}</div><div class="cc">${CL[item.category]||item.category}</div><div class="cb">${rb(item.rarity)}${item.level>1?`<span class="lb">Niv.${item.level}</span>`:''}</div>`;
top.appendChild(info);
card.appendChild(top);
const cs = getCardStats(item);
if (cs) { const d = document.createElement('div'); d.className = 'card-stats'; d.innerHTML = cs; card.appendChild(d); }
// compare button
const cmpBtn = document.createElement('button');
cmpBtn.className = 'card-compare-btn';
cmpBtn.textContent = cmpItems.some(x => x?.id === item.id) ? '✦ Sélectionné' : '⊕ Comparer';
cmpBtn.addEventListener('click', e => { e.stopPropagation(); toggleCompare(item); cmpBtn.textContent = cmpItems.some(x => x?.id === item.id) ? '✦ Sélectionné' : '⊕ Comparer'; card.classList.toggle('pinned', cmpItems.some(x => x?.id === item.id)); });
card.appendChild(cmpBtn);
wrap.appendChild(card);
} else {
const li = document.createElement('div');
li.className = 'li';
li.addEventListener('click', () => openM(item.id));
li.appendChild(makeImgBox(item, 'li-img'));
const txt = document.createElement('div');
txt.className = 'ln'; txt.textContent = item.name;
const meta = document.createElement('div');
meta.className = 'lm';
meta.innerHTML = `${rb(item.rarity)}${item.level>1?`<span class="lb">Niv.${item.level}</span>`:''}<span class="lc">${CL[item.category]||item.category}</span>`;
li.appendChild(txt); li.appendChild(meta);
wrap.appendChild(li);
}
});
rendered += batch.length;
loading = false;
}
// IntersectionObserver on sentinel
function setupObserver() {
if (observer) observer.disconnect();
observer = new IntersectionObserver(entries => {
if (entries[0].isIntersecting) loadMore();
}, { rootMargin: '300px' });
observer.observe(document.getElementById('sentinel'));
}
// ── MODAL ─────────────────────────────────────────────────
function openM(id) {
const item = ITEMS.find(i => i.id===id); if (!item) return;
const ids = Object.entries(item.identifications||{});
const mainStats = [[item.damage,'⚔ Dégâts'],[item.health,'❤ Santé'],[item.defense,'🛡 Défense']].filter(([v]) => v!==null&&v!==undefined);
const usefulIds = ids.filter(([k]) => !BORING.has(k) && !k.startsWith('Required ') && k!=='Classe requise');
const reqIds = ids.filter(([k]) => k.startsWith('Required ') || k==='Classe requise');
const boringIds = ids.filter(([k]) => BORING.has(k));
function statRow(label, val) {
const n = parseFloat(val);
const cls = isNaN(n)?'neu':n>0?'pos':n<0?'neg':'neu';
const disp = isNaN(n)?val:(n>0?'+'+n:String(n));
return `<div class="stat-row-m"><span class="slabel"><span class="sicon">${SICON[label]||'◆'}</span>${label}</span><span class="sval ${cls}">${disp}</span></div>`;
}
const mc = document.getElementById('mc');
mc.innerHTML = '';
// Header
const mh = document.createElement('div'); mh.className = 'mh';
mh.appendChild(makeImgBox(item, 'm-img-box'));
mh.innerHTML += `<div style="flex:1;min-width:0"><div class="mti">${item.name}</div><div class="msu">${CL[item.category]||item.category} · Niveau ${item.level}</div><div style="display:flex;gap:5px;flex-wrap:wrap">${rb(item.rarity)}${item.level>1?`<span class="lb">Niveau ${item.level}</span>`:''}</div></div><button class="mcl" id="close-modal">✕</button>`;
mc.appendChild(mh);
// Body
const mb = document.createElement('div'); mb.className = 'mb';
if (item.lore) { const d=document.createElement('div'); d.className='lore'; d.innerHTML=item.lore.replace(/\n/g,'<br>'); mb.appendChild(d); }
if (mainStats.length) {
const s=document.createElement('div');
s.innerHTML=`<div class="sec-title">Stats de combat</div><div class="stat-block"><div class="stat-list">${mainStats.map(([v,l])=>statRow(l,v)).join('')}</div></div>`;
mb.appendChild(s);
}
if (usefulIds.length) {
const s=document.createElement('div');
s.innerHTML=`<div class="sec-title">Statistiques (${usefulIds.length})</div><div class="stat-block"><div class="stat-list">${usefulIds.map(([k,v])=>statRow(k,v)).join('')}</div></div>`;
mb.appendChild(s);
}
if (reqIds.length) {
const s=document.createElement('div');
s.innerHTML=`<div class="sec-title">Prérequis</div><div class="id-grid">${reqIds.map(([k,v])=>`<span class="id-tag"><strong>${k.replace('Required ','')}</strong>: ${v}</span>`).join('')}</div>`;
mb.appendChild(s);
}
if (boringIds.length) {
const s=document.createElement('div');
s.innerHTML=`<div class="sec-title">Restrictions</div><div class="id-grid">${boringIds.map(([k])=>`<span class="id-tag" style="opacity:.5">${k}</span>`).join('')}</div>`;
mb.appendChild(s);
}
const tech=document.createElement('div');
tech.innerHTML=`<div class="sec-title">Infos techniques</div><div class="tech-grid"><div class="tech-item"><div class="tech-label">ID</div><div class="tech-val">${item.id}</div></div><div class="tech-item"><div class="tech-label">Matériau MMO</div><div class="tech-val">${item.mmo_material||'—'}</div></div><div class="tech-item"><div class="tech-label">Clé MMO</div><div class="tech-val" style="font-size:.78rem">${item.mmo_key||'—'}</div></div><div class="tech-item"><div class="tech-label">CMD</div><div class="tech-val">${item.mmo_cmd??'—'}</div></div></div>`;
mb.appendChild(tech);
mc.appendChild(mb);
document.getElementById('close-modal').addEventListener('click', closeM);
document.getElementById('modal').classList.add('open');
}
function closeM() { document.getElementById('modal').classList.remove('open'); }
document.getElementById('modal').addEventListener('click', e => { if (e.target===document.getElementById('modal')) closeM(); });
document.addEventListener('keydown', e => { if (e.key==='Escape') closeM(); });
document.getElementById('vg').addEventListener('click', () => { view='grid'; document.getElementById('vg').classList.add('active'); document.getElementById('vl').classList.remove('active'); render(); });
document.getElementById('vl').addEventListener('click', () => { view='list'; document.getElementById('vl').classList.add('active'); document.getElementById('vg').classList.remove('active'); render(); });
document.getElementById('reset').addEventListener('click', () => {
document.getElementById('search').value='';
document.querySelectorAll('.fr,.fc,#o-lore,#o-img,#o-stats,#o-ids').forEach(el => el.checked=false);
lmin.value=1; lmax.value=30; lminv.textContent=1; lmaxv.textContent=30;
document.getElementById('sf').innerHTML='';
document.getElementById('sort').value='name-asc';
applyF();
});
// ── COMPARE SYSTEM ────────────────────────────────────────
const cmpItems = [null, null];
function toggleCompare(item) {
const idx = cmpItems.findIndex(x => x?.id === item.id);
if (idx !== -1) { cmpItems[idx] = null; }
else {
const free = cmpItems.findIndex(x => x === null);
cmpItems[free === -1 ? 1 : free] = item;
}
renderCmpBar();
}
function renderCmpBar() {
const bar = document.getElementById('cmp-bar');
const hasSome = cmpItems.some(Boolean);
bar.className = 'cmp-bar' + (hasSome ? ' visible' : '');
document.querySelector('main').style.paddingBottom = hasSome ? '88px' : '';
[0,1].forEach(i => {
const slot = document.getElementById('cmp-slot-'+i);
const item = cmpItems[i];
slot.innerHTML = '';
slot.className = 'cmp-slot' + (item ? ' filled' : '');
if (!item) { slot.innerHTML = '<span class="cmp-slot-empty">Clique ⊕ sur un item</span>'; return; }
const imgBox = document.createElement('div'); imgBox.className = 'cmp-slot-img';
if (item.image_url) {
const img = document.createElement('img'); img.src = IMG+item.image_url; img.alt = item.name;
img.style.cssText = 'width:36px;height:36px;object-fit:contain;image-rendering:pixelated;image-rendering:crisp-edges';
img.onerror = () => { imgBox.textContent = '📦'; };
imgBox.appendChild(img);
} else imgBox.textContent = '📦';
const info = document.createElement('div'); info.className = 'cmp-slot-info';
info.innerHTML = `<div class="cmp-slot-name">${item.name}</div><div class="cmp-slot-sub">${rb(item.rarity)} ${CL[item.category]||item.category}</div>`;
const clr = document.createElement('button'); clr.className = 'cmp-slot-clear'; clr.textContent = '✕';
clr.addEventListener('click', () => { cmpItems[i]=null; renderCmpBar(); });
slot.appendChild(imgBox); slot.appendChild(info); slot.appendChild(clr);
});
document.getElementById('cmp-go').disabled = !(cmpItems[0] && cmpItems[1]);
}
document.getElementById('cmp-go').addEventListener('click', openCmpModal);
document.getElementById('cmp-close-bar').addEventListener('click', () => { cmpItems[0]=null; cmpItems[1]=null; renderCmpBar(); });
document.getElementById('cmp-modal-close').addEventListener('click', () => document.getElementById('cmp-modal').classList.remove('open'));
document.getElementById('cmp-modal').addEventListener('click', e => { if(e.target===document.getElementById('cmp-modal')) document.getElementById('cmp-modal').classList.remove('open'); });
function getStatVal(item, key) {
if (key==='⚔ Dégâts') return item.damage??null;
if (key==='❤ Santé') return item.health??null;
if (key==='🛡 Défense') return item.defense??null;
return item.identifications?.[key]??null;
}
function openCmpModal() {
const [a,b] = cmpItems; if (!a||!b) return;
const allKeys = new Set();
[a,b].forEach(item => {
if (item.damage!=null) allKeys.add('⚔ Dégâts');
if (item.health!=null) allKeys.add('❤ Santé');
if (item.defense!=null) allKeys.add('🛡 Défense');
Object.keys(item.identifications||{}).forEach(k => { if (!BORING.has(k)) allKeys.add(k); });
});
const statKeys = [...allKeys].filter(k => !k.startsWith('Required ') && k!=='Classe requise');
const reqKeys = [...new Set([
...Object.keys(a.identifications||{}).filter(k=>k.startsWith('Required ')||k==='Classe requise'),
...Object.keys(b.identifications||{}).filter(k=>k.startsWith('Required ')||k==='Classe requise'),
])];
const table = document.getElementById('cmp-table');
table.innerHTML = '';
// ── Item headers ──
const items = document.createElement('div'); items.className = 'cmp-items';
[a,b].forEach((item,i) => {
const cell = document.createElement('div'); cell.className = 'cmp-item';
const box = document.createElement('div'); box.className = 'cmp-img';
if (item.image_url) {
const img = document.createElement('img'); img.src = IMG+item.image_url; img.alt = item.name;
img.style.cssText = 'width:64px;height:64px;object-fit:contain;image-rendering:pixelated;image-rendering:crisp-edges';
img.onerror = () => { box.textContent = '📦'; };
box.appendChild(img);
} else box.textContent = '📦';
const info = document.createElement('div');
info.innerHTML = `<div class="cmp-iname">${item.name}</div><div class="cmp-isub">${CL[item.category]||item.category} · Niv.${item.level}</div><div style="display:flex;gap:4px">${rb(item.rarity)}</div>`;
cell.appendChild(box); cell.appendChild(info);
items.appendChild(cell);
});
table.appendChild(items);
// ── Lore (only if at least one has it) ──
if (a.lore || b.lore) {
const lores = document.createElement('div'); lores.className = 'cmp-lores';
[a,b].forEach(item => {
const cell = document.createElement('div'); cell.className = 'cmp-lore';
cell.innerHTML = item.lore ? item.lore.replace(/\n/g,'<br>') : '';
lores.appendChild(cell);
});
table.appendChild(lores);
}
// ── Stat rows ──
function getV(item, key) {
if (key==='⚔ Dégâts') return item.damage??null;
if (key==='❤ Santé') return item.health??null;
if (key==='🛡 Défense') return item.defense??null;
return item.identifications?.[key]??null;
}
let totalAW=0, totalBW=0;
function addSection(keys, title) {
if (!keys.length) return;
// Section header
const sec = document.createElement('div'); sec.className = 'cmp-sec-hdr';
sec.innerHTML = `<span>${title}</span><span class="mid">${title}</span><span>${title}</span>`;
table.appendChild(sec);
keys.forEach(k => {
const va=getV(a,k), vb=getV(b,k);
const na=parseFloat(va), nb=parseFloat(vb);
const bothNum = !isNaN(na)&&!isNaN(nb)&&va!==null&&vb!==null;
let clsA='cv-equal', clsB='cv-equal';
if (bothNum) {
if (na>nb){clsA='cv-better';clsB='cv-worse';totalAW++;}
else if (nb>na){clsB='cv-better';clsA='cv-worse';totalBW++;}
}
if (va===null) clsA='cv-absent';
if (vb===null) clsB='cv-absent';
const fmtA = va===null ? '—' : (typeof va==='number'&&va>0 ? '+'+va : String(va));
const fmtB = vb===null ? '—' : (typeof vb==='number'&&vb>0 ? '+'+vb : String(vb));
const row = document.createElement('div'); row.className = 'cmp-row';
row.innerHTML = `
<div class="cmp-vl"><span class="${clsA}">${fmtA}</span></div>
<div class="cmp-lbl">${SICON[k]||'◆'} ${k}</div>
<div class="cmp-vr"><span class="${clsB}">${fmtB}</span></div>`;
table.appendChild(row);
});
}
addSection(statKeys, 'Statistiques');
addSection(reqKeys, 'Prérequis');
const winner = totalAW>totalBW?a.name:totalBW>totalAW?b.name:null;
document.getElementById('cmp-summary').innerHTML = `
<span style="font-size:.83rem;color:var(--text2)">${a.name} <span class="dsv pos">+${totalAW}</span></span>
<span style="color:var(--text3);font-size:.78rem">stats gagnées</span>
<span style="color:var(--border2)">·</span>
<span style="font-size:.83rem;color:var(--text2)">${b.name} <span class="dsv pos">+${totalBW}</span></span>
${winner ? `<span class="winner-badge">✦ ${winner} l'emporte</span>` : `<span class="winner-badge">Égalité</span>`}
`;
document.getElementById('cmp-modal').classList.add('open');
}
</script>
</body>
</html>