fix: harden station-map component (XSS escape, destroy hook, null guard, bounds)
This commit is contained in:
@@ -10,6 +10,14 @@ L.Icon.Default.mergeOptions({
|
|||||||
shadowUrl: markerShadow,
|
shadowUrl: markerShadow,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function escHtml(str) {
|
||||||
|
return String(str ?? '')
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>')
|
||||||
|
.replace(/"/g, '"');
|
||||||
|
}
|
||||||
|
|
||||||
const CLASSIFICATION_COLOURS = {
|
const CLASSIFICATION_COLOURS = {
|
||||||
current: '#22c55e',
|
current: '#22c55e',
|
||||||
recent: '#64748b',
|
recent: '#64748b',
|
||||||
@@ -20,10 +28,9 @@ const CLASSIFICATION_COLOURS = {
|
|||||||
const UK_CENTRE = [54.0, -2.0];
|
const UK_CENTRE = [54.0, -2.0];
|
||||||
const UK_ZOOM = 6;
|
const UK_ZOOM = 6;
|
||||||
|
|
||||||
export function stationMap(results, meta) {
|
export function stationMap(results) {
|
||||||
return {
|
return {
|
||||||
results,
|
results,
|
||||||
meta,
|
|
||||||
_map: null,
|
_map: null,
|
||||||
_markers: [],
|
_markers: [],
|
||||||
|
|
||||||
@@ -47,7 +54,18 @@ export function stationMap(results, meta) {
|
|||||||
this._markers = [];
|
this._markers = [];
|
||||||
},
|
},
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
if (this._map) {
|
||||||
|
this._map.remove();
|
||||||
|
this._map = null;
|
||||||
|
this._markers = [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
_plotMarkers() {
|
_plotMarkers() {
|
||||||
|
if (!this._map) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this._clearMarkers();
|
this._clearMarkers();
|
||||||
|
|
||||||
if (!this.results || this.results.length === 0) {
|
if (!this.results || this.results.length === 0) {
|
||||||
@@ -65,10 +83,10 @@ export function stationMap(results, meta) {
|
|||||||
|
|
||||||
const popup = `
|
const popup = `
|
||||||
<div style="min-width:160px">
|
<div style="min-width:160px">
|
||||||
<strong style="font-size:13px">${station.name}</strong>${supermarketTag}<br>
|
<strong style="font-size:13px">${escHtml(station.name)}</strong>${supermarketTag}<br>
|
||||||
<span style="font-size:20px;font-weight:700;color:${colour}">${Number(station.price).toFixed(1)}p</span><br>
|
<span style="font-size:20px;font-weight:700;color:${colour}">${Number(station.price).toFixed(1)}p</span><br>
|
||||||
<span style="font-size:12px;color:#6b7280">${miles} miles away</span><br>
|
<span style="font-size:12px;color:#6b7280">${escHtml(miles)} miles away</span><br>
|
||||||
<span style="font-size:11px;color:#9ca3af">${station.address}, ${station.postcode}</span>
|
<span style="font-size:11px;color:#9ca3af">${escHtml(station.address)}, ${escHtml(station.postcode)}</span>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -86,7 +104,11 @@ export function stationMap(results, meta) {
|
|||||||
bounds.push([station.lat, station.lng]);
|
bounds.push([station.lat, station.lng]);
|
||||||
});
|
});
|
||||||
|
|
||||||
this._map.fitBounds(bounds, { padding: [40, 40], maxZoom: 14 });
|
if (bounds.length === 1) {
|
||||||
|
this._map.setView(bounds[0], 14);
|
||||||
|
} else {
|
||||||
|
this._map.fitBounds(bounds, { padding: [40, 40], maxZoom: 14 });
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user