Passed
Push — master ( 3d93f1...0f79c8 )
by EMP
01:27
created

modern/main.js   F

Complexity

Total Complexity 375
Complexity/F 2.52

Size

Lines of Code 1806
Function Count 149

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1249
dl 0
loc 1806
rs 0.8
c 0
b 0
f 0
wmc 375
mnd 226
bc 226
fnc 149
bpm 1.5167
cpm 2.5167
noi 11

34 Functions

Rating   Name   Duplication   Size   Complexity  
A main.js ➔ addMsgFlag 0 10 1
C main.js ➔ errorDialog 0 33 10
A main.js ➔ updateAddressCounts 0 10 3
F main.js ➔ displayExport 0 19 14
A main.js ➔ clearMsgFlags 0 3 1
F main.js ➔ displayMsg 0 204 57
A main.js ➔ getCountryFlag 0 6 2
B main.js ➔ addOwnAccount 0 51 8
A main.js ➔ MsgInfo 0 5 1
A main.js ➔ adjustLevel 0 14 3
A main.js ➔ clearDisplay 0 10 3
A main.js ➔ updateAddressButtons 0 5 1
A main.js ➔ getClockIcon 0 17 5
F main.js ➔ displayFile 0 123 16
B main.js ➔ displayOutMsg 0 56 8
C main.js ➔ addMsg 0 42 9
B main.js ➔ TabState 0 6 8
A main.js ➔ updateLimits 0 16 3
F main.js ➔ showFiles 0 150 33
C main.js ➔ writeVerify 0 27 10
F main.js ➔ deleteAddress 0 45 14
C main.js ➔ setTab 0 68 9
F main.js ➔ showInbox 0 56 16
A main.js ➔ clearWrite 0 12 1
A main.js ➔ addContact 0 30 2
B main.js ➔ addAddress 0 64 5
F main.js ➔ refreshContactList 0 17 17
B main.js ➔ addAccountToTable 0 38 6
C main.js ➔ showDrbox 0 56 11
A main.js ➔ reloadAccount 0 10 1
F main.js ➔ addressCreate 0 25 16
A main.js ➔ addContacts 0 11 2
A main.js ➔ addAddresses 0 7 2
A main.js ➔ setRowsPerPage 0 8 1

How to fix   Complexity   

Complexity

