handle special characters (e.g., newlines) in node data (#31)

This commit is contained in:
root 2025-01-07 18:50:48 +00:00
parent 92dcb58981
commit ca2b7b3dd1

View file

@ -97,7 +97,9 @@
}
</style>
<div id="header">
<div><a href="https://meshmap.net/" title="A nearly live map of Meshtastic nodes seen by the official Meshtastic MQTT server">MeshMap</a></div>
<div>
<a href="https://meshmap.net/" title="A nearly live map of Meshtastic nodes seen by the official Meshtastic MQTT server">MeshMap</a>
</div>
<div>
<a
href="#"
@ -126,8 +128,10 @@
182, 91, 46, 23, 11, 6, 3, 1,
1, 0, 0, 0, 0, 0, 0, 0
]
// encodes html reserved characters
const html = str => str?.replace(/["&<>]/g, match => `&#${match.charCodeAt(0)};`)
// encodes html reserved characters and ascii control characters
const html = str => str
?.replace(/[\x00-\x1F]/g, c => `\\x${c.charCodeAt(0).toString(16).toUpperCase().padStart(2, '0')}`)
.replace(/["&<>]/g, c => `&#${c.charCodeAt(0)};`)
// makes more human-readable time duration strings
const duration = d => {
let s = ''
@ -232,22 +236,23 @@
<div class="title">${html(longName)} (${html(shortName)})</div>
<div>${nodeLink(nodeNum, id)} | ${html(role)} | ${html(hwModel)}</div>
<table><tbody>
${fwVersion ? `<tr><th>Firmware</th><td>${html(fwVersion)}</td></tr>` : ''}
${region ? `<tr><th>Region</th><td>${html(region)}</td></tr>` : ''}
${modemPreset ? `<tr><th>Modem preset</th><td>${html(modemPreset)}</td></tr>` : ''}
${hasDefaultCh ? `<tr><th>Has default channel</th><td>True</td></tr>` : ''}
${onlineLocalNodes ? `<tr><th>Online local nodes</th><td>${onlineLocalNodes}</td></tr>` : ''}
${batteryLevel ? `<tr><th>Power</th><td>${batteryLevel > 100 ? 'Plugged in' : `${batteryLevel}%`}${voltage ? ` (${voltage.toFixed(2)}V)` : ''}</td></tr>` : ''}
${chUtil ? `<tr><th>ChUtil</th><td>${chUtil.toFixed(2)}%</td></tr>` : ''}
${airUtilTx ? `<tr><th>AirUtilTX</th><td>${airUtilTx.toFixed(2)}%</td></tr>` : ''}
${uptime ? `<tr><th>Uptime</th><td>${duration(uptime)}</td></tr>` : ''}
${temperature ? `<tr><th>Temperature</th><td>${temperature.toFixed(1)}&#8451; / ${(temperature * 1.8 + 32).toFixed(1)}&#8457;</td></tr>` : ''}
${relativeHumidity ? `<tr><th>Relative Humidity</th><td>${Math.round(relativeHumidity)}%</td></tr>` : ''}
${fwVersion ? `<tr><th>Firmware</th><td>${html(fwVersion)}</td></tr>` : ''}
${region ? `<tr><th>Region</th><td>${html(region)}</td></tr>` : ''}
${modemPreset ? `<tr><th>Modem preset</th><td>${html(modemPreset)}</td></tr>` : ''}
${hasDefaultCh ? `<tr><th>Has default channel</th><td>True</td></tr>` : ''}
${onlineLocalNodes ? `<tr><th>Online local nodes</th><td>${onlineLocalNodes}</td></tr>` : ''}
${batteryLevel ? `<tr><th>Power</th><td>${batteryLevel > 100 ? 'Plugged in' : `${batteryLevel}%`}` +
`${voltage ? ` (${voltage.toFixed(2)}V)` : ''}</td></tr>` : ''}
${chUtil ? `<tr><th>ChUtil</th><td>${chUtil.toFixed(2)}%</td></tr>` : ''}
${airUtilTx ? `<tr><th>AirUtilTX</th><td>${airUtilTx.toFixed(2)}%</td></tr>` : ''}
${uptime ? `<tr><th>Uptime</th><td>${duration(uptime)}</td></tr>` : ''}
${temperature ? `<tr><th>Temperature</th><td>${temperature.toFixed(1)}&#8451; / ` +
`${(temperature * 1.8 + 32).toFixed(1)}&#8457;</td></tr>` : ''}
${relativeHumidity ? `<tr><th>Relative Humidity</th><td>${Math.round(relativeHumidity)}%</td></tr>` : ''}
${barometricPressure ? `<tr><th>Barometric Pressure</th><td>${Math.round(barometricPressure)} hPa</td></tr>` : ''}
${altitude ? `<tr><th>Altitude</th><td>${altitude.toLocaleString()} m above MSL</td></tr>` : ''}
${precision && precisionMargins[precision-1] ?
`<tr><th>Location precision</th><td>&#177; ${precisionMargins[precision-1].toLocaleString()} m (orange circle)</td></tr>` : ''
}
${altitude ? `<tr><th>Altitude</th><td>${altitude.toLocaleString()} m above MSL</td></tr>` : ''}
${precision && precisionMargins[precision-1] ? "<tr><th>Location precision</th><td>&#177;" +
`${precisionMargins[precision-1].toLocaleString()} m (orange circle)</td></tr>` : ''}
</tbody></table>
<table><thead>
<tr><th>Last seen</th><th>via</th><th>root topic</th><th>channel</th></tr>
@ -255,7 +260,9 @@
${Array.from(
new Map(
Object.entries(seenBy)
.map(([topic, seen]) => (m => ({seen, via: m[3] ?? id, root: m[1], chan: m[2]}))(topic.match(/^(.*)(?:\/2\/e\/(.*)\/(![0-9a-f]+)|\/2\/map\/)$/)))
.map(([topic, seen]) => (m => ({seen, via: m[3] ?? id, root: m[1], chan: m[2]}))(
topic.match(/^(.*)(?:\/2\/e\/(.*)\/(![0-9a-f]+)|\/2\/map\/)$/s)
))
.sort((a, b) => a.seen - b.seen)
.map(v => [v.via, v])
).values(),