-
Notifications
You must be signed in to change notification settings - Fork 2
/
utils.js
171 lines (162 loc) · 4.96 KB
/
utils.js
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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
// TODO make it async
async function loadGpx(path) {
const req = await fetch(path);
if (!req.ok) {
throw new Error(`Cannot load gpx ${path}: ${req.status}`);
}
const gpx = new gpxParser();
gpx.parse(await req.text());
return gpx;
}
// It can be an iso date, or an epoch time in ms or s
function guessTimestamp(s) {
if (-1 !== s.indexOf("T")) {
return Date.parse(s);
}
let v = parseInt(s);
if (v < 30000000000) {
// before end of 1970, so we probably receive sec and not ms
return 1000 * v;
} else {
return v;
}
}
function getURLParams(url = undefined) {
const urlSearchParams = new URLSearchParams((url ?? window.location).search);
const urlObject = Object.fromEntries(urlSearchParams.entries());
const shortcuts = {
A: 'track lat lon at start',
};
const res = {};
// consume possible shortcuts
for (let s in shortcuts) {
if (s in urlObject) {
const keys = shortcuts[s].split(/ /g);
Object.assign(res, Object.fromEntries(urlObject[s].split(/,/g).map((v,i) => [keys[i], v])));
delete urlObject[s];
}
}
// save other params
Object.assign(res, urlObject);
// compact shortcut to specify lskey directly in track
if ((res.track ?? '').indexOf('@') !== -1) {
const [tr, ...rest] = res.track.split('@');
res.track = tr;
if (! res.lskey) {
res.lskey = `hash/${tr}/${rest.join('_')}`
}
}
return res;
}
// Escape text so that it can be used safely as an html content
function safeHTMLText(txt) {
return new Option(txt).innerHTML;
}
function Point(ts, lat, lon) {
this.ts = parseInt(ts); // s
this.lat = parseFloat(lat);
this.lon = parseFloat(lon);
}
let reduceSum = [(a, b) => a + b, 0];
function countKeysAmong(o, ...keys) {
return keys.map((k) => k in o).reduce(...reduceSum);
}
// =============== protectedtex ============
/* Need
<script src="https://www.protectedtext.com/js/sha512.js"></script>
<script src="https://www.protectedtext.com/js/aes.js"></script>
NB: we also use a CORS proxy at heeere.com that has a yes-list of only a few servers (including localhost:7777 and the github of the 3 renards) -->
*/
let protectedTextPassword = "SmcqiZ5qQ9Vd8P9";
function lskeyToDocid(lskey) {
return "cap_nn___" + lskey;
}
function getProtectedTextURL(docid, get = true, cors = true, pass = undefined) {
return (
(cors ? "https://cors.heeere.com/" : "") +
"https://www.protectedtext.com/" +
docid +
(get ? "?action=getJSON" : "") +
(pass === true ? "?" + protectedTextPassword : pass ? "?" + pass : "")
);
}
async function appendSharedContent(lskey, v, pass) {
return await getSharedContent(lskey, pass, v);
}
async function getSharedContent(
lskey,
pass = protectedTextPassword,
alsoAppendValue = undefined
) {
let docid = lskeyToDocid(lskey);
let end = CryptoJS.SHA512("/" + docid).toString();
let req = await fetch(getProtectedTextURL(docid), {
headers: {
Pragma: "no-cache",
"Cache-Control": "no-cache",
},
});
let o = await req.json();
let raw = CryptoJS.AES.decrypt(o.eContent, pass).toString(CryptoJS.enc.Utf8);
let content = raw.substr(0, raw.length - end.length);
if (!alsoAppendValue) {
return content;
}
let initHashContent = content + CryptoJS.SHA512(pass).toString();
initHashContent = CryptoJS.SHA512(initHashContent).toString() + 2;
content += "\n" + alsoAppendValue;
let currentHashContent = content + CryptoJS.SHA512(pass).toString();
currentHashContent = CryptoJS.SHA512(currentHashContent).toString() + 2;
let encryptedContent = CryptoJS.AES.encrypt(content + end, pass).toString();
await fetch(getProtectedTextURL(docid, false), {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
initHashContent,
currentHashContent,
encryptedContent,
action: "save",
}),
});
}
// =============== Vue related ============
const vueSfcLoaderOptions = {
moduleCache: {
vue: Vue,
},
async getFile(url) {
const res = await fetch(url);
if (!res.ok)
throw Object.assign(
new Error(res.status + " " + res.statusText + " " + url),
{
res,
}
);
return {
getContentData: (asBinary) => (asBinary ? res.arrayBuffer() : res.text()),
};
},
addStyle(textContent) {
const style = Object.assign(document.createElement("style"), {
textContent,
});
const ref = document.head.getElementsByTagName("style")[0] || null;
document.head.insertBefore(style, ref);
},
};
function asyncComponent(relativePath) {
const { loadModule } = window["vue3-sfc-loader"];
return Vue.defineAsyncComponent(() => {
return loadModule("./" + relativePath, vueSfcLoaderOptions);
});
}
function getCurrentPosition(options) {
return new Promise(function (resolve, reject) {
if ('geolocation' in navigator) {
navigator.geolocation.getCurrentPosition(resolve, reject, options);
} else {
reject('Geolocation not available in navigator');
}
});
}