mirror of
https://github.com/mjl-/mox.git
synced 2024-12-27 08:53:48 +03:00
934 lines
56 KiB
JavaScript
934 lines
56 KiB
JavaScript
|
"use strict";
|
|||
|
// NOTE: GENERATED by github.com/mjl-/sherpats, DO NOT MODIFY
|
|||
|
var api;
|
|||
|
(function (api) {
|
|||
|
// Validation of "message From" domain.
|
|||
|
let Validation;
|
|||
|
(function (Validation) {
|
|||
|
Validation[Validation["ValidationUnknown"] = 0] = "ValidationUnknown";
|
|||
|
Validation[Validation["ValidationStrict"] = 1] = "ValidationStrict";
|
|||
|
Validation[Validation["ValidationDMARC"] = 2] = "ValidationDMARC";
|
|||
|
Validation[Validation["ValidationRelaxed"] = 3] = "ValidationRelaxed";
|
|||
|
Validation[Validation["ValidationPass"] = 4] = "ValidationPass";
|
|||
|
Validation[Validation["ValidationNeutral"] = 5] = "ValidationNeutral";
|
|||
|
Validation[Validation["ValidationTemperror"] = 6] = "ValidationTemperror";
|
|||
|
Validation[Validation["ValidationPermerror"] = 7] = "ValidationPermerror";
|
|||
|
Validation[Validation["ValidationFail"] = 8] = "ValidationFail";
|
|||
|
Validation[Validation["ValidationSoftfail"] = 9] = "ValidationSoftfail";
|
|||
|
Validation[Validation["ValidationNone"] = 10] = "ValidationNone";
|
|||
|
})(Validation = api.Validation || (api.Validation = {}));
|
|||
|
// AttachmentType is for filtering by attachment type.
|
|||
|
let AttachmentType;
|
|||
|
(function (AttachmentType) {
|
|||
|
AttachmentType["AttachmentIndifferent"] = "";
|
|||
|
AttachmentType["AttachmentNone"] = "none";
|
|||
|
AttachmentType["AttachmentAny"] = "any";
|
|||
|
AttachmentType["AttachmentImage"] = "image";
|
|||
|
AttachmentType["AttachmentPDF"] = "pdf";
|
|||
|
AttachmentType["AttachmentArchive"] = "archive";
|
|||
|
AttachmentType["AttachmentSpreadsheet"] = "spreadsheet";
|
|||
|
AttachmentType["AttachmentDocument"] = "document";
|
|||
|
AttachmentType["AttachmentPresentation"] = "presentation";
|
|||
|
})(AttachmentType = api.AttachmentType || (api.AttachmentType = {}));
|
|||
|
api.structTypes = { "Address": true, "Attachment": true, "ChangeMailboxAdd": true, "ChangeMailboxCounts": true, "ChangeMailboxKeywords": true, "ChangeMailboxRemove": true, "ChangeMailboxRename": true, "ChangeMailboxSpecialUse": true, "ChangeMsgAdd": true, "ChangeMsgFlags": true, "ChangeMsgRemove": true, "Domain": true, "DomainAddressConfig": true, "Envelope": true, "EventStart": true, "EventViewChanges": true, "EventViewErr": true, "EventViewMsgs": true, "EventViewReset": true, "File": true, "Filter": true, "Flags": true, "ForwardAttachments": true, "Mailbox": true, "Message": true, "MessageAddress": true, "MessageEnvelope": true, "MessageItem": true, "NotFilter": true, "Page": true, "ParsedMessage": true, "Part": true, "Query": true, "Request": true, "SpecialUse": true, "SubmitMessage": true };
|
|||
|
api.stringsTypes = { "AttachmentType": true, "Localpart": true };
|
|||
|
api.intsTypes = { "ModSeq": true, "UID": true, "Validation": true };
|
|||
|
api.types = {
|
|||
|
"Request": { "Name": "Request", "Docs": "", "Fields": [{ "Name": "ID", "Docs": "", "Typewords": ["int64"] }, { "Name": "SSEID", "Docs": "", "Typewords": ["int64"] }, { "Name": "ViewID", "Docs": "", "Typewords": ["int64"] }, { "Name": "Cancel", "Docs": "", "Typewords": ["bool"] }, { "Name": "Query", "Docs": "", "Typewords": ["Query"] }, { "Name": "Page", "Docs": "", "Typewords": ["Page"] }] },
|
|||
|
"Query": { "Name": "Query", "Docs": "", "Fields": [{ "Name": "OrderAsc", "Docs": "", "Typewords": ["bool"] }, { "Name": "Filter", "Docs": "", "Typewords": ["Filter"] }, { "Name": "NotFilter", "Docs": "", "Typewords": ["NotFilter"] }] },
|
|||
|
"Filter": { "Name": "Filter", "Docs": "", "Fields": [{ "Name": "MailboxID", "Docs": "", "Typewords": ["int64"] }, { "Name": "MailboxChildrenIncluded", "Docs": "", "Typewords": ["bool"] }, { "Name": "MailboxName", "Docs": "", "Typewords": ["string"] }, { "Name": "Words", "Docs": "", "Typewords": ["[]", "string"] }, { "Name": "From", "Docs": "", "Typewords": ["[]", "string"] }, { "Name": "To", "Docs": "", "Typewords": ["[]", "string"] }, { "Name": "Oldest", "Docs": "", "Typewords": ["nullable", "timestamp"] }, { "Name": "Newest", "Docs": "", "Typewords": ["nullable", "timestamp"] }, { "Name": "Subject", "Docs": "", "Typewords": ["[]", "string"] }, { "Name": "Attachments", "Docs": "", "Typewords": ["AttachmentType"] }, { "Name": "Labels", "Docs": "", "Typewords": ["[]", "string"] }, { "Name": "Headers", "Docs": "", "Typewords": ["[]", "[]", "string"] }, { "Name": "SizeMin", "Docs": "", "Typewords": ["int64"] }, { "Name": "SizeMax", "Docs": "", "Typewords": ["int64"] }] },
|
|||
|
"NotFilter": { "Name": "NotFilter", "Docs": "", "Fields": [{ "Name": "Words", "Docs": "", "Typewords": ["[]", "string"] }, { "Name": "From", "Docs": "", "Typewords": ["[]", "string"] }, { "Name": "To", "Docs": "", "Typewords": ["[]", "string"] }, { "Name": "Subject", "Docs": "", "Typewords": ["[]", "string"] }, { "Name": "Attachments", "Docs": "", "Typewords": ["AttachmentType"] }, { "Name": "Labels", "Docs": "", "Typewords": ["[]", "string"] }] },
|
|||
|
"Page": { "Name": "Page", "Docs": "", "Fields": [{ "Name": "AnchorMessageID", "Docs": "", "Typewords": ["int64"] }, { "Name": "Count", "Docs": "", "Typewords": ["int32"] }, { "Name": "DestMessageID", "Docs": "", "Typewords": ["int64"] }] },
|
|||
|
"ParsedMessage": { "Name": "ParsedMessage", "Docs": "", "Fields": [{ "Name": "ID", "Docs": "", "Typewords": ["int64"] }, { "Name": "Part", "Docs": "", "Typewords": ["Part"] }, { "Name": "Headers", "Docs": "", "Typewords": ["{}", "[]", "string"] }, { "Name": "Texts", "Docs": "", "Typewords": ["[]", "string"] }, { "Name": "HasHTML", "Docs": "", "Typewords": ["bool"] }, { "Name": "ListReplyAddress", "Docs": "", "Typewords": ["nullable", "MessageAddress"] }] },
|
|||
|
"Part": { "Name": "Part", "Docs": "", "Fields": [{ "Name": "BoundaryOffset", "Docs": "", "Typewords": ["int64"] }, { "Name": "HeaderOffset", "Docs": "", "Typewords": ["int64"] }, { "Name": "BodyOffset", "Docs": "", "Typewords": ["int64"] }, { "Name": "EndOffset", "Docs": "", "Typewords": ["int64"] }, { "Name": "RawLineCount", "Docs": "", "Typewords": ["int64"] }, { "Name": "DecodedSize", "Docs": "", "Typewords": ["int64"] }, { "Name": "MediaType", "Docs": "", "Typewords": ["string"] }, { "Name": "MediaSubType", "Docs": "", "Typewords": ["string"] }, { "Name": "ContentTypeParams", "Docs": "", "Typewords": ["{}", "string"] }, { "Name": "ContentID", "Docs": "", "Typewords": ["string"] }, { "Name": "ContentDescription", "Docs": "", "Typewords": ["string"] }, { "Name": "ContentTransferEncoding", "Docs": "", "Typewords": ["string"] }, { "Name": "Envelope", "Docs": "", "Typewords": ["nullable", "Envelope"] }, { "Name": "Parts", "Docs": "", "Typewords": ["[]", "Part"] }, { "Name": "Message", "Docs": "", "Typewords": ["nullable", "Part"] }] },
|
|||
|
"Envelope": { "Name": "Envelope", "Docs": "", "Fields": [{ "Name": "Date", "Docs": "", "Typewords": ["timestamp"] }, { "Name": "Subject", "Docs": "", "Typewords": ["string"] }, { "Name": "From", "Docs": "", "Typewords": ["[]", "Address"] }, { "Name": "Sender", "Docs": "", "Typewords": ["[]", "Address"] }, { "Name": "ReplyTo", "Docs": "", "Typewords": ["[]", "Address"] }, { "Name": "To", "Docs": "", "Typewords": ["[]", "Address"] }, { "Name": "CC", "Docs": "", "Typewords": ["[]", "Address"] }, { "Name": "BCC", "Docs": "", "Typewords": ["[]", "Address"] }, { "Name": "InReplyTo", "Docs": "", "Typewords": ["string"] }, { "Name": "MessageID", "Docs": "", "Typewords": ["string"] }] },
|
|||
|
"Address": { "Name": "Address", "Docs": "", "Fields": [{ "Name": "Name", "Docs": "", "Typewords": ["string"] }, { "Name": "User", "Docs": "", "Typewords": ["string"] }, { "Name": "Host", "Docs": "", "Typewords": ["string"] }] },
|
|||
|
"MessageAddress": { "Name": "MessageAddress", "Docs": "", "Fields": [{ "Name": "Name", "Docs": "", "Typewords": ["string"] }, { "Name": "User", "Docs": "", "Typewords": ["string"] }, { "Name": "Domain", "Docs": "", "Typewords": ["Domain"] }] },
|
|||
|
"Domain": { "Name": "Domain", "Docs": "", "Fields": [{ "Name": "ASCII", "Docs": "", "Typewords": ["string"] }, { "Name": "Unicode", "Docs": "", "Typewords": ["string"] }] },
|
|||
|
"SubmitMessage": { "Name": "SubmitMessage", "Docs": "", "Fields": [{ "Name": "From", "Docs": "", "Typewords": ["string"] }, { "Name": "To", "Docs": "", "Typewords": ["[]", "string"] }, { "Name": "Cc", "Docs": "", "Typewords": ["[]", "string"] }, { "Name": "Bcc", "Docs": "", "Typewords": ["[]", "string"] }, { "Name": "Subject", "Docs": "", "Typewords": ["string"] }, { "Name": "TextBody", "Docs": "", "Typewords": ["string"] }, { "Name": "Attachments", "Docs": "", "Typewords": ["[]", "File"] }, { "Name": "ForwardAttachments", "Docs": "", "Typewords": ["ForwardAttachments"] }, { "Name": "IsForward", "Docs": "", "Typewords": ["bool"] }, { "Name": "ResponseMessageID", "Docs": "", "Typewords": ["int64"] }, { "Name": "ReplyTo", "Docs": "", "Typewords": ["string"] }, { "Name": "UserAgent", "Docs": "", "Typewords": ["string"] }] },
|
|||
|
"File": { "Name": "File", "Docs": "", "Fields": [{ "Name": "Filename", "Docs": "", "Typewords": ["string"] }, { "Name": "DataURI", "Docs": "", "Typewords": ["string"] }] },
|
|||
|
"ForwardAttachments": { "Name": "ForwardAttachments", "Docs": "", "Fields": [{ "Name": "MessageID", "Docs": "", "Typewords": ["int64"] }, { "Name": "Paths", "Docs": "", "Typewords": ["[]", "[]", "int32"] }] },
|
|||
|
"Mailbox": { "Name": "Mailbox", "Docs": "", "Fields": [{ "Name": "ID", "Docs": "", "Typewords": ["int64"] }, { "Name": "Name", "Docs": "", "Typewords": ["string"] }, { "Name": "UIDValidity", "Docs": "", "Typewords": ["uint32"] }, { "Name": "UIDNext", "Docs": "", "Typewords": ["UID"] }, { "Name": "Archive", "Docs": "", "Typewords": ["bool"] }, { "Name": "Draft", "Docs": "", "Typewords": ["bool"] }, { "Name": "Junk", "Docs": "", "Typewords": ["bool"] }, { "Name": "Sent", "Docs": "", "Typewords": ["bool"] }, { "Name": "Trash", "Docs": "", "Typewords": ["bool"] }, { "Name": "Keywords", "Docs": "", "Typewords": ["[]", "string"] }, { "Name": "HaveCounts", "Docs": "", "Typewords": ["bool"] }, { "Name": "Total", "Docs": "", "Typewords": ["int64"] }, { "Name": "Deleted", "Docs": "", "Typewords": ["int64"] }, { "Name": "Unread", "Docs": "", "Typewords": ["int64"] }, { "Name": "Unseen", "Docs": "", "Typewords": ["int64"] }, { "Name": "Size", "Docs": "", "Typewords": ["int64"] }] },
|
|||
|
"EventStart": { "Name": "EventStart", "Docs": "", "Fields": [{ "Name": "SSEID", "Docs": "", "Typewords": ["int64"] }, { "Name": "LoginAddress", "Docs": "", "Typewords": ["MessageAddress"] }, { "Name": "Addresses", "Docs": "", "Typewords": ["[]", "MessageAddress"] }, { "Name": "DomainAddressConfigs", "Docs": "", "Typewords": ["{}", "DomainAddressConfig"] }, { "Name": "MailboxName", "Docs": "", "Typewords": ["string"] }, { "Name": "Mailboxes", "Docs": "", "Typewords": ["[]", "Mailbox"] }] },
|
|||
|
"DomainAddressConfig": { "Name": "DomainAddressConfig", "Docs": "", "Fields": [{ "Name": "LocalpartCatchallSeparator", "Docs": "", "Typewords": ["string"] }, { "Name": "LocalpartCaseSensitive", "Docs": "", "Typewords": ["bool"] }] },
|
|||
|
"EventViewErr": { "Name": "EventViewErr", "Docs": "", "Fields": [{ "Name": "ViewID", "Docs": "", "Typewords": ["int64"] }, { "Name": "RequestID", "Docs": "", "Typewords": ["int64"] }, { "Name": "Err", "Docs": "", "Typewords": ["string"] }] },
|
|||
|
"EventViewReset": { "Name": "EventViewReset", "Docs": "", "Fields": [{ "Name": "ViewID", "Docs": "", "Typewords": ["int64"] }, { "Name": "RequestID", "Docs": "", "Typewords": ["int64"] }] },
|
|||
|
"EventViewMsgs": { "Name": "EventViewMsgs", "Docs": "", "Fields": [{ "Name": "ViewID", "Docs": "", "Typewords": ["int64"] }, { "Name": "RequestID", "Docs": "", "Typewords": ["int64"] }, { "Name": "MessageItems", "Docs": "", "Typewords": ["[]", "MessageItem"] }, { "Name": "ParsedMessage", "Docs": "", "Typewords": ["nullable", "ParsedMessage"] }, { "Name": "ViewEnd", "Docs": "", "Typewords": ["bool"] }] },
|
|||
|
"MessageItem": { "Name": "MessageItem", "Docs": "", "Fields": [{ "Name": "Message", "Docs": "", "Typewords": ["Message"] }, { "Name": "Envelope", "Docs": "", "Typewords": ["MessageEnvelope"] }, { "Name": "Attachments", "Docs": "", "Typewords": ["[]", "Attachment"] }, { "Name": "IsSigned", "Docs": "", "Typewords": ["bool"] }, { "Name": "IsEncrypted", "Docs": "", "Typewords": ["bool"] }, { "Name": "FirstLine", "Docs": "", "Typewords": ["string"] }] },
|
|||
|
"Message": { "Name": "Message", "Docs": "", "Fields": [{ "Name": "ID", "Docs": "", "Typewords": ["int64"] }, { "Name": "UID", "Docs": "", "Typewords": ["UID"] }, { "Name": "MailboxID", "Docs": "", "Typewords": ["int64"] }, { "Name": "ModSeq", "Docs": "", "Typewords": ["ModSeq"] }, { "Name": "CreateSeq", "Docs": "", "Typewords": ["ModSeq"] }, { "Name": "Expunged", "Docs": "", "Typewords": ["bool"] }, { "Name": "MailboxOrigID", "Docs": "", "Typewords": ["int64"] }, { "Name": "MailboxDestinedID", "Docs": "", "Typewords": ["int64"] }, { "Name": "Received", "Docs": "", "Typewords": ["timestamp"] }, { "Name": "RemoteIP", "Docs": "", "Typewords": ["string"] }, { "Name": "RemoteIPMasked1", "Docs": "", "Typewords": ["string"] }, { "Name": "RemoteIPMasked2", "Docs": "", "Typewords": ["string"] }, { "Name": "RemoteIPMasked3", "Docs": "", "Typewords": ["string"] }, { "Name": "EHLODomain", "Docs": "", "Typewords": ["string"] }, { "Name": "MailFrom", "Docs": "", "Typewords": ["string"] }, { "Name": "MailFromLocalpart", "Docs": "", "Typewords": ["Localpart"] }, { "Name": "MailFromDomain", "Docs": "", "Typewords": ["string"] }, { "Name": "RcptToLocalpart", "Docs": "", "Typewords": ["Localpart"] }, { "Name": "RcptToDomain", "Docs": "", "Typewords": ["string"] }, { "Name": "MsgFromLocalpart", "Docs": "", "Typewords": ["Localpart"] }, { "Name": "MsgFromDomain", "Docs": "", "Typewords": ["string"] }, { "Name": "MsgFromOrgDomain", "Docs": "", "Typewords": ["string"] }, { "Name": "EHLOValidated", "Docs": "", "Typewords": ["bool"] }, { "Name": "MailFromValidated", "Docs": "", "Typewords": ["bool"] }, { "Name": "MsgFromValidated", "Docs": "", "Typewords": ["bool"] }, { "Name": "EHLOValidation", "Docs": "", "Typewords": ["Validation"] }, { "Name": "MailFromValidation", "Docs": "", "Typewords": ["Validation"] }, { "Name": "MsgFromValidation", "Docs": "", "Typewords": ["Validation"] }, { "Name": "DKIMDomains", "Docs": "", "Typewords": ["[]", "string"] }, { "Name": "MessageID", "Docs": "", "Typewords": ["string"] }, { "Name": "MessageHash", "Docs": "", "Typewords": ["nullable", "string"] }, { "Name": "Seen", "Docs": "", "Typewords": ["bool"] }, { "Name": "Answered", "Docs": "", "Typewords": ["bool"] }, { "Name": "Flagged", "Docs": "", "Typewords": ["bool"] }, { "Name": "Forwarded", "Docs": "", "Typewords": ["bool"] }, { "Name": "Junk", "Docs": "", "Typewords": ["bool"] }, { "Name": "Notjunk", "Docs": "", "Typewords": ["bool"] }, { "Name": "Deleted", "Docs": "", "Typewords": ["bool"] }, { "Name": "Draft", "Docs": "", "Typewords": ["bool"] }, { "Name": "Phishing", "Docs": "", "Typewords": ["bool"] }, { "Name": "MDNSent", "Docs": "", "Typewords": ["bool"] }, { "Name": "Keywords", "Docs": "", "Typewords": ["[]", "string"] }, { "Name": "Size", "Docs": "", "Typewords": ["int64"] }, { "Name": "TrainedJunk", "Docs": "", "Typewords": ["nullable", "bool"] }, { "Name": "MsgPrefix", "Docs": "", "Typewords": ["nullable", "string"] }, { "Name": "ParsedBuf", "Docs": "", "Typewords": ["nullable", "string"] }] },
|
|||
|
"MessageEnvelope": { "Name": "MessageEnvelope", "Docs": "", "Fields": [{ "Name": "Date", "Docs": "", "Typewords": ["timestamp"] }, { "Name": "Subject", "Docs": "", "Typewords": ["string"] }, { "Name": "From", "Docs": "", "Typewords": ["[]", "MessageAddress"] }, { "Name": "Sender", "Docs": "", "Typewords": ["[]", "MessageAddress"] }, { "Name": "ReplyTo", "Docs": "", "Typewords": ["[]", "MessageAddress"] }, { "Name": "To", "Docs": "", "Typewords": ["[]", "MessageAddress"] }, { "Name": "CC", "Docs": "", "Typewords": ["[]", "MessageAddress"] }, { "Name": "BCC", "Docs": "", "Typewords": ["[]", "MessageAddress"] }, { "Name": "InReplyTo", "Docs": "", "Typewords": ["string"] }, { "Name": "MessageID", "Docs": "", "Typewords": ["string"] }] },
|
|||
|
"Attachment": { "Name": "Attachment", "Docs": "", "Fields": [{ "Name": "Path", "Docs": "", "Typewords": ["[]", "int32"] }, { "Name": "Filename", "Docs": "", "Typewords": ["string"] }, { "Name": "Part", "Docs": "", "Typewords": ["Part"] }] },
|
|||
|
"EventViewChanges": { "Name": "EventViewChanges", "Docs": "", "Fields": [{ "Name": "ViewID", "Docs": "", "Typewords": ["int64"] }, { "Name": "Changes", "Docs": "", "Typewords": ["[]", "[]", "any"] }] },
|
|||
|
"ChangeMsgAdd": { "Name": "ChangeMsgAdd", "Docs": "", "Fields": [{ "Name": "MailboxID", "Docs": "", "Typewords": ["int64"] }, { "Name": "UID", "Docs": "", "Typewords": ["UID"] }, { "Name": "ModSeq", "Docs": "", "Typewords": ["ModSeq"] }, { "Name": "Flags", "Docs": "", "Typewords": ["Flags"] }, { "Name": "Keywords", "Docs": "", "Typewords": ["[]", "string"] }, { "Name": "MessageItem", "Docs": "", "Typewords": ["MessageItem"] }] },
|
|||
|
"Flags": { "Name": "Flags", "Docs": "", "Fields": [{ "Name": "Seen", "Docs": "", "Typewords": ["bool"] }, { "Name": "Answered", "Docs": "", "Typewords": ["bool"] }, { "Name": "Flagged", "Docs": "", "Typewords": ["bool"] }, { "Name": "Forwarded", "Docs": "", "Typewords": ["bool"] }, { "Name": "Junk", "Docs": "", "Typewords": ["bool"] }, { "Name": "Notjunk", "Docs": "", "Typewords": ["bool"] }, { "Name": "Deleted", "Docs": "", "Typewords": ["bool"] }, { "Name": "Draft", "Docs": "", "Typewords": ["bool"] }, { "Name": "Phishing", "Docs": "", "Typewords": ["bool"] }, { "Name": "MDNSent", "Docs": "", "Typewords": ["bool"] }] },
|
|||
|
"ChangeMsgRemove": { "Name": "ChangeMsgRemove", "Docs": "", "Fields": [{ "Name": "MailboxID", "Docs": "", "Typewords": ["int64"] }, { "Name": "UIDs", "Docs": "", "Typewords": ["[]", "UID"] }, { "Name": "ModSeq", "Docs": "", "Typewords": ["ModSeq"] }] },
|
|||
|
"ChangeMsgFlags": { "Name": "ChangeMsgFlags", "Docs": "", "Fields": [{ "Name": "MailboxID", "Docs": "", "Typewords": ["int64"] }, { "Name": "UID", "Docs": "", "Typewords": ["UID"] }, { "Name": "ModSeq", "Docs": "", "Typewords": ["ModSeq"] }, { "Name": "Mask", "Docs": "", "Typewords": ["Flags"] }, { "Name": "Flags", "Docs": "", "Typewords": ["Flags"] }, { "Name": "Keywords", "Docs": "", "Typewords": ["[]", "string"] }] },
|
|||
|
"ChangeMailboxRemove": { "Name": "ChangeMailboxRemove", "Docs": "", "Fields": [{ "Name": "MailboxID", "Docs": "", "Typewords": ["int64"] }, { "Name": "Name", "Docs": "", "Typewords": ["string"] }] },
|
|||
|
"ChangeMailboxAdd": { "Name": "ChangeMailboxAdd", "Docs": "", "Fields": [{ "Name": "Mailbox", "Docs": "", "Typewords": ["Mailbox"] }] },
|
|||
|
"ChangeMailboxRename": { "Name": "ChangeMailboxRename", "Docs": "", "Fields": [{ "Name": "MailboxID", "Docs": "", "Typewords": ["int64"] }, { "Name": "OldName", "Docs": "", "Typewords": ["string"] }, { "Name": "NewName", "Docs": "", "Typewords": ["string"] }, { "Name": "Flags", "Docs": "", "Typewords": ["[]", "string"] }] },
|
|||
|
"ChangeMailboxCounts": { "Name": "ChangeMailboxCounts", "Docs": "", "Fields": [{ "Name": "MailboxID", "Docs": "", "Typewords": ["int64"] }, { "Name": "MailboxName", "Docs": "", "Typewords": ["string"] }, { "Name": "Total", "Docs": "", "Typewords": ["int64"] }, { "Name": "Deleted", "Docs": "", "Typewords": ["int64"] }, { "Name": "Unread", "Docs": "", "Typewords": ["int64"] }, { "Name": "Unseen", "Docs": "", "Typewords": ["int64"] }, { "Name": "Size", "Docs": "", "Typewords": ["int64"] }] },
|
|||
|
"ChangeMailboxSpecialUse": { "Name": "ChangeMailboxSpecialUse", "Docs": "", "Fields": [{ "Name": "MailboxID", "Docs": "", "Typewords": ["int64"] }, { "Name": "MailboxName", "Docs": "", "Typewords": ["string"] }, { "Name": "SpecialUse", "Docs": "", "Typewords": ["SpecialUse"] }] },
|
|||
|
"SpecialUse": { "Name": "SpecialUse", "Docs": "", "Fields": [{ "Name": "Archive", "Docs": "", "Typewords": ["bool"] }, { "Name": "Draft", "Docs": "", "Typewords": ["bool"] }, { "Name": "Junk", "Docs": "", "Typewords": ["bool"] }, { "Name": "Sent", "Docs": "", "Typewords": ["bool"] }, { "Name": "Trash", "Docs": "", "Typewords": ["bool"] }] },
|
|||
|
"ChangeMailboxKeywords": { "Name": "ChangeMailboxKeywords", "Docs": "", "Fields": [{ "Name": "MailboxID", "Docs": "", "Typewords": ["int64"] }, { "Name": "MailboxName", "Docs": "", "Typewords": ["string"] }, { "Name": "Keywords", "Docs": "", "Typewords": ["[]", "string"] }] },
|
|||
|
"UID": { "Name": "UID", "Docs": "", "Values": null },
|
|||
|
"ModSeq": { "Name": "ModSeq", "Docs": "", "Values": null },
|
|||
|
"Validation": { "Name": "Validation", "Docs": "", "Values": [{ "Name": "ValidationUnknown", "Value": 0, "Docs": "" }, { "Name": "ValidationStrict", "Value": 1, "Docs": "" }, { "Name": "ValidationDMARC", "Value": 2, "Docs": "" }, { "Name": "ValidationRelaxed", "Value": 3, "Docs": "" }, { "Name": "ValidationPass", "Value": 4, "Docs": "" }, { "Name": "ValidationNeutral", "Value": 5, "Docs": "" }, { "Name": "ValidationTemperror", "Value": 6, "Docs": "" }, { "Name": "ValidationPermerror", "Value": 7, "Docs": "" }, { "Name": "ValidationFail", "Value": 8, "Docs": "" }, { "Name": "ValidationSoftfail", "Value": 9, "Docs": "" }, { "Name": "ValidationNone", "Value": 10, "Docs": "" }] },
|
|||
|
"AttachmentType": { "Name": "AttachmentType", "Docs": "", "Values": [{ "Name": "AttachmentIndifferent", "Value": "", "Docs": "" }, { "Name": "AttachmentNone", "Value": "none", "Docs": "" }, { "Name": "AttachmentAny", "Value": "any", "Docs": "" }, { "Name": "AttachmentImage", "Value": "image", "Docs": "" }, { "Name": "AttachmentPDF", "Value": "pdf", "Docs": "" }, { "Name": "AttachmentArchive", "Value": "archive", "Docs": "" }, { "Name": "AttachmentSpreadsheet", "Value": "spreadsheet", "Docs": "" }, { "Name": "AttachmentDocument", "Value": "document", "Docs": "" }, { "Name": "AttachmentPresentation", "Value": "presentation", "Docs": "" }] },
|
|||
|
"Localpart": { "Name": "Localpart", "Docs": "", "Values": null },
|
|||
|
};
|
|||
|
api.parser = {
|
|||
|
Request: (v) => api.parse("Request", v),
|
|||
|
Query: (v) => api.parse("Query", v),
|
|||
|
Filter: (v) => api.parse("Filter", v),
|
|||
|
NotFilter: (v) => api.parse("NotFilter", v),
|
|||
|
Page: (v) => api.parse("Page", v),
|
|||
|
ParsedMessage: (v) => api.parse("ParsedMessage", v),
|
|||
|
Part: (v) => api.parse("Part", v),
|
|||
|
Envelope: (v) => api.parse("Envelope", v),
|
|||
|
Address: (v) => api.parse("Address", v),
|
|||
|
MessageAddress: (v) => api.parse("MessageAddress", v),
|
|||
|
Domain: (v) => api.parse("Domain", v),
|
|||
|
SubmitMessage: (v) => api.parse("SubmitMessage", v),
|
|||
|
File: (v) => api.parse("File", v),
|
|||
|
ForwardAttachments: (v) => api.parse("ForwardAttachments", v),
|
|||
|
Mailbox: (v) => api.parse("Mailbox", v),
|
|||
|
EventStart: (v) => api.parse("EventStart", v),
|
|||
|
DomainAddressConfig: (v) => api.parse("DomainAddressConfig", v),
|
|||
|
EventViewErr: (v) => api.parse("EventViewErr", v),
|
|||
|
EventViewReset: (v) => api.parse("EventViewReset", v),
|
|||
|
EventViewMsgs: (v) => api.parse("EventViewMsgs", v),
|
|||
|
MessageItem: (v) => api.parse("MessageItem", v),
|
|||
|
Message: (v) => api.parse("Message", v),
|
|||
|
MessageEnvelope: (v) => api.parse("MessageEnvelope", v),
|
|||
|
Attachment: (v) => api.parse("Attachment", v),
|
|||
|
EventViewChanges: (v) => api.parse("EventViewChanges", v),
|
|||
|
ChangeMsgAdd: (v) => api.parse("ChangeMsgAdd", v),
|
|||
|
Flags: (v) => api.parse("Flags", v),
|
|||
|
ChangeMsgRemove: (v) => api.parse("ChangeMsgRemove", v),
|
|||
|
ChangeMsgFlags: (v) => api.parse("ChangeMsgFlags", v),
|
|||
|
ChangeMailboxRemove: (v) => api.parse("ChangeMailboxRemove", v),
|
|||
|
ChangeMailboxAdd: (v) => api.parse("ChangeMailboxAdd", v),
|
|||
|
ChangeMailboxRename: (v) => api.parse("ChangeMailboxRename", v),
|
|||
|
ChangeMailboxCounts: (v) => api.parse("ChangeMailboxCounts", v),
|
|||
|
ChangeMailboxSpecialUse: (v) => api.parse("ChangeMailboxSpecialUse", v),
|
|||
|
SpecialUse: (v) => api.parse("SpecialUse", v),
|
|||
|
ChangeMailboxKeywords: (v) => api.parse("ChangeMailboxKeywords", v),
|
|||
|
UID: (v) => api.parse("UID", v),
|
|||
|
ModSeq: (v) => api.parse("ModSeq", v),
|
|||
|
Validation: (v) => api.parse("Validation", v),
|
|||
|
AttachmentType: (v) => api.parse("AttachmentType", v),
|
|||
|
Localpart: (v) => api.parse("Localpart", v),
|
|||
|
};
|
|||
|
let defaultOptions = { slicesNullable: true, mapsNullable: true, nullableOptional: true };
|
|||
|
class Client {
|
|||
|
constructor(baseURL = api.defaultBaseURL, options) {
|
|||
|
this.baseURL = baseURL;
|
|||
|
this.options = options;
|
|||
|
if (!options) {
|
|||
|
this.options = defaultOptions;
|
|||
|
}
|
|||
|
}
|
|||
|
withOptions(options) {
|
|||
|
return new Client(this.baseURL, { ...this.options, ...options });
|
|||
|
}
|
|||
|
// Token returns a token to use for an SSE connection. A token can only be used for
|
|||
|
// a single SSE connection. Tokens are stored in memory for a maximum of 1 minute,
|
|||
|
// with at most 10 unused tokens (the most recently created) per account.
|
|||
|
async Token() {
|
|||
|
const fn = "Token";
|
|||
|
const paramTypes = [];
|
|||
|
const returnTypes = [["string"]];
|
|||
|
const params = [];
|
|||
|
return await _sherpaCall(this.baseURL, { ...this.options }, paramTypes, returnTypes, fn, params);
|
|||
|
}
|
|||
|
// Requests sends a new request for an open SSE connection. Any currently active
|
|||
|
// request for the connection will be canceled, but this is done asynchrously, so
|
|||
|
// the SSE connection may still send results for the previous request. Callers
|
|||
|
// should take care to ignore such results. If req.Cancel is set, no new request is
|
|||
|
// started.
|
|||
|
async Request(req) {
|
|||
|
const fn = "Request";
|
|||
|
const paramTypes = [["Request"]];
|
|||
|
const returnTypes = [];
|
|||
|
const params = [req];
|
|||
|
return await _sherpaCall(this.baseURL, { ...this.options }, paramTypes, returnTypes, fn, params);
|
|||
|
}
|
|||
|
// ParsedMessage returns enough to render the textual body of a message. It is
|
|||
|
// assumed the client already has other fields through MessageItem.
|
|||
|
async ParsedMessage(msgID) {
|
|||
|
const fn = "ParsedMessage";
|
|||
|
const paramTypes = [["int64"]];
|
|||
|
const returnTypes = [["ParsedMessage"]];
|
|||
|
const params = [msgID];
|
|||
|
return await _sherpaCall(this.baseURL, { ...this.options }, paramTypes, returnTypes, fn, params);
|
|||
|
}
|
|||
|
// MessageSubmit sends a message by submitting it the outgoing email queue. The
|
|||
|
// message is sent to all addresses listed in the To, Cc and Bcc addresses, without
|
|||
|
// Bcc message header.
|
|||
|
//
|
|||
|
// If a Sent mailbox is configured, messages are added to it after submitting
|
|||
|
// to the delivery queue.
|
|||
|
async MessageSubmit(m) {
|
|||
|
const fn = "MessageSubmit";
|
|||
|
const paramTypes = [["SubmitMessage"]];
|
|||
|
const returnTypes = [];
|
|||
|
const params = [m];
|
|||
|
return await _sherpaCall(this.baseURL, { ...this.options }, paramTypes, returnTypes, fn, params);
|
|||
|
}
|
|||
|
// MessageMove moves messages to another mailbox. If the message is already in
|
|||
|
// the mailbox an error is returned.
|
|||
|
async MessageMove(messageIDs, mailboxID) {
|
|||
|
const fn = "MessageMove";
|
|||
|
const paramTypes = [["[]", "int64"], ["int64"]];
|
|||
|
const returnTypes = [];
|
|||
|
const params = [messageIDs, mailboxID];
|
|||
|
return await _sherpaCall(this.baseURL, { ...this.options }, paramTypes, returnTypes, fn, params);
|
|||
|
}
|
|||
|
// MessageDelete permanently deletes messages, without moving them to the Trash mailbox.
|
|||
|
async MessageDelete(messageIDs) {
|
|||
|
const fn = "MessageDelete";
|
|||
|
const paramTypes = [["[]", "int64"]];
|
|||
|
const returnTypes = [];
|
|||
|
const params = [messageIDs];
|
|||
|
return await _sherpaCall(this.baseURL, { ...this.options }, paramTypes, returnTypes, fn, params);
|
|||
|
}
|
|||
|
// FlagsAdd adds flags, either system flags like \Seen or custom keywords. The
|
|||
|
// flags should be lower-case, but will be converted and verified.
|
|||
|
async FlagsAdd(messageIDs, flaglist) {
|
|||
|
const fn = "FlagsAdd";
|
|||
|
const paramTypes = [["[]", "int64"], ["[]", "string"]];
|
|||
|
const returnTypes = [];
|
|||
|
const params = [messageIDs, flaglist];
|
|||
|
return await _sherpaCall(this.baseURL, { ...this.options }, paramTypes, returnTypes, fn, params);
|
|||
|
}
|
|||
|
// FlagsClear clears flags, either system flags like \Seen or custom keywords.
|
|||
|
async FlagsClear(messageIDs, flaglist) {
|
|||
|
const fn = "FlagsClear";
|
|||
|
const paramTypes = [["[]", "int64"], ["[]", "string"]];
|
|||
|
const returnTypes = [];
|
|||
|
const params = [messageIDs, flaglist];
|
|||
|
return await _sherpaCall(this.baseURL, { ...this.options }, paramTypes, returnTypes, fn, params);
|
|||
|
}
|
|||
|
// MailboxCreate creates a new mailbox.
|
|||
|
async MailboxCreate(name) {
|
|||
|
const fn = "MailboxCreate";
|
|||
|
const paramTypes = [["string"]];
|
|||
|
const returnTypes = [];
|
|||
|
const params = [name];
|
|||
|
return await _sherpaCall(this.baseURL, { ...this.options }, paramTypes, returnTypes, fn, params);
|
|||
|
}
|
|||
|
// MailboxDelete deletes a mailbox and all its messages.
|
|||
|
async MailboxDelete(mailboxID) {
|
|||
|
const fn = "MailboxDelete";
|
|||
|
const paramTypes = [["int64"]];
|
|||
|
const returnTypes = [];
|
|||
|
const params = [mailboxID];
|
|||
|
return await _sherpaCall(this.baseURL, { ...this.options }, paramTypes, returnTypes, fn, params);
|
|||
|
}
|
|||
|
// MailboxEmpty empties a mailbox, removing all messages from the mailbox, but not
|
|||
|
// its child mailboxes.
|
|||
|
async MailboxEmpty(mailboxID) {
|
|||
|
const fn = "MailboxEmpty";
|
|||
|
const paramTypes = [["int64"]];
|
|||
|
const returnTypes = [];
|
|||
|
const params = [mailboxID];
|
|||
|
return await _sherpaCall(this.baseURL, { ...this.options }, paramTypes, returnTypes, fn, params);
|
|||
|
}
|
|||
|
// MailboxRename renames a mailbox, possibly moving it to a new parent. The mailbox
|
|||
|
// ID and its messages are unchanged.
|
|||
|
async MailboxRename(mailboxID, newName) {
|
|||
|
const fn = "MailboxRename";
|
|||
|
const paramTypes = [["int64"], ["string"]];
|
|||
|
const returnTypes = [];
|
|||
|
const params = [mailboxID, newName];
|
|||
|
return await _sherpaCall(this.baseURL, { ...this.options }, paramTypes, returnTypes, fn, params);
|
|||
|
}
|
|||
|
// CompleteRecipient returns autocomplete matches for a recipient, returning the
|
|||
|
// matches, most recently used first, and whether this is the full list and further
|
|||
|
// requests for longer prefixes aren't necessary.
|
|||
|
async CompleteRecipient(search) {
|
|||
|
const fn = "CompleteRecipient";
|
|||
|
const paramTypes = [["string"]];
|
|||
|
const returnTypes = [["[]", "string"], ["bool"]];
|
|||
|
const params = [search];
|
|||
|
return await _sherpaCall(this.baseURL, { ...this.options }, paramTypes, returnTypes, fn, params);
|
|||
|
}
|
|||
|
// MailboxSetSpecialUse sets the special use flags of a mailbox.
|
|||
|
async MailboxSetSpecialUse(mb) {
|
|||
|
const fn = "MailboxSetSpecialUse";
|
|||
|
const paramTypes = [["Mailbox"]];
|
|||
|
const returnTypes = [];
|
|||
|
const params = [mb];
|
|||
|
return await _sherpaCall(this.baseURL, { ...this.options }, paramTypes, returnTypes, fn, params);
|
|||
|
}
|
|||
|
// SSETypes exists to ensure the generated API contains the types, for use in SSE events.
|
|||
|
async SSETypes() {
|
|||
|
const fn = "SSETypes";
|
|||
|
const paramTypes = [];
|
|||
|
const returnTypes = [["EventStart"], ["EventViewErr"], ["EventViewReset"], ["EventViewMsgs"], ["EventViewChanges"], ["ChangeMsgAdd"], ["ChangeMsgRemove"], ["ChangeMsgFlags"], ["ChangeMailboxRemove"], ["ChangeMailboxAdd"], ["ChangeMailboxRename"], ["ChangeMailboxCounts"], ["ChangeMailboxSpecialUse"], ["ChangeMailboxKeywords"], ["Flags"]];
|
|||
|
const params = [];
|
|||
|
return await _sherpaCall(this.baseURL, { ...this.options }, paramTypes, returnTypes, fn, params);
|
|||
|
}
|
|||
|
}
|
|||
|
api.Client = Client;
|
|||
|
api.defaultBaseURL = (function () {
|
|||
|
let p = location.pathname;
|
|||
|
if (p && p[p.length - 1] !== '/') {
|
|||
|
let l = location.pathname.split('/');
|
|||
|
l = l.slice(0, l.length - 1);
|
|||
|
p = '/' + l.join('/') + '/';
|
|||
|
}
|
|||
|
return location.protocol + '//' + location.host + p + 'api/';
|
|||
|
})();
|
|||
|
// NOTE: code below is shared between github.com/mjl-/sherpaweb and github.com/mjl-/sherpats.
|
|||
|
// KEEP IN SYNC.
|
|||
|
api.supportedSherpaVersion = 1;
|
|||
|
// verifyArg typechecks "v" against "typewords", returning a new (possibly modified) value for JSON-encoding.
|
|||
|
// toJS indicate if the data is coming into JS. If so, timestamps are turned into JS Dates. Otherwise, JS Dates are turned into strings.
|
|||
|
// allowUnknownKeys configures whether unknown keys in structs are allowed.
|
|||
|
// types are the named types of the API.
|
|||
|
api.verifyArg = (path, v, typewords, toJS, allowUnknownKeys, types, opts) => {
|
|||
|
return new verifier(types, toJS, allowUnknownKeys, opts).verify(path, v, typewords);
|
|||
|
};
|
|||
|
api.parse = (name, v) => api.verifyArg(name, v, [name], true, false, api.types, defaultOptions);
|
|||
|
class verifier {
|
|||
|
constructor(types, toJS, allowUnknownKeys, opts) {
|
|||
|
this.types = types;
|
|||
|
this.toJS = toJS;
|
|||
|
this.allowUnknownKeys = allowUnknownKeys;
|
|||
|
this.opts = opts;
|
|||
|
}
|
|||
|
verify(path, v, typewords) {
|
|||
|
typewords = typewords.slice(0);
|
|||
|
const ww = typewords.shift();
|
|||
|
const error = (msg) => {
|
|||
|
if (path != '') {
|
|||
|
msg = path + ': ' + msg;
|
|||
|
}
|
|||
|
throw new Error(msg);
|
|||
|
};
|
|||
|
if (typeof ww !== 'string') {
|
|||
|
error('bad typewords');
|
|||
|
return; // should not be necessary, typescript doesn't see error always throws an exception?
|
|||
|
}
|
|||
|
const w = ww;
|
|||
|
const ensure = (ok, expect) => {
|
|||
|
if (!ok) {
|
|||
|
error('got ' + JSON.stringify(v) + ', expected ' + expect);
|
|||
|
}
|
|||
|
return v;
|
|||
|
};
|
|||
|
switch (w) {
|
|||
|
case 'nullable':
|
|||
|
if (v === null || v === undefined && this.opts.nullableOptional) {
|
|||
|
return v;
|
|||
|
}
|
|||
|
return this.verify(path, v, typewords);
|
|||
|
case '[]':
|
|||
|
if (v === null && this.opts.slicesNullable || v === undefined && this.opts.slicesNullable && this.opts.nullableOptional) {
|
|||
|
return v;
|
|||
|
}
|
|||
|
ensure(Array.isArray(v), "array");
|
|||
|
return v.map((e, i) => this.verify(path + '[' + i + ']', e, typewords));
|
|||
|
case '{}':
|
|||
|
if (v === null && this.opts.mapsNullable || v === undefined && this.opts.mapsNullable && this.opts.nullableOptional) {
|
|||
|
return v;
|
|||
|
}
|
|||
|
ensure(v !== null || typeof v === 'object', "object");
|
|||
|
const r = {};
|
|||
|
for (const k in v) {
|
|||
|
r[k] = this.verify(path + '.' + k, v[k], typewords);
|
|||
|
}
|
|||
|
return r;
|
|||
|
}
|
|||
|
ensure(typewords.length == 0, "empty typewords");
|
|||
|
const t = typeof v;
|
|||
|
switch (w) {
|
|||
|
case 'any':
|
|||
|
return v;
|
|||
|
case 'bool':
|
|||
|
ensure(t === 'boolean', 'bool');
|
|||
|
return v;
|
|||
|
case 'int8':
|
|||
|
case 'uint8':
|
|||
|
case 'int16':
|
|||
|
case 'uint16':
|
|||
|
case 'int32':
|
|||
|
case 'uint32':
|
|||
|
case 'int64':
|
|||
|
case 'uint64':
|
|||
|
ensure(t === 'number' && Number.isInteger(v), 'integer');
|
|||
|
return v;
|
|||
|
case 'float32':
|
|||
|
case 'float64':
|
|||
|
ensure(t === 'number', 'float');
|
|||
|
return v;
|
|||
|
case 'int64s':
|
|||
|
case 'uint64s':
|
|||
|
ensure(t === 'number' && Number.isInteger(v) || t === 'string', 'integer fitting in float without precision loss, or string');
|
|||
|
return '' + v;
|
|||
|
case 'string':
|
|||
|
ensure(t === 'string', 'string');
|
|||
|
return v;
|
|||
|
case 'timestamp':
|
|||
|
if (this.toJS) {
|
|||
|
ensure(t === 'string', 'string, with timestamp');
|
|||
|
const d = new Date(v);
|
|||
|
if (d instanceof Date && !isNaN(d.getTime())) {
|
|||
|
return d;
|
|||
|
}
|
|||
|
error('invalid date ' + v);
|
|||
|
}
|
|||
|
else {
|
|||
|
ensure(t === 'object' && v !== null, 'non-null object');
|
|||
|
ensure(v.__proto__ === Date.prototype, 'Date');
|
|||
|
return v.toISOString();
|
|||
|
}
|
|||
|
}
|
|||
|
// We're left with named types.
|
|||
|
const nt = this.types[w];
|
|||
|
if (!nt) {
|
|||
|
error('unknown type ' + w);
|
|||
|
}
|
|||
|
if (v === null) {
|
|||
|
error('bad value ' + v + ' for named type ' + w);
|
|||
|
}
|
|||
|
if (api.structTypes[nt.Name]) {
|
|||
|
const t = nt;
|
|||
|
if (typeof v !== 'object') {
|
|||
|
error('bad value ' + v + ' for struct ' + w);
|
|||
|
}
|
|||
|
const r = {};
|
|||
|
for (const f of t.Fields) {
|
|||
|
r[f.Name] = this.verify(path + '.' + f.Name, v[f.Name], f.Typewords);
|
|||
|
}
|
|||
|
// If going to JSON also verify no unknown fields are present.
|
|||
|
if (!this.allowUnknownKeys) {
|
|||
|
const known = {};
|
|||
|
for (const f of t.Fields) {
|
|||
|
known[f.Name] = true;
|
|||
|
}
|
|||
|
Object.keys(v).forEach((k) => {
|
|||
|
if (!known[k]) {
|
|||
|
error('unknown key ' + k + ' for struct ' + w);
|
|||
|
}
|
|||
|
});
|
|||
|
}
|
|||
|
return r;
|
|||
|
}
|
|||
|
else if (api.stringsTypes[nt.Name]) {
|
|||
|
const t = nt;
|
|||
|
if (typeof v !== 'string') {
|
|||
|
error('mistyped value ' + v + ' for named strings ' + t.Name);
|
|||
|
}
|
|||
|
if (!t.Values || t.Values.length === 0) {
|
|||
|
return v;
|
|||
|
}
|
|||
|
for (const sv of t.Values) {
|
|||
|
if (sv.Value === v) {
|
|||
|
return v;
|
|||
|
}
|
|||
|
}
|
|||
|
error('unknkown value ' + v + ' for named strings ' + t.Name);
|
|||
|
}
|
|||
|
else if (api.intsTypes[nt.Name]) {
|
|||
|
const t = nt;
|
|||
|
if (typeof v !== 'number' || !Number.isInteger(v)) {
|
|||
|
error('mistyped value ' + v + ' for named ints ' + t.Name);
|
|||
|
}
|
|||
|
if (!t.Values || t.Values.length === 0) {
|
|||
|
return v;
|
|||
|
}
|
|||
|
for (const sv of t.Values) {
|
|||
|
if (sv.Value === v) {
|
|||
|
return v;
|
|||
|
}
|
|||
|
}
|
|||
|
error('unknkown value ' + v + ' for named ints ' + t.Name);
|
|||
|
}
|
|||
|
else {
|
|||
|
throw new Error('unexpected named type ' + nt);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
const _sherpaCall = async (baseURL, options, paramTypes, returnTypes, name, params) => {
|
|||
|
if (!options.skipParamCheck) {
|
|||
|
if (params.length !== paramTypes.length) {
|
|||
|
return Promise.reject({ message: 'wrong number of parameters in sherpa call, saw ' + params.length + ' != expected ' + paramTypes.length });
|
|||
|
}
|
|||
|
params = params.map((v, index) => api.verifyArg('params[' + index + ']', v, paramTypes[index], false, false, api.types, options));
|
|||
|
}
|
|||
|
const simulate = async (json) => {
|
|||
|
const config = JSON.parse(json || 'null') || {};
|
|||
|
const waitMinMsec = config.waitMinMsec || 0;
|
|||
|
const waitMaxMsec = config.waitMaxMsec || 0;
|
|||
|
const wait = Math.random() * (waitMaxMsec - waitMinMsec);
|
|||
|
const failRate = config.failRate || 0;
|
|||
|
return new Promise((resolve, reject) => {
|
|||
|
if (options.aborter) {
|
|||
|
options.aborter.abort = () => {
|
|||
|
reject({ message: 'call to ' + name + ' aborted by user', code: 'sherpa:aborted' });
|
|||
|
reject = resolve = () => { };
|
|||
|
};
|
|||
|
}
|
|||
|
setTimeout(() => {
|
|||
|
const r = Math.random();
|
|||
|
if (r < failRate) {
|
|||
|
reject({ message: 'injected failure on ' + name, code: 'server:injected' });
|
|||
|
}
|
|||
|
else {
|
|||
|
resolve();
|
|||
|
}
|
|||
|
reject = resolve = () => { };
|
|||
|
}, waitMinMsec + wait);
|
|||
|
});
|
|||
|
};
|
|||
|
// Only simulate when there is a debug string. Otherwise it would always interfere
|
|||
|
// with setting options.aborter.
|
|||
|
let json = '';
|
|||
|
try {
|
|||
|
json = window.localStorage.getItem('sherpats-debug') || '';
|
|||
|
}
|
|||
|
catch (err) { }
|
|||
|
if (json) {
|
|||
|
await simulate(json);
|
|||
|
}
|
|||
|
// Immediately create promise, so options.aborter is changed before returning.
|
|||
|
const promise = new Promise((resolve, reject) => {
|
|||
|
let resolve1 = (v) => {
|
|||
|
resolve(v);
|
|||
|
resolve1 = () => { };
|
|||
|
reject1 = () => { };
|
|||
|
};
|
|||
|
let reject1 = (v) => {
|
|||
|
reject(v);
|
|||
|
resolve1 = () => { };
|
|||
|
reject1 = () => { };
|
|||
|
};
|
|||
|
const url = baseURL + name;
|
|||
|
const req = new window.XMLHttpRequest();
|
|||
|
if (options.aborter) {
|
|||
|
options.aborter.abort = () => {
|
|||
|
req.abort();
|
|||
|
reject1({ code: 'sherpa:aborted', message: 'request aborted' });
|
|||
|
};
|
|||
|
}
|
|||
|
req.open('POST', url, true);
|
|||
|
if (options.timeoutMsec) {
|
|||
|
req.timeout = options.timeoutMsec;
|
|||
|
}
|
|||
|
req.onload = () => {
|
|||
|
if (req.status !== 200) {
|
|||
|
if (req.status === 404) {
|
|||
|
reject1({ code: 'sherpa:badFunction', message: 'function does not exist' });
|
|||
|
}
|
|||
|
else {
|
|||
|
reject1({ code: 'sherpa:http', message: 'error calling function, HTTP status: ' + req.status });
|
|||
|
}
|
|||
|
return;
|
|||
|
}
|
|||
|
let resp;
|
|||
|
try {
|
|||
|
resp = JSON.parse(req.responseText);
|
|||
|
}
|
|||
|
catch (err) {
|
|||
|
reject1({ code: 'sherpa:badResponse', message: 'bad JSON from server' });
|
|||
|
return;
|
|||
|
}
|
|||
|
if (resp && resp.error) {
|
|||
|
const err = resp.error;
|
|||
|
reject1({ code: err.code, message: err.message });
|
|||
|
return;
|
|||
|
}
|
|||
|
else if (!resp || !resp.hasOwnProperty('result')) {
|
|||
|
reject1({ code: 'sherpa:badResponse', message: "invalid sherpa response object, missing 'result'" });
|
|||
|
return;
|
|||
|
}
|
|||
|
if (options.skipReturnCheck) {
|
|||
|
resolve1(resp.result);
|
|||
|
return;
|
|||
|
}
|
|||
|
let result = resp.result;
|
|||
|
try {
|
|||
|
if (returnTypes.length === 0) {
|
|||
|
if (result) {
|
|||
|
throw new Error('function ' + name + ' returned a value while prototype says it returns "void"');
|
|||
|
}
|
|||
|
}
|
|||
|
else if (returnTypes.length === 1) {
|
|||
|
result = api.verifyArg('result', result, returnTypes[0], true, true, api.types, options);
|
|||
|
}
|
|||
|
else {
|
|||
|
if (result.length != returnTypes.length) {
|
|||
|
throw new Error('wrong number of values returned by ' + name + ', saw ' + result.length + ' != expected ' + returnTypes.length);
|
|||
|
}
|
|||
|
result = result.map((v, index) => api.verifyArg('result[' + index + ']', v, returnTypes[index], true, true, api.types, options));
|
|||
|
}
|
|||
|
}
|
|||
|
catch (err) {
|
|||
|
let errmsg = 'bad types';
|
|||
|
if (err instanceof Error) {
|
|||
|
errmsg = err.message;
|
|||
|
}
|
|||
|
reject1({ code: 'sherpa:badTypes', message: errmsg });
|
|||
|
}
|
|||
|
resolve1(result);
|
|||
|
};
|
|||
|
req.onerror = () => {
|
|||
|
reject1({ code: 'sherpa:connection', message: 'connection failed' });
|
|||
|
};
|
|||
|
req.ontimeout = () => {
|
|||
|
reject1({ code: 'sherpa:timeout', message: 'request timeout' });
|
|||
|
};
|
|||
|
req.setRequestHeader('Content-Type', 'application/json');
|
|||
|
try {
|
|||
|
req.send(JSON.stringify({ params: params }));
|
|||
|
}
|
|||
|
catch (err) {
|
|||
|
reject1({ code: 'sherpa:badData', message: 'cannot marshal to JSON' });
|
|||
|
}
|
|||
|
});
|
|||
|
return await promise;
|
|||
|
};
|
|||
|
})(api || (api = {}));
|
|||
|
// Javascript is generated from typescript, do not modify generated javascript because changes will be overwritten.
|
|||
|
const [dom, style, attr, prop] = (function () {
|
|||
|
// Start of unicode block (rough approximation of script), from https://www.unicode.org/Public/UNIDATA/Blocks.txt
|
|||
|
const scriptblocks = [0x0000, 0x0080, 0x0100, 0x0180, 0x0250, 0x02B0, 0x0300, 0x0370, 0x0400, 0x0500, 0x0530, 0x0590, 0x0600, 0x0700, 0x0750, 0x0780, 0x07C0, 0x0800, 0x0840, 0x0860, 0x0870, 0x08A0, 0x0900, 0x0980, 0x0A00, 0x0A80, 0x0B00, 0x0B80, 0x0C00, 0x0C80, 0x0D00, 0x0D80, 0x0E00, 0x0E80, 0x0F00, 0x1000, 0x10A0, 0x1100, 0x1200, 0x1380, 0x13A0, 0x1400, 0x1680, 0x16A0, 0x1700, 0x1720, 0x1740, 0x1760, 0x1780, 0x1800, 0x18B0, 0x1900, 0x1950, 0x1980, 0x19E0, 0x1A00, 0x1A20, 0x1AB0, 0x1B00, 0x1B80, 0x1BC0, 0x1C00, 0x1C50, 0x1C80, 0x1C90, 0x1CC0, 0x1CD0, 0x1D00, 0x1D80, 0x1DC0, 0x1E00, 0x1F00, 0x2000, 0x2070, 0x20A0, 0x20D0, 0x2100, 0x2150, 0x2190, 0x2200, 0x2300, 0x2400, 0x2440, 0x2460, 0x2500, 0x2580, 0x25A0, 0x2600, 0x2700, 0x27C0, 0x27F0, 0x2800, 0x2900, 0x2980, 0x2A00, 0x2B00, 0x2C00, 0x2C60, 0x2C80, 0x2D00, 0x2D30, 0x2D80, 0x2DE0, 0x2E00, 0x2E80, 0x2F00, 0x2FF0, 0x3000, 0x3040, 0x30A0, 0x3100, 0x3130, 0x3190, 0x31A0, 0x31C0, 0x31F0, 0x3200, 0x3300, 0x3400, 0x4DC0, 0x4E00, 0xA000, 0xA490, 0xA4D0, 0xA500, 0xA640, 0xA6A0, 0xA700, 0xA720, 0xA800, 0xA830, 0xA840, 0xA880, 0xA8E0, 0xA900, 0xA930, 0xA960, 0xA980, 0xA9E0, 0xAA00, 0xAA60, 0xAA80, 0xAAE0, 0xAB00, 0xAB30, 0xAB70, 0xABC0, 0xAC00, 0xD7B0, 0xD800, 0xDB80, 0xDC00, 0xE000, 0xF900, 0xFB00, 0xFB50, 0xFE00, 0xFE10, 0xFE20, 0xFE30, 0xFE50, 0xFE70, 0xFF00, 0xFFF0, 0x10000, 0x10080, 0x10100, 0x10140, 0x10190, 0x101D0, 0x10280, 0x102A0, 0x102E0, 0x10300, 0x10330, 0x10350, 0x10380, 0x103A0, 0x10400, 0x10450, 0x10480, 0x104B0, 0x10500, 0x10530, 0x10570, 0x10600, 0x10780, 0x10800, 0x10840, 0x10860, 0x10880, 0x108E0, 0x10900, 0x10920, 0x10980, 0x109A0, 0x10A00, 0x10A60, 0x10A80, 0x10AC0, 0x10B00, 0x10B40, 0x10B60, 0x10B80, 0x10C00, 0x10C80, 0x10D00, 0x10E60, 0x10E80, 0x10EC0, 0x10F00, 0x10F30, 0x10F70, 0x10FB0, 0x10FE0, 0x11000, 0x11080, 0x110D0, 0x11100, 0x11150, 0x11180, 0x111E0, 0x11200, 0x11280, 0x112B0, 0x11300, 0x11400, 0x11480, 0x11580, 0x11600, 0x11660, 0x11680, 0x11700, 0x11800, 0x118A0, 0x11900, 0x119A0, 0x11A00, 0x11A50, 0x11AB0, 0x11AC0, 0x11B00, 0x11C00, 0x11C70, 0x11D00, 0x11D60, 0x11EE0, 0x11F00, 0x11FB0, 0x11FC0, 0x12000, 0x12400, 0x12480, 0x12F90, 0x13000, 0x13430, 0x14400, 0x16800, 0x16A40, 0x16A70, 0x16AD0, 0x16B00, 0x16E40, 0x16F00, 0x16FE0, 0x17000, 0x18800, 0x18B00, 0x18D00, 0x1AFF0, 0x1B000, 0x1B100, 0x1B130, 0x1B170, 0x1BC00, 0x1BCA0, 0x1CF00, 0x1D000, 0x1D100, 0x1D200, 0x1D2C0, 0x1D2E0, 0x1D300, 0x1D360, 0x1D400, 0x1D800, 0x1DF00, 0x1E000, 0x1E030, 0x1E100, 0x1E290, 0x1E2C0, 0x1E4D0, 0x1E7E0, 0x1E800, 0x1E900, 0x1EC70, 0x1ED00, 0x1EE00, 0x1F000, 0x1F030, 0x1F0A0, 0x1F100, 0x1F200, 0x1F300, 0x1F600, 0x1F650, 0x1F680, 0x1F700, 0x1F780, 0x1F800, 0x1F900, 0x1FA00, 0x1FA70, 0x1FB00, 0x20000, 0x2A700, 0x2B740, 0x2B820, 0x2CEB0, 0x2F800, 0x30000, 0x31350, 0xE0000, 0xE0100, 0xF0000, 0x100000];
|
|||
|
// Find block code belongs in.
|
|||
|
const findBlock = (code) => {
|
|||
|
let s = 0;
|
|||
|
let e = scriptblocks.length;
|
|||
|
while (s < e - 1) {
|
|||
|
let i = Math.floor((s + e) / 2);
|
|||
|
if (code < scriptblocks[i]) {
|
|||
|
e = i;
|
|||
|
}
|
|||
|
else {
|
|||
|
s = i;
|
|||
|
}
|
|||
|
}
|
|||
|
return s;
|
|||
|
};
|
|||
|
// formatText adds s to element e, in a way that makes switching unicode scripts
|
|||
|
// clear, with alternating DOM TextNode and span elements with a "switchscript"
|
|||
|
// class. Useful for highlighting look alikes, e.g. a (ascii 0x61) and а (cyrillic
|
|||
|
// 0x430).
|
|||
|
//
|
|||
|
// This is only called one string at a time, so the UI can still display strings
|
|||
|
// without highlighting switching scripts, by calling formatText on the parts.
|
|||
|
const formatText = (e, s) => {
|
|||
|
// Handle some common cases quickly.
|
|||
|
if (!s) {
|
|||
|
return;
|
|||
|
}
|
|||
|
let ascii = true;
|
|||
|
for (const c of s) {
|
|||
|
const cp = c.codePointAt(0); // For typescript, to check for undefined.
|
|||
|
if (cp !== undefined && cp >= 0x0080) {
|
|||
|
ascii = false;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
if (ascii) {
|
|||
|
e.appendChild(document.createTextNode(s));
|
|||
|
return;
|
|||
|
}
|
|||
|
// todo: handle grapheme clusters? wait for Intl.Segmenter?
|
|||
|
let n = 0; // Number of text/span parts added.
|
|||
|
let str = ''; // Collected so far.
|
|||
|
let block = -1; // Previous block/script.
|
|||
|
let mod = 1;
|
|||
|
const put = (nextblock) => {
|
|||
|
if (n === 0 && nextblock === 0) {
|
|||
|
// Start was non-ascii, second block is ascii, we'll start marked as switched.
|
|||
|
mod = 0;
|
|||
|
}
|
|||
|
if (n % 2 === mod) {
|
|||
|
const x = document.createElement('span');
|
|||
|
x.classList.add('scriptswitch');
|
|||
|
x.appendChild(document.createTextNode(str));
|
|||
|
e.appendChild(x);
|
|||
|
}
|
|||
|
else {
|
|||
|
e.appendChild(document.createTextNode(str));
|
|||
|
}
|
|||
|
n++;
|
|||
|
str = '';
|
|||
|
};
|
|||
|
for (const c of s) {
|
|||
|
// Basic whitespace does not switch blocks. Will probably need to extend with more
|
|||
|
// punctuation in the future. Possibly for digits too. But perhaps not in all
|
|||
|
// scripts.
|
|||
|
if (c === ' ' || c === '\t' || c === '\r' || c === '\n') {
|
|||
|
str += c;
|
|||
|
continue;
|
|||
|
}
|
|||
|
const code = c.codePointAt(0);
|
|||
|
if (block < 0 || !(code >= scriptblocks[block] && (code < scriptblocks[block + 1] || block === scriptblocks.length - 1))) {
|
|||
|
const nextblock = code < 0x0080 ? 0 : findBlock(code);
|
|||
|
if (block >= 0) {
|
|||
|
put(nextblock);
|
|||
|
}
|
|||
|
block = nextblock;
|
|||
|
}
|
|||
|
str += c;
|
|||
|
}
|
|||
|
put(-1);
|
|||
|
};
|
|||
|
const _domKids = (e, l) => {
|
|||
|
l.forEach((c) => {
|
|||
|
const xc = c;
|
|||
|
if (typeof c === 'string') {
|
|||
|
formatText(e, c);
|
|||
|
}
|
|||
|
else if (c instanceof Element) {
|
|||
|
e.appendChild(c);
|
|||
|
}
|
|||
|
else if (c instanceof Function) {
|
|||
|
if (!c.name) {
|
|||
|
throw new Error('function without name');
|
|||
|
}
|
|||
|
e.addEventListener(c.name, c);
|
|||
|
}
|
|||
|
else if (Array.isArray(xc)) {
|
|||
|
_domKids(e, c);
|
|||
|
}
|
|||
|
else if (xc._class) {
|
|||
|
for (const s of xc._class) {
|
|||
|
e.classList.toggle(s, true);
|
|||
|
}
|
|||
|
}
|
|||
|
else if (xc._attrs) {
|
|||
|
for (const k in xc._attrs) {
|
|||
|
e.setAttribute(k, xc._attrs[k]);
|
|||
|
}
|
|||
|
}
|
|||
|
else if (xc._styles) {
|
|||
|
for (const k in xc._styles) {
|
|||
|
const estyle = e.style;
|
|||
|
estyle[k] = xc._styles[k];
|
|||
|
}
|
|||
|
}
|
|||
|
else if (xc._props) {
|
|||
|
for (const k in xc._props) {
|
|||
|
const eprops = e;
|
|||
|
eprops[k] = xc._props[k];
|
|||
|
}
|
|||
|
}
|
|||
|
else if (xc.root) {
|
|||
|
e.appendChild(xc.root);
|
|||
|
}
|
|||
|
else {
|
|||
|
console.log('bad kid', c);
|
|||
|
throw new Error('bad kid');
|
|||
|
}
|
|||
|
});
|
|||
|
return e;
|
|||
|
};
|
|||
|
const dom = {
|
|||
|
_kids: function (e, ...kl) {
|
|||
|
while (e.firstChild) {
|
|||
|
e.removeChild(e.firstChild);
|
|||
|
}
|
|||
|
_domKids(e, kl);
|
|||
|
},
|
|||
|
_attrs: (x) => { return { _attrs: x }; },
|
|||
|
_class: (...x) => { return { _class: x }; },
|
|||
|
// The createElement calls are spelled out so typescript can derive function
|
|||
|
// signatures with a specific HTML*Element return type.
|
|||
|
div: (...l) => _domKids(document.createElement('div'), l),
|
|||
|
span: (...l) => _domKids(document.createElement('span'), l),
|
|||
|
a: (...l) => _domKids(document.createElement('a'), l),
|
|||
|
input: (...l) => _domKids(document.createElement('input'), l),
|
|||
|
textarea: (...l) => _domKids(document.createElement('textarea'), l),
|
|||
|
select: (...l) => _domKids(document.createElement('select'), l),
|
|||
|
option: (...l) => _domKids(document.createElement('option'), l),
|
|||
|
clickbutton: (...l) => _domKids(document.createElement('button'), [attr.type('button'), ...l]),
|
|||
|
submitbutton: (...l) => _domKids(document.createElement('button'), [attr.type('submit'), ...l]),
|
|||
|
form: (...l) => _domKids(document.createElement('form'), l),
|
|||
|
fieldset: (...l) => _domKids(document.createElement('fieldset'), l),
|
|||
|
table: (...l) => _domKids(document.createElement('table'), l),
|
|||
|
thead: (...l) => _domKids(document.createElement('thead'), l),
|
|||
|
tbody: (...l) => _domKids(document.createElement('tbody'), l),
|
|||
|
tr: (...l) => _domKids(document.createElement('tr'), l),
|
|||
|
td: (...l) => _domKids(document.createElement('td'), l),
|
|||
|
th: (...l) => _domKids(document.createElement('th'), l),
|
|||
|
datalist: (...l) => _domKids(document.createElement('datalist'), l),
|
|||
|
h1: (...l) => _domKids(document.createElement('h1'), l),
|
|||
|
h2: (...l) => _domKids(document.createElement('h2'), l),
|
|||
|
br: (...l) => _domKids(document.createElement('br'), l),
|
|||
|
hr: (...l) => _domKids(document.createElement('hr'), l),
|
|||
|
pre: (...l) => _domKids(document.createElement('pre'), l),
|
|||
|
label: (...l) => _domKids(document.createElement('label'), l),
|
|||
|
ul: (...l) => _domKids(document.createElement('ul'), l),
|
|||
|
li: (...l) => _domKids(document.createElement('li'), l),
|
|||
|
iframe: (...l) => _domKids(document.createElement('iframe'), l),
|
|||
|
b: (...l) => _domKids(document.createElement('b'), l),
|
|||
|
img: (...l) => _domKids(document.createElement('img'), l),
|
|||
|
style: (...l) => _domKids(document.createElement('style'), l),
|
|||
|
search: (...l) => _domKids(document.createElement('search'), l),
|
|||
|
};
|
|||
|
const _attr = (k, v) => { const o = {}; o[k] = v; return { _attrs: o }; };
|
|||
|
const attr = {
|
|||
|
title: (s) => _attr('title', s),
|
|||
|
value: (s) => _attr('value', s),
|
|||
|
type: (s) => _attr('type', s),
|
|||
|
tabindex: (s) => _attr('tabindex', s),
|
|||
|
src: (s) => _attr('src', s),
|
|||
|
placeholder: (s) => _attr('placeholder', s),
|
|||
|
href: (s) => _attr('href', s),
|
|||
|
checked: (s) => _attr('checked', s),
|
|||
|
selected: (s) => _attr('selected', s),
|
|||
|
id: (s) => _attr('id', s),
|
|||
|
datalist: (s) => _attr('datalist', s),
|
|||
|
rows: (s) => _attr('rows', s),
|
|||
|
target: (s) => _attr('target', s),
|
|||
|
rel: (s) => _attr('rel', s),
|
|||
|
required: (s) => _attr('required', s),
|
|||
|
multiple: (s) => _attr('multiple', s),
|
|||
|
download: (s) => _attr('download', s),
|
|||
|
disabled: (s) => _attr('disabled', s),
|
|||
|
draggable: (s) => _attr('draggable', s),
|
|||
|
rowspan: (s) => _attr('rowspan', s),
|
|||
|
colspan: (s) => _attr('colspan', s),
|
|||
|
for: (s) => _attr('for', s),
|
|||
|
role: (s) => _attr('role', s),
|
|||
|
arialabel: (s) => _attr('aria-label', s),
|
|||
|
arialive: (s) => _attr('aria-live', s),
|
|||
|
name: (s) => _attr('name', s)
|
|||
|
};
|
|||
|
const style = (x) => { return { _styles: x }; };
|
|||
|
const prop = (x) => { return { _props: x }; };
|
|||
|
return [dom, style, attr, prop];
|
|||
|
})();
|
|||
|
// join elements in l with the results of calls to efn. efn can return
|
|||
|
// HTMLElements, which cannot be inserted into the dom multiple times, hence the
|
|||
|
// function.
|
|||
|
const join = (l, efn) => {
|
|||
|
const r = [];
|
|||
|
const n = l.length;
|
|||
|
for (let i = 0; i < n; i++) {
|
|||
|
r.push(l[i]);
|
|||
|
if (i < n - 1) {
|
|||
|
r.push(efn());
|
|||
|
}
|
|||
|
}
|
|||
|
return r;
|
|||
|
};
|
|||
|
// addLinks turns a line of text into alternating strings and links. Links that
|
|||
|
// would end with interpunction followed by whitespace are returned with that
|
|||
|
// interpunction moved to the next string instead.
|
|||
|
const addLinks = (text) => {
|
|||
|
// todo: look at ../rfc/3986 and fix up regexp. we should probably accept utf-8.
|
|||
|
const re = RegExp('(http|https):\/\/([:%0-9a-zA-Z._~!$&\'/()*+,;=-]+@)?([\\[\\]0-9a-zA-Z.-]+)(:[0-9]+)?([:@%0-9a-zA-Z._~!$&\'/()*+,;=-]*)(\\?[:@%0-9a-zA-Z._~!$&\'/()*+,;=?-]*)?(#[:@%0-9a-zA-Z._~!$&\'/()*+,;=?-]*)?');
|
|||
|
const r = [];
|
|||
|
while (text.length > 0) {
|
|||
|
const l = re.exec(text);
|
|||
|
if (!l) {
|
|||
|
r.push(text);
|
|||
|
break;
|
|||
|
}
|
|||
|
let s = text.substring(0, l.index);
|
|||
|
let url = l[0];
|
|||
|
text = text.substring(l.index + url.length);
|
|||
|
r.push(s);
|
|||
|
// If URL ends with interpunction, and next character is whitespace or end, don't
|
|||
|
// include the interpunction in the URL.
|
|||
|
if (/[!),.:;>?]$/.test(url) && (!text || /^[ \t\r\n]/.test(text))) {
|
|||
|
text = url.substring(url.length - 1) + text;
|
|||
|
url = url.substring(0, url.length - 1);
|
|||
|
}
|
|||
|
r.push(dom.a(url, attr.href(url), attr.target('_blank'), attr.rel('noopener noreferrer')));
|
|||
|
}
|
|||
|
return r;
|
|||
|
};
|
|||
|
// renderText turns text into a renderable element with ">" interpreted as quoted
|
|||
|
// text (with different levels), and URLs replaced by links.
|
|||
|
const renderText = (text) => {
|
|||
|
return dom.div(text.split('\n').map(line => {
|
|||
|
let q = 0;
|
|||
|
for (const c of line) {
|
|||
|
if (c == '>') {
|
|||
|
q++;
|
|||
|
}
|
|||
|
else if (c !== ' ') {
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
if (q == 0) {
|
|||
|
return [addLinks(line), '\n'];
|
|||
|
}
|
|||
|
q = (q - 1) % 3 + 1;
|
|||
|
return dom.div(dom._class('quoted' + q), addLinks(line));
|
|||
|
}));
|
|||
|
};
|
|||
|
const displayName = (s) => {
|
|||
|
// ../rfc/5322:1216
|
|||
|
// ../rfc/5322:1270
|
|||
|
// todo: need support for group addresses (eg "undisclosed recipients").
|
|||
|
// ../rfc/5322:697
|
|||
|
const specials = /[()<>\[\]:;@\\,."]/;
|
|||
|
if (specials.test(s)) {
|
|||
|
return '"' + s.replace('\\', '\\\\').replace('"', '\\"') + '"';
|
|||
|
}
|
|||
|
return s;
|
|||
|
};
|
|||
|
// format an address with both name and email address.
|
|||
|
const formatAddress = (a) => {
|
|||
|
let s = '<' + a.User + '@' + a.Domain.ASCII + '>';
|
|||
|
if (a.Name) {
|
|||
|
s = displayName(a.Name) + ' ' + s;
|
|||
|
}
|
|||
|
return s;
|
|||
|
};
|
|||
|
// returns an address with all available details, including unicode version if
|
|||
|
// available.
|
|||
|
const formatAddressFull = (a) => {
|
|||
|
let s = '';
|
|||
|
if (a.Name) {
|
|||
|
s = a.Name + ' ';
|
|||
|
}
|
|||
|
s += '<' + a.User + '@' + a.Domain.ASCII + '>';
|
|||
|
if (a.Domain.Unicode) {
|
|||
|
s += ' (' + a.User + '@' + a.Domain.Unicode + ')';
|
|||
|
}
|
|||
|
return s;
|
|||
|
};
|
|||
|
// format just the name, or otherwies just the email address.
|
|||
|
const formatAddressShort = (a) => {
|
|||
|
if (a.Name) {
|
|||
|
return a.Name;
|
|||
|
}
|
|||
|
return '<' + a.User + '@' + a.Domain.ASCII + '>';
|
|||
|
};
|
|||
|
// return just the email address.
|
|||
|
const formatEmailASCII = (a) => {
|
|||
|
return a.User + '@' + a.Domain.ASCII;
|
|||
|
};
|
|||
|
const equalAddress = (a, b) => {
|
|||
|
return (!a.User || !b.User || a.User === b.User) && a.Domain.ASCII === b.Domain.ASCII;
|
|||
|
};
|
|||
|
// loadMsgheaderView loads the common message headers into msgheaderelem.
|
|||
|
// if refineKeyword is set, labels are shown and a click causes a call to
|
|||
|
// refineKeyword.
|
|||
|
const loadMsgheaderView = (msgheaderelem, mi, refineKeyword) => {
|
|||
|
const msgenv = mi.Envelope;
|
|||
|
const received = mi.Message.Received;
|
|||
|
const receivedlocal = new Date(received.getTime() - received.getTimezoneOffset() * 60 * 1000);
|
|||
|
dom._kids(msgheaderelem,
|
|||
|
// todo: make addresses clickable, start search (keep current mailbox if any)
|
|||
|
dom.tr(dom.td('From:', style({ textAlign: 'right', color: '#555', whiteSpace: 'nowrap' })), dom.td(style({ width: '100%' }), dom.div(style({ display: 'flex', justifyContent: 'space-between' }), dom.div(join((msgenv.From || []).map(a => formatAddressFull(a)), () => ', ')), dom.div(attr.title('Received: ' + received.toString() + ';\nDate header in message: ' + (msgenv.Date ? msgenv.Date.toString() : '(missing/invalid)')), receivedlocal.toDateString() + ' ' + receivedlocal.toTimeString().split(' ')[0])))), (msgenv.ReplyTo || []).length === 0 ? [] : dom.tr(dom.td('Reply-To:', style({ textAlign: 'right', color: '#555', whiteSpace: 'nowrap' })), dom.td(join((msgenv.ReplyTo || []).map(a => formatAddressFull(a)), () => ', '))), dom.tr(dom.td('To:', style({ textAlign: 'right', color: '#555', whiteSpace: 'nowrap' })), dom.td(join((msgenv.To || []).map(a => formatAddressFull(a)), () => ', '))), (msgenv.CC || []).length === 0 ? [] : dom.tr(dom.td('Cc:', style({ textAlign: 'right', color: '#555', whiteSpace: 'nowrap' })), dom.td(join((msgenv.CC || []).map(a => formatAddressFull(a)), () => ', '))), (msgenv.BCC || []).length === 0 ? [] : dom.tr(dom.td('Bcc:', style({ textAlign: 'right', color: '#555', whiteSpace: 'nowrap' })), dom.td(join((msgenv.BCC || []).map(a => formatAddressFull(a)), () => ', '))), dom.tr(dom.td('Subject:', style({ textAlign: 'right', color: '#555', whiteSpace: 'nowrap' })), dom.td(dom.div(style({ display: 'flex', justifyContent: 'space-between' }), dom.div(msgenv.Subject || ''), dom.div(mi.IsSigned ? dom.span(style({ backgroundColor: '#666', padding: '0px 0.15em', fontSize: '.9em', color: 'white', borderRadius: '.15em' }), 'Message has a signature') : [], mi.IsEncrypted ? dom.span(style({ backgroundColor: '#666', padding: '0px 0.15em', fontSize: '.9em', color: 'white', borderRadius: '.15em' }), 'Message is encrypted') : [], refineKeyword ? (mi.Message.Keywords || []).map(kw => dom.clickbutton(dom._class('keyword'), kw, async function click() {
|
|||
|
await refineKeyword(kw);
|
|||
|
})) : [])))));
|
|||
|
};
|
|||
|
// Javascript is generated from typescript, do not modify generated javascript because changes will be overwritten.
|
|||
|
const init = async () => {
|
|||
|
const pm = api.parser.ParsedMessage(parsedMessage);
|
|||
|
dom._kids(document.body, dom.div(dom._class('pad', 'mono'), style({ whiteSpace: 'pre-wrap' }), join((pm.Texts || []).map(t => renderText(t)), () => dom.hr(style({ margin: '2ex 0' })))));
|
|||
|
};
|
|||
|
init()
|
|||
|
.catch((err) => {
|
|||
|
window.alert('Error: ' + (err.message || '(no message)'));
|
|||
|
});
|