Web Dev Solutions

Catalin Mititiuc

const svgns = "http://www.w3.org/2000/svg", selectedClass = 'selected'; export default class Counter { constructor(svg, container) { this.svg = svg; this.container = container; } dataSelector(troopNumber, allegiance) { return `[data-number="${troopNumber}"][data-allegiance="${allegiance}"]`; } selector(troopNumber, allegiance) { return `.counter${this.dataSelector(troopNumber, allegiance)}`; } position(x, y) { return `g[data-x="${x}"][data-y="${y}"]`; } counterPosition(x, y) { return `.counter[data-x="${x}"][data-x="${y}"]`; } traceSelector(troopNumber, allegiance) { return `polyline.move-trace${this.dataSelector(troopNumber, allegiance)}`; } getCounters() { return this.svg.querySelectorAll(`.counter[data-allegiance][data-number]:not(.clone)`); } getClones(al, n) { return this.svg.querySelectorAll(`.counter.clone[data-allegiance="${al}"][data-number="${n}"]`); } getCounter(al, n) { return this.svg.querySelector(`.counter[data-allegiance="${al}"][data-number="${n}"]:not(.clone)`); } getCounterAndClones(al, n) { return this.svg.querySelectorAll(`.counter[data-allegiance="${al}"][data-number="${n}"]`); } getTrace({ dataset: { allegiance, number } }) { return this.svg.querySelector(this.traceSelector(number, allegiance)); } getCellPosition(cell) { let pt = new DOMPoint(0, 0), transform = getComputedStyle(cell).transform.match(/-?\d+\.?\d*/g), mtx = new DOMMatrix(transform); pt = pt.matrixTransform(mtx); transform = getComputedStyle(cell.parentElement).transform.match(/-?\d+\.?\d*/g); mtx = new DOMMatrix(transform); pt = pt.matrixTransform(mtx); return pt; } // pointerOver(e) { // const { number: troopNumber, allegiance: troopAllegiance } = e.target.dataset, // cp = this.svg.querySelector(`#clip-path-${troopAllegiance}-${troopNumber}`); // if (cp) { // cp.style.display = 'none'; // } // } // pointerOut(e) { // let { number: troopNumber, allegiance: troopAllegiance } = e.target.dataset, // cp = this.svg.querySelector(`#clip-path-${troopAllegiance}-${troopNumber}`); // if (cp) { // let isVisible = // document // .getElementById('toggle-firing-arc-vis') // .querySelector(`input[data-allegiance="${troopAllegiance}"]`) // .checked; // cp.style.display = isVisible ? 'none' : ''; // } // } click(e) { if (e.target.classList.contains(selectedClass)) { e.stopPropagation(); let { number: troopNumber, allegiance: troopAllegiance } = e.target.dataset, trace = this.svg.querySelector(this.traceSelector(troopNumber, troopAllegiance)); if (trace) { let points = trace.getAttribute('points').split(' '), [clonePosX, clonePosY] = points.at(-2).split(',').map(p => parseFloat(p)), { clone } = Array .from(this.getClones(troopAllegiance, troopNumber)) .map(c => { return { clone: c, pos: this.getCellPosition(c.parentElement) }}) .find(({ pos: { x, y }}) => x == clonePosX && y == clonePosY); points.pop(); if (points.length >= 2) { trace.setAttributeNS(null, 'points', points.join(' ')); } else { trace.remove(); } let sl = this.svg.querySelector('.sight-line'); if (sl) { this.container.sightLine.update(clone, this.getCellPosition(clone.parentElement)); } else { this.container.sightLine.draw(e.target.parentElement, clone.parentElement); } const proneCounter = e.target.parentElement.querySelector('[href="#counter-prone"]'); if (proneCounter) { proneCounter.remove(); } this.container.proneFlagCallback(!!clone.parentElement.querySelector('[href="#counter-prone"]')); clone.parentElement.appendChild(e.target); clone.remove(); } } else { e.stopPropagation(); this.select(e.target); } } clickClone(e) { e.stopPropagation(); let { number: troopNumber, allegiance: troopAllegiance } = e.target.dataset, pos = this.getCellPosition(e.target.parentElement); if (this.isSelected(troopNumber, troopAllegiance)) { let trace = this.svg.querySelector(this.traceSelector(troopNumber, troopAllegiance)), points = trace.getAttribute('points').split(' '); if (`${pos.x},${pos.y}` == points.at(0)) { const counter = this.getCounter(troopAllegiance, troopNumber), lockedSl = this.svg.querySelector('.sight-line:not(.active)'); if (!lockedSl) { this.container.sightLine.clear(); } else { this.container.sightLine.update(e.target, this.container.getCellPosition(e.target.parentElement)); } this.container.proneFlagCallback(!!e.target.parentElement.querySelector('[href="#counter-prone"]')); e.target.parentElement.appendChild(counter); e.target.remove(); this.removeClones(counter); trace.remove(); } else { const proneCounter = e.target.parentElement.querySelector('[href="#counter-prone"]'); if (proneCounter) { proneCounter.remove(); } points = points.filter(p => p != `${pos.x},${pos.y}`).join(' '); trace.setAttributeNS(null, 'points', points); e.target.remove(); } } } dblClick(e) { if (e.target.classList.contains(selectedClass)) { let { troopNumber, troopAllegiance } = e.target.dataset, trace = this.svg.querySelector(this.traceSelector(troopNumber, troopAllegiance)); if (!trace) { e.target.remove(); this.svg.querySelectorAll(`#firing-arcs ${this.dataSelector(troopNumber, troopAllegiance)}`).forEach(el => el.remove()); } } } select({ dataset: { allegiance, number }}) { this.unSelect(); let counter = this.getCounter(allegiance, number); if (counter) { counter.classList.add(selectedClass); let existingArcs = this.container.getExistingArcs(allegiance, number); existingArcs.forEach(el => el.removeAttribute('clip-path')); this.container.selectCallback({ prone: this.hasProne(counter), ...counter.dataset }); } } unSelect() { let selected = this.container.getSelected(); if (selected) { let { troopNumber, troopAllegiance } = selected.dataset; selected.classList.remove(selectedClass); selected.removeAttribute('style'); this.container.sightLine.clear(); this .svg .querySelectorAll(`${this.selector(troopNumber, troopAllegiance)}.clone`) .forEach(el => el.removeAttribute('style')); } this.container.clipFiringArcs(); } get(troopNumber, allegiance) { return this.container.getCounter(allegiance, troopNumber); } getAt(x, y) { return this.container.querySelector(`${counterPosition(x, y)}:not(.clone)`); } isSelected(troopNumber, allegiance) { return this.container.svg.querySelector(`${this.selector(troopNumber, allegiance)}.${selectedClass}`) !== null; } place(point) { const selected = this.container.getSelected(), troopAllegiance = selected.dataset.allegiance, troopNumber = selected.dataset.number; let points, counterNodeList = this.getCounterAndClones(troopAllegiance, troopNumber); if (counterNodeList.length > 0 && selected.parentElement.hasAttribute('data-x')) { let trace = this.svg.querySelector(this.traceSelector(troopNumber, troopAllegiance)); let prevCoords = [ selected.parentElement.dataset.x, selected.parentElement.parentElement.dataset.y ] let clone = selected.cloneNode(true); clone.classList.remove(selectedClass); clone.classList.add('clone'); selected.dataset.previous = prevCoords; selected.parentElement.appendChild(clone); point.parentElement.appendChild(selected); selected.childNodes.forEach(n => { if (n.classList.contains('removed')) { n.remove(); } else if ('preexisting' in n.dataset) { delete n.dataset.preexisting; } }); let previous = this.getCellPosition(clone.parentElement), current = this.getCellPosition(selected.parentElement); if (!trace) { trace = document.createElementNS(svgns, 'polyline'); points = `${previous.x},${previous.y} ${current.x},${current.y}`; trace.dataset.number = troopNumber; trace.dataset.allegiance = troopAllegiance; trace.classList.add('move-trace'); this.container.getBoard().prepend(trace); } else { points = `${trace.getAttribute('points')} ${current.x},${current.y}`; } trace.setAttributeNS(null, 'points', points); // clone.addEventListener('click', this.clickClone.bind(this)); } else { selected.removeAttribute('data-x'); point.parentElement.appendChild(selected); } } remove({ dataset: { troopNumber, troopAllegiance }}) { this.container .querySelectorAll(dataSelector(troopNumber, troopAllegiance)) .forEach(el => el.remove()); } removeClones({ dataset: { allegiance, number }}) { this.getClones(allegiance, number).forEach(el => { el.remove() }); } endMove(el) { const { number, allegiance } = el.dataset, trace = this.svg.querySelector(this.traceSelector(number, allegiance)), proneCounter = el.querySelector('[href="#counter-prone"]'); if (trace) { trace.remove(); } delete el.dataset.previous; if (proneCounter) { proneCounter.dataset.preexisting = ''; } this.removeClones(el); this.unSelect(); } hasProne(counter) { const isOnBoard = counter.parentElement.hasAttribute('data-x'); if (isOnBoard) { return !!counter.querySelector('[href="#counter-prone"]'); } return false; } toggleProne() { const selected = this.container.getSelected(), isOnBoard = selected && selected.parentElement.hasAttribute('data-x'); if (selected && isOnBoard) { const proneCounter = selected.querySelector('[href="#counter-prone"]'); if (proneCounter) { if ('preexisting' in proneCounter.dataset) { proneCounter.classList.toggle('removed'); } else { proneCounter.remove(); } } else { const counter = document.createElementNS(svgns, 'use'); counter.setAttributeNS(null, 'href', '#counter-prone'); selected.appendChild(counter); } } } }