Passed
Push — master ( b7bb32...6d501f )
by EMP
01:18
created

modern/main.js   F

Complexity

Total Complexity 264
Complexity/F 2.81

Size

Lines of Code 1548
Function Count 94

Duplication

Duplicated Lines 21
Ratio 1.36 %

Importance

Changes 0
Metric Value
eloc 1133
dl 21
loc 1548
rs 1.068
c 0
b 0
f 0
wmc 264
mnd 170
bc 170
fnc 94
bpm 1.8085
cpm 2.8085
noi 18

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complexity

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

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
const ae = new AllEars(function(ok) {
6
	if (ok) {
7
		if (localStorage.greeting) {
8
			document.getElementById("greeting").textContent = localStorage.greeting;
9
			document.getElementById("txt_pg").value = localStorage.greeting;
10
		} else localStorage.greeting = document.getElementById("greeting").textContent;
11
12
		document.getElementById("txt_skey").style.background = "#466";
13
		document.getElementById("txt_skey").maxLength = "64";
14
	} else {
15
		console.log("Failed to load All-Ears");
16
	}
17
});
18
19
function TabState(cur, max, btnDele, btnUpdt) {
20
	this.cur = cur;
21
	this.max = max;
22
	this.btnDele = btnDele;
23
	this.btnUpdt = btnUpdt;
24
}
25
26
const tabs = [
27
	new TabState(0, 0, false, true), // Inbox
28
	new TabState(0, 0, false, true), // Outbx
29
	new TabState(0, 1, true, false), // Write
30
	new TabState(0, 2, false, false), // Notes
31
	new TabState(0, 2, false, true) // Tools
32
];
33
34
let showHeaders = false;
35
36
let tab = 0;
37
const TAB_INBOX = 0;
38
const TAB_DRBOX = 1;
39
const TAB_WRITE = 2;
40
const TAB_NOTES = 3;
41
const TAB_TOOLS = 4;
42
43
// Helper functions
44
function getCountryName(countryCode) {
45
	switch (countryCode) {
46
		case "??": return "Unknown";
47
		case "DZ": return "Algeria";
48
		case "AO": return "Angola";
49
		case "BJ": return "Benin";
50
		case "BW": return "Botswana";
51
		case "BF": return "Burkina Faso";
52
		case "BI": return "Burundi";
53
		case "CV": return "Cabo Verde";
54
		case "CM": return "Cameroon";
55
		case "CF": return "Central African Republic";
56
		case "TD": return "Chad";
57
		case "KM": return "Comoros";
58
		case "CD": return "Congo";
59
		case "DJ": return "Djibouti";
60
		case "EG": return "Egypt";
61
		case "GQ": return "Equatorial Guinea";
62
		case "ER": return "Eritrea";
63
		case "SZ": return "Eswatini";
64
		case "ET": return "Ethiopia";
65
		case "GA": return "Gabon";
66
		case "GM": return "Gambia";
67
		case "GH": return "Ghana";
68
		case "GW": return "Guinea-Bissau";
69
		case "GN": return "Guinea";
70
		case "CI": return "Ivory Coast";
71
		case "KE": return "Kenya";
72
		case "LS": return "Lesotho";
73
		case "LR": return "Liberia";
74
		case "LY": return "Libya";
75
		case "MG": return "Madagascar";
76
		case "MW": return "Malawi";
77
		case "ML": return "Mali";
78
		case "MR": return "Mauritania";
79
		case "MU": return "Mauritius";
80
		case "YT": return "Mayotte";
81
		case "MA": return "Morocco";
82
		case "MZ": return "Mozambique";
83
		case "NA": return "Namibia";
84
		case "NE": return "Niger";
85
		case "NG": return "Nigeria";
86
		case "CG": return "Republic of the Congo";
87
		case "RW": return "Rwanda";
88
		case "RE": return "Réunion";
89
		case "SH": return "Saint Helena";
90
		case "SN": return "Senegal";
91
		case "SC": return "Seychelles";
92
		case "SL": return "Sierra Leone";
93
		case "SO": return "Somalia";
94
		case "ZA": return "South Africa";
95
		case "SS": return "South Sudan";
96
		case "SD": return "Sudan";
97
		case "ST": return "São Tomé and Príncipe";
98
		case "TZ": return "Tanzania";
99
		case "TG": return "Togo";
100
		case "TN": return "Tunisia";
101
		case "UG": return "Uganda";
102
		case "EH": return "Western Sahara";
103
		case "ZM": return "Zambia";
104
		case "ZW": return "Zimbabwe";
105
		case "AQ": return "Antarctica";
106
		case "BV": return "Bouvet Island";
107
		case "TF": return "French Southern Territories";
108
		case "HM": return "Heard Island and McDonald Islands";
109
		case "GS": return "South Georgia and the South Sandwich Islands";
110
		case "AF": return "Afghanistan";
111
		case "AM": return "Armenia";
112
		case "AZ": return "Azerbaijan";
113
		case "BH": return "Bahrain";
114
		case "BD": return "Bangladesh";
115
		case "BT": return "Bhutan";
116
		case "IO": return "British Indian Ocean Territory";
117
		case "BN": return "Brunei";
118
		case "KH": return "Cambodia";
119
		case "CN": return "China";
120
		case "CC": return "Cocos [Keeling] Islands";
121
		case "GE": return "Georgia";
122
		case "JO": return "Hashemite Kingdom of Jordan";
123
		case "HK": return "Hong Kong";
124
		case "IN": return "India";
125
		case "ID": return "Indonesia";
126
		case "IR": return "Iran";
127
		case "IQ": return "Iraq";
128
		case "IL": return "Israel";
129
		case "JP": return "Japan";
130
		case "KZ": return "Kazakhstan";
131
		case "KW": return "Kuwait";
132
		case "KG": return "Kyrgyzstan";
133
		case "LA": return "Laos";
134
		case "LB": return "Lebanon";
135
		case "MO": return "Macao";
136
		case "MY": return "Malaysia";
137
		case "MV": return "Maldives";
138
		case "MN": return "Mongolia";
139
		case "MM": return "Myanmar";
140
		case "NP": return "Nepal";
141
		case "KP": return "North Korea";
142
		case "OM": return "Oman";
143
		case "PK": return "Pakistan";
144
		case "PS": return "Palestine";
145
		case "PH": return "Philippines";
146
		case "QA": return "Qatar";
147
		case "SA": return "Saudi Arabia";
148
		case "SG": return "Singapore";
149
		case "KR": return "South Korea";
150
		case "LK": return "Sri Lanka";
151
		case "SY": return "Syria";
152
		case "TW": return "Taiwan";
153
		case "TJ": return "Tajikistan";
154
		case "TH": return "Thailand";
155
		case "TR": return "Turkey";
156
		case "TM": return "Turkmenistan";
157
		case "AE": return "United Arab Emirates";
158
		case "UZ": return "Uzbekistan";
159
		case "VN": return "Vietnam";
160
		case "YE": return "Yemen";
161
		case "AL": return "Albania";
162
		case "AD": return "Andorra";
163
		case "AT": return "Austria";
164
		case "BY": return "Belarus";
165
		case "BE": return "Belgium";
166
		case "BA": return "Bosnia and Herzegovina";
167
		case "BG": return "Bulgaria";
168
		case "HR": return "Croatia";
169
		case "CY": return "Cyprus";
170
		case "CZ": return "Czechia";
171
		case "DK": return "Denmark";
172
		case "EE": return "Estonia";
173
		case "FO": return "Faroe Islands";
174
		case "FI": return "Finland";
175
		case "FR": return "France";
176
		case "DE": return "Germany";
177
		case "GI": return "Gibraltar";
178
		case "GR": return "Greece";
179
		case "GG": return "Guernsey";
180
		case "HU": return "Hungary";
181
		case "IS": return "Iceland";
182
		case "IE": return "Ireland";
183
		case "IM": return "Isle of Man";
184
		case "IT": return "Italy";
185
		case "JE": return "Jersey";
186
		case "XK": return "Kosovo";
187
		case "LV": return "Latvia";
188
		case "LI": return "Liechtenstein";
189
		case "LU": return "Luxembourg";
190
		case "MT": return "Malta";
191
		case "MC": return "Monaco";
192
		case "ME": return "Montenegro";
193
		case "NL": return "Netherlands";
194
		case "MK": return "North Macedonia";
195
		case "NO": return "Norway";
196
		case "PL": return "Poland";
197
		case "PT": return "Portugal";
198
		case "LT": return "Republic of Lithuania";
199
		case "MD": return "Republic of Moldova";
200
		case "RO": return "Romania";
201
		case "RU": return "Russia";
202
		case "SM": return "San Marino";
203
		case "RS": return "Serbia";
204
		case "SK": return "Slovakia";
205
		case "SI": return "Slovenia";
206
		case "ES": return "Spain";
207
		case "SJ": return "Svalbard and Jan Mayen";
208
		case "SE": return "Sweden";
209
		case "CH": return "Switzerland";
210
		case "UA": return "Ukraine";
211
		case "GB": return "United Kingdom";
212
		case "VA": return "Vatican City";
213
		case "AX": return "Åland";
214
		case "AI": return "Anguilla";
215
		case "AG": return "Antigua and Barbuda";
216
		case "AW": return "Aruba";
217
		case "BS": return "Bahamas";
218
		case "BB": return "Barbados";
219
		case "BZ": return "Belize";
220
		case "BM": return "Bermuda";
221
		case "BQ": return "Bonaire, Sint Eustatius, and Saba";
222
		case "VG": return "British Virgin Islands";
223
		case "CA": return "Canada";
224
		case "KY": return "Cayman Islands";
225
		case "CR": return "Costa Rica";
226
		case "CU": return "Cuba";
227
		case "CW": return "Curaçao";
228
		case "DM": return "Dominica";
229
		case "DO": return "Dominican Republic";
230
		case "SV": return "El Salvador";
231
		case "GL": return "Greenland";
232
		case "GD": return "Grenada";
233
		case "GP": return "Guadeloupe";
234
		case "GT": return "Guatemala";
235
		case "HT": return "Haiti";
236
		case "HN": return "Honduras";
237
		case "JM": return "Jamaica";
238
		case "MQ": return "Martinique";
239
		case "MX": return "Mexico";
240
		case "MS": return "Montserrat";
241
		case "NI": return "Nicaragua";
242
		case "PA": return "Panama";
243
		case "PR": return "Puerto Rico";
244
		case "BL": return "Saint Barthélemy";
245
		case "LC": return "Saint Lucia";
246
		case "MF": return "Saint Martin";
247
		case "PM": return "Saint Pierre and Miquelon";
248
		case "VC": return "Saint Vincent and the Grenadines";
249
		case "SX": return "Sint Maarten";
250
		case "KN": return "St Kitts and Nevis";
251
		case "TT": return "Trinidad and Tobago";
252
		case "TC": return "Turks and Caicos Islands";
253
		case "VI": return "U.S. Virgin Islands";
254
		case "US": return "United States";
255
		case "AS": return "American Samoa";
256
		case "AU": return "Australia";
257
		case "CX": return "Christmas Island";
258
		case "CK": return "Cook Islands";
259
		case "TL": return "Democratic Republic of Timor-Leste";
260
		case "FM": return "Federated States of Micronesia";
261
		case "FJ": return "Fiji";
262
		case "PF": return "French Polynesia";
263
		case "GU": return "Guam";
264
		case "KI": return "Kiribati";
265
		case "MH": return "Marshall Islands";
266
		case "NR": return "Nauru";
267
		case "NC": return "New Caledonia";
268
		case "NZ": return "New Zealand";
269
		case "NU": return "Niue";
270
		case "NF": return "Norfolk Island";
271
		case "MP": return "Northern Mariana Islands";
272
		case "PW": return "Palau";
273
		case "PG": return "Papua New Guinea";
274
		case "PN": return "Pitcairn Islands";
275
		case "WS": return "Samoa";
276
		case "SB": return "Solomon Islands";
277
		case "TK": return "Tokelau";
278
		case "TO": return "Tonga";
279
		case "TV": return "Tuvalu";
280
		case "UM": return "U.S. Minor Outlying Islands";
281
		case "VU": return "Vanuatu";
282
		case "WF": return "Wallis and Futuna";
283
		case "AR": return "Argentina";
284
		case "BO": return "Bolivia";
285
		case "BR": return "Brazil";
286
		case "CL": return "Chile";
287
		case "CO": return "Colombia";
288
		case "EC": return "Ecuador";
289
		case "FK": return "Falkland Islands";
290
		case "GF": return "French Guiana";
291
		case "GY": return "Guyana";
292
		case "PY": return "Paraguay";
293
		case "PE": return "Peru";
294
		case "SR": return "Suriname";
295
		case "UY": return "Uruguay";
296
		case "VE": return "Venezuela";
297
	}
298
299
	return "Error";
300
}
301
302
function getCountryFlag(countryCode) {
303
	return (!countryCode || countryCode.length !== 2 || countryCode == "??") ? "❔" : sodium.to_string(new Uint8Array([
304
		240, 159, 135, 166 + countryCode.codePointAt(0) - 65,
305
		240, 159, 135, 166 + countryCode.codePointAt(1) - 65
306
	]));
307
}
308
309
function getClockIcon(d) {
310
	const h24 = d.getUTCHours();
311
	let h12 = (h24 === 0 ? 12 : ((h24 > 12) ? h24 - 12 : h24));
312
313
	const m60 = (d.getUTCMinutes() * 60) + d.getUTCSeconds();
314
	let m30 = 0;
315
	if (m60 <= 900) { // <= 15: round down to this hour
316
		m30 = 0;
317
	} else if (m60 > 900 && m60 < 2700) { // 15..45: round to half-past this hour
318
		m30 = 12;
319
	} else { // >= 45: round up to next hour
320
		h12++;
321
		m30 = 0;
322
	}
323
324
	return "&#" + ((128335 + h12) + m30) + ";";
325
}
326
327
function clearDisplay() {
328
	let      el = document.querySelector("article > img");
329
	if (!el) el = document.querySelector("article > audio");
330
	if (!el) el = document.querySelector("article > video");
331
	if (!el) el = document.querySelector("article > embed");
332
	if (!el) el = document.querySelector("article > iframe");
333
	if (!el) return;
334
335
	URL.revokeObjectURL(el.src);
336
	el.remove();
337
}
338
339
function downloadFile(num) {
340
	const a = document.createElement("a");
341
	a.href = URL.createObjectURL(new Blob([ae.GetUplMsgBody(num).buffer]));
342
	a.download = ae.GetUplMsgTitle(num);
343
	a.click();
344
345
	URL.revokeObjectURL(a.href);
346
	a.href = "";
347
	a.download = "";
348
}
349
350
function displayFile(num) {
351
	const fileType = ae.GetUplMsgType(num);
352
	if (!fileType) {downloadFile(num); return;}
353
354
	clearDisplay();
355
	document.querySelector("article").scroll(0, 0);
356
	document.querySelector("article").setAttribute("data-msgid", ae.GetUplMsgIdHex(num));
357
358
	document.getElementById("btn_mdele").disabled = false;
359
	document.getElementById("btn_msave").disabled = false;
360
	document.getElementById("btn_reply").disabled = true;
361
362
	document.getElementById("btn_msave").onclick = function() {downloadFile(num);};
363
364
	document.querySelector("article").children[0].hidden = true;
365
	document.querySelector("article").children[1].textContent = ae.GetUplMsgTitle(num);
366
367
	switch (fileType) {
368
		case "text": {
369
			document.querySelector("article").children[2].hidden = false;
370
			document.querySelector("article").children[2].textContent = sodium.to_string(ae.GetUplMsgBody(num));
371
		break;}
372
373
		case "image": {
374
			document.querySelector("article").children[2].hidden = true;
375
			const img = document.createElement("img");
376
			img.src = URL.createObjectURL(new Blob([ae.GetUplMsgBody(num).buffer]));
377
			document.querySelector("article").appendChild(img);
378
379
			img.onclick = function() {
380
				if (!document.fullscreen)
381
					img.requestFullscreen();
382
				else
383
					document.exitFullscreen();
384
			};
385
		break;}
386
387
		case "audio":
388
		case "video": {
389
			document.querySelector("article").children[2].hidden = true;
390
			const el = document.createElement(fileType);
391
			el.controls = "controls";
392
			el.src = URL.createObjectURL(new Blob([ae.GetUplMsgBody(num).buffer]));
393
			document.querySelector("article").appendChild(el);
394
		break;}
395
396
		case "pdf": {
397
			document.querySelector("article").children[2].hidden = true;
398
			const el = document.createElement("embed");
399
			el.type = "application/pdf";
400
			el.src = URL.createObjectURL(new Blob([ae.GetUplMsgBody(num).buffer], {type: "application/pdf"}));
401
			document.querySelector("article").appendChild(el);
402
		break;}
403
404
		case "html": {
405
			document.querySelector("article").children[2].hidden = true;
406
			const el = document.createElement("iframe");
407
			el.allow = "";
408
			el.sandbox = "";
409
			el.referrerPolicy = "no-referrer";
410
			el.csp = "base-uri 'none'; child-src 'none'; connect-src 'none'; default-src 'none'; font-src 'none'; form-action 'none'; frame-ancestors 'none'; frame-src 'none'; img-src 'none'; manifest-src 'none'; media-src 'none'; object-src 'none'; script-src 'none'; style-src 'none'; worker-src 'none';";
411
			el.srcdoc = sodium.to_string(ae.GetUplMsgBody(num).buffer);
412
			document.querySelector("article").appendChild(el);
413
		break;}
414
	}
415
}
416
417
function displayMsg(isInt, num) {
418
	clearDisplay();
419
420
	document.getElementById("btn_mdele").disabled = false;
421
	document.getElementById("btn_msave").disabled = isInt;
422
423
	document.querySelector("article").scroll(0, 0);
424
	document.querySelector("article").setAttribute("data-msgid", isInt? ae.GetIntMsgIdHex(num) : ae.GetExtMsgIdHex(num));
425
426
	const ts = isInt? ae.GetIntMsgTime(num) : ae.GetExtMsgTime(num);
427
428
	if (!isInt) {
429
		document.getElementById("btn_msave").onclick = function() {
430
			this.blur();
431
432
			const a = document.createElement("a");
433
			a.href = URL.createObjectURL(new Blob([ae.ExportExtMsg(num)]));
434
			a.download = ae.GetExtMsgTitle(num);
435
			a.click();
436
437
			URL.revokeObjectURL(a.href);
438
			a.href = "";
439
			a.download = "";
440
		};
441
	}
442
443
	if (!isInt || (ae.GetIntMsgFrom(num) !== "public" && ae.GetIntMsgFrom(num) !== "system")) {
444
		document.getElementById("btn_reply").disabled = false;
445
446
		document.getElementById("btn_reply").onclick = function() {
447
			document.getElementById("write_recv").value = isInt? ae.GetIntMsgFrom(num) : ae.GetExtMsgReplyAddress(num);
448
			document.getElementById("write_subj").value = isInt? ae.GetIntMsgTitle(num) : ae.GetExtMsgTitle(num);
449
			if (!document.getElementById("write_subj").value.startsWith("Re:")) document.getElementById("write_subj").value = "Re: " + document.getElementById("write_subj").value;
450
			document.querySelector("#write2_pkey > input").value = isInt? ae.GetIntMsgFromPk(num) : "";
451
452
			document.getElementById("write_recv").readOnly = !isInt;
453
			document.getElementById("write_subj").readOnly = !isInt;
454
			document.getElementById("write_subj").setAttribute("data-replyid", isInt? "" : ae.GetExtMsgHdrId(num));
455
456
			tabs[TAB_WRITE].cur = 0;
457
			document.getElementById("btn_write").disabled = false;
458
			document.getElementById("btn_write").click();
459
			document.getElementById("write_body").focus();
460
461
			for (const opt of document.getElementById("write_from").options) {
462
				if (opt.value === (isInt ? ae.GetIntMsgTo(num) : ae.GetExtMsgEnvTo(num).split("@")[0].toLowerCase())) {
463
					opt.selected = true;
464
				}
465
			}
466
		};
467
	} else {
468
		document.getElementById("btn_reply").disabled = true;
469
	}
470
471
	document.querySelector("article").children[0].hidden = false;
472
	document.querySelector("article").children[2].hidden = false;
473
474
	document.getElementById("readmsg_envto").textContent = isInt ? "" : ae.GetExtMsgEnvTo(num);
475
	document.getElementById("readmsg_hdrto").textContent = isInt ? ae.GetIntMsgTo(num) : (ae.GetExtMsgHdrTo(num) + (ae.GetExtMsgDnTo(num) ? " (" + ae.GetExtMsgDnTo(num) + ")" : ""));
476
477
	const tzOs = new Date().getTimezoneOffset();
478
	const msgDate = new Date((ts * 1000) + (tzOs * -60000));
479
	document.getElementById("readmsg_date").children[0].innerHTML = getClockIcon(msgDate);
480
	document.getElementById("readmsg_date").children[1].dateTime = new Date(ts * 1000).toISOString();
481
482
	if (isInt) {
483
		document.querySelector("article").children[1].textContent = ae.GetIntMsgTitle(num);
484
		document.querySelector("article").children[2].textContent = ae.GetIntMsgBody(num);
485
486
		document.getElementById("readmsg_date").children[1].textContent = msgDate.toISOString().slice(0, 19).replace("T", " ");
487
488
		document.getElementById("readmsg_ip").style.visibility = "hidden";
489
		document.getElementById("readmsg_rdns").style.visibility = "hidden";
490
		document.getElementById("readmsg_dkim").style.visibility = "hidden";
491
		document.getElementById("readmsg_greet").style.visibility = "hidden";
492
		document.getElementById("readmsg_cert").style.visibility = "hidden";
493
		document.getElementById("readmsg_envfrom").style.visibility = "hidden";
494
		document.getElementById("readmsg_envto").style.visibility = "hidden";
495
496
		if (ae.GetIntMsgFrom(num) !== "system" && ae.GetIntMsgFrom(num) !== "public") {
497
			document.getElementById("readmsg_tls").style.visibility = "visible";
498
			document.getElementById("readmsg_tls").children[0].textContent = ae.GetIntMsgFromPk(num);
499
		} else document.getElementById("readmsg_tls").style.visibility = "hidden";
500
501
		let symbol = "<span title=\"Invalid level\">&#x26a0;</span>";
502
		if      (ae.GetIntMsgFrom(num) === "system") {if (ae.GetIntMsgLevel(num) === 3) symbol = "<span title=\"System message\">&#x1f162;</span>";} // (S)
503
		else if (ae.GetIntMsgFrom(num) === "public") {if (ae.GetIntMsgLevel(num) === 3) symbol = "<span title=\"Public announcement\">&#x1f15f;</span>";} // (P)
504
		else if (ae.GetIntMsgLevel(num) === 0) symbol = "<span title=\"Level 0 User\">&#x1f10c;</span>"; // 0
505
		else if (ae.GetIntMsgLevel(num) === 1) symbol = "<span title=\"Level 1 User\">&#x278a;</span>"; // 1
506
		else if (ae.GetIntMsgLevel(num) === 2) symbol = "<span title=\"Level 2 User\">&#x278b;</span>"; // 2
507
		else if (ae.GetIntMsgLevel(num) === 3) symbol = "<span title=\"Administrator\">&#x1f150;</span>"; // A (Admin)
508
		document.getElementById("readmsg_hdrfrom").innerHTML = symbol + " " + ae.GetIntMsgFrom(num);
509
510
		let flagText = "";
511
		if (!ae.GetIntMsgFlagVPad(num)) flagText += "<abbr title=\"Invalid padding\">PAD</abbr> ";
512
		if (!ae.GetIntMsgFlagVSig(num)) flagText += "<abbr title=\"Invalid signature\">SIG</abbr> ";
513
		if (ae.GetIntMsgFlagE2ee(num)) flagText += "<abbr title=\"End-to-end encrypted\">E2EE</abbr> ";
514
		document.getElementById("readmsg_flags").children[0].innerHTML = flagText.trim();
515
	} else {
516
		document.querySelector("article").children[2].innerHTML = "";
517
518
		const headers = document.createElement("p");
519
		headers.textContent = ae.GetExtMsgHeaders(num);
520
		headers.className = "mono";
521
		headers.hidden = !showHeaders;
522
		document.querySelector("article").children[2].appendChild(headers);
523
524
		const body = document.createElement("p");
525
		body.innerHTML = ae.GetExtMsgBody(num);
526
		document.querySelector("article").children[2].appendChild(body);
527
528
		document.querySelector("article").children[1].textContent = ae.GetExtMsgTitle(num);
529
		document.querySelector("article").children[1].style.cursor = headers.textContent? "pointer" : "";
530
		document.querySelector("article").children[1].onclick = function() {
531
			if (!headers.textContent) return;
532
			showHeaders = !showHeaders;
533
			headers.hidden = !showHeaders;
534
		};
535
536
		let hdrSecs = Math.abs(ae.GetExtMsgHdrTime(num));
537
		let hdrTime = "";
538
		if (hdrSecs >= 3600) {
539
			const hdrHours = Math.floor(hdrSecs / 3600);
540
			hdrTime += hdrHours.toString() + "h ";
541
			hdrSecs -= hdrHours * 3600;
542
		}
543
		if (hdrSecs >= 60) {
544
			const hdrMins = Math.floor(hdrSecs / 60);
545
			hdrTime += hdrMins.toString() + "m ";
546
			hdrSecs -= hdrMins * 60;
547
		}
548
		hdrTime += hdrSecs + "s";
549
550
		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");
551
		document.getElementById("readmsg_date").children[1].textContent = msgDate.toISOString().slice(0, 19).replace("T", " ") + "; " + hdrTz + " " + ((ae.GetExtMsgHdrTime(num) >= 0) ? "+" : "-") + hdrTime;
552
553
		document.getElementById("readmsg_ip").style.visibility = "visible";
554
		document.getElementById("readmsg_rdns").style.visibility = "visible";
555
		document.getElementById("readmsg_dkim").style.visibility = "visible";
556
		document.getElementById("readmsg_greet").style.visibility = "visible";
557
		document.getElementById("readmsg_tls").style.visibility = "visible";
558
		document.getElementById("readmsg_cert").style.visibility = "visible";
559
		document.getElementById("readmsg_envfrom").style.visibility = "visible";
560
		document.getElementById("readmsg_envto").style.visibility = "visible";
561
562
		const cc = ae.GetExtMsgCountry(num);
563
564
		// DKIM
565
		let dkim = "";
566
		if (ae.GetExtMsgDkim(num)) {
567
			[ // Look for a matching domain in this order
568
				ae.GetExtMsgHdrFrom(num).split("@")[1],
569
				ae.GetExtMsgEnvFrom(num).split("@")[1],
570
				ae.GetExtMsgRdns(num),
571
				ae.GetExtMsgGreet(num),
572
				ae.GetExtMsgTlsDomain(num)
573
			].forEach(function(dom) {
574
				if (dkim) return;
575
				for (let i = 0; i < ae.GetExtMsgDkim(num).domain.length; i++) {
576
					if (ae.GetExtMsgDkim(num).domain[i] === dom) {
577
						dkim = dom + " ✓";
578
						return;
579
					}
580
				}
581
			});
582
583
			if (!dkim) dkim = ae.GetExtMsgDkim(num).domain[0]; // Default to first signature domain
584
		}
585
586
		if (ae.GetExtMsgFlagDkFl(num)) dkim += " (fail)";
587
588
		// Left side
589
		document.getElementById("readmsg_country").textContent = getCountryFlag(cc);
590
		document.getElementById("readmsg_country").title = getCountryName(cc);
591
		document.getElementById("readmsg_ip").children[1].textContent = ae.GetExtMsgIp(num) + (ae.GetExtMsgFlagIpBl(num) ? " ❗" : "");
592
		document.getElementById("readmsg_tls").children[0].textContent = ae.GetExtMsgTLS(num);
593
594
		// Right side
595
		document.getElementById("readmsg_greet").children[0].textContent = ae.GetExtMsgGreet(num) + (ae.GetExtMsgFlagGrDm(num) ? " ✓" : "");
596
		document.getElementById("readmsg_rdns").children[0].textContent = ae.GetExtMsgRdns(num) + (ae.GetExtMsgGreet(num) === ae.GetExtMsgRdns(num) ? " ✓" : "");
597
		document.getElementById("readmsg_cert").children[0].textContent = ae.GetExtMsgTlsDomain(num) ? (ae.GetExtMsgTlsDomain(num) + " ✓") : "";
598
		document.getElementById("readmsg_dkim").children[0].textContent = dkim;
599
		document.getElementById("readmsg_envfrom").textContent = ae.GetExtMsgEnvFrom(num);
600
		document.getElementById("readmsg_hdrfrom").textContent = ae.GetExtMsgHdrFrom(num) + (ae.GetExtMsgDnFrom(num) ? " (" + ae.GetExtMsgDnFrom(num) + ")" : "");
601
602
		let flagText = "";
603
		if (!ae.GetExtMsgFlagVPad(num)) flagText += "<abbr title=\"Invalid padding\">PAD</abbr> ";
604
		if (!ae.GetExtMsgFlagVSig(num)) flagText += "<abbr title=\"Invalid signature\">SIG</abbr> ";
605
		if (!ae.GetExtMsgFlagPExt(num)) flagText += "<abbr title=\"The sender did not use the Extended (ESMTP) protocol\">SMTP</abbr> ";
606
		if (!ae.GetExtMsgFlagQuit(num)) flagText += "<abbr title=\"The sender did not issue the required QUIT command\">QUIT</abbr> ";
607
		if (ae.GetExtMsgFlagRare(num)) flagText += "<abbr title=\"The sender issued unusual command(s)\">RARE</abbr> ";
608
		if (ae.GetExtMsgFlagFail(num)) flagText += "<abbr title=\"The sender issued invalid command(s)\">FAIL</abbr> ";
609
		if (ae.GetExtMsgFlagPErr(num)) flagText += "<abbr title=\"The sender violated the protocol\">PROT</abbr> ";
610
		document.getElementById("readmsg_flags").children[0].innerHTML = flagText.trim();
611
	}
612
}
613
614
function getErrorMessage(err) {
615
	switch (err) {
616
		// 0x01-0x20	Client-side error codes
617
		case 0x01: return "Invalid input";
618
		case 0x02: return "Only administrators can perform this function";
619
		case 0x03: return "_FetchEncrypted: Invalid input";
620
		case 0x04: return "_FetchEncrypted: Failed connecting to server";
621
		case 0x05: return "_FetchEncrypted: Failed decrypting response";
622
		case 0x06: return "_FetchEncrypted: Invalid response length";
623
		case 0x07: return "Invalid response from server";
624
		case 0x08: return "Addr32 encoding failed"];
625
626
		case 0x10: return "Message too short";
627
		case 0x11: return "Name too long";
628
		case 0x12: return "File too large";
629
630
		// 0x21-0x25	Generic
631
		case 0x21: return ["FORMAT",   "Invalid format"];
632
		case 0x22: return ["MISC",     "Unknown error"];
633
		case 0x23: return ["INTERNAL", "Internal server error"];
634
		case 0x24: return ["TODO",     "Functionality missing - in development"];
635
		case 0x25: return ["FIXME",    "Unexpected error encountered"];
636
637
		// 0xE0-0xEF	Message/Create
638
		case 0xE0: return ["MESSAGE_CREATE_EXT_MINLEVEL",        "Account level too low"];
639
		case 0xE1: return ["MESSAGE_CREATE_EXT_FORMAT_FROM",     "Malformed from-address"];
640
		case 0xE2: return ["MESSAGE_CREATE_EXT_FORMAT_TO",       "Malformed to-address"];
641
		case 0xE3: return ["MESSAGE_CREATE_EXT_FORMAT_REPLYID",  "Malformed reply-id"];
642
		case 0xE4: return ["MESSAGE_CREATE_EXT_FORMAT_SUBJECT",  "Malformed subject"];
643
		case 0xE5: return ["MESSAGE_CREATE_EXT_INVALID_REPLYID", "Invalid reply-id"];
644
		case 0xE6: return ["MESSAGE_CREATE_EXT_INVALID_FROM",    "Invalid from-address"];
645
		case 0xE7: return ["MESSAGE_CREATE_EXT_INVALID_TO",      "Invalid to-address"];
646
		case 0xE8: return ["MESSAGE_CREATE_EXT_BODY_SIZE",       "Body too long or short"];
647
		case 0xE9: return ["MESSAGE_CREATE_EXT_BODY_UTF8",       "Body not UTF-8"];
648
		case 0xEA: return ["MESSAGE_CREATE_EXT_BODY_CONTROL",    "Body contains control characters"];
649
		case 0xEB: return ["MESSAGE_CREATE_EXT_LINE_TOOLONG",    "Body exceeds line-length limit"];
650
		case 0xEC: return ["MESSAGE_CREATE_EXT_BODY_FORMAT",     "Malformed body"];
651
		case 0xED: return ["MESSAGE_CREATE_EXT_BODY_TOOSHORT",   "Body too short"];
652
		case 0xEE: return ["MESSAGE_CREATE_EXT_TODOMAIN",        "Invalid to-address domain"];
653
//		case 0xEF: return ["", ""];
654
655
		// 0xF0-0xF9	Message/Create sendMail()
656
		case 0xF0: return ["MESSAGE_CREATE_SENDMAIL_GREET", "Failed greeting receiver server"];
657
		case 0xF1: return ["MESSAGE_CREATE_SENDMAIL_EHLO",  "EHLO command failed"];
658
		case 0xF2: return ["MESSAGE_CREATE_SENDMAIL_STLS",  "STARTTLS command failed"];
659
		case 0xF3: return ["MESSAGE_CREATE_SENDMAIL_SHAKE", "TLS handshake failed"];
660
		case 0xF4: return ["MESSAGE_CREATE_SENDMAIL_NOTLS", "TLS not available"];
661
		case 0xF5: return ["MESSAGE_CREATE_SENDMAIL_MAIL",  "MAIL command failed"];
662
		case 0xF6: return ["MESSAGE_CREATE_SENDMAIL_RCPT",  "RCPT command failed"];
663
		case 0xF7: return ["MESSAGE_CREATE_SENDMAIL_DATA",  "DATA command failed"];
664
		case 0xF8: return ["MESSAGE_CREATE_SENDMAIL_BODY",  "Sending body failed"];
665
//		case 0xF9: return ["", ""];
666
667
		// 0xFA-0xFF	Message/Create Int
668
		case 0xFA: return ["MESSAGE_CREATE_INT_TOOSHORT",     "Message too short"];
669
		case 0xFB: return ["MESSAGE_CREATE_INT_TS_INVALID",   "Invalid timestamp"];
670
		case 0xFC: return ["MESSAGE_CREATE_INT_SUBJECT_SIZE", "Subject too long or short"];
671
		case 0xFD: return ["MESSAGE_CREATE_INT_ADDR_NOTOWN",  "Sender address not owned"];
672
		case 0xFE: return ["MESSAGE_CREATE_INT_TO_NOTACCEPT", "Receiver address does not accept messages"];
673
		case 0xFF: return ["MESSAGE_CREATE_INT_TO_SELF",      "Sending to own account not allowed"];
674
675
		default: return ["???", "Unknown error"];
676
	}
