<!DOCTYPE html> <html lang="en" style="scrollbar-gutter:stable;"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="csrf-token" content="YSA3HA0wBB0QGiYXDmMJGiU4ZAldI3IG7WpLyXQnThnpCNNNOyPNiZDS"> <title data-suffix=" · Catalin Mititiuc"> WebDevCat.me · Catalin Mititiuc</title> <link rel="stylesheet" id="font-bitter-css" href="//fonts.googleapis.com/css?family=Bitter:400,700" type="text/css" media="screen"> <link phx-track-static rel="stylesheet" href="/assets/app-131585bb1e255488c3d2558ee5c81330.css?vsn=d"> <link phx-track-static rel="stylesheet" href="/assets/cgit-313ed4244ed6cc8d5b67d6fbb4ab18c8.css?vsn=d"> <style> article > * { max-width: unset; } div#cgit table.list { table-layout: auto; width: 100%; display: table; } div#cgit div.content { overflow: scroll; } div#cgit table.tabs { table-layout: auto; width: 100%; display: table; } div#cgit table.blob { table-layout: auto; width: 100%; display: table; } div#cgit table.tabs { table-layout: auto; width: 100%; display: table; } td.linenumbers { width: 1px; } td.lines { max-width: 1px; overflow: hidden; } td.linenumbers pre, td.lines pre { line-height: 1.25em; } pre { overflow-x: scroll; overflow-y: hidden; } code { font-size: unset; } </style> <script defer phx-track-static type="text/javascript" src="/assets/app-7bb68f31e771b77e6d1026a2eca15d48.js?vsn=d"> </script> </head> <body class="bg-white"> <header> <div style="display: inline-block;"> <h1><a href="/">Web Dev Solutions</a></h1> <h3 style="text-align: left">Catalin Mititiuc</h3> </div> </header> <main> <article> import { Observable } from "./observable"; const weapons = { rifle: { name: 'Rifle', damage: '4L', shortRange: '1-27', longRange: '28-75' }, smg: { name: 'SMG', damage: '3L', shortRange: '1-15', longRange: '16-25' }, blazer: { name: 'Blazer', damage: '4L', shortRange: '1-17', longRange: '18-105' } } function createIcon(number) { const [icon, use, text] = ['svg', 'use', 'text'].map(t => document.createElementNS(svgns, t)); icon.setAttributeNS(null, 'viewBox', '-6 -6 12 12'); icon.setAttribute('xmlns', svgns); use.setAttributeNS(null, 'href', `assets/images/counters.svg#counter-base`); text.textContent = number; icon.appendChild(use); icon.appendChild(text); return icon; } function createWeaponIcon(type) { const [icon, use] = ['svg', 'use'].map(t => document.createElementNS(svgns, t)); icon.setAttributeNS(null, 'viewBox', '-6 -6 12 12'); icon.setAttribute('xmlns', svgns); icon.classList.add('weapon-icon'); use.setAttributeNS(null, 'href', `assets/images/counters.svg#${type}`); icon.appendChild(use); return icon; } function createRecord(unit) { const { dataset: { allegiance, number, squad }} = unit, primaryWeapon = unit.querySelector('.primary-weapon'), pw = primaryWeapon?.getAttribute('href').replace('#', '') || 'rifle', div = document.createElement('div', { is: 'soldier-record-block' }), spans = Array(6).fill('span').map(t => document.createElement(t)), [tn, sn, pwt, pwd, pwrs, pwrl] = spans; div.setAttribute('class', 'soldier-record'); div.dataset.number = number; div.dataset.allegiance = allegiance; tn.setAttribute('slot', 'troop-number'); tn.appendChild(createIcon(number)); sn.setAttribute('slot', 'squad-number'); sn.appendChild(createIcon(squad || 1)); pwt.setAttribute('slot', 'primary-weapon-type'); pwt.textContent = ' ' + weapons[pw].name; pwt.prepend(createWeaponIcon(pw)); pwd.setAttribute('slot', 'primary-weapon-damage'); pwd.textContent = weapons[pw].damage; pwrs.setAttribute('slot', 'primary-weapon-range-short'); pwrs.textContent = weapons[pw].shortRange; pwrl.setAttribute('slot', 'primary-weapon-range-long'); pwrl.textContent = weapons[pw].longRange; spans.forEach(el => div.appendChild(el)); return div; } function createRecords(units) { const grouped = Array.from(units).reduce((acc, unit) => { acc[unit.dataset.allegiance]?.push(unit) || (acc[unit.dataset.allegiance] = [unit]); return acc; }, {}); for (const al in grouped) { grouped[al] = grouped[al].map(createRecord); } return grouped; } function getRecord({ dataset: { allegiance: al, number: n }}) { const selector = `.soldier-record[data-number="${n}"][data-allegiance="${al}"]`; return document.querySelector(selector); } function deselect() { const selected = getSelected(); if (selected) { selected.classList.remove('selected'); } } function clear() { document.querySelectorAll('#record-sheet .soldier-record').forEach(el => el.remove()); document.querySelector('#attacker-record .name').textContent = 'attacker'; document.querySelector('#defender-record .name').textContent = 'defender'; } function select(data) { const record = data && getRecord(data); const isSelected = record?.classList.contains('selected'); deselect(); if (isSelected || !data) return; record.classList.add('selected'); } function endMove() { const selected = getSelected(); if (selected) { selected.classList.toggle('movement-ended'); } deselect(); } export function getSelected() { return document.querySelector('.soldier-record.selected'); } export function start(startLoc, units) { clear(); const forces = createRecords(units); for (const affiliation in forces) { const container = document.querySelector(`#${affiliation}-record`); const records = container.querySelector('.records'); const name = startLoc.dataset[`${affiliation}Name`]; if (name) { container.querySelector('.name').textContent = name; } forces[affiliation].forEach(r => records.appendChild(r)); } document.querySelectorAll('.soldier-record').forEach(el => el.addEventListener('click', () => Observable.notify('select', el)) ); Observable.subscribe('select', select); Observable.subscribe('endmove', endMove); } </article> </main> <footer> <p>100% Human Made, No AI Used</p> <p>stasis 0.2.12</p> </footer> </body> </html>