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