677
}
678
679
// Interface
680
function errorDialog(err) {
681
	if (typeof(err) !== "number" || err < 1) return;
682
683
	let btnDisable = [];
684
	const btn = document.querySelectorAll("nav > button");
685
	for (let i = 0; i < btn.length; i++) {
686
		btnDisable.push(btn[i].disabled);
687
		btn[i].disabled = true;
688
	}
689
690
	const errMsg = getErrorMessage(err);
691
692
	const dlg = document.querySelector("dialog");
693
	dlg.children[0].style.height = getComputedStyle(document.querySelector("#main1 > div[class='mid']")).height;
694
	dlg.querySelector("h1").textContent = "ERROR 0x" + err.toString(16).padStart(2, "0").toUpperCase();
695
	dlg.querySelector("p").textContent = (typeof(errMsg) === "string") ? errMsg : errMsg[1];
696
	dlg.show();
697
698
	document.querySelector("dialog > div").onclick = function() {
699
		for (let i = 0; i < btn.length; i++) {
700
			btn[i].disabled = btnDisable[i];
701
			dlg.close();
702
		}
703
	}
704
}
705
706
function addMsg(isInt, i) {
707
	const row = document.getElementById("tbl_inbox").insertRow(-1);
708
	row.setAttribute("data-msgid", isInt? ae.GetIntMsgIdHex(i) : ae.GetExtMsgIdHex(i));
709
710
	const ts = isInt? ae.GetIntMsgTime(i) : ae.GetExtMsgTime(i);
711
	const el = document.createElement("time");
712
	el.dateTime = new Date(ts * 1000).toISOString();
713
	el.textContent = new Date((ts * 1000) + (new Date().getTimezoneOffset() * -60000)).toISOString().slice(0, 10);
714
715
	const cellTime = row.insertCell(-1);
716
	cellTime.appendChild(el);
717
718
	const cellSubj = row.insertCell(-1);
719
	cellSubj.textContent = isInt? ae.GetIntMsgTitle(i) : ae.GetExtMsgTitle(i);
720
721
	if (isInt) {
722
		const cellSnd = row.insertCell(-1);
723
		cellSnd.textContent = ae.GetIntMsgFrom(i);
724
		cellSnd.className = (ae.GetIntMsgFrom(i).length === 16) ? "mono" : "";
725
	} else {
726
		const from1 = ae.GetExtMsgHdrFrom(i);
727
		const from2 = from1.substring(from1.indexOf("@") + 1);
728
		const cc = ae.GetExtMsgCountry(i);
729
		const cellSnd1 = row.insertCell(-1);
730
		cellSnd1.textContent = from1.substring(0, from1.indexOf("@"));
731
732
		const flag = document.createElement("abbr");
733
		flag.textContent = getCountryFlag(cc);
734
		flag.title = getCountryName(cc);
735
736
		const fromText = document.createElement("span");
737
		fromText.textContent = " " + from2;
738
739
		const cellSnd2 = row.insertCell(-1);
740
		cellSnd2.appendChild(flag);
741
		cellSnd2.appendChild(fromText);
742
	}
743
744
	row.onclick = function() {
745
		displayMsg(isInt, i);
746
	};
747
}
748
749
function getRowsPerPage() {
750
	const tbl = document.getElementById("tbl_inbox");
751
	tbl.innerHTML = "";
752
	const row = tbl.insertRow(-1);
753
	const cell = row.insertCell(-1);
754
	cell.textContent = "0";
755
756
	const rowsPerPage = Math.floor(getComputedStyle(document.getElementById("div_inbox")).height.replace("px", "") / getComputedStyle(document.querySelector("#tbl_inbox > tbody > tr:first-child")).height.replace("px", "")) - 1; // -1 allows space for 'load more'
757
	tbl.innerHTML = "";
758
	return rowsPerPage;
759
}
760
761
function addMessages() {
762
	const maxExt = ae.GetExtMsgCount();
763
	const maxInt = ae.GetIntMsgCount();
764
765
	if (maxExt + maxInt < 1) {
766
		tabs[TAB_INBOX].max = 0;
767
		return;
768
	}
769
770
	const rowsPerPage = getRowsPerPage();
771
	let skipMsgs = rowsPerPage * tabs[TAB_INBOX].cur;
772
773
	tabs[TAB_INBOX].max = Math.floor((maxExt + maxInt - 1) / rowsPerPage);
774
775
	let numExt = 0;
776
	let numInt = 0;
777
	let numAdd = 0;
778
779
	while (numAdd < rowsPerPage) {
780
		const tsInt = (numInt < maxInt) ? ae.GetIntMsgTime(numInt) : -1;
781
		const tsExt = (numExt < maxExt) ? ae.GetExtMsgTime(numExt) : -1;
782
		if (tsInt === -1 && tsExt === -1) break;
783
784
		if (tsInt !== -1 && (tsExt === -1 || tsInt > tsExt)) {
785
			if (skipMsgs > 0) skipMsgs--; else {addMsg(true, numInt); numAdd++;}
786
			numInt++;
787
		} else if (tsExt !== -1) {
788
			if (skipMsgs > 0) skipMsgs--; else {addMsg(false, numExt); numAdd++;}
789
			numExt++;
790
		}
791
	}
792
793
	if (ae.GetReadyMsgBytes() < ae.GetTotalMsgBytes()) {
794
		const inbox = document.getElementById("tbl_inbox");
795
		const row = inbox.insertRow(-1);
796
		const cell = row.insertCell(-1);
797
		cell.textContent = "Load more (" + Math.round((ae.GetTotalMsgBytes() - ae.GetReadyMsgBytes()) / 1024) + " KiB left)";
798
799
		row.onclick = function() {
800
			this.onclick = "";
801
802
			ae.Message_Browse(false, false, function(errorBrowse) {
803
				document.getElementById("tbl_inbox").style.opacity = 1;
804
805
				if (!errorBrowse) {
806
					addMessages();
807
					addUploads();
808
					addSent();
809
					if (tabs[tab].cur < tabs[tab].max) document.getElementById("btn_rght").disabled = false;
810
				} // else TODO
811
			});
812
		};
813
	}
814
}
815
816
function addUploads() {
817
	const tbl = document.getElementById("tbd_uploads");
818
	tbl.innerHTML = "";
819
820
	for (let i = 0; i < ae.GetUplMsgCount(); i++) {
821
		const row = tbl.insertRow(-1);
822
		row.setAttribute("data-msgid", ae.GetUplMsgIdHex(i));
823
824
		let cell;
825
		cell = row.insertCell(-1); cell.textContent = new Date(ae.GetUplMsgTime(i) * 1000).toISOString().slice(0, 10);
826
827
		cell = row.insertCell(-1); cell.textContent = ae.GetUplMsgTitle(i);
828
		cell.onclick = function() {displayFile(this.parentElement.rowIndex - 1);};
829
830
		cell = row.insertCell(-1); cell.textContent = (ae.GetUplMsgBytes(i) / 1024).toFixed(1);
831
832
		cell = row.insertCell(-1);
833
		if (ae.GetUplMsgIdHex(i)) {
834
			cell.innerHTML = "<button data-msgid=\"" + ae.GetUplMsgIdHex(i) + "\" type=\"button\">X</button>";
835
836
			cell.children[0].onclick = function() {
837
				const tr = this.parentElement.parentElement;
838
				ae.Message_Delete(this.getAttribute("data-msgid"), function(error) {
839
					if (!error) tr.remove();
840
					else console.log("Error " + error);
841
				});
842
			};
843
		}
844
	}
845
}
846
847
function displayOutMsg(num) {
848
	clearDisplay();
849
	document.querySelector("article").scroll(0, 0);
850
	document.querySelector("article").setAttribute("data-msgid", ae.GetOutMsgIdHex(num));
851
852
	document.getElementById("btn_mdele").disabled = false;
853
	document.getElementById("btn_msave").disabled = true;
854
	document.getElementById("btn_reply").disabled = true;
855
856
	document.querySelector("article").children[0].hidden = false;
857
	document.querySelector("article").children[2].hidden = false;
858
859
	document.querySelector("article").children[1].textContent = ae.GetOutMsgSubj(num);
860
	document.querySelector("article").children[2].textContent = ae.GetOutMsgBody(num);
861
862
	document.getElementById("readmsg_dkim").style.visibility    = "hidden";
863
	document.getElementById("readmsg_hdrto").style.visibility   = "visible";
864
	document.getElementById("readmsg_hdrfrom").style.visibility = "visible";
865
	document.getElementById("readmsg_envto").style.visibility   = "visible";
866
	document.getElementById("readmsg_envfrom").style.visibility = "hidden";
867
868
	document.getElementById("readmsg_hdrfrom").textContent = ae.GetOutMsgFrom(num);
869
870
	document.getElementById("readmsg_envto").textContent = ae.GetOutMsgMxDom(num);
871
	document.getElementById("readmsg_hdrto").textContent = ae.GetOutMsgTo(num);
872
873
	const ts = ae.GetOutMsgTime(num);
874
	const tzOs = new Date().getTimezoneOffset();
875
	document.getElementById("readmsg_date").children[1].textContent = new Date((ts * 1000) + (tzOs * -60000)).toISOString().slice(0, 19).replace("T", " ");
876
877
	const isInt = ae.GetOutMsgIsInt(num);
878
	document.getElementById("readmsg_ip").style.visibility    = isInt? "hidden" : "visible";
879
	document.getElementById("readmsg_rdns").style.visibility  = /*isInt?*/ "hidden" /*: "visible"*/; // TODO
880
	document.getElementById("readmsg_tls").style.visibility   = /*isInt?*/ "hidden" /*: "visible"*/; // TODO
881
	document.getElementById("readmsg_cert").style.visibility  = /*isInt?*/ "hidden" /*: "visible"*/; // TODO
882
	document.getElementById("readmsg_greet").style.visibility = isInt? "hidden" : "visible";
883
884
	if (!isInt) {
885
//		const cc = ae.GetExtMsgCountry(num);
886
887
		document.getElementById("readmsg_ip").children[1].textContent = ae.GetOutMsgIp(num);
888
//		document.getElementById("readmsg_country").textContent = getCountryFlag(cc) + " " + getCountryName(cc);
889
//		document.getElementById("readmsg_tls").children[0].textContent = ae.GetOutMsgTLS(num);
890
		document.getElementById("readmsg_greet").children[0].textContent = ae.GetOutMsgGreet(num);
891
	}
892
893
	let flagText = "";
894
	if (!ae.GetOutMsgFlagVPad(num)) flagText += "<abbr title=\"Invalid padding\">PAD</abbr> ";
895
	if (!ae.GetOutMsgFlagVSig(num)) flagText += "<abbr title=\"Invalid signature\">SIG</abbr> ";
896
	if (ae.GetOutMsgFlagE2ee(num)) flagText += "<abbr title=\"End-to-end encrypted\">E2EE</abbr> ";
897
	document.getElementById("readmsg_flags").children[0].innerHTML = flagText.trim();
898
}
899
900
function addSent() {
901
	const tbl = document.getElementById("tbl_drbox");
902
	tbl.innerHTML = "";
903
904
	for (let i = 0; i < ae.GetOutMsgCount(); i++) {
905
		const row = tbl.insertRow(-1);
906
		row.setAttribute("data-msgid", ae.GetOutMsgIdHex(i));
907
908
		let cell;
909
		cell = row.insertCell(-1); cell.textContent = new Date(ae.GetOutMsgTime(i) * 1000).toISOString().slice(0, 10);
910
		cell = row.insertCell(-1); cell.textContent = ae.GetOutMsgSubj(i);
911
		row.onclick = function() {displayOutMsg(i);};
912
	}
913
}
914
915
function updateAddressCounts() {
916
	document.querySelector("#tbd_accs > tr > td:nth-child(3)").textContent = ae.GetAddressCountNormal();
917
	document.querySelector("#tbd_accs > tr > td:nth-child(4)").textContent = ae.GetAddressCountShield();
918
919
	document.getElementById("limit_normal").textContent = (ae.GetAddressCountNormal() + "/" + ae.GetLimitNormalA(ae.GetUserLevel())).padStart(ae.GetLimitNormalA(ae.GetUserLevel()) > 9 ? 5 : 1);
920
	document.getElementById("limit_shield").textContent = (ae.GetAddressCountShield() + "/" + ae.GetLimitShieldA(ae.GetUserLevel())).padStart(ae.GetLimitShieldA(ae.GetUserLevel()) > 9 ? 5 : 1);
921
	document.getElementById("limit_total").textContent = ((ae.GetAddressCountNormal() + ae.GetAddressCountShield()) + "/" + ae.GetAddrPerUser()).padStart(5);
922
923
	const limitReached = (ae.GetAddressCountNormal() + ae.GetAddressCountShield() >= 31);
924
	document.getElementById("btn_address_create_normal").disabled = (limitReached || ae.GetAddressCountNormal() >= ae.GetLimitNormalA(ae.GetUserLevel()));
925
	document.getElementById("btn_address_create_shield").disabled = (limitReached || ae.GetAddressCountShield() >= ae.GetLimitShieldA(ae.GetUserLevel()));
926
}
927
928
function adjustLevel(pubkey, level, c) {
929
	const fs = document.getElementById("tbl_accs");
930
	fs.disabled = true;
931
932
	ae.Account_Update(pubkey, level, function(error) {
933
		fs.disabled = false;
934
935
		if (!error) {
936
			c[4].textContent = level;
937
			c[5].children[0].disabled = (level === 3);
938
			c[6].children[0].disabled = (level === 0);
939
		} else console.log("Error " + error)
940
	});
941
}
942
943
function addAccountToTable(i) {
944
	const tblAccs = document.getElementById("tbd_accs");
945
	const row = tblAccs.insertRow(-1);
946
	let cell;
947
	cell = row.insertCell(-1); cell.textContent = ae.Admin_GetUserPkHex(i);
948
	cell = row.insertCell(-1); cell.textContent = ae.Admin_GetUserSpace(i);
949
	cell = row.insertCell(-1); cell.textContent = ae.Admin_GetUserNAddr(i);
950
	cell = row.insertCell(-1); cell.textContent = ae.Admin_GetUserSAddr(i);
951
	cell = row.insertCell(-1); cell.textContent = ae.Admin_GetUserLevel(i);
952
953
	cell = row.insertCell(-1); cell.innerHTML = "<button type=\"button\" autocomplete=\"off\">+</button>";
954
	cell.children[0].onclick = function() {const c = this.parentElement.parentElement.cells; adjustLevel(c[0].textContent, parseInt(c[4].textContent, 10) + 1, c);};
955
	cell.children[0].disabled = (ae.Admin_GetUserLevel(i) === 3);
956
957
	cell = row.insertCell(-1); cell.innerHTML = "<button type=\"button\" autocomplete=\"off\">&minus;</button>";
958
	cell.children[0].onclick = function() {const c = this.parentElement.parentElement.cells; adjustLevel(c[0].textContent, parseInt(c[4].textContent, 10) - 1, c);};
959
	cell.children[0].disabled = (ae.Admin_GetUserLevel(i) === 0);
960
961
	cell = row.insertCell(-1); cell.innerHTML = "<button type=\"button\" autocomplete=\"off\">X</button>";
962
	cell.children[0].onclick = function() {
963
		const tr = this.parentElement.parentElement;
964
		ae.Account_Delete(tr.cells[0].textContent, function(error) {
965
			if (!error) tr.remove();
966
			else console.log("Error " + error);
967
		});
968
	};
969
}
970
971
function reloadAccount() {
972
	// Limits
973
	const tblLimits = document.getElementById("tbl_limits");
974
	if (ae.IsUserAdmin()) {
975
		for (let i = 0; i < 4; i++) {
976
			tblLimits.rows[i].cells[1].children[0].disabled = false;
977
			tblLimits.rows[i].cells[2].children[0].disabled = false;
978
			tblLimits.rows[i].cells[3].children[0].disabled = false;
979
980
			tblLimits.rows[i].cells[1].children[0].value = ae.GetLimitStorage(i) + 1;
981
			tblLimits.rows[i].cells[2].children[0].value = ae.GetLimitNormalA(i);
982
			tblLimits.rows[i].cells[3].children[0].value = ae.GetLimitShieldA(i);
983
		}
984
	} else {
985
		const lvl = ae.GetUserLevel();
986
		tblLimits.rows[lvl].cells[1].children[0].value = ae.GetLimitStorage(lvl) + 1;
987
		tblLimits.rows[lvl].cells[2].children[0].value = ae.GetLimitNormalA(lvl);
988
		tblLimits.rows[lvl].cells[3].children[0].value = ae.GetLimitShieldA(lvl);
989
	}
990
991
	// Accounts
992
	const tblAccs = document.getElementById("tbd_accs");
993
994
	// All: Our account
995
	const row = tblAccs.insertRow(-1);
996
	let cell;
997
	cell = row.insertCell(-1); cell.textContent = ae.GetUserPkHex();
998
	cell = row.insertCell(-1); cell.textContent = Math.round(ae.GetTotalMsgBytes() / 1048576); // MiB
999
	cell = row.insertCell(-1); cell.textContent = ae.GetAddressCountNormal();
1000
	cell = row.insertCell(-1); cell.textContent = ae.GetAddressCountShield();
1001
	cell = row.insertCell(-1); cell.textContent = ae.GetUserLevel();
1002
	cell = row.insertCell(-1); cell.innerHTML = "<button type=\"button\" autocomplete=\"off\" disabled=\"disabled\">+</button>";
1003
1004
	cell = row.insertCell(-1); cell.innerHTML = "<button id=\"btn_lowme\" type=\"button\" autocomplete=\"off\" disabled=\"disabled\">&minus;</button>";
1005
	cell.children[0].onclick = function() {
1006
		const newLevel = parseInt(row.cells[4].textContent, 10) - 1;
1007
		ae.Account_Update(ae.GetUserPkHex(), newLevel, function(error) {
1008
			if (!error) {
1009
				row.cells[4].textContent = newLevel;
1010
				if (newLevel === 0) {
1011
					document.getElementById("btn_lowme").disabled = true;
1012
					document.getElementById("chk_lowme").disabled = true;
1013
				}
1014
			}
1015
			else console.log("Error " + error);
1016
		});
1017
	};
1018
1019
	cell = row.insertCell(-1); cell.innerHTML = "<button id=\"btn_delme\" type=\"button\" autocomplete=\"off\" disabled=\"disabled\">X</button>";
1020
	cell.children[0].onclick = function() {
1021
		ae.Account_Delete(ae.GetUserPkHex(), function(error) {
1022
			if (!error) {
1023
				row.remove();
1024
				document.getElementById("chk_delme").disabled = true;
1025
			} else console.log("Error " + error);
1026
		});
1027
	};
1028
1029
	document.getElementById("txt_reg").disabled = !ae.IsUserAdmin();
1030
	document.getElementById("btn_reg").disabled = !ae.IsUserAdmin();
1031
	document.getElementById("chk_lowme").disabled = (ae.GetUserLevel() === 0);
1032
1033
	// Contacts
1034
	for (let i = 0; i < ae.GetContactCount(); i++) {
1035
		addContact(
1036
			ae.GetContactMail(i),
1037
			ae.GetContactName(i),
1038
			ae.GetContactNote(i)
1039
		);
1040
	}
1041
1042
	refreshContactList();
1043
1044
	// Addresses
1045
	for (let i = 0; i < ae.GetAddressCount(); i++) {
1046
		addAddress(i);
1047
	}
1048
1049
	updateAddressCounts();
1050
	addMessages();
1051
	addUploads();
1052
	addSent();
1053
1054
	document.getElementById("btn_rght").disabled = (tabs[tab].cur === tabs[tab].max);
1055
}
1056
1057
function deleteAddress(addr) {
1058
	let btns = document.querySelectorAll("#tbl_addrs button");
1059
	for (let i = 0; i < btns.length; i++) btns[i].disabled = true;
1060
1061
	let addressToDelete = -1;
1062
1063
	for (let i = 0; i < ae.GetAddressCount(); i++) {
1064
		if (addr === ae.GetAddress(i)) {
1065
			addressToDelete = i;
1066
			break;
1067
		}
1068
	}
1069
1070
	if (addressToDelete === -1) return;
1071
1072
	ae.Address_Delete(addressToDelete, function(error1) {
1073
		if (!error1) {
1074
			document.getElementById("tbl_addrs").deleteRow(addressToDelete);
1075
			document.getElementById("write_from").remove(addressToDelete);
1076
			updateAddressCounts();
1077
1078
			const limitReached = (ae.GetAddressCountNormal() + ae.GetAddressCountShield() >= 31);
1079
			document.getElementById("btn_address_create_normal").disabled = (limitReached || ae.GetAddressCountNormal() > ae.GetLimitNormalA(ae.GetUserLevel()));
1080
			document.getElementById("btn_address_create_shield").disabled = (limitReached || ae.GetAddressCountShield() > ae.GetLimitShieldA(ae.GetUserLevel()));
1081
1082
			ae.Private_Update(function(error2) {
1083
				if (error2) console.log("Failed to update the Private field: " + error2);
1084
1085
				btns = document.querySelectorAll("#tbl_addrs button");
1086
				for (let i = 0; i < btns.length; i++) btns[i].disabled = false;
1087
			});
1088
		} else {
1089
			console.log("Failed to delete address: " + error1);
1090
1091
			btns = document.querySelectorAll("#tbl_addrs button");
1092
			for (let i = 0; i < btns.length; i++) btns[i].disabled = false;
1093
		}
1094
	});
1095
}
1096
1097
function shieldMix(addr) {
1098
	let newAddr = "";
1099
1100
	for (let i = 0; i < 16; i++) {
1101
		switch (addr.charAt(i)) {
1102
			case '1':
1103
				newAddr += "1iIlL".charAt(Math.floor(Math.random() * 5));
1104
				break;
1105
			case '0':
1106
				newAddr += "0oO".charAt(Math.floor(Math.random() * 3));
1107
				break;
1108
			case 'w':
1109
				newAddr += "VvWw".charAt(Math.floor(Math.random() * 4));
1110
				break;
1111
			default:
1112
				newAddr += (Math.random() > 0.5) ? addr.charAt(i) : addr.charAt(i).toUpperCase();
1113
		}
1114
	}
1115
1116
	return newAddr;
1117
}
1118
1119
function addAddress(num) {
1120
	const addrTable = document.getElementById("tbl_addrs");
1121
	const row = addrTable.insertRow(-1);
1122
	const cellAddr = row.insertCell(-1);
1123
	const cellChk1 = row.insertCell(-1);
1124
	const cellChk2 = row.insertCell(-1);
1125
	const cellBtnD = row.insertCell(-1);
1126
1127
	cellAddr.textContent = ae.GetAddress(num);
1128
	cellAddr.onclick = function() {
1129
		if (cellAddr.textContent.length === 16)
1130
			navigator.clipboard.writeText(shieldMix(cellAddr.textContent) + "@" + ae.GetDomainEml());
1131
		else
1132
			navigator.clipboard.writeText(cellAddr.textContent + "@" + ae.GetDomainEml());
1133
	};
1134
1135
	cellChk1.innerHTML = ae.GetAddressAccExt(num) ? "<input type=\"checkbox\" checked=\"checked\">" : "<input type=\"checkbox\">";
1136
	cellChk2.innerHTML = ae.GetAddressAccInt(num) ? "<input type=\"checkbox\" checked=\"checked\">" : "<input type=\"checkbox\">";
1137
1138
	cellBtnD.innerHTML = "<button type=\"button\">X</button>";
1139
	cellBtnD.onclick = function() {deleteAddress(cellAddr.textContent);};
1140
1141
	const opt = document.createElement("option");
1142
	opt.value = cellAddr.textContent;
1143
	opt.textContent = cellAddr.textContent + "@" + ae.GetDomainEml();
1144
	document.getElementById("write_from").appendChild(opt);
1145
}
1146
1147
document.getElementById("btn_dele").onclick = function() {
1148
	this.blur();
1149
1150
	if (tab === TAB_WRITE) {
1151
		tabs[tab].cur = 0;
1152
		updateTab();
1153
1154
		document.querySelector("#write2_pkey > input").value = "";
1155
1156
		document.getElementById("write_recv").value = "";
1157
		document.getElementById("write_subj").value = "";
1158
		document.getElementById("write_body").value = "";
1159
1160
		document.getElementById("write_recv").readOnly = false;
1161
		document.getElementById("write_subj").readOnly = false;
1162
		document.getElementById("write_subj").setAttribute("data-replyid", "");
1163
1164
		document.getElementById("write_recv").focus();
1165
	}
1166
};
1167
1168
document.getElementById("btn_updt").onclick = function() {
1169
	const btn = this;
1170
	btn.disabled = true;
1171
	btn.blur();
1172
1173
	if (tab === TAB_INBOX) {
1174
		document.getElementById("tbl_inbox").style.opacity = 0.5;
1175
1176
		ae.Message_Browse(true, false, function(error) {
1177
			document.getElementById("tbl_inbox").style.opacity = 1;
1178
1179
			if (!error) {
1180
				addMessages();
1181
				addUploads();
1182
				btn.disabled = false;
1183
			} else {
1184
				console.log("Failed to refresh: " + error);
1185
				btn.disabled = false;
1186
			}
1187
		});
1188
	}
1189
};
1190
1191
document.getElementById("btn_mdele").onclick = function() {
1192
	const btn = this;
1193
	btn.blur();
1194
	btn.disabled = true;
1195
1196
	const delId = document.querySelector("article").getAttribute("data-msgid");
1197
	if (!delId) return;
1198
1199
	ae.Message_Delete(delId, function(error) {
1200
		if (!error) {
1201
			["tbl_inbox", "tbl_drbox", "tbd_uploads"].forEach(function(tbl_name) {
1202
				const tbl = document.getElementById(tbl_name);
1203
				for (let i = 0; i < tbl.rows.length; i++) {if (tbl.rows[i].getAttribute("data-msgid") === delId) tbl.deleteRow(i);}
1204
			});
1205
1206
			addMessages();
1207
			addUploads();
1208
			addSent();
1209
		} else btn.disabled = false; // TODO display error
1210
	});
1211
};
1212
1213
function refreshContactList() {
1214
	const lst = document.getElementById("contact_emails");
1215
	lst.innerHTML = "";
1216
1217
	for (let i = 0; i < ae.GetContactCount(); i++) {
1218
		const el = document.createElement("option");
1219
		el.value = ae.GetContactMail(i);
1220
		lst.appendChild(el);
1221
	}
1222
1223
	if (ae.IsUserAdmin()) {
1224
		const el = document.createElement("option");
1225
		el.value = "public";
1226
		lst.appendChild(el);
1227
	}
1228
}
1229
1230
function addContact(mail, name, note) {
1231
	const tbl = document.getElementById("tbl_ctact");
1232
	const row = tbl.insertRow(-1);
1233
	const cellMail = row.insertCell(-1);
1234
	const cellName = row.insertCell(-1);
1235
	const cellNote = row.insertCell(-1);
1236
	const cellBtnD = row.insertCell(-1);
1237
1238
	cellMail.autocapitalize = "off";
1239
	cellMail.spellcheck = false;
1240
	cellMail.inputMode = "email";
1241
1242
	cellName.autocapitalize = "words";
1243
	cellName.spellcheck = false;
1244
1245
	cellNote.autocapitalize = "off";
1246
	cellNote.spellcheck = false;
1247
1248
	cellMail.textContent = mail;
1249
	cellName.textContent = name;
1250
	cellNote.textContent = note;
1251
	cellBtnD.innerHTML = "<button type=\"button\">X</button>";
1252
1253
	cellMail.contentEditable = true;
1254
	cellName.contentEditable = true;
1255
	cellNote.contentEditable = true;
1256
1257
	cellBtnD.onclick = function() {row.remove();};
1258
}
1259
1260
document.getElementById("btn_newcontact").onclick = function() {
1261
	addContact("", "", "");
1262
};
1263
1264
document.getElementById("btn_savecontacts").onclick = function() {
1265
	while (ae.GetContactCount() > 0) {
1266
		ae.DeleteContact(0);
1267
	}
1268
1269
	for (const row of document.getElementById("tbl_ctact").rows) {
1270
		ae.AddContact(row.cells[0].textContent, row.cells[1].textContent, row.cells[2].textContent);
1271
	}
1272
1273
	refreshContactList();
1274
1275
	const btn = this;
1276
	btn.disabled = true;
1277
1278
	ae.Private_Update(function(error) {
1279
		btn.disabled = false;
1280
1281
		if (error) {
1282
			console.log("Failed contacts update: " + error);
1283
		}
1284
	});
1285
};
1286
1287
function writeVerify() {
1288
	if (
1289
	   !document.getElementById("write_recv").reportValidity()
1290
	|| !document.getElementById("write_subj").reportValidity()
1291
	|| !document.getElementById("write_body").reportValidity()
1292
	) {tabs[TAB_WRITE].cur = 0; return;}
1293
1294
	document.getElementById("div_write_1").hidden = true;
1295
	document.getElementById("div_write_2").hidden = false;
1296
1297
	document.getElementById("write2_recv").textContent = document.getElementById("write_recv").value;
1298
	document.getElementById("write2_subj").textContent = document.getElementById("write_subj").value;
1299
	document.getElementById("write2_rply").textContent = document.getElementById("write_subj").getAttribute("data-replyid");
1300
	document.getElementById("write2_body").textContent = document.getElementById("write_body").value;
1301
1302
	if (document.getElementById("write_recv").value.indexOf("@") >= 0) {
1303
		document.getElementById("write2_from").textContent = document.getElementById("write_from").value + "@" + ae.GetDomainEml();
1304
		document.getElementById("write2_pkey").hidden = true;
1305
	} else {
1306
		document.getElementById("write2_from").textContent = document.getElementById("write_from").value;
1307
		document.getElementById("write2_pkey").hidden = (document.getElementById("write_recv").value === "public");
1308
	}
1309
1310
	document.querySelector("#write2_send > button").disabled = false;
1311
	document.getElementById("write2_btntxt").textContent = (document.getElementById("write_recv").value === "public") ? "Make" : "Send to";
1312
}
1313
1314
function updateTab() {
1315
	switch (tab) {
1316
		case TAB_INBOX:
1317
			addMessages();
1318
		break;
1319
1320
		case TAB_DRBOX:
1321
			addSent();
1322
		break;
1323
1324
		case TAB_WRITE:
1325
			if (tabs[tab].cur === 0) {
1326
				document.getElementById("div_write_1").hidden = false;
1327
				document.getElementById("div_write_2").hidden = true;
1328
				document.getElementById("write_body").focus();
1329
			} else {
1330
				writeVerify();
1331
			}
1332
		break;
1333
1334
		case TAB_NOTES:
1335
			for (let i = 0; i <= tabs[tab].max; i++) {
1336
				document.getElementById("div_notes").children[i].hidden = (i !== tabs[tab].cur);
1337
			}
1338
		break;
1339
1340
		case TAB_TOOLS:
1341
			for (let i = 0; i <= tabs[tab].max; i++) {
1342
				document.getElementById("div_tools").children[i].hidden = (i !== tabs[tab].cur);
1343
			}
1344
		break;
1345
	}
1346
1347
	document.getElementById("btn_left").disabled = (tabs[tab].cur === 0);
1348
	document.getElementById("btn_rght").disabled = (tabs[tab].cur === tabs[tab].max);
1349
}
1350
1351
document.getElementById("btn_left").onclick = function() {
1352
	tabs[tab].cur--;
1353
	if (tabs[tab].cur === 0) this.disabled = true;
1354
	if (tabs[tab].cur < tabs[tab].max) document.getElementById("btn_rght").disabled = false;
1355
	updateTab();
1356
	this.blur();
1357
};
1358
1359
document.getElementById("btn_rght").onclick = function() {
1360
	tabs[tab].cur++;
1361
	if (tabs[tab].cur === tabs[tab].max) this.disabled = true;
1362
	document.getElementById("btn_left").disabled = false;
1363
	updateTab();
1364
	this.blur();
1365
};
1366
1367
const buttons = document.querySelectorAll("#main1 > .top > button");
1368
for (let i = 0; i < buttons.length; i++) {
1369
	buttons[i].onclick = function() {
1370
		tab = i;
1371
1372
		for (let j = 0; j < buttons.length; j++) {
1373
			document.querySelectorAll("#main1 > .mid > div")[j].hidden = (tab !== j);
1374
			buttons[j].disabled = (tab === j);
1375
		}
1376
1377
		document.getElementById("btn_left").disabled = (tabs[tab].cur === 0);
1378
		document.getElementById("btn_rght").disabled = (tabs[tab].cur === tabs[tab].max);
1379
		document.getElementById("btn_dele").disabled = !tabs[tab].btnDele;
1380
		document.getElementById("btn_updt").disabled = !tabs[tab].btnUpdt;
1381
1382
		updateTab();
1383
	};
1384
}
1385
1386
function addressCreate(addr) {
1387
	document.getElementById("btn_address_create_normal").disabled = true;
1388
	document.getElementById("btn_address_create_shield").disabled = true;
1389
1390
	ae.Address_Create(addr, function(error1) {
1391
		if (!error1) {
1392
			ae.Private_Update(function(error2) {
1393
				updateAddressCounts();
1394
1395
				if (!error2) {
1396
					addAddress(ae.GetAddressCount() - 1);
1397
					if (addr !== "SHIELD") {
1398
						document.getElementById("txt_address_create_normal").value = "";
1399
						document.getElementById("txt_address_create_normal").focus();
1400
					}
1401
				} else {
1402
					console.log("Failed to update the Private field: " + error2);
1403
				}
1404
			});
1405
		} else {
1406
			console.log("Failed to add address: " + error1);
1407
			updateAddressCounts();
1408
		}
1409
	});
1410
}
1411
1412
document.getElementById("btn_address_create_normal").onclick = function() {
1413
	if (ae.GetAddressCountNormal() >= ae.GetLimitNormalA(ae.GetUserLevel()) || ae.GetAddressCountNormal() + ae.GetAddressCountShield() >= 31) return;
1414
1415
	const txtNewAddr = document.getElementById("txt_address_create_normal");
1416
	if (!txtNewAddr.reportValidity()) return;
1417
1418
	addressCreate(txtNewAddr.value);
1419
};
1420
1421
document.getElementById("btn_address_create_shield").onclick = function() {
1422
	if (ae.GetAddressCountShield() >= ae.GetLimitShieldA(ae.GetUserLevel()) || ae.GetAddressCountNormal() + ae.GetAddressCountShield() >= 31) return;
1423
1424
	addressCreate("SHIELD");
1425
};
1426
1427
document.getElementById("btn_address_update").onclick = function() {
1428
	const btn = this;
1429
	btn.disabled = true;
1430
1431
	const rows = document.getElementById("tbl_addrs").rows;
1432
1433
	for (let i = 0; i < rows.length; i++) {
1434
		ae.SetAddressAccExt(i, rows[i].getElementsByTagName("input")[0].checked);
1435
		ae.SetAddressAccInt(i, rows[i].getElementsByTagName("input")[1].checked);
1436
	}
1437
1438
	ae.Address_Update(function(error) {
1439
		if (error) console.log("Address/Update failed: " + error);
1440
		btn.disabled = false;
1441
	});
1442
};
1443
1444
1445
document.getElementById("txt_reg").onkeyup = function(event) {
1446
	if (event.key === "Enter") {
1447
		event.preventDefault();
1448
		document.getElementById("btn_reg").click();
1449
	}
1450
};
1451
1452
document.getElementById("btn_reg").onclick = function() {
1453
	const btn = document.getElementById("btn_reg");
1454
	const txt = document.getElementById("txt_reg");
1455
	if (!txt.reportValidity()) return;
1456
	btn.disabled = true;
1457
1458
	ae.Account_Create(txt.value, function(error) {
1459
		if (!error) {
1460
			addAccountToTable(ae.Admin_GetUserCount() - 1);
1461
			txt.value = "";
1462
		} // else TODO
1463
1464
		btn.disabled = false;
1465
	});
1466
};
1467
1468
document.getElementById("chk_delme").onclick = function() {document.getElementById("btn_delme").disabled = !this.checked;};
1469
document.getElementById("chk_lowme").onclick = function() {document.getElementById("btn_lowme").disabled = !this.checked;};
1470
1471
document.getElementById("btn_notepad_saveupl").onclick = function() {
1472
	const np = document.getElementById("txt_notepad");
1473
	np.disabled = true;
1474
1475
	let fname = prompt("Save as...", "Untitled");
1476
	if (!fname.endsWith(".txt")) fname += ".txt";
1477
1478
	ae.Message_Upload(fname, np.value, function(error) {
1479
		if (!error) {
1480
			np.value = "";
1481
			addUploads();
1482
			document.getElementById("tbd_accs").children[0].children[1].textContent = Math.round(ae.GetTotalMsgBytes() / 1024 / 1024);
1483
		} else {
1484
			console.log("Failed to add text: " + error);
1485
		}
1486
1487
		np.disabled = false;
1488
	});
1489
};
1490
1491
document.getElementById("btn_upload").onclick = function() {
1492
	const btn = this;
1493
	const fileSelector = document.createElement("input");
1494
	fileSelector.type = "file";
1495
	fileSelector.click();
1496
1497
	fileSelector.onchange = function() {
1498
		btn.disabled = true;
1499
1500
		const reader = new FileReader();
1501
		reader.onload = function() {
1502
			ae.Message_Upload(fileSelector.files[0].name, new Uint8Array(reader.result), function(error) {
1503
				if (!error) {
1504
					addUploads();
1505
					document.getElementById("tbd_accs").children[0].children[1].textContent = Math.round(ae.GetTotalMsgBytes() / 1024 / 1024);
1506
				} else {
1507
					console.log("Failed upload: " + error);
1508
				}
1509
1510
				btn.disabled = false;
1511
			});
1512
		};
1513
1514
		reader.readAsArrayBuffer(fileSelector.files[0]);
1515
	};
1516
};
1517
1518
document.getElementById("btn_pg").onclick = function() {
1519
	localStorage.greeting = document.getElementById("txt_pg").value;
1520
};
1521
1522
document.querySelector("#write2_send > button").onclick = function() {
1523
	const btn = this;
1524
	btn.disabled = true;
1525
1526
	// Public announcement
1527
	if (document.getElementById("write2_recv").textContent === "public") {
1528
		ae.Message_Public(document.getElementById("write_subj").value, document.getElementById("write_body").value, function(error) {
1529
			if (!error) {
1530
				document.getElementById("write2_btntxt").textContent = "Announced to";
1531
				document.getElementById("write_recv").value = "";
1532
				document.getElementById("write_subj").value = "";
1533
				document.getElementById("write_body").value = "";
1534
			} else {
1535
				// TODO display error
1536
				document.getElementById("write2_btntxt").textContent = "Retry making";
1537
				btn.disabled = false;
1538
			}
1539
		});
1540
1541
		return;
1542
	}
1543
1544
	// Email or internal message
1545
	document.getElementById("write2_btntxt").textContent = "Sending to";
1546
1547
	ae.Message_Create(
1548
		document.getElementById("write_subj").value,
1549
		document.getElementById("write_body").value,
1550
		document.getElementById("write_from").value,
1551
		document.getElementById("write_recv").value,
1552
		document.getElementById("write_subj").getAttribute("data-replyid"),
1553
		(document.getElementById("write2_recv").textContent.indexOf("@") > 0) ? null : sodium.from_base64(document.querySelector("#write2_pkey > input").value, sodium.base64_variants.ORIGINAL_NO_PADDING),
1554
		function(error) {
1555
			if (!error) {
1556
				document.getElementById("write2_btntxt").textContent = "Delivered to";
1557
				document.getElementById("write_recv").value = "";
1558
				document.getElementById("write_subj").value = "";
1559
				document.getElementById("write_body").value = "";
1560
			} else {
1561
				errorDialog(error);
1562
1563
				document.getElementById("write2_btntxt").textContent = "Retry sending to";
1564
				btn.disabled = false;
1565
			}
1566
		}
1567
	);
1568
};
1569
1570
document.getElementById("txt_skey").onfocus = function() {
1571
	document.getElementById("greeting").textContent = localStorage.greeting;
1572
};
1573
1574
document.getElementById("txt_skey").onkeyup = function(event) {
1575
	if (event.key === "Enter") {
1576
		event.preventDefault();
1577
		document.getElementById("btn_enter").click();
1578
	}
1579
};
1580
1581
document.getElementById("btn_enter").onclick = function() {
1582
	const txtSkey = document.getElementById("txt_skey");
1583
1584
	if (txtSkey.value === "") {
1585
		ae.Reset();
1586
		document.getElementById("greeting").textContent = "Data cleared";
1587
		return;
1588
	}
1589
1590
	if (!txtSkey.reportValidity()) return;
1591
1592
	const btn = this;
1593
	btn.disabled = true;
1594
1595
	document.getElementById("txt_skey").disabled = true;
1596
	document.getElementById("txt_skey").style.background = "#233";
1597
1598
	ae.SetKeys(txtSkey.value, function(successSetKeys) {
1599
		if (successSetKeys) {
1600
			document.body.style.cursor = "wait";
1601
1602
			ae.Message_Browse(false, true, function(errorBrowse) {
1603
				document.body.style.cursor = "auto";
1604
1605
				if (!errorBrowse) {
1606
					txtSkey.value = "";
1607
					document.getElementById("div_begin").hidden = true;
1608
					document.getElementById("div_main").hidden = false;
1609
					reloadAccount();
1610
1611
					if (ae.IsUserAdmin()) {
1612
						ae.Account_Browse(function(errorAcc) {
1613
							if (!errorAcc) {
1614
								for (let i = 0; i < ae.Admin_GetUserCount(); i++) {addAccountToTable(i);}
1615
							} else {
1616
								console.log("Failed to Account_Browse: " + errorAcc);
1617
							}
1618
						});
1619
					}
1620
				} else {
1621
					document.getElementById("txt_skey").disabled = false;
1622
					document.getElementById("txt_skey").style.background = "#466";
1623
					btn.focus();
1624
1625
					document.getElementById("greeting").textContent = "Error " + errorBrowse;
1626
					btn.disabled = false;
1627
				}
1628
			});
1629
		} else {
1630
			document.getElementById("txt_skey").disabled = false;
1631
			document.getElementById("txt_skey").style.background = "#466";
1632
			txtSkey.focus();
1633
1634
			document.getElementById("greeting").textContent = "SetKeys failed";
1635
			btn.disabled = false;
1636
		}
1637
	});
1638
};
1639
1640
});
1641