1 parent
d55640d37a
commit
fff8521a17
67 files changed
+11016
-16763
No files matched your search
@@ -0,0 +1,454 @@
|
||||
/* RustBox UI module: 95-bindings-boot.js */
|
||||
function bindEditor() {
|
||||
$$(".editor-toolbar [data-command]").forEach((button) => button.addEventListener("click", () => {
|
||||
$("#compose-editor").focus();
|
||||
document.execCommand(button.dataset.command, false, button.dataset.commandValue || null);
|
||||
}));
|
||||
$("#editor-link").addEventListener("click", async () => {
|
||||
const url = await appPrompt("Insert link", { value: "https://", type: "url", confirmLabel: "Insert link", required: true });
|
||||
if (!url) return;
|
||||
$("#compose-editor").focus();
|
||||
document.execCommand("createLink", false, url);
|
||||
});
|
||||
$("#editor-font").addEventListener("change", (event) => {
|
||||
if (!event.target.value) return;
|
||||
$("#compose-editor").focus();
|
||||
document.execCommand("fontName", false, event.target.value);
|
||||
scheduleDraftSave();
|
||||
});
|
||||
$("#editor-size").addEventListener("change", (event) => {
|
||||
if (!event.target.value) return;
|
||||
$("#compose-editor").focus();
|
||||
document.execCommand("fontSize", false, event.target.value);
|
||||
scheduleDraftSave();
|
||||
});
|
||||
$("#editor-color").addEventListener("input", (event) => {
|
||||
$("#compose-editor").focus();
|
||||
document.execCommand("foreColor", false, event.target.value);
|
||||
scheduleDraftSave();
|
||||
});
|
||||
}
|
||||
|
||||
function bindPaneResizer(selector, preferenceKey, minPx, maxPx) {
|
||||
const handle = $(selector);
|
||||
if (!handle) return;
|
||||
handle.addEventListener("pointerdown", (event) => {
|
||||
if (innerWidth <= 820 || event.button !== 0) return;
|
||||
event.preventDefault();
|
||||
const scaleFactor = Number(getComputedStyle(root).getPropertyValue("--ui-scale-factor")) || 1;
|
||||
const startX = event.clientX;
|
||||
const startRendered = Number(state.prefs[preferenceKey] || minPx) * scaleFactor;
|
||||
handle.setPointerCapture?.(event.pointerId);
|
||||
document.body.classList.add("resizing-pane");
|
||||
const move = (moveEvent) => {
|
||||
const rendered = Math.max(minPx * scaleFactor, Math.min(maxPx * scaleFactor, startRendered + (moveEvent.clientX - startX)));
|
||||
const base = Math.round(rendered / scaleFactor);
|
||||
state.prefs[preferenceKey] = base;
|
||||
const cssName = preferenceKey === "folderPaneWidth" ? "--folder-pane-width" : "--message-pane-width";
|
||||
root.style.setProperty(cssName, `${Math.round(rendered)}px`);
|
||||
handle.setAttribute("aria-valuenow", String(base));
|
||||
};
|
||||
const finish = () => {
|
||||
handle.removeEventListener("pointermove", move);
|
||||
handle.removeEventListener("pointerup", finish);
|
||||
handle.removeEventListener("pointercancel", finish);
|
||||
document.body.classList.remove("resizing-pane");
|
||||
savePreference(preferenceKey, state.prefs[preferenceKey]);
|
||||
};
|
||||
handle.addEventListener("pointermove", move);
|
||||
handle.addEventListener("pointerup", finish);
|
||||
handle.addEventListener("pointercancel", finish);
|
||||
});
|
||||
}
|
||||
|
||||
function bindEvents() {
|
||||
bindPaneResizer("#folder-resizer", "folderPaneWidth", 170, 420);
|
||||
bindPaneResizer("#message-resizer", "messagePaneWidth", 300, 760);
|
||||
bindRecipientAutocomplete("#compose-to");
|
||||
bindRecipientAutocomplete("#compose-cc");
|
||||
bindRecipientAutocomplete("#compose-bcc");
|
||||
$("#login-form").addEventListener("submit", login);
|
||||
$("#logout-btn").addEventListener("click", logout);
|
||||
$("#compose-btn").addEventListener("click", () => openCompose("new"));
|
||||
$("#compose-fullscreen").addEventListener("click", toggleComposeFullscreen);
|
||||
$("#compose-form").addEventListener("submit", sendMessage);
|
||||
$("#compose-attach").addEventListener("click", () => $("#compose-files").click());
|
||||
$("#compose-files").addEventListener("change", async (event) => { await uploadComposeFiles(event.target.files); event.target.value = ""; });
|
||||
$("#editor-image").addEventListener("click", () => { state.composeInsertRange = captureEditorRange(); $("#compose-images").click(); });
|
||||
$("#editor-image-edit").addEventListener("click", () => openInlineImageEditor());
|
||||
$("#compose-images").addEventListener("change", async (event) => { await uploadInlineImage(event.target.files, state.composeInsertRange); state.composeInsertRange = null; event.target.value = ""; });
|
||||
$("#image-edit-form").addEventListener("submit", applyInlineImageEditor);
|
||||
$("#image-edit-reset").addEventListener("click", resetImageCropControls);
|
||||
["#image-crop-top", "#image-crop-right", "#image-crop-bottom", "#image-crop-left"].forEach((selector) => $(selector).addEventListener("input", drawInlineImageCropPreview));
|
||||
$("#image-edit-width").addEventListener("input", updateImageEditOutputs);
|
||||
$("#image-edit-dialog").addEventListener("close", () => { state.imageEditorBitmap?.close?.(); state.imageEditorBitmap = null; state.imageEditorBlob = null; });
|
||||
$("#compose-recover").addEventListener("click", recoverDraft);
|
||||
$("#compose-mode").addEventListener("change", async (event) => {
|
||||
const editor = $("#compose-editor");
|
||||
const previous = editor.dataset.mode || "html";
|
||||
const next = event.target.value;
|
||||
const richContent = previous === "html" && next !== "html" && Boolean(editor.querySelector("img,a,strong,b,em,i,u,s,ul,ol,li,blockquote,[style]"));
|
||||
if (richContent && !await appConfirm("Switching away from HTML can hide or discard formatting when the message is sent. Continue?", { title: "Change compose format", confirmLabel: "Switch format" })) {
|
||||
event.target.value = previous;
|
||||
return;
|
||||
}
|
||||
setComposeMode(next, true);
|
||||
insertIdentitySignature(editor);
|
||||
scheduleDraftSave();
|
||||
});
|
||||
$("#compose-from").addEventListener("change", () => { insertIdentitySignature($("#compose-editor")); updateComposePgpUi(true); });
|
||||
["#compose-to", "#compose-cc", "#compose-bcc"].forEach((selector) => $(selector).addEventListener("input", () => {
|
||||
state.pgpRecipientStatus = [];
|
||||
updateComposePgpUi();
|
||||
scheduleDraftSave();
|
||||
}));
|
||||
$("#compose-subject").addEventListener("input", scheduleDraftSave);
|
||||
["#compose-priority", "#compose-mdn", "#compose-pgp-mode", "#compose-pgp-inline", "#compose-pgp-attach-key", "#compose-pgp-attach-recipient-keys"].forEach((selector) => $(selector).addEventListener("change", async () => {
|
||||
if (selector === "#compose-pgp-attach-recipient-keys" && $(selector).checked) await checkComposePgpRecipients(false, composePgpVisibleRecipients());
|
||||
updateComposePgpUi();
|
||||
scheduleDraftSave();
|
||||
}));
|
||||
$("#compose-editor").addEventListener("input", () => { if (state.composeSelectedImage && !state.composeSelectedImage.isConnected) setSelectedInlineImage(null); scheduleDraftSave(); scheduleUnusedInlineAttachmentCleanup(); });
|
||||
$("#compose-editor").addEventListener("click", (event) => {
|
||||
const image = event.target.closest?.("img[data-rustbox-cid]");
|
||||
setSelectedInlineImage(image || null);
|
||||
});
|
||||
$("#compose-editor").addEventListener("dblclick", (event) => {
|
||||
const image = event.target.closest?.("img[data-rustbox-cid]");
|
||||
if (image) { event.preventDefault(); openInlineImageEditor(image); }
|
||||
});
|
||||
$("#compose-editor").addEventListener("paste", async (event) => {
|
||||
const images = [...(event.clipboardData?.items || [])]
|
||||
.filter((item) => item.kind === "file" && /^image\/(png|jpeg|gif|webp)$/i.test(item.type))
|
||||
.map((item) => item.getAsFile())
|
||||
.filter(Boolean);
|
||||
if (!images.length) return;
|
||||
event.preventDefault();
|
||||
if ($("#compose-mode").value !== "html") return toast("Switch to HTML mode to paste inline images", "warning");
|
||||
const range = captureEditorRange();
|
||||
await uploadInlineImage(images, range);
|
||||
});
|
||||
$("#compose-editor").addEventListener("dragover", (event) => { if ([...event.dataTransfer.types].includes("Files")) event.preventDefault(); });
|
||||
$("#compose-editor").addEventListener("drop", async (event) => {
|
||||
const files = [...event.dataTransfer.files];
|
||||
if (!files.length) return;
|
||||
event.preventDefault();
|
||||
const allImages = files.every((file) => /^image\/(png|jpeg|gif|webp)$/i.test(file.type));
|
||||
if ($("#compose-mode").value === "html" && allImages) await uploadInlineImage(files, editorRangeFromPoint(event.clientX, event.clientY));
|
||||
else await uploadComposeFiles(files);
|
||||
});
|
||||
$("#reply-btn").addEventListener("click", () => openCompose("reply"));
|
||||
$("#reply-all-btn").addEventListener("click", () => openCompose("reply-all"));
|
||||
$("#forward-btn").addEventListener("click", () => openCompose("forward"));
|
||||
$("#delete-btn").addEventListener("click", deleteCurrent);
|
||||
$("#unread-btn").addEventListener("click", toggleUnread);
|
||||
$("#flag-btn").addEventListener("click", toggleFlagged);
|
||||
$("#archive-btn").addEventListener("click", archiveCurrent);
|
||||
$("#spam-btn").addEventListener("click", junkCurrent);
|
||||
$("#move-select").addEventListener("change", async (event) => { const target = event.target.value; event.target.value = ""; if (target) await moveCurrentTo(target); });
|
||||
$("#copy-select").addEventListener("change", async (event) => { const target = event.target.value; event.target.value = ""; if (target) await copyCurrentTo(target); });
|
||||
$("#bulk-select-all").addEventListener("change", (event) => { const source = state.searchAllResults || state.messages; for (const message of source) { const key = messageKey(message); event.target.checked ? state.selectedMessages.add(key) : state.selectedMessages.delete(key); } renderMessages(); });
|
||||
$("#bulk-read").addEventListener("click", () => performBulk("read"));
|
||||
$("#bulk-unread").addEventListener("click", () => performBulk("unread"));
|
||||
$("#bulk-flag").addEventListener("click", () => performBulk("flag"));
|
||||
$("#bulk-unflag").addEventListener("click", () => performBulk("unflag"));
|
||||
$("#bulk-archive").addEventListener("click", () => performBulk("archive"));
|
||||
$("#bulk-select-search").addEventListener("click", selectAllSearchResults);
|
||||
$("#bulk-junk").addEventListener("click", () => performBulk("junk"));
|
||||
$("#bulk-delete").addEventListener("click", () => performBulk("delete"));
|
||||
$("#bulk-move-select").addEventListener("change", async (event) => { const target = event.target.value; event.target.value = ""; if (target) await performBulk("move", target); });
|
||||
$("#bulk-copy-select").addEventListener("change", async (event) => { const target = event.target.value; event.target.value = ""; if (target) await performBulk("copy", target); });
|
||||
$("#bulk-tag-select").addEventListener("change", async (event) => { const tagId = event.target.value; event.target.value = ""; if (tagId) await bulkAddTag(tagId); });
|
||||
$("#select-mode-btn").addEventListener("click", () => setSelectionMode(!state.selectionMode));
|
||||
$("#selection-done").addEventListener("click", () => setSelectionMode(false));
|
||||
$("#refresh-btn").addEventListener("click", () => loadFolders(false));
|
||||
$("#message-sort").addEventListener("change", async (event) => {
|
||||
const value = event.target.value;
|
||||
state.mailPrefs.default_sort = value;
|
||||
$("#mail-pref-sort").value = value;
|
||||
try {
|
||||
state.mailPrefs = await request("/settings/mailbox", { method: "PUT", body: JSON.stringify({ default_sort: value }) });
|
||||
state.page = 0;
|
||||
await loadMessages(true);
|
||||
toast("Message sort updated", "success");
|
||||
} catch (error) { toast(error.message, "error"); }
|
||||
});
|
||||
$("#load-more").addEventListener("click", async () => { state.page += 1; await loadMessages(false); });
|
||||
$("#message-list").addEventListener("scroll", scheduleMessageListStateSave, { passive: true });
|
||||
window.addEventListener("offline", () => setConnectivityState(false));
|
||||
window.addEventListener("online", async () => {
|
||||
setConnectivityState(true);
|
||||
disconnectRealtime();
|
||||
connectRealtime();
|
||||
if (state.accounts.length) {
|
||||
await refreshFolderListOnly();
|
||||
if (state.searchContext) await runSearch(new URLSearchParams(state.searchContext.params), state.searchContext.label, { restoreScrollTop: state.searchContext.scrollTop });
|
||||
else await loadMessages(true);
|
||||
}
|
||||
});
|
||||
$("#search-form").addEventListener("submit", searchMail);
|
||||
$("#advanced-search-btn").addEventListener("click", openAdvancedSearch);
|
||||
$("#advanced-search-form").addEventListener("submit", submitAdvancedSearch);
|
||||
$("#search-reset-btn").addEventListener("click", resetAdvancedSearch);
|
||||
$("#search-save-btn").addEventListener("click", saveAdvancedSearch);
|
||||
$("#contacts-btn").addEventListener("click", openContacts);
|
||||
$("#contact-group-filter").addEventListener("change", async () => { renderContactGroupFilter(); await loadContacts($("#contact-search").value.trim()); });
|
||||
$("#contact-group-new").addEventListener("click", createContactGroup);
|
||||
$("#contact-group-rename").addEventListener("click", renameContactGroup);
|
||||
$("#contact-group-delete").addEventListener("click", deleteContactGroup);
|
||||
$("#tag-add-btn").addEventListener("click", () => openTagDialog());
|
||||
$("#tag-form").addEventListener("submit", saveTag);
|
||||
$("#tag-delete").addEventListener("click", deleteTag);
|
||||
$("#calendar-btn").addEventListener("click", openCalendar);
|
||||
$("#settings-btn").addEventListener("click", () => openSettings("appearance"));
|
||||
$("#shortcuts-open").addEventListener("click", () => $("#shortcuts-dialog").showModal());
|
||||
$("#account-add").addEventListener("click", () => { $("#add-account-error").classList.add("hidden"); $("#add-account-dialog").showModal(); setTimeout(() => $("#add-account-email").focus(), 20); });
|
||||
$("#account-revoke-others").addEventListener("click", revokeOtherSessions);
|
||||
$("#add-account-form").addEventListener("submit", addBrowserAccount);
|
||||
$("#mobile-folders").addEventListener("click", toggleFolderDrawer);
|
||||
$("#folder-drawer-close").addEventListener("click", () => setFolderDrawerOpen(false));
|
||||
$("#folder-drawer-scrim").addEventListener("click", () => setFolderDrawerOpen(false));
|
||||
$("#reader-back").addEventListener("click", closeReader);
|
||||
$("#reader-prev").addEventListener("click", () => navigateMessage(-1));
|
||||
$("#reader-next").addEventListener("click", () => navigateMessage(1));
|
||||
$("#thread-toggle").addEventListener("click", () => {
|
||||
state.threadExpanded = !state.threadExpanded;
|
||||
localStorage.setItem("rustbox.threadExpanded", String(state.threadExpanded));
|
||||
if (state.currentDetail) renderThread(state.currentDetail);
|
||||
});
|
||||
$("#folder-manage-btn").addEventListener("click", openFolderDialog);
|
||||
$("#create-folder-btn").addEventListener("click", createFolder);
|
||||
$("#rename-folder-btn").addEventListener("click", renameFolder);
|
||||
$("#delete-folder-btn").addEventListener("click", deleteFolder);
|
||||
$("#subscribe-folder-btn").addEventListener("click", toggleCurrentFolderSubscription);
|
||||
$("#headers-btn").addEventListener("click", showHeaders);
|
||||
$("#source-btn").addEventListener("click", showMessageSource);
|
||||
$("#quick-filter-btn").addEventListener("click", () => createFilterFromCurrentMessage("sender"));
|
||||
$("#reader-from").addEventListener("click", (event) => { const rect = event.currentTarget.getBoundingClientRect(); openCurrentSenderMenu(rect.left, rect.bottom + 4); });
|
||||
$("#reader-from").addEventListener("keydown", (event) => { if (["Enter", " "].includes(event.key)) { event.preventDefault(); const rect = event.currentTarget.getBoundingClientRect(); openCurrentSenderMenu(rect.left, rect.bottom + 4); } });
|
||||
$("#print-btn").addEventListener("click", printCurrentMessage);
|
||||
$("#pgp-reader-process").addEventListener("click", processCurrentPgpMessage);
|
||||
$("#pgp-reader-discover").addEventListener("click", discoverCurrentPgpSigner);
|
||||
$("#copy-headers-btn").addEventListener("click", copyHeaders);
|
||||
$$(".close-dialog").forEach((button) => button.addEventListener("click", async () => {
|
||||
const dialog = button.closest("dialog");
|
||||
if (dialog?.id === "compose-dialog") await closeComposer();
|
||||
else dialog?.close();
|
||||
}));
|
||||
$("#compose-dialog").addEventListener("cancel", async (event) => { event.preventDefault(); await closeComposer(); });
|
||||
window.addEventListener("beforeunload", () => { if (state.composeDirty && draftHasContent()) storeLocalDraftSnapshot(); });
|
||||
$("#compose-pgp-check").addEventListener("click", () => checkComposePgpRecipients(true));
|
||||
$("#compose-pgp-discover").addEventListener("click", discoverComposePgpRecipients);
|
||||
["#compose-to", "#compose-cc", "#compose-bcc"].forEach((selector) => $(selector).addEventListener("change", () => { state.pgpRecipientStatus = []; updateComposePgpUi(); }));
|
||||
$("#pgp-refresh").addEventListener("click", loadOpenPgp);
|
||||
$("#pgp-pref-save").addEventListener("click", savePgpPreferences);
|
||||
$("#pgp-generate").addEventListener("click", generatePgpKey);
|
||||
$("#pgp-import").addEventListener("click", () => importPgpText());
|
||||
$("#pgp-import-file").addEventListener("click", () => $("#pgp-import-file-input").click());
|
||||
$("#pgp-import-file-input").addEventListener("change", async (event) => { const file = event.target.files?.[0]; if (file) await importPgpFile(file); event.target.value = ""; });
|
||||
$("#pgp-discover").addEventListener("click", () => discoverPgpEmail());
|
||||
$$(".settings-tab").forEach((button) => button.addEventListener("click", () => { selectSettingsTab(button.dataset.tab); if (button.dataset.tab === "sieve") loadSieve(); if (button.dataset.tab === "identities") { loadIdentities(); loadOpenPgp(); } if (button.dataset.tab === "openpgp") loadOpenPgp(); if (button.dataset.tab === "notifications") loadNotificationPreferences(); if (button.dataset.tab === "mailbox") loadMailPreferences(); if (button.dataset.tab === "composing") loadResponseTemplates(); if (button.dataset.tab === "diagnostics") loadDiagnostics(); if (button.dataset.tab === "account") { renderAccountSettings(); loadActiveSessions(); } if (button.dataset.tab === "folders") { loadSpecialFolders(); loadQuota(); } }));
|
||||
$("#pref-theme").addEventListener("change", (event) => savePreference("theme", event.target.value));
|
||||
$("#pref-interface-skin").addEventListener("change", (event) => savePreference("interfaceSkin", event.target.value));
|
||||
$("#pref-density").addEventListener("change", (event) => savePreference("density", event.target.value));
|
||||
$("#pref-preview").addEventListener("change", (event) => savePreference("preview", event.target.value));
|
||||
$("#pref-scale").addEventListener("change", (event) => savePreference("scale", event.target.value));
|
||||
$("#pref-language").addEventListener("change", (event) => savePreference("language", event.target.value));
|
||||
$("#pref-color-preset").addEventListener("change", (event) => savePreference("colorPreset", event.target.value));
|
||||
$("#pref-accent").addEventListener("input", (event) => { $("#pref-accent-value").textContent = event.target.value; savePreference("accentColor", event.target.value); });
|
||||
[["#pref-background", "#pref-background-value", "backgroundColor"], ["#pref-panel", "#pref-panel-value", "panelColor"], ["#pref-text-color", "#pref-text-color-value", "textColor"]].forEach(([input, output, key]) => $(input).addEventListener("input", (event) => { $(output).textContent = event.target.value; savePreference(key, event.target.value); }));
|
||||
$("#pref-font-family").addEventListener("change", (event) => savePreference("fontFamily", event.target.value));
|
||||
[["#pref-font-size", "fontSize"], ["#pref-row-height", "rowHeight"], ["#pref-folder-width", "folderPaneWidth"], ["#pref-message-width", "messagePaneWidth"], ["#pref-radius", "borderRadius"]].forEach(([selector, key]) => $(selector).addEventListener("input", (event) => savePreference(key, Number(event.target.value))));
|
||||
$("#pref-contrast").addEventListener("change", (event) => savePreference("contrast", event.target.value));
|
||||
$("#pref-date-format").addEventListener("change", (event) => { savePreference("dateFormat", event.target.value); renderMessages(); if (state.currentDetail) renderMessageDetail(state.currentDetail); });
|
||||
$("#pref-time-format").addEventListener("change", (event) => { savePreference("timeFormat", event.target.value); renderMessages(); if (state.currentDetail) renderMessageDetail(state.currentDetail); });
|
||||
$("#pref-avatars").addEventListener("change", (event) => savePreference("showAvatars", event.target.checked));
|
||||
$("#pref-attachment-icons").addEventListener("change", (event) => savePreference("showAttachmentIcons", event.target.checked));
|
||||
$("#pref-flags").addEventListener("change", (event) => savePreference("showFlags", event.target.checked));
|
||||
$("#pref-reduce-motion").addEventListener("change", (event) => savePreference("reduceMotion", event.target.checked));
|
||||
[["#pref-icon-size", "iconSize"], ["#pref-sidebar-height", "sidebarItemHeight"], ["#pref-composer-width", "composerWidth"], ["#pref-unread-weight", "unreadFontWeight"]].forEach(([selector, key]) => $(selector).addEventListener("input", (event) => savePreference(key, Number(event.target.value))));
|
||||
$("#pref-reader-width").addEventListener("change", (event) => savePreference("readerMaxWidth", Number(event.target.value)));
|
||||
$("#pref-toolbar-density").addEventListener("change", (event) => savePreference("toolbarDensity", event.target.value));
|
||||
$("#pref-selection-color").addEventListener("input", (event) => { $("#pref-selection-color-value").textContent = event.target.value; savePreference("selectionColor", event.target.value); });
|
||||
$("#pref-unread-badge").addEventListener("change", (event) => savePreference("unreadBadgeStyle", event.target.value));
|
||||
$("#pref-scrollbar-density").addEventListener("change", (event) => savePreference("scrollbarDensity", event.target.value));
|
||||
$("#pref-compact-headers").addEventListener("change", (event) => savePreference("compactHeaders", event.target.checked));
|
||||
$("#pref-show-borders").addEventListener("change", (event) => savePreference("showBorders", event.target.checked));
|
||||
$("#pref-monochrome").addEventListener("change", (event) => savePreference("monochrome", event.target.checked));
|
||||
[["folder", "folderBackground", "#101310"], ["message", "messageBackground", "#181c19"], ["reader", "readerBackground", "#181c19"]].forEach(([name, key, fallback]) => {
|
||||
const enabled = $(`#pref-${name}-bg-enabled`); const color = $(`#pref-${name}-bg`);
|
||||
enabled.addEventListener("change", () => savePreference(key, enabled.checked ? color.value : ""));
|
||||
color.addEventListener("input", () => { if (enabled.checked) savePreference(key, color.value); });
|
||||
});
|
||||
$("#response-template-new").addEventListener("click", () => editResponseTemplate());
|
||||
$("#compose-response-template").addEventListener("change", (event) => { $("#compose-response-insert").disabled = !event.target.value; });
|
||||
$("#compose-response-insert").addEventListener("click", insertSavedResponse);
|
||||
$("#compose-pref-format").addEventListener("change", (event) => savePreference("composeFormat", event.target.value));
|
||||
$("#compose-pref-autosave").addEventListener("change", (event) => savePreference("composeAutosaveMs", Number(event.target.value)));
|
||||
$("#compose-pref-priority").addEventListener("change", (event) => savePreference("defaultPriority", event.target.value));
|
||||
$("#compose-pref-mdn").addEventListener("change", (event) => savePreference("requestMdnDefault", event.target.checked));
|
||||
$("#compose-pref-attachment-reminder").addEventListener("change", (event) => savePreference("attachmentReminder", event.target.checked));
|
||||
$("#compose-pref-font").addEventListener("change", (event) => savePreference("composeFontFamily", event.target.value));
|
||||
$("#compose-pref-reply-position").addEventListener("change", (event) => savePreference("replyPosition", event.target.value));
|
||||
$("#compose-pref-quote-style").addEventListener("change", (event) => savePreference("quoteStyle", event.target.value));
|
||||
$("#compose-pref-forward-mode").addEventListener("change", (event) => savePreference("forwardMode", event.target.value));
|
||||
$("#compose-pref-spellcheck").addEventListener("change", (event) => savePreference("spellcheckEnabled", event.target.checked));
|
||||
$("#compose-markdown-preview").addEventListener("click", previewMarkdown);
|
||||
$("#pref-reset").addEventListener("click", resetAppearance);
|
||||
$("#special-folders-save").addEventListener("click", saveSpecialFolders);
|
||||
$("#special-folders-auto").addEventListener("click", autoDetectSpecialFolders);
|
||||
$("#identity-form").addEventListener("submit", saveIdentity);
|
||||
$("#identity-new").addEventListener("click", () => editIdentity(null));
|
||||
$("#identity-delete").addEventListener("click", deleteIdentity);
|
||||
$$("[data-signature-command]").forEach((button) => button.addEventListener("click", () => {
|
||||
const editor = $("#identity-signature");
|
||||
editor.focus();
|
||||
document.execCommand(button.dataset.signatureCommand, false, null);
|
||||
}));
|
||||
$("#identity-signature-link").addEventListener("click", async () => {
|
||||
const editor = $("#identity-signature");
|
||||
const range = captureEditorRange(editor);
|
||||
const url = await appPrompt("Insert signature link", { placeholder: "https://example.com", type: "url", confirmLabel: "Insert" });
|
||||
if (!url) return;
|
||||
editor.focus();
|
||||
restoreEditorRange(range, editor);
|
||||
document.execCommand("createLink", false, url);
|
||||
});
|
||||
$("#identity-signature-image-btn").addEventListener("click", () => { state.identitySignatureRange = captureEditorRange($("#identity-signature")); $("#identity-signature-image").click(); });
|
||||
$("#identity-signature-image").addEventListener("change", async (event) => { const file = event.target.files?.[0]; await insertIdentitySignatureImage(file); event.target.value = ""; });
|
||||
$("#identity-signature").addEventListener("paste", async (event) => {
|
||||
const file = [...(event.clipboardData?.items || [])].find((item) => item.kind === "file" && /^image\/(png|jpeg|gif|webp)$/i.test(item.type))?.getAsFile();
|
||||
if (!file) return;
|
||||
event.preventDefault();
|
||||
await insertIdentitySignatureImage(file, captureEditorRange($("#identity-signature")));
|
||||
});
|
||||
$("#contact-form").addEventListener("submit", saveContact);
|
||||
$("#contact-new").addEventListener("click", () => editContact(null));
|
||||
$("#contact-import").addEventListener("click", () => $("#contact-import-file").click());
|
||||
$("#contact-import-file").addEventListener("change", (event) => importContactVcard(event.target.files));
|
||||
$("#contact-export").addEventListener("click", downloadContactExport);
|
||||
$("#contact-duplicates").addEventListener("click", showContactDuplicates);
|
||||
$("#contact-photo-upload").addEventListener("click", () => $("#contact-photo-file").click());
|
||||
$("#contact-photo-file").addEventListener("change", (event) => uploadContactPhoto(event.target.files));
|
||||
$("#contact-photo-delete").addEventListener("click", deleteContactPhoto);
|
||||
$("#contact-delete").addEventListener("click", deleteContact);
|
||||
$("#contact-compose").addEventListener("click", composeToContact);
|
||||
$("#contact-search").addEventListener("input", (event) => { clearTimeout(event.target._timer); event.target._timer = setTimeout(() => loadContacts(event.target.value.trim()), 180); });
|
||||
$("#calendar-form").addEventListener("submit", saveCalendarEvent);
|
||||
$("#calendar-import").addEventListener("click", () => $("#calendar-import-file").click());
|
||||
$("#calendar-import-file").addEventListener("change", (event) => importCalendarIcs(event.target.files));
|
||||
$("#calendar-export").addEventListener("click", exportCalendarIcs);
|
||||
$("#calendar-recurrence").addEventListener("change", updateCalendarRecurrenceUi);
|
||||
$("#calendar-new").addEventListener("click", () => editCalendarEvent(null));
|
||||
$("#calendar-cancel").addEventListener("click", () => $("#calendar-form").classList.add("hidden"));
|
||||
$("#calendar-delete").addEventListener("click", deleteCalendarEvent);
|
||||
$("#calendar-view").addEventListener("change", async (event) => { state.calendarView = event.target.value; localStorage.setItem("rustbox.calendarView", state.calendarView); await loadCalendar(); });
|
||||
$("#calendar-prev").addEventListener("click", async () => { shiftCalendarCursor(-1); await loadCalendar(); });
|
||||
$("#calendar-next").addEventListener("click", async () => { shiftCalendarCursor(1); await loadCalendar(); });
|
||||
$("#calendar-today").addEventListener("click", async () => { state.calendarCursor = new Date(); await loadCalendar(); });
|
||||
$("#notify-save").addEventListener("click", saveNotificationPreferences);
|
||||
$("#notify-permission").addEventListener("click", requestNotificationPermission);
|
||||
$("#notify-test").addEventListener("click", testNotification);
|
||||
$("#notify-enabled").addEventListener("change", (event) => { if (event.target.checked && "Notification" in window && Notification.permission === "default") requestNotificationPermission(); });
|
||||
$("#mail-pref-save").addEventListener("click", saveMailPreferences);
|
||||
$("#mail-pref-reconcile-labels")?.addEventListener("click", reconcileImapLabels);
|
||||
$("#diagnostics-refresh").addEventListener("click", loadDiagnostics);
|
||||
$("#diagnostic-imap-reconnect").addEventListener("click", reconnectImap);
|
||||
$("#diagnostic-search-rebuild").addEventListener("click", rebuildSearchIndex);
|
||||
$("#remote-image-load").addEventListener("click", () => { if (state.currentDetail) renderMessageBody(state.currentDetail, true); });
|
||||
$("#remote-image-trust").addEventListener("click", trustCurrentSender);
|
||||
$("#reader-add-sender-contact").addEventListener("click", (event) => { event.stopPropagation(); addSenderToContacts(); });
|
||||
const sieveBuilderFields = [
|
||||
"#sieve-builder-name", "#sieve-builder-enabled", "#sieve-builder-stop",
|
||||
"#sieve-builder-condition", "#sieve-builder-match", "#sieve-builder-header", "#sieve-builder-value", "#sieve-builder-negate",
|
||||
"#sieve-builder-condition-2", "#sieve-builder-match-2", "#sieve-builder-header-2", "#sieve-builder-value-2", "#sieve-builder-negate-2", "#sieve-builder-logic",
|
||||
"#sieve-builder-condition-3", "#sieve-builder-match-3", "#sieve-builder-header-3", "#sieve-builder-value-3", "#sieve-builder-negate-3", "#sieve-builder-logic-3",
|
||||
"#sieve-builder-action", "#sieve-builder-target", "#sieve-builder-action-2", "#sieve-builder-target-2",
|
||||
];
|
||||
sieveBuilderFields.forEach((selector) => {
|
||||
const node = $(selector);
|
||||
if (!node) return;
|
||||
node.addEventListener("input", updateSieveBuilder);
|
||||
node.addEventListener("change", updateSieveBuilder);
|
||||
});
|
||||
$("#sieve-rule-new").addEventListener("click", openNewSieveRuleDialog);
|
||||
$("#sieve-builder-add-condition").addEventListener("click", () => { $("#sieve-builder-extra-conditions").append(createSieveDynamicCondition()); updateSieveBuilder(); });
|
||||
$("#sieve-builder-add-action").addEventListener("click", () => { $("#sieve-builder-extra-actions").append(createSieveDynamicAction()); updateSieveBuilder(); });
|
||||
$("#sieve-vacation-start").addEventListener("change", updateSieveBuilder);
|
||||
$("#sieve-vacation-end").addEventListener("change", updateSieveBuilder);
|
||||
$("#sieve-builder-append").addEventListener("click", () => applySieveVisualRule(false));
|
||||
$("#sieve-builder-replace").addEventListener("click", () => applySieveVisualRule(true));
|
||||
$("#sieve-builder-clear").addEventListener("click", clearSieveRuleBuilder);
|
||||
$("#sieve-editor").addEventListener("input", () => {
|
||||
state.sieveEditingRuleId = null;
|
||||
renderManagedSieveRules();
|
||||
updateSieveBuilder();
|
||||
});
|
||||
$("#sieve-vacation-append").addEventListener("click", appendSieveVacation);
|
||||
$("#sieve-backup").addEventListener("click", backupSieveScripts);
|
||||
$("#sieve-restore").addEventListener("click", () => $("#sieve-restore-file").click());
|
||||
$("#sieve-restore-file").addEventListener("change", (event) => restoreSieveBackup(event.target.files?.[0]));
|
||||
$("#sieve-refresh").addEventListener("click", loadSieve);
|
||||
$("#sieve-new").addEventListener("click", newSieveScript);
|
||||
$("#sieve-check").addEventListener("click", validateSieve);
|
||||
$("#sieve-save").addEventListener("click", saveSieve);
|
||||
$("#sieve-activate").addEventListener("click", activateSieve);
|
||||
$("#sieve-deactivate").addEventListener("click", deactivateSieve);
|
||||
$("#sieve-delete").addEventListener("click", deleteSieve);
|
||||
bindEditor();
|
||||
document.addEventListener("click", (event) => { if (!event.target.closest("#context-menu")) hideContextMenu(); });
|
||||
window.addEventListener("blur", hideContextMenu);
|
||||
document.addEventListener("scroll", hideContextMenu, true);
|
||||
document.addEventListener("keydown", (event) => {
|
||||
const tag = document.activeElement?.tagName || "";
|
||||
if (event.key === "/" && !["INPUT", "TEXTAREA"].includes(tag) && !document.activeElement?.isContentEditable) {
|
||||
event.preventDefault(); $("#search-input").focus();
|
||||
}
|
||||
if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === "n") { event.preventDefault(); openCompose("new"); }
|
||||
if (event.key === "?" && !["INPUT", "TEXTAREA", "SELECT"].includes(tag) && !document.activeElement?.isContentEditable) {
|
||||
event.preventDefault();
|
||||
if (!document.querySelector("dialog[open]")) $("#shortcuts-dialog").showModal();
|
||||
}
|
||||
const dialogOpen = Boolean(document.querySelector("dialog[open]"));
|
||||
const keyboardIdle = !dialogOpen && !["INPUT", "TEXTAREA", "SELECT"].includes(tag) && !document.activeElement?.isContentEditable;
|
||||
if (keyboardIdle) {
|
||||
const globalKey = event.key.toLowerCase();
|
||||
if (globalKey === "c") { event.preventDefault(); openCompose("new"); return; }
|
||||
if (globalKey === "v") { event.preventDefault(); setSelectionMode(!state.selectionMode); return; }
|
||||
}
|
||||
if (keyboardIdle && state.currentDetail) {
|
||||
const key = event.key.toLowerCase();
|
||||
if (key === "x") {
|
||||
const current = state.messages.find((message) => message.uid === state.currentDetail.uid && (message.folder || state.folder) === state.currentDetail.folder);
|
||||
if (current) { event.preventDefault(); setMessageSelected(current, !state.selectedMessages.has(messageKey(current))); }
|
||||
}
|
||||
else if (key === "j") { event.preventDefault(); navigateMessage(1); }
|
||||
else if (key === "k") { event.preventDefault(); navigateMessage(-1); }
|
||||
else if (key === "r") { event.preventDefault(); openCompose("reply"); }
|
||||
else if (key === "a") { event.preventDefault(); openCompose("reply-all"); }
|
||||
else if (key === "f") { event.preventDefault(); openCompose("forward"); }
|
||||
else if (key === "e") { event.preventDefault(); archiveCurrent(); }
|
||||
else if (key === "m") { event.preventDefault(); toggleUnread(); }
|
||||
else if (key === "s") { event.preventDefault(); toggleFlagged(); }
|
||||
else if (event.key === "!") { event.preventDefault(); junkCurrent(); }
|
||||
else if (event.key === "Delete") { event.preventDefault(); deleteCurrent(); }
|
||||
}
|
||||
if (event.key === "Escape" && innerWidth <= 820) {
|
||||
if ($("#folder-pane")?.classList.contains("open")) setFolderDrawerOpen(false);
|
||||
else closeReader();
|
||||
}
|
||||
});
|
||||
matchMedia("(prefers-color-scheme: light)").addEventListener("change", () => { if (state.prefs.theme === "system") applyPreferences(); });
|
||||
window.addEventListener("resize", () => { if (innerWidth > 820) setFolderDrawerOpen(false); });
|
||||
}
|
||||
|
||||
async function boot() {
|
||||
applyPreferences();
|
||||
bindEvents();
|
||||
await loadAuthPolicy();
|
||||
const authenticated = await loadAccounts();
|
||||
if (authenticated) { await loadPreferences(); await loadMailPreferences(); await loadOpenPgp(); await loadIdentities(); await loadResponseTemplates(); await loadFolders(true); await loadSpecialFolders(); await loadTags(); await loadNotificationPreferences(); await loadTrustedSenders(); startCalendarReminderWatcher(); connectRealtime(); } else { await loadTranslations(state.prefs.language); }
|
||||
}
|
||||
|
||||
boot();
|
||||
}) ();
|
||||
Reference in new issue
Block a user