2026-05-06 18:35:19 -05:00

207 lines
7.4 KiB
JavaScript

function renderPins(pins) {
const pinsDiv = document.getElementById("pins");
pinsDiv.innerHTML = '';
const img = document.getElementById("main-map");
const imgWidth = img.clientWidth;
const imgHeight = img.clientHeight;
pins.forEach(pin => {
console.log(pin)
const el = document.createElement('a');
el.className = "pin";
el.href = pin.url;
el.title = pin.label;
el.target = "_blank";
el.style.left = (imgWidth * (pin.x / 100)) + 'px';
el.style.top = (imgHeight * (pin.y / 100)) + 'px';
el.dataset.pinid = pin.id;
el.dataset.x = pin.x;
el.dataset.y = pin.y;
if (pin.pin_type == "general"){
el.innerHTML = '<img src="/static/pin.svg" alt="general pin" />';
}
if (pin.pin_type == "town"){
el.innerHTML = '<img src="/static/town.png" alt="town pin" />';
}
if (pin.pin_type == "dungeon"){
el.innerHTML = '<img src="/static/dungeon.png" alt="dungeon pin" />';
}
const label = document.createElement('div');
label.className = 'pin-label';
label.innerText = pin.label;
el.appendChild(label);
pinsDiv.appendChild(el);
});
renderPinsTable(pins)
}
function fetchPins() {
fetch("/api/pins/")
.then(r => {
if (!r.ok) throw new Error("Pin fetch failed");
return r.json();
})
.then(renderPins)
.catch(() => {
// For demo/development: Show example
renderPins([
{x:15, y:30, label:'Demo Pin', url:'/'}
]);
});
}
function renderPinsTable(pins) {
const tbody = document.querySelector('#pins-table tbody');
tbody.innerHTML = '';
pins.forEach((pin, idx) => {
const tr = document.createElement('tr');
tr.innerHTML = `
<td>${idx+1}</td>
<td>${escapeHTML(pin.label)}</td>
<td><a href="${pin.url}" target="_blank">${pin.url}</a></td>
<td>${pin.x.toFixed(2)}</td>
<td>${pin.y.toFixed(2)}</td>
<td>
<button class="uk-button uk-button-small uk-button-default" onclick="centerMapOnPin(${pin.x},${pin.y})">Center</button>
<button class="uk-button uk-button-small uk-button-primary" onclick="editPin('${pin.id}')">Edit</button>
<button class="uk-button uk-button-small uk-button-danger" onclick="deletePin('${pin.id}')">Delete</button>
</td>
`;
tbody.appendChild(tr);
});
}
function escapeHTML(str) {
// Prevent breaking the DOM if user enters < > or & etc
return (str||'').replace(/[&<>"'`]/g, s => ({
'&':'&amp;', '<':'&lt;', '>':'&gt;', '"':'&quot;', "'":'&#39;', '`':'&#96;'
}[s]));
}
function centerMapOnPin(xPercent, yPercent) {
const mapImg = document.getElementById('main-map');
const container = document.getElementById('map-container');
// Calculate pixel pos relative to image
const imgWidth = mapImg.clientWidth;
const imgHeight = mapImg.clientHeight;
const pinX = imgWidth * (xPercent / 100);
const pinY = imgHeight * (yPercent / 100);
// Center in the scrollable container
const containerWidth = container.clientWidth;
const containerHeight = container.clientHeight;
container.scrollLeft = Math.max(0, pinX - containerWidth / 2);
container.scrollTop = Math.max(0, pinY - containerHeight / 2);
// Highlight the right pin
const pinEl = Array.from(document.querySelectorAll('.pin')).find(
el => Math.abs(parseFloat(el.dataset.x) - xPercent) < 0.0001 &&
Math.abs(parseFloat(el.dataset.y) - yPercent) < 0.0001
);
if(pinEl) {
pinEl.classList.add('pin-highlight');
setTimeout(()=>pinEl.classList.remove('pin-highlight'), 1200);
}
}
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
fetchPins();
document.getElementById('map-container').addEventListener('contextmenu', function(e) {
e.preventDefault();
const mapImg = document.getElementById('main-map');
const rect = mapImg.getBoundingClientRect();
if (!(e.target === mapImg || e.target.id === 'pins')) return;
const x = 100 * (e.clientX - rect.left) / rect.width;
const y = 100 * (e.clientY - rect.top) / rect.height;
document.getElementById('pin-x').value = x;
document.getElementById('pin-y').value = y;
document.getElementById('add-pin-form').reset();
UIkit.modal('#add-pin-modal').show();
});
document.getElementById('add-pin-form').addEventListener('submit', function(e) {
e.preventDefault();
const x = parseFloat(document.getElementById('pin-x').value);
const y = parseFloat(document.getElementById('pin-y').value);
const label = document.getElementById('pin-label').value;
const url = document.getElementById('pin-url').value;
const pin_type = document.getElementById('pin-type').value;
fetch('/api/pins/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCookie('csrftoken')
},
body: JSON.stringify({x, y, label, url, pin_type})
}).then(resp => {
if (resp.ok) {
UIkit.modal('#add-pin-modal').hide();
fetchPins();
} else {
resp.json().then(data => alert(JSON.stringify(data)));
}
});
});
function resizePinsDiv() {
var mapImg = document.getElementById('main-map');
var pinsDiv = document.getElementById('pins');
// Set pins div to same px size as image
pinsDiv.style.width = mapImg.width + 'px';
pinsDiv.style.height = mapImg.height + 'px';
pinsDiv.style.position = 'absolute';
pinsDiv.style.top = '0';
pinsDiv.style.left = '0';
}
document.getElementById('main-map').addEventListener('load', resizePinsDiv);
window.addEventListener('resize', resizePinsDiv);
if (document.getElementById('main-map').complete) resizePinsDiv();
// This code handles the dragging of the map
const container = document.getElementById('map-container');
let isDragging = false;
let startX, startY, scrollLeft, scrollTop;
container.addEventListener('mousedown', function(e) {
isDragging = true;
container.classList.add('dragging');
startX = e.pageX - container.offsetLeft;
startY = e.pageY - container.offsetTop;
scrollLeft = container.scrollLeft;
scrollTop = container.scrollTop;
});
container.addEventListener('mouseleave', function() {
isDragging = false;
container.classList.remove('dragging');
});
container.addEventListener('mouseup', function() {
isDragging = false;
container.classList.remove('dragging');
});
container.addEventListener('mousemove', function(e) {
if (!isDragging) return;
e.preventDefault();
const x = e.pageX - container.offsetLeft;
const y = e.pageY - container.offsetTop;
const walkX = x - startX;
const walkY = y - startY;
container.scrollLeft = scrollLeft - walkX;
container.scrollTop = scrollTop - walkY;
});