Widget:PartyBuilder

 Remove Remove all

 // data sources

const getPage = url => fetch(url) .then(res => res.text) .then(s => {       const parser = new DOMParser;        return parser.parseFromString(s, 'text/html');    });

const data = { charactersFront: null, charactersBack: null, mirrors: null };

let loaded = false; const load = async => { const charactersBase = await getPage('/wiki/Category:Characters') .then(p => [...p.querySelector('table.wikitable').querySelectorAll('tr')]           .slice(1) // remove header row            .map(r => ({                code: r.querySelector('img').alt.slice(3, 10),                name: r.querySelector('a').title,                imgSrc: r.querySelector('img').src,                position: r.querySelectorAll('td')[5].textContent            }))); data.charactersFront = charactersBase.filter(c => c.position == 'Front'); data.charactersBack = charactersBase.filter(c => c.position == 'Back');

data.mirrors = await getPage('/wiki/Category:Mirrors') .then(p => [...p.querySelector('table.wikitable').querySelectorAll('tr')]           .slice(1) // remove header row            .slice(0, -2) // remove upgrade mirrors            .map(r => ({                code: r.querySelector('img').alt.slice(3, 9),                name: r.querySelector('a').title,                imgSrc: r.querySelector('img').src            })));

loaded = true; };

const paramMap = ['1M', '2M', '1C', '2C', '3M', '4M', '5M', '6M', '3C', '4C', '7M', '8M', '9M', '10M', '5C', '6C', '11M', '12M'];

// html elements

const canvas = document.querySelector('canvas#builder'); const ctx = canvas.getContext('2d'); const select = document.querySelector('select#builder'); const remove = document.querySelector('button#builder'); const removeAll = document.querySelector('button#builderAll'); const pre = document.querySelector('pre#builder');

// app state

const states = { idle: 0, charactersFront: 1, charactersBack: 2, mirrors: 3 }; const state = { items: [], state: states.idle, index: null };

const clearOptions = => select.innerHTML = null;

const getImg = url => { const img = new Image; img.src = url; return img; };

const drawImg = (img, x, y) => { ctx.drawImage(img, x * 60, y * 60, 60, 60); };

const defaults = { character: getImg('https://static.miraheze.org/utawarelfwiki/d/d4/ChaBlank_0_m.png'), mirror: getImg('https://static.miraheze.org/utawarelfwiki/b/ba/EquBlank_m.png') };

// app logic

const render = => { ctx.clearRect(0, 0, canvas.width, canvas.height);

for (let i = 0; i < 18; i++) { const item = state.items[i]; let img = null;

if (!item) img = paramMap[i].endsWith('M') ? defaults.mirror : defaults.character; else if (!item.img || !item.img.naturalWidth) img = item.img = getImg(item.imgSrc); else img = item.img;

const draw = => drawImg(img, i % 6, Math.floor(i / 6)); if (img.complete) draw; else img.addEventListener('load', draw); }

const templateStr = state.items.map((item, i) =>       `|${paramMap[i]}=${item.code}|${paramMap[i]}L=${item.name}`) .join(''); pre.textContent = ``; };

const setIdle = => { clearOptions; select.add(new Option('== Choose a slot ==')); select.disabled = true; remove.disabled = true; state.state = states.idle; }

canvas.addEventListener('click', async e => {   const [x, y] = [e.offsetX, e.offsetY];

if (!loaded) { clearOptions; select.add(new Option('== Loading... =='));       await load; }

state.index = Math.floor(x / 60) + Math.floor(y / 60) * 6; clearOptions; if (state.index % 6 == 2) { state.state = states.charactersBack; select.add(new Option('== Choose a character (back) ==')); data.charactersBack.forEach(c => select.add(new Option(c.name))); } else if (state.index % 6 == 3) { state.state = states.charactersFront; select.add(new Option('== Choose a character (front) ==')); data.charactersFront.forEach(c => select.add(new Option(c.name))); } else { state.state = states.mirrors; select.add(new Option('== Choose a mirror ==')); data.mirrors.forEach(m => select.add(new Option(m.name))); }

select.disabled = false; remove.disabled = false; });

select.addEventListener('change', e => {   const store = {        [states.charactersFront]: data.charactersFront,        [states.charactersBack]: data.charactersBack,        [states.mirrors]: data.mirrors    }[state.state];    state.items[state.index] = store[e.target.selectedIndex - 1];    render;    setIdle; });

remove.addEventListener('click', e => {   delete state.items[state.index];    render;    setIdle; });

removeAll.addEventListener('click', e => {   state.items = [];    render;    setIdle; });

render; setIdle;