1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
// Copyright (C) 2026 Danilo M. <danix@danix.xyz>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
.pragma library
// Pure helpers for the user page, tested by test_user.js under node.
// `userctl get` output into an object.
function parse(text) {
const out = {};
for (const line of text.split("\n")) {
const i = line.indexOf("=");
if (i > 0) out[line.slice(0, i)] = line.slice(i + 1);
}
return out;
}
// `userctl keys` output into [{fpr, uid}].
function keys(text) {
return text.split("\n").filter(l => l.includes("\t"))
.map(l => ({ fpr: l.slice(0, l.indexOf("\t")), uid: l.slice(l.indexOf("\t") + 1) }));
}
// `locale -a` prints it_IT.utf8; AccountsService stores it_IT.UTF-8.
function locales(text) {
return text.split("\n")
.filter(l => /\.utf-?8$/i.test(l) && !/^C\./.test(l))
.map(l => l.replace(/\.utf-?8$/i, ".UTF-8"))
.sort();
}
// The name git gets, for the page's preview. ponytail: duplicates userctl's
// gitname; both tests pin the same cases, keep them in step.
function gitName(name) {
const w = name.trim().split(/\s+/).filter(s => s !== "");
if (w.length < 2) return w.join(" ");
return w.slice(0, -1).join(" ") + " " + Array.from(w[w.length - 1])[0] + ".";
}
// Nominatim jsonv2 search results as location labels, "place, region,
// country", deduplicated. Anything that is not a JSON array (an error page,
// a rate-limit notice) is no suggestions rather than an error.
function places(text) {
let rows;
try { rows = JSON.parse(text); } catch (e) { return []; }
if (!Array.isArray(rows)) return [];
const out = [];
for (const r of rows) {
const a = r.address || {};
const parts = [a.city || a.town || a.village || a.hamlet || a.municipality || r.name,
a.state, a.country].filter(p => p);
const label = parts.filter((p, i) => parts.indexOf(p) === i).join(", ");
if (label && !out.includes(label)) out.push(label);
}
return out;
}
|