Complex classes like modern/main.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
"use strict";
2
3
sodium.ready.then(function() {
4
5
let isReady = true;
6
let vaultOk = null;
7
8
const vault = new PostVault(function(ok) {
0 ignored issues
show
Bug introduced by
The variable PostVault seems to be never declared. If this is a global, consider adding a /** global: PostVault */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
9
	if (ok) vaultOk = false;
10
});
11
12
const ae = new AllEars(function(ok) {
13
	if (!ok) {
14
		document.getElementById("greeting").textContent = "Failed loading All-Ears";
15
		return;
16
	}
17
18
	try {
19
		if (localStorage.greeting) {
20
			document.getElementById("greeting").textContent = localStorage.greeting;
21
			document.getElementById("txt_pg").value = localStorage.greeting;
22
		} else localStorage.greeting = document.getElementById("greeting").textContent;
23
	} catch(e) {
24
		document.getElementById("btn_pg").disabled = true;
25
		document.getElementById("txt_pg").disabled = true;
26
		document.getElementById("txt_pg").className = "ita";
27
		document.getElementById("txt_pg").value = "LocalStorage inaccessible";
28
	}
29
30
	document.getElementById("txt_skey").maxLength = "64";
31
});
32
33
function TabState(cur, max, btnDele, btnUpdt) {
34
	this.cur = cur;
35
	this.max = max;
36
	this.btnDele = btnDele;
37
	this.btnUpdt = btnUpdt;
38
}
39
40
const tabs = [
41
	new TabState(0, 0, false, true), // Inbox
42
	new TabState(0, 0, false, false), // Outbx
43
	new TabState(0, 1, true,  false), // Write
44
	new TabState(0, 2, false, false), // Notes
45
	new TabState(0, 2, false, false) // Tools
46
];
47
48
function MsgInfo(msgIdHex, msgType, msgNum) {
49
	this.idHex = msgIdHex;
50
	this.type = msgType;
51
	this.num = msgNum;
52
}
53
54
let msgDisplay = new MsgInfo(null, null, null);
55
let showHeaders = false;
56
let rowsPerPage = 0;
57
58
let tab = 0;
59
const TAB_INBOX = 0;
60
const TAB_DRBOX = 1;
61
const TAB_WRITE = 2;
62
const TAB_NOTES = 3;
63
const TAB_TOOLS = 4;
64
65
// Helper functions
66
function errorDialog(err, focusAfter) {
67
	if (typeof(err) !== "number" || err < 1) return;
68
69
	let btnDisable = [];
70
	const buttons = document.querySelectorAll("nav > button");
71
	buttons.forEach(function(btn) {
72
		btnDisable.push(btn.disabled);
73
		btn.disabled = true;
74
	});
75
76
	const errMsg = ae.getErrorMessage(err);
77
78
	const dlg = document.querySelector("dialog");
79
	dlg.children[0].style.height = getComputedStyle(document.querySelector("#main1 > div[class='mid']")).height;
80
	dlg.querySelector("h1").textContent = "ERROR 0x" + err.toString(16).padStart(2, "0").toUpperCase();
81
	dlg.querySelector("p").textContent = (typeof(errMsg) === "string") ? errMsg : errMsg[1];
82
	dlg.show();
83
84
	document.querySelector("dialog > div").onclick = function() {
85
		buttons.forEach(function(btn, i) {btn.disabled = btnDisable[i];});
86
		dlg.close();
87
		if (focusAfter) focusAfter.focus();
88
	};
89
90
	document.onkeyup = function(event) {
91
		document.onkeyup = null;
92
		event.preventDefault();
93
94
		buttons.forEach(function(btn, i) {btn.disabled = btnDisable[i];});
95
		dlg.close();
96
		if (focusAfter) focusAfter.focus();
97
	};
98
}
99
100
function getCountryFlag(countryCode) {
101
	return (!countryCode || countryCode.length !== 2 || countryCode === "??") ? "❔" : sodium.to_string(new Uint8Array([
102
		240, 159, 135, 166 + countryCode.codePointAt(0) - 65,
103
		240, 159, 135, 166 + countryCode.codePointAt(1) - 65
104
	]));
105
}
106
107
function getClockIcon(d) {
108
	const h24 = d.getUTCHours();
109
	let h12 = (h24 === 0 ? 12 : ((h24 > 12) ? h24 - 12 : h24));
110
111
	const m60 = (d.getUTCMinutes() * 60) + d.getUTCSeconds();
112
	let m30 = 0;
113
	if (m60 <= 900) { // <= 15: round down to this hour
114
		m30 = 0;
115
	} else if (m60 > 900 && m60 < 2700) { // 15..45: round to half-past this hour
116
		m30 = 12;
117
	} else { // >= 45: round up to next hour
118
		h12++;
119
		m30 = 0;
120
	}
121
122
	return String.fromCodePoint((128335 + h12) + m30);
123
}
124
125
function clearDisplay() {
126
	document.getElementById("btn_mnext").disabled = true;
127
	document.getElementById("btn_mprev").disabled = true;
128
	document.getElementById("readmsg_export").hidden = true;
129
130
	const el = document.querySelector("#readmsg_main > img, #readmsg_main > audio, #readmsg_main > video, #readmsg_main > embed, #readmsg_main > iframe");
131
	if (!el) return;
132
	if (el.src) URL.revokeObjectURL(el.src);
133
	el.remove();
134
}
135
136
function clearMsgFlags() {
137
	document.getElementById("readmsg_flags").children[0].replaceChildren();
138
}
139
140
function addMsgFlag(abbr, abbrTitle) {
141
	const parent = document.getElementById("readmsg_flags").children[0];
142
143
	const el = document.createElement("abbr");
144
	el.title = abbrTitle;
145
	el.textContent = abbr;
146
147
	parent.appendChild(document.createTextNode(" "));
148
	parent.appendChild(el);
149
}
150
151
function displayFile(isHistory, num, showNext) {
152
	if (num < 0 || num >= ae.getUplMsgCount()) return;
0 ignored issues
show
Comprehensibility Best Practice introduced by
Are you sure this return statement is not missing an argument? If this is intended, consider adding an explicit undefined like return undefined;.
Loading history...
153
154
	const fileType = ae.getUplMsgType(num);
155
	if (!fileType) {
156
		if (isHistory) return;
0 ignored issues
show
Comprehensibility Best Practice introduced by
Are you sure this return statement is not missing an argument? If this is intended, consider adding an explicit undefined like return undefined;.
Loading history...
157
		if (showNext === true) return displayFile(false, num + 1, true);
158
		if (showNext === false) return displayFile(false, num - 1, false);
159
		ae.downloadUplMsg(num); return;
0 ignored issues
show
Comprehensibility Best Practice introduced by
Are you sure this return statement is not missing an argument? If this is intended, consider adding an explicit undefined like return undefined;.
Loading history...
160
	}
161
162
	clearDisplay();
163
	document.querySelector("article").scroll(0, 0);
164
	document.querySelector("article").setAttribute("data-msgid", ae.getUplMsgIdHex(num));
165
166
	document.getElementById("btn_mdele").disabled = false;
167
	document.getElementById("btn_msave").disabled = false;
168
	document.getElementById("btn_reply").disabled = true;
169
170
	document.getElementById("btn_msave").onclick = function() {ae.downloadUplMsg(num);};
171
172
	document.getElementById("readmsg_info").hidden = true;
173
	document.querySelector("#readmsg_main > h1").textContent = ae.getUplMsgTitle(num);
174
175
	msgDisplay = new MsgInfo(ae.getUplMsgIdHex(num), "upl", num);
176
	if (!isHistory) history.pushState({tab: tab, page: tabs[tab].cur, msg: msgDisplay}, null);
177
178
	document.getElementById("main2").hidden = false;
179
	document.getElementById("main1").hidden = !window.matchMedia("(min-width: 80em)").matches;
180
181
	document.getElementById("btn_mnext").disabled = (num === ae.getUplMsgCount() - 1);
182
	document.getElementById("btn_mprev").disabled = (num === 0);
183
	document.getElementById("btn_mnext").onclick = function() {displayFile(false, num + 1, true);};
184
	document.getElementById("btn_mprev").onclick = function() {displayFile(false, num - 1, false);};
185
186
	if (fileType === "text") {
187
		document.querySelector("#readmsg_main > pre").hidden = false;
188
		try {
189
			document.querySelector("#readmsg_main > pre").textContent = sodium.to_string(ae.getUplMsgBody(num));
190
		} catch(e) {
191
			document.querySelector("#readmsg_main > pre").textContent = "Failed decoding body: " + e.message;
192
		}
193
		return;
0 ignored issues
show
Comprehensibility Best Practice introduced by
Are you sure this return statement is not missing an argument? If this is intended, consider adding an explicit undefined like return undefined;.
Loading history...
194
	}
195
196
	document.querySelector("#readmsg_main > pre").hidden = true;
197
	let el;
198
199
	switch (fileType) {
200
		case "image": {
201
			el = document.createElement("img");
202
			el.src = URL.createObjectURL(new Blob([ae.getUplMsgBody(num).buffer]));
203
			el.onclick = function() {
204
				if (!document.fullscreen)
205
					this.requestFullscreen();
206
				else
207
					document.exitFullscreen();
208
			};
209
		break;}
210
211
		case "audio":
212
		case "video": {
213
			el = document.createElement(fileType);
214
			el.controls = "controls";
215
			el.src = URL.createObjectURL(new Blob([ae.getUplMsgBody(num).buffer]));
216
		break;}
217
218
		case "pdf": {
219
			el = document.createElement("embed");
220
			el.type = "application/pdf";
221
			el.src = URL.createObjectURL(new Blob([ae.getUplMsgBody(num).buffer], {type: "application/pdf"}));
222
		break;}
223
224
		case "html": {
225
			el = document.createElement("iframe");
226
			el.allow = "vertical-scroll";
227
			el.sandbox = "";
228
			el.referrerPolicy = "no-referrer";
229
230
			try {
231
				const sanBody = document.createElement("body");
232
				sanBody.setHTML(sodium.to_string(ae.getUplMsgBody(num).buffer), {sanitizer: new Sanitizer({
0 ignored issues
show
Bug introduced by
The variable Sanitizer seems to be never declared. If this is a global, consider adding a /** global: Sanitizer */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
233
					"allowElements": [
234
						"a","div","p",
235
						"h1","h2","h3","h4","h5","h6",
236
						"em","strong","b","i","u"
237
					],
238
					'allowAttributes': {
239
						"href": ["*"]
240
					}
241
				})});
242
243
				el.srcdoc =
244
				"<html>" +
245
					"<head>" +
246
						"<style>" +
247
							"html, body {background: #080a08; color: #fff; scrollbar-color: #222 #333;}\n" +
248
							"body {opacity:0.55;}\n" +
249
							"body > *:first-child {margin-top: 0; padding-top: 0;}\n" +
250
							"a {color: #fff;}\n" +
251
							"button, input, select, textarea {background: #000; color: #fff;}\n" +
252
						"</style>" +
253
					"</head>" +
254
					sanBody.outerHTML +
255
				"</html>";
256
			} catch(e) {
257
				el.srcdoc = "<!doctype html><html><head><style>body {background: #080a08; color: #fff; opacity:0.55;} h1 {margin: 0;}</style><body><h1>Error</h1><p>" + e.message + "</p></body></html>";
258
			}
259
		break;}
260
261
		case "svg": {
262
			el = document.createElement("iframe");
263
			el.allow = "";
264
			el.sandbox = "";
265
			el.referrerPolicy = "no-referrer";
266
			el.srcdoc = "<!doctype><html><head><style>body,html,svg {margin: 0; padding: 0; border: 0; height: 100%; width: 100%; display: block; background: #080a08;}</style></head><body>" + sodium.to_string(ae.getUplMsgBody(num).buffer) + "</body></html>";
267
		break;}
268
269
		default: return;
0 ignored issues
show
Comprehensibility Best Practice introduced by
Are you sure this return statement is not missing an argument? If this is intended, consider adding an explicit undefined like return undefined;.
Loading history...
270
	}
271
272
	document.getElementById("readmsg_main").appendChild(el);
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
273
}
274
275
document.getElementById("btn_leave").onclick = function() {
276
	document.getElementById("main2").hidden = true;
277
	document.getElementById("main1").hidden = false;
278
};
279
280
function displayMsg(isHistory, isInt, num) {
281
	clearDisplay();
282
	document.getElementById("btn_mdele").disabled = false;
283
284
	document.querySelector("article").scroll(0, 0);
285
	document.querySelector("article").setAttribute("data-msgid", isInt? ae.getIntMsgIdHex(num) : ae.getExtMsgIdHex(num));
286
287
	document.getElementById("btn_msave").disabled = false;
288
	document.getElementById("btn_msave").onclick = function() {displayExport(false, isInt, num);};
289
290
	const ts = isInt? ae.getIntMsgTime(num) : ae.getExtMsgTime(num);
291
292
	if (!isInt || (ae.getIntMsgFrom(num) !== "public" && ae.getIntMsgFrom(num) !== "system")) {
293
		document.getElementById("btn_reply").disabled = false;
294
295
		document.getElementById("btn_reply").onclick = function() {
296
			document.getElementById("write_recv").value = isInt? ae.getIntMsgFrom(num) : ae.getExtMsgReplyAddress(num);
297
			document.getElementById("write_subj").value = isInt? ae.getIntMsgTitle(num) : ae.getExtMsgTitle(num);
298
			if (!document.getElementById("write_subj").value.startsWith("Re:")) document.getElementById("write_subj").value = "Re: " + document.getElementById("write_subj").value;
299
			document.querySelector("#write2_pkey > input").value = isInt? ae.getIntMsgApk(num) : "";
300
301
			document.getElementById("write_recv").readOnly = !isInt;
302
			document.getElementById("write_subj").readOnly = !isInt;
303
			document.getElementById("write_subj").setAttribute("data-replyid", isInt? "" : ae.getExtMsgHdrId(num));
304
305
			tabs[TAB_WRITE].cur = 0;
306
			document.getElementById("btn_write").disabled = false;
307
			document.getElementById("btn_write").click();
308
			document.getElementById("write_body").focus();
309
310
			for (const opt of document.getElementById("write_from").options) {
311
				if (opt.value === (isInt ? ae.getIntMsgTo(num) : ae.getExtMsgEnvTo(num).split("@")[0].toLowerCase())) {
312
					opt.selected = true;
313
				}
314
			}
315
		};
316
	} else {
317
		document.getElementById("btn_reply").disabled = true;
318
	}
319
320
	document.getElementById("readmsg_info").hidden = false;
321
	document.querySelector("#readmsg_main > pre").hidden = false;
322
323
	document.getElementById("readmsg_envto").textContent = isInt ? "" : ae.getExtMsgEnvTo(num);
324
	document.getElementById("readmsg_hdrto").textContent = isInt ? ae.getIntMsgTo(num) : (ae.getExtMsgHdrTo(num));
325
	if(!isInt && ae.getExtMsgDnTo(num)) {
326
		const span = document.createElement("span");
327
		span.className = "sans";
328
		span.textContent = " • " + ae.getExtMsgDnTo(num);
329
		document.getElementById("readmsg_hdrto").appendChild(span);
330
	}
331
332
	const tzOs = new Date().getTimezoneOffset();
333
	const msgDate = new Date((ts * 1000) + (tzOs * -60000));
334
	document.getElementById("readmsg_date").children[0].textContent = getClockIcon(msgDate);
335
	document.getElementById("readmsg_date").children[1].dateTime = new Date(ts * 1000).toISOString();
336
337
	if (isInt) {
338
		document.querySelector("#readmsg_main > h1").textContent = ae.getIntMsgTitle(num);
339
		document.querySelector("#readmsg_main > pre").textContent = ae.getIntMsgBody(num);
340
341
		document.getElementById("readmsg_date").children[1].textContent = msgDate.toISOString().slice(0, 19).replace("T", " ");
342
343
		document.getElementById("readmsg_ip").style.visibility = "hidden";
344
		document.getElementById("readmsg_rdns").style.visibility = "hidden";
345
		document.getElementById("readmsg_dkim").style.visibility = "hidden";
346
		document.getElementById("readmsg_greet").style.visibility = "hidden";
347
		document.getElementById("readmsg_cert").style.visibility = "hidden";
348
		document.getElementById("readmsg_envfrom").style.visibility = "hidden";
349
		document.getElementById("readmsg_envto").style.visibility = "hidden";
350
351
		if (ae.getIntMsgFrom(num) !== "system" && ae.getIntMsgFrom(num) !== "public") {
352
			document.getElementById("readmsg_tls").style.visibility = "visible";
353
			document.getElementById("readmsg_tls").children[0].textContent = ae.getIntMsgApk(num);
354
		} else document.getElementById("readmsg_tls").style.visibility = "hidden";
355
356
		let symbol = document.createElement("span");
357
		if      (ae.getIntMsgLevel(num) === 3 && ae.getIntMsgFrom(num) === "system") {symbol.title = "System message"; symbol.textContent = "🅢";}
358
		else if (ae.getIntMsgLevel(num) === 3 && ae.getIntMsgFrom(num) === "public") {symbol.title = "Public announcement"; symbol.textContent = "🅟";}
359
		else if (ae.getIntMsgLevel(num) === 3) {symbol.title = "Administrator"; symbol.textContent = "🅐";}
360
		else if (ae.getIntMsgLevel(num) === 2) {symbol.title = "Level 2";  symbol.textContent = "➋";}
361
		else if (ae.getIntMsgLevel(num) === 1) {symbol.title = "Level 1";  symbol.textContent = "➊";}
362
		else if (ae.getIntMsgLevel(num) === 0) {symbol.title = "Level 0";  symbol.textContent = "🄌";}
363
		else {symbol.title = "Invalid level"; symbol.textContent = "⚠";}
364
365
		document.getElementById("readmsg_hdrfrom").replaceChildren(symbol, document.createTextNode(" " + ae.getIntMsgFrom(num)));
366
367
		clearMsgFlags();
368
		if (!ae.getIntMsgFlagVPad(num)) addMsgFlag("PAD", "Invalid padding");
369
		if (!ae.getIntMsgFlagVSig(num)) addMsgFlag("SIG", "Invalid signature");
370
		if ( ae.getIntMsgFlagE2ee(num)) addMsgFlag("E2EE", "End-to-end encrypted");
371
	} else {
372
		const headers = document.createElement("p");
373
		headers.textContent = ae.getExtMsgHeaders(num);
374
		headers.className = "mono";
375
		headers.hidden = !showHeaders;
376
377
		const body = document.createElement("p");
378
		body.innerHTML = ae.getExtMsgBody(num, false);
379
380
		document.querySelector("#readmsg_main > pre").replaceChildren(headers, body);
381
382
		const h1 = document.querySelector("#readmsg_main > h1");
383
		h1.textContent = ae.getExtMsgTitle(num);
384
		h1.style.cursor = headers.textContent? "pointer" : "";
385
		h1.onclick = function() {
386
			if (!headers.textContent) return;
387
			showHeaders = !showHeaders;
388
			headers.hidden = !showHeaders;
389
		};
390
391
		let hdrSecs = Math.abs(ae.getExtMsgHdrTime(num));
392
		let hdrTime = "";
393
		if (hdrSecs >= 3600) {
394
			const hdrHours = Math.floor(hdrSecs / 3600);
395
			hdrTime += hdrHours.toString() + "h ";
396
			hdrSecs -= hdrHours * 3600;
397
		}
398
		if (hdrSecs >= 60) {
399
			const hdrMins = Math.floor(hdrSecs / 60);
400
			hdrTime += hdrMins.toString() + "m ";
401
			hdrSecs -= hdrMins * 60;
402
		}
403
		hdrTime += hdrSecs + "s";
404
405
		const hdrTz = (ae.getExtMsgHdrTz(num) >= 0 ? "+" : "-") + Math.floor(Math.abs(ae.getExtMsgHdrTz(num)) / 60).toString().padStart(2, "0") + (Math.abs(ae.getExtMsgHdrTz(num)) % 60).toString().padStart(2, "0");
406
407
		let spans = [document.createElement("span"), document.createElement("span"), document.createElement("span")];
408
		spans[0].textContent = msgDate.toISOString().slice(0, 19).replace("T", " ");
409
		spans[1].className = "sans";
410
		spans[1].textContent = " • ";
411
		spans[2].textContent = hdrTz + " " + ((ae.getExtMsgHdrTime(num) >= 0) ? "+" : "-") + hdrTime;
412
		document.getElementById("readmsg_date").children[1].replaceChildren(...spans);
413
414
		document.getElementById("readmsg_ip").style.visibility = "visible";
415
		document.getElementById("readmsg_rdns").style.visibility = "visible";
416
		document.getElementById("readmsg_dkim").style.visibility = "visible";
417
		document.getElementById("readmsg_greet").style.visibility = "visible";
418
		document.getElementById("readmsg_tls").style.visibility = "visible";
419
		document.getElementById("readmsg_cert").style.visibility = "visible";
420
		document.getElementById("readmsg_envfrom").style.visibility = "visible";
421
		document.getElementById("readmsg_envto").style.visibility = "visible";
422
423
		// DKIM
424
		let dkim = "";
425
		if (ae.getExtMsgDkim(num)) {
426
			[ // Look for a matching domain in this order
427
				ae.getExtMsgHdrFrom(num).split("@")[1],
428
				ae.getExtMsgEnvFrom(num).split("@")[1],
429
				ae.getExtMsgRdns(num),
430
				ae.getExtMsgGreet(num),
431
				ae.getExtMsgTlsDomain(num)
432
			].forEach(function(dom) {
433
				if (dkim) return;
434
				for (let i = 0; i < ae.getExtMsgDkim(num).domain.length; i++) {
435
					if (ae.getExtMsgDkim(num).domain[i] === dom) {
436
						dkim = dom + " ✓";
437
						return;
438
					}
439
				}
440
			});
441
442
			if (!dkim) dkim = ae.getExtMsgDkim(num).domain[0]; // Default to first signature domain
443
		}
444
445
		if (ae.getExtMsgFlagDkFl(num)) dkim += " (fail)";
446
447
		// Left side
448
		document.getElementById("readmsg_country").textContent = getCountryFlag(ae.getExtMsgCcode(num));
449
		document.getElementById("readmsg_country").title = ae.getExtMsgCname(num);
450
		document.getElementById("readmsg_ip").children[1].textContent = ae.getExtMsgIp(num) + (ae.getExtMsgFlagIpBl(num) ? " ❗" : "");
451
		document.getElementById("readmsg_ip").children[2].textContent = " • " + ae.getExtMsgAuSys(num);
452
		document.getElementById("readmsg_tls").children[0].textContent = ae.getExtMsgTLS(num);
453
454
		// Right side
455
		document.getElementById("readmsg_greet").children[0].textContent = ae.getExtMsgGreet(num) + (ae.getExtMsgFlagGrDm(num) ? " ✓" : "");
456
		document.getElementById("readmsg_rdns").children[0].textContent = ae.getExtMsgRdns(num) + (ae.getExtMsgGreet(num).toLowerCase() === ae.getExtMsgRdns(num).toLowerCase() ? " ✓" : "");
457
		document.getElementById("readmsg_cert").children[0].textContent = ae.getExtMsgTlsDomain(num) ? (ae.getExtMsgTlsDomain(num) + " ✓") : "";
458
		document.getElementById("readmsg_dkim").children[0].textContent = dkim;
459
		document.getElementById("readmsg_envfrom").textContent = ae.getExtMsgEnvFrom(num);
460
		document.getElementById("readmsg_hdrfrom").textContent = ae.getExtMsgHdrFrom(num);
461
		if (ae.getExtMsgDnFrom(num)) {
462
			const span = document.createElement("span");
463
			span.className = "sans";
464
			span.textContent = " • " + ae.getExtMsgDnFrom(num);
465
			document.getElementById("readmsg_hdrfrom").appendChild(span);
466
		}
467
468
		clearMsgFlags();
469
		if (!ae.getExtMsgFlagVPad(num)) addMsgFlag("PAD", "Invalid padding");
470
		if (!ae.getExtMsgFlagVSig(num)) addMsgFlag("SIG", "Invalid signature");
471
		if (!ae.getExtMsgFlagPExt(num)) addMsgFlag("SMTP", "The sender did not use the Extended (ESMTP) protocol");
472
		if ( ae.getExtMsgFlagRare(num)) addMsgFlag("RARE", "The sender issued unusual command(s)");
473
		if ( ae.getExtMsgFlagFail(num)) addMsgFlag("FAIL", "The sender issued invalid command(s)");
474
		if ( ae.getExtMsgFlagPErr(num)) addMsgFlag("PROT", "The sender violated the protocol");
475
	}
476
477
	document.getElementById("readmsg_main").hidden = false;
478
	document.getElementById("main2").hidden = false;
479
	document.getElementById("main1").hidden = !window.matchMedia("(min-width: 80em)").matches;
480
481
	msgDisplay = new MsgInfo(isInt? ae.getIntMsgIdHex(num) : ae.getExtMsgIdHex(num), isInt? "int" : "ext", num);
482
	if (!isHistory) history.pushState({tab: tab, page: tabs[tab].cur, msg: msgDisplay}, null);
483
}
484
485
function displayExport(isHistory, isInt, num) {
486
	clearDisplay();
487
	document.getElementById("readmsg_main").hidden = true;
488
	document.getElementById("readmsg_export").hidden = false;
489
	document.getElementById("btn_msave").blur();
490
	document.getElementById("btn_msave").disabled = true;
491
	document.getElementById("btn_reply").disabled = true;
492
	document.getElementById("btn_mdele").disabled = true;
493
494
//	document.querySelector("#readmsg_export > div:nth-child(1)").onclick = function() {};
495
	document.querySelector("#readmsg_export > div:nth-child(2)").onclick = function() {if (isInt) {ae.downloadIntMsg(num);} else {ae.downloadExtMsg(num);} displayMsg(false, isInt, num);};
496
	document.querySelector("#readmsg_export > div:nth-child(3)").onclick = function() {if (isInt) {ae.htmlIntMsg(num, true);} else {ae.htmlExtMsg(num, true);} displayMsg(false, isInt, num);};
497
	document.querySelector("#readmsg_export > div:nth-child(4)").onclick = function() {if (isInt) {ae.txtIntMsg(num, true);} else {ae.txtExtMsg(num, true);} displayMsg(false, isInt, num);};
498
	document.querySelector("#readmsg_export > div:nth-child(5)").onclick = function() {if (isInt) {ae.printIntMsg(num);} else {ae.printExtMsg(num);} displayMsg(false, isInt, num);};
499
	document.querySelector("#readmsg_export > div:nth-child(6)").onclick = function() {navigator.clipboard.writeText(isInt? ae.txtIntMsg(num, false) : ae.txtExtMsg(num, false)); displayMsg(false, isInt, num);};
500
501
	msgDisplay = new MsgInfo(isInt? ae.getIntMsgIdHex(num) : ae.getExtMsgIdHex(num), isInt? "int_exp" : "ext_exp", num);
502
	if (!isHistory) history.pushState({tab: tab, page: tabs[tab].cur, msg: msgDisplay}, null);
503
}
504
505
function displayOutMsg(isHistory, num) {
506
	clearDisplay();
507
	document.querySelector("article").scroll(0, 0);
508
	document.querySelector("article").setAttribute("data-msgid", ae.getOutMsgIdHex(num));
509
510
	document.getElementById("btn_mdele").disabled = false;
511
	document.getElementById("btn_msave").disabled = true;
512
	document.getElementById("btn_reply").disabled = true;
513
514
	document.getElementById("readmsg_info").hidden = false;
515
	document.querySelector("#readmsg_main > pre").hidden = false;
516
517
	document.querySelector("#readmsg_main > h1").textContent = ae.getOutMsgSubj(num);
518
	document.querySelector("#readmsg_main > pre").textContent = ae.getOutMsgBody(num);
519
520
	document.getElementById("readmsg_dkim").style.visibility    = "hidden";
521
	document.getElementById("readmsg_hdrto").style.visibility   = "visible";
522
	document.getElementById("readmsg_hdrfrom").style.visibility = "visible";
523
	document.getElementById("readmsg_envto").style.visibility   = "visible";
524
	document.getElementById("readmsg_envfrom").style.visibility = "hidden";
525
526
	document.getElementById("readmsg_hdrfrom").textContent = ae.getOutMsgFrom(num);
527
528
	document.getElementById("readmsg_envto").textContent = ae.getOutMsgMxDom(num);
529
	document.getElementById("readmsg_hdrto").textContent = ae.getOutMsgTo(num);
530
531
	const ts = ae.getOutMsgTime(num);
532
	const tzOs = new Date().getTimezoneOffset();
533
	document.getElementById("readmsg_date").children[1].textContent = new Date((ts * 1000) + (tzOs * -60000)).toISOString().slice(0, 19).replace("T", " ");
534
535
	const isInt = ae.getOutMsgIsInt(num);
536
	document.getElementById("readmsg_ip").style.visibility    = isInt? "hidden" : "visible";
537
	document.getElementById("readmsg_rdns").style.visibility  = /*isInt?*/ "hidden" /*: "visible"*/; // TODO
538
	document.getElementById("readmsg_tls").style.visibility   = /*isInt?*/ "hidden" /*: "visible"*/; // TODO
539
	document.getElementById("readmsg_cert").style.visibility  = /*isInt?*/ "hidden" /*: "visible"*/; // TODO
540
	document.getElementById("readmsg_greet").style.visibility = isInt? "hidden" : "visible";
541
542
	if (!isInt) {
543
		document.getElementById("readmsg_ip").children[1].textContent = ae.getOutMsgIp(num);
544
		document.getElementById("readmsg_country").textContent = getCountryFlag(ae.getOutMsgCcode(num));
545
		document.getElementById("readmsg_country").title = ae.getOutMsgCname(num);
546
//		document.getElementById("readmsg_tls").children[0].textContent = ae.getOutMsgTLS(num);
547
		document.getElementById("readmsg_greet").children[0].textContent = ae.getOutMsgGreet(num);
548
	}
549
550
	clearMsgFlags();
551
	if (!ae.getOutMsgFlagVPad(num)) addMsgFlag("PAD", "Invalid padding");
552
	if (!ae.getOutMsgFlagVSig(num)) addMsgFlag("SIG", "Invalid signature");
553
	if ( ae.getOutMsgFlagE2ee(num)) addMsgFlag("E2EE", "End-to-end encrypted");
554
555
	document.getElementById("main2").hidden = false;
556
	document.getElementById("main1").hidden = !window.matchMedia("(min-width: 80em)").matches;
557
558
	msgDisplay = new MsgInfo(ae.getOutMsgIdHex(num), "out", num);
559
	if (!isHistory) history.pushState({tab: tab, page: tabs[tab].cur, msg: msgDisplay}, null);
560
}
561
562
function updateAddressButtons() {
563
	const limitReached = (ae.getAddressCountNormal() + ae.getAddressCountShield() >= 31);
564
	document.getElementById("btn_address_create_normal").disabled = (limitReached || ae.getAddressCountNormal() >= ae.getLimitNormalA(ae.getOwnLevel()));
565
	document.getElementById("btn_address_create_shield").disabled = (limitReached || ae.getAddressCountShield() >= ae.getLimitShieldA(ae.getOwnLevel()));
566
}
567
568
function updateAddressCounts() {
569
	document.querySelector("#tbd_accs > tr > td:nth-child(3)").textContent = ae.getAddressCountNormal();
570
	document.querySelector("#tbd_accs > tr > td:nth-child(4)").textContent = ae.getAddressCountShield();
571
572
	document.getElementById("limit_normal").textContent = (ae.getAddressCountNormal() + "/" + ae.getLimitNormalA(ae.getOwnLevel())).padStart(ae.getLimitNormalA(ae.getOwnLevel()) > 9 ? 5 : 1);
573
	document.getElementById("limit_shield").textContent = (ae.getAddressCountShield() + "/" + ae.getLimitShieldA(ae.getOwnLevel())).padStart(ae.getLimitShieldA(ae.getOwnLevel()) > 9 ? 5 : 1);
574
	document.getElementById("limit_total").textContent = ((ae.getAddressCountNormal() + ae.getAddressCountShield()) + "/" + ae.getAddrPerUser()).padStart(5);
575
576
	updateAddressButtons();
577
}
578
579
function addOwnAccount() {
580
	const row = document.getElementById("tbd_accs").insertRow(-1);
581
582
	let cell;
583
	cell = row.insertCell(-1); cell.textContent = ae.getOwnUpk();
584
	cell = row.insertCell(-1); cell.textContent = Math.round(ae.getTotalMsgBytes() / 1048576); // MiB
585
	cell = row.insertCell(-1); cell.textContent = ae.getAddressCountNormal();
586
	cell = row.insertCell(-1); cell.textContent = ae.getAddressCountShield();
587
588
	cell = row.insertCell(-1);
589
	let btn = document.createElement("button");
590
	btn.type = "button";
591
	btn.textContent = "+";
592
	btn.disabled = true;
593
	cell.appendChild(btn);
594
595
	cell = row.insertCell(-1); cell.textContent = ae.getOwnLevel();
596
597
	cell = row.insertCell(-1);
598
	btn = document.createElement("button");
599
	btn.type = "button";
600
	btn.textContent = "−";
601
	btn.disabled = true;
602
	btn.id = "btn_lowme";
603
	btn.onclick = function() {
604
		const newLevel = parseInt(row.cells[5].textContent, 10) - 1;
605
		ae.Account_Update(ae.getOwnUpk(), newLevel, function(error) {
606
			if (error === 0) {
607
				row.cells[5].textContent = newLevel;
608
				if (newLevel === 0) {document.getElementById("btn_lowme").disabled = true;}
609
			} else errorDialog(error);
610
		});
611
	};
612
	cell.appendChild(btn);
613
614
	cell = row.insertCell(-1);
615
	btn = document.createElement("button");
616
	btn.type = "button";
617
	btn.textContent = "X";
618
	btn.disabled = true;
619
	btn.id = "btn_delme";
620
	btn.onclick = function() {
621
		ae.Account_Delete(ae.getOwnUpk(), function(error) {
622
			if (error === 0) {
623
				row.remove();
624
				document.getElementById("fs_users").disabled = true;
625
			} else errorDialog(error);
626
		});
627
	};
628
	cell.appendChild(btn);
629
}
630
631
function adjustLevel(pubkey, level, c) {
632
	const fs = document.getElementById("tbl_accs");
633
	fs.disabled = true;
634
635
	ae.Account_Update(pubkey, level, function(error) {
636
		fs.disabled = false;
637
638
		if (error === 0) {
639
			c[4].children[0].disabled = (level === 3);
640
			c[5].textContent = level;
641
			c[6].children[0].disabled = (level === 0);
642
		} else errorDialog(error);
643
	});
644
}
645
646
function addMsg(isInt, i) {
647
	const row = document.getElementById("tbl_inbox").insertRow(-1);
648
	row.setAttribute("data-msgid", isInt? ae.getIntMsgIdHex(i) : ae.getExtMsgIdHex(i));
649
650
	const ts = isInt? ae.getIntMsgTime(i) : ae.getExtMsgTime(i);
651
	const el = document.createElement("time");
652
	el.dateTime = new Date(ts * 1000).toISOString();
653
	el.textContent = new Date((ts * 1000) + (new Date().getTimezoneOffset() * -60000)).toISOString().slice(0, 10);
654
	let cell = row.insertCell(-1);
655
	cell.appendChild(el);
656
657
	cell = row.insertCell(-1);
658
	cell.textContent = isInt? ae.getIntMsgTitle(i) : ae.getExtMsgTitle(i);
659
	if (!cell.textContent) cell.textContent = "(fail)";
660
661
	if (isInt) {
662
		cell = row.insertCell(-1);
663
		cell.textContent = ae.getIntMsgFrom(i);
664
		cell.className = (ae.getIntMsgFrom(i).length === 16) ? "mono" : "";
665
	} else {
666
		let from1 = ae.getExtMsgHdrFrom(i);
667
		if (!from1) from1 = ae.getExtMsgEnvFrom(i);
668
		const from2 = from1.substring(from1.indexOf("@") + 1);
669
		cell = row.insertCell(-1);
670
		cell.textContent = from1.substring(0, from1.indexOf("@"));
671
672
		const flag = document.createElement("abbr");
673
		flag.textContent = getCountryFlag(ae.getExtMsgCcode(i));
674
		flag.title = ae.getExtMsgCname(i);
675
676
		const fromText = document.createElement("span");
677
		fromText.textContent = " " + from2;
678
679
		cell = row.insertCell(-1);
680
		cell.appendChild(flag);
681
		cell.appendChild(fromText);
682
	}
683
684
	row.onclick = function() {
685
		displayMsg(false, isInt, i);
686
	};
687
}
688
689
function setRowsPerPage(tbl) {
690
	tbl.replaceChildren();
691
	const row = tbl.insertRow(-1);
692
	const cell = row.insertCell(-1);
693
	cell.textContent = "0";
694
	rowsPerPage = Math.floor(getComputedStyle(tbl).height.replace("px", "") / getComputedStyle(tbl.getElementsByTagName("tr")[0]).height.replace("px", ""));
695
	tbl.replaceChildren();
696
}
697
698
function showInbox() {
699
	const tbl = document.getElementById("tbl_inbox");
700
	if (!document.getElementById("main1").hidden) setRowsPerPage(tbl);
701
702
	const maxExt = ae.getExtMsgCount();
703
	const maxInt = ae.getIntMsgCount();
704
	const loadMore = ae.getReadyMsgBytes() < ae.getTotalMsgBytes();
705
706
	if (maxExt + maxInt > 0) {
707
		tabs[TAB_INBOX].max = Math.floor((maxExt + maxInt - (loadMore? 0 : 1)) / rowsPerPage);
708
		document.getElementById("btn_rght").disabled = (tabs[TAB_INBOX].cur >= tabs[TAB_INBOX].max);
709
		tbl.replaceChildren();
710
711
		let skipMsgs = rowsPerPage * tabs[TAB_INBOX].cur;
712
		let numExt = 0;
713
		let numInt = 0;
714
		let numAdd = 0;
715
716
		while (numAdd < rowsPerPage) {
717
			const tsInt = (numInt < maxInt) ? ae.getIntMsgTime(numInt) : -1;
718
			const tsExt = (numExt < maxExt) ? ae.getExtMsgTime(numExt) : -1;
719
			if (tsInt === -1 && tsExt === -1) break;
720
721
			if (tsInt !== -1 && (tsExt === -1 || tsInt > tsExt)) {
722
				if (skipMsgs > 0) skipMsgs--; else {addMsg(true, numInt); numAdd++;}
723
				numInt++;
724
			} else if (tsExt !== -1) {
725
				if (skipMsgs > 0) skipMsgs--; else {addMsg(false, numExt); numAdd++;}
726
				numExt++;
727
			}
728
		}
729
	} else {
730
		tabs[TAB_INBOX].max = 0;
731
	}
732
733
	if (loadMore && tabs[TAB_INBOX].cur >= tabs[TAB_INBOX].max) {
734
		const row = tbl.insertRow(-1);
735
		const cell = row.insertCell(-1);
736
		cell.textContent = "Load more (" + Math.round((ae.getTotalMsgBytes() - ae.getReadyMsgBytes()) / 1024) + " KiB left)";
737
738
		row.onclick = function() {
739
			tbl.style.opacity = 0.5;
740
741
			ae.Message_Browse(false, false, function(errorBrowse) {
742
				tbl.style.opacity = 1;
743
744
				if (errorBrowse !== 0) {
745
					errorDialog(errorBrowse);
746
					return;
747
				}
748
749
				showInbox();
750
			});
751
		};
752
	}
753
}
754
755
function showDrbox() {
756
	const tbl = document.getElementById("tbl_drbox");
757
	if (!document.getElementById("main1").hidden) setRowsPerPage(tbl);
758
759
	const drCount = ae.getOutMsgCount();
760
	const loadMore = ae.getReadyMsgBytes() < ae.getTotalMsgBytes();
761
762
	if (drCount > 0) {
763
		tabs[TAB_DRBOX].max = Math.floor((drCount - (loadMore? 0 : 1)) / rowsPerPage);
764
		document.getElementById("btn_rght").disabled = (tabs[TAB_DRBOX].cur >= tabs[TAB_DRBOX].max);
765
		tbl.replaceChildren();
766
767
		let skipMsgs = rowsPerPage * tabs[TAB_DRBOX].cur;
768
		let numAdd = 0;
769
770
		for (let i = 0; numAdd < rowsPerPage && i < drCount; i++) {
771
			if (skipMsgs > 0) {
772
				skipMsgs--;
773
				continue;
774
			}
775
776
			const row = tbl.insertRow(-1);
777
			row.setAttribute("data-msgid", ae.getOutMsgIdHex(i));
778
779
			let cell;
780
			cell = row.insertCell(-1); cell.textContent = new Date(ae.getOutMsgTime(i) * 1000).toISOString().slice(0, 10);
781
			cell = row.insertCell(-1); cell.textContent = ae.getOutMsgSubj(i);
782
			row.onclick = function() {displayOutMsg(false, i);};
783
784
			numAdd++;
785
		}
786
	} else {
787
		tabs[TAB_DRBOX].max = 0;
788
	}
789
790
	if (loadMore && tabs[TAB_DRBOX].cur >= tabs[TAB_DRBOX].max) {
791
		const row = tbl.insertRow(-1);
792
		const cell = row.insertCell(-1);
793
		cell.textContent = "Load more (" + Math.round((ae.getTotalMsgBytes() - ae.getReadyMsgBytes()) / 1024) + " KiB left)";
794
795
		row.onclick = function() {
796
			tbl.style.opacity = 0.5;
797
798
			ae.Message_Browse(false, false, function(errorBrowse) {
799
				tbl.style.opacity = 1;
800
801
				if (errorBrowse !== 0) {
802
					errorDialog(errorBrowse);
803
					return;
804
				}
805
806
				showDrbox();
807
			});
808
		};
809
	}
810
}
811
812
function showFiles() {
813
	const tbl = document.getElementById("tbl_files");
814
	if (!document.getElementById("main1").hidden) setRowsPerPage(tbl);
815
816
	const msgCount = ae.getUplMsgCount() + (vaultOk? vault.getFileCount() : 0);
817
	const loadMore = (ae.getReadyMsgBytes() < ae.getTotalMsgBytes()) || (vaultOk === false);
818
819
	if (msgCount > 0) {
820
		tabs[TAB_NOTES].max = 2 + Math.floor((msgCount - (loadMore? 0 : 1)) / rowsPerPage);
821
		document.getElementById("btn_rght").disabled = (tabs[TAB_NOTES].cur >= tabs[TAB_NOTES].max);
822
		tbl.replaceChildren();
823
824
		let skipMsgs = rowsPerPage * (tabs[TAB_NOTES].cur - 2);
825
		let numAdd = 0;
826
827
		for (let i = 0; numAdd < rowsPerPage && i < ae.getUplMsgCount(); i++) {
828
			if (skipMsgs > 0) {
829
				skipMsgs--;
830
				continue;
831
			}
832
833
			const row = tbl.insertRow(-1);
834
			row.setAttribute("data-msgid", ae.getUplMsgIdHex(i));
835
			row.className = "rowfile";
836
837
			let cell = row.insertCell(-1);
838
			cell.textContent = new Date(ae.getUplMsgTime(i) * 1000).toISOString().slice(0, 10);
839
			cell.onclick = function() {displayFile(false, i, null);};
840
841
			cell = row.insertCell(-1);
842
			cell.textContent = (ae.getUplMsgBytes(i) / 1024).toFixed(0).padStart(4, " ");
843
			cell.onclick = function() {displayFile(false, i, null);};
844
845
			cell = row.insertCell(-1);
846
			const parentNum = ae.getUplMsgParent(i);
847
			if (typeof(parentNum) === "number") {
848
				cell.textContent = ae.getExtMsgTitle(parentNum);
849
				cell.onclick = function() {displayMsg(false, false, parentNum);};
850
			} else if (parentNum === false) {
851
				cell.textContent = "Upload";
852
			} else {
853
				cell.textContent = "Unknown";
854
			}
855
856
			cell = row.insertCell(-1);
857
			cell.textContent = ae.getUplMsgTitle(i);
858
			cell.onclick = function() {displayFile(false, i, null);};
859
860
			cell = row.insertCell(-1);
861
			const btn = document.createElement("button");
862
			btn.setAttribute("data-msgid", ae.getUplMsgIdHex(i));
863
			btn.type = "button";
864
			btn.textContent = "X";
865
			btn.onclick = function() {
866
				ae.Message_Delete(this.getAttribute("data-msgid"), function(error) {
867
					if (error === 0) showFiles();
868
					else errorDialog(error);
869
				});
870
			};
871
			cell.appendChild(btn);
872
873
			numAdd++;
874
		}
875
876
		if (vaultOk) {
877
			for (let i = 0; numAdd < rowsPerPage && i < 256; i++) {
878
				if (vault.getFileSize(i) < 1) continue;
879
880
				if (skipMsgs > 0) {
881
					skipMsgs--;
882
					continue;
883
				}
884
885
				const row = tbl.insertRow(-1);
886
				row.setAttribute("data-msgid", i);
887
				row.className = "rowfile";
888
889
				let cell = row.insertCell(-1);
890
				cell.textContent = new Date(vault.getFileTime(i) * 1000).toISOString().slice(0, 10);
891
892
				cell = row.insertCell(-1);
893
				cell.textContent = (vault.getFileSize(i) / 1024).toFixed(0).padStart(4, " ");
894
895
				cell = row.insertCell(-1);
896
				cell.textContent = "Vault";
897
898
				cell = row.insertCell(-1);
899
				cell.textContent = vault.getFileName(i);
900
				cell.onclick = function() {vault.downloadFile(i);};
901
902
				cell = row.insertCell(-1);
903
				const btn = document.createElement("button");
904
				btn.type = "button";
905
				btn.textContent = "X";
906
				btn.onclick = function() {
907
					vault.deleteFile(i, function(error) {
908
						if (error === 0) showFiles();
909
						else errorDialog(error);
910
					});
911
				};
912
				cell.appendChild(btn);
913
914
				numAdd++;
915
			}
916
		}
917
	} else {
918
		tabs[TAB_NOTES].max = (vaultOk === false) ? 3 : 2;
919
	}
920
921
	if (loadMore && tabs[TAB_NOTES].cur >= tabs[TAB_NOTES].max) {
922
		const row = tbl.insertRow(-1);
923
		row.className = "rowfilex";
924
925
		let cell = row.insertCell(-1);
926
		if (ae.getReadyMsgBytes() < ae.getTotalMsgBytes()) {
927
			cell.textContent = "Load more (" + Math.round((ae.getTotalMsgBytes() - ae.getReadyMsgBytes()) / 1024) + " KiB left)";
928
			cell.onclick = function() {
929
				tbl.style.opacity = 0.5;
930
931
				ae.Message_Browse(false, false, function(errorBrowse) {
932
					tbl.style.opacity = 1;
933
934
					if (errorBrowse !== 0) {
935
						errorDialog(errorBrowse);
936
						return;
937
					}
938
939
					showFiles();
940
				});
941
			};
942
		}
943
944
		cell = row.insertCell(-1);
945
		if (vaultOk === false) {
946
			cell.textContent = "Open PostVault";
947
			cell.onclick = function() {
948
				tbl.style.opacity = 0.5;
949
950
				vault.getInfo(function() {
951
					tbl.style.opacity = 1;
952
953
					// TODO check for error
954
955
					vaultOk = true;
956
					showFiles();
957
				});
958
			};
959
		}
960
	}
961
}
962
963
function addAccountToTable(i) {
964
	const row = document.getElementById("tbd_accs").insertRow(-1);
965
	let cell;
966
	cell = row.insertCell(-1); cell.textContent = ae.admin_getUserUpk(i);
967
	cell = row.insertCell(-1); cell.textContent = Math.round(ae.admin_getUserKib(i) / 1024);
968
	cell = row.insertCell(-1); cell.textContent = ae.admin_getUserNrm(i);
969
	cell = row.insertCell(-1); cell.textContent = ae.admin_getUserShd(i);
970
971
	cell = row.insertCell(-1);
972
	let btn = document.createElement("button");
973
	btn.type = "button";
974
	btn.textContent = "+";
975
	btn.disabled = (ae.admin_getUserLvl(i) === 3);
976
	btn.onclick = function() {const c = this.parentElement.parentElement.cells; adjustLevel(c[0].textContent, parseInt(c[5].textContent, 10) + 1, c);};
977
	cell.appendChild(btn);
978
979
	cell = row.insertCell(-1); cell.textContent = ae.admin_getUserLvl(i);
980
981
	cell = row.insertCell(-1);
982
	btn = document.createElement("button");
983
	btn.type = "button";
984
	btn.textContent = "−";
985
	btn.disabled = (ae.admin_getUserLvl(i) === 0);
986
	btn.onclick = function() {const c = this.parentElement.parentElement.cells; adjustLevel(c[0].textContent, parseInt(c[5].textContent, 10) - 1, c);};
987
	cell.appendChild(btn);
988
989
	cell = row.insertCell(-1);
990
	btn = document.createElement("button");
991
	btn.type = "button";
992
	btn.textContent = "X";
993
	btn.onclick = function() {
994
		const tr = this.parentElement.parentElement;
995
		ae.Account_Delete(tr.cells[0].textContent, function(error) {
996
			if (error === 0) tr.remove(); else errorDialog(error);
997
		});
998
	};
999
	cell.appendChild(btn);
1000
}
1001
1002
function updateLimits() {
1003
	const tbl = document.querySelector("#tbl_limits > tbody");
1004
1005
	if (ae.isUserAdmin()) {
1006
		for (let i = 0; i < 4; i++) {
1007
			tbl.rows[i].cells[1].children[0].value = ae.getLimitStorage(i);
1008
			tbl.rows[i].cells[2].children[0].value = ae.getLimitNormalA(i);
1009
			tbl.rows[i].cells[3].children[0].value = ae.getLimitShieldA(i);
1010
		}
1011
	} else {
1012
		const lvl = ae.getOwnLevel();
1013
		tbl.rows[lvl].cells[1].children[0].value = ae.getLimitStorage(lvl);
1014
		tbl.rows[lvl].cells[2].children[0].value = ae.getLimitNormalA(lvl);
1015
		tbl.rows[lvl].cells[3].children[0].value = ae.getLimitShieldA(lvl);
1016
	}
1017
}
1018
1019
function deleteAddress(addr) {
1020
	const buttons = document.querySelectorAll("#tbl_addrs button");
1021
	buttons.forEach(function(btn) {btn.disabled = true;});
1022
1023
	let addressToDelete = -1;
1024
	for (let i = 0; i < ae.getAddressCount(); i++) {
1025
		if (addr === ae.getAddress(i)) {
1026
			addressToDelete = i;
1027
			break;
1028
		}
1029
	}
1030
1031
	if (addressToDelete === -1) return;
1032
1033
	ae.Address_Delete(addressToDelete, function(error1) {
1034
		if (error1 !== 0) {
1035
			buttons.forEach(function(btn) {btn.disabled = false;});
1036
			errorDialog(error1);
1037
			return;
1038
		}
1039
1040
		document.getElementById("tbl_addrs").deleteRow(addressToDelete);
1041
		document.getElementById("write_from").remove(addressToDelete);
1042
1043
		if (ae.getAddressCountNormal() > 0) {
1044
			const apkList = document.getElementById("getapk_addr");
1045
			for (let i = 0; i < apkList.children.length; i++) {
1046
				if (apkList.children[i].value === addr) {
1047
					apkList.remove(i);
1048
					break;
1049
				}
1050
			}
1051
		} else {
1052
			document.getElementById("getapk_addr").replaceChildren();
1053
			document.getElementById("btn_getapk").disabled = true;
1054
		}
1055
1056
		updateAddressCounts();
1057
1058
		ae.Private_Update(function(error2) {
1059
			buttons.forEach(function(btn) {btn.disabled = false;});
1060
			if (error2) errorDialog(error2);
1061
		});
1062
	});
1063
}
1064
1065
function addAddress(num) {
1066
	const addrTable = document.getElementById("tbl_addrs");
1067
	const row = addrTable.insertRow(-1);
1068
	const addr = ae.getAddress(num);
1069
1070
	let cell = row.insertCell(-1);
1071
	cell.textContent = addr;
1072
	cell.onclick = function() {navigator.clipboard.writeText(((this.textContent.length === 16) ? ae.shieldMix(this.textContent) : this.textContent) + "@" + ae.getDomainEml());};
1073
1074
	cell = row.insertCell(-1);
1075
	let el = document.createElement("input");
1076
	el.type = "checkbox";
1077
	el.checked = ae.getAddressAccInt(num);
1078
	cell.appendChild(el);
1079
1080
	cell = row.insertCell(-1);
1081
	el = document.createElement("input");
1082
	el.type = "checkbox";
1083
	el.checked = ae.getAddressAccExt(num);
1084
	cell.appendChild(el);
1085
1086
	cell = row.insertCell(-1);
1087
	el = document.createElement("input");
1088
	el.type = "checkbox";
1089
	el.checked = ae.getAddressAllVer(num);
1090
	cell.appendChild(el);
1091
1092
	cell = row.insertCell(-1);
1093
	el = document.createElement("input");
1094
	el.type = "checkbox";
1095
	el.checked = ae.getAddressAttach(num);
1096
	cell.appendChild(el);
1097
1098
	cell = row.insertCell(-1);
1099
	el = document.createElement("input");
1100
	el.type = "checkbox";
1101
	el.checked = ae.getAddressSecure(num);
1102
	cell.appendChild(el);
1103
1104
	cell = row.insertCell(-1);
1105
	el = document.createElement("input");
1106
	el.type = "checkbox";
1107
	el.checked = ae.getAddressOrigin(num);
1108
	cell.appendChild(el);
1109
1110
	cell = row.insertCell(-1);
1111
	el = document.createElement("button");
1112
	el.type = "button";
1113
	el.textContent = "X";
1114
	el.onclick = function() {deleteAddress(addr);};
1115
	cell.appendChild(el);
1116
1117
	el = document.createElement("option");
1118
	el.value = addr;
1119
	el.textContent = addr + "@" + ae.getDomainEml();
1120
	document.getElementById("write_from").appendChild(el);
1121
1122
	if (addr.length !== 16) {
1123
		el = document.createElement("option");
1124
		el.value = addr;
1125
		el.textContent = addr;
1126
		document.getElementById("getapk_addr").appendChild(el);
1127
	}
1128
}
1129
1130
function clearWrite() {
1131
	setTab(false, TAB_WRITE, 0);
1132
1133
	document.querySelector("#write2_pkey > input").value = "";
1134
	document.getElementById("write_body").value = "";
1135
	document.getElementById("write_subj").value = "";
1136
	document.getElementById("write_subj").readOnly = false;
1137
	document.getElementById("write_subj").setAttribute("data-replyid", "");
1138
	document.getElementById("write_recv").value = "";
1139
	document.getElementById("write_recv").readOnly = false;
1140
	document.getElementById("write_recv").focus();
1141
}
1142
1143
// Interface
1144
if (window.matchMedia("(prefers-color-scheme: light)").matches) document.querySelector("head > meta[name='theme-color']").content = "#eef";
1145
window.matchMedia("(prefers-color-scheme: light)").onchange = function() {document.querySelector("head > meta[name='theme-color']").content = window.matchMedia("(prefers-color-scheme: light)").matches? "#eef" : "#001";};
1146
1147
document.getElementById("btn_dele").onclick = function() {
1148
	this.blur();
1149
1150
	if (tab === TAB_WRITE) clearWrite();
1151
};
1152
1153
document.getElementById("btn_updt").onclick = function() {
1154
	const btn = this;
1155
	btn.disabled = true;
1156
	btn.blur();
1157
1158
	if (tab === TAB_INBOX) {
1159
		document.getElementById("tbl_inbox").style.opacity = 0.5;
1160
1161
		ae.Message_Browse(true, false, function(error) {
1162
			btn.disabled = false;
1163
			document.getElementById("tbl_inbox").style.opacity = 1;
1164
1165
			if (error === 0) {
1166
				showInbox();
1167
			} else {
1168
				errorDialog(error);
1169
			}
1170
		});
1171
	}
1172
};
1173
1174
document.getElementById("btn_mdele").onclick = function() {
1175
	const delId = document.querySelector("article").getAttribute("data-msgid");
1176
	if (!delId) return;
1177
1178
	const btn = this;
1179
	btn.blur();
1180
	btn.disabled = true;
1181
1182
	ae.Message_Delete(delId, function(error) {
1183
		if (error !== 0) {
1184
			btn.disabled = false;
1185
			errorDialog(error);
1186
			return;
1187
		}
1188
1189
		switch (tab) {
1190
			case TAB_INBOX: showInbox(); break;
1191
			case TAB_DRBOX: showDrbox(); break;
1192
			case TAB_NOTES: showFiles(); break;
1193
		}
1194
	});
1195
};
1196
1197
function refreshContactList() {
1198
	let opts = [];
1199
1200
	for (let i = 0; i < ae.getContactCount(); i++) {
1201
		const el = document.createElement("option");
1202
		el.value = ae.getContactMail(i);
1203
		opts.push(el);
1204
	}
1205
1206
	if (ae.isUserAdmin()) {
1207
		const el = document.createElement("option");
1208
		el.value = "public";
1209
		opts.push(el);
1210
	}
1211
1212
	document.getElementById("contact_emails").replaceChildren(...opts);
1213
}
1214
1215
function addContact(mail, name, note) {
1216
	const tbl = document.getElementById("tbl_ctact");
1217
	const row = tbl.insertRow(-1);
1218
1219
	let cell = row.insertCell(-1);
1220
	cell.autocapitalize = "off";
1221
	cell.contentEditable = true;
1222
	cell.inputMode = "email";
1223
	cell.spellcheck = false;
1224
	cell.textContent = mail;
1225
1226
	cell = row.insertCell(-1);
1227
	cell.autocapitalize = "words";
1228
	cell.contentEditable = true;
1229
	cell.spellcheck = false;
1230
	cell.textContent = name;
1231
1232
	cell = row.insertCell(-1);
1233
	cell.autocapitalize = "off";
1234
	cell.contentEditable = true;
1235
	cell.spellcheck = false;
1236
	cell.textContent = note;
1237
1238
	cell = row.insertCell(-1);
1239
	const el = document.createElement("button");
1240
	el.type = "button";
1241
	el.textContent = "X";
1242
	el.onclick = function() {row.remove();};
1243
	cell.appendChild(el);
1244
}
1245
1246
function addContacts() {
1247
	for (let i = 0; i < ae.getContactCount(); i++) {
1248
		addContact(
1249
			ae.getContactMail(i),
1250
			ae.getContactName(i),
1251
			ae.getContactNote(i)
1252
		);
1253
	}
1254
1255
	refreshContactList();
1256
}
1257
1258
function addAddresses() {
1259
	for (let i = 0; i < ae.getAddressCount(); i++) {
1260
		addAddress(i);
1261
	}
1262
1263
	document.getElementById("btn_getapk").disabled = (ae.getAddressCountNormal() < 1);
1264
}
1265
1266
function reloadAccount() {
1267
	updateLimits();
1268
	addOwnAccount();
1269
	addContacts();
1270
	addAddresses();
1271
	updateAddressCounts();
1272
1273
	document.getElementById("fs_admin").disabled = !ae.isUserAdmin();
1274
	document.getElementById("txt_notepad").value = ae.getPrivateExtra();
1275
}
1276
1277
document.getElementById("btn_newcontact").onclick = function() {
1278
	addContact("", "", "");
1279
};
1280
1281
document.getElementById("btn_savecontacts").onclick = function() {
1282
	while (ae.getContactCount() > 0) {
1283
		ae.deleteContact(0);
1284
	}
1285
1286
	for (const row of document.getElementById("tbl_ctact").rows) {
1287
		ae.addContact(row.cells[0].textContent, row.cells[1].textContent, row.cells[2].textContent);
1288
	}
1289
1290
	refreshContactList();
1291
1292
	const btn = this;
1293
	btn.disabled = true;
1294
1295
	ae.Private_Update(function(error) {
1296
		btn.disabled = false;
1297
		if (error) errorDialog(error);
1298
	});
1299
};
1300
1301
function writeVerify() {
1302
	if (
1303
	   !document.getElementById("write_recv").reportValidity()
1304
	|| !document.getElementById("write_subj").reportValidity()
1305
	|| !document.getElementById("write_body").reportValidity()
1306
	) return false;
1307
1308
	document.getElementById("div_write_1").hidden = true;
1309
	document.getElementById("div_write_2").hidden = false;
1310
1311
	document.getElementById("write2_recv").textContent = document.getElementById("write_recv").value;
1312
	document.getElementById("write2_subj").textContent = document.getElementById("write_subj").value;
1313
	document.getElementById("write2_rply").textContent = document.getElementById("write_subj").getAttribute("data-replyid");
1314
	document.getElementById("write2_body").textContent = document.getElementById("write_body").value;
1315
1316
	if (document.getElementById("write_recv").value.indexOf("@") >= 0) {
1317
		document.getElementById("write2_from").textContent = document.getElementById("write_from").value + "@" + ae.getDomainEml();
1318
		document.getElementById("write2_pkey").hidden = true;
1319
	} else {
1320
		document.getElementById("write2_from").textContent = document.getElementById("write_from").value;
1321
		document.getElementById("write2_pkey").hidden = (document.getElementById("write_recv").value === "public");
1322
	}
1323
1324
	document.querySelector("#write2_send > button").disabled = false;
1325
	document.getElementById("write2_btntxt").textContent = (document.getElementById("write_recv").value === "public") ? "Make" : "Send to";
1326
	return true;
1327
}
1328
1329
function setTab(isHistory, tabNum, pageNum) {
1330
	tab = tabNum;
1331
	if (pageNum !== -1) tabs[tab].cur = pageNum;
1332
1333
	document.querySelectorAll("#main1 > nav:first-of-type > button").forEach(function(btn, i) {
1334
		document.querySelectorAll("#main1 > .mid > div")[i].hidden = (tab !== i);
1335
		btn.disabled = (tab === i);
1336
	});
1337
1338
	const bigScreen = window.matchMedia("(min-width: 80em)").matches;
1339
	document.getElementById("main2").hidden = !bigScreen;
1340
	document.getElementById("btn_leave").disabled = bigScreen;
1341
1342
	switch (tab) {
1343
		case TAB_INBOX: showInbox(); break;
1344
		case TAB_DRBOX: showDrbox(); break;
1345
1346
		case TAB_WRITE:
1347
			if (tabs[tab].cur === 0) {
1348
				document.getElementById("div_write_1").hidden = false;
1349
				document.getElementById("div_write_2").hidden = true;
1350
				document.getElementById("write_recv").focus();
1351
			} else if (!writeVerify()) {
1352
				tabs[TAB_WRITE].cur = 0;
1353
				return;
1354
			}
1355
		break;
1356
1357
		case TAB_NOTES:
1358
			switch (tabs[tab].cur) {
1359
				case 0:
1360
					document.getElementById("div_notes").children[0].hidden = false;
1361
					document.getElementById("div_notes").children[1].hidden = true;
1362
					document.getElementById("div_notes").children[2].hidden = true;
1363
				break;
1364
1365
				case 1:
1366
					document.getElementById("div_notes").children[0].hidden = true;
1367
					document.getElementById("div_notes").children[1].hidden = false;
1368
					document.getElementById("div_notes").children[2].hidden = true;
1369
1370
					document.querySelector("#div_notepad meter").value = ae.getPrivateExtraSpace() / ae.getPrivateExtraSpaceMax();
1371
				break;
1372
1373
				case 2:
1374
					document.getElementById("div_notes").children[0].hidden = true;
1375
					document.getElementById("div_notes").children[1].hidden = true;
1376
					document.getElementById("div_notes").children[2].hidden = false;
0 ignored issues
show
introduced by
This node falls through to the next case due to this statement. Please add a comment either directly below this line or between the cases to explain.
Loading history...
1377
1378
				default:
1379
					showFiles();
1380
			}
1381
		break;
1382
1383
		case TAB_TOOLS:
1384
			for (let i = 0; i <= tabs[tab].max; i++) {
1385
				document.getElementById("div_tools").children[i].hidden = (i !== tabs[tab].cur);
1386
			}
1387
		break;
1388
	}
1389
1390
	document.getElementById("btn_dele").disabled = !tabs[tab].btnDele;
1391
	document.getElementById("btn_left").disabled = (tabs[tab].cur === 0);
1392
	document.getElementById("btn_rght").disabled = (tabs[tab].cur === tabs[tab].max);
1393
	document.getElementById("btn_updt").disabled = !tabs[tab].btnUpdt;
1394
1395
	if (!isHistory) history.pushState({tab: tab, page: tabs[tab].cur, msg: msgDisplay}, null);
1396
}
1397
1398
window.onpopstate = function(event) {
1399
	if (!isReady || !event.state) return;
1400
	setTab(true, event.state.tab, event.state.page);
1401
	msgDisplay = event.state.msg;
1402
1403
	if (msgDisplay) {
1404
		switch (msgDisplay.type) {
1405
			case "ext": displayMsg(true, false, msgDisplay.num); break;
1406
			case "int": displayMsg(true, true, msgDisplay.num); break;
1407
			case "out": displayOutMsg(true, msgDisplay.num); break;
1408
			case "upl": displayFile(true, msgDisplay.num, null); break;
1409
			case "ext_exp": displayExport(true, false, msgDisplay.num); break;
1410
			case "int_exp": displayExport(true, true, msgDisplay.num); break;
1411
		}
1412
	}
1413
};
1414
1415
document.querySelectorAll("#main1 > nav:first-of-type > button").forEach(function(btn, i) {
1416
	btn.onclick = function() {setTab(false, i, -1);};
1417
});
1418
1419
document.getElementById("btn_left").onclick = function() {
1420
	setTab(false, tab, tabs[tab].cur - 1);
1421
};
1422
1423
document.getElementById("btn_rght").onclick = function() {
1424
	setTab(false, tab, tabs[tab].cur + 1);
1425
};
1426
1427
function addressCreate(addr) {
1428
	document.getElementById("btn_address_create_normal").disabled = true;
1429
	document.getElementById("btn_address_create_shield").disabled = true;
1430
1431
	ae.Address_Create(addr, function(error1) {
1432
		if (error1 !== 0) {
1433
			updateAddressButtons();
1434
			errorDialog(error1, (addr !== "SHIELD") ? document.getElementById("txt_address_create_normal") : null);
1435
			return;
1436
		}
1437
1438
		ae.Private_Update(function(error2) {
1439
			updateAddressCounts();
1440
1441
			addAddress(ae.getAddressCount() - 1);
1442
			if (addr !== "SHIELD") {
1443
				document.getElementById("btn_getapk").disabled = false;
1444
				document.getElementById("txt_address_create_normal").value = "";
1445
				document.getElementById("txt_address_create_normal").focus();
1446
			}
1447
1448
			if (error2 !== 0) errorDialog(error2, (addr !== "SHIELD") ? document.getElementById("txt_address_create_normal") : null);
1449
		});
1450
	});
1451
}
1452
1453
document.getElementById("btn_address_create_normal").onclick = function() {
1454
	if (ae.getAddressCountNormal() >= ae.getLimitNormalA(ae.getOwnLevel()) || ae.getAddressCountNormal() + ae.getAddressCountShield() >= 31) return;
1455
1456
	const txtNewAddr = document.getElementById("txt_address_create_normal");
1457
	if (!txtNewAddr.reportValidity()) return;
1458
1459
	addressCreate(txtNewAddr.value);
1460
};
1461
1462
document.getElementById("txt_address_create_normal").onkeyup = function(event) {
1463
	if (event.key !== "Enter") return;
1464
	event.preventDefault();
1465
	document.getElementById("btn_address_create_normal").click();
1466
};
1467
1468
document.getElementById("btn_address_create_shield").onclick = function() {
1469
	if (ae.getAddressCountShield() >= ae.getLimitShieldA(ae.getOwnLevel()) || ae.getAddressCountNormal() + ae.getAddressCountShield() >= 31) return;
1470
1471
	addressCreate("SHIELD");
1472
};
1473
1474
document.getElementById("btn_address_update").onclick = function() {
1475
	const btn = this;
1476
	btn.disabled = true;
1477
1478
	const rows = document.getElementById("tbl_addrs").rows;
1479
1480
	for (let i = 0; i < rows.length; i++) {
1481
		ae.setAddressAccInt(i, rows[i].getElementsByTagName("input")[0].checked);
1482
		ae.setAddressAccExt(i, rows[i].getElementsByTagName("input")[1].checked);
1483
		ae.setAddressAllVer(i, rows[i].getElementsByTagName("input")[2].checked);
1484
		ae.setAddressAttach(i, rows[i].getElementsByTagName("input")[3].checked);
1485
		ae.setAddressSecure(i, rows[i].getElementsByTagName("input")[4].checked);
1486
		ae.setAddressOrigin(i, rows[i].getElementsByTagName("input")[5].checked);
1487
	}
1488
1489
	ae.Address_Update(function(error) {
1490
		btn.disabled = false;
1491
		if (error) errorDialog(error);
1492
	});
1493
};
1494
1495
1496
document.getElementById("txt_reg").onkeyup = function(event) {
1497
	if (event.key !== "Enter") return;
1498
	event.preventDefault();
1499
	document.getElementById("btn_reg").click();
1500
};
1501
1502
document.getElementById("btn_reg").onclick = function() {
1503
	const btn = document.getElementById("btn_reg");
1504
	const txt = document.getElementById("txt_reg");
1505
	if (!txt.reportValidity()) return;
1506
	btn.disabled = true;
1507
1508
	ae.Account_Create(txt.value, function(error) {
1509
		if (error === 0) {
1510
			addAccountToTable(ae.admin_getUserNum() - 1);
1511
			txt.value = "";
1512
		} else errorDialog(error);
1513
1514
		btn.disabled = false;
1515
	});
1516
};
1517
1518
document.getElementById("chk_dng_usr").onclick = function() {
1519
	document.getElementById("btn_lowme").disabled = !this.checked || (ae.getOwnLevel() === 0);
1520
	document.getElementById("btn_erame").disabled = !this.checked;
1521
	document.getElementById("btn_delme").disabled = !this.checked;
1522
};
1523
1524
document.getElementById("btn_erame").onclick = function() {
1525
	ae.Message_Delete("ALL", function(error) {
1526
		if (error === 0) {
1527
			document.getElementById("chk_dng_usr").checked = false;
1528
			document.getElementById("chk_dng_usr").onclick();
1529
		} else errorDialog(error);
1530
	});
1531
};
1532
1533
document.getElementById("btn_notepad_restore").onclick = function() {
1534
	document.getElementById("txt_notepad").value = ae.getPrivateExtra();
1535
	document.getElementById("btn_notepad_savepad").disabled = true;
1536
	document.getElementById("txt_notepad").oninput = function() {
1537
		this.oninput = null;
1538
		document.getElementById("btn_notepad_savepad").disabled = false;
1539
		document.getElementById("btn_notepad_savepad").textContent = "Save";
1540
	};
1541
};
1542
1543
document.getElementById("txt_notepad").oninput = function() {
1544
	document.getElementById("btn_notepad_savepad").disabled = false;
1545
};
1546
1547
document.getElementById("btn_notepad_savepad").onclick = function() {
1548
	const btn = this;
1549
	btn.disabled = true;
1550
1551
	const error = ae.setPrivateExtra(document.getElementById("txt_notepad").value);
1552
	if (error !== 0) {
1553
		btn.disabled = false;
1554
		errorDialog(error);
1555
		return;
1556
	}
1557
1558
	ae.Private_Update(function(error2) {
1559
		if (error2 !== 0) {
1560
			btn.disabled = false;
1561
			errorDialog(error2);
1562
		} else {
1563
			document.querySelector("#div_notepad meter").value = ae.getPrivateExtraSpace() / ae.getPrivateExtraSpaceMax();
1564
			btn.textContent = "Saved";
1565
			document.getElementById("txt_notepad").oninput = function() {
1566
				this.oninput = null;
1567
				btn.textContent = "Save";
1568
				btn.disabled = false;
1569
			};
1570
		}
1571
	});
1572
};
1573
1574
document.getElementById("btn_notepad_saveupl").onclick = function() {
1575
	const np = document.getElementById("txt_notepad");
1576
	np.disabled = true;
1577
1578
	let fname = prompt("Save as...", "Untitled");
0 ignored issues
show
Debugging Code Best Practice introduced by
The prompt UI element is often considered obtrusive and is generally only used as a temporary measure. Consider replacing it with another UI element.
Loading history...
1579
	if (!fname.endsWith(".txt")) fname += ".txt";
1580
1581
	ae.Message_Upload(fname, np.value, function(error) {
1582
		if (error === 0) {
1583
			np.value = "";
1584
			showFiles();
1585
			document.getElementById("tbd_accs").children[0].children[1].textContent = Math.round(ae.getTotalMsgBytes() / 1024 / 1024);
1586
		} else errorDialog(error);
1587
1588
		np.disabled = false;
1589
	});
1590
};
1591
1592
document.getElementById("btn_upload").onclick = function() {
1593
	const btn = this;
1594
	const fileSelector = document.createElement("input");
1595
	fileSelector.type = "file";
1596
	fileSelector.click();
1597
1598
	fileSelector.onchange = function() {
1599
		btn.disabled = true;
1600
1601
		const reader = new FileReader();
1602
		reader.onload = function() {
1603
			if (vaultOk) { // Vault opened -> upload to PostVault
1604
				vault.uploadFile(fileSelector.files[0].name, new Uint8Array(reader.result), function(status) {
1605
					if (status === 0) {
1606
						showFiles();
1607
					} // TODO else show error
1608
1609
					btn.disabled = false;
1610
				});
1611
			} else { // No vault access -> upload to All-Ears
1612
				ae.Message_Upload(fileSelector.files[0].name, new Uint8Array(reader.result), function(status) {
1613
					if (status === 0) {
1614
						showFiles();
1615
						document.getElementById("tbd_accs").children[0].children[1].textContent = Math.round(ae.getTotalMsgBytes() / 1024 / 1024);
1616
					} else errorDialog(error);
0 ignored issues
show
Bug introduced by
The variable error seems to be never declared. If this is a global, consider adding a /** global: error */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
1617
1618
					btn.disabled = false;
1619
				});
1620
			}
1621
		};
1622
1623
		reader.readAsArrayBuffer(fileSelector.files[0]);
1624
	};
1625
};
1626
1627
document.getElementById("btn_pg").onclick = function() {
1628
//	localStorage.greeting = document.getElementById("txt_pg").value;
1629
};
1630
1631
document.querySelector("#write2_send > button").onclick = function() {
1632
	const btn = this;
1633
	btn.disabled = true;
1634
1635
	// Public announcement
1636
	if (document.getElementById("write2_recv").textContent === "public") {
1637
		ae.Message_Public(document.getElementById("write_subj").value, document.getElementById("write_body").value, function(error) {
1638
			if (error !== 0) {
1639
				document.getElementById("write2_btntxt").textContent = "Retry making";
1640
				btn.disabled = false;
1641
				errorDialog(error);
1642
				return;
1643
			}
1644
1645
			clearWrite();
1646
			displayMsg(false, true, 0);
1647
		});
1648
1649
		return;
1650
	}
1651
1652
	// Email or internal message
1653
	let apk = null;
1654
	if (document.getElementById("write2_recv").textContent.indexOf("@") === -1) {
1655
		const elApk = document.querySelector("#write2_pkey > input");
1656
		if (!elApk.reportValidity()) {
1657
			btn.disabled = false;
1658
			return;
1659
		}
1660
1661
		try {apk = sodium.from_base64(elApk.value, sodium.base64_variants.ORIGINAL_NO_PADDING);}
1662
		catch(e) {
1663
			errorDialog(1); // Invalid input
1664
			btn.disabled = false;
1665
			return;
1666
		}
1667
	}
1668
1669
	document.getElementById("write2_btntxt").textContent = "Sending to";
1670
1671
	ae.Message_Create(
1672
		document.getElementById("write_subj").value,
1673
		document.getElementById("write_body").value,
1674
		document.getElementById("write_from").value,
1675
		document.getElementById("write_recv").value,
1676
		document.getElementById("write_subj").getAttribute("data-replyid"),
1677
		apk,
1678
		function(error) {
1679
			if (error !== 0) {
1680
				errorDialog(error);
1681
				document.getElementById("write2_btntxt").textContent = "Retry sending to";
1682
				btn.disabled = false;
1683
				return;
1684
			}
1685
1686
			showDrbox();
1687
			clearWrite();
1688
			displayOutMsg(false, 0);
1689
		}
1690
	);
1691
};
1692
1693
document.getElementById("btn_sender").onclick = function() {
1694
	ae.Message_Sender(document.getElementById("txt_sender_hash").value, Date.parse(document.getElementById("txt_sender_date").value) / 1000, function(error, result) {
1695
		if (error !== 0) {
1696
			errorDialog(error);
1697
			return;
1698
		}
1699
1700
		document.getElementById("txt_sender_res").value = result;
1701
	});
1702
};
1703
1704
document.getElementById("btn_limits").onclick = function() {
1705
	const btn = this;
1706
	btn.disabled = true;
1707
1708
	const mib = [parseInt(document.getElementById("lim_mib0").value, 10), parseInt(document.getElementById("lim_mib1").value, 10), parseInt(document.getElementById("lim_mib2").value, 10), parseInt(document.getElementById("lim_mib3").value, 10)];
1709
	const nrm = [parseInt(document.getElementById("lim_nrm0").value, 10), parseInt(document.getElementById("lim_nrm1").value, 10), parseInt(document.getElementById("lim_nrm2").value, 10), parseInt(document.getElementById("lim_nrm3").value, 10)];
1710
	const shd = [parseInt(document.getElementById("lim_shd0").value, 10), parseInt(document.getElementById("lim_shd1").value, 10), parseInt(document.getElementById("lim_shd2").value, 10), parseInt(document.getElementById("lim_shd3").value, 10)];
1711
1712
	ae.Setting_Update(mib, nrm, shd, function(error) {
1713
		btn.disabled = false;
1714
1715
		if (error !== 0) {
1716
			errorDialog(error);
1717
		} else {
1718
			updateAddressCounts();
1719
		}
1720
	});
1721
};
1722
1723
document.getElementById("btn_getapk").onclick = function() {
1724
	document.getElementById("getapk_result").textContent = ae.getOwnApk(document.getElementById("getapk_addr").value);
1725
};
1726
1727
document.getElementById("txt_skey").onfocus = function() {
1728
//	document.getElementById("greeting").textContent = localStorage.greeting;
1729
};
1730
1731
document.getElementById("txt_skey").onkeyup = function(event) {
1732
	if (event.key !== "Enter") return;
1733
	event.preventDefault();
1734
	document.getElementById("btn_enter").click();
1735
};
1736
1737
document.getElementById("btn_enter").onclick = function() {
1738
	const txtSkey = document.getElementById("txt_skey");
1739
1740
	if (txtSkey.value === "") {
1741
		ae.reset();
1742
		document.getElementById("greeting").textContent = "Data cleared";
1743
		return;
1744
	}
1745
1746
	if (!txtSkey.reportValidity()) return;
1747
1748
	const btn = this;
1749
	btn.disabled = true;
1750
1751
	document.getElementById("txt_skey").disabled = true;
1752
1753
	ae.setKeys(txtSkey.value, function(successSetKeys) {
1754
		if (!successSetKeys) {
1755
			document.getElementById("txt_skey").disabled = false;
1756
			txtSkey.focus();
1757
1758
			document.getElementById("greeting").textContent = "SetKeys failed";
1759
			btn.disabled = false;
1760
			return;
1761
		}
1762
1763
		if (vaultOk === false) {
1764
			vault.setKeys(txtSkey.value, function(successPv) {
1765
				if (!successPv) vaultOk = null;
1766
			});
1767
		}
1768
1769
		document.body.style.cursor = "wait";
1770
		document.getElementById("greeting").textContent = "Connecting...";
1771
1772
		ae.Message_Browse(false, true, function(statusBrowse) {
1773
			document.body.style.cursor = "";
1774
1775
			if (statusBrowse !== 0 && statusBrowse !== 0x09) {
1776
				document.getElementById("greeting").textContent = ae.getErrorMessage(statusBrowse) + " (0x" + statusBrowse.toString(16).padStart(2, "0").toUpperCase() + ")";
1777
				document.getElementById("txt_skey").disabled = false;
1778
				btn.disabled = false;
1779
				btn.focus();
1780
				return;
1781
			}
1782
1783
			txtSkey.value = "";
1784
			document.getElementById("div_begin").hidden = true;
1785
			document.getElementById("div_main").hidden = false;
1786
			isReady = true;
1787
			reloadAccount();
1788
			history.replaceState({tab: 0, page: 0, msg: msgDisplay}, null);
1789
			setTab(true, 0, 0);
1790
1791
			if (statusBrowse !== 0) errorDialog(statusBrowse);
1792
			if (!ae.isUserAdmin()) return;
1793
1794
			ae.Account_Browse(function(statusAcc) {
1795
				if (statusAcc === 0) {
1796
					for (let i = 0; i < ae.admin_getUserNum(); i++) {addAccountToTable(i);}
1797
					updateLimits();
1798
				} else {
1799
					errorDialog(statusAcc);
1800
				}
1801
			});
1802
		});
1803
	});
1804
};
1805
1806
});
1807